Ver código fonte

- Added terrain & object selection classes - Added CComposedOperation - Refactored clear terrain, it is now an operation - Added rough support for updating terrain type if required

beegee1 12 anos atrás
pai
commit
3358a8efec

+ 3 - 0
lib/CGameState.cpp

@@ -26,6 +26,7 @@
 #include "filesystem/CResourceLoader.h"
 #include "GameConstants.h"
 #include "rmg/CMapGenerator.h"
+#include "CStopWatch.h"
 
 DLL_LINKAGE boost::rand48 ran;
 class CGObjectInstance;
@@ -864,6 +865,7 @@ void CGameState::init(StartInfo * si)
 			if(scenarioOps->createRandomMap)
 			{
                 logGlobal->infoStream() << "Create random map.";
+				CStopWatch sw;
 
 				// Create player settings for RMG
                 BOOST_FOREACH(const auto & pair, scenarioOps->playerInfos)
@@ -902,6 +904,7 @@ void CGameState::init(StartInfo * si)
 					}
 				}
 
+				logGlobal->infoStream() << boost::format("Generated random map in %i ms.") % sw.getDiff();
 			}
 			else
 			{

+ 287 - 78
lib/mapping/CMapEditManager.cpp

@@ -76,6 +76,44 @@ 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::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)
+{
+
+}
+
 CMapOperation::CMapOperation(CMap * map) : map(map)
 {
 
@@ -159,37 +197,30 @@ const CMapOperation * CMapUndoManager::peek(const TStack & stack) const
 }
 
 CMapEditManager::CMapEditManager(CMap * map)
-	: map(map)
+	: map(map), terrainSel(map), objectSel(map)
 {
 
 }
 
-void CMapEditManager::clearTerrain(CRandomGenerator * gen)
+CMap * CMapEditManager::getMap()
 {
-	for(int i = 0; i < map->width; ++i)
-	{
-		for(int j = 0; j < map->height; ++j)
-		{
-			map->getTile(int3(i, j, 0)).terType = ETerrainType::WATER;
-			map->getTile(int3(i, j, 0)).terView = gen->getInteger(20, 32);
+	return map;
+}
 
-			if(map->twoLevel)
-			{
-				map->getTile(int3(i, j, 1)).terType = ETerrainType::ROCK;
-				map->getTile(int3(i, j, 1)).terView = 0;
-			}
-		}
-	}
+void CMapEditManager::clearTerrain(CRandomGenerator * gen/* = nullptr*/)
+{
+	execute(make_unique<CClearTerrainOperation>(map, gen ? gen : &(this->gen)));
 }
 
-void CMapEditManager::drawTerrain(const MapRect & rect, ETerrainType terType, CRandomGenerator * gen)
+void CMapEditManager::drawTerrain(ETerrainType terType, CRandomGenerator * gen/* = nullptr*/)
 {
-	execute(make_unique<CDrawTerrainOperation>(map, rect, terType, gen));
+	execute(make_unique<CDrawTerrainOperation>(map, terrainSel, terType, gen ? gen : &(this->gen)));
+	terrainSel.clearSelection();
 }
 
-void CMapEditManager::insertObject(const int3 & pos, CGObjectInstance * obj)
+void CMapEditManager::insertObject(CGObjectInstance * obj, const int3 & pos)
 {
-	execute(make_unique<CInsertObjectOperation>(map, pos, obj));
+	execute(make_unique<CInsertObjectOperation>(map, obj, pos));
 }
 
 void CMapEditManager::execute(unique_ptr<CMapOperation> && operation)
@@ -198,14 +229,14 @@ void CMapEditManager::execute(unique_ptr<CMapOperation> && operation)
 	undoManager.addOperation(std::move(operation));
 }
 
-void CMapEditManager::undo()
+CTerrainSelection & CMapEditManager::getTerrainSelection()
 {
-	undoManager.undo();
+	return terrainSel;
 }
 
-void CMapEditManager::redo()
+CObjectSelection & CMapEditManager::getObjectSelection()
 {
-	undoManager.redo();
+	return objectSel;
 }
 
 CMapUndoManager & CMapEditManager::getUndoManager()
@@ -213,6 +244,40 @@ CMapUndoManager & CMapEditManager::getUndoManager()
 	return undoManager;
 }
 
+CComposedOperation::CComposedOperation(CMap * map) : CMapOperation(map)
+{
+
+}
+
+void CComposedOperation::execute()
+{
+	BOOST_FOREACH(auto & operation, operations)
+	{
+		operation->execute();
+	}
+}
+
+void CComposedOperation::undo()
+{
+	BOOST_FOREACH(auto & operation, operations)
+	{
+		operation->undo();
+	}
+}
+
+void CComposedOperation::redo()
+{
+	BOOST_FOREACH(auto & operation, operations)
+	{
+		operation->redo();
+	}
+}
+
+void CComposedOperation::addOperation(unique_ptr<CMapOperation> && operation)
+{
+	operations.push_back(std::move(operation));
+}
+
 const std::string TerrainViewPattern::FLIP_MODE_DIFF_IMAGES = "D";
 
 const std::string TerrainViewPattern::RULE_DIRT = "D";
@@ -351,27 +416,24 @@ boost::optional<const TerrainViewPattern &> CTerrainViewPatternConfig::getPatter
 	return boost::optional<const TerrainViewPattern &>();
 }
 
-CDrawTerrainOperation::CDrawTerrainOperation(CMap * map, const MapRect & rect, ETerrainType terType, CRandomGenerator * gen)
-	: CMapOperation(map), rect(rect), terType(terType), gen(gen)
+CDrawTerrainOperation::CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, ETerrainType terType, CRandomGenerator * gen)
+	: CMapOperation(map), terrainSel(terrainSel), terType(terType), gen(gen)
 {
 
 }
 
 void CDrawTerrainOperation::execute()
 {
-	for(int i = rect.x; i < rect.x + rect.width; ++i)
+	BOOST_FOREACH(const auto & pos, terrainSel.getSelectedItems())
 	{
-		for(int j = rect.y; j < rect.y + rect.height; ++j)
-		{
-			map->getTile(int3(i, j, rect.z)).terType = terType;
-		}
+		auto & tile = map->getTile(pos);
+		tile.terType = terType;
+		invalidateTerrainViews(pos);
 	}
 
-	//TODO there are situations where more tiles are affected implicitely
+	updateTerrainTypes();
+	updateTerrainViews();
 	//TODO add coastal bit to extTileFlags appropriately
-
-	MapRect viewRect(int3(rect.x - 1, rect.y - 1, rect.z), rect.width + 2, rect.height + 2); // Has to overlap 1 tile around
-	updateTerrainViews(viewRect & MapRect(int3(0, 0, viewRect.z), map->width, map->height)); // Rect should not overlap map dimensions
 }
 
 void CDrawTerrainOperation::undo()
@@ -389,66 +451,146 @@ std::string CDrawTerrainOperation::getLabel() const
 	return "Draw Terrain";
 }
 
-void CDrawTerrainOperation::updateTerrainViews(const MapRect & rect)
+void CDrawTerrainOperation::updateTerrainTypes()
 {
-	for(int x = rect.x; x < rect.x + rect.width; ++x)
+	auto positions = terrainSel.getSelectedItems();
+	while(!positions.empty())
 	{
-		for(int y = rect.y; y < rect.y + rect.height; ++y)
+		const auto & centerPos = *(positions.begin());
+		auto centerTile = map->getTile(centerPos);
+		auto tiles = getInvalidTiles(centerPos);
+		auto updateTerrainType = [&](const int3 & pos)
 		{
-			const auto & patterns =
-					CTerrainViewPatternConfig::get().getPatternsForGroup(getTerrainGroup(map->getTile(int3(x, y, rect.z)).terType));
+			map->getTile(pos).terType = centerTile.terType;
+			invalidateTerrainViews(pos);
+			logGlobal->debugStream() << boost::format("Update terrain tile at '%s' to type '%i'.") % pos % centerTile.terType;
+		};
 
-			// Detect a pattern which fits best
-			int bestPattern = -1;
-			ValidationResult valRslt(false);
-			for(int k = 0; k < patterns.size(); ++k)
+		// Fill foreign invalid tiles
+		BOOST_FOREACH(const auto & tile, tiles.foreignTiles)
+		{
+			updateTerrainType(tile);
+		}
+
+		if(tiles.nativeTiles.find(centerPos) != tiles.nativeTiles.end())
+		{
+			// Blow up
+			auto rect = extendTileAroundSafely(centerPos);
+			std::set<int3> suitableTiles;
+			int invalidForeignTilesCnt, invalidNativeTilesCnt;
+			invalidForeignTilesCnt = invalidNativeTilesCnt = std::numeric_limits<int>::max();
+			rect.forEach([&](const int3 & posToTest)
 			{
-				const auto & pattern = patterns[k];
-				valRslt = validateTerrainView(int3(x, y, rect.z), pattern);
-				if(valRslt.result)
+				auto & terrainTile = map->getTile(posToTest);
+				if(centerTile.terType != terrainTile.terType)
 				{
-					logGlobal->debugStream() << "Pattern detected at pos " << x << "x" << y << "x" << rect.z << ": P-Nr. " << pattern.id
-						  << ", Flip " << valRslt.flip << ", Repl. " << valRslt.transitionReplacement;
-					bestPattern = k;
-					break;
+					auto formerTerType = terrainTile.terType;
+					terrainTile.terType = centerTile.terType;
+					auto testTile = getInvalidTiles(posToTest);
+					auto addToSuitableTiles = [&](const int3 & pos)
+					{
+						suitableTiles.insert(pos);
+						logGlobal->debugStream() << boost::format("Found suitable tile '%s' for main tile '%s'.") % pos % centerPos;
+					};
+
+					if(testTile.nativeTiles.size() < invalidNativeTilesCnt ||
+							(testTile.nativeTiles.size() == invalidNativeTilesCnt && testTile.foreignTiles.size() < invalidForeignTilesCnt))
+					{
+						suitableTiles.clear();
+						addToSuitableTiles(posToTest);
+					}
+					else if(testTile.nativeTiles.size() == invalidNativeTilesCnt &&
+							testTile.foreignTiles.size() == invalidForeignTilesCnt)
+					{
+						addToSuitableTiles(posToTest);
+					}
+					terrainTile.terType = formerTerType;
 				}
-			}
-			assert(bestPattern != -1);
-			if(bestPattern == -1)
-			{
-				// This shouldn't be the case
-				logGlobal->warnStream() << "No pattern detected at pos " << x << "x" << y << "x" << rect.z;
-				continue;
-			}
+			});
 
-			// Get mapping
-			const TerrainViewPattern & pattern = patterns[bestPattern];
-			std::pair<int, int> mapping;
-			if(valRslt.transitionReplacement.empty())
+			if(suitableTiles.size() == 1)
 			{
-				mapping = pattern.mapping[0];
+				updateTerrainType(*suitableTiles.begin());
 			}
 			else
 			{
-				mapping = valRslt.transitionReplacement == TerrainViewPattern::RULE_DIRT ? pattern.mapping[0] : pattern.mapping[1];
+				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(int i = 0; i < ARRAY_COUNT(directions); ++i)
+				{
+					auto it = suitableTiles.find(centerPos + directions[i]);
+					if(it != suitableTiles.end())
+					{
+						updateTerrainType(*it);
+						break;
+					}
+				}
 			}
+		}
+		else
+		{
+			positions.erase(centerPos);
+		}
+	}
+}
 
-			// Set terrain view
-			auto & tile = map->getTile(int3(x, y, rect.z));
-			if(!pattern.diffImages)
-			{
-				tile.terView = gen->getInteger(mapping.first, mapping.second);
-				tile.extTileFlags = valRslt.flip;
-			}
-			else
+void CDrawTerrainOperation::updateTerrainViews()
+{
+	BOOST_FOREACH(const auto & pos, invalidatedTerViews)
+	{
+		const auto & patterns =
+				CTerrainViewPatternConfig::get().getPatternsForGroup(getTerrainGroup(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];
+			valRslt = validateTerrainView(pos, pattern);
+			if(valRslt.result)
 			{
-				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->getInteger(firstFrame, firstFrame + framesPerRot - 1);
-				tile.extTileFlags =	0;
+				/*logGlobal->debugStream() << boost::format("Pattern detected at pos '%s': Pattern '%s', Flip '%i', Repl. '%s'.") %
+											pos % pattern.id % valRslt.flip % valRslt.transitionReplacement;*/
+				bestPattern = k;
+				break;
 			}
 		}
+		assert(bestPattern != -1);
+		if(bestPattern == -1)
+		{
+			// This shouldn't be the case
+			logGlobal->warnStream() << boost::format("No pattern detected at pos '%s'.") % pos;
+			continue;
+		}
+
+		// Get mapping
+		const TerrainViewPattern & pattern = patterns[bestPattern];
+		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->getInteger(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->getInteger(firstFrame, firstFrame + framesPerRot - 1);
+			tile.extTileFlags =	0;
+		}
 	}
 }
 
@@ -651,13 +793,80 @@ TerrainViewPattern CDrawTerrainOperation::getFlippedPattern(const TerrainViewPat
 	return ret;
 }
 
+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
+{
+	InvalidTiles tiles;
+	auto centerTerType = map->getTile(centerPos).terType;
+	auto rect = extendTileAround(centerPos);
+	rect.forEach([&](const int3 & pos)
+	{
+		if(map->isInTheMap(pos))
+		{
+			auto terType = map->getTile(pos).terType;
+			// Pattern 2x2
+			const int3 translations[4] = { int3(-1, -1, 0), int3(0, -1, 0), int3(-1, 0, 0), int3(0, 0, 0) };
+			bool valid = true;
+			for(int i = 0; i < ARRAY_COUNT(translations); ++i)
+			{
+				valid = true;
+				MapRect square(int3(pos.x + translations[i].x, pos.y + translations[i].y, pos.z), 2, 2);
+				square.forEach([&](const int3 & pos)
+				{
+					if(map->isInTheMap(pos) && map->getTile(pos).terType != terType) valid = false;
+				});
+				if(valid) break;
+			}
+			if(!valid)
+			{
+				if(terType == centerTerType) tiles.nativeTiles.insert(pos);
+				else tiles.foreignTiles.insert(pos);
+			}
+		}
+	});
+	return tiles;
+}
+
+MapRect CDrawTerrainOperation::extendTileAround(const int3 & centerPos) const
+{
+	return MapRect(int3(centerPos.x - 1, centerPos.y - 1, centerPos.z), 3, 3);
+}
+
+MapRect CDrawTerrainOperation::extendTileAroundSafely(const int3 & centerPos) const
+{
+	return extendTileAround(centerPos) & MapRect(int3(0, 0, centerPos.z), map->width, map->height);
+}
+
 CDrawTerrainOperation::ValidationResult::ValidationResult(bool result, const std::string & transitionReplacement /*= ""*/)
 	: result(result), transitionReplacement(transitionReplacement)
 {
 
 }
 
-CInsertObjectOperation::CInsertObjectOperation(CMap * map, const int3 & pos, CGObjectInstance * obj)
+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, ETerrainType::WATER, gen));
+	terrainSel.clearSelection();
+	terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height));
+	addOperation(make_unique<CDrawTerrainOperation>(map, terrainSel, ETerrainType::ROCK, gen));
+}
+
+std::string CClearTerrainOperation::getLabel() const
+{
+	return "Clear Terrain";
+}
+
+CInsertObjectOperation::CInsertObjectOperation(CMap * map, CGObjectInstance * obj, const int3 & pos)
 	: CMapOperation(map), pos(pos), obj(obj)
 {
 

+ 117 - 11
lib/mapping/CMapEditManager.h

@@ -38,19 +38,79 @@ struct DLL_LINKAGE MapRect
 
 	/// 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 i = x; i < right(); ++i)
+		{
+			for(int j = y; j < bottom(); ++j)
+			{
+				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;
+};
+
+/// 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:
-	CMapOperation(CMap * map);
+	explicit CMapOperation(CMap * map);
 	virtual ~CMapOperation() { };
 
 	virtual void execute() = 0;
 	virtual void undo() = 0;
 	virtual void redo() = 0;
-	virtual std::string getLabel() const; /// Returns a display-able name of the operation.
+	virtual std::string getLabel() const = 0; /// Returns a display-able name of the operation.
 
 protected:
 	CMap * map;
@@ -93,28 +153,50 @@ class DLL_LINKAGE CMapEditManager : boost::noncopyable
 {
 public:
 	CMapEditManager(CMap * map);
+	CMap * getMap();
 
 	/// Clears the terrain. The free level is filled with water and the underground level with rock.
-	void clearTerrain(CRandomGenerator * gen);
+	void clearTerrain(CRandomGenerator * gen = nullptr);
+
+	/// Draws terrain at the current terrain selection. The selection will be cleared automatically.
+	void drawTerrain(ETerrainType terType, CRandomGenerator * gen = nullptr);
+	void insertObject(CGObjectInstance * obj, const int3 & pos);
 
-	void drawTerrain(const MapRect & rect, ETerrainType terType, CRandomGenerator * gen);
-	void insertObject(const int3 & pos, CGObjectInstance * obj);
+	CTerrainSelection & getTerrainSelection();
+	CObjectSelection & getObjectSelection();
 
 	CMapUndoManager & getUndoManager();
-	void undo();
-	void redo();
 
 private:
 	void execute(unique_ptr<CMapOperation> && operation);
 
 	CMap * map;
 	CMapUndoManager undoManager;
+	CRandomGenerator gen;
+	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(unique_ptr<CMapOperation> && operation);
+
+private:
+	std::list<unique_ptr<CMapOperation> > operations;
+};
+
 namespace ETerrainGroup
 {
 	enum ETerrainGroup
@@ -214,7 +296,7 @@ private:
 class CDrawTerrainOperation : public CMapOperation
 {
 public:
-	CDrawTerrainOperation(CMap * map, const MapRect & rect, ETerrainType terType, CRandomGenerator * gen);
+	CDrawTerrainOperation(CMap * map, const CTerrainSelection & terrainSel, ETerrainType terType, CRandomGenerator * gen);
 
 	void execute() override;
 	void undo() override;
@@ -232,7 +314,18 @@ private:
 		int flip;
 	};
 
-	void updateTerrainViews(const MapRect & rect);
+	struct InvalidTiles
+	{
+		std::set<int3> foreignTiles, nativeTiles;
+	};
+
+	void updateTerrainTypes();
+	void invalidateTerrainViews(const int3 & centerPos);
+	InvalidTiles getInvalidTiles(const int3 & centerPos) const;
+	MapRect extendTileAround(const int3 & centerPos) const;
+	MapRect extendTileAroundSafely(const int3 & centerPos) const; /// doesn't exceed map size
+
+	void updateTerrainViews();
 	ETerrainGroup::ETerrainGroup getTerrainGroup(ETerrainType terType) const;
 	/// 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).
@@ -246,16 +339,29 @@ private:
 	static const int FLIP_PATTERN_VERTICAL = 2;
 	static const int FLIP_PATTERN_BOTH = 3;
 
-	MapRect rect;
+	CTerrainSelection terrainSel;
 	ETerrainType 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;
+
+private:
+
 };
 
 /// The CInsertObjectOperation class inserts an object to the map.
 class CInsertObjectOperation : public CMapOperation
 {
 public:
-	CInsertObjectOperation(CMap * map, const int3 & pos, CGObjectInstance * obj);
+	CInsertObjectOperation(CMap * map, CGObjectInstance * obj, const int3 & pos);
 
 	void execute() override;
 	void undo() override;

+ 3 - 2
lib/rmg/CMapGenerator.cpp

@@ -422,7 +422,8 @@ void CMapGenerator::genTerrain()
 {
 	map->initTerrain();
 	editManager->clearTerrain(&gen);
-	editManager->drawTerrain(MapRect(int3(10, 10, 0), 20, 30), ETerrainType::GRASS, &gen);
+	editManager->getTerrainSelection().selectRange(MapRect(int3(10, 10, 0), 20, 30));
+	editManager->drawTerrain(ETerrainType::GRASS, &gen);
 }
 
 void CMapGenerator::genTowns()
@@ -445,7 +446,7 @@ void CMapGenerator::genTowns()
 		town->defInfo = VLC->dobjinfo->gobjs[town->ID][town->subID];
 		town->builtBuildings.insert(BuildingID::FORT);
 		town->builtBuildings.insert(BuildingID::DEFAULT);
-		editManager->insertObject(int3(townPos[side].x, townPos[side].y + (i / 2) * 5, 0), town);
+		editManager->insertObject(town, int3(townPos[side].x, townPos[side].y + (i / 2) * 5, 0));
 
 		// Update player info
 		playerInfo.allowedFactions.clear();

+ 2 - 1
test/CMapEditManagerTest.cpp

@@ -60,7 +60,8 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain)
 				int3 pos(posVector[0].Float(), posVector[1].Float(), posVector[2].Float());
 				logGlobal->infoStream() << boost::format("Test pattern '%s' on position x '%d', y '%d', z '%d'.") % patternStr % pos.x % pos.y % pos.z;
 				const auto & originalTile = originalMap->getTile(pos);
-				editManager->drawTerrain(MapRect(pos, 1, 1), originalTile.terType, &gen);
+				editManager->getTerrainSelection().selectRange(MapRect(pos, 1, 1));
+				editManager->drawTerrain(originalTile.terType, &gen);
 				const auto & tile = map->getTile(pos);
 				bool isInRange = false;
 				BOOST_FOREACH(const auto & range, mapping)