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> | ||||