mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-15 01:24:45 +02:00
Merge branch 'cpp-map-editor' into EraseAction
This commit is contained in:
@ -21,6 +21,7 @@ set(editor_SRCS
|
|||||||
playerparams.cpp
|
playerparams.cpp
|
||||||
scenelayer.cpp
|
scenelayer.cpp
|
||||||
mapcontroller.cpp
|
mapcontroller.cpp
|
||||||
|
validator.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(editor_HEADERS
|
set(editor_HEADERS
|
||||||
@ -45,6 +46,7 @@ set(editor_HEADERS
|
|||||||
playerparams.h
|
playerparams.h
|
||||||
scenelayer.h
|
scenelayer.h
|
||||||
mapcontroller.h
|
mapcontroller.h
|
||||||
|
validator.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(editor_FORMS
|
set(editor_FORMS
|
||||||
@ -54,6 +56,7 @@ set(editor_FORMS
|
|||||||
mapsettings.ui
|
mapsettings.ui
|
||||||
playersettings.ui
|
playersettings.ui
|
||||||
playerparams.ui
|
playerparams.ui
|
||||||
|
validator.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
assign_source_group(${editor_SRCS} ${editor_HEADERS} VCMI_launcher.rc)
|
assign_source_group(${editor_SRCS} ${editor_HEADERS} VCMI_launcher.rc)
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "inspector.h"
|
#include "inspector.h"
|
||||||
#include "mapsettings.h"
|
#include "mapsettings.h"
|
||||||
#include "playersettings.h"
|
#include "playersettings.h"
|
||||||
|
#include "validator.h"
|
||||||
|
|
||||||
static CBasicLogConfigurator * logConfig;
|
static CBasicLogConfigurator * logConfig;
|
||||||
|
|
||||||
@ -230,6 +231,20 @@ void MainWindow::saveMap()
|
|||||||
if(!unsaved)
|
if(!unsaved)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
//validate map
|
||||||
|
auto issues = Validator::validate(controller.map());
|
||||||
|
bool critical = false;
|
||||||
|
for(auto & issue : issues)
|
||||||
|
critical |= issue.critical;
|
||||||
|
|
||||||
|
if(!issues.empty())
|
||||||
|
{
|
||||||
|
if(critical)
|
||||||
|
QMessageBox::warning(this, "Map validation", "Map has critical problems and most probably will not be playable. Open Validator from the Map menu to see issues found");
|
||||||
|
else
|
||||||
|
QMessageBox::information(this, "Map validation", "Map has some errors. Open Validator from the Map menu to see issues found");
|
||||||
|
}
|
||||||
|
|
||||||
CMapService mapService;
|
CMapService mapService;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -825,3 +840,9 @@ void MainWindow::onSelectionMade(int level, bool anythingSelected)
|
|||||||
ui->toolErase->setEnabled(anythingSelected);
|
ui->toolErase->setEnabled(anythingSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_actionValidate_triggered()
|
||||||
|
{
|
||||||
|
new Validator(controller.map(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,8 @@ private slots:
|
|||||||
|
|
||||||
void on_actionPlayers_settings_triggered();
|
void on_actionPlayers_settings_triggered();
|
||||||
|
|
||||||
|
void on_actionValidate_triggered();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);
|
void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1024</width>
|
<width>1024</width>
|
||||||
<height>18</height>
|
<height>22</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
@ -69,6 +69,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<addaction name="actionMapSettings"/>
|
<addaction name="actionMapSettings"/>
|
||||||
<addaction name="actionPlayers_settings"/>
|
<addaction name="actionPlayers_settings"/>
|
||||||
|
<addaction name="actionValidate"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuEdit">
|
<widget class="QMenu" name="menuEdit">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -893,6 +894,11 @@
|
|||||||
<string>Neutral</string>
|
<string>Neutral</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionValidate">
|
||||||
|
<property name="text">
|
||||||
|
<string>Validate</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
129
mapeditor/validator.cpp
Normal file
129
mapeditor/validator.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "StdInc.h"
|
||||||
|
#include "validator.h"
|
||||||
|
#include "ui_validator.h"
|
||||||
|
#include "../lib/mapObjects/MapObjects.h"
|
||||||
|
|
||||||
|
Validator::Validator(const CMap * map, QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::Validator)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
show();
|
||||||
|
|
||||||
|
std::array<QString, 2> icons{"mapeditor/icons/mod-update.png", "mapeditor/icons/mod-delete.png"};
|
||||||
|
|
||||||
|
for(auto & issue : Validator::validate(map))
|
||||||
|
{
|
||||||
|
auto * item = new QListWidgetItem(QIcon(icons[issue.critical ? 1 : 0]), issue.message);
|
||||||
|
ui->listWidget->addItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Validator::~Validator()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<Validator::Issue> Validator::validate(const CMap * map)
|
||||||
|
{
|
||||||
|
std::list<Validator::Issue> issues;
|
||||||
|
|
||||||
|
if(!map)
|
||||||
|
{
|
||||||
|
issues.emplace_back("Map is not loaded", true);
|
||||||
|
return issues;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int hplayers = 0;
|
||||||
|
int cplayers = 0;
|
||||||
|
std::map<int, int> amountOfCastles;
|
||||||
|
for(int i = 0; i < map->players.size(); ++i)
|
||||||
|
{
|
||||||
|
auto & p = map->players[i];
|
||||||
|
if(p.canAnyonePlay())
|
||||||
|
amountOfCastles[i] = 0;
|
||||||
|
if(p.canComputerPlay)
|
||||||
|
++cplayers;
|
||||||
|
if(p.canHumanPlay)
|
||||||
|
++hplayers;
|
||||||
|
if(p.allowedFactions.empty())
|
||||||
|
issues.emplace_back(QString("No factions allowed for player %1").arg(i), true);
|
||||||
|
}
|
||||||
|
if(hplayers + cplayers == 0)
|
||||||
|
issues.emplace_back("No players allowed to play this map", true);
|
||||||
|
if(hplayers + cplayers == 1)
|
||||||
|
issues.emplace_back("Map is allowed for one player and cannot be started", true);
|
||||||
|
if(!hplayers)
|
||||||
|
issues.emplace_back("No human players allowed to play this map", true);
|
||||||
|
|
||||||
|
for(auto o : map->objects)
|
||||||
|
{
|
||||||
|
if(auto * ins = dynamic_cast<CArmedInstance*>(o.get()))
|
||||||
|
{
|
||||||
|
if(ins->getOwner() == PlayerColor::UNFLAGGABLE)
|
||||||
|
issues.emplace_back(QString("Armored instance %1 is UNFLAGGABLE but must have NEUTRAL or player owner").arg(ins->instanceName.c_str()), true);
|
||||||
|
}
|
||||||
|
if(auto * ins = dynamic_cast<CGTownInstance*>(o.get()))
|
||||||
|
{
|
||||||
|
bool has = amountOfCastles.count(ins->getOwner().getNum());
|
||||||
|
if(!has && ins->getOwner() != PlayerColor::NEUTRAL)
|
||||||
|
issues.emplace_back(QString("Town %1 has undefined owner %s").arg(ins->instanceName.c_str(), ins->getOwner().getStr().c_str()), true);
|
||||||
|
if(has)
|
||||||
|
++amountOfCastles[ins->getOwner().getNum()];
|
||||||
|
}
|
||||||
|
if(auto * ins = dynamic_cast<CGHeroInstance*>(o.get()))
|
||||||
|
{
|
||||||
|
bool has = amountOfCastles.count(ins->getOwner().getNum());
|
||||||
|
if(!has)
|
||||||
|
issues.emplace_back(QString("Hero %1 must have an owner").arg(ins->instanceName.c_str()), true);
|
||||||
|
else
|
||||||
|
issues.emplace_back(QString("Hero %1: heroes on map are not supported in current version").arg(ins->instanceName.c_str()), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto & mp : amountOfCastles)
|
||||||
|
if(mp.second == 0)
|
||||||
|
issues.emplace_back(QString("Player %1 doesn't have any starting town").arg(mp.first), false);
|
||||||
|
|
||||||
|
bool roadWarning = false;
|
||||||
|
bool riverWarning = false;
|
||||||
|
for(int k : {0, 1})
|
||||||
|
{
|
||||||
|
if(k && !map->twoLevel)
|
||||||
|
break;
|
||||||
|
for(int j = 0; j < map->height; ++j)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < map->width; ++i)
|
||||||
|
{
|
||||||
|
if(map->getTile(int3(i, j, k)).roadType != ROAD_NAMES[0])
|
||||||
|
roadWarning = true;
|
||||||
|
if(map->getTile(int3(i, j, k)).riverType != RIVER_NAMES[0])
|
||||||
|
riverWarning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(roadWarning)
|
||||||
|
issues.emplace_back("Roads are presented on the map but not supported by Map Editor", false);
|
||||||
|
if(riverWarning)
|
||||||
|
issues.emplace_back("Rivers are presented on the map but not supported by Map Editor", false);
|
||||||
|
|
||||||
|
if(map->name.empty())
|
||||||
|
issues.emplace_back("Map name is not specified", false);
|
||||||
|
if(map->description.empty())
|
||||||
|
issues.emplace_back("Map description is not specified", false);
|
||||||
|
}
|
||||||
|
catch(const std::exception & e)
|
||||||
|
{
|
||||||
|
issues.emplace_back(QString("Exception occurs during validation: %1").arg(e.what()), true);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
issues.emplace_back("Unknown exception occurs during validation", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return issues;
|
||||||
|
}
|
33
mapeditor/validator.h
Normal file
33
mapeditor/validator.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef VALIDATOR_H
|
||||||
|
#define VALIDATOR_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "../lib/mapping/CMap.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class Validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Validator : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
struct Issue
|
||||||
|
{
|
||||||
|
QString message;
|
||||||
|
bool critical;
|
||||||
|
|
||||||
|
Issue(const QString & m, bool c): message(m), critical(c) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Validator(const CMap * map, QWidget *parent = nullptr);
|
||||||
|
~Validator();
|
||||||
|
|
||||||
|
static std::list<Issue> validate(const CMap * map);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::Validator *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VALIDATOR_H
|
72
mapeditor/validator.ui
Normal file
72
mapeditor/validator.ui
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Validator</class>
|
||||||
|
<widget class="QDialog" name="Validator">
|
||||||
|
<property name="windowModality">
|
||||||
|
<enum>Qt::NonModal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>482</width>
|
||||||
|
<height>178</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Map validation results</string>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QListWidget" name="listWidget">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>18</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Sunken</enum>
|
||||||
|
</property>
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::NoSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>32</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="resizeMode">
|
||||||
|
<enum>QListView::Adjust</enum>
|
||||||
|
</property>
|
||||||
|
<property name="gridSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>32</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="viewMode">
|
||||||
|
<enum>QListView::ListMode</enum>
|
||||||
|
</property>
|
||||||
|
<property name="uniformItemSizes">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Reference in New Issue
Block a user