1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

Merge branch 'cpp-map-editor' into EraseAction

This commit is contained in:
Nordsoft91 2022-09-07 01:46:33 +04:00 committed by GitHub
commit ce2ab1a431
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 267 additions and 1 deletions

View File

@ -21,6 +21,7 @@ set(editor_SRCS
playerparams.cpp
scenelayer.cpp
mapcontroller.cpp
validator.cpp
)
set(editor_HEADERS
@ -45,6 +46,7 @@ set(editor_HEADERS
playerparams.h
scenelayer.h
mapcontroller.h
validator.h
)
set(editor_FORMS
@ -54,6 +56,7 @@ set(editor_FORMS
mapsettings.ui
playersettings.ui
playerparams.ui
validator.ui
)
assign_source_group(${editor_SRCS} ${editor_HEADERS} VCMI_launcher.rc)

View File

@ -28,6 +28,7 @@
#include "inspector.h"
#include "mapsettings.h"
#include "playersettings.h"
#include "validator.h"
static CBasicLogConfigurator * logConfig;
@ -229,6 +230,20 @@ void MainWindow::saveMap()
if(!unsaved)
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;
try
@ -825,3 +840,9 @@ void MainWindow::onSelectionMade(int level, bool anythingSelected)
ui->toolErase->setEnabled(anythingSelected);
}
}
void MainWindow::on_actionValidate_triggered()
{
new Validator(controller.map(), this);
}

View File

@ -85,6 +85,8 @@ private slots:
void on_actionPlayers_settings_triggered();
void on_actionValidate_triggered();
public slots:
void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);

View File

@ -51,7 +51,7 @@
<x>0</x>
<y>0</y>
<width>1024</width>
<height>18</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -69,6 +69,7 @@
</property>
<addaction name="actionMapSettings"/>
<addaction name="actionPlayers_settings"/>
<addaction name="actionValidate"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
@ -893,6 +894,11 @@
<string>Neutral</string>
</property>
</action>
<action name="actionValidate">
<property name="text">
<string>Validate</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

129
mapeditor/validator.cpp Normal file
View 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
View 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
View 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>