mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Editor prerequisites [part 2] (#889)
This commit is contained in:
		| @@ -68,7 +68,9 @@ set(lib_SRCS | ||||
| 		mapping/CMap.cpp | ||||
| 		mapping/CMapEditManager.cpp | ||||
| 		mapping/CMapInfo.cpp | ||||
| 		mapping/CMapOperation.cpp | ||||
| 		mapping/CMapService.cpp | ||||
| 		mapping/MapEditUtils.cpp | ||||
| 		mapping/MapFormatH3M.cpp | ||||
| 		mapping/MapFormatJson.cpp | ||||
|  | ||||
| @@ -177,6 +179,7 @@ set(lib_SRCS | ||||
| 		IHandlerBase.cpp | ||||
| 		JsonDetail.cpp | ||||
| 		JsonNode.cpp | ||||
| 		LoadProgress.cpp | ||||
| 		LogicalExpression.cpp | ||||
| 		NetPacksLib.cpp | ||||
| 		ObstacleHandler.cpp | ||||
| @@ -301,7 +304,9 @@ set(lib_HEADERS | ||||
| 		mapping/CMapEditManager.h | ||||
| 		mapping/CMap.h | ||||
| 		mapping/CMapInfo.h | ||||
| 		mapping/CMapOperation.h | ||||
| 		mapping/CMapService.h | ||||
| 		mapping/MapEditUtils.h | ||||
| 		mapping/MapFormatH3M.h | ||||
| 		mapping/MapFormatJson.h | ||||
|  | ||||
| @@ -415,6 +420,7 @@ set(lib_HEADERS | ||||
| 		Interprocess.h | ||||
| 		JsonDetail.h | ||||
| 		JsonNode.h | ||||
| 		LoadProgress.h | ||||
| 		LogicalExpression.h | ||||
| 		NetPacksBase.h | ||||
| 		NetPacks.h | ||||
|   | ||||
							
								
								
									
										81
									
								
								lib/LoadProgress.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								lib/LoadProgress.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| /* | ||||
|  * LoadProgress.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 "LoadProgress.h" | ||||
|  | ||||
| using namespace Load; | ||||
|  | ||||
| Progress::Progress(): _progress(std::numeric_limits<Type>::min()) | ||||
| { | ||||
| 	setupSteps(100); | ||||
| } | ||||
|  | ||||
| Type Progress::get() const | ||||
| { | ||||
| 	if(_step >= _maxSteps) | ||||
| 		return _target; | ||||
| 	 | ||||
| 	return static_cast<int>(_progress) + _step * static_cast<int>(_target - _progress) / _maxSteps; | ||||
| } | ||||
|  | ||||
| void Progress::set(Type p) | ||||
| { | ||||
| 	_progress = p; | ||||
| } | ||||
|  | ||||
| bool Progress::finished() const | ||||
| { | ||||
| 	return get() == std::numeric_limits<Type>::max(); | ||||
| } | ||||
|  | ||||
| void Progress::reset(int s) | ||||
| { | ||||
| 	_progress = std::numeric_limits<Type>::min(); | ||||
| 	setupSteps(s); | ||||
| } | ||||
|  | ||||
| void Progress::finish() | ||||
| { | ||||
| 	_progress = _target = std::numeric_limits<Type>::max(); | ||||
| 	_step = std::numeric_limits<Type>::min(); | ||||
| 	_maxSteps = std::numeric_limits<Type>::min(); | ||||
| } | ||||
|  | ||||
| void Progress::setupSteps(int s) | ||||
| { | ||||
| 	setupStepsTill(s, std::numeric_limits<Type>::max()); | ||||
| } | ||||
|  | ||||
| void Progress::setupStepsTill(int s, Type p) | ||||
| { | ||||
| 	if(finished()) | ||||
| 		return; | ||||
| 	 | ||||
| 	if(_step > std::numeric_limits<Type>::min()) | ||||
| 		_progress = get(); | ||||
| 	 | ||||
| 	_step = std::numeric_limits<Type>::min(); | ||||
| 	_maxSteps = s; | ||||
| 	 | ||||
| 	_target = p; | ||||
| } | ||||
|  | ||||
| void Progress::step(int count) | ||||
| { | ||||
| 	if(_step + count > _maxSteps) | ||||
| 	{ | ||||
| 		_step = _maxSteps.load(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		_step += count; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										71
									
								
								lib/LoadProgress.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lib/LoadProgress.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| /* | ||||
|  * LoadProgress.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 <atomic> | ||||
|  | ||||
| namespace Load | ||||
| { | ||||
|  | ||||
| using Type = unsigned char; | ||||
|  | ||||
| /* | ||||
|  * Purpose of that class is to track progress of computations | ||||
|  * Derive from this class if you want to translate user or system | ||||
|  * remaining amount of work needed. | ||||
|  * Tracking of the progress should be from another thread. | ||||
|  */ | ||||
| class DLL_LINKAGE Progress | ||||
| { | ||||
| public: | ||||
| 	 | ||||
| 	//Sets current state to 0. | ||||
| 	//Amount of steps to finish progress will be equal to 100 | ||||
| 	Progress(); | ||||
| 	virtual ~Progress() = default; | ||||
|  | ||||
| 	//Returns current state of the progress | ||||
| 	//To translate it into percentage (to float, for example): | ||||
| 	//float progress = <>.get() / std::numeric_limits<Load::Type>::max(); | ||||
| 	Type get() const; | ||||
| 	 | ||||
| 	//Returns true if current state equal to final state, false otherwise | ||||
| 	bool finished() const; | ||||
| 	 | ||||
| 	//Sets current state equal to the argument | ||||
| 	void set(Type); | ||||
| 	 | ||||
| 	//Sets current state to 0 | ||||
| 	//steps - amount of steps needed to reach final state | ||||
| 	void reset(int steps = 100); | ||||
| 	 | ||||
| 	//Immediately sets state to final | ||||
| 	//finished() will return true after calling this method | ||||
| 	void finish(); | ||||
| 	 | ||||
| 	//Sets amount of steps needed to reach final state | ||||
| 	//doesn't modify current state | ||||
| 	void setupSteps(int steps); | ||||
| 	 | ||||
| 	//Sets amount of steps needed to reach state specified | ||||
| 	//doesn't modify current state | ||||
| 	void setupStepsTill(int steps, Type state); | ||||
| 	 | ||||
| 	//Increases current state by steps count | ||||
| 	//if current state reaches final state, returns immediately | ||||
| 	void step(int count = 1); | ||||
|  | ||||
| private: | ||||
| 	std::atomic<Type> _progress, _target; | ||||
| 	std::atomic<int> _step, _maxSteps; | ||||
| }; | ||||
| } | ||||
| @@ -165,3 +165,17 @@ void CFilesystemList::addLoader(ISimpleResourceLoader * loader, bool writeable) | ||||
| 	if (writeable) | ||||
| 		writeableLoaders.insert(loader); | ||||
| } | ||||
|  | ||||
| bool CFilesystemList::removeLoader(ISimpleResourceLoader * loader) | ||||
| { | ||||
| 	for(auto loaderIterator = loaders.begin(); loaderIterator != loaders.end(); ++loaderIterator) | ||||
| 	{ | ||||
| 		if(loaderIterator->get() == loader) | ||||
| 		{ | ||||
| 			loaders.erase(loaderIterator); | ||||
| 			writeableLoaders.erase(loader); | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -84,4 +84,14 @@ public: | ||||
| 	 * @param writeable - resource shall be treated as writeable | ||||
| 	 */ | ||||
| 	void addLoader(ISimpleResourceLoader * loader, bool writeable); | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Removes loader from the loader list | ||||
| 	 * Take care about memory deallocation | ||||
| 	 * | ||||
| 	 * @param loader The simple resource loader object to remove | ||||
| 	 * | ||||
| 	 * @return if loader was successfully removed | ||||
| 	 */ | ||||
| 	bool removeLoader(ISimpleResourceLoader * loader); | ||||
| }; | ||||
|   | ||||
| @@ -225,6 +225,21 @@ void CResourceHandler::addFilesystem(const std::string & parent, const std::stri | ||||
| 	knownLoaders[identifier] = loader; | ||||
| } | ||||
|  | ||||
| bool CResourceHandler::removeFilesystem(const std::string & parent, const std::string & identifier) | ||||
| { | ||||
| 	if(knownLoaders.count(identifier) == 0) | ||||
| 		return false; | ||||
| 	 | ||||
| 	if(knownLoaders.count(parent) == 0) | ||||
| 		return false; | ||||
| 	 | ||||
| 	auto list = dynamic_cast<CFilesystemList *>(knownLoaders.at(parent)); | ||||
| 	assert(list); | ||||
| 	list->removeLoader(knownLoaders[identifier]); | ||||
| 	knownLoaders.erase(identifier); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| ISimpleResourceLoader * CResourceHandler::createFileSystem(const std::string & prefix, const JsonNode &fsConfig) | ||||
| { | ||||
| 	CFilesystemGenerator generator(prefix); | ||||
|   | ||||
| @@ -87,6 +87,14 @@ public: | ||||
| 	 * @param loader resource loader to add | ||||
| 	 */ | ||||
| 	static void addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader); | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @brief removeFilesystem removes previously added filesystem from global resouce holder | ||||
| 	 * @param parent parent loader containing filesystem | ||||
| 	 * @param identifier name of this loader | ||||
| 	 * @return if filesystem was successfully removed | ||||
| 	 */ | ||||
| 	static bool removeFilesystem(const std::string & parent, const std::string & identifier); | ||||
|  | ||||
| 	/** | ||||
| 	 * @brief createModFileSystem - creates filesystem out of config file | ||||
|   | ||||
| @@ -1400,6 +1400,11 @@ void CGHeroInstance::afterAddToMap(CMap * map) | ||||
| 	if(ID == Obj::HERO) | ||||
| 		map->heroesOnMap.push_back(this); | ||||
| } | ||||
| void CGHeroInstance::afterRemoveFromMap(CMap* map) | ||||
| { | ||||
| 	if (ID == Obj::HERO) | ||||
| 		vstd::erase_if_present(map->heroesOnMap, this); | ||||
| } | ||||
|  | ||||
| void CGHeroInstance::setHeroTypeName(const std::string & identifier) | ||||
| { | ||||
|   | ||||
| @@ -268,6 +268,7 @@ public: | ||||
| 	std::string getObjectName() const override; | ||||
|  | ||||
| 	void afterAddToMap(CMap * map) override; | ||||
| 	void afterRemoveFromMap(CMap * map) override; | ||||
|  | ||||
| 	void updateFrom(const JsonNode & data) override; | ||||
|  | ||||
|   | ||||
| @@ -1441,6 +1441,12 @@ void CGTownInstance::afterAddToMap(CMap * map) | ||||
| 		map->towns.push_back(this); | ||||
| } | ||||
|  | ||||
| void CGTownInstance::afterRemoveFromMap(CMap * map) | ||||
| { | ||||
| 	if (ID == Obj::TOWN) | ||||
| 		vstd::erase_if_present(map->towns, this); | ||||
| } | ||||
|  | ||||
| void CGTownInstance::reset() | ||||
| { | ||||
| 	CGTownInstance::merchantArtifacts.clear(); | ||||
|   | ||||
| @@ -342,6 +342,7 @@ public: | ||||
| 	std::string getObjectName() const override; | ||||
|  | ||||
| 	void afterAddToMap(CMap * map) override; | ||||
| 	void afterRemoveFromMap(CMap * map) override; | ||||
| 	static void reset(); | ||||
|  | ||||
| 	inline bool isBattleOutsideTown(const CGHeroInstance * defendingHero) const | ||||
|   | ||||
| @@ -390,6 +390,11 @@ void CGObjectInstance::afterAddToMap(CMap * map) | ||||
| 	//nothing here | ||||
| } | ||||
|  | ||||
| void CGObjectInstance::afterRemoveFromMap(CMap * map) | ||||
| { | ||||
| 	//nothing here | ||||
| } | ||||
|  | ||||
| void CGObjectInstance::serializeJsonOptions(JsonSerializeFormat & handler) | ||||
| { | ||||
| 	//nothing here | ||||
|   | ||||
| @@ -197,6 +197,7 @@ public: | ||||
| 	void setProperty(ui8 what, ui32 val) override final; | ||||
|  | ||||
| 	virtual void afterAddToMap(CMap * map); | ||||
| 	virtual void afterRemoveFromMap(CMap * map); | ||||
|  | ||||
| 	///Entry point of binary (de-)serialization | ||||
| 	template <typename Handler> void serialize(Handler &h, const int version) | ||||
|   | ||||
| @@ -1425,6 +1425,9 @@ void CGArtifact::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) | ||||
|  | ||||
| void CGArtifact::afterAddToMap(CMap * map) | ||||
| { | ||||
| 	//Artifacts from map objects are never removed | ||||
| 	//FIXME: This should be revertible in map editor | ||||
|  | ||||
| 	if(ID == Obj::SPELL_SCROLL && storedArtifact && storedArtifact->id.getNum() < 0) | ||||
|         map->addNewArtifactInstance(storedArtifact); | ||||
| } | ||||
|   | ||||
| @@ -445,7 +445,7 @@ public: | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| class CGShipyard : public CGObjectInstance, public IShipyard | ||||
| class DLL_LINKAGE CGShipyard : public CGObjectInstance, public IShipyard | ||||
| { | ||||
| public: | ||||
| 	void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed | ||||
|   | ||||
| @@ -233,7 +233,8 @@ CMapHeader::~CMapHeader() | ||||
|  | ||||
| CMap::CMap() | ||||
| 	: checksum(0), grailPos(-1, -1, -1), grailRadius(0), terrain(nullptr), | ||||
| 	guardingCreaturePositions(nullptr) | ||||
| 	guardingCreaturePositions(nullptr), | ||||
| 	uidCounter(0) | ||||
| { | ||||
| 	allHeroes.resize(allowedHeroes.size()); | ||||
| 	allowedAbilities = VLC->skillh->getDefaultAllowed(); | ||||
| @@ -593,6 +594,7 @@ void CMap::addNewArtifactInstance(CArtifactInstance * art) | ||||
|  | ||||
| void CMap::eraseArtifactInstance(CArtifactInstance * art) | ||||
| { | ||||
| 	//TODO: handle for artifacts removed in map editor | ||||
| 	assert(artInstances[art->id.getNum()] == art); | ||||
| 	artInstances[art->id.getNum()].dellNull(); | ||||
| } | ||||
| @@ -603,6 +605,33 @@ void CMap::addNewQuestInstance(CQuest* quest) | ||||
| 	quests.push_back(quest); | ||||
| } | ||||
|  | ||||
| void CMap::removeQuestInstance(CQuest * quest) | ||||
|  | ||||
| { | ||||
| 	//TODO: should be called only by map editor. | ||||
| 	//During game, completed quests or quests from removed objects stay forever | ||||
|  | ||||
| 	//Shift indexes | ||||
| 	auto iter = std::next(quests.begin(), quest->qid); | ||||
| 	iter = quests.erase(iter); | ||||
| 	for (int i = quest->qid; iter != quests.end(); ++i, ++iter) | ||||
| 	{ | ||||
| 		(*iter)->qid = i; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CMap::setUniqueInstanceName(CGObjectInstance * obj) | ||||
|  | ||||
| { | ||||
| 	//this gives object unique name even if objects are removed later | ||||
|  | ||||
| 	auto uid = uidCounter++; | ||||
|  | ||||
| 	boost::format fmt("%s_%d"); | ||||
| 	fmt % obj->typeName % uid; | ||||
| 	obj->instanceName = fmt.str(); | ||||
| } | ||||
|  | ||||
| void CMap::addNewObject(CGObjectInstance * obj) | ||||
| { | ||||
| 	if(obj->id != ObjectInstanceID((si32)objects.size())) | ||||
| @@ -611,17 +640,43 @@ void CMap::addNewObject(CGObjectInstance * obj) | ||||
| 	if(obj->instanceName == "") | ||||
| 		throw std::runtime_error("Object instance name missing"); | ||||
|  | ||||
| 	auto it = instanceNames.find(obj->instanceName); | ||||
| 	if(it != instanceNames.end()) | ||||
| 	if (vstd::contains(instanceNames, obj->instanceName)) | ||||
| 		throw std::runtime_error("Object instance name duplicated: "+obj->instanceName); | ||||
|  | ||||
| 	objects.push_back(obj); | ||||
| 	instanceNames[obj->instanceName] = obj; | ||||
| 	addBlockVisTiles(obj); | ||||
|  | ||||
| 	//TODO: how about defeated heroes recruited again? | ||||
|  | ||||
| 	obj->afterAddToMap(this); | ||||
| } | ||||
|  | ||||
| void CMap::moveObject(CGObjectInstance * obj, const int3 & pos) | ||||
| { | ||||
| 	removeBlockVisTiles(obj); | ||||
| 	obj->pos = pos; | ||||
| 	addBlockVisTiles(obj); | ||||
| } | ||||
|  | ||||
| void CMap::removeObject(CGObjectInstance * obj) | ||||
| { | ||||
| 	removeBlockVisTiles(obj); | ||||
| 	instanceNames.erase(obj->instanceName); | ||||
|  | ||||
| 	//update indeces | ||||
| 	auto iter = std::next(objects.begin(), obj->id.getNum()); | ||||
| 	iter = objects.erase(iter); | ||||
| 	for(int i = obj->id.getNum(); iter != objects.end(); ++i, ++iter) | ||||
| 	{ | ||||
| 		(*iter)->id = ObjectInstanceID(i); | ||||
| 	} | ||||
| 	 | ||||
| 	obj->afterRemoveFromMap(this); | ||||
|  | ||||
| 	//TOOD: Clean artifact instances (mostly worn by hero?) and quests related to this object | ||||
| } | ||||
|  | ||||
| void CMap::initTerrain() | ||||
| { | ||||
| 	int level = twoLevel ? 2 : 1; | ||||
|   | ||||
| @@ -361,9 +361,14 @@ public: | ||||
| 	void eraseArtifactInstance(CArtifactInstance * art); | ||||
|  | ||||
| 	void addNewQuestInstance(CQuest * quest); | ||||
| 	void removeQuestInstance(CQuest * quest); | ||||
|  | ||||
| 	void setUniqueInstanceName(CGObjectInstance * obj); | ||||
| 	///Use only this method when creating new map object instances | ||||
| 	void addNewObject(CGObjectInstance * obj); | ||||
| 	void moveObject(CGObjectInstance * obj, const int3 & dst); | ||||
| 	void removeObject(CGObjectInstance * obj); | ||||
|  | ||||
|  | ||||
| 	/// Gets object of specified type on requested position | ||||
| 	const CGObjectInstance * getObjectiveObjectFrom(int3 pos, Obj::EObj type); | ||||
| @@ -408,6 +413,7 @@ public: | ||||
| private: | ||||
| 	/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground | ||||
| 	TerrainTile*** terrain; | ||||
| 	si32 uidCounter; //TODO: initialize when loading an old map | ||||
|  | ||||
| public: | ||||
| 	template <typename Handler> | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -10,9 +10,8 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "../CRandomGenerator.h" | ||||
| #include "../int3.h" | ||||
| #include "../GameConstants.h" | ||||
| #include "CMapOperation.h" | ||||
| #include "Terrain.h" | ||||
|  | ||||
| class CGObjectInstance; | ||||
| @@ -20,112 +19,6 @@ class CTerrainViewPatternConfig; | ||||
| struct TerrainViewPattern; | ||||
| class CMap; | ||||
|  | ||||
| /// Represents a map rectangle. | ||||
| struct DLL_LINKAGE MapRect | ||||
| { | ||||
| 	MapRect(); | ||||
| 	MapRect(int3 pos, si32 width, si32 height); | ||||
| 	si32 x, y, z; | ||||
| 	si32 width, height; | ||||
|  | ||||
| 	si32 left() const; | ||||
| 	si32 right() const; | ||||
| 	si32 top() const; | ||||
| 	si32 bottom() const; | ||||
|  | ||||
| 	int3 topLeft() const; /// Top left corner of this rect. | ||||
| 	int3 topRight() const; /// Top right corner of this rect. | ||||
| 	int3 bottomLeft() const; /// Bottom left corner of this rect. | ||||
| 	int3 bottomRight() const; /// Bottom right corner of this rect. | ||||
|  | ||||
| 	/// Returns a MapRect of the intersection of this rectangle and the given one. | ||||
| 	MapRect operator&(const MapRect & rect) const; | ||||
|  | ||||
| 	template<typename Func> | ||||
| 	void forEach(Func f) const | ||||
| 	{ | ||||
| 		for(int j = y; j < bottom(); ++j) | ||||
| 		{ | ||||
| 			for(int i = x; i < right(); ++i) | ||||
| 			{ | ||||
| 				f(int3(i, j, z)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| /// Generic selection class to select any type | ||||
| template<typename T> | ||||
| class DLL_LINKAGE CMapSelection | ||||
| { | ||||
| public: | ||||
| 	explicit CMapSelection(CMap * map) : map(map) { } | ||||
| 	virtual ~CMapSelection() { }; | ||||
| 	void select(const T & item) | ||||
| 	{ | ||||
| 		selectedItems.insert(item); | ||||
| 	} | ||||
| 	void deselect(const T & item) | ||||
| 	{ | ||||
| 		selectedItems.erase(item); | ||||
| 	} | ||||
| 	std::set<T> getSelectedItems() | ||||
| 	{ | ||||
| 		return selectedItems; | ||||
| 	} | ||||
| 	CMap * getMap() { return map; } | ||||
| 	virtual void selectRange(const MapRect & rect) { } | ||||
| 	virtual void deselectRange(const MapRect & rect) { } | ||||
| 	virtual void selectAll() { } | ||||
| 	virtual void clearSelection() { } | ||||
|  | ||||
| private: | ||||
| 	std::set<T> selectedItems; | ||||
| 	CMap * map; | ||||
| }; | ||||
|  | ||||
| /// Selection class to select terrain. | ||||
| class DLL_LINKAGE CTerrainSelection : public CMapSelection<int3> | ||||
| { | ||||
| public: | ||||
| 	explicit CTerrainSelection(CMap * map); | ||||
| 	void selectRange(const MapRect & rect) override; | ||||
| 	void deselectRange(const MapRect & rect) override; | ||||
| 	void selectAll() override; | ||||
| 	void clearSelection() override; | ||||
| 	void setSelection(const std::vector<int3> & vec); | ||||
| }; | ||||
|  | ||||
| /// Selection class to select objects. | ||||
| class DLL_LINKAGE CObjectSelection: public CMapSelection<CGObjectInstance *> | ||||
| { | ||||
| public: | ||||
| 	explicit CObjectSelection(CMap * map); | ||||
| }; | ||||
|  | ||||
| /// The abstract base class CMapOperation defines an operation that can be executed, undone and redone. | ||||
| class DLL_LINKAGE CMapOperation : public boost::noncopyable | ||||
| { | ||||
| public: | ||||
| 	explicit CMapOperation(CMap * map); | ||||
| 	virtual ~CMapOperation() { }; | ||||
|  | ||||
| 	virtual void execute() = 0; | ||||
| 	virtual void undo() = 0; | ||||
| 	virtual void redo() = 0; | ||||
| 	virtual std::string getLabel() const = 0; /// Returns a display-able name of the operation. | ||||
|  | ||||
| 	static const int FLIP_PATTERN_HORIZONTAL = 1; | ||||
| 	static const int FLIP_PATTERN_VERTICAL = 2; | ||||
| 	static const int FLIP_PATTERN_BOTH = 3; | ||||
|  | ||||
| protected: | ||||
| 	MapRect extendTileAround(const int3 & centerPos) const; | ||||
| 	MapRect extendTileAroundSafely(const int3 & centerPos) const; /// doesn't exceed map size | ||||
|  | ||||
| 	CMap * map; | ||||
| }; | ||||
|  | ||||
| /// The CMapUndoManager provides the functionality to save operations and undo/redo them. | ||||
| class DLL_LINKAGE CMapUndoManager : boost::noncopyable | ||||
| { | ||||
| @@ -138,6 +31,8 @@ public: | ||||
|  | ||||
| 	/// The undo redo limit is a number which says how many undo/redo items can be saved. The default | ||||
| 	/// value is 10. If the value is 0, no undo/redo history will be maintained. | ||||
| 	 | ||||
| 	/// FIXME: unlimited undo please | ||||
| 	int getUndoRedoLimit() const; | ||||
| 	void setUndoRedoLimit(int value); | ||||
|  | ||||
| @@ -146,6 +41,9 @@ public: | ||||
|  | ||||
| 	void addOperation(std::unique_ptr<CMapOperation> && operation); /// Client code does not need to call this method. | ||||
|  | ||||
| 	//poor man's signal | ||||
| 	void setUndoCallback(std::function<void(bool, bool)> functor); | ||||
|  | ||||
| private: | ||||
| 	typedef std::list<std::unique_ptr<CMapOperation> > TStack; | ||||
|  | ||||
| @@ -155,6 +53,9 @@ private: | ||||
| 	TStack undoStack; | ||||
| 	TStack redoStack; | ||||
| 	int undoRedoLimit; | ||||
|  | ||||
| 	void onUndoRedo(); | ||||
| 	std::function<void(bool allowUndo, bool allowRedo)> undoCallback; | ||||
| }; | ||||
|  | ||||
| /// The map edit manager provides functionality for drawing terrain and placing | ||||
| @@ -178,6 +79,10 @@ public: | ||||
| 	void drawRiver(const std::string & riverType, CRandomGenerator * gen = nullptr); | ||||
|  | ||||
| 	void insertObject(CGObjectInstance * obj); | ||||
| 	void insertObjects(std::set<CGObjectInstance *> & objects); | ||||
| 	void moveObject(CGObjectInstance * obj, const int3 & pos); | ||||
| 	void removeObject(CGObjectInstance * obj); | ||||
| 	void removeObjects(std::set<CGObjectInstance *> & objects); | ||||
|  | ||||
| 	CTerrainSelection & getTerrainSelection(); | ||||
| 	CObjectSelection & getObjectSelection(); | ||||
| @@ -193,228 +98,3 @@ private: | ||||
| 	CTerrainSelection terrainSel; | ||||
| 	CObjectSelection objectSel; | ||||
| }; | ||||
|  | ||||
| /* ---------------------------------------------------------------------------- */ | ||||
| /* Implementation/Detail classes, Private API */ | ||||
| /* ---------------------------------------------------------------------------- */ | ||||
|  | ||||
| /// The CComposedOperation is an operation which consists of several operations. | ||||
| class CComposedOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CComposedOperation(CMap * map); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
|  | ||||
| 	void addOperation(std::unique_ptr<CMapOperation> && operation); | ||||
|  | ||||
| private: | ||||
| 	std::list<std::unique_ptr<CMapOperation> > operations; | ||||
| }; | ||||
|  | ||||
| /// The terrain view pattern describes a specific composition of terrain tiles | ||||
| /// in a 3x3 matrix and notes which terrain view frame numbers can be used. | ||||
| struct DLL_LINKAGE TerrainViewPattern | ||||
| { | ||||
| 	struct WeightedRule | ||||
| 	{ | ||||
| 		WeightedRule(std::string &Name); | ||||
| 		/// Gets true if this rule is a standard rule which means that it has a value of one of the RULE_* constants. | ||||
| 		inline bool isStandardRule() const | ||||
| 		{ | ||||
| 			return standardRule; | ||||
| 		} | ||||
| 		inline bool isAnyRule() const | ||||
| 		{ | ||||
| 			return anyRule; | ||||
| 		} | ||||
| 		inline bool isDirtRule() const | ||||
| 		{ | ||||
| 			return dirtRule; | ||||
| 		} | ||||
| 		inline bool isSandRule() const | ||||
| 		{ | ||||
| 			return sandRule; | ||||
| 		} | ||||
| 		inline bool isTransition() const | ||||
| 		{ | ||||
| 			return transitionRule; | ||||
| 		} | ||||
| 		inline bool isNativeStrong() const | ||||
| 		{ | ||||
| 			return nativeStrongRule; | ||||
| 		} | ||||
| 		inline bool isNativeRule() const | ||||
| 		{ | ||||
| 			return nativeRule; | ||||
| 		} | ||||
| 		void setNative(); | ||||
|  | ||||
| 		/// The name of the rule. Can be any value of the RULE_* constants or a ID of a another pattern. | ||||
| 		//FIXME: remove string variable altogether, use only in constructor | ||||
| 		std::string name; | ||||
| 		/// Optional. A rule can have points. Patterns may have a minimum count of points to reach to be successful. | ||||
| 		int points; | ||||
|  | ||||
| 	private: | ||||
| 		bool standardRule; | ||||
| 		bool anyRule; | ||||
| 		bool dirtRule; | ||||
| 		bool sandRule; | ||||
| 		bool transitionRule; | ||||
| 		bool nativeStrongRule; | ||||
| 		bool nativeRule; | ||||
|  | ||||
| 		WeightedRule(); //only allow string constructor | ||||
| 	}; | ||||
|  | ||||
| 	static const int PATTERN_DATA_SIZE = 9; | ||||
| 	/// Constant for the flip mode different images. Pattern will be flipped and different images will be used(mapping area is divided into 4 parts) | ||||
| 	static const std::string FLIP_MODE_DIFF_IMAGES; | ||||
| 	/// Constant for the rule dirt, meaning a dirty border is required. | ||||
| 	static const std::string RULE_DIRT; | ||||
| 	/// Constant for the rule sand, meaning a sandy border is required. | ||||
| 	static const std::string RULE_SAND; | ||||
| 	/// Constant for the rule transition, meaning a dirty OR sandy border is required. | ||||
| 	static const std::string RULE_TRANSITION; | ||||
| 	/// Constant for the rule native, meaning a native border is required. | ||||
| 	static const std::string RULE_NATIVE; | ||||
| 	/// Constant for the rule native strong, meaning a native type is required. | ||||
| 	static const std::string RULE_NATIVE_STRONG; | ||||
| 	/// Constant for the rule any, meaning a native type, dirty OR sandy border is required. | ||||
| 	static const std::string RULE_ANY; | ||||
|  | ||||
| 	TerrainViewPattern(); | ||||
|  | ||||
| 	/// The pattern data can be visualized as a 3x3 matrix: | ||||
| 	/// [ ][ ][ ] | ||||
| 	/// [ ][ ][ ] | ||||
| 	/// [ ][ ][ ] | ||||
| 	/// | ||||
| 	/// The box in the center belongs always to the native terrain type and | ||||
| 	/// is the point of origin. Depending on the terrain type different rules | ||||
| 	/// can be used. Their meaning differs also from type to type. | ||||
| 	/// | ||||
| 	/// std::vector -> several rules can be used in one cell | ||||
| 	std::array<std::vector<WeightedRule>, PATTERN_DATA_SIZE> data; | ||||
|  | ||||
| 	/// The identifier of the pattern, if it's referenced from a another pattern. | ||||
| 	std::string id; | ||||
|  | ||||
| 	/// This describes the mapping between this pattern and the corresponding range of frames | ||||
| 	/// which should be used for the ter view. | ||||
| 	/// | ||||
| 	/// std::vector -> size=1: typical, size=2: if this pattern should map to two different types of borders | ||||
| 	/// std::pair   -> 1st value: lower range, 2nd value: upper range | ||||
| 	std::vector<std::pair<int, int> > mapping; | ||||
| 	/// If diffImages is true, different images/frames are used to place a rotated terrain view. If it's false | ||||
| 	/// the same frame will be used and rotated. | ||||
| 	bool diffImages; | ||||
| 	/// The rotationTypesCount is only used if diffImages is true and holds the number how many rotation types(horizontal, etc...) | ||||
| 	/// are supported. | ||||
| 	int rotationTypesCount; | ||||
|  | ||||
| 	/// The minimum and maximum points to reach to validate the pattern successfully. | ||||
| 	int minPoints, maxPoints; | ||||
| }; | ||||
|  | ||||
| /// The terrain view pattern config loads pattern data from the filesystem. | ||||
| class DLL_LINKAGE CTerrainViewPatternConfig : public boost::noncopyable | ||||
| { | ||||
| public: | ||||
| 	typedef std::vector<TerrainViewPattern> TVPVector; | ||||
|  | ||||
| 	CTerrainViewPatternConfig(); | ||||
| 	~CTerrainViewPatternConfig(); | ||||
|  | ||||
| 	const std::vector<TVPVector> & getTerrainViewPatterns(const Terrain & terrain) const; | ||||
| 	boost::optional<const TerrainViewPattern &> getTerrainViewPatternById(std::string patternId, const std::string & id) const; | ||||
| 	boost::optional<const TVPVector &> getTerrainViewPatternsById(const Terrain & terrain, const std::string & id) const; | ||||
| 	const TVPVector * getTerrainTypePatternById(const std::string & id) const; | ||||
| 	void flipPattern(TerrainViewPattern & pattern, int flip) const; | ||||
|  | ||||
| private: | ||||
| 	std::map<std::string, std::vector<TVPVector> > terrainViewPatterns; | ||||
| 	std::map<std::string, TVPVector> terrainTypePatterns; | ||||
| }; | ||||
|  | ||||
| /// The CDrawTerrainOperation class draws a terrain area on the map. | ||||
| class CDrawTerrainOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, Terrain terType, CRandomGenerator * gen); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| private: | ||||
| 	struct ValidationResult | ||||
| 	{ | ||||
| 		ValidationResult(bool result, const std::string & transitionReplacement = ""); | ||||
|  | ||||
| 		bool result; | ||||
| 		/// The replacement of a T rule, either D or S. | ||||
| 		std::string transitionReplacement; | ||||
| 		int flip; | ||||
| 	}; | ||||
|  | ||||
| 	struct InvalidTiles | ||||
| 	{ | ||||
| 		std::set<int3> foreignTiles, nativeTiles; | ||||
| 		bool centerPosValid; | ||||
|  | ||||
| 		InvalidTiles() : centerPosValid(false) { } | ||||
| 	}; | ||||
|  | ||||
| 	void updateTerrainTypes(); | ||||
| 	void invalidateTerrainViews(const int3 & centerPos); | ||||
| 	InvalidTiles getInvalidTiles(const int3 & centerPos) const; | ||||
|  | ||||
| 	void updateTerrainViews(); | ||||
| 	/// Validates the terrain view of the given position and with the given pattern. The first method wraps the | ||||
| 	/// second method to validate the terrain view with the given pattern in all four flip directions(horizontal, vertical). | ||||
| 	ValidationResult validateTerrainView(const int3 & pos, const std::vector<TerrainViewPattern> * pattern, int recDepth = 0) const; | ||||
| 	ValidationResult validateTerrainViewInner(const int3 & pos, const TerrainViewPattern & pattern, int recDepth = 0) const; | ||||
|  | ||||
| 	CTerrainSelection terrainSel; | ||||
| 	Terrain terType; | ||||
| 	CRandomGenerator * gen; | ||||
| 	std::set<int3> invalidatedTerViews; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CTerrainViewPatternUtils | ||||
| { | ||||
| public: | ||||
| 	static void printDebuggingInfoAboutTile(const CMap * map, int3 pos); | ||||
| }; | ||||
|  | ||||
| /// The CClearTerrainOperation clears+initializes the terrain. | ||||
| class CClearTerrainOperation : public CComposedOperation | ||||
| { | ||||
| public: | ||||
| 	CClearTerrainOperation(CMap * map, CRandomGenerator * gen); | ||||
|  | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| private: | ||||
|  | ||||
| }; | ||||
|  | ||||
| /// The CInsertObjectOperation class inserts an object to the map. | ||||
| class CInsertObjectOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CInsertObjectOperation(CMap * map, CGObjectInstance * obj); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| private: | ||||
| 	CGObjectInstance * obj; | ||||
| }; | ||||
|   | ||||
							
								
								
									
										674
									
								
								lib/mapping/CMapOperation.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										674
									
								
								lib/mapping/CMapOperation.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,674 @@ | ||||
| /* | ||||
|  * CMapOperation.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 "CMapOperation.h" | ||||
|  | ||||
| #include "../VCMI_Lib.h" | ||||
| #include "CMap.h" | ||||
| #include "MapEditUtils.h"  | ||||
|  | ||||
| CMapOperation::CMapOperation(CMap* map) : map(map) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| std::string CMapOperation::getLabel() const | ||||
| { | ||||
| 	return ""; | ||||
| } | ||||
|  | ||||
|  | ||||
| MapRect CMapOperation::extendTileAround(const int3& centerPos) const | ||||
| { | ||||
| 	return MapRect(int3(centerPos.x - 1, centerPos.y - 1, centerPos.z), 3, 3); | ||||
| } | ||||
|  | ||||
| MapRect CMapOperation::extendTileAroundSafely(const int3& centerPos) const | ||||
| { | ||||
| 	return extendTileAround(centerPos) & MapRect(int3(0, 0, centerPos.z), map->width, map->height); | ||||
| } | ||||
|  | ||||
| CComposedOperation::CComposedOperation(CMap* map) : CMapOperation(map) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CComposedOperation::execute() | ||||
| { | ||||
| 	for(auto & operation : operations) | ||||
| 	{ | ||||
| 		operation->execute(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CComposedOperation::undo() | ||||
| { | ||||
| 	//reverse order | ||||
| 	for(auto operation = operations.rbegin(); operation != operations.rend(); operation++) | ||||
| 	{ | ||||
| 		operation->get()->undo(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CComposedOperation::redo() | ||||
| { | ||||
| 	for(auto & operation : operations) | ||||
| 	{ | ||||
| 		operation->redo(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::string CComposedOperation::getLabel() const | ||||
| { | ||||
| 	std::string ret = "Composed operation: "; | ||||
| 	for(auto & operation : operations) | ||||
| 	{ | ||||
| 		ret.append(operation->getLabel() + ";"); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void CComposedOperation::addOperation(std::unique_ptr<CMapOperation>&& operation) | ||||
| { | ||||
| 	operations.push_back(std::move(operation)); | ||||
| } | ||||
|  | ||||
| CDrawTerrainOperation::CDrawTerrainOperation(CMap* map, const CTerrainSelection& terrainSel, Terrain terType, CRandomGenerator* gen) | ||||
| 	: CMapOperation(map), terrainSel(terrainSel), terType(terType), gen(gen) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CDrawTerrainOperation::execute() | ||||
| { | ||||
| 	for(const auto & pos : terrainSel.getSelectedItems()) | ||||
| 	{ | ||||
| 		auto & tile = map->getTile(pos); | ||||
| 		tile.terType = terType; | ||||
| 		invalidateTerrainViews(pos); | ||||
| 	} | ||||
|  | ||||
| 	updateTerrainTypes(); | ||||
| 	updateTerrainViews(); | ||||
| } | ||||
|  | ||||
| void CDrawTerrainOperation::undo() | ||||
| { | ||||
| 	//TODO | ||||
| } | ||||
|  | ||||
| void CDrawTerrainOperation::redo() | ||||
| { | ||||
| 	//TODO | ||||
| } | ||||
|  | ||||
| std::string CDrawTerrainOperation::getLabel() const | ||||
| { | ||||
| 	return "Draw Terrain"; | ||||
| } | ||||
|  | ||||
| void CDrawTerrainOperation::updateTerrainTypes() | ||||
| { | ||||
| 	auto positions = terrainSel.getSelectedItems(); | ||||
| 	while(!positions.empty()) | ||||
| 	{ | ||||
| 		const auto & centerPos = *(positions.begin()); | ||||
| 		auto centerTile = map->getTile(centerPos); | ||||
| 		//logGlobal->debug("Set terrain tile at pos '%s' to type '%s'", centerPos, centerTile.terType); | ||||
| 		auto tiles = getInvalidTiles(centerPos); | ||||
| 		auto updateTerrainType = [&](const int3& pos) | ||||
| 		{ | ||||
| 			map->getTile(pos).terType = centerTile.terType; | ||||
| 			positions.insert(pos); | ||||
| 			invalidateTerrainViews(pos); | ||||
| 			//logGlobal->debug("Set additional terrain tile at pos '%s' to type '%s'", pos, centerTile.terType); | ||||
| 		}; | ||||
|  | ||||
| 		// Fill foreign invalid tiles | ||||
| 		for(const auto & tile : tiles.foreignTiles) | ||||
| 		{ | ||||
| 			updateTerrainType(tile); | ||||
| 		} | ||||
|  | ||||
| 		tiles = getInvalidTiles(centerPos); | ||||
| 		if(tiles.nativeTiles.find(centerPos) != tiles.nativeTiles.end()) | ||||
| 		{ | ||||
| 			// Blow up | ||||
| 			auto rect = extendTileAroundSafely(centerPos); | ||||
| 			std::set<int3> suitableTiles; | ||||
| 			int invalidForeignTilesCnt = std::numeric_limits<int>::max(), invalidNativeTilesCnt = 0; | ||||
| 			bool centerPosValid = false; | ||||
| 			rect.forEach([&](const int3& posToTest) | ||||
| 				{ | ||||
| 					auto & terrainTile = map->getTile(posToTest); | ||||
| 					if(centerTile.terType != terrainTile.terType) | ||||
| 					{ | ||||
| 						auto formerTerType = terrainTile.terType; | ||||
| 						terrainTile.terType = centerTile.terType; | ||||
| 						auto testTile = getInvalidTiles(posToTest); | ||||
|  | ||||
| 						int nativeTilesCntNorm = testTile.nativeTiles.empty() ? std::numeric_limits<int>::max() : (int)testTile.nativeTiles.size(); | ||||
|  | ||||
| 						bool putSuitableTile = false; | ||||
| 						bool addToSuitableTiles = false; | ||||
| 						if(testTile.centerPosValid) | ||||
| 						{ | ||||
| 							if(!centerPosValid) | ||||
| 							{ | ||||
| 								centerPosValid = true; | ||||
| 								putSuitableTile = true; | ||||
| 							} | ||||
| 							else | ||||
| 							{ | ||||
| 								if(testTile.foreignTiles.size() < invalidForeignTilesCnt) | ||||
| 								{ | ||||
| 									putSuitableTile = true; | ||||
| 								} | ||||
| 								else | ||||
| 								{ | ||||
| 									addToSuitableTiles = true; | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 						else if(!centerPosValid) | ||||
| 						{ | ||||
| 							if((nativeTilesCntNorm > invalidNativeTilesCnt) || | ||||
| 								(nativeTilesCntNorm == invalidNativeTilesCnt && testTile.foreignTiles.size() < invalidForeignTilesCnt)) | ||||
| 							{ | ||||
| 								putSuitableTile = true; | ||||
| 							} | ||||
| 							else if(nativeTilesCntNorm == invalidNativeTilesCnt && testTile.foreignTiles.size() == invalidForeignTilesCnt) | ||||
| 							{ | ||||
| 								addToSuitableTiles = true; | ||||
| 							} | ||||
| 						} | ||||
|  | ||||
| 						if(putSuitableTile) | ||||
| 						{ | ||||
| 							//if(!suitableTiles.empty()) | ||||
| 							//{ | ||||
| 							//	logGlobal->debug("Clear suitables tiles."); | ||||
| 							//} | ||||
|  | ||||
| 							invalidNativeTilesCnt = nativeTilesCntNorm; | ||||
| 							invalidForeignTilesCnt = static_cast<int>(testTile.foreignTiles.size()); | ||||
| 							suitableTiles.clear(); | ||||
| 							addToSuitableTiles = true; | ||||
| 						} | ||||
|  | ||||
| 						if(addToSuitableTiles) | ||||
| 						{ | ||||
| 							suitableTiles.insert(posToTest); | ||||
| 						} | ||||
|  | ||||
| 						terrainTile.terType = formerTerType; | ||||
| 					} | ||||
| 				}); | ||||
|  | ||||
| 			if(suitableTiles.size() == 1) | ||||
| 			{ | ||||
| 				updateTerrainType(*suitableTiles.begin()); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				static const int3 directions[] = { int3(0, -1, 0), int3(-1, 0, 0), int3(0, 1, 0), int3(1, 0, 0), | ||||
| 											int3(-1, -1, 0), int3(-1, 1, 0), int3(1, 1, 0), int3(1, -1, 0) }; | ||||
| 				for(auto & direction : directions) | ||||
| 				{ | ||||
| 					auto it = suitableTiles.find(centerPos + direction); | ||||
| 					if (it != suitableTiles.end()) | ||||
| 					{ | ||||
| 						updateTerrainType(*it); | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			// add invalid native tiles which are not in the positions list | ||||
| 			for(const auto & nativeTile : tiles.nativeTiles) | ||||
| 			{ | ||||
| 				if (positions.find(nativeTile) == positions.end()) | ||||
| 				{ | ||||
| 					positions.insert(nativeTile); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			positions.erase(centerPos); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CDrawTerrainOperation::updateTerrainViews() | ||||
| { | ||||
| 	for(const auto & pos : invalidatedTerViews) | ||||
| 	{ | ||||
| 		const auto & patterns = VLC->terviewh->getTerrainViewPatterns(map->getTile(pos).terType); | ||||
|  | ||||
| 		// Detect a pattern which fits best | ||||
| 		int bestPattern = -1; | ||||
| 		ValidationResult valRslt(false); | ||||
| 		for(int k = 0; k < patterns.size(); ++k) | ||||
| 		{ | ||||
| 			const auto & pattern = patterns[k]; | ||||
| 			//(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) | ||||
| 			valRslt = validateTerrainView(pos, &pattern); | ||||
| 			if (valRslt.result) | ||||
| 			{ | ||||
| 				bestPattern = k; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		//assert(bestPattern != -1); | ||||
| 		if(bestPattern == -1) | ||||
| 		{ | ||||
| 			// This shouldn't be the case | ||||
| 			logGlobal->warn("No pattern detected at pos '%s'.", pos.toString()); | ||||
| 			CTerrainViewPatternUtils::printDebuggingInfoAboutTile(map, pos); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		// Get mapping | ||||
| 		const TerrainViewPattern& pattern = patterns[bestPattern][valRslt.flip]; | ||||
| 		std::pair<int, int> mapping; | ||||
| 		if(valRslt.transitionReplacement.empty()) | ||||
| 		{ | ||||
| 			mapping = pattern.mapping[0]; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			mapping = valRslt.transitionReplacement == TerrainViewPattern::RULE_DIRT ? pattern.mapping[0] : pattern.mapping[1]; | ||||
| 		} | ||||
|  | ||||
| 		// Set terrain view | ||||
| 		auto & tile = map->getTile(pos); | ||||
| 		if(!pattern.diffImages) | ||||
| 		{ | ||||
| 			tile.terView = gen->nextInt(mapping.first, mapping.second); | ||||
| 			tile.extTileFlags = valRslt.flip; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			const int framesPerRot = (mapping.second - mapping.first + 1) / pattern.rotationTypesCount; | ||||
| 			int flip = (pattern.rotationTypesCount == 2 && valRslt.flip == 2) ? 1 : valRslt.flip; | ||||
| 			int firstFrame = mapping.first + flip * framesPerRot; | ||||
| 			tile.terView = gen->nextInt(firstFrame, firstFrame + framesPerRot - 1); | ||||
| 			tile.extTileFlags = 0; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainView(const int3& pos, const std::vector<TerrainViewPattern>* pattern, int recDepth) const | ||||
| { | ||||
| 	for(int flip = 0; flip < 4; ++flip) | ||||
| 	{ | ||||
| 		auto valRslt = validateTerrainViewInner(pos, pattern->at(flip), recDepth); | ||||
| 		if(valRslt.result) | ||||
| 		{ | ||||
| 			valRslt.flip = flip; | ||||
| 			return valRslt; | ||||
| 		} | ||||
| 	} | ||||
| 	return ValidationResult(false); | ||||
| } | ||||
|  | ||||
| CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainViewInner(const int3& pos, const TerrainViewPattern& pattern, int recDepth) const | ||||
| { | ||||
| 	auto centerTerType = map->getTile(pos).terType; | ||||
| 	int totalPoints = 0; | ||||
| 	std::string transitionReplacement; | ||||
|  | ||||
| 	for(int i = 0; i < 9; ++i) | ||||
| 	{ | ||||
| 		// The center, middle cell can be skipped | ||||
| 		if(i == 4) | ||||
| 		{ | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		// Get terrain group of the current cell | ||||
| 		int cx = pos.x + (i % 3) - 1; | ||||
| 		int cy = pos.y + (i / 3) - 1; | ||||
| 		int3 currentPos(cx, cy, pos.z); | ||||
| 		bool isAlien = false; | ||||
| 		Terrain terType; | ||||
| 		if(!map->isInTheMap(currentPos)) | ||||
| 		{ | ||||
| 			// position is not in the map, so take the ter type from the neighbor tile | ||||
| 			bool widthTooHigh = currentPos.x >= map->width; | ||||
| 			bool widthTooLess = currentPos.x < 0; | ||||
| 			bool heightTooHigh = currentPos.y >= map->height; | ||||
| 			bool heightTooLess = currentPos.y < 0; | ||||
|  | ||||
| 			if((widthTooHigh && heightTooHigh) || (widthTooHigh && heightTooLess) || (widthTooLess && heightTooHigh) || (widthTooLess && heightTooLess)) | ||||
| 			{ | ||||
| 				terType = centerTerType; | ||||
| 			} | ||||
| 			else if(widthTooHigh) | ||||
| 			{ | ||||
| 				terType = map->getTile(int3(currentPos.x - 1, currentPos.y, currentPos.z)).terType; | ||||
| 			} | ||||
| 			else if(heightTooHigh) | ||||
| 			{ | ||||
| 				terType = map->getTile(int3(currentPos.x, currentPos.y - 1, currentPos.z)).terType; | ||||
| 			} | ||||
| 			else if(widthTooLess) | ||||
| 			{ | ||||
| 				terType = map->getTile(int3(currentPos.x + 1, currentPos.y, currentPos.z)).terType; | ||||
| 			} | ||||
| 			else if(heightTooLess) | ||||
| 			{ | ||||
| 				terType = map->getTile(int3(currentPos.x, currentPos.y + 1, currentPos.z)).terType; | ||||
| 			} | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			terType = map->getTile(currentPos).terType; | ||||
| 			if(terType != centerTerType && (terType.isPassable() || centerTerType.isPassable())) | ||||
| 			{ | ||||
| 				isAlien = true; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Validate all rules per cell | ||||
| 		int topPoints = -1; | ||||
| 		for(auto & elem : pattern.data[i]) | ||||
| 		{ | ||||
| 			TerrainViewPattern::WeightedRule rule = elem; | ||||
| 			if(!rule.isStandardRule()) | ||||
| 			{ | ||||
| 				if(recDepth == 0 && map->isInTheMap(currentPos)) | ||||
| 				{ | ||||
| 					if(terType == centerTerType) | ||||
| 					{ | ||||
| 						const auto & patternForRule = VLC->terviewh->getTerrainViewPatternsById(centerTerType, rule.name); | ||||
| 						if(auto p = patternForRule) | ||||
| 						{ | ||||
| 							auto rslt = validateTerrainView(currentPos, &(*p), 1); | ||||
| 							if(rslt.result) topPoints = std::max(topPoints, rule.points); | ||||
| 						} | ||||
| 					} | ||||
| 					continue; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					rule.setNative(); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			auto applyValidationRslt = [&](bool rslt) | ||||
| 			{ | ||||
| 				if(rslt) | ||||
| 				{ | ||||
| 					topPoints = std::max(topPoints, rule.points); | ||||
| 				} | ||||
| 			}; | ||||
|  | ||||
| 			// Validate cell with the ruleset of the pattern | ||||
| 			bool nativeTestOk, nativeTestStrongOk; | ||||
| 			nativeTestOk = nativeTestStrongOk = (rule.isNativeStrong() || rule.isNativeRule()) && !isAlien; | ||||
|  | ||||
| 			if(centerTerType == Terrain("dirt")) | ||||
| 			{ | ||||
| 				nativeTestOk = rule.isNativeRule() && !terType.isTransitionRequired(); | ||||
| 				bool sandTestOk = (rule.isSandRule() || rule.isTransition()) | ||||
| 					&& terType.isTransitionRequired(); | ||||
| 				applyValidationRslt(rule.isAnyRule() || sandTestOk || nativeTestOk || nativeTestStrongOk); | ||||
| 			} | ||||
| 			else if(centerTerType == Terrain("sand")) | ||||
| 			{ | ||||
| 				applyValidationRslt(true); | ||||
| 			} | ||||
| 			else if(centerTerType.isTransitionRequired()) //water, rock and some special terrains require sand transition | ||||
| 			{ | ||||
| 				bool sandTestOk = (rule.isSandRule() || rule.isTransition()) | ||||
| 					&& isAlien; | ||||
| 				applyValidationRslt(rule.isAnyRule() || sandTestOk || nativeTestOk); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				bool dirtTestOk = (rule.isDirtRule() || rule.isTransition()) | ||||
| 					&& isAlien && !terType.isTransitionRequired(); | ||||
| 				bool sandTestOk = (rule.isSandRule() || rule.isTransition()) | ||||
| 					&& terType.isTransitionRequired(); | ||||
|  | ||||
| 				if(transitionReplacement.empty() && rule.isTransition() | ||||
| 					&& (dirtTestOk || sandTestOk)) | ||||
| 				{ | ||||
| 					transitionReplacement = dirtTestOk ? TerrainViewPattern::RULE_DIRT : TerrainViewPattern::RULE_SAND; | ||||
| 				} | ||||
| 				if(rule.isTransition()) | ||||
| 				{ | ||||
| 					applyValidationRslt((dirtTestOk && transitionReplacement != TerrainViewPattern::RULE_SAND) || | ||||
| 						(sandTestOk && transitionReplacement != TerrainViewPattern::RULE_DIRT)); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					applyValidationRslt(rule.isAnyRule() || dirtTestOk || sandTestOk || nativeTestOk); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if(topPoints == -1) | ||||
| 		{ | ||||
| 			return ValidationResult(false); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			totalPoints += topPoints; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(totalPoints >= pattern.minPoints && totalPoints <= pattern.maxPoints) | ||||
| 	{ | ||||
| 		return ValidationResult(true, transitionReplacement); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return ValidationResult(false); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CDrawTerrainOperation::invalidateTerrainViews(const int3& centerPos) | ||||
| { | ||||
| 	auto rect = extendTileAroundSafely(centerPos); | ||||
| 	rect.forEach([&](const int3& pos) | ||||
| 		{ | ||||
| 			invalidatedTerViews.insert(pos); | ||||
| 		}); | ||||
| } | ||||
|  | ||||
| CDrawTerrainOperation::InvalidTiles CDrawTerrainOperation::getInvalidTiles(const int3& centerPos) const | ||||
| { | ||||
| 	//TODO: this is very expensive function for RMG, needs optimization | ||||
| 	InvalidTiles tiles; | ||||
| 	auto centerTerType = map->getTile(centerPos).terType; | ||||
| 	auto rect = extendTileAround(centerPos); | ||||
| 	rect.forEach([&](const int3& pos) | ||||
| 		{ | ||||
| 			if(map->isInTheMap(pos)) | ||||
| 			{ | ||||
| 				auto ptrConfig = VLC->terviewh; | ||||
| 				auto terType = map->getTile(pos).terType; | ||||
| 				auto valid = validateTerrainView(pos, ptrConfig->getTerrainTypePatternById("n1")).result; | ||||
|  | ||||
| 				// Special validity check for rock & water | ||||
| 				if(valid && (terType.isWater() || !terType.isPassable())) | ||||
| 				{ | ||||
| 					static const std::string patternIds[] = { "s1", "s2" }; | ||||
| 					for(auto & patternId : patternIds) | ||||
| 					{ | ||||
| 						valid = !validateTerrainView(pos, ptrConfig->getTerrainTypePatternById(patternId)).result; | ||||
| 						if(!valid) break; | ||||
| 					} | ||||
| 				} | ||||
| 				// Additional validity check for non rock OR water | ||||
| 				else if(!valid && (terType.isLand() && terType.isPassable())) | ||||
| 				{ | ||||
| 					static const std::string patternIds[] = { "n2", "n3" }; | ||||
| 					for (auto & patternId : patternIds) | ||||
| 					{ | ||||
| 						valid = validateTerrainView(pos, ptrConfig->getTerrainTypePatternById(patternId)).result; | ||||
| 						if(valid) break; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if(!valid) | ||||
| 				{ | ||||
| 					if(terType == centerTerType) tiles.nativeTiles.insert(pos); | ||||
| 					else tiles.foreignTiles.insert(pos); | ||||
| 				} | ||||
| 				else if(centerPos == pos) | ||||
| 				{ | ||||
| 					tiles.centerPosValid = true; | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	return tiles; | ||||
| } | ||||
|  | ||||
| CDrawTerrainOperation::ValidationResult::ValidationResult(bool result, const std::string& transitionReplacement) | ||||
| 	: result(result), transitionReplacement(transitionReplacement), flip(0) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| CClearTerrainOperation::CClearTerrainOperation(CMap* map, CRandomGenerator* gen) : CComposedOperation(map) | ||||
| { | ||||
| 	CTerrainSelection terrainSel(map); | ||||
| 	terrainSel.selectRange(MapRect(int3(0, 0, 0), map->width, map->height)); | ||||
| 	addOperation(make_unique<CDrawTerrainOperation>(map, terrainSel, Terrain("water"), gen)); | ||||
| 	if(map->twoLevel) | ||||
| 	{ | ||||
| 		terrainSel.clearSelection(); | ||||
| 		terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height)); | ||||
| 		addOperation(make_unique<CDrawTerrainOperation>(map, terrainSel, Terrain("rock"), gen)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| std::string CClearTerrainOperation::getLabel() const | ||||
| { | ||||
| 	return "Clear Terrain"; | ||||
| } | ||||
|  | ||||
| CInsertObjectOperation::CInsertObjectOperation(CMap* map, CGObjectInstance* obj) | ||||
| 	: CMapOperation(map), obj(obj) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CInsertObjectOperation::execute() | ||||
| { | ||||
| 	obj->id = ObjectInstanceID(map->objects.size()); | ||||
|  | ||||
| 	do | ||||
| 	{ | ||||
| 		map->setUniqueInstanceName(obj); | ||||
| 	} while(vstd::contains(map->instanceNames, obj->instanceName)); | ||||
|  | ||||
| 	map->addNewObject(obj); | ||||
| } | ||||
|  | ||||
| void CInsertObjectOperation::undo() | ||||
| { | ||||
| 	map->removeObject(obj); | ||||
| } | ||||
|  | ||||
| void CInsertObjectOperation::redo() | ||||
| { | ||||
| 	execute(); | ||||
| } | ||||
|  | ||||
| std::string CInsertObjectOperation::getLabel() const | ||||
| { | ||||
| 	return "Insert Object"; | ||||
| } | ||||
|  | ||||
| CMoveObjectOperation::CMoveObjectOperation(CMap* map, CGObjectInstance* obj, const int3& targetPosition) | ||||
| 	: CMapOperation(map), | ||||
| 	obj(obj), | ||||
| 	initialPos(obj->pos), | ||||
| 	targetPos(targetPosition) | ||||
| { | ||||
| } | ||||
|  | ||||
| void CMoveObjectOperation::execute() | ||||
| { | ||||
| 	map->moveObject(obj, targetPos); | ||||
| } | ||||
|  | ||||
| void CMoveObjectOperation::undo() | ||||
| { | ||||
| 	map->moveObject(obj, initialPos); | ||||
| } | ||||
|  | ||||
| void CMoveObjectOperation::redo() | ||||
| { | ||||
| 	execute(); | ||||
| } | ||||
|  | ||||
| std::string CMoveObjectOperation::getLabel() const | ||||
| { | ||||
| 	return "Move Object"; | ||||
| } | ||||
|  | ||||
| CRemoveObjectOperation::CRemoveObjectOperation(CMap* map, CGObjectInstance* obj) | ||||
| 	: CMapOperation(map), obj(obj) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| CRemoveObjectOperation::~CRemoveObjectOperation() | ||||
| { | ||||
| 	//when operation is destroyed and wasn't undone, the object is lost forever | ||||
|  | ||||
| 	if(!obj) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	//do not destroy an object that belongs to map | ||||
| 	if(!vstd::contains(map->instanceNames, obj->instanceName)) | ||||
| 	{ | ||||
| 		delete obj; | ||||
| 		obj = nullptr; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CRemoveObjectOperation::execute() | ||||
| { | ||||
| 	map->removeObject(obj); | ||||
| } | ||||
|  | ||||
| void CRemoveObjectOperation::undo() | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		//set new id, but do not rename object | ||||
| 		obj->id = ObjectInstanceID((si32)map->objects.size()); | ||||
| 		map->addNewObject(obj); | ||||
| 	} | ||||
| 	catch(const std::exception& e) | ||||
| 	{ | ||||
| 		logGlobal->error(e.what()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CRemoveObjectOperation::redo() | ||||
| { | ||||
| 	execute(); | ||||
| } | ||||
|  | ||||
| std::string CRemoveObjectOperation::getLabel() const | ||||
| { | ||||
| 	return "Remove Object"; | ||||
| } | ||||
							
								
								
									
										161
									
								
								lib/mapping/CMapOperation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								lib/mapping/CMapOperation.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| /* | ||||
|  * CMapOperation.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 "../int3.h" | ||||
| #include "MapEditUtils.h" | ||||
|  | ||||
| class CGObjectInstance; | ||||
| class CMap; | ||||
| class CRandomGenerator; | ||||
|  | ||||
| /// The abstract base class CMapOperation defines an operation that can be executed, undone and redone. | ||||
| class DLL_LINKAGE CMapOperation : public boost::noncopyable | ||||
| { | ||||
| public: | ||||
| 	explicit CMapOperation(CMap * map); | ||||
| 	virtual ~CMapOperation() = default; | ||||
|  | ||||
| 	virtual void execute() = 0; | ||||
| 	virtual void undo() = 0; | ||||
| 	virtual void redo() = 0; | ||||
| 	virtual std::string getLabel() const = 0; /// Returns a operation display name. | ||||
|  | ||||
| 	static const int FLIP_PATTERN_HORIZONTAL = 1; | ||||
| 	static const int FLIP_PATTERN_VERTICAL = 2; | ||||
| 	static const int FLIP_PATTERN_BOTH = 3; | ||||
|  | ||||
| protected: | ||||
| 	MapRect extendTileAround(const int3 & centerPos) const; | ||||
| 	MapRect extendTileAroundSafely(const int3 & centerPos) const; /// doesn't exceed map size | ||||
|  | ||||
| 	CMap * map; | ||||
| }; | ||||
|  | ||||
| /// The CComposedOperation is an operation which consists of several operations. | ||||
| class DLL_LINKAGE CComposedOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CComposedOperation(CMap * map); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| 	void addOperation(std::unique_ptr<CMapOperation> && operation); | ||||
|  | ||||
| private: | ||||
| 	std::list<std::unique_ptr<CMapOperation> > operations; | ||||
| }; | ||||
|  | ||||
| /// The CDrawTerrainOperation class draws a terrain area on the map. | ||||
| class CDrawTerrainOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, Terrain terType, CRandomGenerator * gen); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| private: | ||||
| 	struct ValidationResult | ||||
| 	{ | ||||
| 		ValidationResult(bool result, const std::string & transitionReplacement = ""); | ||||
|  | ||||
| 		bool result; | ||||
| 		/// The replacement of a T rule, either D or S. | ||||
| 		std::string transitionReplacement; | ||||
| 		int flip; | ||||
| 	}; | ||||
|  | ||||
| 	struct InvalidTiles | ||||
| 	{ | ||||
| 		std::set<int3> foreignTiles, nativeTiles; | ||||
| 		bool centerPosValid; | ||||
|  | ||||
| 		InvalidTiles() : centerPosValid(false) { } | ||||
| 	}; | ||||
|  | ||||
| 	void updateTerrainTypes(); | ||||
| 	void invalidateTerrainViews(const int3 & centerPos); | ||||
| 	InvalidTiles getInvalidTiles(const int3 & centerPos) const; | ||||
|  | ||||
| 	void updateTerrainViews(); | ||||
| 	/// Validates the terrain view of the given position and with the given pattern. The first method wraps the | ||||
| 	/// second method to validate the terrain view with the given pattern in all four flip directions(horizontal, vertical). | ||||
| 	ValidationResult validateTerrainView(const int3 & pos, const std::vector<TerrainViewPattern> * pattern, int recDepth = 0) const; | ||||
| 	ValidationResult validateTerrainViewInner(const int3 & pos, const TerrainViewPattern & pattern, int recDepth = 0) const; | ||||
|  | ||||
| 	CTerrainSelection terrainSel; | ||||
| 	Terrain terType; | ||||
| 	CRandomGenerator* gen; | ||||
| 	std::set<int3> invalidatedTerViews; | ||||
| }; | ||||
|  | ||||
| /// The CClearTerrainOperation clears+initializes the terrain. | ||||
| class CClearTerrainOperation : public CComposedOperation | ||||
| { | ||||
| public: | ||||
| 	CClearTerrainOperation(CMap * map, CRandomGenerator * gen); | ||||
|  | ||||
| 	std::string getLabel() const override; | ||||
| }; | ||||
|  | ||||
| /// The CInsertObjectOperation class inserts an object to the map. | ||||
| class CInsertObjectOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CInsertObjectOperation(CMap * map, CGObjectInstance * obj); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| private: | ||||
| 	CGObjectInstance * obj; | ||||
| }; | ||||
|  | ||||
| /// The CMoveObjectOperation class moves object to another position | ||||
| class CMoveObjectOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CMoveObjectOperation(CMap * map, CGObjectInstance * obj, const int3 & targetPosition); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| private: | ||||
| 	CGObjectInstance * obj; | ||||
| 	int3 initialPos; | ||||
| 	int3 targetPos; | ||||
| }; | ||||
|  | ||||
| /// The CRemoveObjectOperation class removes object from the map | ||||
| class CRemoveObjectOperation : public CMapOperation | ||||
| { | ||||
| public: | ||||
| 	CRemoveObjectOperation(CMap* map, CGObjectInstance * obj); | ||||
| 	~CRemoveObjectOperation(); | ||||
|  | ||||
| 	void execute() override; | ||||
| 	void undo() override; | ||||
| 	void redo() override; | ||||
| 	std::string getLabel() const override; | ||||
|  | ||||
| private: | ||||
| 	CGObjectInstance* obj; | ||||
| }; | ||||
							
								
								
									
										371
									
								
								lib/mapping/MapEditUtils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								lib/mapping/MapEditUtils.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,371 @@ | ||||
| /* | ||||
|  * MapEditUtils.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 "MapEditUtils.h" | ||||
|  | ||||
| #include "../filesystem/Filesystem.h" | ||||
| #include "../JsonNode.h" | ||||
| #include "CMap.h" | ||||
| #include "CMapOperation.h" | ||||
|  | ||||
| MapRect::MapRect() : x(0), y(0), z(0), width(0), height(0) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| MapRect::MapRect(int3 pos, si32 width, si32 height) : x(pos.x), y(pos.y), z(pos.z), width(width), height(height) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| MapRect MapRect::operator&(const MapRect& rect) const | ||||
| { | ||||
| 	bool intersect = right() > rect.left() && rect.right() > left() | ||||
| 		&& bottom() > rect.top() && rect.bottom() > top() | ||||
| 		&& z == rect.z; | ||||
| 	if (intersect) | ||||
| 	{ | ||||
| 		MapRect ret; | ||||
| 		ret.x = std::max(left(), rect.left()); | ||||
| 		ret.y = std::max(top(), rect.top()); | ||||
| 		ret.z = rect.z; | ||||
| 		ret.width = std::min(right(), rect.right()) - ret.x; | ||||
| 		ret.height = std::min(bottom(), rect.bottom()) - ret.y; | ||||
| 		return ret; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return MapRect(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| si32 MapRect::left() const | ||||
| { | ||||
| 	return x; | ||||
| } | ||||
|  | ||||
| si32 MapRect::right() const | ||||
| { | ||||
| 	return x + width; | ||||
| } | ||||
|  | ||||
| si32 MapRect::top() const | ||||
| { | ||||
| 	return y; | ||||
| } | ||||
|  | ||||
| si32 MapRect::bottom() const | ||||
| { | ||||
| 	return y + height; | ||||
| } | ||||
|  | ||||
| int3 MapRect::topLeft() const | ||||
| { | ||||
| 	return int3(x, y, z); | ||||
| } | ||||
|  | ||||
| int3 MapRect::topRight() const | ||||
| { | ||||
| 	return int3(right(), y, z); | ||||
| } | ||||
|  | ||||
| int3 MapRect::bottomLeft() const | ||||
| { | ||||
| 	return int3(x, bottom(), z); | ||||
| } | ||||
|  | ||||
| int3 MapRect::bottomRight() const | ||||
| { | ||||
| 	return int3(right(), bottom(), z); | ||||
| } | ||||
|  | ||||
| CTerrainSelection::CTerrainSelection(CMap * map) : CMapSelection(map) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| void CTerrainSelection::selectRange(const MapRect& rect) | ||||
| { | ||||
| 	rect.forEach([this](const int3 pos) | ||||
| 	{ | ||||
| 		this->select(pos); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| void CTerrainSelection::deselectRange(const MapRect& rect) | ||||
| { | ||||
| 	rect.forEach([this](const int3 pos) | ||||
| 	{ | ||||
| 		this->deselect(pos); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| void CTerrainSelection::setSelection(const std::vector<int3> & vec) | ||||
| { | ||||
| 	for (const auto & pos : vec) | ||||
| 		this->select(pos); | ||||
| } | ||||
|  | ||||
| void CTerrainSelection::selectAll() | ||||
| { | ||||
| 	selectRange(MapRect(int3(0, 0, 0), getMap()->width, getMap()->height)); | ||||
| 	selectRange(MapRect(int3(0, 0, 1), getMap()->width, getMap()->height)); | ||||
| } | ||||
|  | ||||
| void CTerrainSelection::clearSelection() | ||||
| { | ||||
| 	deselectRange(MapRect(int3(0, 0, 0), getMap()->width, getMap()->height)); | ||||
| 	deselectRange(MapRect(int3(0, 0, 1), getMap()->width, getMap()->height)); | ||||
| } | ||||
|  | ||||
| CObjectSelection::CObjectSelection(CMap * map) : CMapSelection(map) | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| const std::string TerrainViewPattern::FLIP_MODE_DIFF_IMAGES = "D"; | ||||
|  | ||||
| const std::string TerrainViewPattern::RULE_DIRT = "D"; | ||||
| const std::string TerrainViewPattern::RULE_SAND = "S"; | ||||
| const std::string TerrainViewPattern::RULE_TRANSITION = "T"; | ||||
| const std::string TerrainViewPattern::RULE_NATIVE = "N"; | ||||
| const std::string TerrainViewPattern::RULE_NATIVE_STRONG = "N!"; | ||||
| const std::string TerrainViewPattern::RULE_ANY = "?"; | ||||
|  | ||||
| TerrainViewPattern::TerrainViewPattern() : diffImages(false), rotationTypesCount(0), minPoints(0) | ||||
| { | ||||
| 	maxPoints = std::numeric_limits<int>::max(); | ||||
| } | ||||
|  | ||||
| TerrainViewPattern::WeightedRule::WeightedRule(std::string& Name) : points(0), name(Name) | ||||
| { | ||||
| 	standardRule = (TerrainViewPattern::RULE_ANY == Name || TerrainViewPattern::RULE_DIRT == Name | ||||
| 		|| TerrainViewPattern::RULE_NATIVE == Name || TerrainViewPattern::RULE_SAND == Name | ||||
| 		|| TerrainViewPattern::RULE_TRANSITION == Name || TerrainViewPattern::RULE_NATIVE_STRONG == Name); | ||||
| 	anyRule = (Name == TerrainViewPattern::RULE_ANY); | ||||
| 	dirtRule = (Name == TerrainViewPattern::RULE_DIRT); | ||||
| 	sandRule = (Name == TerrainViewPattern::RULE_SAND); | ||||
| 	transitionRule = (Name == TerrainViewPattern::RULE_TRANSITION); | ||||
| 	nativeStrongRule = (Name == TerrainViewPattern::RULE_NATIVE_STRONG); | ||||
| 	nativeRule = (Name == TerrainViewPattern::RULE_NATIVE); | ||||
| } | ||||
|  | ||||
| void TerrainViewPattern::WeightedRule::setNative() | ||||
| { | ||||
| 	nativeRule = true; | ||||
| 	standardRule = true; | ||||
| 	//TODO: would look better as a bitfield | ||||
| 	dirtRule = sandRule = transitionRule = nativeStrongRule = anyRule = false; //no idea what they mean, but look mutually exclusive | ||||
| } | ||||
|  | ||||
| CTerrainViewPatternConfig::CTerrainViewPatternConfig() | ||||
| { | ||||
| 	const JsonNode config(ResourceID("config/terrainViewPatterns.json")); | ||||
| 	static const std::string patternTypes[] = { "terrainView", "terrainType" }; | ||||
| 	for (int i = 0; i < ARRAY_COUNT(patternTypes); ++i) | ||||
| 	{ | ||||
| 		const auto& patternsVec = config[patternTypes[i]].Vector(); | ||||
| 		for (const auto& ptrnNode : patternsVec) | ||||
| 		{ | ||||
| 			TerrainViewPattern pattern; | ||||
|  | ||||
| 			// Read pattern data | ||||
| 			const JsonVector& data = ptrnNode["data"].Vector(); | ||||
| 			assert(data.size() == 9); | ||||
| 			for (int j = 0; j < data.size(); ++j) | ||||
| 			{ | ||||
| 				std::string cell = data[j].String(); | ||||
| 				boost::algorithm::erase_all(cell, " "); | ||||
| 				std::vector<std::string> rules; | ||||
| 				boost::split(rules, cell, boost::is_any_of(",")); | ||||
| 				for (const std::string & ruleStr : rules) | ||||
| 				{ | ||||
| 					std::vector<std::string> ruleParts; | ||||
| 					boost::split(ruleParts, ruleStr, boost::is_any_of("-")); | ||||
| 					TerrainViewPattern::WeightedRule rule(ruleParts[0]); | ||||
| 					assert(!rule.name.empty()); | ||||
| 					if (ruleParts.size() > 1) | ||||
| 					{ | ||||
| 						rule.points = boost::lexical_cast<int>(ruleParts[1]); | ||||
| 					} | ||||
| 					pattern.data[j].push_back(rule); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Read various properties | ||||
| 			pattern.id = ptrnNode["id"].String(); | ||||
| 			assert(!pattern.id.empty()); | ||||
| 			pattern.minPoints = static_cast<int>(ptrnNode["minPoints"].Float()); | ||||
| 			pattern.maxPoints = static_cast<int>(ptrnNode["maxPoints"].Float()); | ||||
| 			if (pattern.maxPoints == 0) | ||||
| 				pattern.maxPoints = std::numeric_limits<int>::max(); | ||||
|  | ||||
| 			// Read mapping | ||||
| 			if (i == 0) | ||||
| 			{ | ||||
| 				const auto & mappingStruct = ptrnNode["mapping"].Struct(); | ||||
| 				for (const auto & mappingPair : mappingStruct) | ||||
| 				{ | ||||
| 					TerrainViewPattern terGroupPattern = pattern; | ||||
| 					auto mappingStr = mappingPair.second.String(); | ||||
| 					boost::algorithm::erase_all(mappingStr, " "); | ||||
| 					auto colonIndex = mappingStr.find_first_of(":"); | ||||
| 					const auto & flipMode = mappingStr.substr(0, colonIndex); | ||||
| 					terGroupPattern.diffImages = TerrainViewPattern::FLIP_MODE_DIFF_IMAGES == &(flipMode[flipMode.length() - 1]); | ||||
| 					if (terGroupPattern.diffImages) | ||||
| 					{ | ||||
| 						terGroupPattern.rotationTypesCount = boost::lexical_cast<int>(flipMode.substr(0, flipMode.length() - 1)); | ||||
| 						assert(terGroupPattern.rotationTypesCount == 2 || terGroupPattern.rotationTypesCount == 4); | ||||
| 					} | ||||
| 					mappingStr = mappingStr.substr(colonIndex + 1); | ||||
| 					std::vector<std::string> mappings; | ||||
| 					boost::split(mappings, mappingStr, boost::is_any_of(",")); | ||||
| 					for (const std::string & mapping : mappings) | ||||
| 					{ | ||||
| 						std::vector<std::string> range; | ||||
| 						boost::split(range, mapping, boost::is_any_of("-")); | ||||
| 						terGroupPattern.mapping.push_back(std::make_pair(boost::lexical_cast<int>(range[0]), | ||||
| 							boost::lexical_cast<int>(range.size() > 1 ? range[1] : range[0]))); | ||||
| 					} | ||||
|  | ||||
| 					// Add pattern to the patterns map | ||||
| 					std::vector<TerrainViewPattern> terrainViewPatternFlips{terGroupPattern}; | ||||
|  | ||||
| 					for (int i = 1; i < 4; ++i) | ||||
| 					{ | ||||
| 						//auto p = terGroupPattern; | ||||
| 						flipPattern(terGroupPattern, i); //FIXME: we flip in place - doesn't make much sense now, but used to work | ||||
| 						terrainViewPatternFlips.push_back(terGroupPattern); | ||||
| 					} | ||||
|  | ||||
| 					terrainViewPatterns[mappingPair.first].push_back(terrainViewPatternFlips); | ||||
| 				} | ||||
| 			} | ||||
| 			else if (i == 1) | ||||
| 			{ | ||||
| 				terrainTypePatterns[pattern.id].push_back(pattern); | ||||
| 				for (int i = 1; i < 4; ++i) | ||||
| 				{ | ||||
| 					//auto p = pattern; | ||||
| 					flipPattern(pattern, i); ///FIXME: we flip in place - doesn't make much sense now | ||||
| 					terrainTypePatterns[pattern.id].push_back(pattern); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CTerrainViewPatternConfig::~CTerrainViewPatternConfig() | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| const std::vector<CTerrainViewPatternConfig::TVPVector> & CTerrainViewPatternConfig::getTerrainViewPatterns(const Terrain & terrain) const | ||||
| { | ||||
| 	auto iter = terrainViewPatterns.find(Terrain::Manager::getInfo(terrain).terrainViewPatterns); | ||||
| 	if (iter == terrainViewPatterns.end()) | ||||
| 		return terrainViewPatterns.at("normal"); | ||||
| 	return iter->second; | ||||
| } | ||||
|  | ||||
| boost::optional<const TerrainViewPattern &> CTerrainViewPatternConfig::getTerrainViewPatternById(std::string patternId, const std::string & id) const | ||||
| { | ||||
| 	auto iter = terrainViewPatterns.find(patternId); | ||||
| 	const std::vector<TVPVector> & groupPatterns = (iter == terrainViewPatterns.end()) ? terrainViewPatterns.at("normal") : iter->second; | ||||
|  | ||||
| 	for (const TVPVector & patternFlips : groupPatterns) | ||||
| 	{ | ||||
| 		const TerrainViewPattern & pattern = patternFlips.front(); | ||||
|  | ||||
| 		if (id == pattern.id) | ||||
| 		{ | ||||
| 			return boost::optional<const TerrainViewPattern&>(pattern); | ||||
| 		} | ||||
| 	} | ||||
| 	return boost::optional<const TerrainViewPattern&>(); | ||||
| } | ||||
|  | ||||
| boost::optional<const CTerrainViewPatternConfig::TVPVector &> CTerrainViewPatternConfig::getTerrainViewPatternsById(const Terrain & terrain, const std::string & id) const | ||||
| { | ||||
| 	const std::vector<TVPVector> & groupPatterns = getTerrainViewPatterns(terrain); | ||||
| 	for (const TVPVector & patternFlips : groupPatterns) | ||||
| 	{ | ||||
| 		const TerrainViewPattern & pattern = patternFlips.front(); | ||||
| 		if (id == pattern.id) | ||||
| 		{ | ||||
| 			return boost::optional<const TVPVector&>(patternFlips); | ||||
| 		} | ||||
| 	} | ||||
| 	return boost::optional<const TVPVector&>(); | ||||
| } | ||||
|  | ||||
|  | ||||
| const CTerrainViewPatternConfig::TVPVector* CTerrainViewPatternConfig::getTerrainTypePatternById(const std::string& id) const | ||||
| { | ||||
| 	auto it = terrainTypePatterns.find(id); | ||||
| 	assert(it != terrainTypePatterns.end()); | ||||
| 	return &(it->second); | ||||
| } | ||||
|  | ||||
| void CTerrainViewPatternConfig::flipPattern(TerrainViewPattern & pattern, int flip) const | ||||
| { | ||||
| 	//flip in place to avoid expensive constructor. Seriously. | ||||
|  | ||||
| 	if (flip == 0) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	//always flip horizontal | ||||
| 	for (int i = 0; i < 3; ++i) | ||||
| 	{ | ||||
| 		int y = i * 3; | ||||
| 		std::swap(pattern.data[y], pattern.data[y + 2]); | ||||
| 	} | ||||
| 	//flip vertical only at 2nd step | ||||
| 	if (flip == CMapOperation::FLIP_PATTERN_VERTICAL) | ||||
| 	{ | ||||
| 		for (int i = 0; i < 3; ++i) | ||||
| 		{ | ||||
| 			std::swap(pattern.data[i], pattern.data[6 + i]); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| void CTerrainViewPatternUtils::printDebuggingInfoAboutTile(const CMap * map, int3 pos) | ||||
| { | ||||
| 	logGlobal->debug("Printing detailed info about nearby map tiles of pos '%s'", pos.toString()); | ||||
| 	for (int y = pos.y - 2; y <= pos.y + 2; ++y) | ||||
| 	{ | ||||
| 		std::string line; | ||||
| 		const int PADDED_LENGTH = 10; | ||||
| 		for (int x = pos.x - 2; x <= pos.x + 2; ++x) | ||||
| 		{ | ||||
| 			auto debugPos = int3(x, y, pos.z); | ||||
| 			if (map->isInTheMap(debugPos)) | ||||
| 			{ | ||||
| 				auto debugTile = map->getTile(debugPos); | ||||
|  | ||||
| 				std::string terType = static_cast<std::string>(debugTile.terType).substr(0, 6); | ||||
| 				line += terType; | ||||
| 				line.insert(line.end(), PADDED_LENGTH - terType.size(), ' '); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				line += "X"; | ||||
| 				line.insert(line.end(), PADDED_LENGTH - 1, ' '); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		logGlobal->debug(line); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										233
									
								
								lib/mapping/MapEditUtils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								lib/mapping/MapEditUtils.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,233 @@ | ||||
| /* | ||||
|  * MapEditUtils.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 "../int3.h" | ||||
| #include "../CRandomGenerator.h" | ||||
| #include "Terrain.h" | ||||
|  | ||||
| class CGObjectInstance; | ||||
| class CMap; | ||||
|  | ||||
| /// Represents a map rectangle. | ||||
| struct DLL_LINKAGE MapRect | ||||
| { | ||||
| 	MapRect(); | ||||
| 	MapRect(int3 pos, si32 width, si32 height); | ||||
| 	si32 x, y, z; | ||||
| 	si32 width, height; | ||||
|  | ||||
| 	si32 left() const; | ||||
| 	si32 right() const; | ||||
| 	si32 top() const; | ||||
| 	si32 bottom() const; | ||||
|  | ||||
| 	int3 topLeft() const; /// Top left corner of this rect. | ||||
| 	int3 topRight() const; /// Top right corner of this rect. | ||||
| 	int3 bottomLeft() const; /// Bottom left corner of this rect. | ||||
| 	int3 bottomRight() const; /// Bottom right corner of this rect. | ||||
|  | ||||
| 	/// Returns a MapRect of the intersection of this rectangle and the given one. | ||||
| 	MapRect operator&(const MapRect& rect) const; | ||||
|  | ||||
| 	template<typename Func> | ||||
| 	void forEach(Func f) const | ||||
| 	{ | ||||
| 		for(int j = y; j < bottom(); ++j) | ||||
| 		{ | ||||
| 			for(int i = x; i < right(); ++i) | ||||
| 			{ | ||||
| 				f(int3(i, j, z)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| /// Generic selection class to select any type | ||||
| template<typename T> | ||||
| class DLL_LINKAGE CMapSelection | ||||
| { | ||||
| public: | ||||
| 	explicit CMapSelection(CMap* map) : map(map) { } | ||||
| 	virtual ~CMapSelection() = default; | ||||
| 	void select(const T & item) | ||||
| 	{ | ||||
| 		selectedItems.insert(item); | ||||
| 	} | ||||
| 	void deselect(const T & item) | ||||
| 	{ | ||||
| 		selectedItems.erase(item); | ||||
| 	} | ||||
| 	std::set<T> getSelectedItems() | ||||
| 	{ | ||||
| 		return selectedItems; | ||||
| 	} | ||||
| 	CMap* getMap() { return map; } | ||||
| 	virtual void selectRange(const MapRect & rect) { } | ||||
| 	virtual void deselectRange(const MapRect & rect) { } | ||||
| 	virtual void selectAll() { } | ||||
| 	virtual void clearSelection() { } | ||||
|  | ||||
| private: | ||||
| 	std::set<T> selectedItems; | ||||
| 	CMap* map; | ||||
| }; | ||||
|  | ||||
| /// Selection class to select terrain. | ||||
| class DLL_LINKAGE CTerrainSelection : public CMapSelection<int3> | ||||
| { | ||||
| public: | ||||
| 	explicit CTerrainSelection(CMap * map); | ||||
| 	void selectRange(const MapRect & rect) override; | ||||
| 	void deselectRange(const MapRect & rect) override; | ||||
| 	void selectAll() override; | ||||
| 	void clearSelection() override; | ||||
| 	void setSelection(const std::vector<int3> & vec); | ||||
| }; | ||||
|  | ||||
| /// Selection class to select objects. | ||||
| class DLL_LINKAGE CObjectSelection : public CMapSelection<CGObjectInstance *> | ||||
| { | ||||
| public: | ||||
| 	explicit CObjectSelection(CMap * map); | ||||
| }; | ||||
|  | ||||
| /// The terrain view pattern describes a specific composition of terrain tiles | ||||
| /// in a 3x3 matrix and notes which terrain view frame numbers can be used. | ||||
| struct DLL_LINKAGE TerrainViewPattern | ||||
| { | ||||
| 	struct WeightedRule | ||||
| 	{ | ||||
| 		WeightedRule(std::string& Name); | ||||
| 		/// Gets true if this rule is a standard rule which means that it has a value of one of the RULE_* constants. | ||||
| 		inline bool isStandardRule() const | ||||
| 		{ | ||||
| 			return standardRule; | ||||
| 		} | ||||
| 		inline bool isAnyRule() const | ||||
| 		{ | ||||
| 			return anyRule; | ||||
| 		} | ||||
| 		inline bool isDirtRule() const | ||||
| 		{ | ||||
| 			return dirtRule; | ||||
| 		} | ||||
| 		inline bool isSandRule() const | ||||
| 		{ | ||||
| 			return sandRule; | ||||
| 		} | ||||
| 		inline bool isTransition() const | ||||
| 		{ | ||||
| 			return transitionRule; | ||||
| 		} | ||||
| 		inline bool isNativeStrong() const | ||||
| 		{ | ||||
| 			return nativeStrongRule; | ||||
| 		} | ||||
| 		inline bool isNativeRule() const | ||||
| 		{ | ||||
| 			return nativeRule; | ||||
| 		} | ||||
| 		void setNative(); | ||||
|  | ||||
| 		/// The name of the rule. Can be any value of the RULE_* constants or a ID of a another pattern. | ||||
| 		//FIXME: remove string variable altogether, use only in constructor | ||||
| 		std::string name; | ||||
| 		/// Optional. A rule can have points. Patterns may have a minimum count of points to reach to be successful. | ||||
| 		int points; | ||||
|  | ||||
| 	private: | ||||
| 		bool standardRule; | ||||
| 		bool anyRule; | ||||
| 		bool dirtRule; | ||||
| 		bool sandRule; | ||||
| 		bool transitionRule; | ||||
| 		bool nativeStrongRule; | ||||
| 		bool nativeRule; | ||||
|  | ||||
| 		WeightedRule(); //only allow string constructor | ||||
| 	}; | ||||
|  | ||||
| 	static const int PATTERN_DATA_SIZE = 9; | ||||
| 	/// Constant for the flip mode different images. Pattern will be flipped and different images will be used(mapping area is divided into 4 parts) | ||||
| 	static const std::string FLIP_MODE_DIFF_IMAGES; | ||||
| 	/// Constant for the rule dirt, meaning a dirty border is required. | ||||
| 	static const std::string RULE_DIRT; | ||||
| 	/// Constant for the rule sand, meaning a sandy border is required. | ||||
| 	static const std::string RULE_SAND; | ||||
| 	/// Constant for the rule transition, meaning a dirty OR sandy border is required. | ||||
| 	static const std::string RULE_TRANSITION; | ||||
| 	/// Constant for the rule native, meaning a native border is required. | ||||
| 	static const std::string RULE_NATIVE; | ||||
| 	/// Constant for the rule native strong, meaning a native type is required. | ||||
| 	static const std::string RULE_NATIVE_STRONG; | ||||
| 	/// Constant for the rule any, meaning a native type, dirty OR sandy border is required. | ||||
| 	static const std::string RULE_ANY; | ||||
|  | ||||
| 	TerrainViewPattern(); | ||||
|  | ||||
| 	/// The pattern data can be visualized as a 3x3 matrix: | ||||
| 	/// [ ][ ][ ] | ||||
| 	/// [ ][ ][ ] | ||||
| 	/// [ ][ ][ ] | ||||
| 	/// | ||||
| 	/// The box in the center belongs always to the native terrain type and | ||||
| 	/// is the point of origin. Depending on the terrain type different rules | ||||
| 	/// can be used. Their meaning differs also from type to type. | ||||
| 	/// | ||||
| 	/// std::vector -> several rules can be used in one cell | ||||
| 	std::array<std::vector<WeightedRule>, PATTERN_DATA_SIZE> data; | ||||
|  | ||||
| 	/// The identifier of the pattern, if it's referenced from a another pattern. | ||||
| 	std::string id; | ||||
|  | ||||
| 	/// This describes the mapping between this pattern and the corresponding range of frames | ||||
| 	/// which should be used for the ter view. | ||||
| 	/// | ||||
| 	/// std::vector -> size=1: typical, size=2: if this pattern should map to two different types of borders | ||||
| 	/// std::pair   -> 1st value: lower range, 2nd value: upper range | ||||
| 	std::vector<std::pair<int, int> > mapping; | ||||
| 	/// If diffImages is true, different images/frames are used to place a rotated terrain view. If it's false | ||||
| 	/// the same frame will be used and rotated. | ||||
| 	bool diffImages; | ||||
| 	/// The rotationTypesCount is only used if diffImages is true and holds the number how many rotation types(horizontal, etc...) | ||||
| 	/// are supported. | ||||
| 	int rotationTypesCount; | ||||
|  | ||||
| 	/// The minimum and maximum points to reach to validate the pattern successfully. | ||||
| 	int minPoints, maxPoints; | ||||
| }; | ||||
|  | ||||
| /// The terrain view pattern config loads pattern data from the filesystem. | ||||
| class DLL_LINKAGE CTerrainViewPatternConfig : public boost::noncopyable | ||||
| { | ||||
| public: | ||||
| 	typedef std::vector<TerrainViewPattern> TVPVector; | ||||
|  | ||||
| 	CTerrainViewPatternConfig(); | ||||
| 	~CTerrainViewPatternConfig(); | ||||
|  | ||||
| 	const std::vector<TVPVector> & getTerrainViewPatterns(const Terrain & terrain) const; | ||||
| 	boost::optional<const TerrainViewPattern &> getTerrainViewPatternById(std::string patternId, const std::string & id) const; | ||||
| 	boost::optional<const TVPVector &> getTerrainViewPatternsById(const Terrain & terrain, const std::string & id) const; | ||||
| 	const TVPVector * getTerrainTypePatternById(const std::string & id) const; | ||||
| 	void flipPattern(TerrainViewPattern & pattern, int flip) const; | ||||
|  | ||||
| private: | ||||
| 	std::map<std::string, std::vector<TVPVector> > terrainViewPatterns; | ||||
| 	std::map<std::string, TVPVector> terrainTypePatterns; | ||||
| }; | ||||
|  | ||||
| class DLL_LINKAGE CTerrainViewPatternUtils | ||||
| { | ||||
| public: | ||||
| 	static void printDebuggingInfoAboutTile(const CMap * map, int3 pos); | ||||
| }; | ||||
| @@ -203,7 +203,7 @@ void CMapGenOptions::setMapTemplate(const CRmgTemplate * value) | ||||
| { | ||||
| 	mapTemplate = value; | ||||
| 	//TODO validate & adapt options according to template | ||||
| 	assert(0); | ||||
| 	//assert(0); | ||||
| } | ||||
|  | ||||
| void CMapGenOptions::finalize(CRandomGenerator & rand) | ||||
| @@ -401,21 +401,21 @@ bool CMapGenOptions::checkOptions() const | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand) const | ||||
| std::vector<const CRmgTemplate *> CMapGenOptions::getPossibleTemplates() const | ||||
| { | ||||
| 	int3 tplSize(width, height, (hasTwoLevels ? 2 : 1)); | ||||
| 	auto humanPlayers = countHumanPlayers(); | ||||
| 	 | ||||
|  | ||||
| 	auto templates = VLC->tplh->getTemplates(); | ||||
| 	 | ||||
|  | ||||
| 	vstd::erase_if(templates, [this, &tplSize, humanPlayers](const CRmgTemplate * tmpl) | ||||
| 	{ | ||||
| 		if(!tmpl->matchesSize(tplSize)) | ||||
| 			return true; | ||||
| 		 | ||||
|  | ||||
| 		if(!tmpl->isWaterContentAllowed(getWaterContent())) | ||||
| 			return true; | ||||
| 		 | ||||
|  | ||||
| 		if(getPlayerCount() != -1) | ||||
| 		{ | ||||
| 			if (!tmpl->getPlayers().isInRange(getPlayerCount())) | ||||
| @@ -427,17 +427,23 @@ const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand | ||||
| 			if(humanPlayers > *boost::min_element(tmpl->getPlayers().getNumbers())) | ||||
| 				return true; | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		if(compOnlyPlayerCount != -1) | ||||
| 		{ | ||||
| 			if (!tmpl->getCpuPlayers().isInRange(compOnlyPlayerCount)) | ||||
| 				return true; | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		return false; | ||||
| 	}); | ||||
| 	 | ||||
| 	// Select tpl | ||||
|  | ||||
| 	return templates; | ||||
| } | ||||
|  | ||||
| const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand) const | ||||
| { | ||||
| 	auto templates = getPossibleTemplates(); | ||||
|  | ||||
| 	if(templates.empty()) | ||||
| 		return nullptr; | ||||
| 	 | ||||
|   | ||||
| @@ -142,6 +142,8 @@ public: | ||||
| 	const CRmgTemplate * getMapTemplate() const; | ||||
| 	void setMapTemplate(const CRmgTemplate * value); | ||||
|  | ||||
| 	std::vector<const CRmgTemplate *> getPossibleTemplates() const; | ||||
|  | ||||
| 	/// Finalizes the options. All random sizes for various properties will be overwritten by numbers from | ||||
| 	/// a random number generator by keeping the options in a valid state. Check options should return true, otherwise | ||||
| 	/// this function fails. | ||||
|   | ||||
| @@ -121,15 +121,20 @@ void CMapGenerator::initQuestArtsRemaining() | ||||
|  | ||||
| std::unique_ptr<CMap> CMapGenerator::generate() | ||||
| { | ||||
| 	Load::Progress::reset(); | ||||
| 	Load::Progress::setupStepsTill(5, 30); | ||||
| 	try | ||||
| 	{ | ||||
| 		addHeaderInfo(); | ||||
| 		map->initTiles(*this); | ||||
| 		Load::Progress::step(); | ||||
| 		initPrisonsRemaining(); | ||||
| 		initQuestArtsRemaining(); | ||||
| 		genZones(); | ||||
| 		Load::Progress::step(); | ||||
| 		map->map().calculateGuardingGreaturePositions(); //clear map so that all tiles are unguarded | ||||
| 		map->addModificators(); | ||||
| 		Load::Progress::step(3); | ||||
| 		fillZones(); | ||||
| 		//updated guarded tiles will be calculated in CGameState::initMapObjects() | ||||
| 		map->getZones().clear(); | ||||
| @@ -138,6 +143,7 @@ std::unique_ptr<CMap> CMapGenerator::generate() | ||||
| 	{ | ||||
| 		logGlobal->error("Random map generation received exception: %s", e.what()); | ||||
| 	} | ||||
| 	Load::Progress::finish(); | ||||
| 	return std::move(map->mapInstance); | ||||
| } | ||||
|  | ||||
| @@ -284,13 +290,15 @@ void CMapGenerator::fillZones() | ||||
|  | ||||
| 	//we need info about all town types to evaluate dwellings and pandoras with creatures properly | ||||
| 	//place main town in the middle | ||||
| 	 | ||||
| 	Load::Progress::setupStepsTill(map->getZones().size(), 50); | ||||
| 	for(auto it : map->getZones()) | ||||
| 	{ | ||||
| 		it.second->initFreeTiles(); | ||||
| 		it.second->initModificators(); | ||||
| 		Progress::Progress::step(); | ||||
| 	} | ||||
|  | ||||
| 	Load::Progress::setupStepsTill(map->getZones().size(), 240); | ||||
| 	std::vector<std::shared_ptr<Zone>> treasureZones; | ||||
| 	for(auto it : map->getZones()) | ||||
| 	{ | ||||
| @@ -298,6 +306,8 @@ void CMapGenerator::fillZones() | ||||
| 		 | ||||
| 		if (it.second->getType() == ETemplateZoneType::TREASURE) | ||||
| 			treasureZones.push_back(it.second); | ||||
|  | ||||
| 		Progress::Progress::step(); | ||||
| 	} | ||||
|  | ||||
| 	//find place for Grail | ||||
| @@ -312,6 +322,8 @@ void CMapGenerator::fillZones() | ||||
| 	map->map().grailPos = *RandomGeneratorUtil::nextItem(grailZone->freePaths().getTiles(), rand); | ||||
|  | ||||
| 	logGlobal->info("Zones filled successfully"); | ||||
|  | ||||
| 	Load::Progress::set(250); | ||||
| } | ||||
|  | ||||
| void CMapGenerator::findZonesForQuestArts() | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include "CMapGenOptions.h" | ||||
| #include "../int3.h" | ||||
| #include "CRmgTemplate.h" | ||||
| #include "../LoadProgress.h" | ||||
|  | ||||
| class CRmgTemplate; | ||||
| class CMapGenOptions; | ||||
| @@ -26,7 +27,7 @@ class Zone; | ||||
| typedef std::vector<JsonNode> JsonVector; | ||||
|  | ||||
| /// The map generator creates a map randomly. | ||||
| class DLL_LINKAGE CMapGenerator | ||||
| class DLL_LINKAGE CMapGenerator: public Load::Progress | ||||
| { | ||||
| public: | ||||
| 	struct Config | ||||
|   | ||||
| @@ -32,6 +32,8 @@ 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) | ||||
| 	{ | ||||
| @@ -51,14 +53,22 @@ 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 & templateName) const | ||||
| const CRmgTemplate * CRmgTemplateStorage::getTemplate(const std::string & templateFullId) const | ||||
| { | ||||
| 	auto iter = templates.find(templateName); | ||||
| 	auto iter = templates.find(templateFullId); | ||||
| 	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; | ||||
|   | ||||
| @@ -29,10 +29,12 @@ 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 & templateName) const; | ||||
| 	const CRmgTemplate * getTemplate(const std::string & templateFullId) const; | ||||
| 	const CRmgTemplate * getTemplateByName(const std::string & templateName) const; | ||||
| 	std::vector<const CRmgTemplate *> getTemplates() const; | ||||
|  | ||||
| private: | ||||
| 	std::map<std::string, CRmgTemplate> templates; | ||||
| 	std::map<std::string, CRmgTemplate> templates; //FIXME: doesn't IHandlerBase cover this? | ||||
| 	std::map<std::string, CRmgTemplate> templatesByName; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,7 @@ namespace rmg | ||||
| 	void toAbsolute(Tileset & tiles, const int3 & position); | ||||
| 	void toRelative(Tileset & tiles, const int3 & position); | ||||
| 	 | ||||
| 	class Area | ||||
| 	class DLL_LINKAGE Area | ||||
| 	{ | ||||
| 	public: | ||||
| 		Area() = default; | ||||
|   | ||||
| @@ -12,8 +12,8 @@ | ||||
| #include "../ConstTransitivePtr.h" | ||||
| #include "../GameConstants.h" | ||||
|  | ||||
| const ui32 SERIALIZATION_VERSION = 804; | ||||
| const ui32 MINIMAL_SERIALIZATION_VERSION = 804; | ||||
| const ui32 SERIALIZATION_VERSION = 805; | ||||
| const ui32 MINIMAL_SERIALIZATION_VERSION = 805; | ||||
| const std::string SAVEGAME_MAGIC = "VCMISVG"; | ||||
|  | ||||
| class CHero; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user