| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -65,6 +65,7 @@ endif() | ||||
| option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF) | ||||
| option(ENABLE_LUA "Enable compilation of LUA scripting module" OFF) | ||||
| option(ENABLE_LAUNCHER "Enable compilation of launcher" ON) | ||||
| option(ENABLE_EDITOR "Enable compilation of map editor" ON) | ||||
| if(APPLE_IOS) | ||||
| 	set(BUNDLE_IDENTIFIER_PREFIX "" CACHE STRING "Bundle identifier prefix") | ||||
| 	set(APP_DISPLAY_NAME "VCMI" CACHE STRING "App name on the home screen") | ||||
| @@ -324,7 +325,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) | ||||
| @@ -455,6 +456,9 @@ endif() | ||||
| if(ENABLE_LAUNCHER) | ||||
| 	add_subdirectory(launcher) | ||||
| endif() | ||||
| if(ENABLE_EDITOR) | ||||
| 	add_subdirectory(mapeditor) | ||||
| endif() | ||||
| add_subdirectory(client) | ||||
| add_subdirectory(server) | ||||
| add_subdirectory_with_folder("AI" AI) | ||||
| @@ -494,7 +498,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 | ||||
|   | ||||
| @@ -119,7 +119,8 @@ | ||||
|             "cacheVariables": { | ||||
|                 "CMAKE_SYSTEM_NAME": "iOS", | ||||
|                 "FORCE_BUNDLED_FL": "ON", | ||||
|                 "FORCE_BUNDLED_MINIZIP": "ON" | ||||
|                 "FORCE_BUNDLED_MINIZIP": "ON", | ||||
|                 "ENABLE_EDITOR" : "OFF" | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|   | ||||
							
								
								
									
										109
									
								
								config/schemas/terrain.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,109 @@ | ||||
| { | ||||
| 	"type":"object", | ||||
| 	"$schema": "http://json-schema.org/draft-04/schema", | ||||
| 	"title" : "VCMI terrain format", | ||||
| 	"description" : "Format used to define new terrains in VCMI", | ||||
| 	"required" : [ "tiles", "code", "moveCost" ], | ||||
|  | ||||
| 	"additionalProperties" : false, | ||||
| 	"properties":{ | ||||
| 		"moveCost": | ||||
| 		{ | ||||
| 			"type": "number", | ||||
| 			"description": "How many movement points needed to move hero" | ||||
| 		}, | ||||
| 		"minimapUnblocked": | ||||
| 		{ | ||||
| 			"type": "array", | ||||
| 			"description": "Color of terrain on minimap without unpassable objects", | ||||
| 			"minItems": 3, | ||||
| 			"maxItems": 3, | ||||
| 			"items": | ||||
| 			{ | ||||
| 				"type": "number" | ||||
| 			} | ||||
| 		}, | ||||
| 		"minimapBlocked": | ||||
| 		{ | ||||
| 			"type": "array", | ||||
| 			"description": "Color of terrain on minimap with unpassable objects", | ||||
| 			"minItems": 3, | ||||
| 			"maxItems": 3, | ||||
| 			"items": | ||||
| 			{ | ||||
| 				"type": "number" | ||||
| 			} | ||||
| 		}, | ||||
| 		"music": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "Music filename to play on this terrain on adventure map" | ||||
| 		}, | ||||
| 		"tiles": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "Name of file with graphicks", | ||||
| 			"format": "defFile" | ||||
| 		}, | ||||
| 		"type": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "Type of this terrain. Can be land, water, subterranean or rock", | ||||
| 			"enum": ["LAND", "WATER", "SUB", "ROCK"] | ||||
| 		}, | ||||
| 		"rockTerrain": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "The name of tock type terrain which will be used as borders in the underground" | ||||
| 		}, | ||||
| 		"river": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "River type which should be used for that terrain", | ||||
| 			"enum": ["", "rw", "ri", "rm", "rl"] | ||||
| 		}, | ||||
| 		"horseSoundId": | ||||
| 		{ | ||||
| 			"type": "number", | ||||
| 			"description": "Id of horse sound to be played when hero is moving across terrain" | ||||
| 		}, | ||||
| 		"text": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "Text to be shown when mouse if over terrain" | ||||
| 		}, | ||||
| 		"code": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "Two-letters unique indentifier for this terrain. Used for terrain serializaion" | ||||
| 		}, | ||||
| 		"battleFields": | ||||
| 		{ | ||||
| 			"type": "array", | ||||
| 			"description": "array of battleFields for this terrain", | ||||
| 			"items": | ||||
| 			{ | ||||
| 				"type": "string" | ||||
| 			} | ||||
| 		}, | ||||
| 		"prohibitTransitions": | ||||
| 		{ | ||||
| 			"type": "array", | ||||
| 			"description": "array or terrain names, which is prohibited to make transition from/to", | ||||
| 			"items": | ||||
| 			{ | ||||
| 				"type": "string" | ||||
| 			} | ||||
| 		}, | ||||
| 		"transitionRequired": | ||||
| 		{ | ||||
| 			"type": "boolean", | ||||
| 			"description": "If sand/dirt transition required from/to other terrains" | ||||
| 		}, | ||||
| 		"terrainViewPatterns": | ||||
| 		{ | ||||
| 			"type": "string", | ||||
| 			"description": "Can be normal, dirt, water, rock" | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -34,8 +34,6 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const | ||||
| 		templates[fullKey].setId(name); | ||||
| 		templates[fullKey].serializeJson(handler); | ||||
| 		templates[fullKey].validate(); | ||||
|  | ||||
| 		templatesByName[name] = templates[fullKey]; | ||||
| 	} | ||||
| 	catch(const std::exception & e) | ||||
| 	{ | ||||
| @@ -55,22 +53,14 @@ std::vector<JsonNode> CRmgTemplateStorage::loadLegacyData(size_t dataSize) | ||||
| 	//it would be cool to load old rmg.txt files | ||||
| } | ||||
|  | ||||
| const CRmgTemplate * CRmgTemplateStorage::getTemplate(const std::string & templateFullId) const | ||||
| const CRmgTemplate * CRmgTemplateStorage::getTemplate(const std::string & templateName) const | ||||
| { | ||||
| 	auto iter = templates.find(templateFullId); | ||||
| 	auto iter = templates.find(templateName); | ||||
| 	if(iter==templates.end()) | ||||
| 		return nullptr; | ||||
| 	return &iter->second; | ||||
| } | ||||
|  | ||||
| const CRmgTemplate * CRmgTemplateStorage::getTemplateByName(const std::string & templateName) const | ||||
| { | ||||
| 	auto iter = templatesByName.find(templateName); | ||||
| 	if(iter == templatesByName.end()) | ||||
| 		return nullptr; | ||||
| 	return &iter->second; | ||||
| } | ||||
|  | ||||
| std::vector<const CRmgTemplate *> CRmgTemplateStorage::getTemplates() const | ||||
| { | ||||
| 	std::vector<const CRmgTemplate *> result; | ||||
|   | ||||
| @@ -31,13 +31,11 @@ public: | ||||
| 	virtual void loadObject(std::string scope, std::string name, const JsonNode & data) override; | ||||
| 	virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; | ||||
| 	 | ||||
| 	const CRmgTemplate * getTemplate(const std::string & templateFullId) const; | ||||
| 	const CRmgTemplate * getTemplateByName(const std::string & templateName) const; | ||||
| 	const CRmgTemplate* getTemplate(const std::string & templateName) const; | ||||
| 	std::vector<const CRmgTemplate *> getTemplates() const; | ||||
|  | ||||
| private: | ||||
| 	std::map<std::string, CRmgTemplate> templates; //FIXME: doesn't IHandlerBase cover this? | ||||
| 	std::map<std::string, CRmgTemplate> templatesByName; | ||||
| 	std::map<std::string, CRmgTemplate> templates; | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										777
									
								
								mapeditor/Animation.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,777 @@ | ||||
| /* | ||||
|  * Animation.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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| //code is copied from vcmiclient/CAnimation.cpp with minimal changes | ||||
|  | ||||
| #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; | ||||
|  | ||||
| /// 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() | ||||
| { | ||||
| } | ||||
|  | ||||
| /************************************************************************* | ||||
|  *  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 | ||||
| 	{ | ||||
| 		images[group][frame] = getFromExtraDef(source[group][frame]["file"].String());; | ||||
| 		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); | ||||
| 	} | ||||
|  | ||||
| #if 0 //this code is not used but maybe requred if there will be configurable sprites | ||||
| 	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); | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| #if 0 //can be required in image preview? | ||||
| 	for(auto & group : images) | ||||
| 		for(auto & image : group.second) | ||||
| 			image.second->playerColored(player); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| 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)); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										94
									
								
								mapeditor/Animation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,94 @@ | ||||
| /* | ||||
|  * Animation.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 | ||||
| //code is copied from vcmiclient/CAnimation.h with minimal changes | ||||
|  | ||||
| #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(size_t sourceGroup, size_t sourceFrame, 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); | ||||
| }; | ||||
							
								
								
									
										160
									
								
								mapeditor/BitmapHandler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,160 @@ | ||||
| /* | ||||
|  * BitmapHandler.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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| //code is copied from vcmiclient/CBitmapHandler.cpp with minimal changes | ||||
|  | ||||
| #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(const std::string & path, const 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(const std::string & path, const 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 QImage | ||||
| 			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 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) | ||||
| 	} | ||||
|  | ||||
| 	QImage loadBitmap(const std::string & fname, bool setKey) | ||||
| 	{ | ||||
| 		for(const auto dir : {"DATA/", "SPRITES/"}) | ||||
| 		{ | ||||
| 			auto image = loadBitmapFromDir(dir, fname, setKey); | ||||
| 			if(!image.isNull()) | ||||
| 				return image; | ||||
| 		} | ||||
| 		logGlobal->error("Error: Failed to find file %s", fname); | ||||
| 		return {}; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										23
									
								
								mapeditor/BitmapHandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | ||||
| /* | ||||
|  * BitmapHandler.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 | ||||
| //code is copied from vcmiclient/CBitmapHandler.h with minimal changes | ||||
|  | ||||
| #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(const std::string & fname, bool setKey = true); | ||||
| } | ||||
|  | ||||
							
								
								
									
										136
									
								
								mapeditor/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,136 @@ | ||||
| set(editor_SRCS | ||||
| 		StdInc.cpp | ||||
| 		main.cpp | ||||
| 		launcherdirs.cpp | ||||
| 		jsonutils.cpp | ||||
| 		mainwindow.cpp | ||||
| 		BitmapHandler.cpp | ||||
| 		maphandler.cpp | ||||
| 		Animation.cpp | ||||
| 		graphics.cpp | ||||
| 		windownewmap.cpp | ||||
| 		generatorprogress.cpp | ||||
| 		mapview.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 | ||||
| 		inspector/questwidget.cpp | ||||
| ) | ||||
|  | ||||
| set(editor_HEADERS | ||||
| 		StdInc.h | ||||
| 		launcherdirs.h | ||||
| 		jsonutils.h | ||||
| 		mainwindow.h | ||||
| 		BitmapHandler.h | ||||
| 		maphandler.h | ||||
| 		Animation.h | ||||
| 		graphics.h | ||||
| 		windownewmap.h | ||||
| 		generatorprogress.h | ||||
| 		mapview.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 | ||||
| 		inspector/questwidget.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 | ||||
| 		inspector/questwidget.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" | ||||
							
								
								
									
										55
									
								
								mapeditor/StdInc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,55 @@ | ||||
| #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> | ||||
|  | ||||
| VCMI_LIB_USING_NAMESPACE | ||||
|  | ||||
| using NumericPointer = typename std::conditional<sizeof(void *) == sizeof(unsigned long long), | ||||
| 												 unsigned long long, unsigned int>::type; | ||||
|  | ||||
| template<class Type> | ||||
| NumericPointer data_cast(Type * _pointer) | ||||
| { | ||||
| 	static_assert(sizeof(Type *) == sizeof(NumericPointer), | ||||
| 				  "Cannot compile for that architecture, see NumericPointer definition"); | ||||
|  | ||||
| 	return reinterpret_cast<NumericPointer>(_pointer); | ||||
| } | ||||
|  | ||||
| template<class Type> | ||||
| Type * data_cast(NumericPointer _numeric) | ||||
| { | ||||
| 	static_assert(sizeof(Type *) == sizeof(NumericPointer), | ||||
| 				  "Cannot compile for that architecture, see NumericPointer definition"); | ||||
|  | ||||
| 	return reinterpret_cast<Type *>(_numeric); | ||||
| } | ||||
|  | ||||
| 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 | ||||
| } | ||||
							
								
								
									
										47
									
								
								mapeditor/generatorprogress.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,47 @@ | ||||
| /* | ||||
|  * generatorprogress.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 "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(); | ||||
| } | ||||
							
								
								
									
										32
									
								
								mapeditor/generatorprogress.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,32 @@ | ||||
| /* | ||||
|  * generatorprogress.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 <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; | ||||
| }; | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										345
									
								
								mapeditor/graphics.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,345 @@ | ||||
| /* | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| //code is copied from vcmiclient/Graphics.cpp with minimal changes | ||||
| #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" | ||||
|  | ||||
| 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 : VLC->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); | ||||
| 	} | ||||
| 	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(VLC->creatures()); | ||||
| 	addImageListEntries(VLC->heroTypes()); | ||||
| 	addImageListEntries(VLC->artifacts()); | ||||
| 	addImageListEntries(VLC->factions()); | ||||
| 	addImageListEntries(VLC->spells()); | ||||
| 	addImageListEntries(VLC->skills()); | ||||
| } | ||||
							
								
								
									
										85
									
								
								mapeditor/graphics.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,85 @@ | ||||
| /* | ||||
|  * 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 | ||||
| //code is copied from vcmiclient/Graphics.h with minimal changes | ||||
|  | ||||
| #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 | 
							
								
								
									
										152
									
								
								mapeditor/inspector/armywidget.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,152 @@ | ||||
| /* | ||||
|  * armywidget.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 "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]->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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										57
									
								
								mapeditor/inspector/armywidget.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
|  * armywidget.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../StdInc.h" | ||||
| #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; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										849
									
								
								mapeditor/inspector/inspector.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,849 @@ | ||||
| /* | ||||
|  * inspector.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 "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" | ||||
| #include "questwidget.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); | ||||
| 	//INIT_OBJ_TYPE(CGEvent); | ||||
| 	//INIT_OBJ_TYPE(CGSeerHut); | ||||
| } | ||||
|  | ||||
| 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::PRISON) | ||||
| 		o->tempOwner = PlayerColor::NEUTRAL; | ||||
| 	 | ||||
| 	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); | ||||
| 	 | ||||
| 	{ //Sex | ||||
| 		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); | ||||
| 	 | ||||
| 	{ //Hero type | ||||
| 		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); | ||||
| 	{ //Character | ||||
| 		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; | ||||
| 	 | ||||
| 	addProperty("Message", o->message, new MessageDelegate, false); | ||||
| 	 | ||||
| 	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(CGSeerHut * o) | ||||
| { | ||||
| 	if(!o) return; | ||||
|  | ||||
| 	{ //Mission type | ||||
| 		auto * delegate = new InspectorDelegate; | ||||
| 		delegate->options << "Reach level" << "Stats" << "Kill hero" << "Kill creature" << "Artifact" << "Army" << "Resources" << "Hero" << "Player"; | ||||
| 		addProperty<CQuest::Emission>("Mission type", o->quest->missionType, delegate, false); | ||||
| 	} | ||||
| 	 | ||||
| 	addProperty("First visit text", o->quest->firstVisitText, new MessageDelegate, false); | ||||
| 	addProperty("Next visit text", o->quest->nextVisitText, new MessageDelegate, false); | ||||
| 	addProperty("Completed text", o->quest->completedText, new MessageDelegate, false); | ||||
| 	 | ||||
| 	{ //Quest | ||||
| 		auto * delegate = new QuestDelegate(*map, *o); | ||||
| 		addProperty("Quest", PropertyEditorPlaceholder(), delegate, false); | ||||
| 	} | ||||
| 	 | ||||
| 	{ //Reward | ||||
| 		auto * delegate = new RewardsSeerhutDelegate(*map, *o); | ||||
| 		addProperty("Reward", PropertyEditorPlaceholder(), delegate, false); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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); | ||||
| 	UPDATE_OBJ_PROPERTIES(CGSeerHut); | ||||
| 	 | ||||
| 	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); | ||||
| 	SET_PROPERTIES(CGSeerHut); | ||||
| } | ||||
|  | ||||
| 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(key == "Remove after") | ||||
| 		o->removeAfterVisit = stringToBool(value.toString()); | ||||
| 	 | ||||
| 	if(key == "Human trigger") | ||||
| 		o->humanActivate = stringToBool(value.toString()); | ||||
| 	 | ||||
| 	if(key == "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(); | ||||
| } | ||||
|  | ||||
| void Inspector::setProperty(CGSeerHut * o, const QString & key, const QVariant & value) | ||||
| { | ||||
| 	if(!o) return; | ||||
| 	 | ||||
| 	if(key == "Mission type") | ||||
| 	{ | ||||
| 		if(value == "Reach level") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_LEVEL; | ||||
| 		if(value == "Stats") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_PRIMARY_STAT; | ||||
| 		if(value == "Kill hero") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_KILL_HERO; | ||||
| 		if(value == "Kill creature") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_KILL_CREATURE; | ||||
| 		if(value == "Artifact") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_ART; | ||||
| 		if(value == "Army") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_ARMY; | ||||
| 		if(value == "Resources") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_RESOURCES; | ||||
| 		if(value == "Hero") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_HERO; | ||||
| 		if(value == "Player") | ||||
| 			o->quest->missionType = CQuest::Emission::MISSION_PLAYER; | ||||
| 	} | ||||
| 	 | ||||
| 	if(key == "First visit text") | ||||
| 		o->quest->firstVisitText = value.toString().toStdString(); | ||||
| 	if(key == "Next visit text") | ||||
| 		o->quest->nextVisitText = value.toString().toStdString(); | ||||
| 	if(key == "Completed text") | ||||
| 		o->quest->completedText = value.toString().toStdString(); | ||||
| } | ||||
|  | ||||
|  | ||||
| //===============IMPLEMENT PROPERTY VALUE TYPE============================ | ||||
| QTableWidgetItem * Inspector::addProperty(CGObjectInstance * value) | ||||
| { | ||||
| 	return new QTableWidgetItem(QString::number(data_cast<CGObjectInstance>(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); | ||||
| } | ||||
|  | ||||
| QTableWidgetItem * Inspector::addProperty(CQuest::Emission value) | ||||
| { | ||||
| 	QString str; | ||||
| 	switch (value) { | ||||
| 		case CQuest::Emission::MISSION_LEVEL: | ||||
| 			str = "Reach level"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_PRIMARY_STAT: | ||||
| 			str = "Stats"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_KILL_HERO: | ||||
| 			str = "Kill hero"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_KILL_CREATURE: | ||||
| 			str = "Kill creature"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_ART: | ||||
| 			str = "Artifact"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_ARMY: | ||||
| 			str = "Army"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_RESOURCES: | ||||
| 			str = "Resources"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_HERO: | ||||
| 			str = "Hero"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_PLAYER: | ||||
| 			str = "Player"; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_KEYMASTER: | ||||
| 			str = "Key master"; | ||||
| 			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); | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										164
									
								
								mapeditor/inspector/inspector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,164 @@ | ||||
| /* | ||||
|  * inspector.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../StdInc.h" | ||||
| #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); | ||||
| 	//DECLARE_OBJ_TYPE(CGSeerHut); | ||||
| 	 | ||||
| 	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_OBJ_PROPERTY_METHODS(CGSeerHut); | ||||
|  | ||||
| //===============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(CQuest::Emission 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; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										63
									
								
								mapeditor/inspector/messagewidget.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,63 @@ | ||||
| /* | ||||
|  * messagewidget.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 "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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										44
									
								
								mapeditor/inspector/messagewidget.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * messagewidget.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
| #include "../StdInc.h" | ||||
| #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; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										193
									
								
								mapeditor/inspector/questwidget.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,193 @@ | ||||
| /* | ||||
|  * questwidget.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 "questwidget.h" | ||||
| #include "ui_questwidget.h" | ||||
| #include "../lib/VCMI_Lib.h" | ||||
| #include "../lib/CSkillHandler.h" | ||||
| #include "../lib/CArtHandler.h" | ||||
| #include "../lib/CCreatureHandler.h" | ||||
| #include "../lib/CHeroHandler.h" | ||||
| #include "../lib/StringConstants.h" | ||||
|  | ||||
| QuestWidget::QuestWidget(const CMap & _map, CGSeerHut & _sh, QWidget *parent) : | ||||
| 	QDialog(parent), | ||||
| 	map(_map), | ||||
| 	seerhut(_sh), | ||||
| 	ui(new Ui::QuestWidget) | ||||
| { | ||||
| 	ui->setupUi(this); | ||||
| } | ||||
|  | ||||
| QuestWidget::~QuestWidget() | ||||
| { | ||||
| 	delete ui; | ||||
| } | ||||
|  | ||||
| void QuestWidget::obtainData() | ||||
| { | ||||
| 	assert(seerhut.quest); | ||||
| 	bool activeId = false; | ||||
| 	bool activeAmount = false; | ||||
| 	switch(seerhut.quest->missionType) { | ||||
| 		case CQuest::Emission::MISSION_LEVEL: | ||||
| 			activeAmount = true; | ||||
| 			ui->targetId->addItem("Reach level"); | ||||
| 			ui->targetAmount->setText(QString::number(seerhut.quest->m13489val)); | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_PRIMARY_STAT: | ||||
| 			activeId = true; | ||||
| 			activeAmount = true; | ||||
| 			for(auto s : PrimarySkill::names) | ||||
| 				ui->targetId->addItem(QString::fromStdString(s)); | ||||
| 			for(int i = 0; i < seerhut.quest->m2stats.size(); ++i) | ||||
| 			{ | ||||
| 				if(seerhut.quest->m2stats[i] > 0) | ||||
| 				{ | ||||
| 					ui->targetId->setCurrentIndex(i); | ||||
| 					ui->targetAmount->setText(QString::number(seerhut.quest->m2stats[i])); | ||||
| 					break; //TODO: support multiple stats | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_KILL_HERO: | ||||
| 			activeId = true; | ||||
| 			//TODO: implement | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_KILL_CREATURE: | ||||
| 			activeId = true; | ||||
| 			//TODO: implement | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_ART: | ||||
| 			activeId = true; | ||||
| 			for(int i = 0; i < map.allowedArtifact.size(); ++i) | ||||
| 				ui->targetId->addItem(QString::fromStdString(VLC->arth->objects.at(i)->getName())); | ||||
| 			if(!seerhut.quest->m5arts.empty()) | ||||
| 				ui->targetId->setCurrentIndex(seerhut.quest->m5arts.front()); | ||||
| 			//TODO: support multiple artifacts | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_ARMY: | ||||
| 			activeId = true; | ||||
| 			activeAmount = true; | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_RESOURCES: | ||||
| 			activeId = true; | ||||
| 			activeAmount = true; | ||||
| 			for(auto s : GameConstants::RESOURCE_NAMES) | ||||
| 				ui->targetId->addItem(QString::fromStdString(s)); | ||||
| 			for(int i = 0; i < seerhut.quest->m7resources.size(); ++i) | ||||
| 			{ | ||||
| 				if(seerhut.quest->m7resources[i] > 0) | ||||
| 				{ | ||||
| 					ui->targetId->setCurrentIndex(i); | ||||
| 					ui->targetAmount->setText(QString::number(seerhut.quest->m7resources[i])); | ||||
| 					break; //TODO: support multiple resources | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_HERO: | ||||
| 			activeId = true; | ||||
| 			for(int i = 0; i < map.allowedHeroes.size(); ++i) | ||||
| 				ui->targetId->addItem(QString::fromStdString(VLC->heroh->objects.at(i)->getName())); | ||||
| 			ui->targetId->setCurrentIndex(seerhut.quest->m13489val); | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_PLAYER: | ||||
| 			activeId = true; | ||||
| 			for(auto s : GameConstants::PLAYER_COLOR_NAMES) | ||||
| 				ui->targetId->addItem(QString::fromStdString(s)); | ||||
| 			ui->targetId->setCurrentIndex(seerhut.quest->m13489val); | ||||
| 			break; | ||||
| 		case CQuest::Emission::MISSION_KEYMASTER: | ||||
| 			break; | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| 	 | ||||
| 	ui->targetId->setEnabled(activeId); | ||||
| 	ui->targetAmount->setEnabled(activeAmount); | ||||
| } | ||||
|  | ||||
| QString QuestWidget::commitChanges() | ||||
| { | ||||
| 	assert(seerhut.quest); | ||||
| 	switch(seerhut.quest->missionType) { | ||||
| 		case CQuest::Emission::MISSION_LEVEL: | ||||
| 			seerhut.quest->m13489val = ui->targetAmount->text().toInt(); | ||||
| 			return QString("Reach lvl ").append(ui->targetAmount->text()); | ||||
| 		case CQuest::Emission::MISSION_PRIMARY_STAT: | ||||
| 			seerhut.quest->m2stats.resize(sizeof(PrimarySkill::names), 0); | ||||
| 			seerhut.quest->m2stats[ui->targetId->currentIndex()] = ui->targetAmount->text().toInt(); | ||||
| 			//TODO: support multiple stats | ||||
| 			return ui->targetId->currentText().append(ui->targetAmount->text()); | ||||
| 		case CQuest::Emission::MISSION_KILL_HERO: | ||||
| 			//TODO: implement | ||||
| 			return QString("N/A"); | ||||
| 		case CQuest::Emission::MISSION_KILL_CREATURE: | ||||
| 			//TODO: implement | ||||
| 			return QString("N/A"); | ||||
| 		case CQuest::Emission::MISSION_ART: | ||||
| 			seerhut.quest->m5arts.clear(); | ||||
| 			seerhut.quest->m5arts.push_back(ui->targetId->currentIndex()); | ||||
| 			//TODO: support multiple artifacts | ||||
| 			return ui->targetId->currentText(); | ||||
| 		case CQuest::Emission::MISSION_ARMY: | ||||
| 			//TODO: implement | ||||
| 			return QString("N/A"); | ||||
| 		case CQuest::Emission::MISSION_RESOURCES: | ||||
| 			seerhut.quest->m7resources.resize(sizeof(GameConstants::RESOURCE_NAMES), 0); | ||||
| 			seerhut.quest->m7resources[ui->targetId->currentIndex()] = ui->targetAmount->text().toInt(); | ||||
| 			//TODO: support resources | ||||
| 			return ui->targetId->currentText().append(ui->targetAmount->text()); | ||||
| 		case CQuest::Emission::MISSION_HERO: | ||||
| 			seerhut.quest->m13489val = ui->targetId->currentIndex(); | ||||
| 			return ui->targetId->currentText(); | ||||
| 		case CQuest::Emission::MISSION_PLAYER: | ||||
| 			seerhut.quest->m13489val = ui->targetId->currentIndex(); | ||||
| 			return ui->targetId->currentText(); | ||||
| 		case CQuest::Emission::MISSION_KEYMASTER: | ||||
| 			return QString("N/A"); | ||||
| 		default: | ||||
| 			return QString("N/A"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QuestDelegate::QuestDelegate(const CMap & m, CGSeerHut & t): map(m), seerhut(t), QStyledItemDelegate() | ||||
| { | ||||
| } | ||||
|  | ||||
| QWidget * QuestDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const | ||||
| { | ||||
| 	return new QuestWidget(map, seerhut, parent); | ||||
| } | ||||
|  | ||||
| void QuestDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const | ||||
| { | ||||
| 	if(auto *ed = qobject_cast<QuestWidget *>(editor)) | ||||
| 	{ | ||||
| 		ed->obtainData(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QStyledItemDelegate::setEditorData(editor, index); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void QuestDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const | ||||
| { | ||||
| 	if(auto *ed = qobject_cast<QuestWidget *>(editor)) | ||||
| 	{ | ||||
| 		auto quest = ed->commitChanges(); | ||||
| 		model->setData(index, quest); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QStyledItemDelegate::setModelData(editor, model, index); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										52
									
								
								mapeditor/inspector/questwidget.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,52 @@ | ||||
| /* | ||||
|  * questwidget.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
| #include "../StdInc.h" | ||||
| #include <QDialog> | ||||
| #include "../lib/mapObjects/CQuest.h" | ||||
| #include "../lib/mapping/CMap.h" | ||||
|  | ||||
| namespace Ui { | ||||
| class QuestWidget; | ||||
| } | ||||
|  | ||||
| class QuestWidget : public QDialog | ||||
| { | ||||
| 	Q_OBJECT | ||||
|  | ||||
| public: | ||||
| 	explicit QuestWidget(const CMap &, CGSeerHut &, QWidget *parent = nullptr); | ||||
| 	~QuestWidget(); | ||||
| 	 | ||||
| 	void obtainData(); | ||||
| 	QString commitChanges(); | ||||
|  | ||||
| private: | ||||
| 	CGSeerHut & seerhut; | ||||
| 	const CMap & map; | ||||
| 	Ui::QuestWidget *ui; | ||||
| }; | ||||
|  | ||||
| class QuestDelegate : public QStyledItemDelegate | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	using QStyledItemDelegate::QStyledItemDelegate; | ||||
| 	 | ||||
| 	QuestDelegate(const CMap &, CGSeerHut &); | ||||
| 	 | ||||
| 	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: | ||||
| 	CGSeerHut & seerhut; | ||||
| 	const CMap & map; | ||||
| }; | ||||
							
								
								
									
										50
									
								
								mapeditor/inspector/questwidget.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,50 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>QuestWidget</class> | ||||
|  <widget class="QDialog" name="QuestWidget"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>429</width> | ||||
|     <height>89</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Mission goal</string> | ||||
|   </property> | ||||
|   <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|    <item> | ||||
|     <widget class="QComboBox" name="targetId"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QLineEdit" name="targetAmount"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="maximumSize"> | ||||
|       <size> | ||||
|        <width>60</width> | ||||
|        <height>16777215</height> | ||||
|       </size> | ||||
|      </property> | ||||
|      <property name="inputMethodHints"> | ||||
|       <set>Qt::ImhDigitsOnly</set> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
							
								
								
									
										427
									
								
								mapeditor/inspector/rewardswidget.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,427 @@ | ||||
| /* | ||||
|  * rewardswidget.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 "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), | ||||
| 	seerhut(nullptr), | ||||
| 	ui(new Ui::RewardsWidget) | ||||
| { | ||||
| 	ui->setupUi(this); | ||||
| 	 | ||||
| 	for(auto & type : rewardTypes) | ||||
| 		ui->rewardType->addItem(QString::fromStdString(type)); | ||||
| } | ||||
|  | ||||
| RewardsWidget::RewardsWidget(const CMap & m, CGSeerHut & p, QWidget *parent) : | ||||
| 	QDialog(parent), | ||||
| 	map(m), | ||||
| 	pandora(nullptr), | ||||
| 	seerhut(&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(RewardType typeId) | ||||
| { | ||||
| 	assert(typeId < rewardTypes.size()); | ||||
| 	QList<QString> result; | ||||
| 	 | ||||
| 	switch (typeId) { | ||||
| 		case RewardType::RESOURCE: | ||||
| 			//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 RewardType::PRIMARY_SKILL: | ||||
| 			for(auto s : PrimarySkill::names) | ||||
| 				result.append(QString::fromStdString(s)); | ||||
| 			break; | ||||
| 			 | ||||
| 		case RewardType::SECONDARY_SKILL: | ||||
| 			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 RewardType::ARTIFACT: | ||||
| 			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 RewardType::SPELL: | ||||
| 			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 RewardType::CREATURE: | ||||
| 			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(RewardType(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(RewardType::EXPERIENCE, 0, pandora->gainedExp); | ||||
| 		if(pandora->manaDiff) | ||||
| 			addReward(RewardType::MANA, 0, pandora->manaDiff); | ||||
| 		if(pandora->moraleDiff) | ||||
| 			addReward(RewardType::MORALE, 0, pandora->moraleDiff); | ||||
| 		if(pandora->luckDiff) | ||||
| 			addReward(RewardType::LUCK, 0, pandora->luckDiff); | ||||
| 		if(pandora->resources.nonZero()) | ||||
| 		{ | ||||
| 			for(Res::ResourceSet::nziterator resiter(pandora->resources); resiter.valid(); ++resiter) | ||||
| 				addReward(RewardType::RESOURCE, resiter->resType, resiter->resVal); | ||||
| 		} | ||||
| 		for(int idx = 0; idx < pandora->primskills.size(); ++idx) | ||||
| 		{ | ||||
| 			if(pandora->primskills[idx]) | ||||
| 				addReward(RewardType::PRIMARY_SKILL, idx, pandora->primskills[idx]); | ||||
| 		} | ||||
| 		assert(pandora->abilities.size() == pandora->abilityLevels.size()); | ||||
| 		for(int idx = 0; idx < pandora->abilities.size(); ++idx) | ||||
| 		{ | ||||
| 			addReward(RewardType::SECONDARY_SKILL, pandora->abilities[idx].getNum(), pandora->abilityLevels[idx]); | ||||
| 		} | ||||
| 		for(auto art : pandora->artifacts) | ||||
| 		{ | ||||
| 			addReward(RewardType::ARTIFACT, art.getNum(), 1); | ||||
| 		} | ||||
| 		for(auto spell : pandora->spells) | ||||
| 		{ | ||||
| 			addReward(RewardType::SPELL, spell.getNum(), 1); | ||||
| 		} | ||||
| 		for(int i = 0; i < pandora->creatures.Slots().size(); ++i) | ||||
| 		{ | ||||
| 			if(auto c = pandora->creatures.getCreature(SlotID(i))) | ||||
| 				addReward(RewardType::CREATURE, c->getId(), pandora->creatures.getStackCount(SlotID(i))); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(seerhut) | ||||
| 	{ | ||||
| 		switch(seerhut->rewardType) | ||||
| 		{ | ||||
| 			case CGSeerHut::ERewardType::EXPERIENCE: | ||||
| 				addReward(RewardType::EXPERIENCE, 0, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::MANA_POINTS: | ||||
| 				addReward(RewardType::MANA, 0, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::MORALE_BONUS: | ||||
| 				addReward(RewardType::MORALE, 0, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::LUCK_BONUS: | ||||
| 				addReward(RewardType::LUCK, 0, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::RESOURCES: | ||||
| 				addReward(RewardType::RESOURCE, seerhut->rID, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::PRIMARY_SKILL: | ||||
| 				addReward(RewardType::PRIMARY_SKILL, seerhut->rID, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::SECONDARY_SKILL: | ||||
| 				addReward(RewardType::SECONDARY_SKILL, seerhut->rID, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::ARTIFACT: | ||||
| 				addReward(RewardType::ARTIFACT, seerhut->rID, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::SPELL: | ||||
| 				addReward(RewardType::SPELL, seerhut->rID, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			case CGSeerHut::ERewardType::CREATURE: | ||||
| 				addReward(RewardType::CREATURE, seerhut->rID, seerhut->rVal); | ||||
| 				break; | ||||
| 				 | ||||
| 			default: | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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 RewardType::EXPERIENCE: | ||||
| 					pandora->gainedExp = amount; | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::MANA: | ||||
| 					pandora->manaDiff = amount; | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::MORALE: | ||||
| 					pandora->moraleDiff = amount; | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::LUCK: | ||||
| 					pandora->luckDiff = amount; | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::RESOURCE: | ||||
| 					pandora->resources.at(listId) = amount; | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::PRIMARY_SKILL: | ||||
| 					pandora->primskills[listId] = amount; | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::SECONDARY_SKILL: | ||||
| 					pandora->abilities.push_back(SecondarySkill(listId)); | ||||
| 					pandora->abilityLevels.push_back(amount); | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::ARTIFACT: | ||||
| 					pandora->artifacts.push_back(ArtifactID(listId)); | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::SPELL: | ||||
| 					pandora->spells.push_back(SpellID(listId)); | ||||
| 					break; | ||||
| 					 | ||||
| 				case RewardType::CREATURE: | ||||
| 					auto slot = pandora->creatures.getFreeSlot(); | ||||
| 					if(slot != SlotID() && amount > 0) | ||||
| 						pandora->creatures.addToSlot(slot, CreatureID(listId), amount); | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if(seerhut) | ||||
| 	{ | ||||
| 		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(); | ||||
| 			seerhut->rewardType = CGSeerHut::ERewardType(typeId + 1); | ||||
| 			seerhut->rID = listId; | ||||
| 			seerhut->rVal = amount; | ||||
| 		} | ||||
| 	} | ||||
| 	return haveRewards; | ||||
| } | ||||
|  | ||||
| void RewardsWidget::on_rewardList_activated(int index) | ||||
| { | ||||
| 	ui->rewardAmount->setText(QStringLiteral("1")); | ||||
| } | ||||
|  | ||||
| void RewardsWidget::addReward(RewardsWidget::RewardType typeId, int listId, int amount) | ||||
| { | ||||
| 	//for seerhut there could be the only one reward | ||||
| 	if(!pandora && seerhut && rewards) | ||||
| 		return; | ||||
| 	 | ||||
| 	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(RewardType(ui->rewardType->currentIndex()), ui->rewardList->currentIndex(), ui->rewardAmount->text().toInt()); | ||||
| } | ||||
|  | ||||
|  | ||||
| void RewardsWidget::on_buttonRemove_clicked() | ||||
| { | ||||
| 	auto currentRow = ui->rewardsTable->currentRow(); | ||||
| 	if(currentRow != -1) | ||||
| 	{ | ||||
| 		ui->rewardsTable->removeRow(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()));*/ | ||||
| } | ||||
|  | ||||
| void RewardsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const | ||||
| { | ||||
| 	if(auto * ed = qobject_cast<RewardsWidget *>(editor)) | ||||
| 	{ | ||||
| 		ed->obtainData(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QStyledItemDelegate::setEditorData(editor, index); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void RewardsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const | ||||
| { | ||||
| 	if(auto * ed = qobject_cast<RewardsWidget *>(editor)) | ||||
| 	{ | ||||
| 		auto hasReward = ed->commitChanges(); | ||||
| 		model->setData(index, "dummy"); | ||||
| 		if(hasReward) | ||||
| 			model->setData(index, "HAS REWARD"); | ||||
| 		else | ||||
| 			model->setData(index, ""); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QStyledItemDelegate::setModelData(editor, model, index); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| RewardsPandoraDelegate::RewardsPandoraDelegate(const CMap & m, CGPandoraBox & t): map(m), pandora(t), RewardsDelegate() | ||||
| { | ||||
| } | ||||
|  | ||||
| QWidget * RewardsPandoraDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const | ||||
| { | ||||
| 	return new RewardsWidget(map, pandora, parent); | ||||
| } | ||||
|  | ||||
| RewardsSeerhutDelegate::RewardsSeerhutDelegate(const CMap & m, CGSeerHut & t): map(m), seerhut(t), RewardsDelegate() | ||||
| { | ||||
| } | ||||
|  | ||||
| QWidget * RewardsSeerhutDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const | ||||
| { | ||||
| 	return new RewardsWidget(map, seerhut, parent); | ||||
| } | ||||
							
								
								
									
										98
									
								
								mapeditor/inspector/rewardswidget.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,98 @@ | ||||
| /* | ||||
|  * rewardswidget.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
| #include "../StdInc.h" | ||||
| #include <QDialog> | ||||
| #include "../lib/mapObjects/CGPandoraBox.h" | ||||
| #include "../lib/mapObjects/CQuest.h" | ||||
| #include "../lib/mapping/CMap.h" | ||||
|  | ||||
| namespace Ui { | ||||
| class RewardsWidget; | ||||
| } | ||||
|  | ||||
| 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: | ||||
| 	enum RewardType | ||||
| 	{ | ||||
| 		EXPERIENCE = 0, MANA, MORALE, LUCK, RESOURCE, PRIMARY_SKILL, SECONDARY_SKILL, ARTIFACT, SPELL, CREATURE | ||||
| 	}; | ||||
| 	 | ||||
| 	explicit RewardsWidget(const CMap &, CGPandoraBox &, QWidget *parent = nullptr); | ||||
| 	explicit RewardsWidget(const CMap &, CGSeerHut &, 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(RewardType typeId, int listId, int amount); | ||||
| 	QList<QString> getListForType(RewardType typeId); | ||||
| 	 | ||||
| 	Ui::RewardsWidget *ui; | ||||
| 	CGPandoraBox * pandora; | ||||
| 	CGSeerHut * seerhut; | ||||
| 	const CMap & map; | ||||
| 	int rewards = 0; | ||||
| }; | ||||
|  | ||||
| class RewardsDelegate : public QStyledItemDelegate | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	using QStyledItemDelegate::QStyledItemDelegate; | ||||
| 	 | ||||
| 	void setEditorData(QWidget *editor, const QModelIndex &index) const override; | ||||
| 	void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; | ||||
| }; | ||||
|  | ||||
| class RewardsPandoraDelegate : public RewardsDelegate | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	RewardsPandoraDelegate(const CMap &, CGPandoraBox &); | ||||
| 	 | ||||
| 	QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; | ||||
| 	 | ||||
| private: | ||||
| 	CGPandoraBox & pandora; | ||||
| 	const CMap & map; | ||||
| }; | ||||
|  | ||||
| class RewardsSeerhutDelegate : public RewardsDelegate | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	RewardsSeerhutDelegate(const CMap &, CGSeerHut &); | ||||
| 	 | ||||
| 	QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; | ||||
| 	 | ||||
| private: | ||||
| 	CGSeerHut & seerhut; | ||||
| 	const CMap & map; | ||||
| }; | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										250
									
								
								mapeditor/inspector/townbulidingswidget.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,250 @@ | ||||
| /* | ||||
|  * townbuildingswidget.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 "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::getBuildingsFromModel(int modelColumn, Qt::CheckState checkState) | ||||
| { | ||||
| 	std::set<BuildingID> result; | ||||
| 	for(int i = 0; i < model.rowCount(); ++i) | ||||
| 	{ | ||||
| 		if(auto * item = model.item(i, modelColumn)) | ||||
| 			if(item->checkState() == checkState) | ||||
| 				result.emplace(item->data(Qt::UserRole).toInt()); | ||||
| 	} | ||||
| 	 | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| std::set<BuildingID> TownBulidingsWidget::getForbiddenBuildings() | ||||
| { | ||||
| 	return getBuildingsFromModel(1, Qt::Unchecked); | ||||
| } | ||||
|  | ||||
| std::set<BuildingID> TownBulidingsWidget::getBuiltBuildings() | ||||
| { | ||||
| 	return getBuildingsFromModel(2, Qt::Checked); | ||||
| } | ||||
|  | ||||
| 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); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										63
									
								
								mapeditor/inspector/townbulidingswidget.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,63 @@ | ||||
| /* | ||||
|  * townbuildingswidget.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../StdInc.h" | ||||
| #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: | ||||
| 	std::set<BuildingID> getBuildingsFromModel(int modelColumn, Qt::CheckState checkState); | ||||
| 	 | ||||
| 	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> | ||||
| }; | ||||
|  | ||||
							
								
								
									
										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(); | ||||
| } | ||||
							
								
								
									
										1110
									
								
								mapeditor/mainwindow.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										145
									
								
								mapeditor/mainwindow.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,145 @@ | ||||
| #pragma once | ||||
|  | ||||
| #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(TerrainId terrain); | ||||
| 	void roadOrRiverButtonClicked(ui8 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; | ||||
| }; | ||||
							
								
								
									
										1120
									
								
								mapeditor/mainwindow.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										508
									
								
								mapeditor/mapcontroller.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,508 @@ | ||||
| /* | ||||
|  * mapcontroller.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 "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) | ||||
| { | ||||
| 	for(int i : {0, 1}) | ||||
| 	{ | ||||
| 		_scenes[i].reset(new MapScene(i)); | ||||
| 		_miniscenes[i].reset(new MinimapScene(i)); | ||||
| 	} | ||||
| 	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() | ||||
| { | ||||
| 	//there might be extra skills, arts and spells not imported from map | ||||
| 	if(VLC->skillh->getDefaultAllowed().size() > map()->allowedAbilities.size()) | ||||
| 	{ | ||||
| 		map()->allowedAbilities.resize(VLC->skillh->getDefaultAllowed().size()); | ||||
| 	} | ||||
| 	if(VLC->arth->getDefaultAllowed().size() > map()->allowedArtifact.size()) | ||||
| 	{ | ||||
| 		map()->allowedArtifact.resize(VLC->arth->getDefaultAllowed().size()); | ||||
| 	} | ||||
| 	if(VLC->spellh->getDefaultAllowed().size() > map()->allowedSpell.size()) | ||||
| 	{ | ||||
| 		map()->allowedSpell.resize(VLC->spellh->getDefaultAllowed().size()); | ||||
| 	} | ||||
| 	if(VLC->heroh->getDefaultAllowed().size() > map()->allowedHeroes.size()) | ||||
| 	{ | ||||
| 		map()->allowedHeroes.resize(VLC->heroh->getDefaultAllowed().size()); | ||||
| 	} | ||||
| 	 | ||||
| 	//fix owners for objects | ||||
| 	for(auto obj : _map->objects) | ||||
| 	{ | ||||
| 		//setup proper names (hero name will be fixed later | ||||
| 		if(obj->ID != Obj::HERO && obj->ID != Obj::PRISON && (obj->typeName.empty() || obj->subTypeName.empty())) | ||||
| 		{ | ||||
| 			auto handler = VLC->objtypeh->getHandlerFor(obj->ID, obj->subID); | ||||
| 			obj->typeName = handler->typeName; | ||||
| 			obj->subTypeName = handler->subTypeName; | ||||
| 		} | ||||
| 		//fix flags | ||||
| 		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<CGLighthouse*>(obj.get()) || | ||||
| 			   dynamic_cast<CGHeroInstance*>(obj.get())) | ||||
| 				obj->tempOwner = PlayerColor::NEUTRAL; | ||||
| 		} | ||||
| 		//fix hero instance | ||||
| 		if(auto * nih = dynamic_cast<CGHeroInstance*>(obj.get())) | ||||
| 		{ | ||||
| 			map()->allowedHeroes.at(nih->subID) = true; | ||||
| 			auto type = VLC->heroh->objects[nih->subID]; | ||||
| 			assert(type->heroClass); | ||||
| 			//TODO: find a way to get proper type name | ||||
| 			if(obj->ID == Obj::HERO) | ||||
| 				nih->typeName = "hero"; | ||||
| 			if(obj->ID == Obj::PRISON) | ||||
| 				nih->typeName = "prison"; | ||||
| 			nih->subTypeName = type->heroClass->identifier; | ||||
| 			 | ||||
| 			nih->type = type; | ||||
| 			if(nih->name.empty()) | ||||
| 				nih->name = nih->type->name; | ||||
| 			if(nih->biography.empty()) | ||||
| 				nih->biography = nih->type->biography; | ||||
| 			 | ||||
| 			if(nih->ID == Obj::HERO) //not prison | ||||
| 				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); | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
| 		//fix spell scrolls | ||||
| 		if(auto * art = dynamic_cast<CGArtifact*>(obj.get())) | ||||
| 		{ | ||||
| 			if(art->ID == Obj::SPELL_SCROLL && !art->storedArtifact) | ||||
| 			{ | ||||
| 				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())); | ||||
| 				art->storedArtifact = a; | ||||
| 			} | ||||
| 			else | ||||
| 				map()->allowedArtifact.at(art->subID) = true; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MapController::setMap(std::unique_ptr<CMap> cmap) | ||||
| { | ||||
| 	_map = std::move(cmap); | ||||
| 	 | ||||
| 	repairMap(); | ||||
| 	 | ||||
| 	for(int i : {0, 1}) | ||||
| 	{ | ||||
| 		_scenes[i].reset(new MapScene(i)); | ||||
| 		_miniscenes[i].reset(new MinimapScene(i)); | ||||
| 	} | ||||
| 	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()); | ||||
| 	for(int i : {0, 1}) | ||||
| 	{ | ||||
| 		_scenes[i]->initialize(*this); | ||||
| 		_miniscenes[i]->initialize(*this); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MapController::commitTerrainChange(int level, const TerrainId & 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, ui8 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(RoadId(type), &CRandomGenerator::getDefault()); | ||||
| 	else | ||||
| 		_map->getEditManager()->drawRiver(RiverId(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 = SelectionObjectsLayer::NOTHING; | ||||
| 		_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 = SelectionObjectsLayer::MOVEMENT; | ||||
| 	_scenes[level]->selectionObjectsView.draw(); | ||||
| } | ||||
|  | ||||
| void MapController::commitObstacleFill(int level) | ||||
| { | ||||
| 	auto selection = _scenes[level]->selectionTerrainView.selection(); | ||||
| 	if(selection.empty()) | ||||
| 		return; | ||||
| 	 | ||||
| 	//split by zones | ||||
| 	std::map<TerrainId, ObstacleProxy> terrainSelected; | ||||
| 	for(auto & t : selection) | ||||
| 	{ | ||||
| 		auto tl = _map->getTile(t); | ||||
| 		if(tl.blocked || tl.visitable) | ||||
| 			continue; | ||||
| 		 | ||||
| 		terrainSelected[tl.terType->id].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 = SelectionObjectsLayer::NOTHING; | ||||
| 	 | ||||
| 	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 = SelectionObjectsLayer::NOTHING; | ||||
| 	_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 | ||||
| { | ||||
| 	//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(newObj->ID == Obj::GRAIL && objCounter >= 1) //special case for grail | ||||
| 	{ | ||||
| 		auto typeName = QString::fromStdString(newObj->typeName); | ||||
| 		auto subTypeName = QString::fromStdString(newObj->subTypeName); | ||||
| 		error = QString("There can be only one grail object on the map"); | ||||
| 		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; | ||||
| 	} | ||||
| 	 | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void MapController::undo() | ||||
| { | ||||
| 	_map->getEditManager()->getUndoManager().undo(); | ||||
| 	resetMapHandler(); | ||||
| 	sceneForceUpdate(); | ||||
| 	main->mapChanged(); | ||||
| } | ||||
|  | ||||
| void MapController::redo() | ||||
| { | ||||
| 	_map->getEditManager()->getUndoManager().redo(); | ||||
| 	resetMapHandler(); | ||||
| 	sceneForceUpdate(); | ||||
| 	main->mapChanged(); | ||||
| } | ||||
							
								
								
									
										69
									
								
								mapeditor/mapcontroller.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * mapcontroller.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 "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 TerrainId & terrain); | ||||
| 	void commitRoadOrRiverChange(int level, ui8 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(); | ||||
| }; | ||||
							
								
								
									
										
											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" | ||||
							
								
								
									
										546
									
								
								mapeditor/maphandler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,546 @@ | ||||
| /* | ||||
|  * maphandler.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 | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| //code is copied from vcmiclient/mapHandler.cpp with minimal changes | ||||
| #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() | ||||
| { | ||||
| 	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; | ||||
| 	std::map<std::string, std::string> roadFiles; | ||||
| 	std::map<std::string, std::string> riverFiles; | ||||
| 	for(const auto & terrain : VLC->terrainTypeHandler->terrains()) | ||||
| 	{ | ||||
| 		terrainFiles[terrain.name] = terrain.tilesFilename; | ||||
| 	} | ||||
| 	for(const auto & river : VLC->terrainTypeHandler->rivers()) | ||||
| 	{ | ||||
| 		riverFiles[river.fileName] = river.fileName; | ||||
| 	} | ||||
| 	for(const auto & road : VLC->terrainTypeHandler->roads()) | ||||
| 	{ | ||||
| 		roadFiles[road.fileName] = road.fileName; | ||||
| 	} | ||||
| 	 | ||||
| 	loadFlipped(terrainAnimations, terrainImages, terrainFiles); | ||||
| 	loadFlipped(riverAnimations, riverImages, riverFiles); | ||||
| 	loadFlipped(roadAnimations, roadImages, roadFiles); | ||||
| } | ||||
|  | ||||
| void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z) | ||||
| { | ||||
| 	auto & tinfo = map->getTile(int3(x, y, z)); | ||||
| 	ui8 rotation = tinfo.extTileFlags % 4; | ||||
| 	 | ||||
| 	//TODO: use ui8 instead of string key | ||||
| 	auto terrainName = tinfo.terType->name; | ||||
| 	 | ||||
| 	if(terrainImages.at(terrainName).size() <= tinfo.terView) | ||||
| 		return; | ||||
| 	 | ||||
| 	bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3); | ||||
| 	painter.drawImage(x * tileSize, y * tileSize, terrainImages.at(terrainName)[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->id != Road::NO_ROAD) | ||||
| 	{ | ||||
| 		auto roadName = tinfoUpper->roadType->fileName; | ||||
| 		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(roadName).size() > tinfoUpper->roadDir) | ||||
| 		{ | ||||
| 			painter.drawImage(QPoint(x * tileSize, y * tileSize), roadImages.at(roadName)[tinfoUpper->roadDir]->mirrored(hflip, vflip), source); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if(tinfo.roadType->id != Road::NO_ROAD) //print road from this tile | ||||
| 	{ | ||||
| 		auto roadName = tinfo.roadType->fileName; | ||||
| 		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(roadName).size() > tinfo.roadDir) | ||||
| 		{ | ||||
| 			painter.drawImage(QPoint(x * tileSize, y * tileSize + tileSize / 2), roadImages.at(roadName)[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->id == River::NO_RIVER) | ||||
| 		return; | ||||
| 	 | ||||
| 	//TODO: use ui8 instead of string key | ||||
| 	auto riverName = tinfo.riverType->fileName; | ||||
|  | ||||
| 	if(riverImages.at(riverName).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(riverName)[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) | ||||
| 		{ | ||||
| 			//workaround 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) | ||||
| 				{ | ||||
| 					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); | ||||
| 	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 = tile.terType->minimapUnblocked; | ||||
| 	if (tile.blocked && (!tile.visitable)) | ||||
| 		color = 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) | ||||
| 			{ | ||||
| 				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 | ||||
| 			{ | ||||
| 				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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										115
									
								
								mapeditor/maphandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,115 @@ | ||||
| /* | ||||
|  * maphandler.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 | ||||
| //code is copied from vcmiclient/mapHandler.h with minimal changes | ||||
|  | ||||
| #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); | ||||
| }; | ||||
							
								
								
									
										112
									
								
								mapeditor/mapsettings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,112 @@ | ||||
| /* | ||||
|  * mapsettings.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 "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(); | ||||
| } | ||||
							
								
								
									
										34
									
								
								mapeditor/mapsettings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| /* | ||||
|  * mapsettings.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 <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; | ||||
| }; | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										459
									
								
								mapeditor/mapview.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,459 @@ | ||||
| /* | ||||
|  * mapview.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 "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; | ||||
|  | ||||
| 	//TODO: cast parent->parent to MainWindow in order to show coordinates or another way to do it? | ||||
| 	//main->setStatusMessage(QString("x: %1 y: %2").arg(tile.x, tile.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 = SelectionObjectsLayer::MOVEMENT; | ||||
| 			} | ||||
| 			else if(mouseEvent->buttons() & Qt::LeftButton) | ||||
| 			{ | ||||
| 				if(sc->selectionObjectsView.selectionMode == SelectionObjectsLayer::SELECTION) | ||||
| 				{ | ||||
| 					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 = SelectionObjectsLayer::SELECTION; | ||||
| 						} | ||||
| 						else | ||||
| 							sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT; | ||||
| 					} | ||||
| 					else | ||||
| 					{ | ||||
| 						if(!(qApp->keyboardModifiers() & Qt::ControlModifier)) | ||||
| 							sc->selectionObjectsView.clear(); | ||||
| 						sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT; | ||||
| 						sc->selectionObjectsView.selectObject(obj); | ||||
| 					} | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					sc->selectionObjectsView.clear(); | ||||
| 					sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::SELECTION; | ||||
| 				} | ||||
| 			} | ||||
| 			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 == SelectionObjectsLayer::MOVEMENT) | ||||
| 		{ | ||||
| 			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 = SelectionObjectsLayer::NOTHING; | ||||
| 			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); | ||||
| } | ||||
							
								
								
									
										140
									
								
								mapeditor/mapview.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,140 @@ | ||||
| /* | ||||
|  * mapview.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 <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; | ||||
| }; | ||||
							
								
								
									
										78
									
								
								mapeditor/objectbrowser.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,78 @@ | ||||
| /* | ||||
|  * objectbrowser.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 "objectbrowser.h" | ||||
| #include "../lib/mapObjects/CObjectClassesHandler.h" | ||||
|  | ||||
| ObjectBrowser::ObjectBrowser(QObject *parent) | ||||
| 	: QSortFilterProxyModel{parent}, terrain(Terrain::ANY_TERRAIN) | ||||
| { | ||||
| } | ||||
|  | ||||
| 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_TERRAIN) | ||||
| 		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); | ||||
|  | ||||
| 	//if we are here, just text filter will be applied | ||||
| 	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)); | ||||
| } | ||||
|  | ||||
							
								
								
									
										27
									
								
								mapeditor/objectbrowser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,27 @@ | ||||
| /* | ||||
|  * objectbrowser.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 <QSortFilterProxyModel> | ||||
| #include "../lib/Terrain.h" | ||||
|  | ||||
| class ObjectBrowser : public QSortFilterProxyModel | ||||
| { | ||||
| public: | ||||
| 	explicit ObjectBrowser(QObject *parent = nullptr); | ||||
|  | ||||
| 	TerrainId 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; | ||||
| }; | ||||
							
								
								
									
										147
									
								
								mapeditor/playerparams.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,147 @@ | ||||
| /* | ||||
|  * playerparams.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 "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; | ||||
| 				const auto name = ctown->faction ? town->getObjectName() : town->name + ", (random)"; | ||||
| 				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(tr("Player ID: %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; | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										49
									
								
								mapeditor/playerparams.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,49 @@ | ||||
| /* | ||||
|  * playerparams.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <QWidget> | ||||
| #include "../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; | ||||
| }; | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										89
									
								
								mapeditor/playersettings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,89 @@ | ||||
| /* | ||||
|  * playersettings.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 "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; | ||||
| 	const int minAllowedPlayers = 2; | ||||
| 	for(auto & p : controller.map()->players) | ||||
| 	{ | ||||
| 		if(p.canAnyonePlay()) | ||||
| 		{ | ||||
| 			paramWidgets.push_back(new PlayerParams(controller, players)); | ||||
| 			ui->playersLayout->addWidget(paramWidgets.back()); | ||||
| 			++players; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(players < minAllowedPlayers) | ||||
| 		ui->playersCount->setCurrentText(""); | ||||
| 	else | ||||
| 		ui->playersCount->setCurrentIndex(players - minAllowedPlayers); | ||||
| 	 | ||||
| 	setAttribute(Qt::WA_DeleteOnClose); | ||||
| } | ||||
|  | ||||
| PlayerSettings::~PlayerSettings() | ||||
| { | ||||
| 	delete ui; | ||||
| } | ||||
|  | ||||
| void PlayerSettings::on_playersCount_currentIndexChanged(int index) | ||||
| { | ||||
| 	const auto selectedPlayerCount = index + 2; | ||||
| 	assert(selectedPlayerCount <= controller.map()->players.size()); | ||||
|  | ||||
| 	for(int i = 0; i < selectedPlayerCount; ++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 >= selectedPlayerCount; --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(); | ||||
| } | ||||
|  | ||||
							
								
								
									
										41
									
								
								mapeditor/playersettings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * playersettings.h, part of VCMI engine | ||||
|  * | ||||
|  * Authors: listed in file AUTHORS in main folder | ||||
|  * | ||||
|  * License: GNU General Public License v2.0 or later | ||||
|  * Full text of license available in license.txt file, in main folder | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "StdInc.h" | ||||
| #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; | ||||
| }; | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										568
									
								
								mapeditor/scenelayer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,568 @@ | ||||
| /* | ||||
|  * scenelayer.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 "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(Qt::transparent); | ||||
| 	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(Qt::transparent); | ||||
| 	 | ||||
| 	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(Qt::transparent); | ||||
| 	 | ||||
| 	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(Qt::transparent); | ||||
| 	draw(false); | ||||
| } | ||||
|  | ||||
| void ObjectsLayer::draw(bool onlyDirty) | ||||
| { | ||||
| 	if(!pixmap) | ||||
| 		return; | ||||
| 	 | ||||
| 	if(!map) | ||||
| 		return; | ||||
| 	 | ||||
| 	pixmap->fill(Qt::transparent); | ||||
| 	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(); | ||||
| 	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(Qt::transparent); | ||||
| 	 | ||||
| 	QPainter painter(pixmap.get()); | ||||
| 	painter.setCompositionMode(QPainter::CompositionMode_Source); | ||||
| 	painter.setPen(Qt::white); | ||||
| 	 | ||||
| 	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 == SelectionMode::MOVEMENT && (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)); | ||||
| 	 | ||||
| 	draw(); | ||||
| } | ||||
|  | ||||
| void MinimapViewLayer::draw() | ||||
| { | ||||
| 	if(!map) | ||||
| 		return; | ||||
| 	 | ||||
| 	pixmap->fill(Qt::transparent); | ||||
| 	 | ||||
| 	//maybe not optimal but ok | ||||
| 	QPainter painter(pixmap.get()); | ||||
| 	painter.setPen(Qt::white); | ||||
| 	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(); | ||||
| } | ||||
							
								
								
									
										190
									
								
								mapeditor/scenelayer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,190 @@ | ||||
| /* | ||||
|  * scenelayer.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/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: | ||||
| 	enum SelectionMode | ||||
| 	{ | ||||
| 		NOTHING, SELECTION, MOVEMENT | ||||
| 	}; | ||||
| 	 | ||||
| 	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 | ||||
| 	SelectionMode selectionMode = SelectionMode::NOTHING; | ||||
|  | ||||
| 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; | ||||
| 	 | ||||
| }; | ||||
							
								
								
									
										172
									
								
								mapeditor/validator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,172 @@ | ||||
| /* | ||||
|  * validator.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 "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); | ||||
| 				} | ||||
| 			} | ||||
| 			if(o->getOwner() != PlayerColor::NEUTRAL && o->getOwner().getNum() < map->players.size()) | ||||
| 			{ | ||||
| 				if(!map->players[o->getOwner().getNum()].canAnyonePlay()) | ||||
| 					issues.emplace_back(QString("Object %1 is assinged to non-playable player %2").arg(o->instanceName.c_str(), o->getOwner().getStr().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(tr("Town %1 has undefined owner %2").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); | ||||
| 				} | ||||
| 				if(ins->type) | ||||
| 				{ | ||||
| 					if(!map->allowedHeroes[ins->type->getId().getNum()]) | ||||
| 						issues.emplace_back(QString("Hero %1 is prohibited by map settings").arg(ins->type->getName().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->getObjectName().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->getObjectName().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; | ||||
| } | ||||
							
								
								
									
										40
									
								
								mapeditor/validator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * validator.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 <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; | ||||
| }; | ||||
							
								
								
									
										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> | ||||
							
								
								
									
										417
									
								
								mapeditor/windownewmap.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,417 @@ | ||||
| /* | ||||
|  * windownewmap.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 "../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(); | ||||
|  | ||||
| 	show(); | ||||
|  | ||||
| 	//setup initial parameters | ||||
| 	mapGenOptions.setWidth(ui->widthTxt->text().toInt()); | ||||
| 	mapGenOptions.setHeight(ui->heightTxt->text().toInt()); | ||||
| 	bool twoLevel = ui->twoLevelCheck->isChecked(); | ||||
| 	mapGenOptions.setHasTwoLevels(twoLevel); | ||||
| 	updateTemplateList(); | ||||
| } | ||||
|  | ||||
| WindowNewMap::~WindowNewMap() | ||||
| { | ||||
| 	saveUserSettings(); | ||||
| 	delete ui; | ||||
| } | ||||
|  | ||||
| void WindowNewMap::loadUserSettings() | ||||
| { | ||||
| 	//load window 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; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	auto templateName = s.value(newMapTemplate); | ||||
| 	if (templateName.isValid()) | ||||
| 	{ | ||||
| 		updateTemplateList(); | ||||
| 		 | ||||
| 		auto* templ = VLC->tplh->getTemplate(templateName.toString().toStdString()); | ||||
| 		if (templ) | ||||
| 		{ | ||||
| 			ui->templateCombo->setCurrentText(templateName.toString()); | ||||
| 			//TODO: validate inside this method | ||||
| 			mapGenOptions.setMapTemplate(templ); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			//Display problem on status bar | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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()) | ||||
| 	{ | ||||
| 		s.setValue(newMapTemplate, templateName); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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_okButton_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; | ||||
| 		} | ||||
| 		 | ||||
| 		int seed = std::time(nullptr); | ||||
| 		if(ui->checkSeed->isChecked() && !ui->lineSeed->text().isEmpty()) | ||||
| 			seed = ui->lineSeed->text().toInt(); | ||||
| 			 | ||||
| 		CMapGenerator generator(mapGenOptions, seed); | ||||
| 		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 = data_cast<const CRmgTemplate>(ui->templateCombo->currentData().toLongLong()); | ||||
| 	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("[default]", 0); | ||||
|  | ||||
| 	for(auto * templ : templates) | ||||
| 	{ | ||||
| 		ui->templateCombo->addItem(QString::fromStdString(templ->getName()), data_cast(templ)); | ||||
| 	} | ||||
|  | ||||
| 	ui->templateCombo->setCurrentIndex(0); | ||||
| } | ||||
|  | ||||
| void WindowNewMap::on_checkSeed_toggled(bool checked) | ||||
| { | ||||
| 	ui->lineSeed->setEnabled(checked); | ||||
| } | ||||
|  | ||||
							
								
								
									
										102
									
								
								mapeditor/windownewmap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,102 @@ | ||||
| /* | ||||
|  * windownewmap.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 <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 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_okButton_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); | ||||
|  | ||||
| 	void on_checkSeed_toggled(bool checked); | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	void updateTemplateList(); | ||||
|  | ||||
| 	void loadUserSettings(); | ||||
| 	void saveUserSettings(); | ||||
|  | ||||
| private: | ||||
| 	Ui::WindowNewMap *ui; | ||||
|  | ||||
| 	CMapGenOptions mapGenOptions; | ||||
| 	bool randomMap = false; | ||||
| }; | ||||
							
								
								
									
										816
									
								
								mapeditor/windownewmap.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,816 @@ | ||||
| <?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>444</width> | ||||
|     <height>445</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>999</width> | ||||
|     <height>999</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>91</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>140</y> | ||||
|      <width>431</width> | ||||
|      <height>301</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>51</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>51</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>230</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 class="QLineEdit" name="lineSeed"> | ||||
|     <property name="enabled"> | ||||
|      <bool>false</bool> | ||||
|     </property> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>280</x> | ||||
|       <y>270</y> | ||||
|       <width>131</width> | ||||
|       <height>21</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="inputMethodHints"> | ||||
|      <set>Qt::ImhDigitsOnly</set> | ||||
|     </property> | ||||
|     <property name="text"> | ||||
|      <string>0</string> | ||||
|     </property> | ||||
|    </widget> | ||||
|    <widget class="QCheckBox" name="checkSeed"> | ||||
|     <property name="geometry"> | ||||
|      <rect> | ||||
|       <x>110</x> | ||||
|       <y>270</y> | ||||
|       <width>161</width> | ||||
|       <height>20</height> | ||||
|      </rect> | ||||
|     </property> | ||||
|     <property name="text"> | ||||
|      <string>Custom seed</string> | ||||
|     </property> | ||||
|    </widget> | ||||
|   </widget> | ||||
|   <widget class="QCheckBox" name="randomMapCheck"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>10</x> | ||||
|      <y>120</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="okButton"> | ||||
|       <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> | ||||