mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-22 22:13:35 +02:00
Support terrain editing
This commit is contained in:
parent
c096b8dd00
commit
a63c00a90e
@ -13,6 +13,7 @@ set(editor_SRCS
|
||||
windownewmap.cpp
|
||||
generatorprogress.cpp
|
||||
mapview.cpp
|
||||
radiopushbutton.cpp
|
||||
)
|
||||
|
||||
set(editor_HEADERS
|
||||
@ -29,6 +30,7 @@ set(editor_HEADERS
|
||||
windownewmap.h
|
||||
generatorprogress.h
|
||||
mapview.h
|
||||
radiopushbutton.h
|
||||
)
|
||||
|
||||
set(editor_FORMS
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "StdInc.h"
|
||||
#include "generatorprogress.h"
|
||||
#include "ui_generatorprogress.h"
|
||||
#include <thread>
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "StdInc.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
@ -15,6 +16,7 @@
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/mapping/CMapEditManager.h"
|
||||
#include "../lib/Terrain.h"
|
||||
#include "../lib/mapObjects/CObjectClassesHandler.h"
|
||||
|
||||
@ -112,6 +114,11 @@ MainWindow::~MainWindow()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
MapView * MainWindow::getMapView()
|
||||
{
|
||||
return ui->mapView;
|
||||
}
|
||||
|
||||
void MainWindow::setStatusMessage(const QString & status)
|
||||
{
|
||||
statusBar()->showMessage(status);
|
||||
@ -119,23 +126,12 @@ void MainWindow::setStatusMessage(const QString & status)
|
||||
|
||||
void MainWindow::reloadMap(int level)
|
||||
{
|
||||
MapHandler mapHandler(map.get());
|
||||
//auto mapSizePx = mapHandler->surface.rect();
|
||||
//float ratio = std::fmin(mapSizePx.width() / 192., mapSizePx.height() / 192.);*/
|
||||
//minimap = mapHandler->surface;
|
||||
//minimap.setDevicePixelRatio(ratio);
|
||||
|
||||
for(int j = 0; j < map->height; ++j)
|
||||
{
|
||||
for(int i = 0; i < map->width; ++i)
|
||||
{
|
||||
mapHandler.drawTerrainTile(i, j, level);
|
||||
mapHandler.drawObjects(i, j, level);
|
||||
}
|
||||
}
|
||||
|
||||
auto mapSizePx = mapHandler.surface.rect();
|
||||
float ratio = std::fmin(mapSizePx.width() / 192., mapSizePx.height() / 192.);
|
||||
minimap = mapHandler.surface;
|
||||
minimap.setDevicePixelRatio(ratio);
|
||||
|
||||
scenes[level]->updateViews(mapHandler.surface);
|
||||
scenes[level]->updateViews();
|
||||
|
||||
//sceneMini->clear();
|
||||
//sceneMini->addPixmap(minimap);
|
||||
@ -146,6 +142,11 @@ CMap * MainWindow::getMap()
|
||||
return map.get();
|
||||
}
|
||||
|
||||
MapHandler * MainWindow::getMapHandler()
|
||||
{
|
||||
return mapHandler.get();
|
||||
}
|
||||
|
||||
void MainWindow::setMapRaw(std::unique_ptr<CMap> cmap)
|
||||
{
|
||||
map = std::move(cmap);
|
||||
@ -158,6 +159,9 @@ void MainWindow::setMap(bool isNew)
|
||||
filename.clear();
|
||||
|
||||
setWindowTitle(filename + "* - VCMI Map Editor");
|
||||
|
||||
mapHandler.reset(new MapHandler(map.get()));
|
||||
|
||||
reloadMap();
|
||||
if(map->twoLevel)
|
||||
reloadMap(1);
|
||||
@ -262,12 +266,32 @@ void MainWindow::on_actionSave_triggered()
|
||||
saveMap();
|
||||
}
|
||||
|
||||
void MainWindow::terrainButtonClicked(Terrain terrain)
|
||||
{
|
||||
std::vector<int3> v(scenes[mapLevel]->selectionTerrainView.selection().begin(), scenes[mapLevel]->selectionTerrainView.selection().end());
|
||||
if(v.empty())
|
||||
return;
|
||||
|
||||
scenes[mapLevel]->selectionTerrainView.clear();
|
||||
scenes[mapLevel]->selectionTerrainView.draw();
|
||||
|
||||
map->getEditManager()->getTerrainSelection().setSelection(v);
|
||||
map->getEditManager()->drawTerrain(terrain, &CRandomGenerator::getDefault());
|
||||
|
||||
for(auto & t : v)
|
||||
scenes[mapLevel]->terrainView.setDirty(t);
|
||||
scenes[mapLevel]->terrainView.draw();
|
||||
}
|
||||
|
||||
void MainWindow::loadObjectsTree()
|
||||
{
|
||||
for(auto & terrain : Terrain::Manager::terrains())
|
||||
{
|
||||
ui->listTerrains->addItem(QString::fromStdString(terrain));
|
||||
QPushButton *b = new QPushButton(QString::fromStdString(terrain));
|
||||
ui->terrainLayout->addWidget(b);
|
||||
connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain); });
|
||||
}
|
||||
ui->terrainLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
|
||||
/*
|
||||
createHandler(bth, "Bonus type", pomtime);
|
||||
createHandler(generaltexth, "General text", pomtime);
|
||||
@ -350,3 +374,41 @@ void MainWindow::on_actionGrid_triggered(bool checked)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::on_toolBrush_clicked(bool checked)
|
||||
{
|
||||
//ui->toolBrush->setChecked(false);
|
||||
ui->toolBrush2->setChecked(false);
|
||||
ui->toolBrush4->setChecked(false);
|
||||
ui->toolArea->setChecked(false);
|
||||
ui->toolLasso->setChecked(false);
|
||||
|
||||
if(checked)
|
||||
{
|
||||
ui->mapView->selectionTool = MapView::SelectionTool::Brush;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->mapView->selectionTool = MapView::SelectionTool::None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::on_toolArea_clicked(bool checked)
|
||||
{
|
||||
ui->toolBrush->setChecked(false);
|
||||
ui->toolBrush2->setChecked(false);
|
||||
ui->toolBrush4->setChecked(false);
|
||||
//ui->toolArea->setChecked(false);
|
||||
ui->toolLasso->setChecked(false);
|
||||
|
||||
if(checked)
|
||||
{
|
||||
ui->mapView->selectionTool = MapView::SelectionTool::Area;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->mapView->selectionTool = MapView::SelectionTool::None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,11 @@
|
||||
#include <QMainWindow>
|
||||
#include <QGraphicsScene>
|
||||
#include <QStandardItemModel>
|
||||
#include "../lib/Terrain.h"
|
||||
|
||||
#include "maphandler.h"
|
||||
|
||||
#include "mapview.h"
|
||||
|
||||
class CMap;
|
||||
|
||||
namespace Ui {
|
||||
@ -28,11 +29,14 @@ public:
|
||||
void saveMap();
|
||||
|
||||
CMap * getMap();
|
||||
MapHandler * getMapHandler();
|
||||
|
||||
void loadObjectsTree();
|
||||
|
||||
void setStatusMessage(const QString & status);
|
||||
|
||||
MapView * getMapView();
|
||||
|
||||
private slots:
|
||||
void on_actionOpen_triggered();
|
||||
|
||||
@ -48,9 +52,16 @@ private slots:
|
||||
|
||||
void on_actionGrid_triggered(bool checked);
|
||||
|
||||
void on_toolBrush_clicked(bool checked);
|
||||
|
||||
void on_toolArea_clicked(bool checked);
|
||||
|
||||
void terrainButtonClicked(Terrain terrain);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
|
||||
std::unique_ptr<MapHandler> mapHandler;
|
||||
std::array<MapScene *, 2> scenes;
|
||||
QGraphicsScene * sceneMini;
|
||||
QPixmap minimap;
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1055</width>
|
||||
<height>737</height>
|
||||
<width>1024</width>
|
||||
<height>768</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -17,6 +17,12 @@
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="MapView" name="mapView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
@ -29,7 +35,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1055</width>
|
||||
<width>1024</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -69,15 +75,39 @@
|
||||
<addaction name="actionFill"/>
|
||||
</widget>
|
||||
<widget class="QDockWidget" name="dockWidget_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>524287</width>
|
||||
<height>524287</height>
|
||||
</size>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_2">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>524287</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QGraphicsView" name="minimapView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -106,10 +136,34 @@
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QDockWidget" name="dockWidget_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>292</width>
|
||||
<height>183</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>524287</width>
|
||||
<height>524287</height>
|
||||
</size>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>2</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
@ -122,131 +176,9 @@
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Surface</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page_5">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>262</width>
|
||||
<height>170</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Terrains</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listTerrains"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_6">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>262</width>
|
||||
<height>170</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Roads</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listRoads"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_7">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>262</width>
|
||||
<height>170</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Rivers</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="listRivers"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -288,6 +220,337 @@
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QDockWidget" name="dockWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>118</width>
|
||||
<height>372</height>
|
||||
</size>
|
||||
</property>
|
||||
<attribute name="dockWidgetArea">
|
||||
<number>1</number>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>118</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Brush</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="toolBrush">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="toolBrush2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="toolBrush4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="toolArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>[]</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="toolLasso">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>O</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="toolErase">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>E</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>118</width>
|
||||
<height>395</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Terrains</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="terrainLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="roadPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>118</width>
|
||||
<height>395</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Roads</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="riverPage">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Rivers</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<action name="actionOpen">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
@ -313,43 +576,6 @@
|
||||
<string>U/G</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action1x1">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1x1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action2x2">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2x2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action3x3">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>3x3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action4x4">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>4x4</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionErase">
|
||||
<property name="text">
|
||||
<string>Erase</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPass">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
|
@ -19,7 +19,7 @@ static bool objectBlitOrderSorter(const TerrainTileObject & a, const TerrainTile
|
||||
}
|
||||
|
||||
MapHandler::MapHandler(const CMap * Map):
|
||||
map(Map), surface(Map->width * tileSize, Map->height * tileSize), painter(&surface)
|
||||
map(Map)
|
||||
{
|
||||
init();
|
||||
}
|
||||
@ -82,7 +82,7 @@ void MapHandler::initTerrainGraphics()
|
||||
ttiles.resize(sizes.x * sizes.y * sizes.z);
|
||||
}
|
||||
|
||||
void MapHandler::drawTerrainTile(int x, int y, int z)
|
||||
void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z)
|
||||
{
|
||||
auto & tinfo = map->getTile(int3(x, y, z));
|
||||
//Rect destRect(realTileRect);
|
||||
@ -331,7 +331,7 @@ MapHandler::AnimBitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance
|
||||
return MapHandler::AnimBitmapHolder(bitmap);
|
||||
}
|
||||
|
||||
void MapHandler::drawObjects(int x, int y, int z)
|
||||
void MapHandler::drawObjects(QPainter & painter, int x, int y, int z)
|
||||
{
|
||||
auto & objects = ttiles[z * (sizes.x * sizes.y) + y * sizes.x + x].objects;
|
||||
for(auto & object : objects)
|
||||
|
@ -70,8 +70,6 @@ public:
|
||||
std::vector<TerrainTile2> ttiles; //informations about map tiles
|
||||
int3 sizes; //map size (x = width, y = height, z = number of levels)
|
||||
const CMap * map;
|
||||
QPixmap surface;
|
||||
QPainter painter;
|
||||
|
||||
//terrain graphics
|
||||
|
||||
@ -88,13 +86,13 @@ public:
|
||||
TFlippedAnimations riverAnimations;//[river type, rotation]
|
||||
TFlippedCache riverImages;//[river type, view type, rotation]
|
||||
|
||||
void drawTerrainTile(int x, int y, int z);
|
||||
void drawTerrainTile(QPainter & painter, int x, int y, int z);
|
||||
/// draws a river segment on current tile
|
||||
//void drawRiver(const TerrainTile & tinfo) const;
|
||||
/// draws a road segment on current tile
|
||||
//void drawRoad(const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const;
|
||||
/// draws all objects on current tile (higher-level logic, unlike other draw*** methods)
|
||||
void drawObjects(int x, int y, int z);
|
||||
void drawObjects(QPainter & painter, int x, int y, int z);
|
||||
//void drawObject(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, SDL_Rect * sourceRect, bool moving) const;
|
||||
//void drawHeroFlag(SDL_Surface * targetSurf, std::shared_ptr<IImage> source, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const;
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
#include "StdInc.h"
|
||||
#include "mapview.h"
|
||||
#include "mainwindow.h"
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
|
||||
MapView::MapView(QWidget *parent):
|
||||
QGraphicsView(parent)
|
||||
QGraphicsView(parent),
|
||||
selectionTool(MapView::SelectionTool::Brush)
|
||||
{
|
||||
}
|
||||
|
||||
@ -16,14 +18,77 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent)
|
||||
{
|
||||
this->update();
|
||||
|
||||
auto pos = mouseEvent->pos();
|
||||
auto * sc = static_cast<MapScene*>(scene());
|
||||
if(!sc)
|
||||
return;
|
||||
|
||||
main->setStatusMessage(QString("x: %1 y: %2").arg(QString::number(pos.x()), QString::number(pos.y())));
|
||||
auto pos = mapToScene(mouseEvent->pos()); //TODO: do we need to check size?
|
||||
int3 tile(pos.x() / 32, pos.y() / 32, sc->level);
|
||||
|
||||
if(tile == tilePrev) //do not redraw
|
||||
return;
|
||||
|
||||
tilePrev = tile;
|
||||
|
||||
//main->setStatusMessage(QString("x: %1 y: %2").arg(QString::number(pos.x()), QString::number(pos.y())));
|
||||
|
||||
switch(selectionTool)
|
||||
{
|
||||
case MapView::SelectionTool::Brush:
|
||||
if(pressedOnSelected)
|
||||
sc->selectionTerrainView.erase(tile);
|
||||
else
|
||||
sc->selectionTerrainView.select(tile);
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Area:
|
||||
sc->selectionTerrainView.clear();
|
||||
for(int j = std::min(tile.y, tileStart.y); j < std::max(tile.y, tileStart.y); ++j)
|
||||
{
|
||||
for(int i = std::min(tile.x, tileStart.x); i < std::max(tile.x, tileStart.x); ++i)
|
||||
{
|
||||
sc->selectionTerrainView.select(int3(i, j, sc->level));
|
||||
}
|
||||
}
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MapView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
this->update();
|
||||
|
||||
auto * sc = static_cast<MapScene*>(scene());
|
||||
if(!sc)
|
||||
return;
|
||||
|
||||
mouseStart = mapToScene(event->pos());
|
||||
tileStart = tilePrev = int3(mouseStart.x() / 32, mouseStart.y() / 32, sc->level);
|
||||
|
||||
if(sc->selectionTerrainView.selection().count(tileStart))
|
||||
pressedOnSelected = true;
|
||||
else
|
||||
pressedOnSelected = false;
|
||||
|
||||
switch(selectionTool)
|
||||
{
|
||||
case MapView::SelectionTool::Brush:
|
||||
if(pressedOnSelected)
|
||||
sc->selectionTerrainView.erase(tileStart);
|
||||
else
|
||||
sc->selectionTerrainView.select(tileStart);
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Area:
|
||||
sc->selectionTerrainView.clear();
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
}
|
||||
|
||||
//main->setStatusMessage(QString("x: %1 y: %2").arg(QString::number(event->pos().x()), QString::number(event->pos().y())));
|
||||
}
|
||||
|
||||
void MapView::mouseReleaseEvent(QMouseEvent *event)
|
||||
@ -35,20 +100,20 @@ MapScene::MapScene(MainWindow *parent, int l):
|
||||
QGraphicsScene(parent),
|
||||
gridView(parent, this),
|
||||
passabilityView(parent, this),
|
||||
selectionTerrainView(parent, this),
|
||||
terrainView(parent, this),
|
||||
main(parent),
|
||||
level(l)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MapScene::updateViews(const QPixmap & surface)
|
||||
void MapScene::updateViews()
|
||||
{
|
||||
if(item)
|
||||
removeItem(item.get());
|
||||
item.reset(addPixmap(surface));
|
||||
|
||||
terrainView.update();
|
||||
gridView.update();
|
||||
passabilityView.update();
|
||||
selectionTerrainView.update();
|
||||
}
|
||||
|
||||
BasicView::BasicView(MainWindow * m, MapScene * s): main(m), scene(s)
|
||||
@ -77,14 +142,28 @@ void BasicView::show(bool show)
|
||||
isShown = show;
|
||||
}
|
||||
|
||||
void BasicView::redraw()
|
||||
{
|
||||
if(item)
|
||||
scene->removeItem(item.get());
|
||||
|
||||
if(isShown)
|
||||
{
|
||||
item.reset(scene->addPixmap(*pixmap));
|
||||
}
|
||||
}
|
||||
|
||||
GridView::GridView(MainWindow * m, MapScene * s): BasicView(m, s)
|
||||
{
|
||||
}
|
||||
|
||||
void GridView::update()
|
||||
{
|
||||
show(false);
|
||||
auto map = main->getMap();
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
show(false);
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
@ -109,8 +188,11 @@ PassabilityView::PassabilityView(MainWindow * m, MapScene * s): BasicView(m, s)
|
||||
|
||||
void PassabilityView::update()
|
||||
{
|
||||
show(false);
|
||||
auto map = main->getMap();
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
show(false);
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
@ -133,3 +215,147 @@ void PassabilityView::update()
|
||||
|
||||
show(isShown);
|
||||
}
|
||||
|
||||
SelectionTerrainView::SelectionTerrainView(MainWindow * m, MapScene * s): BasicView(m, s)
|
||||
{
|
||||
}
|
||||
|
||||
void SelectionTerrainView::update()
|
||||
{
|
||||
auto map = main->getMap();
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
show(false);
|
||||
|
||||
area.clear();
|
||||
areaAdd.clear();
|
||||
areaErase.clear();
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
|
||||
show(true); //always visible
|
||||
}
|
||||
|
||||
void SelectionTerrainView::draw()
|
||||
{
|
||||
if(!pixmap)
|
||||
return;
|
||||
|
||||
QPainter painter(pixmap.get());
|
||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
for(auto & t : areaAdd)
|
||||
{
|
||||
painter.fillRect(t.x * 32, t.y * 32, 31, 31, QColor(128, 128, 128, 96));
|
||||
}
|
||||
for(auto & t : areaErase)
|
||||
{
|
||||
painter.fillRect(t.x * 32, t.y * 32, 31, 31, QColor(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
areaAdd.clear();
|
||||
areaErase.clear();
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void SelectionTerrainView::select(const int3 & tile)
|
||||
{
|
||||
if(!area.count(tile))
|
||||
{
|
||||
area.insert(tile);
|
||||
areaAdd.insert(tile);
|
||||
areaErase.erase(tile);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTerrainView::erase(const int3 & tile)
|
||||
{
|
||||
if(area.count(tile))
|
||||
{
|
||||
area.erase(tile);
|
||||
areaErase.insert(tile);
|
||||
areaAdd.erase(tile);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTerrainView::clear()
|
||||
{
|
||||
areaErase = area;
|
||||
areaAdd.clear();
|
||||
area.clear();
|
||||
}
|
||||
|
||||
const std::set<int3> & SelectionTerrainView::selection() const
|
||||
{
|
||||
return area;
|
||||
}
|
||||
|
||||
TerrainView::TerrainView(MainWindow * m, MapScene * s): BasicView(m, s)
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainView::update()
|
||||
{
|
||||
auto map = main->getMap();
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
show(false);
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
draw(false);
|
||||
|
||||
show(true); //always visible
|
||||
}
|
||||
|
||||
void TerrainView::setDirty(const int3 & tile)
|
||||
{
|
||||
dirty.insert(tile);
|
||||
}
|
||||
|
||||
void TerrainView::draw(bool onlyDirty)
|
||||
{
|
||||
if(!pixmap)
|
||||
return;
|
||||
|
||||
auto map = main->getMap();
|
||||
|
||||
QPainter painter(pixmap.get());
|
||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
|
||||
if(onlyDirty)
|
||||
{
|
||||
std::set<int3> forRedrawing(dirty), neighbours;
|
||||
for(auto & t : dirty)
|
||||
{
|
||||
for(auto & tt : int3::getDirs())
|
||||
{
|
||||
if(map->isInTheMap(t + tt))
|
||||
neighbours.insert(t + tt);
|
||||
}
|
||||
}
|
||||
for(auto & t : neighbours)
|
||||
{
|
||||
for(auto & tt : int3::getDirs())
|
||||
{
|
||||
forRedrawing.insert(t);
|
||||
if(map->isInTheMap(t + tt))
|
||||
forRedrawing.insert(t + tt);
|
||||
}
|
||||
}
|
||||
for(auto & t : forRedrawing)
|
||||
{
|
||||
main->getMapHandler()->drawTerrainTile(painter, t.x, t.y, scene->level);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int j = 0; j < map->height; ++j)
|
||||
for(int i = 0; i < map->width; ++i)
|
||||
main->getMapHandler()->drawTerrainTile(painter, i, j, scene->level);
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include "../lib/int3.h"
|
||||
|
||||
class int3;
|
||||
class MainWindow;
|
||||
class MapScene;
|
||||
|
||||
@ -15,6 +17,7 @@ public:
|
||||
virtual void update() = 0;
|
||||
|
||||
void show(bool show);
|
||||
void redraw();
|
||||
|
||||
protected:
|
||||
MainWindow * main;
|
||||
@ -29,11 +32,19 @@ private:
|
||||
|
||||
class MapView : public QGraphicsView
|
||||
{
|
||||
public:
|
||||
enum class SelectionTool
|
||||
{
|
||||
None, Brush, Brush2, Brush4, Area, Lasso
|
||||
};
|
||||
|
||||
public:
|
||||
MapView(QWidget *parent);
|
||||
|
||||
void setMain(MainWindow * m);
|
||||
|
||||
SelectionTool selectionTool;
|
||||
|
||||
public slots:
|
||||
void mouseMoveEvent(QMouseEvent * mouseEvent) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
@ -41,6 +52,11 @@ public slots:
|
||||
|
||||
private:
|
||||
MainWindow * main;
|
||||
|
||||
QPointF mouseStart;
|
||||
int3 tileStart;
|
||||
int3 tilePrev;
|
||||
bool pressedOnSelected;
|
||||
};
|
||||
|
||||
class GridView: public BasicView
|
||||
@ -59,20 +75,53 @@ public:
|
||||
void update() override;
|
||||
};
|
||||
|
||||
class SelectionTerrainView: public BasicView
|
||||
{
|
||||
public:
|
||||
SelectionTerrainView(MainWindow * m, MapScene * s);
|
||||
|
||||
void update() override;
|
||||
|
||||
void draw();
|
||||
void select(const int3 & tile);
|
||||
void erase(const int3 & tile);
|
||||
void clear();
|
||||
|
||||
const std::set<int3> & selection() const;
|
||||
|
||||
private:
|
||||
std::set<int3> area, areaAdd, areaErase;
|
||||
};
|
||||
|
||||
class TerrainView: public BasicView
|
||||
{
|
||||
public:
|
||||
TerrainView(MainWindow * m, MapScene * s);
|
||||
|
||||
void update() override;
|
||||
|
||||
void draw(bool onlyDirty = true);
|
||||
void setDirty(const int3 & tile);
|
||||
|
||||
private:
|
||||
std::set<int3> dirty;
|
||||
};
|
||||
|
||||
class MapScene : public QGraphicsScene
|
||||
{
|
||||
public:
|
||||
MapScene(MainWindow *parent, int l);
|
||||
|
||||
void updateViews(const QPixmap & surface);
|
||||
void updateViews();
|
||||
|
||||
GridView gridView;
|
||||
PassabilityView passabilityView;
|
||||
SelectionTerrainView selectionTerrainView;
|
||||
TerrainView terrainView;
|
||||
|
||||
const int level;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QGraphicsPixmapItem> item;
|
||||
MainWindow * main;
|
||||
};
|
||||
|
||||
|
7
mapeditor/radiopushbutton.cpp
Normal file
7
mapeditor/radiopushbutton.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "radiopushbutton.h"
|
||||
|
||||
RadioPushButton::RadioPushButton()
|
||||
{
|
||||
|
||||
}
|
12
mapeditor/radiopushbutton.h
Normal file
12
mapeditor/radiopushbutton.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef RADIOPUSHBUTTON_H
|
||||
#define RADIOPUSHBUTTON_H
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
class RadioPushButton : public QPushButton
|
||||
{
|
||||
public:
|
||||
RadioPushButton();
|
||||
};
|
||||
|
||||
#endif // RADIOPUSHBUTTON_H
|
@ -1,3 +1,4 @@
|
||||
#include "StdInc.h"
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
#include "Spoiler.h"
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "StdInc.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/rmg/CRmgTemplateStorage.h"
|
||||
#include "../lib/rmg/CRmgTemplate.h"
|
||||
|
Loading…
Reference in New Issue
Block a user