mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
template editor
This commit is contained in:
@@ -8,6 +8,6 @@ sudo apt-get update
|
||||
# - debian build settings at debian/control
|
||||
sudo apt-get install libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev libboost-iostreams-dev \
|
||||
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
|
||||
qtbase5-dev qttools5-dev \
|
||||
qtbase5-dev qttools5-dev libqt5svg5-dev \
|
||||
ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev \
|
||||
libminizip-dev libfuzzylite-dev libsqlite3-dev # Optional dependencies
|
||||
|
||||
@@ -8,6 +8,6 @@ sudo apt-get update
|
||||
# - debian build settings at debian/control
|
||||
sudo apt-get install libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev libboost-iostreams-dev \
|
||||
libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev \
|
||||
qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools \
|
||||
qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools qt6-svg-dev libqt6svg6-dev \
|
||||
ninja-build zlib1g-dev libavformat-dev libswscale-dev libtbb-dev libluajit-5.1-dev \
|
||||
libminizip-dev libfuzzylite-dev libsqlite3-dev # Optional dependencies
|
||||
|
||||
@@ -108,6 +108,7 @@ endif()
|
||||
include(CMakeDependentOption)
|
||||
cmake_dependent_option(ENABLE_INNOEXTRACT "Enable innoextract for GOG file extraction in launcher" ON "ENABLE_LAUNCHER" OFF)
|
||||
cmake_dependent_option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON "NOT ENABLE_GOLDMASTER" OFF)
|
||||
cmake_dependent_option(ENABLE_TEMPLATE_EDITOR "Enable template editor inside map editor" ON "ENABLE_EDITOR" OFF)
|
||||
|
||||
option(VCMI_PORTMASTER "PortMaster build" OFF)
|
||||
|
||||
@@ -253,6 +254,10 @@ if(ENABLE_EDITOR)
|
||||
add_definitions(-DENABLE_EDITOR)
|
||||
endif()
|
||||
|
||||
if(ENABLE_TEMPLATE_EDITOR)
|
||||
add_definitions(-DENABLE_TEMPLATE_EDITOR)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SINGLE_APP_BUILD)
|
||||
add_definitions(-DENABLE_SINGLE_APP_BUILD)
|
||||
endif()
|
||||
@@ -521,6 +526,16 @@ if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
|
||||
|
||||
if(ENABLE_TEMPLATE_EDITOR)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Svg Xml)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Svg Xml)
|
||||
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
find_package(QT NAMES Qt6 REQUIRED COMPONENTS SvgWidgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS SvgWidgets)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS LinguistTools)
|
||||
add_definitions(-DENABLE_QT_TRANSLATIONS)
|
||||
|
||||
@@ -189,7 +189,8 @@
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"FORCE_BUNDLED_FL": "ON"
|
||||
"FORCE_BUNDLED_FL": "ON",
|
||||
"ENABLE_TEMPLATE_EDITOR": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -207,7 +208,8 @@
|
||||
"default-release"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"ENABLE_TEMPLATE_EDITOR": "OFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -44,6 +44,10 @@
|
||||
"treasureLikeZone" : { "type" : "number" },
|
||||
"customObjectsLikeZone" : { "type" : "number" },
|
||||
|
||||
"visPositionX" : { "type" : "number" },
|
||||
"visPositionY" : { "type" : "number" },
|
||||
"visSize" : { "type" : "number" },
|
||||
|
||||
"terrainTypes": {"$ref" : "#/definitions/stringArray"},
|
||||
"bannedTerrains": {"$ref" : "#/definitions/stringArray"},
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ To compile, the following packages (and their development counterparts) are need
|
||||
|
||||
For Ubuntu and Debian you need to install this list of packages:
|
||||
|
||||
`sudo apt-get install cmake g++ clang libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev zlib1g-dev libavformat-dev libswscale-dev libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev libboost-iostreams-dev qtbase5-dev libtbb-dev libluajit-5.1-dev liblzma-dev libsqlite3-dev qttools5-dev ninja-build ccache`
|
||||
`sudo apt-get install cmake g++ clang libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev zlib1g-dev libavformat-dev libswscale-dev libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev libboost-iostreams-dev qtbase5-dev libqt5svg5-dev libtbb-dev libluajit-5.1-dev liblzma-dev libsqlite3-dev qttools5-dev ninja-build ccache`
|
||||
|
||||
Alternatively if you have VCMI installed from repository or PPA you can use:
|
||||
|
||||
|
||||
@@ -144,14 +144,17 @@ ZoneOptions::ZoneOptions():
|
||||
type(ETemplateZoneType::PLAYER_START),
|
||||
size(1),
|
||||
maxTreasureValue(0),
|
||||
owner(std::nullopt),
|
||||
owner(PlayerColor(0)),
|
||||
matchTerrainToTown(true),
|
||||
townsAreSameType(false),
|
||||
monsterStrength(EMonsterStrength::ZONE_NORMAL),
|
||||
townsLikeZone(NO_ZONE),
|
||||
minesLikeZone(NO_ZONE),
|
||||
terrainTypeLikeZone(NO_ZONE),
|
||||
treasureLikeZone(NO_ZONE)
|
||||
treasureLikeZone(NO_ZONE),
|
||||
customObjectsLikeZone(NO_ZONE),
|
||||
visPosition(Point(0, 0)),
|
||||
visSize(1.0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -331,6 +334,26 @@ TRmgTemplateZoneId ZoneOptions::getTownsLikeZone() const
|
||||
return townsLikeZone;
|
||||
}
|
||||
|
||||
Point ZoneOptions::getVisPosition() const
|
||||
{
|
||||
return visPosition;
|
||||
}
|
||||
|
||||
void ZoneOptions::setVisPosition(Point value)
|
||||
{
|
||||
visPosition = value;
|
||||
}
|
||||
|
||||
float ZoneOptions::getVisSize() const
|
||||
{
|
||||
return visSize;
|
||||
}
|
||||
|
||||
void ZoneOptions::setVisSize(float value)
|
||||
{
|
||||
visSize = value;
|
||||
}
|
||||
|
||||
void ZoneOptions::addConnection(const ZoneConnection & connection)
|
||||
{
|
||||
connectedZoneIds.push_back(connection.getOtherZoneId(getId()));
|
||||
@@ -496,6 +519,9 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
|
||||
}
|
||||
|
||||
handler.serializeStruct("customObjects", objectConfig);
|
||||
handler.serializeInt("visPositionX", visPosition.x);
|
||||
handler.serializeInt("visPositionY", visPosition.y);
|
||||
handler.serializeFloat("visSize", visSize);
|
||||
}
|
||||
|
||||
ZoneConnection::ZoneConnection():
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "../int3.h"
|
||||
#include "../GameConstants.h"
|
||||
#include "../Point.h"
|
||||
#include "../ResourceSet.h"
|
||||
#include "ObjectInfo.h"
|
||||
#include "ObjectConfig.h"
|
||||
@@ -21,6 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class JsonSerializeFormat;
|
||||
struct CompoundMapObjectID;
|
||||
class TemplateEditor;
|
||||
|
||||
enum class ETemplateZoneType
|
||||
{
|
||||
@@ -93,6 +95,10 @@ enum class ERoadOption
|
||||
|
||||
class DLL_LINKAGE ZoneConnection
|
||||
{
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
friend class ::TemplateEditor;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
ZoneConnection();
|
||||
@@ -120,11 +126,18 @@ private:
|
||||
|
||||
class DLL_LINKAGE ZoneOptions
|
||||
{
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
friend class ::TemplateEditor;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static const TRmgTemplateZoneId NO_ZONE;
|
||||
|
||||
class DLL_LINKAGE CTownInfo
|
||||
{
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
friend class ::TemplateEditor;
|
||||
#endif
|
||||
public:
|
||||
CTownInfo();
|
||||
|
||||
@@ -228,6 +241,12 @@ public:
|
||||
TRmgTemplateZoneId getCustomObjectsLikeZone() const;
|
||||
TRmgTemplateZoneId getTownsLikeZone() const;
|
||||
|
||||
Point getVisPosition() const;
|
||||
void setVisPosition(Point value);
|
||||
|
||||
float getVisSize() const;
|
||||
void setVisSize(float value);
|
||||
|
||||
protected:
|
||||
TRmgTemplateZoneId id;
|
||||
ETemplateZoneType type;
|
||||
@@ -235,6 +254,9 @@ protected:
|
||||
ui32 maxTreasureValue;
|
||||
std::optional<int> owner;
|
||||
|
||||
Point visPosition;
|
||||
float visSize;
|
||||
|
||||
ObjectConfig objectConfig;
|
||||
CTownInfo playerTowns;
|
||||
CTownInfo neutralTowns;
|
||||
@@ -268,11 +290,18 @@ protected:
|
||||
/// The CRmgTemplate describes a random map template.
|
||||
class DLL_LINKAGE CRmgTemplate : boost::noncopyable
|
||||
{
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
friend class ::TemplateEditor;
|
||||
#endif
|
||||
|
||||
public:
|
||||
using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<rmg::ZoneOptions>>;
|
||||
|
||||
class DLL_LINKAGE CPlayerCountRange
|
||||
{
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
friend class ::TemplateEditor;
|
||||
#endif
|
||||
public:
|
||||
void addRange(int lower, int upper);
|
||||
void addNumber(int value);
|
||||
|
||||
@@ -49,6 +49,15 @@ set(editor_SRCS
|
||||
campaigneditor/startingbonus.cpp
|
||||
campaigneditor/campaignview.cpp
|
||||
)
|
||||
if(ENABLE_TEMPLATE_EDITOR)
|
||||
set(editor_SRCS
|
||||
${editor_SRCS}
|
||||
templateeditor/templateeditor.cpp
|
||||
templateeditor/templateview.cpp
|
||||
templateeditor/graphicelements.cpp
|
||||
templateeditor/algorithm.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(editor_HEADERS
|
||||
StdInc.h
|
||||
@@ -102,6 +111,15 @@ set(editor_HEADERS
|
||||
campaigneditor/startingbonus.h
|
||||
campaigneditor/campaignview.h
|
||||
)
|
||||
if(ENABLE_TEMPLATE_EDITOR)
|
||||
set(editor_HEADERS
|
||||
${editor_HEADERS}
|
||||
templateeditor/templateeditor.h
|
||||
templateeditor/templateview.h
|
||||
templateeditor/graphicelements.h
|
||||
templateeditor/algorithm.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(editor_FORMS
|
||||
mainwindow.ui
|
||||
@@ -137,6 +155,12 @@ set(editor_FORMS
|
||||
campaigneditor/scenarioproperties.ui
|
||||
campaigneditor/startingbonus.ui
|
||||
)
|
||||
if(ENABLE_TEMPLATE_EDITOR)
|
||||
set(editor_FORMS
|
||||
${editor_FORMS}
|
||||
templateeditor/templateeditor.ui
|
||||
)
|
||||
endif()
|
||||
|
||||
set(editor_RESOURCES
|
||||
resources.qrc
|
||||
@@ -254,6 +278,10 @@ if(APPLE)
|
||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER vcmieditor)
|
||||
endif()
|
||||
|
||||
if(ENABLE_TEMPLATE_EDITOR)
|
||||
target_compile_definitions(vcmieditor PRIVATE ENABLE_TEMPLATE_EDITOR)
|
||||
endif()
|
||||
|
||||
# Qt defines 'emit' as macros, which conflicts with TBB definition of method with same name
|
||||
target_compile_definitions(vcmieditor PRIVATE QT_NO_EMIT)
|
||||
|
||||
@@ -262,6 +290,12 @@ if(ENABLE_STATIC_LIBS OR NOT (ENABLE_EDITOR AND ENABLE_LAUNCHER))
|
||||
endif()
|
||||
|
||||
target_link_libraries(vcmieditor vcmi vcmiqt Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
|
||||
if(ENABLE_TEMPLATE_EDITOR)
|
||||
target_link_libraries(vcmieditor vcmi vcmiqt Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Xml)
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
target_link_libraries(vcmieditor vcmi vcmiqt Qt6::SvgWidgets)
|
||||
endif()
|
||||
endif()
|
||||
target_include_directories(vcmieditor
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
#include "../Global.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
#include <QtSvg>
|
||||
#include <QSvgRenderer>
|
||||
#include <QDomDocument>
|
||||
#endif
|
||||
#include <QStringList>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/mapping/MapFormatJson.h"
|
||||
#include "../lib/modding/ModIncompatibility.h"
|
||||
#include "../lib/rmg/CRmgTemplate.h"
|
||||
#include "../lib/serializer/JsonSerializer.h"
|
||||
#include "../lib/serializer/JsonDeserializer.h"
|
||||
#include "../lib/serializer/CSaveFile.h"
|
||||
|
||||
std::unique_ptr<CMap> Helper::openMapInternal(const QString & filenameSelect)
|
||||
{
|
||||
@@ -77,6 +81,38 @@ std::shared_ptr<CampaignState> Helper::openCampaignInternal(const QString & file
|
||||
throw std::runtime_error("Corrupted campaign");
|
||||
}
|
||||
|
||||
std::map<std::string, std::shared_ptr<CRmgTemplate>> Helper::openTemplateInternal(const QString & filenameSelect)
|
||||
{
|
||||
QFileInfo fi(filenameSelect);
|
||||
std::string fname = fi.fileName().toStdString();
|
||||
std::string fdir = fi.dir().path().toStdString();
|
||||
|
||||
ResourcePath resId("MAPEDITOR/" + fname, EResType::JSON);
|
||||
|
||||
//addFilesystem takes care about memory deallocation if case of failure, no memory leak here
|
||||
auto mapEditorFilesystem = std::make_unique<CFilesystemLoader>("MAPEDITOR/", fdir, 0);
|
||||
CResourceHandler::removeFilesystem("local", "mapEditor");
|
||||
CResourceHandler::addFilesystem("local", "mapEditor", std::move(mapEditorFilesystem));
|
||||
|
||||
if(!CResourceHandler::get("mapEditor")->existsResource(resId))
|
||||
throw std::runtime_error("Cannot open template from this folder");
|
||||
|
||||
auto data = CResourceHandler::get()->load(resId)->readAll();
|
||||
JsonNode nodes(reinterpret_cast<std::byte *>(data.first.get()), data.second, resId.getName());
|
||||
|
||||
std::map<std::string, std::shared_ptr<CRmgTemplate>> templates;
|
||||
for(auto & node : nodes.Struct())
|
||||
{
|
||||
JsonDeserializer handler(nullptr, node.second);
|
||||
auto rmg = std::make_shared<CRmgTemplate>();
|
||||
rmg->serializeJson(handler);
|
||||
rmg->validate();
|
||||
templates[node.first] = rmg;
|
||||
}
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
void Helper::saveCampaign(std::shared_ptr<CampaignState> campaignState, const QString & filename)
|
||||
{
|
||||
auto jsonCampaign = CampaignHandler::writeHeaderToJson(*campaignState);
|
||||
@@ -107,3 +143,25 @@ void Helper::saveCampaign(std::shared_ptr<CampaignState> campaignState, const QS
|
||||
auto jsonCampaignStr = jsonCampaign.toString();
|
||||
saver->addFile("header.json")->write(reinterpret_cast<const ui8 *>(jsonCampaignStr.data()), jsonCampaignStr.length());
|
||||
}
|
||||
|
||||
void Helper::saveTemplate(std::map<std::string, std::shared_ptr<CRmgTemplate>> tpl, const QString & filename)
|
||||
{
|
||||
JsonMap data;
|
||||
|
||||
for(auto & node : tpl)
|
||||
{
|
||||
JsonNode actual;
|
||||
{
|
||||
JsonSerializer handler(nullptr, actual);
|
||||
node.second->serializeJson(handler);
|
||||
}
|
||||
data[node.first] = actual;
|
||||
}
|
||||
|
||||
auto byteData = JsonNode(data).toBytes();
|
||||
QByteArray byteDataArray = QByteArray(reinterpret_cast<const char*>(byteData.data()), static_cast<int>(byteData.size()));
|
||||
QFile file(filename);
|
||||
|
||||
if(file.open(QIODevice::WriteOnly))
|
||||
file.write(byteDataArray);
|
||||
}
|
||||
|
||||
@@ -12,10 +12,13 @@
|
||||
|
||||
class CMap;
|
||||
class CampaignState;
|
||||
class CRmgTemplate;
|
||||
|
||||
namespace Helper
|
||||
{
|
||||
std::unique_ptr<CMap> openMapInternal(const QString &);
|
||||
std::shared_ptr<CampaignState> openCampaignInternal(const QString &);
|
||||
std::map<std::string, std::shared_ptr<CRmgTemplate>> openTemplateInternal(const QString &);
|
||||
void saveCampaign(std::shared_ptr<CampaignState> campaignState, const QString & filename);
|
||||
void saveTemplate(std::map<std::string, std::shared_ptr<CRmgTemplate>> tpl, const QString & filename);
|
||||
}
|
||||
BIN
mapeditor/icons/dice.png
Normal file
BIN
mapeditor/icons/dice.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
1147
mapeditor/icons/templateSquare.svg
Normal file
1147
mapeditor/icons/templateSquare.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 70 KiB |
9
mapeditor/icons/zone-add.svg
Normal file
9
mapeditor/icons/zone-add.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<g transform="translate(0 -1028.4)">
|
||||
<path d="m22 12c0 5.523-4.477 10-10 10-5.5228 0-10-4.477-10-10 0-5.5228 4.4772-10 10-10 5.523 0 10 4.4772 10 10z" transform="translate(0 1029.4)" fill="#27ae60"/>
|
||||
<path d="m22 12c0 5.523-4.477 10-10 10-5.5228 0-10-4.477-10-10 0-5.5228 4.4772-10 10-10 5.523 0 10 4.4772 10 10z" transform="translate(0 1028.4)" fill="#2ecc71"/>
|
||||
<path d="m6.0001 1042.4h4.9999v5h2v-5h5v-2h-5v-5h-2v5h-4.9999v2z" fill="#27ae60"/>
|
||||
<path d="m6 1041.4h5v5h2v-5h5v-2h-5v-5h-2v5h-5v2z" fill="#ecf0f1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 810 B |
9
mapeditor/icons/zone-remove.svg
Normal file
9
mapeditor/icons/zone-remove.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<g transform="translate(0 -1028.4)">
|
||||
<path d="m22 12c0 5.523-4.477 10-10 10-5.5228 0-10-4.477-10-10 0-5.5228 4.4772-10 10-10 5.523 0 10 4.4772 10 10z" transform="translate(0 1029.4)" fill="#c0392b"/>
|
||||
<path d="m22 12c0 5.523-4.477 10-10 10-5.5228 0-10-4.477-10-10 0-5.5228 4.4772-10 10-10 5.523 0 10 4.4772 10 10z" transform="translate(0 1028.4)" fill="#e74c3c"/>
|
||||
<rect height="4" width="12" y="1039.4" x="6" fill="#c0392b"/>
|
||||
<rect height="3" width="12" y="1039.4" x="6" fill="#ecf0f1"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 783 B |
12
mapeditor/icons/zones-layout.svg
Normal file
12
mapeditor/icons/zones-layout.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<g transform="translate(0 -1028.4)">
|
||||
<path d="m1 1035.4v1 1 2 1 1 1 1 2 1 2 1c0 1.1 0.8954 2 2 2h9 9c1.105 0 2-0.9 2-2v-1-2-4-2-3-1-1h-22z" fill="#bdc3c7"/>
|
||||
<path d="m3 2c-1.1046 0-2 0.8954-2 2v3h22v-3c0-1.1046-0.895-2-2-2h-9-9z" transform="translate(0 1028.4)" fill="#bdc3c7"/>
|
||||
<path d="m1 6v1 1 2 1 1 1 1 2 1 2 1c0 1.105 0.8954 2 2 2h9 9c1.105 0 2-0.895 2-2v-1-2-4-2-3-1-1h-22z" transform="translate(0 1028.4)" fill="#ecf0f1"/>
|
||||
<path d="m4 4a1 1 0 1 1 -2 0 1 1 0 1 1 2 0z" transform="translate(0 1028.4)" fill="#c0392b"/>
|
||||
<path d="m4 4a1 1 0 1 1 -2 0 1 1 0 1 1 2 0z" transform="translate(3 1028.4)" fill="#27ae60"/>
|
||||
<path d="m4 4a1 1 0 1 1 -2 0 1 1 0 1 1 2 0z" transform="translate(6 1028.4)" fill="#f39c12"/>
|
||||
<path d="m3 1036.4v1 10 1h1 2 2v-12h-2-2zm6 0v1 2h12v-2-1h-1-10zm0 4v8h1 10 1v-1-6-1h-1-10z" fill="#bdc3c7"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -50,6 +50,9 @@
|
||||
#include "validator.h"
|
||||
#include "helper.h"
|
||||
#include "campaigneditor/campaigneditor.h"
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
#include "templateeditor/templateeditor.h"
|
||||
#endif
|
||||
|
||||
QJsonValue jsonFromPixmap(const QPixmap &p)
|
||||
{
|
||||
@@ -261,6 +264,11 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
ui->actionZoom_out->setIcon(QIcon{":/icons/zoom_minus.png"});
|
||||
ui->actionZoom_reset->setIcon(QIcon{":/icons/zoom_zero.png"});
|
||||
ui->actionCampaignEditor->setIcon(QIcon{":/icons/mapeditor.64x64.png"});
|
||||
ui->actionTemplateEditor->setIcon(QIcon{":/icons/dice.png"});
|
||||
|
||||
#ifndef ENABLE_TEMPLATE_EDITOR
|
||||
ui->actionTemplateEditor->setVisible(false);
|
||||
#endif
|
||||
|
||||
loadUserSettings(); //For example window size
|
||||
setTitle();
|
||||
@@ -606,6 +614,17 @@ void MainWindow::on_actionCampaignEditor_triggered()
|
||||
CampaignEditor::showCampaignEditor();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionTemplateEditor_triggered()
|
||||
{
|
||||
#ifdef ENABLE_TEMPLATE_EDITOR
|
||||
if(!getAnswerAboutUnsavedChanges())
|
||||
return;
|
||||
|
||||
hide();
|
||||
TemplateEditor::showTemplateEditor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::on_actionNew_triggered()
|
||||
{
|
||||
if(getAnswerAboutUnsavedChanges())
|
||||
|
||||
@@ -80,6 +80,8 @@ private slots:
|
||||
|
||||
void on_actionCampaignEditor_triggered();
|
||||
|
||||
void on_actionTemplateEditor_triggered();
|
||||
|
||||
void on_actionNew_triggered();
|
||||
|
||||
void on_actionLevel_triggered();
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<addaction name="actionExport"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCampaignEditor"/>
|
||||
<addaction name="actionTemplateEditor"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionh3m_converter"/>
|
||||
<addaction name="actionh3c_converter"/>
|
||||
@@ -148,6 +149,7 @@
|
||||
<addaction name="actionSave"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionCampaignEditor"/>
|
||||
<addaction name="actionTemplateEditor"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionUndo"/>
|
||||
<addaction name="actionRedo"/>
|
||||
@@ -1078,6 +1080,14 @@
|
||||
<string>Campaign editor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionTemplateEditor">
|
||||
<property name="text">
|
||||
<string>Template editor</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Template editor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLevel">
|
||||
<property name="text">
|
||||
<string>View underground</string>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<file>icons/document-open.png</file>
|
||||
<file>icons/document-open-recent.png</file>
|
||||
<file>icons/document-save.png</file>
|
||||
<file>icons/dice.png</file>
|
||||
<file>icons/edit-clear.png</file>
|
||||
<file>icons/edit-copy.png</file>
|
||||
<file>icons/edit-cut.png</file>
|
||||
@@ -42,5 +43,10 @@
|
||||
<file>icons/zoom_minus.png</file>
|
||||
<file>icons/zoom_plus.png</file>
|
||||
<file>icons/zoom_zero.png</file>
|
||||
|
||||
<file>icons/templateSquare.svg</file>
|
||||
<file>icons/zone-add.svg</file>
|
||||
<file>icons/zone-remove.svg</file>
|
||||
<file>icons/zones-layout.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
133
mapeditor/templateeditor/algorithm.cpp
Normal file
133
mapeditor/templateeditor/algorithm.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* algorithm.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "algorithm.h"
|
||||
|
||||
double Algorithm::distance(double x1, double y1, double x2, double y2)
|
||||
{
|
||||
return std::sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) + 1e-9;
|
||||
}
|
||||
|
||||
bool Algorithm::edgesIntersect(const Node& a, const Node& b, const Node& c, const Node& d)
|
||||
{
|
||||
auto cross = [](double x1, double y1, double x2, double y2) {
|
||||
return x1 * y2 - y1 * x2;
|
||||
};
|
||||
|
||||
double dx1 = b.x - a.x, dy1 = b.y - a.y;
|
||||
double dx2 = d.x - c.x, dy2 = d.y - c.y;
|
||||
|
||||
double delta = cross(dx1, dy1, dx2, dy2);
|
||||
if (std::abs(delta) < 1e-10) return false; // Parallel
|
||||
|
||||
// Compute intersection
|
||||
double s = cross(c.x - a.x, c.y - a.y, dx2, dy2) / delta;
|
||||
double t = cross(c.x - a.x, c.y - a.y, dx1, dy1) / delta;
|
||||
|
||||
return s > 0 && s < 1 && t > 0 && t < 1;
|
||||
}
|
||||
|
||||
void Algorithm::forceDirectedLayout(std::vector<Node> & nodes, const std::vector<Edge> & edges, int iterations, double width, double height)
|
||||
{
|
||||
const double area = width * height;
|
||||
const double k = std::sqrt(area / nodes.size());
|
||||
|
||||
for (int it = 0; it < iterations; ++it)
|
||||
{
|
||||
// Reset forces
|
||||
for (auto& node : nodes)
|
||||
node.dx = node.dy = 0;
|
||||
|
||||
// Repulsive forces
|
||||
for (size_t i = 0; i < nodes.size(); ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < nodes.size(); ++j)
|
||||
{
|
||||
double dx = nodes[i].x - nodes[j].x;
|
||||
double dy = nodes[i].y - nodes[j].y;
|
||||
double dist = distance(nodes[i].x, nodes[i].y, nodes[j].x, nodes[j].y);
|
||||
double force = (k * k) / dist;
|
||||
|
||||
nodes[i].dx += (dx / dist) * force;
|
||||
nodes[i].dy += (dy / dist) * force;
|
||||
nodes[j].dx -= (dx / dist) * force;
|
||||
nodes[j].dy -= (dy / dist) * force;
|
||||
}
|
||||
}
|
||||
|
||||
// Attractive forces
|
||||
for (const auto& edge : edges)
|
||||
{
|
||||
Node& u = nodes[edge.from];
|
||||
Node& v = nodes[edge.to];
|
||||
double dx = u.x - v.x;
|
||||
double dy = u.y - v.y;
|
||||
double dist = distance(u.x, u.y, v.x, v.y);
|
||||
double force = (dist * dist) / k;
|
||||
|
||||
double fx = (dx / dist) * force;
|
||||
double fy = (dy / dist) * force;
|
||||
|
||||
u.dx -= fx;
|
||||
u.dy -= fy;
|
||||
v.dx += fx;
|
||||
v.dy += fy;
|
||||
}
|
||||
|
||||
// Edge crossing penalty
|
||||
for (size_t i = 0; i < edges.size(); ++i) {
|
||||
for (size_t j = i + 1; j < edges.size(); ++j) {
|
||||
const Edge& e1 = edges[i];
|
||||
const Edge& e2 = edges[j];
|
||||
|
||||
if (e1.from == e2.from || e1.from == e2.to ||
|
||||
e1.to == e2.from || e1.to == e2.to)
|
||||
continue; // Skip if they share nodes
|
||||
|
||||
Node& a = nodes[e1.from];
|
||||
Node& b = nodes[e1.to];
|
||||
Node& c = nodes[e2.from];
|
||||
Node& d = nodes[e2.to];
|
||||
|
||||
if (edgesIntersect(a, b, c, d)) {
|
||||
double strength = 0.05;
|
||||
|
||||
a.dx += strength * (a.x - c.x);
|
||||
a.dy += strength * (a.y - c.y);
|
||||
b.dx += strength * (b.x - d.x);
|
||||
b.dy += strength * (b.y - d.y);
|
||||
c.dx += strength * (c.x - a.x);
|
||||
c.dy += strength * (c.y - a.y);
|
||||
d.dx += strength * (d.x - b.x);
|
||||
d.dy += strength * (d.y - b.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply displacement
|
||||
for (auto& node : nodes)
|
||||
{
|
||||
node.x += std::max(-5.0, std::min(5.0, node.dx));
|
||||
node.y += std::max(-5.0, std::min(5.0, node.dy));
|
||||
|
||||
// Keep within bounds
|
||||
node.x = std::min(width, std::max(0.0, node.x));
|
||||
node.y = std::min(height, std::max(0.0, node.y));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& node : nodes)
|
||||
{
|
||||
// Center around 0
|
||||
node.x -= width / 2;
|
||||
node.y -= height / 2;
|
||||
}
|
||||
}
|
||||
32
mapeditor/templateeditor/algorithm.h
Normal file
32
mapeditor/templateeditor/algorithm.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* algorithm.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../StdInc.h"
|
||||
|
||||
class Algorithm
|
||||
{
|
||||
public:
|
||||
struct Node
|
||||
{
|
||||
double x, y;
|
||||
double dx = 0, dy = 0;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct Edge
|
||||
{
|
||||
int from, to;
|
||||
};
|
||||
|
||||
static double distance(double x1, double y1, double x2, double y2);
|
||||
static bool edgesIntersect(const Node& a, const Node& b, const Node& c, const Node& d);
|
||||
static void forceDirectedLayout(std::vector<Node>& nodes, const std::vector<Edge>& edges, int iterations, double width, double height);
|
||||
};
|
||||
292
mapeditor/templateeditor/graphicelements.cpp
Normal file
292
mapeditor/templateeditor/graphicelements.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* graphicelements.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "graphicelements.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
#include "../../lib/rmg/CRmgTemplate.h"
|
||||
|
||||
QDomElement CardItem::getElementById(const QDomDocument& doc, const QString& id)
|
||||
{
|
||||
QDomElement root = doc.documentElement();
|
||||
|
||||
std::function<QDomElement(const QDomElement&)> findById = [&](const QDomElement& elem) -> QDomElement {
|
||||
if (elem.attribute("id") == id)
|
||||
return elem;
|
||||
|
||||
QDomElement child = elem.firstChildElement();
|
||||
while (!child.isNull())
|
||||
{
|
||||
QDomElement found = findById(child);
|
||||
if (!found.isNull())
|
||||
return found;
|
||||
child = child.nextSiblingElement();
|
||||
}
|
||||
return QDomElement();
|
||||
};
|
||||
|
||||
return findById(root);
|
||||
}
|
||||
|
||||
bool isBlackTextNeeded(const QColor& bg)
|
||||
{
|
||||
int r = bg.red();
|
||||
int g = bg.green();
|
||||
int b = bg.blue();
|
||||
|
||||
double luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
||||
return luminance > 0.5;
|
||||
}
|
||||
|
||||
CardItem::CardItem():
|
||||
selectCallback(nullptr),
|
||||
posChangeCallback(nullptr),
|
||||
useBlackText(false),
|
||||
mousePressed(false)
|
||||
{
|
||||
QFile file(":/icons/templateSquare.svg");
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QByteArray data = file.readAll();
|
||||
doc.setContent(data);
|
||||
|
||||
updateContent();
|
||||
}
|
||||
|
||||
void CardItem::setSelectCallback(std::function<void(bool)> func)
|
||||
{
|
||||
selectCallback = func;
|
||||
}
|
||||
|
||||
void CardItem::setPosChangeCallback(std::function<void(QPointF)> func)
|
||||
{
|
||||
posChangeCallback = func;
|
||||
}
|
||||
|
||||
void CardItem::updateContent()
|
||||
{
|
||||
setSharedRenderer(new QSvgRenderer(doc.toByteArray()));
|
||||
}
|
||||
|
||||
void CardItem::setFillColor(QColor color)
|
||||
{
|
||||
auto squareElem = getElementById(doc, "rect");
|
||||
squareElem.setAttribute("style", squareElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:" + color.name() + ";"));
|
||||
|
||||
useBlackText = isBlackTextNeeded(color);
|
||||
}
|
||||
|
||||
void CardItem::setMultiFillColor(QColor color1, QColor color2)
|
||||
{
|
||||
auto squareElem = getElementById(doc, "rect");
|
||||
squareElem.setAttribute("style", squareElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:url(#gradientExtra);"));
|
||||
auto gradientStopElem1 = getElementById(doc, "gradientExtraColorStop1");
|
||||
auto gradientStopElem2 = getElementById(doc, "gradientExtraColorStop2");
|
||||
auto gradientStopElem3 = getElementById(doc, "gradientExtraColorStop3");
|
||||
auto gradientStopElem4 = getElementById(doc, "gradientExtraColorStop4");
|
||||
auto gradientStopElem5 = getElementById(doc, "gradientExtraColorStop5");
|
||||
gradientStopElem1.setAttribute("style", gradientStopElem1.attribute("style").replace(QRegularExpression("stop-color:.*?;"), "stop-color:" + color1.name() + ";"));
|
||||
gradientStopElem2.setAttribute("style", gradientStopElem2.attribute("style").replace(QRegularExpression("stop-color:.*?;"), "stop-color:" + color2.name() + ";"));
|
||||
gradientStopElem3.setAttribute("style", gradientStopElem3.attribute("style").replace(QRegularExpression("stop-color:.*?;"), "stop-color:" + color1.name() + ";"));
|
||||
gradientStopElem4.setAttribute("style", gradientStopElem4.attribute("style").replace(QRegularExpression("stop-color:.*?;"), "stop-color:" + color2.name() + ";"));
|
||||
gradientStopElem5.setAttribute("style", gradientStopElem5.attribute("style").replace(QRegularExpression("stop-color:.*?;"), "stop-color:" + color1.name() + ";"));
|
||||
|
||||
useBlackText = isBlackTextNeeded(color1);
|
||||
}
|
||||
|
||||
void CardItem::setPlayerColor(PlayerColor color)
|
||||
{
|
||||
std::map<PlayerColor, std::pair<QString, QString>> colors =
|
||||
{
|
||||
{ PlayerColor(0), { "#F80000", "#920000" } }, //red
|
||||
{ PlayerColor(1), { "#0000F8", "#000092" } }, //blue
|
||||
{ PlayerColor(2), { "#9B7251", "#35271C" } }, //tan
|
||||
{ PlayerColor(3), { "#00FC00", "#009600" } }, //green
|
||||
{ PlayerColor(4), { "#F88000", "#924B00" } }, //orange
|
||||
{ PlayerColor(5), { "#F800F8", "#920092" } }, //purple
|
||||
{ PlayerColor(6), { "#00FCF8", "#009694" } }, //teal
|
||||
{ PlayerColor(7), { "#C07888", "#5A3840" } }, //pink
|
||||
};
|
||||
auto squareElem = getElementById(doc, "rect");
|
||||
squareElem.setAttribute("style", squareElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:url(#gradientPlayer);"));
|
||||
auto gradientStopElem1 = getElementById(doc, "gradientPlayerColorStop1");
|
||||
auto gradientStopElem2 = getElementById(doc, "gradientPlayerColorStop2");
|
||||
gradientStopElem1.setAttribute("style", gradientStopElem1.attribute("style").replace(QRegularExpression("stop-color:.*?;"), "stop-color:" + colors[color].first + ";"));
|
||||
gradientStopElem2.setAttribute("style", gradientStopElem2.attribute("style").replace(QRegularExpression("stop-color:.*?;"), "stop-color:" + colors[color].second + ";"));
|
||||
|
||||
useBlackText = isBlackTextNeeded(QColor(colors[color].first));
|
||||
}
|
||||
|
||||
void CardItem::setJunction(bool val)
|
||||
{
|
||||
auto squareElem = getElementById(doc, "rectJunction");
|
||||
squareElem.setAttribute("style", squareElem.attribute("style").replace(QRegularExpression("stroke-opacity:.*?;"), "stroke-opacity:" + QString::fromStdString(val ? "0.3" : "0.0") + ";"));
|
||||
}
|
||||
|
||||
void CardItem::setId(int val)
|
||||
{
|
||||
auto textIdElem = getElementById(doc, "textId");
|
||||
textIdElem.setAttribute("style", textIdElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:" + QColor(useBlackText ? Qt::black : Qt::white).name() + ";"));
|
||||
textIdElem.firstChild().setNodeValue(QString::number(val));
|
||||
|
||||
id = val;
|
||||
}
|
||||
|
||||
int CardItem::getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
void CardItem::setResAmount(GameResID res, int val)
|
||||
{
|
||||
std::map<GameResID, QString> names =
|
||||
{
|
||||
{ GameResID::WOOD, "Wood" },
|
||||
{ GameResID::ORE, "Ore" },
|
||||
{ GameResID::MERCURY, "Mercury" },
|
||||
{ GameResID::SULFUR, "Sulfur" },
|
||||
{ GameResID::CRYSTAL, "Crystal" },
|
||||
{ GameResID::GEMS, "Gems" },
|
||||
{ GameResID::GOLD, "Gold" },
|
||||
};
|
||||
auto textElem = getElementById(doc, "text" + names[res]);
|
||||
textElem.setAttribute("style", textElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:" + QColor(useBlackText ? Qt::black : Qt::white).name() + ";"));
|
||||
textElem.firstChild().setNodeValue(val ? QString::number(val) : "");
|
||||
|
||||
auto iconElem = getElementById(doc, "icon" + names[res]);
|
||||
iconElem.setAttribute("opacity", val ? "1.0" : "0.1");
|
||||
}
|
||||
|
||||
void CardItem::setChestValue(int val)
|
||||
{
|
||||
auto textElem = getElementById(doc, "textChest");
|
||||
textElem.setAttribute("style", textElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:" + QColor(useBlackText ? Qt::black : Qt::white).name() + ";"));
|
||||
textElem.firstChild().setNodeValue(val ? QString::number(val) : "");
|
||||
|
||||
auto iconElem = getElementById(doc, "iconChest");
|
||||
iconElem.setAttribute("opacity", val ? "1.0" : "0.1");
|
||||
}
|
||||
|
||||
void CardItem::setSword(EMonsterStrength::EMonsterStrength val)
|
||||
{
|
||||
int level = 0;
|
||||
if(val == EMonsterStrength::ZONE_WEAK || val == EMonsterStrength::GLOBAL_WEAK)
|
||||
level = 1;
|
||||
else if(val == EMonsterStrength::ZONE_NORMAL || val == EMonsterStrength::GLOBAL_NORMAL)
|
||||
level = 2;
|
||||
else if(val == EMonsterStrength::ZONE_STRONG || val == EMonsterStrength::GLOBAL_STRONG)
|
||||
level = 3;
|
||||
|
||||
getElementById(doc, "iconSword1").setAttribute("opacity", level > 0 ? "1.0" : "0.1");
|
||||
getElementById(doc, "iconSword2").setAttribute("opacity", level > 1 ? "1.0" : "0.1");
|
||||
getElementById(doc, "iconSword3").setAttribute("opacity", level > 2 ? "1.0" : "0.1");
|
||||
}
|
||||
|
||||
void CardItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if(event->button() == Qt::LeftButton)
|
||||
{
|
||||
// set element in grid
|
||||
double xx = x() + (boundingRect().width() / 2);
|
||||
double yy = y() + (boundingRect().height() / 2);
|
||||
xx = GRID_SIZE * round(xx / GRID_SIZE);
|
||||
yy = GRID_SIZE * round(yy / GRID_SIZE);
|
||||
setPos(xx - (boundingRect().width() / 2), yy - (boundingRect().height() / 2));
|
||||
}
|
||||
|
||||
QGraphicsSvgItem::mouseReleaseEvent(event);
|
||||
|
||||
if(posChangeCallback)
|
||||
posChangeCallback(pos());
|
||||
|
||||
mousePressed = false;
|
||||
}
|
||||
|
||||
void CardItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
QGraphicsSvgItem::mousePressEvent(event);
|
||||
|
||||
mousePressed = true;
|
||||
}
|
||||
|
||||
QVariant CardItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
if(change == ItemSelectedHasChanged && selectCallback)
|
||||
selectCallback(isSelected());
|
||||
else if(change == ItemPositionHasChanged && posChangeCallback && mousePressed)
|
||||
posChangeCallback(pos());
|
||||
|
||||
return QGraphicsSvgItem::itemChange(change, value);
|
||||
}
|
||||
|
||||
LineItem::LineItem():
|
||||
clickCallback(nullptr)
|
||||
{
|
||||
setZValue(-2);
|
||||
for(int i = 0; i < 10; i++) // render multiple times to increase outline effect
|
||||
{
|
||||
auto tmpTextItem = new QGraphicsTextItem(this);
|
||||
tmpTextItem->setZValue(-1);
|
||||
QFont font;
|
||||
font.setPointSize(18);
|
||||
tmpTextItem->setFont(font);
|
||||
QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect();
|
||||
shadowEffect->setBlurRadius(10);
|
||||
shadowEffect->setEnabled(true);
|
||||
shadowEffect->setOffset(0, 0);
|
||||
shadowEffect->setColor(Qt::black);
|
||||
tmpTextItem->setGraphicsEffect(shadowEffect);
|
||||
tmpTextItem->setDefaultTextColor(Qt::white);
|
||||
textItem.push_back(tmpTextItem);
|
||||
}
|
||||
}
|
||||
|
||||
void LineItem::setLineToolTip(const QString &toolTip)
|
||||
{
|
||||
for(auto & tmpTextItem : textItem)
|
||||
tmpTextItem->setToolTip(toolTip);
|
||||
setToolTip(toolTip);
|
||||
}
|
||||
|
||||
void LineItem::setClickCallback(std::function<void()> func)
|
||||
{
|
||||
clickCallback = func;
|
||||
}
|
||||
|
||||
void LineItem::setText(QString text)
|
||||
{
|
||||
for(auto & tmpTextItem : textItem)
|
||||
{
|
||||
tmpTextItem->setPlainText(text);
|
||||
QRectF lineRect = boundingRect();
|
||||
QRectF textRect = tmpTextItem->boundingRect();
|
||||
tmpTextItem->setPos(QPointF(lineRect.x() + (lineRect.width() / 2) - (textRect.width() / 2), lineRect.y() + (lineRect.height() / 2) - (textRect.height() / 2)));
|
||||
}
|
||||
}
|
||||
|
||||
void LineItem::setId(int val)
|
||||
{
|
||||
id = val;
|
||||
}
|
||||
|
||||
int LineItem::getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
void LineItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if(event->button() == Qt::LeftButton && clickCallback)
|
||||
clickCallback();
|
||||
|
||||
QGraphicsLineItem::mousePressEvent(event);
|
||||
}
|
||||
73
mapeditor/templateeditor/graphicelements.h
Normal file
73
mapeditor/templateeditor/graphicelements.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* graphicelements.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QGraphicsSvgItem>
|
||||
|
||||
#include "../StdInc.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
#include "../../lib/rmg/CRmgTemplate.h"
|
||||
|
||||
class CardItem : public QGraphicsSvgItem
|
||||
{
|
||||
private:
|
||||
QDomElement getElementById(const QDomDocument& doc, const QString& id);
|
||||
|
||||
QDomDocument doc;
|
||||
bool useBlackText;
|
||||
int id = -1;
|
||||
bool mousePressed;
|
||||
|
||||
std::function<void(bool)> selectCallback;
|
||||
std::function<void(QPointF)> posChangeCallback;
|
||||
public:
|
||||
CardItem();
|
||||
|
||||
static constexpr double GRID_SIZE = 10.;
|
||||
|
||||
void setSelectCallback(std::function<void(bool)> func);
|
||||
void setPosChangeCallback(std::function<void(QPointF)> func);
|
||||
|
||||
void updateContent();
|
||||
void setFillColor(QColor color);
|
||||
void setMultiFillColor(QColor color1, QColor color2);
|
||||
void setPlayerColor(PlayerColor color);
|
||||
void setJunction(bool val);
|
||||
void setId(int val);
|
||||
int getId();
|
||||
void setResAmount(GameResID res, int val);
|
||||
void setChestValue(int val);
|
||||
void setSword(EMonsterStrength::EMonsterStrength val);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
|
||||
};
|
||||
|
||||
class LineItem : public QGraphicsLineItem
|
||||
{
|
||||
private:
|
||||
std::vector<QGraphicsTextItem *> textItem;
|
||||
std::function<void()> clickCallback;
|
||||
int id = -1;
|
||||
|
||||
static constexpr int CLICKABLE_PADDING_AROUND_LINE = 10;
|
||||
public:
|
||||
LineItem();
|
||||
void setClickCallback(std::function<void()> func);
|
||||
void setText(QString text);
|
||||
void setId(int val);
|
||||
int getId();
|
||||
void setLineToolTip(const QString &toolTip);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
};
|
||||
1070
mapeditor/templateeditor/templateeditor.cpp
Normal file
1070
mapeditor/templateeditor/templateeditor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
131
mapeditor/templateeditor/templateeditor.h
Normal file
131
mapeditor/templateeditor/templateeditor.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* templateeditor.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
|
||||
#include "templateview.h"
|
||||
|
||||
#include "../StdInc.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
|
||||
class CRmgTemplate;
|
||||
class CardItem;
|
||||
class LineItem;
|
||||
class ZoneOptions;
|
||||
namespace rmg {
|
||||
class ZoneOptions;
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class TemplateEditor;
|
||||
}
|
||||
|
||||
class TemplateEditor : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TemplateEditor();
|
||||
~TemplateEditor();
|
||||
|
||||
static void showTemplateEditor();
|
||||
|
||||
private slots:
|
||||
void on_actionOpen_triggered();
|
||||
void on_actionSave_as_triggered();
|
||||
void on_actionNew_triggered();
|
||||
void on_actionSave_triggered();
|
||||
void on_actionAutoPosition_triggered();
|
||||
void on_actionZoom_in_triggered();
|
||||
void on_actionZoom_out_triggered();
|
||||
void on_actionZoom_auto_triggered();
|
||||
void on_actionZoom_reset_triggered();
|
||||
void on_actionAddZone_triggered();
|
||||
void on_actionRemoveZone_triggered();
|
||||
void on_comboBoxTemplateSelection_activated(int index);
|
||||
void on_pushButtonAddSubTemplate_clicked();
|
||||
void on_pushButtonRemoveSubTemplate_clicked();
|
||||
void on_pushButtonRenameSubTemplate_clicked();
|
||||
void on_spinBoxZoneVisPosX_valueChanged();
|
||||
void on_spinBoxZoneVisPosY_valueChanged();
|
||||
void on_doubleSpinBoxZoneVisSize_valueChanged();
|
||||
void on_comboBoxZoneType_currentTextChanged(const QString &text);
|
||||
void on_comboBoxZoneOwner_currentTextChanged(const QString &text);
|
||||
void on_spinBoxZoneSize_valueChanged();
|
||||
void on_spinBoxTownCountPlayer_valueChanged();
|
||||
void on_spinBoxCastleCountPlayer_valueChanged();
|
||||
void on_spinBoxTownDensityPlayer_valueChanged();
|
||||
void on_spinBoxCastleDensityPlayer_valueChanged();
|
||||
void on_spinBoxTownCountNeutral_valueChanged();
|
||||
void on_spinBoxCastleCountNeutral_valueChanged();
|
||||
void on_spinBoxTownDensityNeutral_valueChanged();
|
||||
void on_spinBoxCastleDensityNeutral_valueChanged();
|
||||
void on_checkBoxMatchTerrainToTown_stateChanged(int state);
|
||||
void on_checkBoxTownsAreSameType_stateChanged(int state);
|
||||
void on_comboBoxMonsterStrength_currentTextChanged(const QString &text);
|
||||
void on_spinBoxZoneId_valueChanged();
|
||||
void on_spinBoxZoneLinkTowns_valueChanged();
|
||||
void on_spinBoxZoneLinkMines_valueChanged();
|
||||
void on_spinBoxZoneLinkTerrain_valueChanged();
|
||||
void on_spinBoxZoneLinkTreasure_valueChanged();
|
||||
void on_spinBoxZoneLinkCustomObjects_valueChanged();
|
||||
void on_checkBoxZoneLinkTowns_stateChanged(int state);
|
||||
void on_checkBoxZoneLinkMines_stateChanged(int state);
|
||||
void on_checkBoxZoneLinkTerrain_stateChanged(int state);
|
||||
void on_checkBoxZoneLinkTreasure_stateChanged(int state);
|
||||
void on_checkBoxZoneLinkCustomObjects_stateChanged(int state);
|
||||
void on_checkBoxAllowedWaterContentNone_stateChanged(int state);
|
||||
void on_checkBoxAllowedWaterContentNormal_stateChanged(int state);
|
||||
void on_checkBoxAllowedWaterContentIslands_stateChanged(int state);
|
||||
void on_pushButtonConnectionAdd_clicked();
|
||||
void on_pushButtonOpenTerrainTypes_clicked();
|
||||
void on_pushButtonOpenBannedTerrainTypes_clicked();
|
||||
void on_pushButtonAllowedTowns_clicked();
|
||||
void on_pushButtonBannedTowns_clicked();
|
||||
void on_pushButtonTownHints_clicked();
|
||||
void on_pushButtonAllowedMonsters_clicked();
|
||||
void on_pushButtonBannedMonsters_clicked();
|
||||
void on_pushButtonTreasure_clicked();
|
||||
void on_pushButtonMines_clicked();
|
||||
void on_pushButtonCustomObjects_clicked();
|
||||
|
||||
private:
|
||||
bool getAnswerAboutUnsavedChanges();
|
||||
void setTitle();
|
||||
void changed();
|
||||
void saveTemplate();
|
||||
void initContent();
|
||||
void loadContent(bool autoPosition = false);
|
||||
void saveContent();
|
||||
void loadZoneMenuContent(bool onlyPosition = false);
|
||||
void saveZoneMenuContent();
|
||||
void loadZoneConnectionMenuContent();
|
||||
void updateConnectionLines(bool recreate = false);
|
||||
void autoPositionZones();
|
||||
void updateZonePositions();
|
||||
QString getZoneToolTip(std::shared_ptr<rmg::ZoneOptions> zone);
|
||||
void updateZoneCards(TRmgTemplateZoneId id = -1);
|
||||
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
Ui::TemplateEditor *ui;
|
||||
|
||||
std::unique_ptr<TemplateScene> templateScene;
|
||||
|
||||
QString filename;
|
||||
bool unsaved = false;
|
||||
std::map<std::string, std::shared_ptr<CRmgTemplate>> templates;
|
||||
std::string selectedTemplate;
|
||||
int selectedZone;
|
||||
|
||||
std::map<TRmgTemplateZoneId, CardItem *> cards;
|
||||
std::vector<LineItem *> lines;
|
||||
};
|
||||
1393
mapeditor/templateeditor/templateeditor.ui
Normal file
1393
mapeditor/templateeditor/templateeditor.ui
Normal file
File diff suppressed because it is too large
Load Diff
60
mapeditor/templateeditor/templateview.cpp
Normal file
60
mapeditor/templateeditor/templateview.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* templateview.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "templateview.h"
|
||||
|
||||
TemplateScene::TemplateScene():
|
||||
QGraphicsScene(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TemplateView::TemplateView(QWidget * parent):
|
||||
QGraphicsView(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void TemplateView::setZoomLevel(int level)
|
||||
{
|
||||
zoomlevel = level;
|
||||
|
||||
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
||||
float scale = pow(1.1, zoomlevel);
|
||||
QTransform matrix;
|
||||
matrix.scale(scale, scale);
|
||||
setTransform(matrix);
|
||||
}
|
||||
|
||||
void TemplateView::changeZoomLevel(bool increase)
|
||||
{
|
||||
if(increase)
|
||||
zoomlevel++;
|
||||
else
|
||||
zoomlevel--;
|
||||
|
||||
setZoomLevel(zoomlevel);
|
||||
}
|
||||
|
||||
void TemplateView::autoFit()
|
||||
{
|
||||
fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
|
||||
|
||||
zoomlevel = log(transform().m11()) / log(1.1f);
|
||||
}
|
||||
|
||||
void TemplateView::wheelEvent(QWheelEvent *e)
|
||||
{
|
||||
if (e->angleDelta().y() > 0)
|
||||
changeZoomLevel(true);
|
||||
else
|
||||
changeZoomLevel(false);
|
||||
|
||||
e->accept();
|
||||
}
|
||||
37
mapeditor/templateeditor/templateview.h
Normal file
37
mapeditor/templateeditor/templateview.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* templateview.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <QGraphicsPixmapItem>
|
||||
|
||||
class TemplateScene : public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT;
|
||||
public:
|
||||
TemplateScene();
|
||||
};
|
||||
|
||||
class TemplateView : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
int zoomlevel = 0;
|
||||
|
||||
public:
|
||||
TemplateView(QWidget * parent);
|
||||
|
||||
void setZoomLevel(int level);
|
||||
void changeZoomLevel(bool increase);
|
||||
void autoFit();
|
||||
|
||||
void wheelEvent(QWheelEvent * e) override;
|
||||
};
|
||||
Reference in New Issue
Block a user