Squashing editor
3
.github/workflows/github.yml
vendored
@@ -5,6 +5,7 @@ on:
|
||||
branches:
|
||||
- features/*
|
||||
- develop
|
||||
- cpp-map-editor
|
||||
pull_request:
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
@@ -46,7 +47,7 @@ jobs:
|
||||
pack: 1
|
||||
cpack_args: -D CPACK_NSIS_EXECUTABLE=`which makensis`
|
||||
extension: exe
|
||||
cmake_args: -G Ninja
|
||||
cmake_args: -G Ninja -DENABLE_EDITOR=0
|
||||
- platform: msvc
|
||||
os: windows-latest
|
||||
test: 0
|
||||
|
@@ -41,7 +41,7 @@ addons:
|
||||
notification_email: coverity@arseniyshestakov.com
|
||||
build_command_prepend: cov-configure --compiler clang-3.6 --comptype clangcc &&
|
||||
cov-configure --comptype clangcxx --compiler clang++-3.6 && cmake -G Ninja ..
|
||||
-DCMAKE_BUILD_TYPE=DEBUG -DENABLE_LAUNCHER=0
|
||||
-DCMAKE_BUILD_TYPE=DEBUG -DENABLE_LAUNCHER=0 -DENABLE_EDITOR=0
|
||||
build_command: ninja -j 3
|
||||
branch_pattern: coverity_scan
|
||||
|
||||
|
@@ -44,6 +44,7 @@ set(VCMI_VERSION_PATCH 0)
|
||||
option(ENABLE_ERM "Enable compilation of ERM scripting module" ON)
|
||||
option(ENABLE_LUA "Enable compilation of LUA scripting module" ON)
|
||||
option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
|
||||
option(ENABLE_EDITOR "Enable compilation of map editor" ON)
|
||||
option(ENABLE_TEST "Enable compilation of unit tests" ON)
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0")
|
||||
option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
|
||||
@@ -247,7 +248,7 @@ if(TARGET SDL2_ttf::SDL2_ttf)
|
||||
endif()
|
||||
find_package(TBB REQUIRED)
|
||||
|
||||
if(ENABLE_LAUNCHER)
|
||||
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
|
||||
# Widgets finds its own dependencies (QtGui and QtCore).
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
|
||||
@@ -356,6 +357,9 @@ add_subdirectory_with_folder("AI" AI)
|
||||
if(ENABLE_LAUNCHER)
|
||||
add_subdirectory(launcher)
|
||||
endif()
|
||||
if(ENABLE_EDITOR)
|
||||
add_subdirectory(mapeditor)
|
||||
endif()
|
||||
if(ENABLE_TEST)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
@@ -390,7 +394,7 @@ if(WIN32)
|
||||
set(debug_postfix d)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LAUNCHER)
|
||||
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
|
||||
get_target_property(QtCore_location Qt${QT_VERSION_MAJOR}::Core LOCATION)
|
||||
get_filename_component(Qtbin_folder ${QtCore_location} PATH)
|
||||
file(GLOB dep_files
|
||||
|
@@ -388,7 +388,7 @@
|
||||
},
|
||||
"updateConfigUrl" : {
|
||||
"type" : "string",
|
||||
"default" : "https://raw.githubusercontent.com/vcmi/vcmi-updates/master/vcmi-updates.json"
|
||||
"default" : "https://raw.githubusercontent.com/Nordsoft91/vcmi-autoupdate/main/autoUpdate.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,59 +12,11 @@
|
||||
|
||||
#include "../../lib/JsonNode.h"
|
||||
#include "../../lib/filesystem/CFileInputStream.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
|
||||
const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch
|
||||
|
||||
bool isCompatible(const QString & verMin, const QString & verMax)
|
||||
{
|
||||
QList<int> vcmiVersionList = {GameConstants::VCMI_VERSION_MAJOR,
|
||||
GameConstants::VCMI_VERSION_MINOR,
|
||||
GameConstants::VCMI_VERSION_PATCH};
|
||||
|
||||
if(!verMin.isEmpty())
|
||||
{
|
||||
QStringList verMinList = verMin.split(".");
|
||||
assert(verMinList.size() == maxSections);
|
||||
bool compatibleMin = true;
|
||||
for(int i = 0; i < maxSections; i++)
|
||||
{
|
||||
if(verMinList[i].toInt() < vcmiVersionList[i])
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(verMinList[i].toInt() > vcmiVersionList[i])
|
||||
{
|
||||
compatibleMin = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!compatibleMin)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!verMax.isEmpty())
|
||||
{
|
||||
QStringList verMaxList = verMax.split(".");
|
||||
assert(verMaxList.size() == maxSections);
|
||||
for(int i = 0; i < maxSections; i++)
|
||||
{
|
||||
if(verMaxList[i].toInt() > vcmiVersionList[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(verMaxList[i].toInt() < vcmiVersionList[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModEntry::compareVersions(QString lesser, QString greater)
|
||||
{
|
||||
static const int maxSections = 3; // versions consist from up to 3 sections, major.minor.patch
|
||||
|
||||
QStringList lesserList = lesser.split(".");
|
||||
QStringList greaterList = greater.split(".");
|
||||
|
||||
@@ -140,15 +92,6 @@ bool CModEntry::isUpdateable() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CModEntry::isCompatible() const
|
||||
{
|
||||
if(!isInstalled())
|
||||
return false;
|
||||
|
||||
auto compatibility = localData["compatibility"].toMap();
|
||||
return ::isCompatible(compatibility["min"].toString(), compatibility["max"].toString());
|
||||
}
|
||||
|
||||
bool CModEntry::isEssential() const
|
||||
{
|
||||
return getValue("storedLocaly").toBool();
|
||||
@@ -159,11 +102,6 @@ bool CModEntry::isInstalled() const
|
||||
return !localData.isEmpty();
|
||||
}
|
||||
|
||||
bool CModEntry::isValid() const
|
||||
{
|
||||
return !localData.isEmpty() || !repository.isEmpty();
|
||||
}
|
||||
|
||||
int CModEntry::getModStatus() const
|
||||
{
|
||||
int status = 0;
|
||||
@@ -255,11 +193,7 @@ static QVariant getValue(QVariant input, QString path)
|
||||
QString remainder = "/" + path.section('/', 2, -1);
|
||||
|
||||
entryName.remove(0, 1);
|
||||
QMap<QString, QString> keyNormalize;
|
||||
for(auto & key : input.toMap().keys())
|
||||
keyNormalize[key.toLower()] = key;
|
||||
|
||||
return getValue(input.toMap().value(keyNormalize[entryName]), remainder);
|
||||
return getValue(input.toMap().value(entryName), remainder);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -269,7 +203,6 @@ static QVariant getValue(QVariant input, QString path)
|
||||
|
||||
CModEntry CModList::getMod(QString modname) const
|
||||
{
|
||||
modname = modname.toLower();
|
||||
QVariantMap repo;
|
||||
QVariantMap local = localModList[modname].toMap();
|
||||
QVariantMap settings;
|
||||
@@ -313,14 +246,14 @@ CModEntry CModList::getMod(QString modname) const
|
||||
QVariant repoVal = getValue(entry, path);
|
||||
if(repoVal.isValid())
|
||||
{
|
||||
auto repoValMap = repoVal.toMap();
|
||||
auto compatibility = repoValMap["compatibility"].toMap();
|
||||
if(isCompatible(compatibility["min"].toString(), compatibility["max"].toString()))
|
||||
if(repo.empty())
|
||||
{
|
||||
if(repo.empty() || CModEntry::compareVersions(repo["version"].toString(), repoValMap["version"].toString()))
|
||||
{
|
||||
repo = repoValMap;
|
||||
}
|
||||
repo = repoVal.toMap();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(CModEntry::compareVersions(repo["version"].toString(), repoVal.toMap()["version"].toString()))
|
||||
repo = repoVal.toMap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,12 +297,12 @@ QVector<QString> CModList::getModList() const
|
||||
{
|
||||
for(auto it = repo.begin(); it != repo.end(); it++)
|
||||
{
|
||||
knownMods.insert(it.key().toLower());
|
||||
knownMods.insert(it.key());
|
||||
}
|
||||
}
|
||||
for(auto it = localModList.begin(); it != localModList.end(); it++)
|
||||
{
|
||||
knownMods.insert(it.key().toLower());
|
||||
knownMods.insert(it.key());
|
||||
}
|
||||
|
||||
for(auto entry : knownMods)
|
||||
|
@@ -51,10 +51,6 @@ public:
|
||||
bool isInstalled() const;
|
||||
// vcmi essential files
|
||||
bool isEssential() const;
|
||||
// checks if verison is compatible with vcmi
|
||||
bool isCompatible() const;
|
||||
// returns if has any data
|
||||
bool isValid() const;
|
||||
|
||||
// see ModStatus enum
|
||||
int getModStatus() const;
|
||||
|
@@ -245,7 +245,6 @@ bool CModFilterModel::filterMatchesThis(const QModelIndex & source) const
|
||||
{
|
||||
CModEntry mod = base->getMod(source.data(ModRoles::ModNameRole).toString());
|
||||
return (mod.getModStatus() & filterMask) == filteredType &&
|
||||
mod.isValid() &&
|
||||
QSortFilterProxyModel::filterAcceptsRow(source.row(), source.parent());
|
||||
}
|
||||
|
||||
|
@@ -169,10 +169,6 @@ bool CModManager::canEnableMod(QString modname)
|
||||
if(!mod.isInstalled())
|
||||
return addError(modname, "Mod must be installed first");
|
||||
|
||||
//check for compatibility
|
||||
if(!mod.isCompatible())
|
||||
return addError(modname, "Mod is not compatible, please update VCMI and checkout latest mod revisions");
|
||||
|
||||
for(auto modEntry : mod.getValue("depends").toStringList())
|
||||
{
|
||||
if(!modList->hasMod(modEntry)) // required mod is not available
|
||||
|
@@ -544,14 +544,10 @@ CModInfo::Version CModInfo::Version::fromString(std::string from)
|
||||
{
|
||||
auto pointPos = from.find('.');
|
||||
major = std::stoi(from.substr(0, pointPos));
|
||||
if(pointPos != std::string::npos)
|
||||
{
|
||||
from = from.substr(pointPos + 1);
|
||||
pointPos = from.find('.');
|
||||
minor = std::stoi(from.substr(0, pointPos));
|
||||
if(pointPos != std::string::npos)
|
||||
patch = std::stoi(from.substr(pointPos + 1));
|
||||
}
|
||||
from = from.substr(pointPos);
|
||||
pointPos = from.find('.');
|
||||
minor = std::stoi(from.substr(0, pointPos));
|
||||
patch = std::stoi(from.substr(pointPos));
|
||||
}
|
||||
catch(const std::invalid_argument & e)
|
||||
{
|
||||
@@ -654,11 +650,8 @@ void CModInfo::loadLocalData(const JsonNode & data)
|
||||
}
|
||||
|
||||
//check compatibility
|
||||
bool wasEnabled = enabled;
|
||||
enabled &= vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin);
|
||||
enabled &= vcmiCompatibleMax.isNull() || vcmiCompatibleMax.compatible(Version::GameVersion());
|
||||
if(wasEnabled && !enabled)
|
||||
logGlobal->warn("Mod %s is incompatible with current version of VCMI and cannot be enabled", name);
|
||||
|
||||
if (enabled)
|
||||
validation = validated ? PASSED : PENDING;
|
||||
|
@@ -240,6 +240,19 @@ public:
|
||||
static std::string getModDir(std::string name);
|
||||
static std::string getModFile(std::string name);
|
||||
|
||||
//TODO: remove as soon as backward compatilibity for versions earlier 806 is not preserved.
|
||||
template <typename Handler> void serialize(Handler &h, const int ver)
|
||||
{
|
||||
h & identifier;
|
||||
h & description;
|
||||
h & name;
|
||||
h & dependencies;
|
||||
h & conflicts;
|
||||
h & config;
|
||||
h & checksum;
|
||||
h & validation;
|
||||
h & enabled;
|
||||
}
|
||||
private:
|
||||
void loadLocalData(const JsonNode & data);
|
||||
};
|
||||
@@ -361,33 +374,41 @@ public:
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
if(h.saving)
|
||||
if(version < 806)
|
||||
{
|
||||
h & allMods; //don't serialize mods
|
||||
h & activeMods;
|
||||
for(auto & m : activeMods)
|
||||
h & allMods[m].version;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<TModID> newActiveMods;
|
||||
h & newActiveMods;
|
||||
for(auto & m : newActiveMods)
|
||||
if(h.saving)
|
||||
{
|
||||
if(!allMods.count(m))
|
||||
throw Incompatibility(m + " unkown mod");
|
||||
|
||||
CModInfo::Version mver;
|
||||
h & mver;
|
||||
if(!allMods[m].version.isNull() && !mver.isNull() && !allMods[m].version.compatible(mver))
|
||||
{
|
||||
std::string err = allMods[m].name +
|
||||
": version needed " + mver.toString() +
|
||||
"but you have installed " + allMods[m].version.toString();
|
||||
throw Incompatibility(err);
|
||||
}
|
||||
allMods[m].enabled = true;
|
||||
h & activeMods;
|
||||
for(auto & m : activeMods)
|
||||
h & allMods[m].version;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<TModID> newActiveMods;
|
||||
h & newActiveMods;
|
||||
for(auto & m : newActiveMods)
|
||||
{
|
||||
if(!allMods.count(m))
|
||||
throw Incompatibility(m + " unkown mod");
|
||||
|
||||
CModInfo::Version mver;
|
||||
h & mver;
|
||||
if(!allMods[m].version.isNull() && !mver.isNull() && !allMods[m].version.compatible(mver))
|
||||
{
|
||||
std::string err = allMods[m].name +
|
||||
": version needed " + mver.toString() +
|
||||
"but you have installed " + allMods[m].version.toString();
|
||||
throw Incompatibility(err);
|
||||
}
|
||||
allMods[m].enabled = true;
|
||||
}
|
||||
std::swap(activeMods, newActiveMods);
|
||||
}
|
||||
std::swap(activeMods, newActiveMods);
|
||||
}
|
||||
|
||||
h & settings;
|
||||
|
780
mapeditor/Animation.cpp
Normal file
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
* CAnimation.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 "Animation.h"
|
||||
|
||||
#include "BitmapHandler.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/ISimpleResourceLoader.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/CRandomGenerator.h"
|
||||
|
||||
|
||||
typedef std::map<size_t, std::vector<JsonNode>> source_map;
|
||||
//typedef std::map<size_t, IImage*> image_map;
|
||||
//typedef std::map<size_t, image_map > group_map;
|
||||
|
||||
/// Class for def loading
|
||||
/// After loading will store general info (palette and frame offsets) and pointer to file itself
|
||||
class DefFile
|
||||
{
|
||||
private:
|
||||
|
||||
struct SSpriteDef
|
||||
{
|
||||
ui32 size;
|
||||
ui32 format; /// format in which pixel data is stored
|
||||
ui32 fullWidth; /// full width and height of frame, including borders
|
||||
ui32 fullHeight;
|
||||
ui32 width; /// width and height of pixel data, borders excluded
|
||||
ui32 height;
|
||||
si32 leftMargin;
|
||||
si32 topMargin;
|
||||
};
|
||||
//offset[group][frame] - offset of frame data in file
|
||||
std::map<size_t, std::vector <size_t> > offset;
|
||||
|
||||
std::unique_ptr<ui8[]> data;
|
||||
std::unique_ptr<QVector<QRgb>> palette;
|
||||
|
||||
public:
|
||||
DefFile(std::string Name);
|
||||
~DefFile();
|
||||
|
||||
std::shared_ptr<QImage> loadFrame(size_t frame, size_t group) const;
|
||||
|
||||
const std::map<size_t, size_t> getEntries() const;
|
||||
};
|
||||
|
||||
class ImageLoader
|
||||
{
|
||||
QImage * image;
|
||||
ui8 * lineStart;
|
||||
ui8 * position;
|
||||
QPoint spriteSize, margins, fullSize;
|
||||
public:
|
||||
//load size raw pixels from data
|
||||
inline void Load(size_t size, const ui8 * data);
|
||||
//set size pixels to color
|
||||
inline void Load(size_t size, ui8 color=0);
|
||||
inline void EndLine();
|
||||
//init image with these sizes and palette
|
||||
inline void init(QPoint SpriteSize, QPoint Margins, QPoint FullSize);
|
||||
|
||||
ImageLoader(QImage * Img);
|
||||
~ImageLoader();
|
||||
};
|
||||
|
||||
// Extremely simple file cache. TODO: smarter, more general solution
|
||||
class FileCache
|
||||
{
|
||||
static const int cacheSize = 50; //Max number of cached files
|
||||
struct FileData
|
||||
{
|
||||
ResourceID name;
|
||||
size_t size;
|
||||
std::unique_ptr<ui8[]> data;
|
||||
|
||||
std::unique_ptr<ui8[]> getCopy()
|
||||
{
|
||||
auto ret = std::unique_ptr<ui8[]>(new ui8[size]);
|
||||
std::copy(data.get(), data.get() + size, ret.get());
|
||||
return ret;
|
||||
}
|
||||
FileData(ResourceID name_, size_t size_, std::unique_ptr<ui8[]> data_):
|
||||
name{std::move(name_)},
|
||||
size{size_},
|
||||
data{std::move(data_)}
|
||||
{}
|
||||
};
|
||||
|
||||
std::deque<FileData> cache;
|
||||
public:
|
||||
std::unique_ptr<ui8[]> getCachedFile(ResourceID rid)
|
||||
{
|
||||
for(auto & file : cache)
|
||||
{
|
||||
if (file.name == rid)
|
||||
return file.getCopy();
|
||||
}
|
||||
// Still here? Cache miss
|
||||
if (cache.size() > cacheSize)
|
||||
cache.pop_front();
|
||||
|
||||
auto data = CResourceHandler::get()->load(rid)->readAll();
|
||||
|
||||
cache.emplace_back(std::move(rid), data.second, std::move(data.first));
|
||||
|
||||
return cache.back().getCopy();
|
||||
}
|
||||
};
|
||||
|
||||
enum class DefType : uint32_t
|
||||
{
|
||||
SPELL = 0x40,
|
||||
SPRITE = 0x41,
|
||||
CREATURE = 0x42,
|
||||
MAP = 0x43,
|
||||
MAP_HERO = 0x44,
|
||||
TERRAIN = 0x45,
|
||||
CURSOR = 0x46,
|
||||
INTERFACE = 0x47,
|
||||
SPRITE_FRAME = 0x48,
|
||||
BATTLE_HERO = 0x49
|
||||
};
|
||||
|
||||
static FileCache animationCache;
|
||||
|
||||
/*************************************************************************
|
||||
* DefFile, class used for def loading *
|
||||
*************************************************************************/
|
||||
|
||||
DefFile::DefFile(std::string Name):
|
||||
data(nullptr)
|
||||
{
|
||||
|
||||
#if 0
|
||||
static QRgba H3_ORIG_PALETTE[8] =
|
||||
{
|
||||
{ 0, 255, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 150, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 100, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 50, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 0, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 255, 0, SDL_ALPHA_OPAQUE},
|
||||
{180, 0, 255, SDL_ALPHA_OPAQUE},
|
||||
{ 0, 255, 0, SDL_ALPHA_OPAQUE}
|
||||
};
|
||||
#endif // 0
|
||||
|
||||
//First 8 colors in def palette used for transparency
|
||||
static QRgb H3Palette[8] =
|
||||
{
|
||||
qRgba(0, 0, 0, 0), // 100% - transparency
|
||||
qRgba(0, 0, 0, 32), // 75% - shadow border,
|
||||
qRgba(0, 0, 0, 64), // TODO: find exact value
|
||||
qRgba(0, 0, 0, 128), // TODO: for transparency
|
||||
qRgba(0, 0, 0, 128), // 50% - shadow body
|
||||
qRgba(0, 0, 0, 0), // 100% - selection highlight
|
||||
qRgba(0, 0, 0, 128), // 50% - shadow body below selection
|
||||
qRgba(0, 0, 0, 64) // 75% - shadow border below selection
|
||||
};
|
||||
data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION));
|
||||
|
||||
palette = std::make_unique<QVector<QRgb>>(256);
|
||||
int it = 0;
|
||||
|
||||
ui32 type = read_le_u32(data.get() + it);
|
||||
it+=4;
|
||||
//int width = read_le_u32(data + it); it+=4;//not used
|
||||
//int height = read_le_u32(data + it); it+=4;
|
||||
it+=8;
|
||||
ui32 totalBlocks = read_le_u32(data.get() + it);
|
||||
it+=4;
|
||||
|
||||
for (ui32 i= 0; i<256; i++)
|
||||
{
|
||||
ui8 c[3];
|
||||
c[0] = data[it++];
|
||||
c[1] = data[it++];
|
||||
c[2] = data[it++];
|
||||
(*palette)[i] = qRgba(c[0], c[1], c[2], 255);
|
||||
}
|
||||
|
||||
switch(static_cast<DefType>(type))
|
||||
{
|
||||
case DefType::SPELL:
|
||||
(*palette)[0] = H3Palette[0];
|
||||
break;
|
||||
case DefType::SPRITE:
|
||||
case DefType::SPRITE_FRAME:
|
||||
for(ui32 i= 0; i<8; i++)
|
||||
(*palette)[i] = H3Palette[i];
|
||||
break;
|
||||
case DefType::CREATURE:
|
||||
(*palette)[0] = H3Palette[0];
|
||||
(*palette)[1] = H3Palette[1];
|
||||
(*palette)[4] = H3Palette[4];
|
||||
(*palette)[5] = H3Palette[5];
|
||||
(*palette)[6] = H3Palette[6];
|
||||
(*palette)[7] = H3Palette[7];
|
||||
break;
|
||||
case DefType::MAP:
|
||||
case DefType::MAP_HERO:
|
||||
(*palette)[0] = H3Palette[0];
|
||||
(*palette)[1] = H3Palette[1];
|
||||
(*palette)[4] = H3Palette[4];
|
||||
//5 = owner flag, handled separately
|
||||
break;
|
||||
case DefType::TERRAIN:
|
||||
(*palette)[0] = H3Palette[0];
|
||||
(*palette)[1] = H3Palette[1];
|
||||
(*palette)[2] = H3Palette[2];
|
||||
(*palette)[3] = H3Palette[3];
|
||||
(*palette)[4] = H3Palette[4];
|
||||
break;
|
||||
case DefType::CURSOR:
|
||||
(*palette)[0] = H3Palette[0];
|
||||
break;
|
||||
case DefType::INTERFACE:
|
||||
(*palette)[0] = H3Palette[0];
|
||||
(*palette)[1] = H3Palette[1];
|
||||
(*palette)[4] = H3Palette[4];
|
||||
//player colors handled separately
|
||||
//TODO: disallow colorizing other def types
|
||||
break;
|
||||
case DefType::BATTLE_HERO:
|
||||
(*palette)[0] = H3Palette[0];
|
||||
(*palette)[1] = H3Palette[1];
|
||||
(*palette)[4] = H3Palette[4];
|
||||
break;
|
||||
default:
|
||||
logAnim->error("Unknown def type %d in %s", type, Name);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
for (ui32 i=0; i<totalBlocks; i++)
|
||||
{
|
||||
size_t blockID = read_le_u32(data.get() + it);
|
||||
it+=4;
|
||||
size_t totalEntries = read_le_u32(data.get() + it);
|
||||
it+=12;
|
||||
//8 unknown bytes - skipping
|
||||
|
||||
//13 bytes for name of every frame in this block - not used, skipping
|
||||
it+= 13 * (int)totalEntries;
|
||||
|
||||
for (ui32 j=0; j<totalEntries; j++)
|
||||
{
|
||||
size_t currOffset = read_le_u32(data.get() + it);
|
||||
offset[blockID].push_back(currOffset);
|
||||
it += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<QImage> DefFile::loadFrame(size_t frame, size_t group) const
|
||||
{
|
||||
std::map<size_t, std::vector <size_t> >::const_iterator it;
|
||||
it = offset.find(group);
|
||||
assert (it != offset.end());
|
||||
|
||||
const ui8 * FDef = data.get()+it->second[frame];
|
||||
|
||||
const SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef);
|
||||
SSpriteDef sprite;
|
||||
|
||||
sprite.format = read_le_u32(&sd.format);
|
||||
sprite.fullWidth = read_le_u32(&sd.fullWidth);
|
||||
sprite.fullHeight = read_le_u32(&sd.fullHeight);
|
||||
sprite.width = read_le_u32(&sd.width);
|
||||
sprite.height = read_le_u32(&sd.height);
|
||||
sprite.leftMargin = read_le_u32(&sd.leftMargin);
|
||||
sprite.topMargin = read_le_u32(&sd.topMargin);
|
||||
|
||||
ui32 currentOffset = sizeof(SSpriteDef);
|
||||
|
||||
//special case for some "old" format defs (SGTWMTA.DEF and SGTWMTB.DEF)
|
||||
|
||||
if(sprite.format == 1 && sprite.width > sprite.fullWidth && sprite.height > sprite.fullHeight)
|
||||
{
|
||||
sprite.leftMargin = 0;
|
||||
sprite.topMargin = 0;
|
||||
sprite.width = sprite.fullWidth;
|
||||
sprite.height = sprite.fullHeight;
|
||||
|
||||
currentOffset -= 16;
|
||||
}
|
||||
|
||||
const ui32 BaseOffset = currentOffset;
|
||||
|
||||
|
||||
std::shared_ptr<QImage> img = std::make_shared<QImage>(sprite.fullWidth, sprite.fullHeight, QImage::Format_Indexed8);
|
||||
if(!img)
|
||||
throw std::runtime_error("Image memory cannot be allocated");
|
||||
|
||||
ImageLoader loader(img.get());
|
||||
loader.init(QPoint(sprite.width, sprite.height),
|
||||
QPoint(sprite.leftMargin, sprite.topMargin),
|
||||
QPoint(sprite.fullWidth, sprite.fullHeight));
|
||||
|
||||
switch(sprite.format)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
//pixel data is not compressed, copy data to surface
|
||||
for(ui32 i=0; i<sprite.height; i++)
|
||||
{
|
||||
loader.Load(sprite.width, FDef + currentOffset);
|
||||
currentOffset += sprite.width;
|
||||
loader.EndLine();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
//for each line we have offset of pixel data
|
||||
const ui32 * RWEntriesLoc = reinterpret_cast<const ui32 *>(FDef+currentOffset);
|
||||
currentOffset += sizeof(ui32) * sprite.height;
|
||||
|
||||
for(ui32 i=0; i<sprite.height; i++)
|
||||
{
|
||||
//get position of the line
|
||||
currentOffset=BaseOffset + read_le_u32(RWEntriesLoc + i);
|
||||
ui32 TotalRowLength = 0;
|
||||
|
||||
while(TotalRowLength<sprite.width)
|
||||
{
|
||||
ui8 segmentType = FDef[currentOffset++];
|
||||
ui32 length = FDef[currentOffset++] + 1;
|
||||
|
||||
if(segmentType==0xFF)//Raw data
|
||||
{
|
||||
loader.Load(length, FDef + currentOffset);
|
||||
currentOffset+=length;
|
||||
}
|
||||
else// RLE
|
||||
{
|
||||
loader.Load(length, segmentType);
|
||||
}
|
||||
TotalRowLength += length;
|
||||
}
|
||||
|
||||
loader.EndLine();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset);
|
||||
|
||||
for(ui32 i=0; i<sprite.height; i++)
|
||||
{
|
||||
ui32 TotalRowLength=0;
|
||||
|
||||
while(TotalRowLength<sprite.width)
|
||||
{
|
||||
ui8 segment=FDef[currentOffset++];
|
||||
ui8 code = segment / 32;
|
||||
ui8 length = (segment & 31) + 1;
|
||||
|
||||
if(code==7)//Raw data
|
||||
{
|
||||
loader.Load(length, FDef + currentOffset);
|
||||
currentOffset += length;
|
||||
}
|
||||
else//RLE
|
||||
{
|
||||
loader.Load(length, code);
|
||||
}
|
||||
TotalRowLength+=length;
|
||||
}
|
||||
loader.EndLine();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
for(ui32 i=0; i<sprite.height; i++)
|
||||
{
|
||||
currentOffset = BaseOffset + read_le_u16(FDef + BaseOffset+i*2*(sprite.width/32));
|
||||
ui32 TotalRowLength=0;
|
||||
|
||||
while(TotalRowLength<sprite.width)
|
||||
{
|
||||
ui8 segment = FDef[currentOffset++];
|
||||
ui8 code = segment / 32;
|
||||
ui8 length = (segment & 31) + 1;
|
||||
|
||||
if(code==7)//Raw data
|
||||
{
|
||||
loader.Load(length, FDef + currentOffset);
|
||||
currentOffset += length;
|
||||
}
|
||||
else//RLE
|
||||
{
|
||||
loader.Load(length, code);
|
||||
}
|
||||
TotalRowLength += length;
|
||||
}
|
||||
loader.EndLine();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logGlobal->error("Error: unsupported format of def file: %d", sprite.format);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
img->setColorTable(*palette);
|
||||
return img;
|
||||
}
|
||||
|
||||
DefFile::~DefFile() = default;
|
||||
|
||||
const std::map<size_t, size_t > DefFile::getEntries() const
|
||||
{
|
||||
std::map<size_t, size_t > ret;
|
||||
|
||||
for (auto & elem : offset)
|
||||
ret[elem.first] = elem.second.size();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Classes for image loaders - helpers for loading from def files *
|
||||
*************************************************************************/
|
||||
|
||||
ImageLoader::ImageLoader(QImage * Img):
|
||||
image(Img),
|
||||
lineStart(Img->bits()),
|
||||
position(Img->bits())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ImageLoader::init(QPoint SpriteSize, QPoint Margins, QPoint FullSize)
|
||||
{
|
||||
spriteSize = SpriteSize;
|
||||
margins = Margins;
|
||||
fullSize = FullSize;
|
||||
|
||||
memset((void *)image->bits(), 0, fullSize.y() * fullSize.x());
|
||||
|
||||
lineStart = image->bits();
|
||||
lineStart += margins.y() * fullSize.x() + margins.x();
|
||||
position = lineStart;
|
||||
}
|
||||
|
||||
inline void ImageLoader::Load(size_t size, const ui8 * data)
|
||||
{
|
||||
if(size)
|
||||
{
|
||||
memcpy((void *)position, data, size);
|
||||
position += size;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ImageLoader::Load(size_t size, ui8 color)
|
||||
{
|
||||
if (size)
|
||||
{
|
||||
memset((void *)position, color, size);
|
||||
position += size;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ImageLoader::EndLine()
|
||||
{
|
||||
lineStart += fullSize.x();
|
||||
position = lineStart;
|
||||
}
|
||||
|
||||
ImageLoader::~ImageLoader()
|
||||
{
|
||||
//SDL_UnlockSurface(image->surf);
|
||||
//SDL_SetColorKey(image->surf, SDL_TRUE, 0);
|
||||
//TODO: RLE if compressed and bpp>1
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Classes for images, support loading from file and drawing on surface *
|
||||
*************************************************************************/
|
||||
|
||||
std::shared_ptr<QImage> Animation::getFromExtraDef(std::string filename)
|
||||
{
|
||||
size_t pos = filename.find(':');
|
||||
if (pos == -1)
|
||||
return nullptr;
|
||||
Animation anim(filename.substr(0, pos));
|
||||
pos++;
|
||||
size_t frame = atoi(filename.c_str()+pos);
|
||||
size_t group = 0;
|
||||
pos = filename.find(':', pos);
|
||||
if (pos != -1)
|
||||
{
|
||||
pos++;
|
||||
group = frame;
|
||||
frame = atoi(filename.c_str()+pos);
|
||||
}
|
||||
anim.load(frame ,group);
|
||||
auto ret = anim.images[group][frame];
|
||||
anim.images.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Animation::loadFrame(size_t frame, size_t group)
|
||||
{
|
||||
if(size(group) <= frame)
|
||||
{
|
||||
printError(frame, group, "LoadFrame");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto image = getImage(frame, group, false);
|
||||
if(image)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//try to get image from def
|
||||
if(source[group][frame].getType() == JsonNode::JsonType::DATA_NULL)
|
||||
{
|
||||
if(defFile)
|
||||
{
|
||||
auto frameList = defFile->getEntries();
|
||||
|
||||
if(vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present
|
||||
{
|
||||
images[group][frame] = defFile->loadFrame(frame, group);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// still here? image is missing
|
||||
|
||||
printError(frame, group, "LoadFrame");
|
||||
images[group][frame] = std::make_shared<QImage>("DEFAULT");
|
||||
}
|
||||
else //load from separate file
|
||||
{
|
||||
auto img = getFromExtraDef(source[group][frame]["file"].String());
|
||||
//if(!img)
|
||||
|
||||
//img = std::make_shared<QImage>(source[group][frame]);
|
||||
|
||||
images[group][frame] = img;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Animation::unloadFrame(size_t frame, size_t group)
|
||||
{
|
||||
auto image = getImage(frame, group, false);
|
||||
if(image)
|
||||
{
|
||||
images[group].erase(frame);
|
||||
|
||||
if(images[group].empty())
|
||||
images.erase(group);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Animation::init()
|
||||
{
|
||||
if(defFile)
|
||||
{
|
||||
const std::map<size_t, size_t> defEntries = defFile->getEntries();
|
||||
|
||||
for (auto & defEntry : defEntries)
|
||||
source[defEntry.first].resize(defEntry.second);
|
||||
}
|
||||
|
||||
ResourceID resID(std::string("SPRITES/") + name, EResType::TEXT);
|
||||
|
||||
//if(vstd::contains(graphics->imageLists, resID.getName()))
|
||||
//initFromJson(graphics->imageLists[resID.getName()]);
|
||||
|
||||
auto configList = CResourceHandler::get()->getResourcesWithName(resID);
|
||||
|
||||
for(auto & loader : configList)
|
||||
{
|
||||
auto stream = loader->load(resID);
|
||||
std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
|
||||
stream->read(textData.get(), stream->getSize());
|
||||
|
||||
const JsonNode config((char*)textData.get(), stream->getSize());
|
||||
|
||||
//initFromJson(config);
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::printError(size_t frame, size_t group, std::string type) const
|
||||
{
|
||||
logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name, group, frame);
|
||||
}
|
||||
|
||||
Animation::Animation(std::string Name):
|
||||
name(Name),
|
||||
preloaded(false),
|
||||
defFile()
|
||||
{
|
||||
size_t dotPos = name.find_last_of('.');
|
||||
if ( dotPos!=-1 )
|
||||
name.erase(dotPos);
|
||||
std::transform(name.begin(), name.end(), name.begin(), toupper);
|
||||
|
||||
ResourceID resource(std::string("SPRITES/") + name, EResType::ANIMATION);
|
||||
|
||||
if(CResourceHandler::get()->existsResource(resource))
|
||||
defFile = std::make_shared<DefFile>(name);
|
||||
|
||||
init();
|
||||
|
||||
if(source.empty())
|
||||
logAnim->error("Animation %s failed to load", Name);
|
||||
}
|
||||
|
||||
Animation::Animation():
|
||||
name(""),
|
||||
preloaded(false),
|
||||
defFile()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
Animation::~Animation() = default;
|
||||
|
||||
void Animation::duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup)
|
||||
{
|
||||
if(!source.count(sourceGroup))
|
||||
{
|
||||
logAnim->error("Group %d missing in %s", sourceGroup, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if(source[sourceGroup].size() <= sourceFrame)
|
||||
{
|
||||
logAnim->error("Frame [%d %d] missing in %s", sourceGroup, sourceFrame, name);
|
||||
return;
|
||||
}
|
||||
|
||||
//todo: clone actual loaded Image object
|
||||
JsonNode clone(source[sourceGroup][sourceFrame]);
|
||||
|
||||
if(clone.getType() == JsonNode::JsonType::DATA_NULL)
|
||||
{
|
||||
std::string temp = name+":"+boost::lexical_cast<std::string>(sourceGroup)+":"+boost::lexical_cast<std::string>(sourceFrame);
|
||||
clone["file"].String() = temp;
|
||||
}
|
||||
|
||||
source[targetGroup].push_back(clone);
|
||||
|
||||
size_t index = source[targetGroup].size() - 1;
|
||||
|
||||
if(preloaded)
|
||||
load(index, targetGroup);
|
||||
}
|
||||
|
||||
void Animation::setCustom(std::string filename, size_t frame, size_t group)
|
||||
{
|
||||
if (source[group].size() <= frame)
|
||||
source[group].resize(frame+1);
|
||||
source[group][frame]["file"].String() = filename;
|
||||
//FIXME: update image if already loaded
|
||||
}
|
||||
|
||||
std::shared_ptr<QImage> Animation::getImage(size_t frame, size_t group, bool verbose) const
|
||||
{
|
||||
auto groupIter = images.find(group);
|
||||
if (groupIter != images.end())
|
||||
{
|
||||
auto imageIter = groupIter->second.find(frame);
|
||||
if (imageIter != groupIter->second.end())
|
||||
return imageIter->second;
|
||||
}
|
||||
if (verbose)
|
||||
printError(frame, group, "GetImage");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Animation::load()
|
||||
{
|
||||
for (auto & elem : source)
|
||||
for (size_t image=0; image < elem.second.size(); image++)
|
||||
loadFrame(image, elem.first);
|
||||
}
|
||||
|
||||
void Animation::unload()
|
||||
{
|
||||
for (auto & elem : source)
|
||||
for (size_t image=0; image < elem.second.size(); image++)
|
||||
unloadFrame(image, elem.first);
|
||||
|
||||
}
|
||||
|
||||
void Animation::preload()
|
||||
{
|
||||
if(!preloaded)
|
||||
{
|
||||
preloaded = true;
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::loadGroup(size_t group)
|
||||
{
|
||||
if (vstd::contains(source, group))
|
||||
for (size_t image=0; image < source[group].size(); image++)
|
||||
loadFrame(image, group);
|
||||
}
|
||||
|
||||
void Animation::unloadGroup(size_t group)
|
||||
{
|
||||
if (vstd::contains(source, group))
|
||||
for (size_t image=0; image < source[group].size(); image++)
|
||||
unloadFrame(image, group);
|
||||
}
|
||||
|
||||
void Animation::load(size_t frame, size_t group)
|
||||
{
|
||||
loadFrame(frame, group);
|
||||
}
|
||||
|
||||
void Animation::unload(size_t frame, size_t group)
|
||||
{
|
||||
unloadFrame(frame, group);
|
||||
}
|
||||
|
||||
size_t Animation::size(size_t group) const
|
||||
{
|
||||
auto iter = source.find(group);
|
||||
if (iter != source.end())
|
||||
return iter->second.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Animation::horizontalFlip()
|
||||
{
|
||||
for(auto & group : images)
|
||||
for(auto & image : group.second)
|
||||
*image.second = image.second->transformed(QTransform::fromScale(-1, 1));
|
||||
}
|
||||
|
||||
void Animation::verticalFlip()
|
||||
{
|
||||
for(auto & group : images)
|
||||
for(auto & image : group.second)
|
||||
*image.second = image.second->transformed(QTransform::fromScale(1, -1));
|
||||
}
|
||||
|
||||
void Animation::playerColored(PlayerColor player)
|
||||
{
|
||||
//for(auto & group : images)
|
||||
//for(auto & image : group.second)
|
||||
//image.second->playerColored(player);
|
||||
}
|
||||
|
||||
void Animation::createFlippedGroup(const size_t sourceGroup, const size_t targetGroup)
|
||||
{
|
||||
for(size_t frame = 0; frame < size(sourceGroup); ++frame)
|
||||
{
|
||||
duplicateImage(sourceGroup, frame, targetGroup);
|
||||
|
||||
auto image = getImage(frame, targetGroup);
|
||||
*image = image->transformed(QTransform::fromScale(1, -1));
|
||||
}
|
||||
}
|
87
mapeditor/Animation.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef ANIMATION_H
|
||||
#define ANIMATION_H
|
||||
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include <QRgb>
|
||||
#include <QImage>
|
||||
|
||||
/*
|
||||
* Base class for images, can be used for non-animation pictures as well
|
||||
*/
|
||||
|
||||
class DefFile;
|
||||
/// Class for handling animation
|
||||
class Animation
|
||||
{
|
||||
private:
|
||||
//source[group][position] - file with this frame, if string is empty - image located in def file
|
||||
std::map<size_t, std::vector<JsonNode>> source;
|
||||
|
||||
//bitmap[group][position], store objects with loaded bitmaps
|
||||
std::map<size_t, std::map<size_t, std::shared_ptr<QImage> > > images;
|
||||
|
||||
//animation file name
|
||||
std::string name;
|
||||
|
||||
bool preloaded;
|
||||
|
||||
std::shared_ptr<DefFile> defFile;
|
||||
|
||||
//loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded
|
||||
bool loadFrame(size_t frame, size_t group);
|
||||
|
||||
//unloadFrame, returns true if image has been unloaded ( either deleted or decreased refCount)
|
||||
bool unloadFrame(size_t frame, size_t group);
|
||||
|
||||
//initialize animation from file
|
||||
//void initFromJson(const JsonNode & input);
|
||||
void init();
|
||||
|
||||
//to get rid of copy-pasting error message :]
|
||||
void printError(size_t frame, size_t group, std::string type) const;
|
||||
|
||||
//not a very nice method to get image from another def file
|
||||
//TODO: remove after implementing resource manager
|
||||
std::shared_ptr<QImage> getFromExtraDef(std::string filename);
|
||||
|
||||
public:
|
||||
Animation(std::string Name);
|
||||
Animation();
|
||||
~Animation();
|
||||
|
||||
//duplicates frame at [sourceGroup, sourceFrame] as last frame in targetGroup
|
||||
//and loads it if animation is preloaded
|
||||
void duplicateImage(const size_t sourceGroup, const size_t sourceFrame, const size_t targetGroup);
|
||||
|
||||
// adjust the color of the animation, used in battle spell effects, e.g. Cloned objects
|
||||
|
||||
//add custom surface to the selected position.
|
||||
void setCustom(std::string filename, size_t frame, size_t group=0);
|
||||
|
||||
std::shared_ptr<QImage> getImage(size_t frame, size_t group=0, bool verbose=true) const;
|
||||
|
||||
//all available frames
|
||||
void load ();
|
||||
void unload();
|
||||
void preload();
|
||||
|
||||
//all frames from group
|
||||
void loadGroup (size_t group);
|
||||
void unloadGroup(size_t group);
|
||||
|
||||
//single image
|
||||
void load (size_t frame, size_t group=0);
|
||||
void unload(size_t frame, size_t group=0);
|
||||
|
||||
//total count of frames in group (including not loaded)
|
||||
size_t size(size_t group=0) const;
|
||||
|
||||
void horizontalFlip();
|
||||
void verticalFlip();
|
||||
void playerColored(PlayerColor player);
|
||||
|
||||
void createFlippedGroup(const size_t sourceGroup, const size_t targetGroup);
|
||||
};
|
||||
|
||||
#endif // ANIMATION_H
|
169
mapeditor/BitmapHandler.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// BitmapHandler.cpp
|
||||
// vcmieditor
|
||||
//
|
||||
// Created by nordsoft on 29.08.2022.
|
||||
//
|
||||
#include "StdInc.h"
|
||||
#include "BitmapHandler.h"
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
|
||||
#include <QBitmap>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
|
||||
namespace BitmapHandler
|
||||
{
|
||||
QImage loadH3PCX(ui8 * data, size_t size);
|
||||
|
||||
QImage loadBitmapFromDir(std::string path, std::string fname, bool setKey=true);
|
||||
|
||||
bool isPCX(const ui8 *header)//check whether file can be PCX according to header
|
||||
{
|
||||
ui32 fSize = read_le_u32(header + 0);
|
||||
ui32 width = read_le_u32(header + 4);
|
||||
ui32 height = read_le_u32(header + 8);
|
||||
return fSize == width*height || fSize == width*height*3;
|
||||
}
|
||||
|
||||
enum Epcxformat
|
||||
{
|
||||
PCX8B,
|
||||
PCX24B
|
||||
};
|
||||
|
||||
QImage loadH3PCX(ui8 * pcx, size_t size)
|
||||
{
|
||||
//SDL_Surface * ret;
|
||||
|
||||
Epcxformat format;
|
||||
int it=0;
|
||||
|
||||
ui32 fSize = read_le_u32(pcx + it); it+=4;
|
||||
ui32 width = read_le_u32(pcx + it); it+=4;
|
||||
ui32 height = read_le_u32(pcx + it); it+=4;
|
||||
|
||||
if (fSize==width*height*3)
|
||||
format=PCX24B;
|
||||
else if (fSize==width*height)
|
||||
format=PCX8B;
|
||||
else
|
||||
return QImage();
|
||||
|
||||
QSize qsize(width, height);
|
||||
|
||||
if (format==PCX8B)
|
||||
{
|
||||
it = 0xC;
|
||||
//auto bitmap = QBitmap::fromData(qsize, pcx + it);
|
||||
QImage image(pcx + it, width, height, QImage::Format_Indexed8);
|
||||
|
||||
//palette - last 256*3 bytes
|
||||
QVector<QRgb> colorTable;
|
||||
it = (int)size-256*3;
|
||||
for (int i=0;i<256;i++)
|
||||
{
|
||||
char bytes[3];
|
||||
bytes[0] = pcx[it++];
|
||||
bytes[1] = pcx[it++];
|
||||
bytes[2] = pcx[it++];
|
||||
colorTable.append(qRgb(bytes[0], bytes[1], bytes[2]));
|
||||
}
|
||||
image.setColorTable(colorTable);
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{
|
||||
QImage image(pcx + it, width, height, QImage::Format_RGB32);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
QImage loadBitmapFromDir(std::string path, std::string fname, bool setKey)
|
||||
{
|
||||
if(!fname.size())
|
||||
{
|
||||
logGlobal->warn("Call to loadBitmap with void fname!");
|
||||
return QImage();
|
||||
}
|
||||
if (!CResourceHandler::get()->existsResource(ResourceID(path + fname, EResType::IMAGE)))
|
||||
{
|
||||
return QImage();
|
||||
}
|
||||
|
||||
auto fullpath = CResourceHandler::get()->getResourceName(ResourceID(path + fname, EResType::IMAGE));
|
||||
auto readFile = CResourceHandler::get()->load(ResourceID(path + fname, EResType::IMAGE))->readAll();
|
||||
|
||||
if (isPCX(readFile.first.get()))
|
||||
{//H3-style PCX
|
||||
auto image = BitmapHandler::loadH3PCX(readFile.first.get(), readFile.second);
|
||||
if(!image.isNull())
|
||||
{
|
||||
if(image.bitPlaneCount() == 1 && setKey)
|
||||
{
|
||||
QVector<QRgb> colorTable = image.colorTable();
|
||||
colorTable[0] = qRgba(255, 255, 255, 0);
|
||||
image.setColorTable(colorTable);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Failed to open %s as H3 PCX!", fname);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{ //loading via SDL_Image
|
||||
QImage image(QString::fromStdString(fullpath->make_preferred().string()));
|
||||
if(!image.isNull())
|
||||
{
|
||||
if(image.bitPlaneCount() == 1)
|
||||
{
|
||||
//set correct value for alpha\unused channel
|
||||
QVector<QRgb> colorTable = image.colorTable();
|
||||
for(auto & c : colorTable)
|
||||
c = qRgb(qRed(c), qGreen(c), qBlue(c));
|
||||
image.setColorTable(colorTable);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Failed to open %s via QImage", fname);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
return QImage();
|
||||
// When modifying anything here please check two use cases:
|
||||
// 1) Vampire mansion in Necropolis (not 1st color is transparent)
|
||||
// 2) Battle background when fighting on grass/dirt, topmost sky part (NO transparent color)
|
||||
// 3) New objects that may use 24-bit images for icons (e.g. witchking arts)
|
||||
/*if (ret->format->palette)
|
||||
{
|
||||
CSDL_Ext::setDefaultColorKeyPresize(ret);
|
||||
}
|
||||
else if (ret->format->Amask)
|
||||
{
|
||||
SDL_SetSurfaceBlendMode(ret, SDL_BLENDMODE_BLEND);
|
||||
}
|
||||
else // always set
|
||||
{
|
||||
CSDL_Ext::setDefaultColorKey(ret);
|
||||
}
|
||||
return ret;*/
|
||||
}
|
||||
|
||||
QImage loadBitmap(std::string fname, bool setKey)
|
||||
{
|
||||
QImage image = loadBitmapFromDir("DATA/", fname, setKey);
|
||||
if(image.isNull())
|
||||
{
|
||||
image = loadBitmapFromDir("SPRITES/", fname, setKey);
|
||||
if(image.isNull())
|
||||
{
|
||||
logGlobal->error("Error: Failed to find file %s", fname);
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
20
mapeditor/BitmapHandler.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// BitmapHandler.hpp
|
||||
// vcmieditor
|
||||
//
|
||||
// Created by nordsoft on 29.08.2022.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define read_le_u16(p) (* reinterpret_cast<const ui16 *>(p))
|
||||
#define read_le_u32(p) (* reinterpret_cast<const ui32 *>(p))
|
||||
|
||||
#include <QImage>
|
||||
|
||||
namespace BitmapHandler
|
||||
{
|
||||
//Load file from /DATA or /SPRITES
|
||||
QImage loadBitmap(std::string fname, bool setKey=true);
|
||||
}
|
||||
|
108
mapeditor/CGameInfo.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* CGameInfo.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 "CGameInfo.h"
|
||||
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
|
||||
const CGameInfo * CGI;
|
||||
CClientState * CCS = nullptr;
|
||||
CServerHandler * CSH;
|
||||
|
||||
|
||||
CGameInfo::CGameInfo()
|
||||
{
|
||||
generaltexth = nullptr;
|
||||
mh = nullptr;
|
||||
townh = nullptr;
|
||||
globalServices = nullptr;
|
||||
}
|
||||
|
||||
void CGameInfo::setFromLib()
|
||||
{
|
||||
globalServices = VLC;
|
||||
modh = VLC->modh;
|
||||
generaltexth = VLC->generaltexth;
|
||||
creh = VLC->creh;
|
||||
townh = VLC->townh;
|
||||
heroh = VLC->heroh;
|
||||
objh = VLC->objh;
|
||||
spellh = VLC->spellh;
|
||||
skillh = VLC->skillh;
|
||||
objtypeh = VLC->objtypeh;
|
||||
obstacleHandler = VLC->obstacleHandler;
|
||||
battleFieldHandler = VLC->battlefieldsHandler;
|
||||
}
|
||||
|
||||
const ArtifactService * CGameInfo::artifacts() const
|
||||
{
|
||||
return globalServices->artifacts();
|
||||
}
|
||||
|
||||
const BattleFieldService * CGameInfo::battlefields() const
|
||||
{
|
||||
return globalServices->battlefields();
|
||||
}
|
||||
|
||||
const CreatureService * CGameInfo::creatures() const
|
||||
{
|
||||
return globalServices->creatures();
|
||||
}
|
||||
|
||||
const FactionService * CGameInfo::factions() const
|
||||
{
|
||||
return globalServices->factions();
|
||||
}
|
||||
|
||||
const HeroClassService * CGameInfo::heroClasses() const
|
||||
{
|
||||
return globalServices->heroClasses();
|
||||
}
|
||||
|
||||
const HeroTypeService * CGameInfo::heroTypes() const
|
||||
{
|
||||
return globalServices->heroTypes();
|
||||
}
|
||||
|
||||
const scripting::Service * CGameInfo::scripts() const
|
||||
{
|
||||
return globalServices->scripts();
|
||||
}
|
||||
|
||||
const spells::Service * CGameInfo::spells() const
|
||||
{
|
||||
return globalServices->spells();
|
||||
}
|
||||
|
||||
const SkillService * CGameInfo::skills() const
|
||||
{
|
||||
return globalServices->skills();
|
||||
}
|
||||
|
||||
const ObstacleService * CGameInfo::obstacles() const
|
||||
{
|
||||
return globalServices->obstacles();
|
||||
}
|
||||
|
||||
|
||||
void CGameInfo::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
|
||||
{
|
||||
logGlobal->error("CGameInfo::updateEntity call is not expected.");
|
||||
}
|
||||
|
||||
spells::effects::Registry * CGameInfo::spellEffects()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const spells::effects::Registry * CGameInfo::spellEffects() const
|
||||
{
|
||||
return globalServices->spellEffects();
|
||||
}
|
93
mapeditor/CGameInfo.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* CGameInfo.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 <vcmi/Services.h>
|
||||
|
||||
#include "../lib/ConstTransitivePtr.h"
|
||||
|
||||
class CModHandler;
|
||||
class CMapHandler;
|
||||
class CHeroHandler;
|
||||
class CCreatureHandler;
|
||||
class CSpellHandler;
|
||||
class CSkillHandler;
|
||||
class CBuildingHandler;
|
||||
class CObjectHandler;
|
||||
class CSoundHandler;
|
||||
class CMusicHandler;
|
||||
class CObjectClassesHandler;
|
||||
class CTownHandler;
|
||||
class CGeneralTextHandler;
|
||||
class CConsoleHandler;
|
||||
class CCursorHandler;
|
||||
class CGameState;
|
||||
class IMainVideoPlayer;
|
||||
class CServerHandler;
|
||||
class BattleFieldHandler;
|
||||
class ObstacleHandler;
|
||||
|
||||
class CMap;
|
||||
|
||||
|
||||
//a class for non-mechanical client GUI classes
|
||||
class CClientState
|
||||
{
|
||||
public:
|
||||
CSoundHandler * soundh;
|
||||
CMusicHandler * musich;
|
||||
CConsoleHandler * consoleh;
|
||||
CCursorHandler * curh;
|
||||
IMainVideoPlayer * videoh;
|
||||
};
|
||||
extern CClientState * CCS;
|
||||
|
||||
/// CGameInfo class
|
||||
/// for allowing different functions for accessing game informations
|
||||
class CGameInfo : public Services
|
||||
{
|
||||
public:
|
||||
const ArtifactService * artifacts() const override;
|
||||
const CreatureService * creatures() const override;
|
||||
const FactionService * factions() const override;
|
||||
const HeroClassService * heroClasses() const override;
|
||||
const HeroTypeService * heroTypes() const override;
|
||||
const scripting::Service * scripts() const override;
|
||||
const spells::Service * spells() const override;
|
||||
const SkillService * skills() const override;
|
||||
const ObstacleService * obstacles() const override;
|
||||
const BattleFieldService * battlefields() const override;
|
||||
|
||||
void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
|
||||
|
||||
const spells::effects::Registry * spellEffects() const override;
|
||||
spells::effects::Registry * spellEffects() override;
|
||||
|
||||
|
||||
ConstTransitivePtr<CModHandler> modh; //public?
|
||||
ConstTransitivePtr<BattleFieldHandler> battleFieldHandler;
|
||||
ConstTransitivePtr<CHeroHandler> heroh;
|
||||
ConstTransitivePtr<CCreatureHandler> creh;
|
||||
ConstTransitivePtr<CSpellHandler> spellh;
|
||||
ConstTransitivePtr<CSkillHandler> skillh;
|
||||
ConstTransitivePtr<CObjectHandler> objh;
|
||||
ConstTransitivePtr<CObjectClassesHandler> objtypeh;
|
||||
ConstTransitivePtr<ObstacleHandler> obstacleHandler;
|
||||
CGeneralTextHandler * generaltexth;
|
||||
CMapHandler * mh;
|
||||
CTownHandler * townh;
|
||||
|
||||
void setFromLib();
|
||||
|
||||
CGameInfo();
|
||||
private:
|
||||
const Services * globalServices;
|
||||
};
|
||||
extern const CGameInfo* CGI;
|
139
mapeditor/CMakeLists.txt
Normal file
@@ -0,0 +1,139 @@
|
||||
set(editor_SRCS
|
||||
StdInc.cpp
|
||||
main.cpp
|
||||
launcherdirs.cpp
|
||||
jsonutils.cpp
|
||||
mainwindow.cpp
|
||||
CGameInfo.cpp
|
||||
BitmapHandler.cpp
|
||||
maphandler.cpp
|
||||
Animation.cpp
|
||||
graphics.cpp
|
||||
spoiler.cpp
|
||||
windownewmap.cpp
|
||||
generatorprogress.cpp
|
||||
mapview.cpp
|
||||
radiopushbutton.cpp
|
||||
objectbrowser.cpp
|
||||
mapsettings.cpp
|
||||
playersettings.cpp
|
||||
playerparams.cpp
|
||||
scenelayer.cpp
|
||||
mapcontroller.cpp
|
||||
validator.cpp
|
||||
inspector/inspector.cpp
|
||||
inspector/townbulidingswidget.cpp
|
||||
inspector/armywidget.cpp
|
||||
inspector/messagewidget.cpp
|
||||
inspector/rewardswidget.cpp
|
||||
)
|
||||
|
||||
set(editor_HEADERS
|
||||
StdInc.h
|
||||
launcherdirs.h
|
||||
jsonutils.h
|
||||
mainwindow.h
|
||||
CGameInfo.h
|
||||
BitmapHandler.h
|
||||
maphandler.h
|
||||
Animation.h
|
||||
graphics.h
|
||||
spoiler.h
|
||||
windownewmap.h
|
||||
generatorprogress.h
|
||||
mapview.h
|
||||
radiopushbutton.h
|
||||
objectbrowser.h
|
||||
mapsettings.h
|
||||
playersettings.h
|
||||
playerparams.h
|
||||
scenelayer.h
|
||||
mapcontroller.h
|
||||
validator.h
|
||||
inspector/inspector.h
|
||||
inspector/townbulidingswidget.h
|
||||
inspector/armywidget.h
|
||||
inspector/messagewidget.h
|
||||
inspector/rewardswidget.h
|
||||
)
|
||||
|
||||
set(editor_FORMS
|
||||
mainwindow.ui
|
||||
windownewmap.ui
|
||||
generatorprogress.ui
|
||||
mapsettings.ui
|
||||
playersettings.ui
|
||||
playerparams.ui
|
||||
validator.ui
|
||||
inspector/townbulidingswidget.ui
|
||||
inspector/armywidget.ui
|
||||
inspector/messagewidget.ui
|
||||
inspector/rewardswidget.ui
|
||||
)
|
||||
|
||||
assign_source_group(${editor_SRCS} ${editor_HEADERS} mapeditor.rc)
|
||||
|
||||
# Tell CMake to run moc when necessary:
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
if(POLICY CMP0071)
|
||||
cmake_policy(SET CMP0071 NEW)
|
||||
endif()
|
||||
|
||||
# As moc files are generated in the binary dir, tell CMake
|
||||
# to always look for includes there:
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
if(TARGET Qt6::Core)
|
||||
qt_wrap_ui(editor_UI_HEADERS ${editor_FORMS})
|
||||
else()
|
||||
qt5_wrap_ui(editor_UI_HEADERS ${editor_FORMS})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(editor_ICON mapeditor.rc)
|
||||
endif()
|
||||
|
||||
add_executable(vcmieditor WIN32 ${editor_SRCS} ${editor_HEADERS} ${editor_UI_HEADERS} ${editor_ICON})
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(vcmieditor
|
||||
PROPERTIES
|
||||
OUTPUT_NAME "VCMI_mapeditor"
|
||||
PROJECT_LABEL "VCMI_mapeditor"
|
||||
)
|
||||
|
||||
# FIXME: Can't to get CMP0020 working with Vcpkg and CMake 3.8.2
|
||||
# So far I tried:
|
||||
# - cmake_minimum_required set to 2.8.11 globally and in this file
|
||||
# - cmake_policy in all possible places
|
||||
# - used NO_POLICY_SCOPE to make sure no other parts reset policies
|
||||
# Still nothing worked, warning kept appearing and WinMain didn't link automatically
|
||||
target_link_libraries(vcmieditor Qt${QT_VERSION_MAJOR}::WinMain)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
# This makes Xcode project prettier by moving vcmilauncher_autogen directory into vcmiclient subfolder
|
||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER vcmieditor)
|
||||
endif()
|
||||
|
||||
target_link_libraries(vcmieditor vcmi Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
|
||||
target_include_directories(vcmieditor
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
vcmi_set_output_dir(vcmieditor "")
|
||||
enable_pch(vcmieditor)
|
||||
|
||||
# Copy to build directory for easier debugging
|
||||
add_custom_command(TARGET vcmieditor POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/mapeditor/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/icons
|
||||
)
|
||||
|
||||
install(TARGETS vcmieditor DESTINATION ${BIN_DIR})
|
||||
# copy whole directory
|
||||
install(DIRECTORY icons DESTINATION ${DATA_DIR}/mapeditor)
|
||||
# Install icons and desktop file on Linux
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
install(FILES "vcmilauncher.desktop" DESTINATION share/applications)
|
||||
endif()
|
1
mapeditor/StdInc.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "StdInc.h"
|
32
mapeditor/StdInc.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Global.h"
|
||||
|
||||
#define VCMI_EDITOR_VERSION "0.1"
|
||||
#define VCMI_EDITOR_NAME "VCMI Map Editor"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QStringList>
|
||||
#include <QSet>
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
inline QString pathToQString(const boost::filesystem::path & path)
|
||||
{
|
||||
#ifdef VCMI_WINDOWS
|
||||
return QString::fromStdWString(path.wstring());
|
||||
#else
|
||||
return QString::fromStdString(path.string());
|
||||
#endif
|
||||
}
|
||||
|
||||
inline boost::filesystem::path qstringToPath(const QString & path)
|
||||
{
|
||||
#ifdef VCMI_WINDOWS
|
||||
return boost::filesystem::path(path.toStdWString());
|
||||
#else
|
||||
return boost::filesystem::path(path.toUtf8().data());
|
||||
#endif
|
||||
}
|
38
mapeditor/generatorprogress.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "StdInc.h"
|
||||
#include "generatorprogress.h"
|
||||
#include "ui_generatorprogress.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
GeneratorProgress::GeneratorProgress(Load::Progress & source, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::GeneratorProgress),
|
||||
source(source)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
setWindowFlags(Qt::Window);
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
GeneratorProgress::~GeneratorProgress()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
void GeneratorProgress::update()
|
||||
{
|
||||
while(!source.finished())
|
||||
{
|
||||
int status = float(source.get()) / 2.55f;
|
||||
ui->progressBar->setValue(status);
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
//delete source;
|
||||
close();
|
||||
}
|
26
mapeditor/generatorprogress.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef GENERATORPROGRESS_H
|
||||
#define GENERATORPROGRESS_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "../lib/LoadProgress.h"
|
||||
|
||||
namespace Ui {
|
||||
class GeneratorProgress;
|
||||
}
|
||||
|
||||
class GeneratorProgress : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GeneratorProgress(Load::Progress & source, QWidget *parent = nullptr);
|
||||
~GeneratorProgress();
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
Ui::GeneratorProgress *ui;
|
||||
Load::Progress & source;
|
||||
};
|
||||
|
||||
#endif // GENERATORPROGRESS_H
|
46
mapeditor/generatorprogress.ui
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GeneratorProgress</class>
|
||||
<widget class="QDialog" name="GeneratorProgress">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>60</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Generating map</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
378
mapeditor/graphics.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Graphics.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 "graphics.h"
|
||||
|
||||
#include <vcmi/Entity.h>
|
||||
#include <vcmi/ArtifactService.h>
|
||||
#include <vcmi/CreatureService.h>
|
||||
#include <vcmi/FactionService.h>
|
||||
#include <vcmi/HeroTypeService.h>
|
||||
#include <vcmi/SkillService.h>
|
||||
#include <vcmi/spells/Service.h>
|
||||
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
#include "../lib/filesystem/CBinaryReader.h"
|
||||
#include "Animation.h"
|
||||
#include "../lib/CThreadHelper.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../CCallback.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "BitmapHandler.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/CStopWatch.h"
|
||||
#include "../lib/mapObjects/CObjectClassesHandler.h"
|
||||
#include "../lib/mapObjects/CObjectHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "CGameInfo.h"
|
||||
|
||||
Graphics * graphics = nullptr;
|
||||
|
||||
void Graphics::loadPaletteAndColors()
|
||||
{
|
||||
auto textFile = CResourceHandler::get()->load(ResourceID("DATA/PLAYERS.PAL"))->readAll();
|
||||
std::string pals((char*)textFile.first.get(), textFile.second);
|
||||
|
||||
playerColorPalette.resize(256);
|
||||
playerColors.resize(PlayerColor::PLAYER_LIMIT_I);
|
||||
int startPoint = 24; //beginning byte; used to read
|
||||
for(int i=0; i<256; ++i)
|
||||
{
|
||||
QColor col;
|
||||
col.setRed(pals[startPoint++]);
|
||||
col.setGreen(pals[startPoint++]);
|
||||
col.setBlue(pals[startPoint++]);
|
||||
col.setAlpha(255);
|
||||
startPoint++;
|
||||
playerColorPalette[i] = col.rgba();
|
||||
}
|
||||
|
||||
neutralColorPalette.resize(32);
|
||||
|
||||
auto stream = CResourceHandler::get()->load(ResourceID("config/NEUTRAL.PAL"));
|
||||
CBinaryReader reader(stream.get());
|
||||
|
||||
for(int i=0; i<32; ++i)
|
||||
{
|
||||
QColor col;
|
||||
col.setRed(reader.readUInt8());
|
||||
col.setGreen(reader.readUInt8());
|
||||
col.setBlue(reader.readUInt8());
|
||||
col.setAlpha(255);
|
||||
reader.readUInt8(); // this is "flags" entry, not alpha
|
||||
neutralColorPalette[i] = col.rgba();
|
||||
}
|
||||
|
||||
//colors initialization
|
||||
QColor colors[] = {
|
||||
{0xff,0, 0, 255},
|
||||
{0x31,0x52,0xff,255},
|
||||
{0x9c,0x73,0x52,255},
|
||||
{0x42,0x94,0x29,255},
|
||||
|
||||
{0xff,0x84,0, 255},
|
||||
{0x8c,0x29,0xa5,255},
|
||||
{0x09,0x9c,0xa5,255},
|
||||
{0xc6,0x7b,0x8c,255}};
|
||||
|
||||
for(int i=0;i<8;i++)
|
||||
{
|
||||
playerColors[i] = colors[i].rgba();
|
||||
}
|
||||
//gray
|
||||
neutralColor = qRgba(0x84, 0x84, 0x84, 0xFF);
|
||||
}
|
||||
|
||||
Graphics::Graphics()
|
||||
{
|
||||
#if 0
|
||||
|
||||
std::vector<Task> tasks; //preparing list of graphics to load
|
||||
tasks += std::bind(&Graphics::loadFonts,this);
|
||||
tasks += std::bind(&Graphics::loadPaletteAndColors,this);
|
||||
tasks += std::bind(&Graphics::initializeBattleGraphics,this);
|
||||
tasks += std::bind(&Graphics::loadErmuToPicture,this);
|
||||
tasks += std::bind(&Graphics::initializeImageLists,this);
|
||||
|
||||
CThreadHelper th(&tasks,std::max((ui32)1,boost::thread::hardware_concurrency()));
|
||||
th.run();
|
||||
#else
|
||||
loadPaletteAndColors();
|
||||
initializeImageLists();
|
||||
#endif
|
||||
|
||||
//(!) do not load any CAnimation here
|
||||
}
|
||||
|
||||
Graphics::~Graphics()
|
||||
{
|
||||
}
|
||||
|
||||
void Graphics::load()
|
||||
{
|
||||
loadHeroAnimations();
|
||||
loadHeroFlagAnimations();
|
||||
}
|
||||
|
||||
void Graphics::loadHeroAnimations()
|
||||
{
|
||||
for(auto & elem : CGI->heroh->classes.objects)
|
||||
{
|
||||
for (auto templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->getIndex())->getTemplates())
|
||||
{
|
||||
if (!heroAnimations.count(templ->animationFile))
|
||||
heroAnimations[templ->animationFile] = loadHeroAnimation(templ->animationFile);
|
||||
}
|
||||
}
|
||||
|
||||
boatAnimations[0] = loadHeroAnimation("AB01_.DEF");
|
||||
boatAnimations[1] = loadHeroAnimation("AB02_.DEF");
|
||||
boatAnimations[2] = loadHeroAnimation("AB03_.DEF");
|
||||
|
||||
|
||||
mapObjectAnimations["AB01_.DEF"] = boatAnimations[0];
|
||||
mapObjectAnimations["AB02_.DEF"] = boatAnimations[1];
|
||||
mapObjectAnimations["AB03_.DEF"] = boatAnimations[2];
|
||||
}
|
||||
void Graphics::loadHeroFlagAnimations()
|
||||
{
|
||||
static const std::vector<std::string> HERO_FLAG_ANIMATIONS =
|
||||
{
|
||||
"AF00", "AF01","AF02","AF03",
|
||||
"AF04", "AF05","AF06","AF07"
|
||||
};
|
||||
|
||||
static const std::vector< std::vector<std::string> > BOAT_FLAG_ANIMATIONS =
|
||||
{
|
||||
{
|
||||
"ABF01L", "ABF01G", "ABF01R", "ABF01D",
|
||||
"ABF01B", "ABF01P", "ABF01W", "ABF01K"
|
||||
},
|
||||
{
|
||||
"ABF02L", "ABF02G", "ABF02R", "ABF02D",
|
||||
"ABF02B", "ABF02P", "ABF02W", "ABF02K"
|
||||
},
|
||||
{
|
||||
"ABF03L", "ABF03G", "ABF03R", "ABF03D",
|
||||
"ABF03B", "ABF03P", "ABF03W", "ABF03K"
|
||||
}
|
||||
};
|
||||
|
||||
for(const auto & name : HERO_FLAG_ANIMATIONS)
|
||||
heroFlagAnimations.push_back(loadHeroFlagAnimation(name));
|
||||
|
||||
for(int i = 0; i < BOAT_FLAG_ANIMATIONS.size(); i++)
|
||||
for(const auto & name : BOAT_FLAG_ANIMATIONS[i])
|
||||
boatFlagAnimations[i].push_back(loadHeroFlagAnimation(name));
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> Graphics::loadHeroFlagAnimation(const std::string & name)
|
||||
{
|
||||
//first - group number to be rotated, second - group number after rotation
|
||||
static const std::vector<std::pair<int,int> > rotations =
|
||||
{
|
||||
{6,10}, {7,11}, {8,12}, {1,13},
|
||||
{2,14}, {3,15}
|
||||
};
|
||||
|
||||
std::shared_ptr<Animation> anim = std::make_shared<Animation>(name);
|
||||
anim->preload();
|
||||
|
||||
for(const auto & rotation : rotations)
|
||||
{
|
||||
const int sourceGroup = rotation.first;
|
||||
const int targetGroup = rotation.second;
|
||||
|
||||
anim->createFlippedGroup(sourceGroup, targetGroup);
|
||||
}
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> Graphics::loadHeroAnimation(const std::string &name)
|
||||
{
|
||||
//first - group number to be rotated, second - group number after rotation
|
||||
static const std::vector<std::pair<int,int> > rotations =
|
||||
{
|
||||
{6,10}, {7,11}, {8,12}, {1,13},
|
||||
{2,14}, {3,15}
|
||||
};
|
||||
|
||||
std::shared_ptr<Animation> anim = std::make_shared<Animation>(name);
|
||||
anim->preload();
|
||||
|
||||
|
||||
for(const auto & rotation : rotations)
|
||||
{
|
||||
const int sourceGroup = rotation.first;
|
||||
const int targetGroup = rotation.second;
|
||||
|
||||
anim->createFlippedGroup(sourceGroup, targetGroup);
|
||||
}
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
void Graphics::blueToPlayersAdv(QImage * sur, PlayerColor player)
|
||||
{
|
||||
if(sur->format() == QImage::Format_Indexed8)
|
||||
{
|
||||
auto palette = sur->colorTable();
|
||||
if(player < PlayerColor::PLAYER_LIMIT)
|
||||
{
|
||||
for(int i = 0; i < 32; ++i)
|
||||
palette[224 + i] = playerColorPalette[player.getNum() * 32 + i];
|
||||
}
|
||||
else if(player == PlayerColor::NEUTRAL)
|
||||
{
|
||||
palette = neutralColorPalette;
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Wrong player id in blueToPlayersAdv (%s)!", player.getStr());
|
||||
return;
|
||||
}
|
||||
//FIXME: not all player colored images have player palette at last 32 indexes
|
||||
//NOTE: following code is much more correct but still not perfect (bugged with status bar)
|
||||
sur->setColorTable(palette);
|
||||
|
||||
#if 0
|
||||
|
||||
SDL_Color * bluePalette = playerColorPalette + 32;
|
||||
|
||||
SDL_Palette * oldPalette = sur->format->palette;
|
||||
|
||||
SDL_Palette * newPalette = SDL_AllocPalette(256);
|
||||
|
||||
for(size_t destIndex = 0; destIndex < 256; destIndex++)
|
||||
{
|
||||
SDL_Color old = oldPalette->colors[destIndex];
|
||||
|
||||
bool found = false;
|
||||
|
||||
for(size_t srcIndex = 0; srcIndex < 32; srcIndex++)
|
||||
{
|
||||
if(old.b == bluePalette[srcIndex].b && old.g == bluePalette[srcIndex].g && old.r == bluePalette[srcIndex].r)
|
||||
{
|
||||
found = true;
|
||||
newPalette->colors[destIndex] = palette[srcIndex];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
newPalette->colors[destIndex] = old;
|
||||
}
|
||||
|
||||
SDL_SetSurfacePalette(sur, newPalette);
|
||||
|
||||
SDL_FreePalette(newPalette);
|
||||
|
||||
#endif // 0
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO: implement. H3 method works only for images with palettes.
|
||||
// Add some kind of player-colored overlay?
|
||||
// Or keep palette approach here and replace only colors of specific value(s)
|
||||
// Or just wait for OpenGL support?
|
||||
logGlobal->warn("Image must have palette to be player-colored!");
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> Graphics::getAnimation(const CGObjectInstance* obj)
|
||||
{
|
||||
if(obj->ID == Obj::HERO)
|
||||
return getHeroAnimation(obj->appearance);
|
||||
return getAnimation(obj->appearance);
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> Graphics::getHeroAnimation(const std::shared_ptr<const ObjectTemplate> info)
|
||||
{
|
||||
if(info->animationFile.empty())
|
||||
{
|
||||
logGlobal->warn("Def name for hero (%d,%d) is empty!", info->id, info->subid);
|
||||
return std::shared_ptr<Animation>();
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> ret = loadHeroAnimation(info->animationFile);
|
||||
|
||||
//already loaded
|
||||
if(ret)
|
||||
{
|
||||
ret->preload();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = std::make_shared<Animation>(info->animationFile);
|
||||
heroAnimations[info->animationFile] = ret;
|
||||
|
||||
ret->preload();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> Graphics::getAnimation(const std::shared_ptr<const ObjectTemplate> info)
|
||||
{
|
||||
if(info->animationFile.empty())
|
||||
{
|
||||
logGlobal->warn("Def name for obj (%d,%d) is empty!", info->id, info->subid);
|
||||
return std::shared_ptr<Animation>();
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> ret = mapObjectAnimations[info->animationFile];
|
||||
|
||||
//already loaded
|
||||
if(ret)
|
||||
{
|
||||
ret->preload();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = std::make_shared<Animation>(info->animationFile);
|
||||
mapObjectAnimations[info->animationFile] = ret;
|
||||
|
||||
ret->preload();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Graphics::addImageListEntry(size_t index, const std::string & listName, const std::string & imageName)
|
||||
{
|
||||
if (!imageName.empty())
|
||||
{
|
||||
JsonNode entry;
|
||||
entry["frame"].Integer() = index;
|
||||
entry["file"].String() = imageName;
|
||||
|
||||
imageLists["SPRITES/" + listName]["images"].Vector().push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void Graphics::addImageListEntries(const EntityService * service)
|
||||
{
|
||||
auto cb = std::bind(&Graphics::addImageListEntry, this, _1, _2, _3);
|
||||
|
||||
auto loopCb = [&](const Entity * entity, bool & stop)
|
||||
{
|
||||
entity->registerIcons(cb);
|
||||
};
|
||||
|
||||
service->forEachBase(loopCb);
|
||||
}
|
||||
|
||||
void Graphics::initializeImageLists()
|
||||
{
|
||||
addImageListEntries(CGI->creatures());
|
||||
addImageListEntries(CGI->heroTypes());
|
||||
addImageListEntries(CGI->artifacts());
|
||||
addImageListEntries(CGI->factions());
|
||||
addImageListEntries(CGI->spells());
|
||||
addImageListEntries(CGI->skills());
|
||||
}
|
84
mapeditor/graphics.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Graphics.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 "../lib/GameConstants.h"
|
||||
#include <QImage>
|
||||
|
||||
class CGHeroInstance;
|
||||
class CGTownInstance;
|
||||
class CHeroClass;
|
||||
struct InfoAboutHero;
|
||||
struct InfoAboutTown;
|
||||
class CGObjectInstance;
|
||||
class ObjectTemplate;
|
||||
class Animation;
|
||||
class EntityService;
|
||||
class JsonNode;
|
||||
|
||||
/// Handles fonts, hero images, town images, various graphics
|
||||
class Graphics
|
||||
{
|
||||
void addImageListEntry(size_t index, const std::string & listName, const std::string & imageName);
|
||||
|
||||
void addImageListEntries(const EntityService * service);
|
||||
|
||||
void initializeBattleGraphics();
|
||||
void loadPaletteAndColors();
|
||||
|
||||
void loadHeroAnimations();
|
||||
//loads animation and adds required rotated frames
|
||||
std::shared_ptr<Animation> loadHeroAnimation(const std::string &name);
|
||||
|
||||
void loadHeroFlagAnimations();
|
||||
|
||||
//loads animation and adds required rotated frames
|
||||
std::shared_ptr<Animation> loadHeroFlagAnimation(const std::string &name);
|
||||
|
||||
void loadErmuToPicture();
|
||||
void loadFogOfWar();
|
||||
void loadFonts();
|
||||
void initializeImageLists();
|
||||
|
||||
public:
|
||||
//various graphics
|
||||
QVector<QRgb> playerColors; //array [8]
|
||||
QRgb neutralColor;
|
||||
QVector<QRgb> playerColorPalette; //palette to make interface colors good - array of size [256]
|
||||
QVector<QRgb> neutralColorPalette;
|
||||
|
||||
// [hero class def name] //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
|
||||
std::map< std::string, std::shared_ptr<Animation> > heroAnimations;
|
||||
std::vector< std::shared_ptr<Animation> > heroFlagAnimations;
|
||||
|
||||
// [boat type: 0 .. 2] //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
|
||||
std::array< std::shared_ptr<Animation>, 3> boatAnimations;
|
||||
|
||||
std::array< std::vector<std::shared_ptr<Animation> >, 3> boatFlagAnimations;
|
||||
|
||||
//all other objects (not hero or boat)
|
||||
std::map< std::string, std::shared_ptr<Animation> > mapObjectAnimations;
|
||||
|
||||
std::map<std::string, JsonNode> imageLists;
|
||||
|
||||
//functions
|
||||
Graphics();
|
||||
~Graphics();
|
||||
|
||||
void load();
|
||||
|
||||
void blueToPlayersAdv(QImage * sur, PlayerColor player); //replaces blue interface colour with a color of player
|
||||
|
||||
std::shared_ptr<Animation> getAnimation(const CGObjectInstance * obj);
|
||||
std::shared_ptr<Animation> getAnimation(const std::shared_ptr<const ObjectTemplate> info);
|
||||
std::shared_ptr<Animation> getHeroAnimation(const std::shared_ptr<const ObjectTemplate> info);
|
||||
};
|
||||
|
||||
extern Graphics * graphics;
|
BIN
mapeditor/icons/menu-game.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
mapeditor/icons/menu-mods.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
mapeditor/icons/menu-settings.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
mapeditor/icons/mod-delete.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
mapeditor/icons/mod-disabled.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
mapeditor/icons/mod-download.png
Normal file
After Width: | Height: | Size: 895 B |
BIN
mapeditor/icons/mod-enabled.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
mapeditor/icons/mod-update.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
142
mapeditor/inspector/armywidget.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "armywidget.h"
|
||||
#include "ui_armywidget.h"
|
||||
#include "CCreatureHandler.h"
|
||||
|
||||
|
||||
ArmyWidget::ArmyWidget(CArmedInstance & a, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
army(a),
|
||||
ui(new Ui::ArmyWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
uiCounts[0] = ui->count0; uiSlots[0] = ui->slot0;
|
||||
uiCounts[1] = ui->count1; uiSlots[1] = ui->slot1;
|
||||
uiCounts[2] = ui->count2; uiSlots[2] = ui->slot2;
|
||||
uiCounts[3] = ui->count3; uiSlots[3] = ui->slot3;
|
||||
uiCounts[4] = ui->count4; uiSlots[4] = ui->slot4;
|
||||
uiCounts[5] = ui->count5; uiSlots[5] = ui->slot5;
|
||||
uiCounts[6] = ui->count6; uiSlots[6] = ui->slot6;
|
||||
|
||||
for(int i = 0; i < TOTAL_SLOTS; ++i)
|
||||
{
|
||||
uiCounts[i]->setInputMask("d0000");
|
||||
uiCounts[i]->setText("1");
|
||||
uiSlots[i]->addItem("");
|
||||
uiSlots[i]->setItemData(0, -1);
|
||||
|
||||
for(int c = 0; c < VLC->creh->objects.size(); ++c)
|
||||
{
|
||||
auto creature = VLC->creh->objects[c];
|
||||
uiSlots[i]->insertItem(c + 1, tr(creature->getPluralName().c_str()));
|
||||
uiSlots[i]->setItemData(c + 1, creature->getId().getNum());
|
||||
}
|
||||
}
|
||||
|
||||
ui->formationTight->setChecked(true);
|
||||
}
|
||||
|
||||
int ArmyWidget::searchItemIndex(int slotId, CreatureID creId) const
|
||||
{
|
||||
for(int i = 0; i < uiSlots[slotId]->count(); ++i)
|
||||
{
|
||||
if(creId.getNum() == uiSlots[slotId]->itemData(i).toInt())
|
||||
return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ArmyWidget::obtainData()
|
||||
{
|
||||
for(int i = 0; i < TOTAL_SLOTS; ++i)
|
||||
{
|
||||
if(army.hasStackAtSlot(SlotID(i)))
|
||||
{
|
||||
auto * creature = army.getCreature(SlotID(i));
|
||||
uiSlots[i]->setCurrentIndex(searchItemIndex(i, creature->getId()));
|
||||
uiCounts[i]->setText(QString::number(army.getStackCount(SlotID(i))));
|
||||
}
|
||||
}
|
||||
|
||||
if(army.formation)
|
||||
ui->formationTight->setChecked(true);
|
||||
else
|
||||
ui->formationWide->setChecked(true);
|
||||
}
|
||||
|
||||
bool ArmyWidget::commitChanges()
|
||||
{
|
||||
bool isArmed = false;
|
||||
for(int i = 0; i < TOTAL_SLOTS; ++i)
|
||||
{
|
||||
CreatureID creId(uiSlots[i]->itemData(uiSlots[i]->currentIndex()).toInt());
|
||||
if(creId == -1)
|
||||
{
|
||||
if(army.hasStackAtSlot(SlotID(i)))
|
||||
army.eraseStack(SlotID(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
isArmed = true;
|
||||
int amount = uiCounts[i]->text().toInt();
|
||||
if(amount)
|
||||
{
|
||||
army.setCreature(SlotID(i), creId, amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(army.hasStackAtSlot(SlotID(i)))
|
||||
army.eraseStack(SlotID(i));
|
||||
army.putStack(SlotID(i), new CStackInstance(creId, amount, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
army.setFormation(ui->formationTight->isChecked());
|
||||
return isArmed;
|
||||
}
|
||||
|
||||
ArmyWidget::~ArmyWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ArmyDelegate::ArmyDelegate(CArmedInstance & t): army(t), QStyledItemDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget * ArmyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
return new ArmyWidget(army, parent);
|
||||
}
|
||||
|
||||
void ArmyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
if(auto * ed = qobject_cast<ArmyWidget *>(editor))
|
||||
{
|
||||
ed->obtainData();
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setEditorData(editor, index);
|
||||
}
|
||||
}
|
||||
|
||||
void ArmyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
if(auto * ed = qobject_cast<ArmyWidget *>(editor))
|
||||
{
|
||||
auto isArmed = ed->commitChanges();
|
||||
model->setData(index, "dummy");
|
||||
if(isArmed)
|
||||
model->setData(index, "HAS ARMY");
|
||||
else
|
||||
model->setData(index, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
}
|
49
mapeditor/inspector/armywidget.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef ARMYWIDGET_H
|
||||
#define ARMYWIDGET_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "../lib/mapObjects/CArmedInstance.h"
|
||||
|
||||
const int TOTAL_SLOTS = 7;
|
||||
|
||||
namespace Ui {
|
||||
class ArmyWidget;
|
||||
}
|
||||
|
||||
class ArmyWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ArmyWidget(CArmedInstance &, QWidget *parent = nullptr);
|
||||
~ArmyWidget();
|
||||
|
||||
void obtainData();
|
||||
bool commitChanges();
|
||||
|
||||
private:
|
||||
int searchItemIndex(int slotId, CreatureID creId) const;
|
||||
|
||||
Ui::ArmyWidget *ui;
|
||||
CArmedInstance & army;
|
||||
std::array<QLineEdit*, TOTAL_SLOTS> uiCounts;
|
||||
std::array<QComboBox*, TOTAL_SLOTS> uiSlots;
|
||||
};
|
||||
|
||||
class ArmyDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
ArmyDelegate(CArmedInstance &);
|
||||
|
||||
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
|
||||
private:
|
||||
CArmedInstance & army;
|
||||
};
|
||||
|
||||
#endif // ARMYWIDGET_H
|
298
mapeditor/inspector/armywidget.ui
Normal file
@@ -0,0 +1,298 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ArmyWidget</class>
|
||||
<widget class="QDialog" name="ArmyWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>318</width>
|
||||
<height>314</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>318</width>
|
||||
<height>314</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Army settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="6" column="0">
|
||||
<widget class="QComboBox" name="slot6">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QComboBox" name="slot3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QComboBox" name="slot2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="count6">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="count2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="count1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="count5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QRadioButton" name="formationWide">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Wide formation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="count3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QComboBox" name="slot1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="slot0">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QComboBox" name="slot4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="count4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QComboBox" name="slot5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="count0">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QRadioButton" name="formationTight">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tight formation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
735
mapeditor/inspector/inspector.cpp
Normal file
@@ -0,0 +1,735 @@
|
||||
#include "StdInc.h"
|
||||
#include "inspector.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CRandomGenerator.h"
|
||||
#include "../lib/mapObjects/CObjectClassesHandler.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
|
||||
#include "townbulidingswidget.h"
|
||||
#include "armywidget.h"
|
||||
#include "messagewidget.h"
|
||||
#include "rewardswidget.h"
|
||||
|
||||
//===============IMPLEMENT OBJECT INITIALIZATION FUNCTIONS================
|
||||
Initializer::Initializer(CGObjectInstance * o, const PlayerColor & pl) : defaultPlayer(pl)
|
||||
{
|
||||
logGlobal->info("New object instance initialized");
|
||||
///IMPORTANT! initialize order should be from base objects to derived objects
|
||||
INIT_OBJ_TYPE(CGResource);
|
||||
INIT_OBJ_TYPE(CGArtifact);
|
||||
INIT_OBJ_TYPE(CArmedInstance);
|
||||
INIT_OBJ_TYPE(CGShipyard);
|
||||
INIT_OBJ_TYPE(CGGarrison);
|
||||
INIT_OBJ_TYPE(CGMine);
|
||||
INIT_OBJ_TYPE(CGDwelling);
|
||||
INIT_OBJ_TYPE(CGTownInstance);
|
||||
INIT_OBJ_TYPE(CGCreature);
|
||||
INIT_OBJ_TYPE(CGHeroInstance);
|
||||
INIT_OBJ_TYPE(CGSignBottle);
|
||||
INIT_OBJ_TYPE(CGLighthouse);
|
||||
//INIT_OBJ_TYPE(CGPandoraBox);
|
||||
}
|
||||
|
||||
bool stringToBool(const QString & s)
|
||||
{
|
||||
if(s == "TRUE")
|
||||
return true;
|
||||
//if(s == "FALSE")
|
||||
return false;
|
||||
}
|
||||
|
||||
void Initializer::initialize(CArmedInstance * o)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGSignBottle * o)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGCreature * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->character = CGCreature::Character::HOSTILE;
|
||||
o->putStack(SlotID(0), new CStackInstance(CreatureID(o->subID), 0, false));
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGDwelling * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->tempOwner = defaultPlayer;
|
||||
|
||||
switch(o->ID)
|
||||
{
|
||||
case Obj::RANDOM_DWELLING:
|
||||
case Obj::RANDOM_DWELLING_LVL:
|
||||
case Obj::RANDOM_DWELLING_FACTION:
|
||||
o->initRandomObjectInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGGarrison * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->tempOwner = defaultPlayer;
|
||||
o->removableUnits = true;
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGShipyard * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->tempOwner = defaultPlayer;
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGLighthouse * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->tempOwner = defaultPlayer;
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGHeroInstance * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->tempOwner = defaultPlayer;
|
||||
if(o->ID == Obj::HERO)
|
||||
{
|
||||
for(auto t : VLC->heroh->objects)
|
||||
{
|
||||
if(t->heroClass == VLC->heroh->classes.objects[o->subID].get())
|
||||
{
|
||||
o->type = VLC->heroh->objects[o->subID];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!o->type)
|
||||
o->type = VLC->heroh->objects.at(o->subID);
|
||||
|
||||
o->name = o->type->getName();
|
||||
o->sex = o->type->sex;
|
||||
o->biography = o->type->biography;
|
||||
o->portrait = o->type->imageIndex;
|
||||
o->randomizeArmy(o->type->heroClass->faction);
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGTownInstance * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
const std::vector<std::string> castleLevels{"village", "fort", "citadel", "castle", "capitol"};
|
||||
int lvl = vstd::find_pos(castleLevels, o->appearance->stringID);
|
||||
o->builtBuildings.insert(BuildingID::DEFAULT);
|
||||
if(lvl > -1) o->builtBuildings.insert(BuildingID::TAVERN);
|
||||
if(lvl > 0) o->builtBuildings.insert(BuildingID::FORT);
|
||||
if(lvl > 1) o->builtBuildings.insert(BuildingID::CITADEL);
|
||||
if(lvl > 2) o->builtBuildings.insert(BuildingID::CASTLE);
|
||||
if(lvl > 3) o->builtBuildings.insert(BuildingID::CAPITOL);
|
||||
|
||||
for(auto spell : VLC->spellh->objects) //add all regular spells to town
|
||||
{
|
||||
if(!spell->isSpecial() && !spell->isCreatureAbility())
|
||||
o->possibleSpells.push_back(spell->id);
|
||||
}
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGArtifact * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(o->ID == Obj::SPELL_SCROLL)
|
||||
{
|
||||
std::vector<SpellID> out;
|
||||
for(auto spell : VLC->spellh->objects) //spellh size appears to be greater (?)
|
||||
{
|
||||
//if(map->isAllowedSpell(spell->id))
|
||||
{
|
||||
out.push_back(spell->id);
|
||||
}
|
||||
}
|
||||
auto a = CArtifactInstance::createScroll(*RandomGeneratorUtil::nextItem(out, CRandomGenerator::getDefault()));
|
||||
o->storedArtifact = a;
|
||||
}
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGMine * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->tempOwner = defaultPlayer;
|
||||
o->producedResource = Res::ERes(o->subID);
|
||||
o->producedQuantity = o->defaultResProduction();
|
||||
}
|
||||
|
||||
void Initializer::initialize(CGResource * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
o->amount = CGResource::RANDOM_AMOUNT;
|
||||
}
|
||||
|
||||
//===============IMPLEMENT PROPERTIES SETUP===============================
|
||||
void Inspector::updateProperties(CArmedInstance * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
auto * delegate = new ArmyDelegate(*o);
|
||||
addProperty("Army", PropertyEditorPlaceholder(), delegate, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGDwelling * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Owner", o->tempOwner, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGLighthouse * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Owner", o->tempOwner, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGGarrison * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Owner", o->tempOwner, false);
|
||||
addProperty("Removable units", o->removableUnits, InspectorDelegate::boolDelegate(), false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGShipyard * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Owner", o->tempOwner, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGHeroInstance * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Owner", o->tempOwner, o->ID == Obj::PRISON); //field is not editable for prison
|
||||
addProperty<int>("Experience", o->exp, false);
|
||||
addProperty("Hero class", o->type->heroClass->getName(), true);
|
||||
|
||||
{
|
||||
auto * delegate = new InspectorDelegate;
|
||||
delegate->options << "MALE" << "FEMALE";
|
||||
addProperty<std::string>("Sex", (o->sex ? "FEMALE" : "MALE"), delegate , false);
|
||||
}
|
||||
addProperty("Name", o->name, false);
|
||||
addProperty("Biography", o->biography, new MessageDelegate, false);
|
||||
addProperty("Portrait", o->portrait, false);
|
||||
|
||||
{
|
||||
auto * delegate = new InspectorDelegate;
|
||||
for(int i = 0; i < VLC->heroh->objects.size(); ++i)
|
||||
{
|
||||
if(map->allowedHeroes.at(i))
|
||||
{
|
||||
if(o->ID == Obj::PRISON || (o->type && VLC->heroh->objects[i]->heroClass->getIndex() == o->type->heroClass->getIndex()))
|
||||
delegate->options << QObject::tr(VLC->heroh->objects[i]->getName().c_str());
|
||||
}
|
||||
}
|
||||
addProperty("Hero type", o->type->getName(), delegate, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGTownInstance * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Town name", o->name, false);
|
||||
|
||||
auto * delegate = new TownBuildingsDelegate(*o);
|
||||
addProperty("Buildings", PropertyEditorPlaceholder(), delegate, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGArtifact * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Message", o->message, false);
|
||||
|
||||
CArtifactInstance * instance = o->storedArtifact;
|
||||
if(instance)
|
||||
{
|
||||
SpellID spellId = instance->getGivenSpellID();
|
||||
if(spellId != -1)
|
||||
{
|
||||
auto * delegate = new InspectorDelegate;
|
||||
for(auto spell : VLC->spellh->objects)
|
||||
{
|
||||
//if(map->isAllowedSpell(spell->id))
|
||||
delegate->options << QObject::tr(spell->name.c_str());
|
||||
}
|
||||
addProperty("Spell", VLC->spellh->objects[spellId]->name, delegate, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGMine * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Owner", o->tempOwner, false);
|
||||
addProperty("Resource", o->producedResource);
|
||||
addProperty("Productivity", o->producedQuantity, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGResource * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Amount", o->amount, false);
|
||||
addProperty("Message", o->message, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGSignBottle * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Message", o->message, new MessageDelegate, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGCreature * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Message", o->message, false);
|
||||
{
|
||||
auto * delegate = new InspectorDelegate;
|
||||
delegate->options << "COMPLIANT" << "FRIENDLY" << "AGRESSIVE" << "HOSTILE" << "SAVAGE";
|
||||
addProperty<CGCreature::Character>("Character", (CGCreature::Character)o->character, delegate, false);
|
||||
}
|
||||
addProperty("Never flees", o->neverFlees, InspectorDelegate::boolDelegate(), false);
|
||||
addProperty("Not growing", o->notGrowingTeam, InspectorDelegate::boolDelegate(), false);
|
||||
addProperty("Artifact reward", o->gainedArtifact); //TODO: implement in setProperty
|
||||
addProperty("Army", PropertyEditorPlaceholder(), true);
|
||||
addProperty("Amount", o->stacks[SlotID(0)]->count, false);
|
||||
//addProperty("Resources reward", o->resources); //TODO: implement in setProperty
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGPandoraBox * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
auto * delegate = new RewardsPandoraDelegate(*map, *o);
|
||||
addProperty("Reward", PropertyEditorPlaceholder(), delegate, false);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGEvent * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
addProperty("Remove after", o->removeAfterVisit, InspectorDelegate::boolDelegate(), false);
|
||||
addProperty("Human trigger", o->humanActivate, InspectorDelegate::boolDelegate(), false);
|
||||
addProperty("Cpu trigger", o->computerActivate, InspectorDelegate::boolDelegate(), false);
|
||||
//ui8 availableFor; //players whom this event is available for
|
||||
}
|
||||
|
||||
|
||||
void Inspector::updateProperties()
|
||||
{
|
||||
if(!obj)
|
||||
return;
|
||||
table->setRowCount(0); //cleanup table
|
||||
|
||||
addProperty("Indentifier", obj);
|
||||
addProperty("ID", obj->ID.getNum());
|
||||
addProperty("SubID", obj->subID);
|
||||
addProperty("InstanceName", obj->instanceName);
|
||||
addProperty("TypeName", obj->typeName);
|
||||
addProperty("SubTypeName", obj->subTypeName);
|
||||
|
||||
if(!dynamic_cast<CGHeroInstance*>(obj))
|
||||
{
|
||||
auto factory = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID);
|
||||
addProperty("IsStatic", factory->isStaticObject());
|
||||
}
|
||||
|
||||
auto * delegate = new InspectorDelegate();
|
||||
delegate->options << "NEUTRAL";
|
||||
for(int p = 0; p < map->players.size(); ++p)
|
||||
if(map->players[p].canAnyonePlay())
|
||||
delegate->options << QString("PLAYER %1").arg(p);
|
||||
addProperty("Owner", obj->tempOwner, delegate, true);
|
||||
|
||||
UPDATE_OBJ_PROPERTIES(CArmedInstance);
|
||||
UPDATE_OBJ_PROPERTIES(CGResource);
|
||||
UPDATE_OBJ_PROPERTIES(CGArtifact);
|
||||
UPDATE_OBJ_PROPERTIES(CGMine);
|
||||
UPDATE_OBJ_PROPERTIES(CGGarrison);
|
||||
UPDATE_OBJ_PROPERTIES(CGShipyard);
|
||||
UPDATE_OBJ_PROPERTIES(CGDwelling);
|
||||
UPDATE_OBJ_PROPERTIES(CGTownInstance);
|
||||
UPDATE_OBJ_PROPERTIES(CGCreature);
|
||||
UPDATE_OBJ_PROPERTIES(CGHeroInstance);
|
||||
UPDATE_OBJ_PROPERTIES(CGSignBottle);
|
||||
UPDATE_OBJ_PROPERTIES(CGLighthouse);
|
||||
UPDATE_OBJ_PROPERTIES(CGPandoraBox);
|
||||
UPDATE_OBJ_PROPERTIES(CGEvent);
|
||||
|
||||
table->show();
|
||||
}
|
||||
|
||||
//===============IMPLEMENT PROPERTY UPDATE================================
|
||||
void Inspector::setProperty(const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!obj)
|
||||
return;
|
||||
|
||||
if(key == "Owner")
|
||||
{
|
||||
PlayerColor owner(value.toString().mid(6).toInt()); //receiving PLAYER N, N has index 6
|
||||
if(value == "NEUTRAL")
|
||||
owner = PlayerColor::NEUTRAL;
|
||||
if(value == "UNFLAGGABLE")
|
||||
owner = PlayerColor::UNFLAGGABLE;
|
||||
obj->tempOwner = owner;
|
||||
}
|
||||
|
||||
SET_PROPERTIES(CArmedInstance);
|
||||
SET_PROPERTIES(CGTownInstance);
|
||||
SET_PROPERTIES(CGArtifact);
|
||||
SET_PROPERTIES(CGMine);
|
||||
SET_PROPERTIES(CGResource);
|
||||
SET_PROPERTIES(CGDwelling);
|
||||
SET_PROPERTIES(CGGarrison);
|
||||
SET_PROPERTIES(CGCreature);
|
||||
SET_PROPERTIES(CGHeroInstance);
|
||||
SET_PROPERTIES(CGShipyard);
|
||||
SET_PROPERTIES(CGSignBottle);
|
||||
SET_PROPERTIES(CGLighthouse);
|
||||
SET_PROPERTIES(CGPandoraBox);
|
||||
SET_PROPERTIES(CGEvent);
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CArmedInstance * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGLighthouse * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGPandoraBox * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGEvent * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if("Remove after")
|
||||
o->removeAfterVisit = stringToBool(value.toString());
|
||||
|
||||
if("Human trigger")
|
||||
o->humanActivate = stringToBool(value.toString());
|
||||
|
||||
if("Cpu trigger")
|
||||
o->computerActivate = stringToBool(value.toString());
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGTownInstance * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Town name")
|
||||
o->name = value.toString().toStdString();
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGSignBottle * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Message")
|
||||
o->message = value.toString().toStdString();
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGMine * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Productivity")
|
||||
o->producedQuantity = value.toString().toInt();
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGArtifact * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Message")
|
||||
o->message = value.toString().toStdString();
|
||||
|
||||
if(o->storedArtifact && key == "Spell")
|
||||
{
|
||||
for(auto spell : VLC->spellh->objects)
|
||||
{
|
||||
if(spell->name == value.toString().toStdString())
|
||||
{
|
||||
o->storedArtifact = CArtifactInstance::createScroll(spell->getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGDwelling * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGGarrison * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Removable units")
|
||||
o->removableUnits = stringToBool(value.toString());
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGHeroInstance * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Sex")
|
||||
o->sex = value.toString() == "MALE" ? 0 : 1;
|
||||
|
||||
if(key == "Name")
|
||||
o->name = value.toString().toStdString();
|
||||
|
||||
if(key == "Hero type")
|
||||
{
|
||||
for(auto t : VLC->heroh->objects)
|
||||
{
|
||||
if(t->getName() == value.toString().toStdString())
|
||||
o->type = t.get();
|
||||
}
|
||||
o->name = o->type->getName();
|
||||
o->sex = o->type->sex;
|
||||
o->biography = o->type->biography;
|
||||
o->portrait = o->type->imageIndex;
|
||||
o->randomizeArmy(o->type->heroClass->faction);
|
||||
updateProperties(); //updating other properties after change
|
||||
}
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGShipyard * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGResource * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Amount")
|
||||
o->amount = value.toString().toInt();
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGCreature * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Message")
|
||||
o->message = value.toString().toStdString();
|
||||
if(key == "Character")
|
||||
{
|
||||
//COMPLIANT = 0, FRIENDLY = 1, AGRESSIVE = 2, HOSTILE = 3, SAVAGE = 4
|
||||
if(value == "COMPLIANT")
|
||||
o->character = CGCreature::Character::COMPLIANT;
|
||||
if(value == "FRIENDLY")
|
||||
o->character = CGCreature::Character::FRIENDLY;
|
||||
if(value == "AGRESSIVE")
|
||||
o->character = CGCreature::Character::AGRESSIVE;
|
||||
if(value == "HOSTILE")
|
||||
o->character = CGCreature::Character::HOSTILE;
|
||||
if(value == "SAVAGE")
|
||||
o->character = CGCreature::Character::SAVAGE;
|
||||
}
|
||||
if(key == "Never flees")
|
||||
o->neverFlees = stringToBool(value.toString());
|
||||
if(key == "Not growing")
|
||||
o->notGrowingTeam = stringToBool(value.toString());
|
||||
if(key == "Amount")
|
||||
o->stacks[SlotID(0)]->count = value.toString().toInt();
|
||||
}
|
||||
|
||||
|
||||
//===============IMPLEMENT PROPERTY VALUE TYPE============================
|
||||
QTableWidgetItem * Inspector::addProperty(CGObjectInstance * value)
|
||||
{
|
||||
using NumericPointer = unsigned long long;
|
||||
static_assert(sizeof(CGObjectInstance *) == sizeof(NumericPointer),
|
||||
"Compilied for 64 bit arcitecture. Use NumericPointer = unsigned int");
|
||||
return new QTableWidgetItem(QString::number(reinterpret_cast<NumericPointer>(value)));
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(Inspector::PropertyEditorPlaceholder value)
|
||||
{
|
||||
auto item = new QTableWidgetItem("");
|
||||
item->setData(Qt::UserRole, QString("PropertyEditor"));
|
||||
return item;
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(unsigned int value)
|
||||
{
|
||||
return new QTableWidgetItem(QString::number(value));
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(int value)
|
||||
{
|
||||
return new QTableWidgetItem(QString::number(value));
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(bool value)
|
||||
{
|
||||
return new QTableWidgetItem(value ? "TRUE" : "FALSE");
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(const std::string & value)
|
||||
{
|
||||
return addProperty(QString::fromStdString(value));
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(const QString & value)
|
||||
{
|
||||
return new QTableWidgetItem(value);
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(const int3 & value)
|
||||
{
|
||||
return new QTableWidgetItem(QString("(%1, %2, %3)").arg(value.x, value.y, value.z));
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(const PlayerColor & value)
|
||||
{
|
||||
auto str = QString("PLAYER %1").arg(value.getNum());
|
||||
if(value == PlayerColor::NEUTRAL)
|
||||
str = "NEUTRAL";
|
||||
if(value == PlayerColor::UNFLAGGABLE)
|
||||
str = "UNFLAGGABLE";
|
||||
return new QTableWidgetItem(str);
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(const Res::ERes & value)
|
||||
{
|
||||
QString str;
|
||||
switch (value) {
|
||||
case Res::ERes::WOOD:
|
||||
str = "WOOD";
|
||||
break;
|
||||
case Res::ERes::ORE:
|
||||
str = "ORE";
|
||||
break;
|
||||
case Res::ERes::SULFUR:
|
||||
str = "SULFUR";
|
||||
break;
|
||||
case Res::ERes::GEMS:
|
||||
str = "GEMS";
|
||||
break;
|
||||
case Res::ERes::MERCURY:
|
||||
str = "MERCURY";
|
||||
break;
|
||||
case Res::ERes::CRYSTAL:
|
||||
str = "CRYSTAL";
|
||||
break;
|
||||
case Res::ERes::GOLD:
|
||||
str = "GOLD";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return new QTableWidgetItem(str);
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(CGCreature::Character value)
|
||||
{
|
||||
QString str;
|
||||
switch (value) {
|
||||
case CGCreature::Character::COMPLIANT:
|
||||
str = "COMPLIANT";
|
||||
break;
|
||||
case CGCreature::Character::FRIENDLY:
|
||||
str = "FRIENDLY";
|
||||
break;
|
||||
case CGCreature::Character::AGRESSIVE:
|
||||
str = "AGRESSIVE";
|
||||
break;
|
||||
case CGCreature::Character::HOSTILE:
|
||||
str = "HOSTILE";
|
||||
break;
|
||||
case CGCreature::Character::SAVAGE:
|
||||
str = "SAVAGE";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return new QTableWidgetItem(str);
|
||||
}
|
||||
|
||||
//========================================================================
|
||||
|
||||
Inspector::Inspector(CMap * m, CGObjectInstance * o, QTableWidget * t): obj(o), table(t), map(m)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Delegates
|
||||
*/
|
||||
|
||||
InspectorDelegate * InspectorDelegate::boolDelegate()
|
||||
{
|
||||
auto * d = new InspectorDelegate;
|
||||
d->options << "TRUE" << "FALSE";
|
||||
return d;
|
||||
}
|
||||
|
||||
QWidget * InspectorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
return new QComboBox(parent);
|
||||
}
|
||||
|
||||
void InspectorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
if(QComboBox *ed = qobject_cast<QComboBox *>(editor))
|
||||
{
|
||||
ed->addItems(options);
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setEditorData(editor, index);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
if(QComboBox *ed = qobject_cast<QComboBox *>(editor))
|
||||
{
|
||||
if(!options.isEmpty())
|
||||
{
|
||||
QMap<int, QVariant> data;
|
||||
data[0] = options[ed->currentIndex()];
|
||||
model->setItemData(index, data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
}
|
||||
|
154
mapeditor/inspector/inspector.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#ifndef INSPECTOR_H
|
||||
#define INSPECTOR_H
|
||||
|
||||
#include <QTableWidget>
|
||||
#include <QTableWidgetItem>
|
||||
#include <QStyledItemDelegate>
|
||||
#include "../lib/int3.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/mapObjects/MapObjects.h"
|
||||
#include "../lib/ResourceSet.h"
|
||||
|
||||
#define DECLARE_OBJ_TYPE(x) void initialize(x*);
|
||||
#define DECLARE_OBJ_PROPERTY_METHODS(x) \
|
||||
void updateProperties(x*); \
|
||||
void setProperty(x*, const QString &, const QVariant &);
|
||||
|
||||
#define INIT_OBJ_TYPE(x) initialize(dynamic_cast<x*>(o))
|
||||
#define UPDATE_OBJ_PROPERTIES(x) updateProperties(dynamic_cast<x*>(obj))
|
||||
#define SET_PROPERTIES(x) setProperty(dynamic_cast<x*>(obj), key, value)
|
||||
|
||||
|
||||
class Initializer
|
||||
{
|
||||
public:
|
||||
//===============DECLARE MAP OBJECTS======================================
|
||||
DECLARE_OBJ_TYPE(CArmedInstance);
|
||||
DECLARE_OBJ_TYPE(CGShipyard);
|
||||
DECLARE_OBJ_TYPE(CGTownInstance);
|
||||
DECLARE_OBJ_TYPE(CGArtifact);
|
||||
DECLARE_OBJ_TYPE(CGMine);
|
||||
DECLARE_OBJ_TYPE(CGResource);
|
||||
DECLARE_OBJ_TYPE(CGDwelling);
|
||||
DECLARE_OBJ_TYPE(CGGarrison);
|
||||
DECLARE_OBJ_TYPE(CGHeroInstance);
|
||||
DECLARE_OBJ_TYPE(CGCreature);
|
||||
DECLARE_OBJ_TYPE(CGSignBottle);
|
||||
DECLARE_OBJ_TYPE(CGLighthouse);
|
||||
//DECLARE_OBJ_TYPE(CGEvent);
|
||||
//DECLARE_OBJ_TYPE(CGPandoraBox);
|
||||
|
||||
|
||||
Initializer(CGObjectInstance *, const PlayerColor &);
|
||||
|
||||
private:
|
||||
PlayerColor defaultPlayer;
|
||||
};
|
||||
|
||||
class Inspector
|
||||
{
|
||||
protected:
|
||||
struct PropertyEditorPlaceholder {};
|
||||
|
||||
//===============DECLARE PROPERTIES SETUP=================================
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CArmedInstance);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGTownInstance);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGShipyard);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGArtifact);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGMine);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGResource);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGDwelling);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGGarrison);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGHeroInstance);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGCreature);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGSignBottle);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGLighthouse);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGPandoraBox);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGEvent);
|
||||
|
||||
//===============DECLARE PROPERTY VALUE TYPE==============================
|
||||
QTableWidgetItem * addProperty(unsigned int value);
|
||||
QTableWidgetItem * addProperty(int value);
|
||||
QTableWidgetItem * addProperty(const std::string & value);
|
||||
QTableWidgetItem * addProperty(const QString & value);
|
||||
QTableWidgetItem * addProperty(const int3 & value);
|
||||
QTableWidgetItem * addProperty(const PlayerColor & value);
|
||||
QTableWidgetItem * addProperty(const Res::ERes & value);
|
||||
QTableWidgetItem * addProperty(bool value);
|
||||
QTableWidgetItem * addProperty(CGObjectInstance * value);
|
||||
QTableWidgetItem * addProperty(CGCreature::Character value);
|
||||
QTableWidgetItem * addProperty(PropertyEditorPlaceholder value);
|
||||
|
||||
//===============END OF DECLARATION=======================================
|
||||
|
||||
public:
|
||||
Inspector(CMap *, CGObjectInstance *, QTableWidget *);
|
||||
|
||||
void setProperty(const QString & key, const QVariant & value);
|
||||
|
||||
void updateProperties();
|
||||
|
||||
protected:
|
||||
|
||||
template<class T>
|
||||
void addProperty(const QString & key, const T & value, QAbstractItemDelegate * delegate, bool restricted)
|
||||
{
|
||||
auto * itemValue = addProperty(value);
|
||||
if(restricted)
|
||||
itemValue->setFlags(Qt::NoItemFlags);
|
||||
|
||||
QTableWidgetItem * itemKey = nullptr;
|
||||
if(keyItems.contains(key))
|
||||
{
|
||||
itemKey = keyItems[key];
|
||||
table->setItem(table->row(itemKey), 1, itemValue);
|
||||
if(delegate)
|
||||
table->setItemDelegateForRow(table->row(itemKey), delegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemKey = new QTableWidgetItem(key);
|
||||
itemKey->setFlags(Qt::NoItemFlags);
|
||||
keyItems[key] = itemKey;
|
||||
|
||||
table->setRowCount(row + 1);
|
||||
table->setItem(row, 0, itemKey);
|
||||
table->setItem(row, 1, itemValue);
|
||||
table->setItemDelegateForRow(row, delegate);
|
||||
++row;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void addProperty(const QString & key, const T & value, bool restricted = true)
|
||||
{
|
||||
addProperty<T>(key, value, nullptr, restricted);
|
||||
}
|
||||
|
||||
protected:
|
||||
int row = 0;
|
||||
QTableWidget * table;
|
||||
CGObjectInstance * obj;
|
||||
QMap<QString, QTableWidgetItem*> keyItems;
|
||||
CMap * map;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class InspectorDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static InspectorDelegate * boolDelegate();
|
||||
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
|
||||
QStringList options;
|
||||
};
|
||||
|
||||
#endif // INSPECTOR_H
|
53
mapeditor/inspector/messagewidget.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "messagewidget.h"
|
||||
#include "ui_messagewidget.h"
|
||||
|
||||
MessageWidget::MessageWidget(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::MessageWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
MessageWidget::~MessageWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MessageWidget::setMessage(const QString & m)
|
||||
{
|
||||
ui->messageEdit->setPlainText(m);
|
||||
}
|
||||
|
||||
QString MessageWidget::getMessage() const
|
||||
{
|
||||
return ui->messageEdit->toPlainText();
|
||||
}
|
||||
|
||||
QWidget * MessageDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
return new MessageWidget(parent);
|
||||
}
|
||||
|
||||
void MessageDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
if(auto *ed = qobject_cast<MessageWidget *>(editor))
|
||||
{
|
||||
ed->setMessage(index.data().toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setEditorData(editor, index);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
if(auto *ed = qobject_cast<MessageWidget *>(editor))
|
||||
{
|
||||
model->setData(index, ed->getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
}
|
37
mapeditor/inspector/messagewidget.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef MESSAGEWIDGET_H
|
||||
#define MESSAGEWIDGET_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class MessageWidget;
|
||||
}
|
||||
|
||||
class MessageWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MessageWidget(QWidget *parent = nullptr);
|
||||
~MessageWidget();
|
||||
|
||||
void setMessage(const QString &);
|
||||
QString getMessage() const;
|
||||
|
||||
private:
|
||||
Ui::MessageWidget *ui;
|
||||
};
|
||||
|
||||
|
||||
class MessageDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
#endif // MESSAGEWIDGET_H
|
33
mapeditor/inspector/messagewidget.ui
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MessageWidget</class>
|
||||
<widget class="QDialog" name="MessageWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>306</width>
|
||||
<height>201</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>306</width>
|
||||
<height>201</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Message</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPlainTextEdit" name="messageEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
329
mapeditor/inspector/rewardswidget.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
#include "rewardswidget.h"
|
||||
#include "ui_rewardswidget.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "../lib/StringConstants.h"
|
||||
|
||||
RewardsWidget::RewardsWidget(const CMap & m, CGPandoraBox & p, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
map(m),
|
||||
pandora(&p),
|
||||
ui(new Ui::RewardsWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
for(auto & type : rewardTypes)
|
||||
ui->rewardType->addItem(QString::fromStdString(type));
|
||||
}
|
||||
|
||||
RewardsWidget::~RewardsWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QList<QString> RewardsWidget::getListForType(int typeId)
|
||||
{
|
||||
assert(typeId < rewardTypes.size());
|
||||
QList<QString> result;
|
||||
|
||||
switch (typeId) {
|
||||
case 4: //resources
|
||||
//to convert string to index WOOD = 0, MERCURY, ORE, SULFUR, CRYSTAL, GEMS, GOLD, MITHRIL,
|
||||
result.append("Wood");
|
||||
result.append("Mercury");
|
||||
result.append("Ore");
|
||||
result.append("Sulfur");
|
||||
result.append("Crystals");
|
||||
result.append("Gems");
|
||||
result.append("Gold");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
for(auto s : PrimarySkill::names)
|
||||
result.append(QString::fromStdString(s));
|
||||
break;
|
||||
|
||||
case 6:
|
||||
//abilities
|
||||
for(int i = 0; i < map.allowedAbilities.size(); ++i)
|
||||
{
|
||||
if(map.allowedAbilities[i])
|
||||
result.append(QString::fromStdString(VLC->skillh->objects.at(i)->getName()));
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
//arts
|
||||
for(int i = 0; i < map.allowedArtifact.size(); ++i)
|
||||
{
|
||||
if(map.allowedArtifact[i])
|
||||
result.append(QString::fromStdString(VLC->arth->objects.at(i)->getName()));
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
//spells
|
||||
for(int i = 0; i < map.allowedSpell.size(); ++i)
|
||||
{
|
||||
if(map.allowedSpell[i])
|
||||
result.append(QString::fromStdString(VLC->spellh->objects.at(i)->getName()));
|
||||
}
|
||||
break;
|
||||
|
||||
case 9:
|
||||
//creatures
|
||||
for(auto creature : VLC->creh->objects)
|
||||
{
|
||||
result.append(QString::fromStdString(creature->getName()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void RewardsWidget::on_rewardType_activated(int index)
|
||||
{
|
||||
ui->rewardList->clear();
|
||||
ui->rewardList->setEnabled(true);
|
||||
assert(index < rewardTypes.size());
|
||||
|
||||
auto l = getListForType(index);
|
||||
if(l.empty())
|
||||
ui->rewardList->setEnabled(false);
|
||||
|
||||
for(auto & s : l)
|
||||
ui->rewardList->addItem(s);
|
||||
}
|
||||
|
||||
void RewardsWidget::obtainData()
|
||||
{
|
||||
if(pandora)
|
||||
{
|
||||
if(pandora->gainedExp > 0)
|
||||
addReward(0, 0, pandora->gainedExp);
|
||||
if(pandora->manaDiff)
|
||||
addReward(1, 0, pandora->manaDiff);
|
||||
if(pandora->moraleDiff)
|
||||
addReward(2, 0, pandora->moraleDiff);
|
||||
if(pandora->luckDiff)
|
||||
addReward(3, 0, pandora->luckDiff);
|
||||
if(pandora->resources.nonZero())
|
||||
{
|
||||
for(Res::ResourceSet::nziterator resiter(pandora->resources); resiter.valid(); ++resiter)
|
||||
addReward(4, resiter->resType, resiter->resVal);
|
||||
}
|
||||
for(int idx = 0; idx < pandora->primskills.size(); ++idx)
|
||||
{
|
||||
if(pandora->primskills[idx])
|
||||
addReward(5, idx, pandora->primskills[idx]);
|
||||
}
|
||||
assert(pandora->abilities.size() == pandora->abilityLevels.size());
|
||||
for(int idx = 0; idx < pandora->abilities.size(); ++idx)
|
||||
{
|
||||
addReward(6, pandora->abilities[idx].getNum(), pandora->abilityLevels[idx]);
|
||||
}
|
||||
for(auto art : pandora->artifacts)
|
||||
{
|
||||
addReward(7, art.getNum(), 1);
|
||||
}
|
||||
for(auto spell : pandora->spells)
|
||||
{
|
||||
addReward(8, spell.getNum(), 1);
|
||||
}
|
||||
for(int i = 0; i < pandora->creatures.Slots().size(); ++i)
|
||||
{
|
||||
if(auto c = pandora->creatures.getCreature(SlotID(i)))
|
||||
addReward(9, c->getId(), pandora->creatures.getStackCount(SlotID(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RewardsWidget::commitChanges()
|
||||
{
|
||||
bool haveRewards = false;
|
||||
if(pandora)
|
||||
{
|
||||
pandora->abilities.clear();
|
||||
pandora->abilityLevels.clear();
|
||||
pandora->primskills.resize(GameConstants::PRIMARY_SKILLS, 0);
|
||||
pandora->resources = Res::ResourceSet();
|
||||
pandora->artifacts.clear();
|
||||
pandora->spells.clear();
|
||||
pandora->creatures.clear();
|
||||
|
||||
for(int row = 0; row < rewards; ++row)
|
||||
{
|
||||
haveRewards = true;
|
||||
int typeId = ui->rewardsTable->item(row, 0)->data(Qt::UserRole).toInt();
|
||||
int listId = ui->rewardsTable->item(row, 1) ? ui->rewardsTable->item(row, 1)->data(Qt::UserRole).toInt() : 0;
|
||||
int amount = ui->rewardsTable->item(row, 2)->data(Qt::UserRole).toInt();
|
||||
switch(typeId)
|
||||
{
|
||||
case 0:
|
||||
pandora->gainedExp = amount;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
pandora->manaDiff = amount;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pandora->moraleDiff = amount;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
pandora->luckDiff = amount;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
pandora->resources.at(listId) = amount;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
pandora->primskills[listId] = amount;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
pandora->abilities.push_back(SecondarySkill(listId));
|
||||
pandora->abilityLevels.push_back(amount);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
pandora->artifacts.push_back(ArtifactID(listId));
|
||||
break;
|
||||
|
||||
case 8:
|
||||
pandora->spells.push_back(SpellID(listId));
|
||||
break;
|
||||
|
||||
case 9:
|
||||
auto slot = pandora->creatures.getFreeSlot();
|
||||
if(slot != SlotID() && amount > 0)
|
||||
pandora->creatures.addToSlot(slot, CreatureID(listId), amount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return haveRewards;
|
||||
}
|
||||
|
||||
void RewardsWidget::on_rewardList_activated(int index)
|
||||
{
|
||||
ui->rewardAmount->setText(QString::number(1));
|
||||
}
|
||||
|
||||
void RewardsWidget::addReward(int typeId, int listId, int amount)
|
||||
{
|
||||
ui->rewardsTable->setRowCount(++rewards);
|
||||
|
||||
auto itemType = new QTableWidgetItem(QString::fromStdString(rewardTypes[typeId]));
|
||||
itemType->setData(Qt::UserRole, typeId);
|
||||
ui->rewardsTable->setItem(rewards - 1, 0, itemType);
|
||||
|
||||
auto l = getListForType(typeId);
|
||||
if(!l.empty())
|
||||
{
|
||||
auto itemCurr = new QTableWidgetItem(getListForType(typeId)[listId]);
|
||||
itemCurr->setData(Qt::UserRole, listId);
|
||||
ui->rewardsTable->setItem(rewards - 1, 1, itemCurr);
|
||||
}
|
||||
|
||||
QString am = QString::number(amount);
|
||||
switch(ui->rewardType->currentIndex())
|
||||
{
|
||||
case 6:
|
||||
if(amount <= 1)
|
||||
am = "Basic";
|
||||
if(amount == 2)
|
||||
am = "Advanced";
|
||||
if(amount >= 3)
|
||||
am = "Expert";
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 8:
|
||||
am = "";
|
||||
amount = 1;
|
||||
break;
|
||||
}
|
||||
auto itemCount = new QTableWidgetItem(am);
|
||||
itemCount->setData(Qt::UserRole, amount);
|
||||
ui->rewardsTable->setItem(rewards - 1, 2, itemCount);
|
||||
}
|
||||
|
||||
|
||||
void RewardsWidget::on_buttonAdd_clicked()
|
||||
{
|
||||
addReward(ui->rewardType->currentIndex(), ui->rewardList->currentIndex(), ui->rewardAmount->text().toInt());
|
||||
}
|
||||
|
||||
|
||||
void RewardsWidget::on_buttonRemove_clicked()
|
||||
{
|
||||
ui->rewardsTable->removeRow(ui->rewardsTable->currentRow());
|
||||
--rewards;
|
||||
}
|
||||
|
||||
|
||||
void RewardsWidget::on_buttonClear_clicked()
|
||||
{
|
||||
ui->rewardsTable->clear();
|
||||
rewards = 0;
|
||||
}
|
||||
|
||||
|
||||
void RewardsWidget::on_rewardsTable_itemSelectionChanged()
|
||||
{
|
||||
/*auto type = ui->rewardsTable->item(ui->rewardsTable->currentRow(), 0);
|
||||
ui->rewardType->setCurrentIndex(type->data(Qt::UserRole).toInt());
|
||||
ui->rewardType->activated(ui->rewardType->currentIndex());
|
||||
|
||||
type = ui->rewardsTable->item(ui->rewardsTable->currentRow(), 1);
|
||||
ui->rewardList->setCurrentIndex(type->data(Qt::UserRole).toInt());
|
||||
ui->rewardList->activated(ui->rewardList->currentIndex());
|
||||
|
||||
type = ui->rewardsTable->item(ui->rewardsTable->currentRow(), 2);
|
||||
ui->rewardAmount->setText(QString::number(type->data(Qt::UserRole).toInt()));*/
|
||||
}
|
||||
|
||||
|
||||
RewardsPandoraDelegate::RewardsPandoraDelegate(const CMap & m, CGPandoraBox & t): map(m), pandora(t), QStyledItemDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget * RewardsPandoraDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
return new RewardsWidget(map, pandora, parent);
|
||||
}
|
||||
|
||||
void RewardsPandoraDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
if(auto * ed = qobject_cast<RewardsWidget *>(editor))
|
||||
{
|
||||
ed->obtainData();
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setEditorData(editor, index);
|
||||
}
|
||||
}
|
||||
|
||||
void RewardsPandoraDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
if(auto * ed = qobject_cast<RewardsWidget *>(editor))
|
||||
{
|
||||
auto isArmed = ed->commitChanges();
|
||||
model->setData(index, "dummy");
|
||||
if(isArmed)
|
||||
model->setData(index, "HAS REWARD");
|
||||
else
|
||||
model->setData(index, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
}
|
79
mapeditor/inspector/rewardswidget.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef REWARDSWIDGET_H
|
||||
#define REWARDSWIDGET_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "../lib/mapObjects/CGPandoraBox.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
|
||||
namespace Ui {
|
||||
class RewardsWidget;
|
||||
}
|
||||
|
||||
/*
|
||||
ui32 gainedExp;
|
||||
si32 manaDiff; //amount of gained / lost mana
|
||||
si32 moraleDiff; //morale modifier
|
||||
si32 luckDiff; //luck modifier
|
||||
TResources resources;//gained / lost resources
|
||||
std::vector<si32> primskills;//gained / lost prim skills
|
||||
std::vector<SecondarySkill> abilities; //gained abilities
|
||||
std::vector<si32> abilityLevels; //levels of gained abilities
|
||||
std::vector<ArtifactID> artifacts; //gained artifacts
|
||||
std::vector<SpellID> spells; //gained spells
|
||||
CCreatureSet creatures; //gained creatures
|
||||
*/
|
||||
|
||||
const std::array<std::string, 10> rewardTypes{"Experience", "Mana", "Morale", "Luck", "Resource", "Primary skill", "Secondary skill", "Artifact", "Spell", "Creature"};
|
||||
|
||||
class RewardsWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RewardsWidget(const CMap &, CGPandoraBox &, QWidget *parent = nullptr);
|
||||
~RewardsWidget();
|
||||
|
||||
void obtainData();
|
||||
bool commitChanges();
|
||||
|
||||
private slots:
|
||||
void on_rewardType_activated(int index);
|
||||
|
||||
void on_rewardList_activated(int index);
|
||||
|
||||
void on_buttonAdd_clicked();
|
||||
|
||||
void on_buttonRemove_clicked();
|
||||
|
||||
void on_buttonClear_clicked();
|
||||
|
||||
void on_rewardsTable_itemSelectionChanged();
|
||||
|
||||
private:
|
||||
void addReward(int typeId, int listId, int amount);
|
||||
QList<QString> getListForType(int typeId);
|
||||
|
||||
Ui::RewardsWidget *ui;
|
||||
CGPandoraBox * pandora;
|
||||
const CMap & map;
|
||||
int rewards = 0;
|
||||
};
|
||||
|
||||
class RewardsPandoraDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
RewardsPandoraDelegate(const CMap &, CGPandoraBox &);
|
||||
|
||||
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
|
||||
private:
|
||||
CGPandoraBox & pandora;
|
||||
const CMap & map;
|
||||
};
|
||||
|
||||
#endif // REWARDSWIDGET_H
|
83
mapeditor/inspector/rewardswidget.ui
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RewardsWidget</class>
|
||||
<widget class="QDialog" name="RewardsWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>645</width>
|
||||
<height>335</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Rewards</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="buttonRemove">
|
||||
<property name="text">
|
||||
<string>Remove selected</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<widget class="QLineEdit" name="rewardAmount">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="buttonClear">
|
||||
<property name="text">
|
||||
<string>Delete all</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="buttonAdd">
|
||||
<property name="text">
|
||||
<string>Add or change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="5">
|
||||
<widget class="QTableWidget" name="rewardsTable">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="3">
|
||||
<widget class="QComboBox" name="rewardList"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QComboBox" name="rewardType"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
243
mapeditor/inspector/townbulidingswidget.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
#include "townbulidingswidget.h"
|
||||
#include "ui_townbulidingswidget.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
|
||||
std::string defaultBuildingIdConversion(BuildingID bId)
|
||||
{
|
||||
switch(bId)
|
||||
{
|
||||
case BuildingID::DEFAULT: return "DEFAULT";
|
||||
case BuildingID::MAGES_GUILD_1: return "MAGES_GUILD_1";
|
||||
case BuildingID::MAGES_GUILD_2: return "MAGES_GUILD_2";
|
||||
case BuildingID::MAGES_GUILD_3: return "MAGES_GUILD_3";
|
||||
case BuildingID::MAGES_GUILD_4: return "MAGES_GUILD_4";
|
||||
case BuildingID::MAGES_GUILD_5: return "MAGES_GUILD_5";
|
||||
case BuildingID::TAVERN: return "TAVERN";
|
||||
case BuildingID::SHIPYARD: return "SHIPYARD";
|
||||
case BuildingID::FORT: return "FORT";
|
||||
case BuildingID::CITADEL: return "CITADEL";
|
||||
case BuildingID::CASTLE: return "CASTLE";
|
||||
case BuildingID::VILLAGE_HALL: return "VILLAGE_HALL";
|
||||
case BuildingID::TOWN_HALL: return "TOWN_HALL";
|
||||
case BuildingID::CITY_HALL: return "CITY_HALL";
|
||||
case BuildingID::CAPITOL: return "CAPITOL";
|
||||
case BuildingID::MARKETPLACE: return "MARKETPLACE";
|
||||
case BuildingID::RESOURCE_SILO: return "RESOURCE_SILO";
|
||||
case BuildingID::BLACKSMITH: return "BLACKSMITH";
|
||||
case BuildingID::SPECIAL_1: return "SPECIAL_1";
|
||||
case BuildingID::SPECIAL_2: return "SPECIAL_2";
|
||||
case BuildingID::SPECIAL_3: return "SPECIAL_3";
|
||||
case BuildingID::SPECIAL_4: return "SPECIAL_4";
|
||||
case BuildingID::HORDE_1: return "HORDE_1";
|
||||
case BuildingID::HORDE_1_UPGR: return "HORDE_1_UPGR";
|
||||
case BuildingID::HORDE_2: return "HORDE_2";
|
||||
case BuildingID::HORDE_2_UPGR: return "HORDE_2_UPGR";
|
||||
case BuildingID::SHIP: return "SHIP";
|
||||
case BuildingID::GRAIL: return "GRAIL";
|
||||
case BuildingID::EXTRA_TOWN_HALL: return "EXTRA_TOWN_HALL";
|
||||
case BuildingID::EXTRA_CITY_HALL: return "EXTRA_CITY_HALL";
|
||||
case BuildingID::EXTRA_CAPITOL: return "EXTRA_CAPITOL";
|
||||
case BuildingID::DWELL_LVL_1: return "DWELL_LVL_1";
|
||||
case BuildingID::DWELL_LVL_2: return "DWELL_LVL_2";
|
||||
case BuildingID::DWELL_LVL_3: return "DWELL_LVL_3";
|
||||
case BuildingID::DWELL_LVL_4: return "DWELL_LVL_4";
|
||||
case BuildingID::DWELL_LVL_5: return "DWELL_LVL_5";
|
||||
case BuildingID::DWELL_LVL_6: return "DWELL_LVL_6";
|
||||
case BuildingID::DWELL_LVL_7: return "DWELL_LVL_7";
|
||||
case BuildingID::DWELL_LVL_1_UP: return "DWELL_LVL_1_UP";
|
||||
case BuildingID::DWELL_LVL_2_UP: return "DWELL_LVL_2_UP";
|
||||
case BuildingID::DWELL_LVL_3_UP: return "DWELL_LVL_3_UP";
|
||||
case BuildingID::DWELL_LVL_4_UP: return "DWELL_LVL_4_UP";
|
||||
case BuildingID::DWELL_LVL_5_UP: return "DWELL_LVL_5_UP";
|
||||
case BuildingID::DWELL_LVL_6_UP: return "DWELL_LVL_6_UP";
|
||||
case BuildingID::DWELL_LVL_7_UP: return "DWELL_LVL_7_UP";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
TownBulidingsWidget::TownBulidingsWidget(CGTownInstance & t, QWidget *parent) :
|
||||
town(t),
|
||||
QDialog(parent),
|
||||
ui(new Ui::TownBulidingsWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->treeView->setModel(&model);
|
||||
//ui->treeView->setColumnCount(3);
|
||||
model.setHorizontalHeaderLabels(QStringList() << QStringLiteral("Type") << QStringLiteral("Enabled") << QStringLiteral("Built"));
|
||||
|
||||
//setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
TownBulidingsWidget::~TownBulidingsWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QStandardItem * TownBulidingsWidget::addBuilding(const CTown & ctown, int bId, std::set<si32> & remaining)
|
||||
{
|
||||
BuildingID buildingId(bId);
|
||||
const CBuilding * building = ctown.buildings.at(buildingId);
|
||||
if(!building)
|
||||
{
|
||||
remaining.erase(bId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QString name = tr(building->Name().c_str());
|
||||
|
||||
if(name.isEmpty())
|
||||
name = QString::fromStdString(defaultBuildingIdConversion(buildingId));
|
||||
|
||||
QList<QStandardItem *> checks;
|
||||
|
||||
checks << new QStandardItem(name);
|
||||
checks.back()->setData(bId, Qt::UserRole);
|
||||
|
||||
checks << new QStandardItem;
|
||||
checks.back()->setCheckable(true);
|
||||
checks.back()->setCheckState(town.forbiddenBuildings.count(buildingId) ? Qt::Unchecked : Qt::Checked);
|
||||
checks.back()->setData(bId, Qt::UserRole);
|
||||
|
||||
checks << new QStandardItem;
|
||||
checks.back()->setCheckable(true);
|
||||
checks.back()->setCheckState(town.builtBuildings.count(buildingId) ? Qt::Checked : Qt::Unchecked);
|
||||
checks.back()->setData(bId, Qt::UserRole);
|
||||
|
||||
if(building->getBase() == buildingId)
|
||||
{
|
||||
model.appendRow(checks);
|
||||
}
|
||||
else
|
||||
{
|
||||
QStandardItem * parent = nullptr;
|
||||
std::vector<QModelIndex> stack;
|
||||
stack.push_back(QModelIndex());
|
||||
while(!parent && !stack.empty())
|
||||
{
|
||||
auto pindex = stack.back();
|
||||
stack.pop_back();
|
||||
for(int i = 0; i < model.rowCount(pindex); ++i)
|
||||
{
|
||||
QModelIndex index = model.index(i, 0, pindex);
|
||||
if(building->upgrade == model.itemFromIndex(index)->data(Qt::UserRole).toInt())
|
||||
{
|
||||
parent = model.itemFromIndex(index);
|
||||
break;
|
||||
}
|
||||
if(model.hasChildren(index))
|
||||
stack.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
if(!parent)
|
||||
parent = addBuilding(ctown, building->upgrade.getNum(), remaining);
|
||||
|
||||
if(!parent)
|
||||
{
|
||||
remaining.erase(bId);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
parent->appendRow(checks);
|
||||
}
|
||||
|
||||
remaining.erase(bId);
|
||||
return checks.front();
|
||||
}
|
||||
|
||||
void TownBulidingsWidget::addBuildings(const CTown & ctown)
|
||||
{
|
||||
auto buildings = ctown.getAllBuildings();
|
||||
while(!buildings.empty())
|
||||
{
|
||||
addBuilding(ctown, *buildings.begin(), buildings);
|
||||
}
|
||||
ui->treeView->resizeColumnToContents(0);
|
||||
ui->treeView->resizeColumnToContents(1);
|
||||
ui->treeView->resizeColumnToContents(2);
|
||||
}
|
||||
|
||||
std::set<BuildingID> TownBulidingsWidget::getForbiddenBuildings()
|
||||
{
|
||||
std::set<BuildingID> result;
|
||||
for(int i = 0; i < model.rowCount(); ++i)
|
||||
{
|
||||
if(auto * item = model.item(i, 1))
|
||||
if(item->checkState() == Qt::Unchecked)
|
||||
result.emplace(item->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::set<BuildingID> TownBulidingsWidget::getBuiltBuildings()
|
||||
{
|
||||
std::set<BuildingID> result;
|
||||
for(int i = 0; i < model.rowCount(); ++i)
|
||||
{
|
||||
if(auto * item = model.item(i, 2))
|
||||
if(item->checkState() == Qt::Checked)
|
||||
result.emplace(item->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TownBulidingsWidget::on_treeView_expanded(const QModelIndex &index)
|
||||
{
|
||||
ui->treeView->resizeColumnToContents(0);
|
||||
}
|
||||
|
||||
void TownBulidingsWidget::on_treeView_collapsed(const QModelIndex &index)
|
||||
{
|
||||
ui->treeView->resizeColumnToContents(0);
|
||||
}
|
||||
|
||||
|
||||
TownBuildingsDelegate::TownBuildingsDelegate(CGTownInstance & t): town(t), QStyledItemDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget * TownBuildingsDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
return new TownBulidingsWidget(town, parent);
|
||||
}
|
||||
|
||||
void TownBuildingsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
if(auto * ed = qobject_cast<TownBulidingsWidget *>(editor))
|
||||
{
|
||||
auto * ctown = town.town;
|
||||
if(!ctown)
|
||||
ctown = VLC->townh->randomTown;
|
||||
if(!ctown)
|
||||
throw std::runtime_error("No Town defined for type selected");
|
||||
|
||||
ed->addBuildings(*ctown);
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setEditorData(editor, index);
|
||||
}
|
||||
}
|
||||
|
||||
void TownBuildingsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
||||
{
|
||||
if(auto * ed = qobject_cast<TownBulidingsWidget *>(editor))
|
||||
{
|
||||
town.forbiddenBuildings = ed->getForbiddenBuildings();
|
||||
town.builtBuildings = ed->getBuiltBuildings();
|
||||
|
||||
auto data = model->itemData(index);
|
||||
model->setData(index, "dummy");
|
||||
model->setItemData(index, data); //dummy change to trigger signal
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
}
|
||||
|
||||
|
53
mapeditor/inspector/townbulidingswidget.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef TOWNBULIDINGSWIDGET_H
|
||||
#define TOWNBULIDINGSWIDGET_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "../lib/mapObjects/CGTownInstance.h"
|
||||
|
||||
namespace Ui {
|
||||
class TownBulidingsWidget;
|
||||
}
|
||||
|
||||
class TownBulidingsWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QStandardItem * addBuilding(const CTown & ctown, int bId, std::set<si32> & remaining);
|
||||
|
||||
public:
|
||||
explicit TownBulidingsWidget(CGTownInstance &, QWidget *parent = nullptr);
|
||||
~TownBulidingsWidget();
|
||||
|
||||
void addBuildings(const CTown & ctown);
|
||||
std::set<BuildingID> getForbiddenBuildings();
|
||||
std::set<BuildingID> getBuiltBuildings();
|
||||
|
||||
private slots:
|
||||
void on_treeView_expanded(const QModelIndex &index);
|
||||
|
||||
void on_treeView_collapsed(const QModelIndex &index);
|
||||
|
||||
private:
|
||||
Ui::TownBulidingsWidget *ui;
|
||||
CGTownInstance & town;
|
||||
mutable QStandardItemModel model;
|
||||
};
|
||||
|
||||
class TownBuildingsDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
TownBuildingsDelegate(CGTownInstance &);
|
||||
|
||||
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
|
||||
|
||||
private:
|
||||
CGTownInstance & town;
|
||||
//std::set<BuildingID>
|
||||
};
|
||||
|
||||
#endif // TOWNBULIDINGSWIDGET_H
|
49
mapeditor/inspector/townbulidingswidget.ui
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TownBulidingsWidget</class>
|
||||
<widget class="QDialog" name="TownBulidingsWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>480</width>
|
||||
<height>280</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>480</width>
|
||||
<height>280</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Buildings</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerCascadingSectionResizes">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
125
mapeditor/jsonutils.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* jsonutils.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 "jsonutils.h"
|
||||
#include "../lib/filesystem/FileStream.h"
|
||||
|
||||
static QVariantMap JsonToMap(const JsonMap & json)
|
||||
{
|
||||
QVariantMap map;
|
||||
for(auto & entry : json)
|
||||
{
|
||||
map.insert(QString::fromUtf8(entry.first.c_str()), JsonUtils::toVariant(entry.second));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static QVariantList JsonToList(const JsonVector & json)
|
||||
{
|
||||
QVariantList list;
|
||||
for(auto & entry : json)
|
||||
{
|
||||
list.push_back(JsonUtils::toVariant(entry));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static JsonVector VariantToList(QVariantList variant)
|
||||
{
|
||||
JsonVector vector;
|
||||
for(auto & entry : variant)
|
||||
{
|
||||
vector.push_back(JsonUtils::toJson(entry));
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
static JsonMap VariantToMap(QVariantMap variant)
|
||||
{
|
||||
JsonMap map;
|
||||
for(auto & entry : variant.toStdMap())
|
||||
{
|
||||
map[entry.first.toUtf8().data()] = JsonUtils::toJson(entry.second);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
namespace JsonUtils
|
||||
{
|
||||
|
||||
QVariant toVariant(const JsonNode & node)
|
||||
{
|
||||
switch(node.getType())
|
||||
{
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_NULL:
|
||||
return QVariant();
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_BOOL:
|
||||
return QVariant(node.Bool());
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_FLOAT:
|
||||
return QVariant(node.Float());
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_STRING:
|
||||
return QVariant(QString::fromUtf8(node.String().c_str()));
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_VECTOR:
|
||||
return JsonToList(node.Vector());
|
||||
break;
|
||||
case JsonNode::JsonType::DATA_STRUCT:
|
||||
return JsonToMap(node.Struct());
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant JsonFromFile(QString filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
file.open(QFile::ReadOnly);
|
||||
auto data = file.readAll();
|
||||
|
||||
if(data.size() == 0)
|
||||
{
|
||||
logGlobal->error("Failed to open file %s", filename.toUtf8().data());
|
||||
return QVariant();
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonNode node(data.data(), data.size());
|
||||
return toVariant(node);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode toJson(QVariant object)
|
||||
{
|
||||
JsonNode ret;
|
||||
|
||||
if(object.canConvert<QVariantMap>())
|
||||
ret.Struct() = VariantToMap(object.toMap());
|
||||
else if(object.canConvert<QVariantList>())
|
||||
ret.Vector() = VariantToList(object.toList());
|
||||
else if(object.userType() == QMetaType::QString)
|
||||
ret.String() = object.toString().toUtf8().data();
|
||||
else if(object.userType() == QMetaType::Bool)
|
||||
ret.Bool() = object.toBool();
|
||||
else if(object.canConvert<double>())
|
||||
ret.Float() = object.toFloat();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JsonToFile(QString filename, QVariant object)
|
||||
{
|
||||
FileStream file(qstringToPath(filename), std::ios::out | std::ios_base::binary);
|
||||
file << toJson(object).toJson();
|
||||
}
|
||||
|
||||
}
|
22
mapeditor/jsonutils.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* jsonutils.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 <QVariant>
|
||||
#include "../lib/JsonNode.h"
|
||||
|
||||
namespace JsonUtils
|
||||
{
|
||||
QVariant toVariant(const JsonNode & node);
|
||||
QVariant JsonFromFile(QString filename);
|
||||
|
||||
JsonNode toJson(QVariant object);
|
||||
void JsonToFile(QString filename, QVariant object);
|
||||
}
|
36
mapeditor/launcherdirs.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* launcherdirs.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 "launcherdirs.h"
|
||||
|
||||
#include "../lib/VCMIDirs.h"
|
||||
|
||||
static CLauncherDirs launcherDirsGlobal;
|
||||
|
||||
CLauncherDirs::CLauncherDirs()
|
||||
{
|
||||
QDir().mkdir(downloadsPath());
|
||||
QDir().mkdir(modsPath());
|
||||
}
|
||||
|
||||
CLauncherDirs & CLauncherDirs::get()
|
||||
{
|
||||
return launcherDirsGlobal;
|
||||
}
|
||||
|
||||
QString CLauncherDirs::downloadsPath()
|
||||
{
|
||||
return pathToQString(VCMIDirs::get().userCachePath() / "downloads");
|
||||
}
|
||||
|
||||
QString CLauncherDirs::modsPath()
|
||||
{
|
||||
return pathToQString(VCMIDirs::get().userDataPath() / "Mods");
|
||||
}
|
22
mapeditor/launcherdirs.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* launcherdirs.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
|
||||
|
||||
/// similar to lib/VCMIDirs, controls where all launcher-related data will be stored
|
||||
class CLauncherDirs
|
||||
{
|
||||
public:
|
||||
CLauncherDirs();
|
||||
|
||||
static CLauncherDirs & get();
|
||||
|
||||
QString downloadsPath();
|
||||
QString modsPath();
|
||||
};
|
19
mapeditor/main.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* main.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 <QApplication>
|
||||
#include "StdInc.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
QApplication vcmieditor(argc, argv);
|
||||
MainWindow mainWindow;
|
||||
return vcmieditor.exec();
|
||||
}
|
1118
mapeditor/mainwindow.cpp
Normal file
148
mapeditor/mainwindow.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QGraphicsScene>
|
||||
#include <QStandardItemModel>
|
||||
#include "mapcontroller.h"
|
||||
#include "../lib/Terrain.h"
|
||||
|
||||
|
||||
class CMap;
|
||||
class ObjectBrowser;
|
||||
class CGObjectInstance;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
const QString teamName = "VCMI Team";
|
||||
const QString appName = "VCMI Map Editor";
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QString mainWindowSizeSetting = "MainWindow/Size";
|
||||
const QString mainWindowPositionSetting = "MainWindow/Position";
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
void initializeMap(bool isNew);
|
||||
|
||||
void saveMap();
|
||||
bool openMap(const QString &);
|
||||
|
||||
MapView * mapView();
|
||||
|
||||
void loadObjectsTree();
|
||||
|
||||
void setStatusMessage(const QString & status);
|
||||
|
||||
int getMapLevel() const {return mapLevel;}
|
||||
|
||||
MapController controller;
|
||||
|
||||
private slots:
|
||||
void on_actionOpen_triggered();
|
||||
|
||||
void on_actionSave_as_triggered();
|
||||
|
||||
void on_actionNew_triggered();
|
||||
|
||||
void on_actionLevel_triggered();
|
||||
|
||||
void on_actionSave_triggered();
|
||||
|
||||
void on_actionErase_triggered();
|
||||
|
||||
void on_actionUndo_triggered();
|
||||
|
||||
void on_actionRedo_triggered();
|
||||
|
||||
void on_actionPass_triggered(bool checked);
|
||||
|
||||
void on_actionGrid_triggered(bool checked);
|
||||
|
||||
void on_toolBrush_clicked(bool checked);
|
||||
|
||||
void on_toolArea_clicked(bool checked);
|
||||
|
||||
void terrainButtonClicked(Terrain terrain);
|
||||
void roadOrRiverButtonClicked(std::string type, bool isRoad);
|
||||
|
||||
void on_toolErase_clicked();
|
||||
|
||||
void on_treeView_activated(const QModelIndex &index);
|
||||
|
||||
void on_terrainFilterCombo_currentTextChanged(const QString &arg1);
|
||||
|
||||
void on_filter_textChanged(const QString &arg1);
|
||||
|
||||
void on_actionFill_triggered();
|
||||
|
||||
void on_toolBrush2_clicked(bool checked);
|
||||
|
||||
void on_toolBrush4_clicked(bool checked);
|
||||
|
||||
void on_inspectorWidget_itemChanged(QTableWidgetItem *item);
|
||||
|
||||
void on_actionMapSettings_triggered();
|
||||
|
||||
void on_actionPlayers_settings_triggered();
|
||||
|
||||
void on_actionValidate_triggered();
|
||||
|
||||
void on_actionUpdate_appearance_triggered();
|
||||
|
||||
void on_actionRecreate_obstacles_triggered();
|
||||
|
||||
void switchDefaultPlayer(const PlayerColor &);
|
||||
|
||||
public slots:
|
||||
|
||||
void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);
|
||||
void loadInspector(CGObjectInstance * obj, bool switchTab);
|
||||
void mapChanged();
|
||||
void enableUndo(bool enable);
|
||||
void enableRedo(bool enable);
|
||||
void onSelectionMade(int level, bool anythingSelected);
|
||||
void onPlayersChanged();
|
||||
|
||||
void displayStatus(const QString& message, int timeout = 2000);
|
||||
|
||||
private:
|
||||
void preparePreview(const QModelIndex &index, bool createNew);
|
||||
void addGroupIntoCatalog(const std::string & groupName, bool staticOnly);
|
||||
void addGroupIntoCatalog(const std::string & groupName, bool useCustomName, bool staticOnly, int ID);
|
||||
|
||||
QAction * getActionPlayer(const PlayerColor &);
|
||||
|
||||
void changeBrushState(int idx);
|
||||
void setTitle();
|
||||
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
bool getAnswerAboutUnsavedChanges();
|
||||
|
||||
void loadUserSettings();
|
||||
void saveUserSettings();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
ObjectBrowser * objectBrowser = nullptr;
|
||||
QGraphicsScene * scenePreview;
|
||||
|
||||
QString filename;
|
||||
bool unsaved = false;
|
||||
|
||||
QStandardItemModel objectsModel;
|
||||
|
||||
int mapLevel = 0;
|
||||
|
||||
std::set<int> catalog;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
1120
mapeditor/mainwindow.ui
Normal file
466
mapeditor/mapcontroller.cpp
Normal file
@@ -0,0 +1,466 @@
|
||||
#include "mapcontroller.h"
|
||||
|
||||
#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"
|
||||
#include "../lib/rmg/ObstaclePlacer.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "mapview.h"
|
||||
#include "scenelayer.h"
|
||||
#include "maphandler.h"
|
||||
#include "mainwindow.h"
|
||||
#include "inspector/inspector.h"
|
||||
|
||||
|
||||
MapController::MapController(MainWindow * m): main(m)
|
||||
{
|
||||
_scenes[0].reset(new MapScene(0));
|
||||
_scenes[1].reset(new MapScene(1));
|
||||
_miniscenes[0].reset(new MinimapScene(0));
|
||||
_miniscenes[1].reset(new MinimapScene(1));
|
||||
connectScenes();
|
||||
}
|
||||
|
||||
void MapController::connectScenes()
|
||||
{
|
||||
for (int level = 0; level <= 1; level++)
|
||||
{
|
||||
//selections for both layers will be handled separately
|
||||
QObject::connect(_scenes[level].get(), &MapScene::selected, [this, level](bool anythingSelected)
|
||||
{
|
||||
main->onSelectionMade(level, anythingSelected);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MapController::~MapController()
|
||||
{
|
||||
}
|
||||
|
||||
const std::unique_ptr<CMap> & MapController::getMapUniquePtr() const
|
||||
{
|
||||
return _map;
|
||||
}
|
||||
|
||||
CMap * MapController::map()
|
||||
{
|
||||
return _map.get();
|
||||
}
|
||||
|
||||
MapHandler * MapController::mapHandler()
|
||||
{
|
||||
return _mapHandler.get();
|
||||
}
|
||||
|
||||
MapScene * MapController::scene(int level)
|
||||
{
|
||||
return _scenes[level].get();
|
||||
}
|
||||
|
||||
MinimapScene * MapController::miniScene(int level)
|
||||
{
|
||||
return _miniscenes[level].get();
|
||||
}
|
||||
|
||||
void MapController::repairMap()
|
||||
{
|
||||
//fix owners for objects
|
||||
for(auto obj : _map->objects)
|
||||
{
|
||||
if(obj->getOwner() == PlayerColor::UNFLAGGABLE)
|
||||
{
|
||||
if(dynamic_cast<CGMine*>(obj.get()) ||
|
||||
dynamic_cast<CGDwelling*>(obj.get()) ||
|
||||
dynamic_cast<CGTownInstance*>(obj.get()) ||
|
||||
dynamic_cast<CGGarrison*>(obj.get()) ||
|
||||
dynamic_cast<CGShipyard*>(obj.get()) ||
|
||||
dynamic_cast<CGHeroInstance*>(obj.get()))
|
||||
obj->tempOwner = PlayerColor::NEUTRAL;
|
||||
}
|
||||
//fix hero instance
|
||||
if(auto * nih = dynamic_cast<CGHeroInstance*>(obj.get()))
|
||||
{
|
||||
auto type = VLC->heroh->objects[nih->subID];
|
||||
|
||||
if(nih->ID == Obj::HERO)
|
||||
nih->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
|
||||
//fix spells
|
||||
if(nih->spellbookContainsSpell(SpellID::PRESET))
|
||||
{
|
||||
nih->removeSpellFromSpellbook(SpellID::PRESET);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto spellID : type->spells)
|
||||
nih->addSpellToSpellbook(spellID);
|
||||
}
|
||||
//fix portrait
|
||||
if(nih->portrait < 0 || nih->portrait == 255)
|
||||
nih->portrait = type->imageIndex;
|
||||
}
|
||||
//fix town instance
|
||||
if(auto * tnh = dynamic_cast<CGTownInstance*>(obj.get()))
|
||||
{
|
||||
if(tnh->getTown())
|
||||
{
|
||||
vstd::erase_if(tnh->builtBuildings, [tnh](BuildingID bid)
|
||||
{
|
||||
return !tnh->getTown()->buildings.count(bid);
|
||||
});
|
||||
vstd::erase_if(tnh->forbiddenBuildings, [tnh](BuildingID bid)
|
||||
{
|
||||
return !tnh->getTown()->buildings.count(bid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//there might be extra skills, arts and spells not imported from map
|
||||
if(VLC->skillh->getDefaultAllowed().size() > map()->allowedAbilities.size())
|
||||
{
|
||||
for(int i = map()->allowedAbilities.size(); i < VLC->skillh->getDefaultAllowed().size(); ++i)
|
||||
map()->allowedAbilities.push_back(false);
|
||||
}
|
||||
if(VLC->arth->getDefaultAllowed().size() > map()->allowedArtifact.size())
|
||||
{
|
||||
for(int i = map()->allowedArtifact.size(); i < VLC->arth->getDefaultAllowed().size(); ++i)
|
||||
map()->allowedArtifact.push_back(false);
|
||||
}
|
||||
if(VLC->spellh->getDefaultAllowed().size() > map()->allowedSpell.size())
|
||||
{
|
||||
for(int i = map()->allowedSpell.size(); i < VLC->spellh->getDefaultAllowed().size(); ++i)
|
||||
map()->allowedSpell.push_back(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MapController::setMap(std::unique_ptr<CMap> cmap)
|
||||
{
|
||||
_map = std::move(cmap);
|
||||
|
||||
repairMap();
|
||||
|
||||
_scenes[0].reset(new MapScene(0));
|
||||
_scenes[1].reset(new MapScene(1));
|
||||
_miniscenes[0].reset(new MinimapScene(0));
|
||||
_miniscenes[1].reset(new MinimapScene(1));
|
||||
resetMapHandler();
|
||||
sceneForceUpdate();
|
||||
|
||||
connectScenes();
|
||||
|
||||
_map->getEditManager()->getUndoManager().setUndoCallback([this](bool allowUndo, bool allowRedo)
|
||||
{
|
||||
main->enableUndo(allowUndo);
|
||||
main->enableRedo(allowRedo);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void MapController::sceneForceUpdate()
|
||||
{
|
||||
_scenes[0]->updateViews();
|
||||
_miniscenes[0]->updateViews();
|
||||
if(_map->twoLevel)
|
||||
{
|
||||
_scenes[1]->updateViews();
|
||||
_miniscenes[1]->updateViews();
|
||||
}
|
||||
}
|
||||
|
||||
void MapController::sceneForceUpdate(int level)
|
||||
{
|
||||
_scenes[level]->updateViews();
|
||||
_miniscenes[level]->updateViews();
|
||||
}
|
||||
|
||||
void MapController::resetMapHandler()
|
||||
{
|
||||
if(!_mapHandler)
|
||||
_mapHandler.reset(new MapHandler());
|
||||
_mapHandler->reset(map());
|
||||
_scenes[0]->initialize(*this);
|
||||
_scenes[1]->initialize(*this);
|
||||
_miniscenes[0]->initialize(*this);
|
||||
_miniscenes[1]->initialize(*this);
|
||||
}
|
||||
|
||||
void MapController::commitTerrainChange(int level, const Terrain & terrain)
|
||||
{
|
||||
std::vector<int3> v(_scenes[level]->selectionTerrainView.selection().begin(),
|
||||
_scenes[level]->selectionTerrainView.selection().end());
|
||||
if(v.empty())
|
||||
return;
|
||||
|
||||
_scenes[level]->selectionTerrainView.clear();
|
||||
_scenes[level]->selectionTerrainView.draw();
|
||||
|
||||
_map->getEditManager()->getTerrainSelection().setSelection(v);
|
||||
_map->getEditManager()->drawTerrain(terrain, &CRandomGenerator::getDefault());
|
||||
|
||||
for(auto & t : v)
|
||||
_scenes[level]->terrainView.setDirty(t);
|
||||
_scenes[level]->terrainView.draw();
|
||||
|
||||
_miniscenes[level]->updateViews();
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
void MapController::commitRoadOrRiverChange(int level, const std::string & type, bool isRoad)
|
||||
{
|
||||
std::vector<int3> v(_scenes[level]->selectionTerrainView.selection().begin(),
|
||||
_scenes[level]->selectionTerrainView.selection().end());
|
||||
if(v.empty())
|
||||
return;
|
||||
|
||||
_scenes[level]->selectionTerrainView.clear();
|
||||
_scenes[level]->selectionTerrainView.draw();
|
||||
|
||||
_map->getEditManager()->getTerrainSelection().setSelection(v);
|
||||
if(isRoad)
|
||||
_map->getEditManager()->drawRoad(type, &CRandomGenerator::getDefault());
|
||||
else
|
||||
_map->getEditManager()->drawRiver(type, &CRandomGenerator::getDefault());
|
||||
|
||||
for(auto & t : v)
|
||||
_scenes[level]->terrainView.setDirty(t);
|
||||
_scenes[level]->terrainView.draw();
|
||||
|
||||
_miniscenes[level]->updateViews();
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
void MapController::commitObjectErase(int level)
|
||||
{
|
||||
auto selectedObjects = _scenes[level]->selectionObjectsView.getSelection();
|
||||
if (selectedObjects.size() > 1)
|
||||
{
|
||||
//mass erase => undo in one operation
|
||||
_map->getEditManager()->removeObjects(selectedObjects);
|
||||
}
|
||||
else if (selectedObjects.size() == 1)
|
||||
{
|
||||
_map->getEditManager()->removeObject(*selectedObjects.begin());
|
||||
}
|
||||
else //nothing to erase - shouldn't be here
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto obj : selectedObjects)
|
||||
{
|
||||
//invalidate tiles under objects
|
||||
_mapHandler->invalidate(_mapHandler->getTilesUnderObject(obj));
|
||||
}
|
||||
|
||||
_scenes[level]->selectionObjectsView.clear();
|
||||
_scenes[level]->objectsView.draw();
|
||||
_scenes[level]->selectionObjectsView.draw();
|
||||
_scenes[level]->passabilityView.update();
|
||||
|
||||
_miniscenes[level]->updateViews();
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
bool MapController::discardObject(int level) const
|
||||
{
|
||||
_scenes[level]->selectionObjectsView.clear();
|
||||
if(_scenes[level]->selectionObjectsView.newObject)
|
||||
{
|
||||
delete _scenes[level]->selectionObjectsView.newObject;
|
||||
_scenes[level]->selectionObjectsView.newObject = nullptr;
|
||||
_scenes[level]->selectionObjectsView.shift = QPoint(0, 0);
|
||||
_scenes[level]->selectionObjectsView.selectionMode = 0;
|
||||
_scenes[level]->selectionObjectsView.draw();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MapController::createObject(int level, CGObjectInstance * obj) const
|
||||
{
|
||||
_scenes[level]->selectionObjectsView.newObject = obj;
|
||||
_scenes[level]->selectionObjectsView.selectionMode = 2;
|
||||
_scenes[level]->selectionObjectsView.draw();
|
||||
}
|
||||
|
||||
void MapController::commitObstacleFill(int level)
|
||||
{
|
||||
auto selection = _scenes[level]->selectionTerrainView.selection();
|
||||
if(selection.empty())
|
||||
return;
|
||||
|
||||
//split by zones
|
||||
std::map<Terrain, ObstacleProxy> terrainSelected;
|
||||
for(auto & t : selection)
|
||||
{
|
||||
auto tl = _map->getTile(t);
|
||||
if(tl.blocked || tl.visitable)
|
||||
continue;
|
||||
|
||||
terrainSelected[tl.terType].blockedArea.add(t);
|
||||
}
|
||||
|
||||
for(auto & sel : terrainSelected)
|
||||
{
|
||||
sel.second.collectPossibleObstacles(sel.first);
|
||||
sel.second.placeObstacles(_map.get(), CRandomGenerator::getDefault());
|
||||
}
|
||||
|
||||
_mapHandler->invalidateObjects();
|
||||
|
||||
_scenes[level]->selectionTerrainView.clear();
|
||||
_scenes[level]->selectionTerrainView.draw();
|
||||
_scenes[level]->objectsView.draw();
|
||||
_scenes[level]->passabilityView.update();
|
||||
|
||||
_miniscenes[level]->updateViews();
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
void MapController::commitObjectChange(int level)
|
||||
{
|
||||
//for( auto * o : _scenes[level]->selectionObjectsView.getSelection())
|
||||
//_mapHandler->invalidate(o);
|
||||
|
||||
_scenes[level]->objectsView.draw();
|
||||
_scenes[level]->selectionObjectsView.draw();
|
||||
_scenes[level]->passabilityView.update();
|
||||
|
||||
_miniscenes[level]->updateViews();
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
|
||||
void MapController::commitChangeWithoutRedraw()
|
||||
{
|
||||
//DO NOT REDRAW
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
void MapController::commitObjectShift(int level)
|
||||
{
|
||||
auto shift = _scenes[level]->selectionObjectsView.shift;
|
||||
bool makeShift = !shift.isNull();
|
||||
if(makeShift)
|
||||
{
|
||||
for(auto * obj : _scenes[level]->selectionObjectsView.getSelection())
|
||||
{
|
||||
int3 pos = obj->pos;
|
||||
pos.z = level;
|
||||
pos.x += shift.x(); pos.y += shift.y();
|
||||
|
||||
auto prevPositions = _mapHandler->getTilesUnderObject(obj);
|
||||
_map->getEditManager()->moveObject(obj, pos);
|
||||
_mapHandler->invalidate(prevPositions);
|
||||
_mapHandler->invalidate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
_scenes[level]->selectionObjectsView.newObject = nullptr;
|
||||
_scenes[level]->selectionObjectsView.shift = QPoint(0, 0);
|
||||
_scenes[level]->selectionObjectsView.selectionMode = 0;
|
||||
|
||||
if(makeShift)
|
||||
{
|
||||
_scenes[level]->objectsView.draw();
|
||||
_scenes[level]->selectionObjectsView.draw();
|
||||
_scenes[level]->passabilityView.update();
|
||||
|
||||
_miniscenes[level]->updateViews();
|
||||
main->mapChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void MapController::commitObjectCreate(int level)
|
||||
{
|
||||
auto * newObj = _scenes[level]->selectionObjectsView.newObject;
|
||||
if(!newObj)
|
||||
return;
|
||||
|
||||
auto shift = _scenes[level]->selectionObjectsView.shift;
|
||||
|
||||
int3 pos = newObj->pos;
|
||||
pos.z = level;
|
||||
pos.x += shift.x(); pos.y += shift.y();
|
||||
|
||||
newObj->pos = pos;
|
||||
|
||||
Initializer init(newObj, defaultPlayer);
|
||||
|
||||
_map->getEditManager()->insertObject(newObj);
|
||||
_mapHandler->invalidate(newObj);
|
||||
|
||||
_scenes[level]->selectionObjectsView.newObject = nullptr;
|
||||
_scenes[level]->selectionObjectsView.shift = QPoint(0, 0);
|
||||
_scenes[level]->selectionObjectsView.selectionMode = 0;
|
||||
_scenes[level]->objectsView.draw();
|
||||
_scenes[level]->selectionObjectsView.draw();
|
||||
_scenes[level]->passabilityView.update();
|
||||
|
||||
_miniscenes[level]->updateViews();
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
bool MapController::canPlaceObject(int level, CGObjectInstance * newObj, QString & error) const
|
||||
{
|
||||
//need this because of possible limits
|
||||
auto rmgInfo = VLC->objtypeh->getHandlerFor(newObj->ID, newObj->subID)->getRMGInfo();
|
||||
|
||||
//find all objects of such type
|
||||
int objCounter = 0;
|
||||
for(auto o : _map->objects)
|
||||
{
|
||||
if(o->ID == newObj->ID && o->subID == newObj->subID)
|
||||
{
|
||||
++objCounter;
|
||||
}
|
||||
}
|
||||
|
||||
if((rmgInfo.mapLimit && objCounter >= rmgInfo.mapLimit)
|
||||
|| (newObj->ID == Obj::GRAIL && objCounter >= 1)) //special case for grail
|
||||
{
|
||||
auto typeName = QString::fromStdString(newObj->typeName);
|
||||
auto subTypeName = QString::fromStdString(newObj->subTypeName);
|
||||
error = QString("Reached map limit for object %1 - %2").arg(typeName, subTypeName);
|
||||
return false; //maplimit reached
|
||||
}
|
||||
if(defaultPlayer == PlayerColor::NEUTRAL && (newObj->ID == Obj::HERO || newObj->ID == Obj::RANDOM_HERO))
|
||||
{
|
||||
error = "Hero cannot be created as NEUTRAL";
|
||||
return false;
|
||||
}
|
||||
if(defaultPlayer != PlayerColor::NEUTRAL && newObj->ID == Obj::PRISON)
|
||||
{
|
||||
error = "Prison must be a NEUTRAL";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(newObj->ID == Obj::ARTIFACT && !_map->allowedArtifact.at(newObj->subID))
|
||||
{
|
||||
error = "Artifact is not allowed. Check map settings.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapController::undo()
|
||||
{
|
||||
_map->getEditManager()->getUndoManager().undo();
|
||||
resetMapHandler();
|
||||
sceneForceUpdate();
|
||||
main->mapChanged();
|
||||
}
|
||||
|
||||
void MapController::redo()
|
||||
{
|
||||
_map->getEditManager()->getUndoManager().redo();
|
||||
resetMapHandler();
|
||||
sceneForceUpdate();
|
||||
main->mapChanged();
|
||||
}
|
62
mapeditor/mapcontroller.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef MAPCONTROLLER_H
|
||||
#define MAPCONTROLLER_H
|
||||
|
||||
#include "maphandler.h"
|
||||
#include "mapview.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/Terrain.h"
|
||||
|
||||
class MainWindow;
|
||||
class MapController
|
||||
{
|
||||
public:
|
||||
MapController(MainWindow *);
|
||||
MapController(const MapController &) = delete;
|
||||
MapController(const MapController &&) = delete;
|
||||
~MapController();
|
||||
|
||||
void setMap(std::unique_ptr<CMap>);
|
||||
|
||||
void repairMap();
|
||||
|
||||
const std::unique_ptr<CMap> & getMapUniquePtr() const; //to be used for map saving
|
||||
CMap * map();
|
||||
MapHandler * mapHandler();
|
||||
MapScene * scene(int level);
|
||||
MinimapScene * miniScene(int level);
|
||||
|
||||
void resetMapHandler();
|
||||
|
||||
void sceneForceUpdate();
|
||||
void sceneForceUpdate(int level);
|
||||
|
||||
void commitTerrainChange(int level, const Terrain & terrain);
|
||||
void commitRoadOrRiverChange(int level, const std::string & type, bool isRoad);
|
||||
void commitObjectErase(const CGObjectInstance* obj);
|
||||
void commitObjectErase(int level);
|
||||
void commitObstacleFill(int level);
|
||||
void commitChangeWithoutRedraw();
|
||||
void commitObjectShift(int level);
|
||||
void commitObjectCreate(int level);
|
||||
void commitObjectChange(int level);
|
||||
|
||||
bool discardObject(int level) const;
|
||||
void createObject(int level, CGObjectInstance * obj) const;
|
||||
bool canPlaceObject(int level, CGObjectInstance * obj, QString & error) const;
|
||||
|
||||
void undo();
|
||||
void redo();
|
||||
|
||||
PlayerColor defaultPlayer;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CMap> _map;
|
||||
std::unique_ptr<MapHandler> _mapHandler;
|
||||
MainWindow * main;
|
||||
mutable std::array<std::unique_ptr<MapScene>, 2> _scenes;
|
||||
mutable std::array<std::unique_ptr<MinimapScene>, 2> _miniscenes;
|
||||
|
||||
void connectScenes();
|
||||
};
|
||||
|
||||
#endif // MAPCONTROLLER_H
|
BIN
mapeditor/mapeditor.ico
Normal file
After Width: | Height: | Size: 79 KiB |
1
mapeditor/mapeditor.rc
Normal file
@@ -0,0 +1 @@
|
||||
IDI_ICON1 ICON "mapeditor.ico"
|
538
mapeditor/maphandler.cpp
Normal file
@@ -0,0 +1,538 @@
|
||||
#include "StdInc.h"
|
||||
#include "maphandler.h"
|
||||
#include "graphics.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/mapObjects/CObjectClassesHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/JsonDetail.h"
|
||||
|
||||
const int tileSize = 32;
|
||||
|
||||
static bool objectBlitOrderSorter(const TileObject & a, const TileObject & b)
|
||||
{
|
||||
return MapHandler::compareObjectBlitOrder(a.obj, b.obj);
|
||||
}
|
||||
|
||||
int MapHandler::index(int x, int y, int z) const
|
||||
{
|
||||
return z * (sizes.x * sizes.y) + y * sizes.x + x;
|
||||
}
|
||||
|
||||
int MapHandler::index(const int3 & p) const
|
||||
{
|
||||
return index(p.x, p.y, p.z);
|
||||
}
|
||||
|
||||
MapHandler::MapHandler()
|
||||
{
|
||||
initTerrainGraphics();
|
||||
logGlobal->info("\tPreparing terrain, roads, rivers, borders");
|
||||
}
|
||||
|
||||
void MapHandler::reset(const CMap * Map)
|
||||
{
|
||||
ttiles.clear();
|
||||
map = Map;
|
||||
|
||||
//sizes of terrain
|
||||
sizes.x = map->width;
|
||||
sizes.y = map->height;
|
||||
sizes.z = map->twoLevel ? 2 : 1;
|
||||
|
||||
initObjectRects();
|
||||
logGlobal->info("\tMaking object rects");
|
||||
}
|
||||
|
||||
void MapHandler::initTerrainGraphics()
|
||||
{
|
||||
static const std::map<std::string, std::string> ROAD_FILES =
|
||||
{
|
||||
{ROAD_NAMES[1], "dirtrd"},
|
||||
{ROAD_NAMES[2], "gravrd"},
|
||||
{ROAD_NAMES[3], "cobbrd"}
|
||||
};
|
||||
|
||||
static const std::map<std::string, std::string> RIVER_FILES =
|
||||
{
|
||||
{RIVER_NAMES[1], "clrrvr"},
|
||||
{RIVER_NAMES[2], "icyrvr"},
|
||||
{RIVER_NAMES[3], "mudrvr"},
|
||||
{RIVER_NAMES[4], "lavrvr"}
|
||||
};
|
||||
|
||||
|
||||
auto loadFlipped = [](TFlippedAnimations & animation, TFlippedCache & cache, const std::map<std::string, std::string> & files)
|
||||
{
|
||||
for(auto & type : files)
|
||||
{
|
||||
animation[type.first] = make_unique<Animation>(type.second);
|
||||
animation[type.first]->preload();
|
||||
const size_t views = animation[type.first]->size(0);
|
||||
cache[type.first].resize(views);
|
||||
|
||||
for(int j = 0; j < views; j++)
|
||||
cache[type.first][j] = animation[type.first]->getImage(j);
|
||||
}
|
||||
};
|
||||
|
||||
std::map<std::string, std::string> terrainFiles;
|
||||
for(auto & terrain : Terrain::Manager::terrains())
|
||||
{
|
||||
terrainFiles[terrain] = Terrain::Manager::getInfo(terrain).tilesFilename;
|
||||
}
|
||||
|
||||
loadFlipped(terrainAnimations, terrainImages, terrainFiles);
|
||||
loadFlipped(roadAnimations, roadImages, ROAD_FILES);
|
||||
loadFlipped(riverAnimations, riverImages, RIVER_FILES);
|
||||
}
|
||||
|
||||
void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z)
|
||||
{
|
||||
auto & tinfo = map->getTile(int3(x, y, z));
|
||||
ui8 rotation = tinfo.extTileFlags % 4;
|
||||
|
||||
if(terrainImages.at(tinfo.terType).size() <= tinfo.terView)
|
||||
return;
|
||||
|
||||
bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);
|
||||
painter.drawImage(x * tileSize, y * tileSize, terrainImages.at(tinfo.terType)[tinfo.terView]->mirrored(hflip, vflip));
|
||||
}
|
||||
|
||||
void MapHandler::drawRoad(QPainter & painter, int x, int y, int z)
|
||||
{
|
||||
auto & tinfo = map->getTile(int3(x, y, z));
|
||||
auto * tinfoUpper = map->isInTheMap(int3(x, y - 1, z)) ? &map->getTile(int3(x, y - 1, z)) : nullptr;
|
||||
|
||||
if (tinfoUpper && tinfoUpper->roadType != ROAD_NAMES[0])
|
||||
{
|
||||
QRect source(0, tileSize / 2, tileSize, tileSize / 2);
|
||||
ui8 rotation = (tinfoUpper->extTileFlags >> 4) % 4;
|
||||
bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);
|
||||
if(roadImages.at(tinfoUpper->roadType).size() > tinfoUpper->roadDir)
|
||||
{
|
||||
painter.drawImage(QPoint(x * tileSize, y * tileSize), roadImages.at(tinfoUpper->roadType)[tinfoUpper->roadDir]->mirrored(hflip, vflip), source);
|
||||
}
|
||||
}
|
||||
|
||||
if(tinfo.roadType != ROAD_NAMES[0]) //print road from this tile
|
||||
{
|
||||
QRect source(0, 0, tileSize, tileSize / 2);
|
||||
ui8 rotation = (tinfo.extTileFlags >> 4) % 4;
|
||||
bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);
|
||||
if(roadImages.at(tinfo.roadType).size() > tinfo.roadDir)
|
||||
{
|
||||
painter.drawImage(QPoint(x * tileSize, y * tileSize + tileSize / 2), roadImages.at(tinfo.roadType)[tinfo.roadDir]->mirrored(hflip, vflip), source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapHandler::drawRiver(QPainter & painter, int x, int y, int z)
|
||||
{
|
||||
auto & tinfo = map->getTile(int3(x, y, z));
|
||||
|
||||
if(tinfo.riverType == RIVER_NAMES[0])
|
||||
return;
|
||||
|
||||
if(riverImages.at(tinfo.riverType).size() <= tinfo.riverDir)
|
||||
return;
|
||||
|
||||
ui8 rotation = (tinfo.extTileFlags >> 2) % 4;
|
||||
bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);
|
||||
|
||||
painter.drawImage(x * tileSize, y * tileSize, riverImages.at(tinfo.riverType)[tinfo.riverDir]->mirrored(hflip, vflip));
|
||||
}
|
||||
|
||||
void setPlayerColor(QImage * sur, PlayerColor player)
|
||||
{
|
||||
if(player == PlayerColor::UNFLAGGABLE)
|
||||
return;
|
||||
if(sur->format() == QImage::Format_Indexed8)
|
||||
{
|
||||
QRgb color = graphics->neutralColor;
|
||||
if(player != PlayerColor::NEUTRAL && player < PlayerColor::PLAYER_LIMIT)
|
||||
color = graphics->playerColors.at(player.getNum());
|
||||
|
||||
sur->setColor(5, color);
|
||||
}
|
||||
else
|
||||
logGlobal->warn("Warning, setPlayerColor called on not 8bpp surface!");
|
||||
}
|
||||
|
||||
void MapHandler::initObjectRects()
|
||||
{
|
||||
ttiles.resize(sizes.x * sizes.y * sizes.z);
|
||||
|
||||
//initializing objects / rects
|
||||
for(const CGObjectInstance * elem : map->objects)
|
||||
{
|
||||
CGObjectInstance *obj = const_cast<CGObjectInstance *>(elem);
|
||||
if( !obj
|
||||
|| (obj->ID==Obj::HERO && static_cast<const CGHeroInstance*>(obj)->inTownGarrison) //garrisoned hero
|
||||
|| (obj->ID==Obj::BOAT && static_cast<const CGBoat*>(obj)->hero)) //boat with hero (hero graphics is used)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_ptr<Animation> animation = graphics->getAnimation(obj);
|
||||
|
||||
//no animation at all
|
||||
if(!animation)
|
||||
continue;
|
||||
|
||||
//empty animation
|
||||
if(animation->size(0) == 0)
|
||||
continue;
|
||||
|
||||
auto image = animation->getImage(0, obj->ID == Obj::HERO ? 2 : 0);
|
||||
if(!image)
|
||||
{
|
||||
//workaound for prisons
|
||||
image = animation->getImage(0, 0);
|
||||
if(!image)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
for(int fx=0; fx < obj->getWidth(); ++fx)
|
||||
{
|
||||
for(int fy=0; fy < obj->getHeight(); ++fy)
|
||||
{
|
||||
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
|
||||
QRect cr(image->width() - fx * tileSize - tileSize,
|
||||
image->height() - fy * tileSize - tileSize,
|
||||
image->width(),
|
||||
image->height());
|
||||
|
||||
TileObject toAdd(obj, cr);
|
||||
|
||||
if( map->isInTheMap(currTile) && // within map
|
||||
cr.x() + cr.width() > 0 && // image has data on this tile
|
||||
cr.y() + cr.height() > 0 &&
|
||||
obj->coveringAt(currTile.x, currTile.y) // object is visible here
|
||||
)
|
||||
{
|
||||
ttiles[index(currTile)].push_back(toAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto & tt : ttiles)
|
||||
{
|
||||
stable_sort(tt.begin(), tt.end(), objectBlitOrderSorter);
|
||||
}
|
||||
}
|
||||
|
||||
bool MapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b)
|
||||
{
|
||||
if (!a)
|
||||
return true;
|
||||
if (!b)
|
||||
return false;
|
||||
if (a->appearance->printPriority != b->appearance->printPriority)
|
||||
return a->appearance->printPriority > b->appearance->printPriority;
|
||||
|
||||
if(a->pos.y != b->pos.y)
|
||||
return a->pos.y < b->pos.y;
|
||||
|
||||
if(b->ID==Obj::HERO && a->ID!=Obj::HERO)
|
||||
return true;
|
||||
if(b->ID!=Obj::HERO && a->ID==Obj::HERO)
|
||||
return false;
|
||||
|
||||
if(!a->isVisitable() && b->isVisitable())
|
||||
return true;
|
||||
if(!b->isVisitable() && a->isVisitable())
|
||||
return false;
|
||||
if(a->pos.x < b->pos.x)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
TileObject::TileObject(CGObjectInstance * obj_, QRect rect_)
|
||||
: obj(obj_),
|
||||
rect(rect_)
|
||||
{
|
||||
}
|
||||
|
||||
TileObject::~TileObject()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<QImage> MapHandler::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor color, int group) const
|
||||
{
|
||||
if(!hero || hero->boat)
|
||||
return std::shared_ptr<QImage>();
|
||||
|
||||
return findFlagBitmapInternal(graphics->heroFlagAnimations.at(color.getNum()), anim, group, hero->moveDir, !hero->isStanding);
|
||||
}
|
||||
|
||||
std::shared_ptr<QImage> MapHandler::findFlagBitmapInternal(std::shared_ptr<Animation> animation, int anim, int group, ui8 dir, bool moving) const
|
||||
{
|
||||
size_t groupSize = animation->size(group);
|
||||
if(groupSize == 0)
|
||||
return nullptr;
|
||||
|
||||
if(moving)
|
||||
return animation->getImage(anim % groupSize, group);
|
||||
else
|
||||
return animation->getImage((anim / 4) % groupSize, group);
|
||||
}
|
||||
|
||||
MapHandler::AnimBitmapHolder MapHandler::findObjectBitmap(const CGObjectInstance * obj, int anim, int group) const
|
||||
{
|
||||
if(!obj)
|
||||
return MapHandler::AnimBitmapHolder();
|
||||
|
||||
// normal object
|
||||
std::shared_ptr<Animation> animation = graphics->getAnimation(obj);
|
||||
size_t groupSize = animation->size(group);
|
||||
if(groupSize == 0)
|
||||
return MapHandler::AnimBitmapHolder();
|
||||
|
||||
animation->playerColored(obj->tempOwner);
|
||||
auto bitmap = animation->getImage(anim % groupSize, group);
|
||||
|
||||
if(!bitmap)
|
||||
return MapHandler::AnimBitmapHolder();
|
||||
|
||||
setPlayerColor(bitmap.get(), obj->tempOwner);
|
||||
|
||||
return MapHandler::AnimBitmapHolder(bitmap);
|
||||
}
|
||||
|
||||
std::vector<TileObject> & MapHandler::getObjects(int x, int y, int z)
|
||||
{
|
||||
return ttiles[index(x, y, z)];
|
||||
}
|
||||
|
||||
void MapHandler::drawObjects(QPainter & painter, int x, int y, int z)
|
||||
{
|
||||
for(auto & object : getObjects(x, y, z))
|
||||
{
|
||||
const CGObjectInstance * obj = object.obj;
|
||||
if(!obj)
|
||||
{
|
||||
logGlobal->error("Stray map object that isn't fading");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t animationFrame = 0;
|
||||
|
||||
auto objData = findObjectBitmap(obj, animationFrame, obj->ID == Obj::HERO ? 2 : 0);
|
||||
if(obj->ID == Obj::HERO && obj->tempOwner.isValidPlayer())
|
||||
objData.flagBitmap = findFlagBitmap(dynamic_cast<const CGHeroInstance*>(obj), 0, obj->tempOwner, 4);
|
||||
|
||||
if(objData.objBitmap)
|
||||
{
|
||||
auto pos = obj->getPosition();
|
||||
|
||||
painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.objBitmap, object.rect);
|
||||
|
||||
if(objData.flagBitmap)
|
||||
{
|
||||
if(x == pos.x && y == pos.y)
|
||||
painter.drawImage(QPoint((x - 2) * tileSize, (y - 1) * tileSize), *objData.flagBitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapHandler::drawObject(QPainter & painter, const TileObject & object)
|
||||
{
|
||||
const CGObjectInstance * obj = object.obj;
|
||||
if (!obj)
|
||||
{
|
||||
logGlobal->error("Stray map object that isn't fading");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t animationFrame = 0;
|
||||
|
||||
auto objData = findObjectBitmap(obj, animationFrame, obj->ID == Obj::HERO ? 2 : 0);
|
||||
if(obj->ID == Obj::HERO && obj->tempOwner.isValidPlayer())
|
||||
objData.flagBitmap = findFlagBitmap(dynamic_cast<const CGHeroInstance*>(obj), 0, obj->tempOwner, 0);
|
||||
|
||||
if (objData.objBitmap)
|
||||
{
|
||||
auto pos = obj->getPosition();
|
||||
|
||||
painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.objBitmap);
|
||||
|
||||
if (objData.flagBitmap)
|
||||
{
|
||||
if(object.rect.x() == pos.x && object.rect.y() == pos.y)
|
||||
painter.drawImage(pos.x * tileSize - object.rect.x(), pos.y * tileSize - object.rect.y(), *objData.flagBitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MapHandler::drawObjectAt(QPainter & painter, const CGObjectInstance * obj, int x, int y)
|
||||
{
|
||||
if (!obj)
|
||||
{
|
||||
logGlobal->error("Stray map object that isn't fading");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t animationFrame = 0;
|
||||
|
||||
auto objData = findObjectBitmap(obj, animationFrame, obj->ID == Obj::HERO ? 2 : 0);
|
||||
std::vector<std::shared_ptr<QImage>> debugFlagImages;
|
||||
if(obj->ID == Obj::HERO && obj->tempOwner.isValidPlayer())
|
||||
objData.flagBitmap = findFlagBitmap(dynamic_cast<const CGHeroInstance*>(obj), 0, obj->tempOwner, 4);
|
||||
|
||||
if (objData.objBitmap)
|
||||
{
|
||||
painter.drawImage(QPoint((x + 1) * 32 - objData.objBitmap->width(), (y + 1) * 32 - objData.objBitmap->height()), *objData.objBitmap);
|
||||
|
||||
if (objData.flagBitmap)
|
||||
painter.drawImage(QPoint((x + 1) * 32 - objData.objBitmap->width(), (y + 1) * 32 - objData.objBitmap->height()), *objData.flagBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
QRgb MapHandler::getTileColor(int x, int y, int z)
|
||||
{
|
||||
// if object at tile is owned - it will be colored as its owner
|
||||
for(auto & object : getObjects(x, y, z))
|
||||
{
|
||||
if(!object.obj->getBlockedPos().count(int3(x, y, z)))
|
||||
continue;
|
||||
|
||||
PlayerColor player = object.obj->getOwner();
|
||||
if(player == PlayerColor::NEUTRAL)
|
||||
return graphics->neutralColor;
|
||||
else
|
||||
if (player < PlayerColor::PLAYER_LIMIT)
|
||||
return graphics->playerColors[player.getNum()];
|
||||
}
|
||||
|
||||
// else - use terrain color (blocked version or normal)
|
||||
auto & tile = map->getTile(int3(x, y, z));
|
||||
auto color = Terrain::Manager::getInfo(tile.terType).minimapUnblocked;
|
||||
if (tile.blocked && (!tile.visitable))
|
||||
color = Terrain::Manager::getInfo(tile.terType).minimapBlocked;
|
||||
|
||||
return qRgb(color[0], color[1], color[2]);
|
||||
}
|
||||
|
||||
void MapHandler::drawMinimapTile(QPainter & painter, int x, int y, int z)
|
||||
{
|
||||
painter.setPen(getTileColor(x, y, z));
|
||||
painter.drawPoint(x, y);
|
||||
}
|
||||
|
||||
void MapHandler::invalidate(int x, int y, int z)
|
||||
{
|
||||
auto & objects = getObjects(x, y, z);
|
||||
|
||||
for(auto obj = objects.begin(); obj != objects.end();)
|
||||
{
|
||||
//object was removed
|
||||
if(std::find(map->objects.begin(), map->objects.end(), obj->obj) == map->objects.end())
|
||||
{
|
||||
obj = objects.erase(obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
//object was moved
|
||||
auto & pos = obj->obj->pos;
|
||||
if(pos.z != z || pos.x < x || pos.y < y || pos.x - obj->obj->getWidth() >= x || pos.y - obj->obj->getHeight() >= y)
|
||||
{
|
||||
obj = objects.erase(obj);
|
||||
continue;
|
||||
}
|
||||
|
||||
++obj;
|
||||
}
|
||||
|
||||
stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter);
|
||||
}
|
||||
|
||||
void MapHandler::invalidate(CGObjectInstance * obj)
|
||||
{
|
||||
std::shared_ptr<Animation> animation = graphics->getAnimation(obj);
|
||||
|
||||
//no animation at all or empty animation
|
||||
if(!animation || animation->size(0) == 0)
|
||||
return;
|
||||
|
||||
auto image = animation->getImage(0, obj->ID == Obj::HERO ? 2 : 0);
|
||||
if(!image)
|
||||
return;
|
||||
|
||||
for(int fx=0; fx < obj->getWidth(); ++fx)
|
||||
{
|
||||
for(int fy=0; fy < obj->getHeight(); ++fy)
|
||||
{
|
||||
//object presented on the tile
|
||||
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
|
||||
QRect cr(image->width() - fx * tileSize - tileSize, image->height() - fy * tileSize - tileSize, image->width(), image->height());
|
||||
|
||||
if( map->isInTheMap(currTile) && // within map
|
||||
cr.x() + cr.width() > 0 && // image has data on this tile
|
||||
cr.y() + cr.height() > 0 &&
|
||||
obj->coveringAt(currTile.x, currTile.y) // object is visible here
|
||||
)
|
||||
{
|
||||
auto & objects = ttiles[index(currTile)];
|
||||
bool found = false;
|
||||
for(auto & o : objects)
|
||||
{
|
||||
if(o.obj == obj)
|
||||
{
|
||||
o.rect = cr;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
objects.emplace_back(obj, cr);
|
||||
|
||||
stable_sort(objects.begin(), objects.end(), objectBlitOrderSorter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int3> MapHandler::getTilesUnderObject(CGObjectInstance * obj) const
|
||||
{
|
||||
std::vector<int3> result;
|
||||
for(int fx=0; fx < obj->getWidth(); ++fx)
|
||||
{
|
||||
for(int fy=0; fy < obj->getHeight(); ++fy)
|
||||
{
|
||||
//object presented on the tile
|
||||
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
|
||||
if(map->isInTheMap(currTile) && // within map
|
||||
obj->coveringAt(currTile.x, currTile.y) // object is visible here
|
||||
)
|
||||
{
|
||||
result.push_back(currTile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MapHandler::invalidateObjects()
|
||||
{
|
||||
for(auto obj : map->objects)
|
||||
{
|
||||
invalidate(obj);
|
||||
}
|
||||
}
|
||||
|
||||
void MapHandler::invalidate(const std::vector<int3> & tiles)
|
||||
{
|
||||
for(auto & currTile : tiles)
|
||||
{
|
||||
invalidate(currTile.x, currTile.y, currTile.z);
|
||||
}
|
||||
}
|
107
mapeditor/maphandler.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#ifndef MAPHANDLER_H
|
||||
#define MAPHANDLER_H
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "Animation.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QRect>
|
||||
|
||||
class CGObjectInstance;
|
||||
class CGBoat;
|
||||
class PlayerColor;
|
||||
|
||||
struct TileObject
|
||||
{
|
||||
CGObjectInstance *obj;
|
||||
QRect rect;
|
||||
|
||||
TileObject(CGObjectInstance *obj_, QRect rect_);
|
||||
~TileObject();
|
||||
};
|
||||
|
||||
using TileObjects = std::vector<TileObject>; //pointers to objects being on this tile with rects to be easier to blit this tile on screen
|
||||
|
||||
class MapHandler
|
||||
{
|
||||
public:
|
||||
struct AnimBitmapHolder
|
||||
{
|
||||
std::shared_ptr<QImage> objBitmap; // main object bitmap
|
||||
std::shared_ptr<QImage> flagBitmap; // flag bitmap for the object (probably only for heroes and boats with heroes)
|
||||
|
||||
AnimBitmapHolder(std::shared_ptr<QImage> objBitmap_ = nullptr, std::shared_ptr<QImage> flagBitmap_ = nullptr)
|
||||
: objBitmap(objBitmap_),
|
||||
flagBitmap(flagBitmap_)
|
||||
{}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
int index(int x, int y, int z) const;
|
||||
int index(const int3 &) const;
|
||||
|
||||
std::shared_ptr<QImage> findFlagBitmapInternal(std::shared_ptr<Animation> animation, int anim, int group, ui8 dir, bool moving) const;
|
||||
std::shared_ptr<QImage> findFlagBitmap(const CGHeroInstance * obj, int anim, const PlayerColor color, int group) const;
|
||||
AnimBitmapHolder findObjectBitmap(const CGObjectInstance * obj, int anim, int group = 0) const;
|
||||
|
||||
//FIXME: unique_ptr should be enough, but fails to compile in MSVS 2013
|
||||
typedef std::map<std::string, std::shared_ptr<Animation>> TFlippedAnimations; //[type, rotation]
|
||||
typedef std::map<std::string, std::vector<std::shared_ptr<QImage>>> TFlippedCache;//[type, view type, rotation]
|
||||
|
||||
TFlippedAnimations terrainAnimations;//[terrain type, rotation]
|
||||
TFlippedCache terrainImages;//[terrain type, view type, rotation]
|
||||
|
||||
TFlippedAnimations roadAnimations;//[road type, rotation]
|
||||
TFlippedCache roadImages;//[road type, view type, rotation]
|
||||
|
||||
TFlippedAnimations riverAnimations;//[river type, rotation]
|
||||
TFlippedCache riverImages;//[river type, view type, rotation]
|
||||
|
||||
std::vector<TileObjects> ttiles; //informations about map tiles
|
||||
int3 sizes; //map size (x = width, y = height, z = number of levels)
|
||||
const CMap * map;
|
||||
|
||||
enum class EMapCacheType : char
|
||||
{
|
||||
TERRAIN, OBJECTS, ROADS, RIVERS, FOW, HEROES, HERO_FLAGS, FRAME, AFTER_LAST
|
||||
};
|
||||
|
||||
void initObjectRects();
|
||||
void initTerrainGraphics();
|
||||
QRgb getTileColor(int x, int y, int z);
|
||||
|
||||
public:
|
||||
MapHandler();
|
||||
~MapHandler() = default;
|
||||
|
||||
void reset(const CMap * Map);
|
||||
|
||||
void updateWater();
|
||||
|
||||
void drawTerrainTile(QPainter & painter, int x, int y, int z);
|
||||
/// draws a river segment on current tile
|
||||
void drawRiver(QPainter & painter, int x, int y, int z);
|
||||
/// draws a road segment on current tile
|
||||
void drawRoad(QPainter & painter, int x, int y, int z);
|
||||
|
||||
void invalidate(int x, int y, int z); //invalidates all objects in particular tile
|
||||
void invalidate(CGObjectInstance *); //invalidates object rects
|
||||
void invalidate(const std::vector<int3> &); //invalidates all tiles
|
||||
void invalidateObjects(); //invalidates all objects on the map
|
||||
std::vector<int3> getTilesUnderObject(CGObjectInstance *) const;
|
||||
|
||||
/// draws all objects on current tile (higher-level logic, unlike other draw*** methods)
|
||||
void drawObjects(QPainter & painter, int x, int y, int z);
|
||||
void drawObject(QPainter & painter, const TileObject & object);
|
||||
void drawObjectAt(QPainter & painter, const CGObjectInstance * object, int x, int y);
|
||||
std::vector<TileObject> & getObjects(int x, int y, int z);
|
||||
|
||||
void drawMinimapTile(QPainter & painter, int x, int y, int z);
|
||||
|
||||
static bool compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b);
|
||||
};
|
||||
|
||||
#endif // MAPHANDLER_H
|
102
mapeditor/mapsettings.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "mapsettings.h"
|
||||
#include "ui_mapsettings.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
|
||||
MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::MapSettings),
|
||||
controller(ctrl)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
assert(controller.map());
|
||||
|
||||
ui->mapNameEdit->setText(tr(controller.map()->name.c_str()));
|
||||
ui->mapDescriptionEdit->setPlainText(tr(controller.map()->description.c_str()));
|
||||
|
||||
show();
|
||||
|
||||
|
||||
for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(VLC->skillh->objects[i]->getName()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(controller.map()->allowedAbilities[i] ? Qt::Checked : Qt::Unchecked);
|
||||
ui->listAbilities->addItem(item);
|
||||
}
|
||||
for(int i = 0; i < controller.map()->allowedSpell.size(); ++i)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(VLC->spellh->objects[i]->getName()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(controller.map()->allowedSpell[i] ? Qt::Checked : Qt::Unchecked);
|
||||
ui->listSpells->addItem(item);
|
||||
}
|
||||
for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(VLC->arth->objects[i]->getName()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(controller.map()->allowedArtifact[i] ? Qt::Checked : Qt::Unchecked);
|
||||
ui->listArts->addItem(item);
|
||||
}
|
||||
for(int i = 0; i < controller.map()->allowedHeroes.size(); ++i)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(VLC->heroh->objects[i]->getName()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(controller.map()->allowedHeroes[i] ? Qt::Checked : Qt::Unchecked);
|
||||
ui->listHeroes->addItem(item);
|
||||
}
|
||||
|
||||
//ui8 difficulty;
|
||||
//ui8 levelLimit;
|
||||
|
||||
//std::string victoryMessage;
|
||||
//std::string defeatMessage;
|
||||
//ui16 victoryIconIndex;
|
||||
//ui16 defeatIconIndex;
|
||||
|
||||
//std::vector<PlayerInfo> players; /// The default size of the vector is PlayerColor::PLAYER_LIMIT.
|
||||
}
|
||||
|
||||
MapSettings::~MapSettings()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MapSettings::on_pushButton_clicked()
|
||||
{
|
||||
controller.map()->name = ui->mapNameEdit->text().toStdString();
|
||||
controller.map()->description = ui->mapDescriptionEdit->toPlainText().toStdString();
|
||||
controller.commitChangeWithoutRedraw();
|
||||
|
||||
for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
|
||||
{
|
||||
auto * item = ui->listAbilities->item(i);
|
||||
controller.map()->allowedAbilities[i] = item->checkState() == Qt::Checked;
|
||||
}
|
||||
for(int i = 0; i < controller.map()->allowedSpell.size(); ++i)
|
||||
{
|
||||
auto * item = ui->listSpells->item(i);
|
||||
controller.map()->allowedSpell[i] = item->checkState() == Qt::Checked;
|
||||
}
|
||||
for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
|
||||
{
|
||||
auto * item = ui->listArts->item(i);
|
||||
controller.map()->allowedArtifact[i] = item->checkState() == Qt::Checked;
|
||||
}
|
||||
for(int i = 0; i < controller.map()->allowedHeroes.size(); ++i)
|
||||
{
|
||||
auto * item = ui->listHeroes->item(i);
|
||||
controller.map()->allowedHeroes[i] = item->checkState() == Qt::Checked;
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
27
mapeditor/mapsettings.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef MAPSETTINGS_H
|
||||
#define MAPSETTINGS_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "mapcontroller.h"
|
||||
|
||||
namespace Ui {
|
||||
class MapSettings;
|
||||
}
|
||||
|
||||
class MapSettings : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MapSettings(MapController & controller, QWidget *parent = nullptr);
|
||||
~MapSettings();
|
||||
|
||||
private slots:
|
||||
void on_pushButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::MapSettings *ui;
|
||||
MapController & controller;
|
||||
};
|
||||
|
||||
#endif // MAPSETTINGS_H
|
175
mapeditor/mapsettings.ui
Normal file
@@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MapSettings</class>
|
||||
<widget class="QDialog" name="MapSettings">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>454</width>
|
||||
<height>480</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Map settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Map name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mapNameEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Map description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="mapDescriptionEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Abilities</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QListWidget" name="listAbilities">
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerItem</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Adjust</enum>
|
||||
</property>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::Batched</enum>
|
||||
</property>
|
||||
<property name="batchSize">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>Spells</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QListWidget" name="listSpells">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerItem</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::Batched</enum>
|
||||
</property>
|
||||
<property name="batchSize">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_4">
|
||||
<attribute name="title">
|
||||
<string>Artifacts</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QListWidget" name="listArts">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerItem</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::Batched</enum>
|
||||
</property>
|
||||
<property name="batchSize">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_5">
|
||||
<attribute name="title">
|
||||
<string>Heroes</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QListWidget" name="listHeroes">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="horizontalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerItem</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="layoutMode">
|
||||
<enum>QListView::Batched</enum>
|
||||
</property>
|
||||
<property name="batchSize">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Ok</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
448
mapeditor/mapview.cpp
Normal file
@@ -0,0 +1,448 @@
|
||||
#include "StdInc.h"
|
||||
#include "mapview.h"
|
||||
#include "mainwindow.h"
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include "mapcontroller.h"
|
||||
|
||||
MinimapView::MinimapView(QWidget * parent):
|
||||
QGraphicsView(parent)
|
||||
{
|
||||
// Disable scrollbars
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
}
|
||||
|
||||
void MinimapView::dimensions()
|
||||
{
|
||||
fitInView(0, 0, controller->map()->width, controller->map()->height, Qt::KeepAspectRatio);
|
||||
}
|
||||
|
||||
void MinimapView::setController(MapController * ctrl)
|
||||
{
|
||||
controller = ctrl;
|
||||
}
|
||||
|
||||
void MinimapView::mouseMoveEvent(QMouseEvent *mouseEvent)
|
||||
{
|
||||
this->update();
|
||||
|
||||
auto * sc = static_cast<MinimapScene*>(scene());
|
||||
if(!sc)
|
||||
return;
|
||||
|
||||
int w = sc->viewport.viewportWidth();
|
||||
int h = sc->viewport.viewportHeight();
|
||||
auto pos = mapToScene(mouseEvent->pos());
|
||||
pos.setX(pos.x() - w / 2);
|
||||
pos.setY(pos.y() - h / 2);
|
||||
|
||||
QPointF point = pos * 32;
|
||||
|
||||
emit cameraPositionChanged(point);
|
||||
}
|
||||
|
||||
void MinimapView::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
MapView::MapView(QWidget * parent):
|
||||
QGraphicsView(parent),
|
||||
selectionTool(MapView::SelectionTool::None)
|
||||
{
|
||||
}
|
||||
|
||||
void MapView::cameraChanged(const QPointF & pos)
|
||||
{
|
||||
//ui->mapView->translate(pos.x(), pos.y());
|
||||
horizontalScrollBar()->setValue(pos.x());
|
||||
verticalScrollBar()->setValue(pos.y());
|
||||
}
|
||||
|
||||
|
||||
void MapView::setController(MapController * ctrl)
|
||||
{
|
||||
controller = ctrl;
|
||||
}
|
||||
|
||||
void MapView::mouseMoveEvent(QMouseEvent *mouseEvent)
|
||||
{
|
||||
this->update();
|
||||
|
||||
auto * sc = static_cast<MapScene*>(scene());
|
||||
if(!sc || !controller->map())
|
||||
return;
|
||||
|
||||
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(mouseEvent->buttons() & Qt::RightButton)
|
||||
sc->selectionTerrainView.erase(tile);
|
||||
else if(mouseEvent->buttons() == Qt::LeftButton)
|
||||
sc->selectionTerrainView.select(tile);
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Brush2:
|
||||
{
|
||||
std::array<int3, 4> extra{ int3{0, 0, 0}, int3{1, 0, 0}, int3{0, 1, 0}, int3{1, 1, 0} };
|
||||
for(auto & e : extra)
|
||||
{
|
||||
if(mouseEvent->buttons() & Qt::RightButton)
|
||||
sc->selectionTerrainView.erase(tile + e);
|
||||
else if(mouseEvent->buttons() == Qt::LeftButton)
|
||||
sc->selectionTerrainView.select(tile + e);
|
||||
}
|
||||
}
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Brush4:
|
||||
{
|
||||
std::array<int3, 16> extra{
|
||||
int3{-1, -1, 0}, int3{0, -1, 0}, int3{1, -1, 0}, int3{2, -1, 0},
|
||||
int3{-1, 0, 0}, int3{0, 0, 0}, int3{1, 0, 0}, int3{2, 0, 0},
|
||||
int3{-1, 1, 0}, int3{0, 1, 0}, int3{1, 1, 0}, int3{2, 1, 0},
|
||||
int3{-1, 2, 0}, int3{0, 2, 0}, int3{1, 2, 0}, int3{2, 2, 0}
|
||||
};
|
||||
for(auto & e : extra)
|
||||
{
|
||||
if(mouseEvent->buttons() & Qt::RightButton)
|
||||
sc->selectionTerrainView.erase(tile + e);
|
||||
else if(mouseEvent->buttons() == Qt::LeftButton)
|
||||
sc->selectionTerrainView.select(tile + e);
|
||||
}
|
||||
}
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Area:
|
||||
if(mouseEvent->buttons() & Qt::RightButton || !mouseEvent->buttons() & Qt::LeftButton)
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case MapView::SelectionTool::None:
|
||||
if(mouseEvent->buttons() & Qt::RightButton)
|
||||
break;
|
||||
|
||||
auto sh = tile - tileStart;
|
||||
sc->selectionObjectsView.shift = QPoint(sh.x, sh.y);
|
||||
|
||||
if(sh.x || sh.y)
|
||||
{
|
||||
if(sc->selectionObjectsView.newObject)
|
||||
{
|
||||
sc->selectionObjectsView.shift = QPoint(tile.x, tile.y);
|
||||
sc->selectionObjectsView.selectObject(sc->selectionObjectsView.newObject);
|
||||
sc->selectionObjectsView.selectionMode = 2;
|
||||
}
|
||||
else if(mouseEvent->buttons() & Qt::LeftButton)
|
||||
{
|
||||
if(sc->selectionObjectsView.selectionMode == 1)
|
||||
{
|
||||
sc->selectionObjectsView.clear();
|
||||
sc->selectionObjectsView.selectObjects(tileStart.x, tileStart.y, tile.x, tile.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sc->selectionObjectsView.draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MapView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
this->update();
|
||||
|
||||
auto * sc = static_cast<MapScene*>(scene());
|
||||
if(!sc || !controller->map())
|
||||
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:
|
||||
sc->selectionObjectsView.clear();
|
||||
sc->selectionObjectsView.draw();
|
||||
|
||||
if(event->button() == Qt::RightButton)
|
||||
sc->selectionTerrainView.erase(tileStart);
|
||||
else if(event->button() == Qt::LeftButton)
|
||||
sc->selectionTerrainView.select(tileStart);
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Brush2:
|
||||
sc->selectionObjectsView.clear();
|
||||
sc->selectionObjectsView.draw();
|
||||
{
|
||||
std::array<int3, 4> extra{ int3{0, 0, 0}, int3{1, 0, 0}, int3{0, 1, 0}, int3{1, 1, 0} };
|
||||
for(auto & e : extra)
|
||||
{
|
||||
if(event->button() == Qt::RightButton)
|
||||
sc->selectionTerrainView.erase(tileStart + e);
|
||||
else if(event->button() == Qt::LeftButton)
|
||||
sc->selectionTerrainView.select(tileStart + e);
|
||||
}
|
||||
}
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Brush4:
|
||||
sc->selectionObjectsView.clear();
|
||||
sc->selectionObjectsView.draw();
|
||||
{
|
||||
std::array<int3, 16> extra{
|
||||
int3{-1, -1, 0}, int3{0, -1, 0}, int3{1, -1, 0}, int3{2, -1, 0},
|
||||
int3{-1, 0, 0}, int3{0, 0, 0}, int3{1, 0, 0}, int3{2, 0, 0},
|
||||
int3{-1, 1, 0}, int3{0, 1, 0}, int3{1, 1, 0}, int3{2, 1, 0},
|
||||
int3{-1, 2, 0}, int3{0, 2, 0}, int3{1, 2, 0}, int3{2, 2, 0}
|
||||
};
|
||||
for(auto & e : extra)
|
||||
{
|
||||
if(event->button() == Qt::RightButton)
|
||||
sc->selectionTerrainView.erase(tileStart + e);
|
||||
else if(event->button() == Qt::LeftButton)
|
||||
sc->selectionTerrainView.select(tileStart + e);
|
||||
}
|
||||
}
|
||||
sc->selectionTerrainView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::Area:
|
||||
if(event->button() == Qt::RightButton)
|
||||
break;
|
||||
|
||||
sc->selectionTerrainView.clear();
|
||||
sc->selectionTerrainView.draw();
|
||||
sc->selectionObjectsView.clear();
|
||||
sc->selectionObjectsView.draw();
|
||||
break;
|
||||
|
||||
case MapView::SelectionTool::None:
|
||||
sc->selectionTerrainView.clear();
|
||||
sc->selectionTerrainView.draw();
|
||||
|
||||
if(sc->selectionObjectsView.newObject && sc->selectionObjectsView.isSelected(sc->selectionObjectsView.newObject))
|
||||
{
|
||||
if(event->button() == Qt::RightButton)
|
||||
controller->discardObject(sc->level);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(event->button() == Qt::LeftButton)
|
||||
{
|
||||
auto * obj = sc->selectionObjectsView.selectObjectAt(tileStart.x, tileStart.y);
|
||||
if(obj)
|
||||
{
|
||||
if(sc->selectionObjectsView.isSelected(obj))
|
||||
{
|
||||
if(qApp->keyboardModifiers() & Qt::ControlModifier)
|
||||
{
|
||||
sc->selectionObjectsView.deselectObject(obj);
|
||||
sc->selectionObjectsView.selectionMode = 1;
|
||||
}
|
||||
else
|
||||
sc->selectionObjectsView.selectionMode = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!(qApp->keyboardModifiers() & Qt::ControlModifier))
|
||||
sc->selectionObjectsView.clear();
|
||||
sc->selectionObjectsView.selectionMode = 2;
|
||||
sc->selectionObjectsView.selectObject(obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sc->selectionObjectsView.clear();
|
||||
sc->selectionObjectsView.selectionMode = 1;
|
||||
}
|
||||
}
|
||||
sc->selectionObjectsView.shift = QPoint(0, 0);
|
||||
sc->selectionObjectsView.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)
|
||||
{
|
||||
this->update();
|
||||
|
||||
auto * sc = static_cast<MapScene*>(scene());
|
||||
if(!sc || !controller->map())
|
||||
return;
|
||||
|
||||
switch(selectionTool)
|
||||
{
|
||||
case MapView::SelectionTool::None:
|
||||
if(event->button() == Qt::RightButton)
|
||||
break;
|
||||
//switch position
|
||||
bool tab = false;
|
||||
if(sc->selectionObjectsView.selectionMode == 2)
|
||||
{
|
||||
if(sc->selectionObjectsView.newObject)
|
||||
{
|
||||
QString errorMsg;
|
||||
if(controller->canPlaceObject(sc->level, sc->selectionObjectsView.newObject, errorMsg))
|
||||
controller->commitObjectCreate(sc->level);
|
||||
else
|
||||
{
|
||||
QMessageBox::information(this, "Can't place object", errorMsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
controller->commitObjectShift(sc->level);
|
||||
}
|
||||
else
|
||||
{
|
||||
sc->selectionObjectsView.selectionMode = 0;
|
||||
sc->selectionObjectsView.shift = QPoint(0, 0);
|
||||
sc->selectionObjectsView.draw();
|
||||
tab = true;
|
||||
//check if we have only one object
|
||||
}
|
||||
auto selection = sc->selectionObjectsView.getSelection();
|
||||
if(selection.size() == 1)
|
||||
{
|
||||
emit openObjectProperties(*selection.begin(), tab);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MapView::viewportEvent(QEvent *event)
|
||||
{
|
||||
if(auto * sc = static_cast<MapScene*>(scene()))
|
||||
{
|
||||
//auto rect = sceneRect();
|
||||
auto rect = mapToScene(viewport()->geometry()).boundingRect();
|
||||
controller->miniScene(sc->level)->viewport.setViewport(rect.x() / 32, rect.y() / 32, rect.width() / 32, rect.height() / 32);
|
||||
}
|
||||
return QGraphicsView::viewportEvent(event);
|
||||
}
|
||||
|
||||
MapSceneBase::MapSceneBase(int lvl):
|
||||
QGraphicsScene(nullptr),
|
||||
level(lvl)
|
||||
{
|
||||
}
|
||||
|
||||
void MapSceneBase::initialize(MapController & controller)
|
||||
{
|
||||
for(auto * layer : getAbstractLayers())
|
||||
layer->initialize(controller);
|
||||
}
|
||||
|
||||
void MapSceneBase::updateViews()
|
||||
{
|
||||
for(auto * layer : getAbstractLayers())
|
||||
layer->update();
|
||||
}
|
||||
|
||||
MapScene::MapScene(int lvl):
|
||||
MapSceneBase(lvl),
|
||||
gridView(this),
|
||||
passabilityView(this),
|
||||
selectionTerrainView(this),
|
||||
terrainView(this),
|
||||
objectsView(this),
|
||||
selectionObjectsView(this),
|
||||
isTerrainSelected(false),
|
||||
isObjectSelected(false)
|
||||
{
|
||||
connect(&selectionTerrainView, &SelectionTerrainLayer::selectionMade, this, &MapScene::terrainSelected);
|
||||
connect(&selectionObjectsView, &SelectionObjectsLayer::selectionMade, this, &MapScene::objectSelected);
|
||||
}
|
||||
|
||||
std::list<AbstractLayer *> MapScene::getAbstractLayers()
|
||||
{
|
||||
//sequence is important because it defines rendering order
|
||||
return {
|
||||
&terrainView,
|
||||
&objectsView,
|
||||
&gridView,
|
||||
&passabilityView,
|
||||
&selectionTerrainView,
|
||||
&selectionObjectsView
|
||||
};
|
||||
}
|
||||
|
||||
void MapScene::updateViews()
|
||||
{
|
||||
MapSceneBase::updateViews();
|
||||
|
||||
terrainView.show(true);
|
||||
objectsView.show(true);
|
||||
selectionTerrainView.show(true);
|
||||
selectionObjectsView.show(true);
|
||||
}
|
||||
|
||||
void MapScene::terrainSelected(bool anythingSelected)
|
||||
{
|
||||
isTerrainSelected = anythingSelected;
|
||||
emit selected(isTerrainSelected || isObjectSelected);
|
||||
}
|
||||
|
||||
void MapScene::objectSelected(bool anythingSelected)
|
||||
{
|
||||
isObjectSelected = anythingSelected;
|
||||
emit selected(isTerrainSelected || isObjectSelected);
|
||||
}
|
||||
|
||||
MinimapScene::MinimapScene(int lvl):
|
||||
MapSceneBase(lvl),
|
||||
minimapView(this),
|
||||
viewport(this)
|
||||
{
|
||||
}
|
||||
|
||||
std::list<AbstractLayer *> MinimapScene::getAbstractLayers()
|
||||
{
|
||||
//sequence is important because it defines rendering order
|
||||
return {
|
||||
&minimapView,
|
||||
&viewport
|
||||
};
|
||||
}
|
||||
|
||||
void MinimapScene::updateViews()
|
||||
{
|
||||
MapSceneBase::updateViews();
|
||||
|
||||
minimapView.show(true);
|
||||
viewport.show(true);
|
||||
}
|
133
mapeditor/mapview.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#ifndef MAPVIEW_H
|
||||
#define MAPVIEW_H
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
#include "scenelayer.h"
|
||||
#include "../lib/int3.h"
|
||||
|
||||
|
||||
class CGObjectInstance;
|
||||
class MainWindow;
|
||||
class MapController;
|
||||
|
||||
class MapSceneBase : public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT;
|
||||
public:
|
||||
MapSceneBase(int lvl);
|
||||
|
||||
const int level;
|
||||
|
||||
virtual void updateViews();
|
||||
virtual void initialize(MapController &);
|
||||
|
||||
protected:
|
||||
virtual std::list<AbstractLayer *> getAbstractLayers() = 0;
|
||||
};
|
||||
|
||||
class MinimapScene : public MapSceneBase
|
||||
{
|
||||
public:
|
||||
MinimapScene(int lvl);
|
||||
|
||||
void updateViews() override;
|
||||
|
||||
MinimapLayer minimapView;
|
||||
MinimapViewLayer viewport;
|
||||
|
||||
protected:
|
||||
std::list<AbstractLayer *> getAbstractLayers() override;
|
||||
};
|
||||
|
||||
class MapScene : public MapSceneBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MapScene(int lvl);
|
||||
|
||||
void updateViews() override;
|
||||
|
||||
GridLayer gridView;
|
||||
PassabilityLayer passabilityView;
|
||||
SelectionTerrainLayer selectionTerrainView;
|
||||
TerrainLayer terrainView;
|
||||
ObjectsLayer objectsView;
|
||||
SelectionObjectsLayer selectionObjectsView;
|
||||
|
||||
signals:
|
||||
void selected(bool anything);
|
||||
|
||||
public slots:
|
||||
void terrainSelected(bool anything);
|
||||
void objectSelected(bool anything);
|
||||
|
||||
protected:
|
||||
std::list<AbstractLayer *> getAbstractLayers() override;
|
||||
|
||||
bool isTerrainSelected;
|
||||
bool isObjectSelected;
|
||||
|
||||
};
|
||||
|
||||
class MapView : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class SelectionTool
|
||||
{
|
||||
None, Brush, Brush2, Brush4, Area, Lasso
|
||||
};
|
||||
|
||||
public:
|
||||
MapView(QWidget * parent);
|
||||
void setController(MapController *);
|
||||
|
||||
SelectionTool selectionTool;
|
||||
|
||||
public slots:
|
||||
void mouseMoveEvent(QMouseEvent * mouseEvent) override;
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
void cameraChanged(const QPointF & pos);
|
||||
|
||||
signals:
|
||||
void openObjectProperties(CGObjectInstance *, bool switchTab);
|
||||
//void viewportChanged(const QRectF & rect);
|
||||
|
||||
protected:
|
||||
bool viewportEvent(QEvent *event) override;
|
||||
|
||||
private:
|
||||
MapController * controller = nullptr;
|
||||
QPointF mouseStart;
|
||||
int3 tileStart;
|
||||
int3 tilePrev;
|
||||
bool pressedOnSelected;
|
||||
};
|
||||
|
||||
class MinimapView : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MinimapView(QWidget * parent);
|
||||
void setController(MapController *);
|
||||
|
||||
void dimensions();
|
||||
|
||||
public slots:
|
||||
void mouseMoveEvent(QMouseEvent * mouseEvent) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
signals:
|
||||
void cameraPositionChanged(const QPointF & newPosition);
|
||||
|
||||
private:
|
||||
MapController * controller = nullptr;
|
||||
|
||||
int displayWidth = 192;
|
||||
int displayHeight = 192;
|
||||
};
|
||||
|
||||
#endif // MAPVIEW_H
|
68
mapeditor/objectbrowser.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "objectbrowser.h"
|
||||
#include "../lib/mapObjects/CObjectClassesHandler.h"
|
||||
|
||||
ObjectBrowser::ObjectBrowser(QObject *parent)
|
||||
: QSortFilterProxyModel{parent}, terrain(Terrain::ANY)
|
||||
{
|
||||
}
|
||||
|
||||
bool ObjectBrowser::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
|
||||
{
|
||||
bool result = QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
||||
|
||||
QModelIndex currentIndex = sourceModel()->index(source_row, 0, source_parent);
|
||||
int childCount = currentIndex.model()->rowCount(currentIndex);
|
||||
if(childCount)
|
||||
return false;
|
||||
|
||||
auto item = dynamic_cast<QStandardItemModel*>(sourceModel())->itemFromIndex(currentIndex);
|
||||
if(!item)
|
||||
return result;
|
||||
|
||||
if(!filterAcceptsRowText(source_row, source_parent))
|
||||
return false;
|
||||
|
||||
if(terrain == Terrain::ANY)
|
||||
return result;
|
||||
|
||||
auto data = item->data().toJsonObject();
|
||||
if(data.empty())
|
||||
return result;
|
||||
|
||||
auto objIdJson = data["id"];
|
||||
if(objIdJson == QJsonValue::Undefined)
|
||||
return result;
|
||||
|
||||
auto objId = data["id"].toInt();
|
||||
auto objSubId = data["subid"].toInt();
|
||||
auto templateId = data["template"].toInt();
|
||||
|
||||
auto factory = VLC->objtypeh->getHandlerFor(objId, objSubId);
|
||||
auto templ = factory->getTemplates()[templateId];
|
||||
|
||||
result = result & templ->canBePlacedAt(terrain);
|
||||
|
||||
//text filter
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ObjectBrowser::filterAcceptsRowText(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
if(source_parent.isValid())
|
||||
{
|
||||
if(filterAcceptsRowText(source_parent.row(), source_parent.parent()))
|
||||
return true;
|
||||
}
|
||||
|
||||
QModelIndex index = sourceModel()->index(source_row, 0 ,source_parent);
|
||||
if(!index.isValid())
|
||||
return false;
|
||||
|
||||
auto item = dynamic_cast<QStandardItemModel*>(sourceModel())->itemFromIndex(index);
|
||||
if(!item)
|
||||
return false;
|
||||
|
||||
return (filter.isNull() || filter.isEmpty() || item->text().contains(filter, Qt::CaseInsensitive));
|
||||
}
|
||||
|
20
mapeditor/objectbrowser.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef OBJECTBROWSER_H
|
||||
#define OBJECTBROWSER_H
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include "../lib/Terrain.h"
|
||||
|
||||
class ObjectBrowser : public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
explicit ObjectBrowser(QObject *parent = nullptr);
|
||||
|
||||
Terrain terrain;
|
||||
QString filter;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override;
|
||||
bool filterAcceptsRowText(int source_row, const QModelIndex &source_parent) const;
|
||||
};
|
||||
|
||||
#endif // OBJECTBROWSER_H
|
139
mapeditor/playerparams.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "StdInc.h"
|
||||
#include "playerparams.h"
|
||||
#include "ui_playerparams.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
|
||||
PlayerParams::PlayerParams(MapController & ctrl, int playerId, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::PlayerParams),
|
||||
controller(ctrl)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
playerColor = playerId;
|
||||
assert(controller.map()->players.size() > playerColor);
|
||||
playerInfo = controller.map()->players[playerColor];
|
||||
|
||||
//load factions
|
||||
for(auto idx : VLC->townh->getAllowedFactions())
|
||||
{
|
||||
CFaction * faction = VLC->townh->objects.at(idx);
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(faction->name));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(idx));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
ui->allowedFactions->addItem(item);
|
||||
if(playerInfo.allowedFactions.count(idx))
|
||||
item->setCheckState(Qt::Checked);
|
||||
else
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
QObject::connect(ui->allowedFactions, SIGNAL(itemChanged(QListWidgetItem*)),
|
||||
this, SLOT(allowedFactionsCheck(QListWidgetItem*)));
|
||||
|
||||
//load checks
|
||||
bool canHumanPlay = playerInfo.canHumanPlay; //need variable to restore after signal received
|
||||
playerInfo.canComputerPlay = true; //computer always can play
|
||||
ui->radioCpu->setChecked(true);
|
||||
if(canHumanPlay)
|
||||
ui->radioHuman->setChecked(true);
|
||||
if(playerInfo.isFactionRandom)
|
||||
ui->randomFaction->setChecked(true);
|
||||
if(playerInfo.generateHeroAtMainTown)
|
||||
ui->generateHero->setChecked(true);
|
||||
|
||||
//load towns
|
||||
int foundMainTown = -1;
|
||||
for(int i = 0, townIndex = 0; i < controller.map()->objects.size(); ++i)
|
||||
{
|
||||
if(auto town = dynamic_cast<CGTownInstance*>(controller.map()->objects[i].get()))
|
||||
{
|
||||
auto * ctown = town->town;
|
||||
if(!ctown)
|
||||
ctown = VLC->townh->randomTown;
|
||||
if(ctown && town->getOwner().getNum() == playerColor)
|
||||
{
|
||||
if(playerInfo.hasMainTown && playerInfo.posOfMainTown == town->pos)
|
||||
foundMainTown = townIndex;
|
||||
auto name = town->name + ", (random)";
|
||||
if(ctown->faction)
|
||||
name = town->getObjectName();
|
||||
ui->mainTown->addItem(tr(name.c_str()), QVariant::fromValue(i));
|
||||
++townIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(foundMainTown > -1)
|
||||
{
|
||||
ui->mainTown->setCurrentIndex(foundMainTown + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->generateHero->setEnabled(false);
|
||||
playerInfo.hasMainTown = false;
|
||||
playerInfo.generateHeroAtMainTown = false;
|
||||
playerInfo.posOfMainTown = int3(-1, -1, -1);
|
||||
}
|
||||
|
||||
ui->playerColor->setTitle(QString("PlayerID: %1").arg(playerId));
|
||||
show();
|
||||
}
|
||||
|
||||
PlayerParams::~PlayerParams()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PlayerParams::on_radioHuman_toggled(bool checked)
|
||||
{
|
||||
if(checked)
|
||||
playerInfo.canHumanPlay = true;
|
||||
}
|
||||
|
||||
|
||||
void PlayerParams::on_radioCpu_toggled(bool checked)
|
||||
{
|
||||
if(checked)
|
||||
playerInfo.canHumanPlay = false;
|
||||
}
|
||||
|
||||
|
||||
void PlayerParams::on_generateHero_stateChanged(int arg1)
|
||||
{
|
||||
playerInfo.generateHeroAtMainTown = ui->generateHero->isChecked();
|
||||
}
|
||||
|
||||
|
||||
void PlayerParams::on_randomFaction_stateChanged(int arg1)
|
||||
{
|
||||
playerInfo.isFactionRandom = ui->randomFaction->isChecked();
|
||||
}
|
||||
|
||||
|
||||
void PlayerParams::allowedFactionsCheck(QListWidgetItem * item)
|
||||
{
|
||||
if(item->checkState() == Qt::Checked)
|
||||
playerInfo.allowedFactions.insert(item->data(Qt::UserRole).toInt());
|
||||
else
|
||||
playerInfo.allowedFactions.erase(item->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
|
||||
void PlayerParams::on_mainTown_activated(int index)
|
||||
{
|
||||
if(index == 0) //default
|
||||
{
|
||||
ui->generateHero->setEnabled(false);
|
||||
ui->generateHero->setChecked(false);
|
||||
playerInfo.hasMainTown = false;
|
||||
playerInfo.posOfMainTown = int3(-1, -1, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->generateHero->setEnabled(true);
|
||||
auto town = controller.map()->objects.at(ui->mainTown->currentData().toInt());
|
||||
playerInfo.hasMainTown = true;
|
||||
playerInfo.posOfMainTown = town->pos;
|
||||
}
|
||||
}
|
||||
|
42
mapeditor/playerparams.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef PLAYERPARAMS_H
|
||||
#define PLAYERPARAMS_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "mapcontroller.h"
|
||||
|
||||
namespace Ui {
|
||||
class PlayerParams;
|
||||
}
|
||||
|
||||
class PlayerParams : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlayerParams(MapController & controller, int playerId, QWidget *parent = nullptr);
|
||||
~PlayerParams();
|
||||
|
||||
PlayerInfo playerInfo;
|
||||
int playerColor;
|
||||
|
||||
private slots:
|
||||
void on_radioHuman_toggled(bool checked);
|
||||
|
||||
void on_radioCpu_toggled(bool checked);
|
||||
|
||||
void on_mainTown_activated(int index);
|
||||
|
||||
void on_generateHero_stateChanged(int arg1);
|
||||
|
||||
void on_randomFaction_stateChanged(int arg1);
|
||||
|
||||
void allowedFactionsCheck(QListWidgetItem *);
|
||||
|
||||
private:
|
||||
Ui::PlayerParams *ui;
|
||||
|
||||
MapController & controller;
|
||||
};
|
||||
|
||||
#endif // PLAYERPARAMS_H
|
142
mapeditor/playerparams.ui
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlayerParams</class>
|
||||
<widget class="QWidget" name="PlayerParams">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>505</width>
|
||||
<height>160</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>160</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<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="QGroupBox" name="playerColor">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>160</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>GroupBox</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="3" column="0">
|
||||
<widget class="QComboBox" name="teamId">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>No team</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="radioHuman">
|
||||
<property name="text">
|
||||
<string>Human/CPU</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="radioCpu">
|
||||
<property name="text">
|
||||
<string>CPU only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Team</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Main town</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QCheckBox" name="randomFaction">
|
||||
<property name="text">
|
||||
<string>Random faction</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="generateHero">
|
||||
<property name="text">
|
||||
<string>Generate hero at main</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="mainTown">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>(default)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="3">
|
||||
<widget class="QListWidget" name="allowedFactions">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
77
mapeditor/playersettings.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "StdInc.h"
|
||||
#include "playersettings.h"
|
||||
#include "ui_playersettings.h"
|
||||
#include "playerparams.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
PlayerSettings::PlayerSettings(MapController & ctrl, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PlayerSettings),
|
||||
controller(ctrl)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
show();
|
||||
|
||||
int players = 0;
|
||||
for(auto & p : controller.map()->players)
|
||||
{
|
||||
if(p.canAnyonePlay())
|
||||
{
|
||||
paramWidgets.push_back(new PlayerParams(controller, players));
|
||||
ui->playersLayout->addWidget(paramWidgets.back());
|
||||
++players;
|
||||
}
|
||||
}
|
||||
|
||||
if(players < 2)
|
||||
ui->playersCount->setCurrentText("");
|
||||
else
|
||||
ui->playersCount->setCurrentIndex(players - 2);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
PlayerSettings::~PlayerSettings()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PlayerSettings::on_playersCount_currentIndexChanged(int index)
|
||||
{
|
||||
assert(index + 2 <= controller.map()->players.size());
|
||||
|
||||
for(int i = 0; i < index + 2; ++i)
|
||||
{
|
||||
if(i < paramWidgets.size())
|
||||
continue;
|
||||
|
||||
auto & p = controller.map()->players[i];
|
||||
p.canComputerPlay = true;
|
||||
paramWidgets.push_back(new PlayerParams(controller, i));
|
||||
ui->playersLayout->addWidget(paramWidgets.back());
|
||||
}
|
||||
|
||||
assert(!paramWidgets.empty());
|
||||
for(int i = paramWidgets.size() - 1; i >= index + 2; --i)
|
||||
{
|
||||
auto & p = controller.map()->players[i];
|
||||
p.canComputerPlay = false;
|
||||
p.canHumanPlay = false;
|
||||
ui->playersLayout->removeWidget(paramWidgets[i]);
|
||||
delete paramWidgets[i];
|
||||
paramWidgets.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PlayerSettings::on_pushButton_clicked()
|
||||
{
|
||||
for(auto * w : paramWidgets)
|
||||
{
|
||||
controller.map()->players[w->playerColor] = w->playerInfo;
|
||||
}
|
||||
|
||||
controller.commitChangeWithoutRedraw();
|
||||
close();
|
||||
}
|
||||
|
34
mapeditor/playersettings.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef PLAYERSETTINGS_H
|
||||
#define PLAYERSETTINGS_H
|
||||
|
||||
#include "StdInc.h"
|
||||
#include <QDialog>
|
||||
#include "playerparams.h"
|
||||
|
||||
namespace Ui {
|
||||
class PlayerSettings;
|
||||
}
|
||||
|
||||
class PlayerSettings : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlayerSettings(MapController & controller, QWidget *parent = nullptr);
|
||||
~PlayerSettings();
|
||||
|
||||
private slots:
|
||||
|
||||
void on_playersCount_currentIndexChanged(int index);
|
||||
|
||||
void on_pushButton_clicked();
|
||||
|
||||
private:
|
||||
Ui::PlayerSettings *ui;
|
||||
|
||||
std::vector<PlayerParams*> paramWidgets;
|
||||
|
||||
MapController & controller;
|
||||
};
|
||||
|
||||
#endif // PLAYERSETTINGS_H
|
117
mapeditor/playersettings.ui
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PlayerSettings</class>
|
||||
<widget class="QDialog" name="PlayerSettings">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>654</width>
|
||||
<height>283</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Player settings</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0" colspan="8">
|
||||
<widget class="QScrollArea" name="players">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="playerscomon">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>628</width>
|
||||
<height>187</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<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="playersLayout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Players</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="playersCount">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>7</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="6" colspan="2">
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>Ok</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
7
mapeditor/radiopushbutton.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "StdInc.h"
|
||||
#include "radiopushbutton.h"
|
||||
|
||||
RadioPushButton::RadioPushButton()
|
||||
{
|
||||
|
||||
}
|
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
|
564
mapeditor/scenelayer.cpp
Normal file
@@ -0,0 +1,564 @@
|
||||
#include "StdInc.h"
|
||||
#include "scenelayer.h"
|
||||
#include "mainwindow.h"
|
||||
#include "../lib/mapping/CMapEditManager.h"
|
||||
#include "inspector/inspector.h"
|
||||
#include "mapview.h"
|
||||
#include "mapcontroller.h"
|
||||
|
||||
AbstractLayer::AbstractLayer(MapSceneBase * s): scene(s)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractLayer::initialize(MapController & controller)
|
||||
{
|
||||
map = controller.map();
|
||||
handler = controller.mapHandler();
|
||||
}
|
||||
|
||||
void AbstractLayer::show(bool show)
|
||||
{
|
||||
if(isShown == show)
|
||||
return;
|
||||
|
||||
isShown = show;
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void AbstractLayer::redraw()
|
||||
{
|
||||
if(item)
|
||||
{
|
||||
if(pixmap && isShown)
|
||||
item->setPixmap(*pixmap);
|
||||
else
|
||||
item->setPixmap(emptyPixmap);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pixmap && isShown)
|
||||
item.reset(scene->addPixmap(*pixmap));
|
||||
else
|
||||
item.reset(scene->addPixmap(emptyPixmap));
|
||||
}
|
||||
}
|
||||
|
||||
GridLayer::GridLayer(MapSceneBase * s): AbstractLayer(s)
|
||||
{
|
||||
}
|
||||
|
||||
void GridLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
QPainter painter(pixmap.get());
|
||||
painter.setPen(QColor(0, 0, 0, 190));
|
||||
|
||||
for(int j = 0; j < map->height; ++j)
|
||||
{
|
||||
painter.drawLine(0, j * 32, map->width * 32 - 1, j * 32);
|
||||
}
|
||||
for(int i = 0; i < map->width; ++i)
|
||||
{
|
||||
painter.drawLine(i * 32, 0, i * 32, map->height * 32 - 1);
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
PassabilityLayer::PassabilityLayer(MapSceneBase * s): AbstractLayer(s)
|
||||
{
|
||||
}
|
||||
|
||||
void PassabilityLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
|
||||
if(scene->level == 0 || map->twoLevel)
|
||||
{
|
||||
QPainter painter(pixmap.get());
|
||||
for(int j = 0; j < map->height; ++j)
|
||||
{
|
||||
for(int i = 0; i < map->width; ++i)
|
||||
{
|
||||
auto tl = map->getTile(int3(i, j, scene->level));
|
||||
if(tl.blocked || tl.visitable)
|
||||
{
|
||||
painter.fillRect(i * 32, j * 32, 31, 31, tl.visitable ? QColor(200, 200, 0, 64) : QColor(255, 0, 0, 64));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
SelectionTerrainLayer::SelectionTerrainLayer(MapSceneBase * s): AbstractLayer(s)
|
||||
{
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
area.clear();
|
||||
areaAdd.clear();
|
||||
areaErase.clear();
|
||||
onSelection();
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::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 SelectionTerrainLayer::select(const int3 & tile)
|
||||
{
|
||||
if(!map || !map->isInTheMap(tile))
|
||||
return;
|
||||
|
||||
if(!area.count(tile))
|
||||
{
|
||||
area.insert(tile);
|
||||
areaAdd.insert(tile);
|
||||
areaErase.erase(tile);
|
||||
}
|
||||
onSelection();
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::erase(const int3 & tile)
|
||||
{
|
||||
if(!map || !map->isInTheMap(tile))
|
||||
return;
|
||||
|
||||
if(area.count(tile))
|
||||
{
|
||||
area.erase(tile);
|
||||
areaErase.insert(tile);
|
||||
areaAdd.erase(tile);
|
||||
}
|
||||
onSelection();
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::clear()
|
||||
{
|
||||
areaErase = area;
|
||||
areaAdd.clear();
|
||||
area.clear();
|
||||
onSelection();
|
||||
}
|
||||
|
||||
const std::set<int3> & SelectionTerrainLayer::selection() const
|
||||
{
|
||||
return area;
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::onSelection()
|
||||
{
|
||||
emit selectionMade(!area.empty());
|
||||
}
|
||||
|
||||
|
||||
TerrainLayer::TerrainLayer(MapSceneBase * s): AbstractLayer(s)
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
draw(false);
|
||||
}
|
||||
|
||||
void TerrainLayer::setDirty(const int3 & tile)
|
||||
{
|
||||
dirty.insert(tile);
|
||||
}
|
||||
|
||||
void TerrainLayer::draw(bool onlyDirty)
|
||||
{
|
||||
if(!pixmap)
|
||||
return;
|
||||
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
handler->drawTerrainTile(painter, t.x, t.y, scene->level);
|
||||
handler->drawRiver(painter, t.x, t.y, scene->level);
|
||||
handler->drawRoad(painter, t.x, t.y, scene->level);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int j = 0; j < map->height; ++j)
|
||||
{
|
||||
for(int i = 0; i < map->width; ++i)
|
||||
{
|
||||
handler->drawTerrainTile(painter, i, j, scene->level);
|
||||
handler->drawRiver(painter, i, j, scene->level);
|
||||
handler->drawRoad(painter, i, j, scene->level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dirty.clear();
|
||||
redraw();
|
||||
}
|
||||
|
||||
ObjectsLayer::ObjectsLayer(MapSceneBase * s): AbstractLayer(s)
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectsLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
draw(false);
|
||||
}
|
||||
|
||||
void ObjectsLayer::draw(bool onlyDirty)
|
||||
{
|
||||
if(!pixmap)
|
||||
return;
|
||||
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
QPainter painter(pixmap.get());
|
||||
std::set<const CGObjectInstance *> drawen;
|
||||
|
||||
|
||||
for(int j = 0; j < map->height; ++j)
|
||||
{
|
||||
for(int i = 0; i < map->width; ++i)
|
||||
{
|
||||
handler->drawObjects(painter, i, j, scene->level);
|
||||
/*auto & objects = main->getMapHandler()->getObjects(i, j, scene->level);
|
||||
for(auto & object : objects)
|
||||
{
|
||||
if(!object.obj || drawen.count(object.obj))
|
||||
continue;
|
||||
|
||||
if(!onlyDirty || dirty.count(object.obj))
|
||||
{
|
||||
main->getMapHandler()->drawObject(painter, object);
|
||||
drawen.insert(object.obj);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
dirty.clear();
|
||||
redraw();
|
||||
}
|
||||
|
||||
void ObjectsLayer::setDirty(int x, int y)
|
||||
{
|
||||
/*auto & objects = main->getMapHandler()->getObjects(x, y, scene->level);
|
||||
for(auto & object : objects)
|
||||
{
|
||||
if(object.obj)
|
||||
dirty.insert(object.obj);
|
||||
}*/
|
||||
}
|
||||
|
||||
void ObjectsLayer::setDirty(const CGObjectInstance * object)
|
||||
{
|
||||
}
|
||||
|
||||
SelectionObjectsLayer::SelectionObjectsLayer(MapSceneBase * s): AbstractLayer(s), newObject(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
selectedObjects.clear();
|
||||
onSelection();
|
||||
shift = QPoint();
|
||||
if(newObject)
|
||||
delete newObject;
|
||||
newObject = nullptr;
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
//pixmap->fill(QColor(0, 0, 0, 0));
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::draw()
|
||||
{
|
||||
if(!pixmap)
|
||||
return;
|
||||
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
|
||||
QPainter painter(pixmap.get());
|
||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
painter.setPen(QColor(255, 255, 255));
|
||||
|
||||
for(auto * obj : selectedObjects)
|
||||
{
|
||||
if(obj != newObject)
|
||||
{
|
||||
QRect bbox(obj->getPosition().x, obj->getPosition().y, 1, 1);
|
||||
for(auto & t : obj->getBlockedPos())
|
||||
{
|
||||
QPoint topLeft(std::min(t.x, bbox.topLeft().x()), std::min(t.y, bbox.topLeft().y()));
|
||||
bbox.setTopLeft(topLeft);
|
||||
QPoint bottomRight(std::max(t.x, bbox.bottomRight().x()), std::max(t.y, bbox.bottomRight().y()));
|
||||
bbox.setBottomRight(bottomRight);
|
||||
}
|
||||
|
||||
painter.setOpacity(1.0);
|
||||
painter.drawRect(bbox.x() * 32, bbox.y() * 32, bbox.width() * 32, bbox.height() * 32);
|
||||
}
|
||||
|
||||
//show translation
|
||||
if(selectionMode == 2 && (shift.x() || shift.y()))
|
||||
{
|
||||
painter.setOpacity(0.5);
|
||||
auto newPos = QPoint(obj->getPosition().x, obj->getPosition().y) + shift;
|
||||
handler->drawObjectAt(painter, obj, newPos.x(), newPos.y());
|
||||
}
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y) const
|
||||
{
|
||||
if(!map || !map->isInTheMap(int3(x, y, scene->level)))
|
||||
return nullptr;
|
||||
|
||||
auto & objects = handler->getObjects(x, y, scene->level);
|
||||
|
||||
//visitable is most important
|
||||
for(auto & object : objects)
|
||||
{
|
||||
if(!object.obj)
|
||||
continue;
|
||||
|
||||
if(object.obj->visitableAt(x, y))
|
||||
{
|
||||
return object.obj;
|
||||
}
|
||||
}
|
||||
|
||||
//if not visitable tile - try to get blocked
|
||||
for(auto & object : objects)
|
||||
{
|
||||
if(!object.obj)
|
||||
continue;
|
||||
|
||||
if(object.obj->blockingAt(x, y))
|
||||
{
|
||||
return object.obj;
|
||||
}
|
||||
}
|
||||
|
||||
//finally, we can take any object
|
||||
for(auto & object : objects)
|
||||
{
|
||||
if(!object.obj)
|
||||
continue;
|
||||
|
||||
if(object.obj->coveringAt(x, y))
|
||||
{
|
||||
return object.obj;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::selectObjects(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
if(x1 > x2)
|
||||
std::swap(x1, x2);
|
||||
|
||||
if(y1 > y2)
|
||||
std::swap(y1, y2);
|
||||
|
||||
for(int j = y1; j < y2; ++j)
|
||||
{
|
||||
for(int i = x1; i < x2; ++i)
|
||||
{
|
||||
for(auto & o : handler->getObjects(i, j, scene->level))
|
||||
selectObject(o.obj, false); //do not inform about each object added
|
||||
}
|
||||
}
|
||||
onSelection();
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::selectObject(CGObjectInstance * obj, bool inform /* = true */)
|
||||
{
|
||||
selectedObjects.insert(obj);
|
||||
if (inform)
|
||||
{
|
||||
onSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::deselectObject(CGObjectInstance * obj)
|
||||
{
|
||||
selectedObjects.erase(obj);
|
||||
}
|
||||
|
||||
bool SelectionObjectsLayer::isSelected(const CGObjectInstance * obj) const
|
||||
{
|
||||
return selectedObjects.count(const_cast<CGObjectInstance*>(obj));
|
||||
}
|
||||
|
||||
std::set<CGObjectInstance*> SelectionObjectsLayer::getSelection() const
|
||||
{
|
||||
return selectedObjects;
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::clear()
|
||||
{
|
||||
selectedObjects.clear();
|
||||
onSelection();
|
||||
shift.setX(0);
|
||||
shift.setY(0);
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::onSelection()
|
||||
{
|
||||
emit selectionMade(!selectedObjects.empty());
|
||||
}
|
||||
|
||||
MinimapLayer::MinimapLayer(MapSceneBase * s): AbstractLayer(s)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MinimapLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap.reset(new QPixmap(map->width, map->height));
|
||||
|
||||
QPainter painter(pixmap.get());
|
||||
//coordinate transfomation
|
||||
for(int j = 0; j < map->height; ++j)
|
||||
{
|
||||
for(int i = 0; i < map->width; ++i)
|
||||
{
|
||||
handler->drawMinimapTile(painter, i, j, scene->level);
|
||||
}
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
MinimapViewLayer::MinimapViewLayer(MapSceneBase * s): AbstractLayer(s)
|
||||
{
|
||||
}
|
||||
|
||||
void MinimapViewLayer::update()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap.reset(new QPixmap(map->width, map->height));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
|
||||
QPainter painter(pixmap.get());
|
||||
painter.setPen(QColor(255, 255, 255));
|
||||
painter.drawRect(x, y, w, h);
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void MinimapViewLayer::draw()
|
||||
{
|
||||
if(!map)
|
||||
return;
|
||||
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
|
||||
//maybe not optimal but ok
|
||||
QPainter painter(pixmap.get());
|
||||
painter.setPen(QColor(255, 255, 255));
|
||||
painter.drawRect(x, y, w, h);
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void MinimapViewLayer::setViewport(int _x, int _y, int _w, int _h)
|
||||
{
|
||||
x = _x;
|
||||
y = _y;
|
||||
w = _w;
|
||||
h = _h;
|
||||
draw();
|
||||
}
|
178
mapeditor/scenelayer.h
Normal file
@@ -0,0 +1,178 @@
|
||||
#ifndef SCENELAYER_H
|
||||
#define SCENELAYER_H
|
||||
|
||||
#include "../lib/int3.h"
|
||||
|
||||
class MapSceneBase;
|
||||
class MapScene;
|
||||
class CGObjectInstance;
|
||||
class MapController;
|
||||
class CMap;
|
||||
class MapHandler;
|
||||
|
||||
class AbstractLayer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AbstractLayer(MapSceneBase * s);
|
||||
|
||||
virtual void update() = 0;
|
||||
|
||||
void show(bool show);
|
||||
void redraw();
|
||||
void initialize(MapController & controller);
|
||||
|
||||
protected:
|
||||
MapSceneBase * scene;
|
||||
CMap * map = nullptr;
|
||||
MapHandler * handler = nullptr;
|
||||
bool isShown = false;
|
||||
|
||||
std::unique_ptr<QPixmap> pixmap;
|
||||
QPixmap emptyPixmap;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QGraphicsPixmapItem> item;
|
||||
};
|
||||
|
||||
|
||||
class GridLayer: public AbstractLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
GridLayer(MapSceneBase * s);
|
||||
|
||||
void update() override;
|
||||
};
|
||||
|
||||
class PassabilityLayer: public AbstractLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PassabilityLayer(MapSceneBase * s);
|
||||
|
||||
void update() override;
|
||||
};
|
||||
|
||||
class SelectionTerrainLayer: public AbstractLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectionTerrainLayer(MapSceneBase* s);
|
||||
|
||||
void update() override;
|
||||
|
||||
void draw();
|
||||
void select(const int3 & tile);
|
||||
void erase(const int3 & tile);
|
||||
void clear();
|
||||
|
||||
const std::set<int3> & selection() const;
|
||||
|
||||
signals:
|
||||
void selectionMade(bool anythingSlected);
|
||||
|
||||
private:
|
||||
std::set<int3> area, areaAdd, areaErase;
|
||||
|
||||
void onSelection();
|
||||
};
|
||||
|
||||
|
||||
class TerrainLayer: public AbstractLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TerrainLayer(MapSceneBase * s);
|
||||
|
||||
void update() override;
|
||||
|
||||
void draw(bool onlyDirty = true);
|
||||
void setDirty(const int3 & tile);
|
||||
|
||||
private:
|
||||
std::set<int3> dirty;
|
||||
};
|
||||
|
||||
|
||||
class ObjectsLayer: public AbstractLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ObjectsLayer(MapSceneBase * s);
|
||||
|
||||
void update() override;
|
||||
|
||||
void draw(bool onlyDirty = true); //TODO: implement dirty
|
||||
|
||||
void setDirty(int x, int y);
|
||||
void setDirty(const CGObjectInstance * object);
|
||||
|
||||
private:
|
||||
std::set<const CGObjectInstance *> objDirty;
|
||||
std::set<int3> dirty;
|
||||
};
|
||||
|
||||
|
||||
class SelectionObjectsLayer: public AbstractLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectionObjectsLayer(MapSceneBase* s);
|
||||
|
||||
void update() override;
|
||||
|
||||
void draw();
|
||||
|
||||
CGObjectInstance * selectObjectAt(int x, int y) const;
|
||||
void selectObjects(int x1, int y1, int x2, int y2);
|
||||
void selectObject(CGObjectInstance *, bool inform = true);
|
||||
void deselectObject(CGObjectInstance *);
|
||||
bool isSelected(const CGObjectInstance *) const;
|
||||
std::set<CGObjectInstance*> getSelection() const;
|
||||
void moveSelection(int x, int y);
|
||||
void clear();
|
||||
|
||||
QPoint shift;
|
||||
CGObjectInstance * newObject;
|
||||
//FIXME: magic number
|
||||
int selectionMode = 0; //0 - nothing, 1 - selection, 2 - movement
|
||||
|
||||
signals:
|
||||
void selectionMade(bool anythingSlected);
|
||||
|
||||
private:
|
||||
std::set<CGObjectInstance *> selectedObjects;
|
||||
|
||||
void onSelection();
|
||||
};
|
||||
|
||||
class MinimapLayer: public AbstractLayer
|
||||
{
|
||||
public:
|
||||
MinimapLayer(MapSceneBase * s);
|
||||
|
||||
void update() override;
|
||||
};
|
||||
|
||||
class MinimapViewLayer: public AbstractLayer
|
||||
{
|
||||
public:
|
||||
MinimapViewLayer(MapSceneBase * s);
|
||||
|
||||
void setViewport(int x, int y, int w, int h);
|
||||
|
||||
void draw();
|
||||
void update() override;
|
||||
|
||||
int viewportX() const {return x;}
|
||||
int viewportY() const {return y;}
|
||||
int viewportWidth() const {return w;}
|
||||
int viewportHeight() const {return h;}
|
||||
|
||||
private:
|
||||
int x = 0, y = 0, w = 1, h = 1;
|
||||
|
||||
};
|
||||
|
||||
#endif // SCENELAYER_H
|
59
mapeditor/spoiler.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "StdInc.h"
|
||||
#include <QPropertyAnimation>
|
||||
|
||||
#include "spoiler.h"
|
||||
|
||||
Spoiler::Spoiler(const QString & title, const int animationDuration, QWidget *parent) : QWidget(parent), animationDuration(animationDuration)
|
||||
{
|
||||
toggleButton.setStyleSheet("QToolButton { border: none; }");
|
||||
toggleButton.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
toggleButton.setArrowType(Qt::ArrowType::RightArrow);
|
||||
toggleButton.setText(title);
|
||||
toggleButton.setCheckable(true);
|
||||
toggleButton.setChecked(false);
|
||||
|
||||
headerLine.setFrameShape(QFrame::HLine);
|
||||
headerLine.setFrameShadow(QFrame::Sunken);
|
||||
headerLine.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
|
||||
|
||||
contentArea.setStyleSheet("QScrollArea { background-color: white; border: none; }");
|
||||
contentArea.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
// start out collapsed
|
||||
contentArea.setMaximumHeight(0);
|
||||
contentArea.setMinimumHeight(0);
|
||||
// let the entire widget grow and shrink with its content
|
||||
toggleAnimation.addAnimation(new QPropertyAnimation(this, "minimumHeight"));
|
||||
toggleAnimation.addAnimation(new QPropertyAnimation(this, "maximumHeight"));
|
||||
toggleAnimation.addAnimation(new QPropertyAnimation(&contentArea, "maximumHeight"));
|
||||
// don't waste space
|
||||
mainLayout.setVerticalSpacing(0);
|
||||
mainLayout.setContentsMargins(0, 0, 0, 0);
|
||||
int row = 0;
|
||||
mainLayout.addWidget(&toggleButton, row, 0, 1, 1, Qt::AlignLeft);
|
||||
mainLayout.addWidget(&headerLine, row++, 2, 1, 1);
|
||||
mainLayout.addWidget(&contentArea, row, 0, 1, 3);
|
||||
setLayout(&mainLayout);
|
||||
QObject::connect(&toggleButton, &QToolButton::clicked, [this](const bool checked) {
|
||||
toggleButton.setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);
|
||||
toggleAnimation.setDirection(checked ? QAbstractAnimation::Forward : QAbstractAnimation::Backward);
|
||||
toggleAnimation.start();
|
||||
});
|
||||
}
|
||||
|
||||
void Spoiler::setContentLayout(QLayout & contentLayout)
|
||||
{
|
||||
delete contentArea.layout();
|
||||
contentArea.setLayout(&contentLayout);
|
||||
const auto collapsedHeight = sizeHint().height() - contentArea.maximumHeight();
|
||||
auto contentHeight = contentLayout.sizeHint().height();
|
||||
for (int i = 0; i < toggleAnimation.animationCount() - 1; ++i) {
|
||||
QPropertyAnimation * spoilerAnimation = static_cast<QPropertyAnimation *>(toggleAnimation.animationAt(i));
|
||||
spoilerAnimation->setDuration(animationDuration);
|
||||
spoilerAnimation->setStartValue(collapsedHeight);
|
||||
spoilerAnimation->setEndValue(collapsedHeight + contentHeight);
|
||||
}
|
||||
QPropertyAnimation * contentAnimation = static_cast<QPropertyAnimation *>(toggleAnimation.animationAt(toggleAnimation.animationCount() - 1));
|
||||
contentAnimation->setDuration(animationDuration);
|
||||
contentAnimation->setStartValue(0);
|
||||
contentAnimation->setEndValue(contentHeight);
|
||||
}
|
27
mapeditor/spoiler.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef SPOILER_H
|
||||
#define SPOILER_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QGridLayout>
|
||||
#include <QParallelAnimationGroup>
|
||||
#include <QScrollArea>
|
||||
#include <QToolButton>
|
||||
#include <QWidget>
|
||||
|
||||
class Spoiler : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QGridLayout mainLayout;
|
||||
QToolButton toggleButton;
|
||||
QFrame headerLine;
|
||||
QParallelAnimationGroup toggleAnimation;
|
||||
QScrollArea contentArea;
|
||||
int animationDuration{300};
|
||||
|
||||
public:
|
||||
explicit Spoiler(const QString & title = "", const int animationDuration = 300, QWidget *parent = 0);
|
||||
void setContentLayout(QLayout & contentLayout);
|
||||
};
|
||||
|
||||
#endif // SPOILER_H
|
159
mapeditor/validator.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "StdInc.h"
|
||||
#include "validator.h"
|
||||
#include "ui_validator.h"
|
||||
#include "../lib/mapObjects/MapObjects.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
|
||||
Validator::Validator(const CMap * map, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::Validator)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
show();
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
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
|
||||
{
|
||||
//check player settings
|
||||
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);
|
||||
|
||||
//checking all objects in the map
|
||||
for(auto o : map->objects)
|
||||
{
|
||||
//owners for objects
|
||||
if(o->getOwner() == PlayerColor::UNFLAGGABLE)
|
||||
{
|
||||
if(dynamic_cast<CGMine*>(o.get()) ||
|
||||
dynamic_cast<CGDwelling*>(o.get()) ||
|
||||
dynamic_cast<CGTownInstance*>(o.get()) ||
|
||||
dynamic_cast<CGGarrison*>(o.get()) ||
|
||||
dynamic_cast<CGHeroInstance*>(o.get()))
|
||||
{
|
||||
issues.emplace_back(QString("Armored instance %1 is UNFLAGGABLE but must have NEUTRAL or player owner").arg(o->instanceName.c_str()), true);
|
||||
}
|
||||
}
|
||||
//checking towns
|
||||
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()];
|
||||
}
|
||||
//checking heroes and prisons
|
||||
if(auto * ins = dynamic_cast<CGHeroInstance*>(o.get()))
|
||||
{
|
||||
if(ins->ID == Obj::PRISON)
|
||||
{
|
||||
if(ins->getOwner() != PlayerColor::NEUTRAL)
|
||||
issues.emplace_back(QString("Prison %1 must be a NEUTRAL").arg(ins->instanceName.c_str()), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
if(ins->type)
|
||||
{
|
||||
if(!map->allowedHeroes[ins->type->getId().getNum()])
|
||||
issues.emplace_back(QString("Hero %1 is prohibited by map settings").arg(ins->instanceName.c_str()), false);
|
||||
}
|
||||
else
|
||||
issues.emplace_back(QString("Hero %1 has an empty type and must be removed").arg(ins->instanceName.c_str()), true);
|
||||
}
|
||||
|
||||
//checking for arts
|
||||
if(auto * ins = dynamic_cast<CGArtifact*>(o.get()))
|
||||
{
|
||||
if(ins->ID == Obj::SPELL_SCROLL)
|
||||
{
|
||||
if(ins->storedArtifact)
|
||||
{
|
||||
if(!map->allowedSpell[ins->storedArtifact->id.getNum()])
|
||||
issues.emplace_back(QString("Spell scroll %1 is prohibited by map settings").arg(ins->instanceName.c_str()), false);
|
||||
}
|
||||
else
|
||||
issues.emplace_back(QString("Spell scroll %1 doesn't have instance assigned and must be removed").arg(ins->instanceName.c_str()), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ins->ID == Obj::ARTIFACT && !map->allowedArtifact[ins->subID])
|
||||
{
|
||||
issues.emplace_back(QString("Artifact %1 is prohibited by map settings").arg(ins->instanceName.c_str()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//verification of starting towns
|
||||
for(auto & mp : amountOfCastles)
|
||||
if(mp.second == 0)
|
||||
issues.emplace_back(QString("Player %1 doesn't have any starting town").arg(mp.first), false);
|
||||
|
||||
//verification of map name and description
|
||||
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
@@ -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
@@ -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>
|
405
mapeditor/windownewmap.cpp
Normal file
@@ -0,0 +1,405 @@
|
||||
#include "StdInc.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/rmg/CRmgTemplateStorage.h"
|
||||
#include "../lib/rmg/CRmgTemplate.h"
|
||||
#include "../lib/rmg/CMapGenerator.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/mapping/CMapEditManager.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
|
||||
#include "windownewmap.h"
|
||||
#include "ui_windownewmap.h"
|
||||
#include "mainwindow.h"
|
||||
#include "generatorprogress.h"
|
||||
|
||||
WindowNewMap::WindowNewMap(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::WindowNewMap)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
setWindowModality(Qt::ApplicationModal);
|
||||
|
||||
loadUserSettings();
|
||||
|
||||
ui->widthTxt->setInputMask("d00");
|
||||
ui->heightTxt->setInputMask("d00");
|
||||
|
||||
//setup initial parameters - can depend on loaded settings
|
||||
mapGenOptions.setWidth(ui->widthTxt->text().toInt());
|
||||
mapGenOptions.setHeight(ui->heightTxt->text().toInt());
|
||||
bool twoLevel = ui->twoLevelCheck->isChecked();
|
||||
mapGenOptions.setHasTwoLevels(twoLevel);
|
||||
updateTemplateList();
|
||||
|
||||
loadLastTemplate();
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
WindowNewMap::~WindowNewMap()
|
||||
{
|
||||
saveUserSettings();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void WindowNewMap::loadUserSettings()
|
||||
{
|
||||
//load last saved settings
|
||||
QSettings s(Ui::teamName, Ui::appName);
|
||||
|
||||
auto width = s.value(newMapWidth);
|
||||
if (width.isValid())
|
||||
{
|
||||
ui->widthTxt->setText(width.toString());
|
||||
}
|
||||
auto height = s.value(newMapHeight);
|
||||
if (height.isValid())
|
||||
{
|
||||
ui->heightTxt->setText(height.toString());
|
||||
}
|
||||
auto twoLevel = s.value(newMapTwoLevel);
|
||||
if (twoLevel.isValid())
|
||||
{
|
||||
ui->twoLevelCheck->setChecked(twoLevel.toBool());
|
||||
}
|
||||
auto generateRandom = s.value(newMapGenerateRandom);
|
||||
if (generateRandom.isValid())
|
||||
{
|
||||
ui->randomMapCheck->setChecked(generateRandom.toBool());
|
||||
}
|
||||
auto players = s.value(newMapPlayers);
|
||||
if (players.isValid())
|
||||
{
|
||||
ui->humanCombo->setCurrentIndex(players.toInt());
|
||||
}
|
||||
auto cpuPlayers = s.value(newMapCpuPlayers);
|
||||
if (cpuPlayers.isValid())
|
||||
{
|
||||
ui->cpuCombo->setCurrentIndex(cpuPlayers.toInt());
|
||||
}
|
||||
//TODO: teams when implemented
|
||||
|
||||
auto waterContent = s.value(newMapWaterContent);
|
||||
if (waterContent.isValid())
|
||||
{
|
||||
switch (waterContent.toInt())
|
||||
{
|
||||
case EWaterContent::RANDOM:
|
||||
ui->waterOpt1->setChecked(true); break;
|
||||
case EWaterContent::NONE:
|
||||
ui->waterOpt2->setChecked(true); break;
|
||||
case EWaterContent::NORMAL:
|
||||
ui->waterOpt3->setChecked(true); break;
|
||||
case EWaterContent::ISLANDS:
|
||||
ui->waterOpt4->setChecked(true); break;
|
||||
}
|
||||
|
||||
}
|
||||
auto monsterStrength = s.value(newMapMonsterStrength);
|
||||
if (monsterStrength.isValid())
|
||||
{
|
||||
switch (monsterStrength.toInt())
|
||||
{
|
||||
case EMonsterStrength::RANDOM:
|
||||
ui->monsterOpt1->setChecked(true); break;
|
||||
case EMonsterStrength::GLOBAL_WEAK:
|
||||
ui->monsterOpt2->setChecked(true); break;
|
||||
case EMonsterStrength::GLOBAL_NORMAL:
|
||||
ui->monsterOpt3->setChecked(true); break;
|
||||
case EMonsterStrength::GLOBAL_STRONG:
|
||||
ui->monsterOpt4->setChecked(true); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WindowNewMap::loadLastTemplate()
|
||||
{
|
||||
//this requires already loaded template list
|
||||
|
||||
QSettings s(Ui::teamName, Ui::appName);
|
||||
auto templateName = s.value(newMapTemplate);
|
||||
if (templateName.isValid())
|
||||
{
|
||||
auto qstr = templateName.toString();
|
||||
|
||||
//Template might have been removed, then silently comboBox will be set to empty string
|
||||
auto index = ui->templateCombo->findText(qstr);
|
||||
ui->templateCombo->setCurrentIndex(index);
|
||||
on_templateCombo_activated(index);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowNewMap::saveUserSettings()
|
||||
{
|
||||
QSettings s(Ui::teamName, Ui::appName);
|
||||
s.setValue(newMapWidth, ui->widthTxt->text().toInt());
|
||||
s.setValue(newMapHeight, ui->heightTxt->text().toInt());
|
||||
s.setValue(newMapTwoLevel, ui->twoLevelCheck->isChecked());
|
||||
s.setValue(newMapGenerateRandom, ui->randomMapCheck->isChecked());
|
||||
|
||||
s.setValue(newMapPlayers,ui->humanCombo->currentIndex());
|
||||
s.setValue(newMapCpuPlayers,ui->cpuCombo->currentIndex());
|
||||
//TODO: teams when implemented
|
||||
|
||||
EWaterContent::EWaterContent water = EWaterContent::RANDOM;
|
||||
if(ui->waterOpt1->isChecked())
|
||||
water = EWaterContent::RANDOM;
|
||||
else if(ui->waterOpt2->isChecked())
|
||||
water = EWaterContent::NONE;
|
||||
else if(ui->waterOpt3->isChecked())
|
||||
water = EWaterContent::NORMAL;
|
||||
else if(ui->waterOpt4->isChecked())
|
||||
water = EWaterContent::ISLANDS;
|
||||
s.setValue(newMapWaterContent, static_cast<int>(water));
|
||||
|
||||
EMonsterStrength::EMonsterStrength monster = EMonsterStrength::RANDOM;
|
||||
if(ui->monsterOpt1->isChecked())
|
||||
monster = EMonsterStrength::RANDOM;
|
||||
else if(ui->monsterOpt2->isChecked())
|
||||
monster = EMonsterStrength::GLOBAL_WEAK;
|
||||
else if(ui->monsterOpt3->isChecked())
|
||||
monster = EMonsterStrength::GLOBAL_NORMAL;
|
||||
else if(ui->monsterOpt4->isChecked())
|
||||
monster = EMonsterStrength::GLOBAL_STRONG;
|
||||
s.setValue(newMapMonsterStrength, static_cast<int>(monster));
|
||||
|
||||
auto templateName = ui->templateCombo->currentText();
|
||||
if (templateName.size() && templateName != defaultTemplate)
|
||||
{
|
||||
s.setValue(newMapTemplate, templateName);
|
||||
}
|
||||
else
|
||||
{
|
||||
s.setValue(newMapTemplate, "");
|
||||
}
|
||||
}
|
||||
|
||||
void WindowNewMap::on_cancelButton_clicked()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void generateRandomMap(CMapGenerator & gen, MainWindow * window)
|
||||
{
|
||||
window->controller.setMap(gen.generate());
|
||||
}
|
||||
|
||||
std::unique_ptr<CMap> generateEmptyMap(CMapGenOptions & options)
|
||||
{
|
||||
std::unique_ptr<CMap> map(new CMap);
|
||||
map->version = EMapFormat::VCMI;
|
||||
map->width = options.getWidth();
|
||||
map->height = options.getHeight();
|
||||
map->twoLevel = options.getHasTwoLevels();
|
||||
|
||||
map->initTerrain();
|
||||
map->getEditManager()->clearTerrain(&CRandomGenerator::getDefault());
|
||||
|
||||
return std::move(map);
|
||||
}
|
||||
|
||||
void WindowNewMap::on_okButtong_clicked()
|
||||
{
|
||||
EWaterContent::EWaterContent water = EWaterContent::RANDOM;
|
||||
EMonsterStrength::EMonsterStrength monster = EMonsterStrength::RANDOM;
|
||||
if(ui->waterOpt1->isChecked())
|
||||
water = EWaterContent::RANDOM;
|
||||
if(ui->waterOpt2->isChecked())
|
||||
water = EWaterContent::NONE;
|
||||
if(ui->waterOpt3->isChecked())
|
||||
water = EWaterContent::NORMAL;
|
||||
if(ui->waterOpt4->isChecked())
|
||||
water = EWaterContent::ISLANDS;
|
||||
if(ui->monsterOpt1->isChecked())
|
||||
monster = EMonsterStrength::RANDOM;
|
||||
if(ui->monsterOpt2->isChecked())
|
||||
monster = EMonsterStrength::GLOBAL_WEAK;
|
||||
if(ui->monsterOpt3->isChecked())
|
||||
monster = EMonsterStrength::GLOBAL_NORMAL;
|
||||
if(ui->monsterOpt4->isChecked())
|
||||
monster = EMonsterStrength::GLOBAL_STRONG;
|
||||
|
||||
mapGenOptions.setWaterContent(water);
|
||||
mapGenOptions.setMonsterStrength(monster);
|
||||
|
||||
std::unique_ptr<CMap> nmap;
|
||||
if(ui->randomMapCheck->isChecked())
|
||||
{
|
||||
//verify map template
|
||||
if(mapGenOptions.getPossibleTemplates().empty())
|
||||
{
|
||||
QMessageBox::warning(this, "No template", "No template for parameters scecified. Random map cannot be generated.");
|
||||
return;
|
||||
}
|
||||
|
||||
CMapGenerator generator(mapGenOptions);
|
||||
auto progressBarWnd = new GeneratorProgress(generator, this);
|
||||
progressBarWnd->show();
|
||||
|
||||
try
|
||||
{
|
||||
auto f = std::async(std::launch::async, &CMapGenerator::generate, &generator);
|
||||
progressBarWnd->update();
|
||||
nmap = f.get();
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
{
|
||||
QMessageBox::critical(this, "RMG failure", e.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto f = std::async(std::launch::async, &::generateEmptyMap, std::ref(mapGenOptions));
|
||||
nmap = f.get();
|
||||
}
|
||||
|
||||
|
||||
static_cast<MainWindow*>(parent())->controller.setMap(std::move(nmap));
|
||||
static_cast<MainWindow*>(parent())->initializeMap(true);
|
||||
close();
|
||||
}
|
||||
|
||||
void WindowNewMap::on_sizeCombo_activated(int index)
|
||||
{
|
||||
std::map<int, std::pair<int, int>> sizes
|
||||
{
|
||||
{0, {36, 36}},
|
||||
{1, {72, 72}},
|
||||
{2, {108, 108}},
|
||||
{3, {144, 144}},
|
||||
};
|
||||
|
||||
ui->widthTxt->setText(QString::number(sizes[index].first));
|
||||
ui->heightTxt->setText(QString::number(sizes[index].second));
|
||||
}
|
||||
|
||||
|
||||
void WindowNewMap::on_twoLevelCheck_stateChanged(int arg1)
|
||||
{
|
||||
bool twoLevel = ui->twoLevelCheck->isChecked();
|
||||
mapGenOptions.setHasTwoLevels(twoLevel);
|
||||
updateTemplateList();
|
||||
}
|
||||
|
||||
|
||||
void WindowNewMap::on_humanCombo_activated(int index)
|
||||
{
|
||||
int humans = players.at(index);
|
||||
if(humans > playerLimit)
|
||||
{
|
||||
humans = playerLimit;
|
||||
ui->humanCombo->setCurrentIndex(humans);
|
||||
return;
|
||||
}
|
||||
|
||||
mapGenOptions.setPlayerCount(humans);
|
||||
|
||||
int teams = mapGenOptions.getTeamCount();
|
||||
if(teams > humans - 1)
|
||||
{
|
||||
teams = humans - 1;
|
||||
//TBD
|
||||
}
|
||||
|
||||
int cpu = mapGenOptions.getCompOnlyPlayerCount();
|
||||
if(cpu > playerLimit - humans)
|
||||
{
|
||||
cpu = playerLimit - humans;
|
||||
ui->cpuCombo->setCurrentIndex(cpu + 1);
|
||||
}
|
||||
|
||||
int cpuTeams = mapGenOptions.getCompOnlyTeamCount(); //comp only players - 1
|
||||
if(cpuTeams > cpu - 1)
|
||||
{
|
||||
cpuTeams = cpu - 1;
|
||||
//TBD
|
||||
}
|
||||
|
||||
//void setMapTemplate(const CRmgTemplate * value);
|
||||
updateTemplateList();
|
||||
}
|
||||
|
||||
|
||||
void WindowNewMap::on_cpuCombo_activated(int index)
|
||||
{
|
||||
int humans = mapGenOptions.getPlayerCount();
|
||||
int cpu = cpuPlayers.at(index);
|
||||
if(cpu > playerLimit - humans)
|
||||
{
|
||||
cpu = playerLimit - humans;
|
||||
ui->cpuCombo->setCurrentIndex(cpu + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
mapGenOptions.setCompOnlyPlayerCount(cpu);
|
||||
updateTemplateList();
|
||||
}
|
||||
|
||||
|
||||
void WindowNewMap::on_randomMapCheck_stateChanged(int arg1)
|
||||
{
|
||||
randomMap = ui->randomMapCheck->isChecked();
|
||||
ui->templateCombo->setEnabled(randomMap);
|
||||
updateTemplateList();
|
||||
}
|
||||
|
||||
|
||||
void WindowNewMap::on_templateCombo_activated(int index)
|
||||
{
|
||||
if(index == 0)
|
||||
{
|
||||
mapGenOptions.setMapTemplate(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto * templ = VLC->tplh->getTemplateByName(ui->templateCombo->currentText().toStdString());
|
||||
mapGenOptions.setMapTemplate(templ);
|
||||
}
|
||||
|
||||
|
||||
void WindowNewMap::on_widthTxt_textChanged(const QString &arg1)
|
||||
{
|
||||
int sz = arg1.toInt();
|
||||
if(sz > 1)
|
||||
{
|
||||
mapGenOptions.setWidth(arg1.toInt());
|
||||
updateTemplateList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WindowNewMap::on_heightTxt_textChanged(const QString &arg1)
|
||||
{
|
||||
int sz = arg1.toInt();
|
||||
if(sz > 1)
|
||||
{
|
||||
mapGenOptions.setHeight(arg1.toInt());
|
||||
updateTemplateList();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowNewMap::updateTemplateList()
|
||||
{
|
||||
ui->templateCombo->clear();
|
||||
ui->templateCombo->setCurrentIndex(-1);
|
||||
|
||||
if(!randomMap)
|
||||
return;
|
||||
|
||||
mapGenOptions.setMapTemplate(nullptr);
|
||||
auto templates = mapGenOptions.getPossibleTemplates();
|
||||
if(templates.empty())
|
||||
return;
|
||||
|
||||
ui->templateCombo->addItem(defaultTemplate);
|
||||
|
||||
for(auto * templ : templates)
|
||||
{
|
||||
ui->templateCombo->addItem(QString::fromStdString(templ->getName()));
|
||||
}
|
||||
|
||||
ui->templateCombo->setCurrentIndex(0);
|
||||
}
|
96
mapeditor/windownewmap.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef WINDOWNEWMAP_H
|
||||
#define WINDOWNEWMAP_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "../lib/rmg/CMapGenOptions.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class WindowNewMap;
|
||||
}
|
||||
|
||||
class WindowNewMap : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QString newMapWidth = "NewMapWindow/Width";
|
||||
const QString newMapHeight = "NewMapWindow/Height";
|
||||
const QString newMapTwoLevel = "NewMapWindow/TwoLevel";
|
||||
const QString newMapGenerateRandom = "NewMapWindow/GenerateRandom";
|
||||
const QString newMapPlayers = "NewMapWindow/Players"; //map index
|
||||
const QString newMapCpuPlayers = "NewMapWindow/CpuPlayers"; //map index
|
||||
const QString newMapWaterContent = "NewMapWindow/WaterContent";
|
||||
const QString newMapMonsterStrength = "NewMapWindow/MonsterStrength";
|
||||
const QString newMapTemplate = "NewMapWindow/Template";
|
||||
|
||||
const QString defaultTemplate = "[default]";
|
||||
|
||||
const int playerLimit = 8;
|
||||
|
||||
const std::map<int, int> players
|
||||
{
|
||||
{0, CMapGenOptions::RANDOM_SIZE},
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{3, 3},
|
||||
{4, 4},
|
||||
{5, 5},
|
||||
{6, 6},
|
||||
{7, 7},
|
||||
{8, 8}
|
||||
};
|
||||
|
||||
const std::map<int, int> cpuPlayers
|
||||
{
|
||||
{0, CMapGenOptions::RANDOM_SIZE},
|
||||
{1, 0},
|
||||
{2, 1},
|
||||
{3, 2},
|
||||
{4, 3},
|
||||
{5, 4},
|
||||
{6, 5},
|
||||
{7, 6},
|
||||
{8, 7}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit WindowNewMap(QWidget *parent = nullptr);
|
||||
~WindowNewMap();
|
||||
|
||||
private slots:
|
||||
void on_cancelButton_clicked();
|
||||
|
||||
void on_okButtong_clicked();
|
||||
|
||||
void on_sizeCombo_activated(int index);
|
||||
|
||||
void on_twoLevelCheck_stateChanged(int arg1);
|
||||
|
||||
void on_humanCombo_activated(int index);
|
||||
|
||||
void on_cpuCombo_activated(int index);
|
||||
|
||||
void on_randomMapCheck_stateChanged(int arg1);
|
||||
|
||||
void on_templateCombo_activated(int index);
|
||||
|
||||
void on_widthTxt_textChanged(const QString &arg1);
|
||||
|
||||
void on_heightTxt_textChanged(const QString &arg1);
|
||||
|
||||
private:
|
||||
|
||||
void updateTemplateList();
|
||||
|
||||
void loadUserSettings();
|
||||
void loadLastTemplate();
|
||||
void saveUserSettings();
|
||||
|
||||
private:
|
||||
Ui::WindowNewMap *ui;
|
||||
|
||||
CMapGenOptions mapGenOptions;
|
||||
bool randomMap = false;
|
||||
};
|
||||
|
||||
#endif // WINDOWNEWMAP_H
|
784
mapeditor/windownewmap.ui
Normal file
@@ -0,0 +1,784 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>WindowNewMap</class>
|
||||
<widget class="QDialog" name="WindowNewMap">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>448</width>
|
||||
<height>379</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>390</width>
|
||||
<height>351</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>448</width>
|
||||
<height>379</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Create new map</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>291</width>
|
||||
<height>81</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Map size</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>261</width>
|
||||
<height>68</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="twoLevelCheck">
|
||||
<property name="text">
|
||||
<string>Two level map</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="widthTxt">
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>36</string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLineEdit" name="heightTxt">
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>36</string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>96</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>8</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="sizeCombo">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>96</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>S (36x36)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>M (72x72)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>L (108x108)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>XL (144x144)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>120</y>
|
||||
<width>431</width>
|
||||
<height>251</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Random map</string>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>411</width>
|
||||
<height>91</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Players</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>391</width>
|
||||
<height>68</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,1">
|
||||
<item row="0" column="3">
|
||||
<widget class="QComboBox" name="humanTeamsCombo">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QComboBox" name="cpuTeamsCombo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>96</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Human/Computer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="humanCombo">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>96</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Random</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>7</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Computer only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="cpuCombo">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Random</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>7</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>170</y>
|
||||
<width>411</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Monster strength</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>411</width>
|
||||
<height>26</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1,1,1,1">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="monsterOpt1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Random</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="monsterOpt2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Weak</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="monsterOpt3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Normal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="monsterOpt4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Strong</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>120</y>
|
||||
<width>411</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>480</width>
|
||||
<height>96</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Water content</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>20</y>
|
||||
<width>411</width>
|
||||
<height>26</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,1,1,1">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="waterOpt1">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>144</width>
|
||||
<height>96</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Random</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="waterOpt2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>144</width>
|
||||
<height>96</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="waterOpt3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>144</width>
|
||||
<height>96</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Normal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="waterOpt4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>144</width>
|
||||
<height>96</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Islands</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>220</y>
|
||||
<width>411</width>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Template</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="templateCombo">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QCheckBox" name="randomMapCheck">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>100</y>
|
||||
<width>291</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Generate random map</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>20</y>
|
||||
<width>111</width>
|
||||
<height>101</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="okButtong">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ok</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>36</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|