Browse Source

- Added basic mock/test generation - Added stub for terrain editing

beegee1 12 years ago
parent
commit
0311e5e6f5

+ 3 - 0
Global.h

@@ -94,6 +94,9 @@ typedef boost::int32_t si32; //signed int 32 bits (4 bytes)
 typedef boost::int16_t si16; //signed int 16 bits (2 bytes)
 typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 
+// Fixed width bool data type is important for serialization
+static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
+
 #if defined _M_X64 && defined _WIN32 //Win64 -> cannot load 32-bit DLLs for video handling
 	#define DISABLE_VIDEO
 #endif

+ 24 - 21
client/CPreGame.cpp

@@ -165,9 +165,17 @@ void updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader *
 		PlayerSettings &pset = sInfo.playerInfos[i];
 		pset.color = i;
 		if(pinfo.canHumanPlay && namesIt != playerNames.cend())
+		{
 			setPlayer(pset, namesIt++->first, playerNames);
+		}
 		else
+		{
 			setPlayer(pset, 0, playerNames);
+			if(!pinfo.canHumanPlay)
+			{
+				pset.compOnly = true;
+			}
+		}
 
 		pset.castle = pinfo.defaultCastle();
 		pset.hero = pinfo.defaultHero();
@@ -873,11 +881,6 @@ void CSelectionScreen::startScenario()
 		{
 			saveGameName = sInfo.mapname;
 		}
-		if(sInfo.createRandomMap)
-		{
-			// Random map generation fails for now, so don't start game...
-			return;
-		}
 
 		StartInfo * si = new StartInfo(sInfo);
 		CGP->removeFromGui();
@@ -1138,9 +1141,9 @@ void SelectionTab::parseGames(const std::vector<ResourceID> &files, bool multi)
 
 			allItems.push_back(mapInfo);
 		}
-		catch(std::exception & e)
+		catch(const std::exception & e)
 		{
-			tlog3 << "Failed to process " << files[i].getName() <<": " << e.what() << std::endl;
+			tlog3 << "Error: Failed to process " << files[i].getName() <<": " << e.what() << std::endl;
 		}
 	}
 }
@@ -1590,8 +1593,8 @@ RandomMapTab::RandomMapTab()
 
 	// Map Size
 	mapSizeBtnGroup = new CHighlightableButtonsGroup(0);
-	mapSizeBtnGroup->pos.y = 81;
-	mapSizeBtnGroup->pos.x = 158;
+	mapSizeBtnGroup->pos.y += 81;
+	mapSizeBtnGroup->pos.x += 158;
 	const std::vector<std::string> mapSizeBtns = boost::assign::list_of("RANSIZS")("RANSIZM")("RANSIZL")("RANSIZX");
 	addButtonsToGroup(mapSizeBtnGroup, mapSizeBtns, 0, 3, 47, 198);
 	mapSizeBtnGroup->select(1, false);
@@ -1621,8 +1624,8 @@ RandomMapTab::RandomMapTab()
 	const int BTNS_GROUP_LEFT_MARGIN = 67;
 	// Amount of players
 	playersCntGroup = new CHighlightableButtonsGroup(0);
-	playersCntGroup->pos.y = 153;
-	playersCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
+	playersCntGroup->pos.y += 153;
+	playersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(playersCntGroup, numberDefs, 1, 8, NUMBERS_WIDTH, 204, 212);
 	playersCntGroup->onChange = [&](int btnId)
 	{
@@ -1635,8 +1638,8 @@ RandomMapTab::RandomMapTab()
 
 	// Amount of teams
 	teamsCntGroup = new CHighlightableButtonsGroup(0);
-	teamsCntGroup->pos.y = 219;
-	teamsCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
+	teamsCntGroup->pos.y += 219;
+	teamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(teamsCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 214, 222);
 	teamsCntGroup->onChange = [&](int btnId)
 	{
@@ -1646,8 +1649,8 @@ RandomMapTab::RandomMapTab()
 
 	// Computer only players
 	compOnlyPlayersCntGroup = new CHighlightableButtonsGroup(0);
-	compOnlyPlayersCntGroup->pos.y = 285;
-	compOnlyPlayersCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
+	compOnlyPlayersCntGroup->pos.y += 285;
+	compOnlyPlayersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
 	compOnlyPlayersCntGroup->select(0, true);
 	compOnlyPlayersCntGroup->onChange = [&](int btnId)
@@ -1660,8 +1663,8 @@ RandomMapTab::RandomMapTab()
 
 	// Computer only teams
 	compOnlyTeamsCntGroup = new CHighlightableButtonsGroup(0);
-	compOnlyTeamsCntGroup->pos.y = 351;
-	compOnlyTeamsCntGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
+	compOnlyTeamsCntGroup->pos.y += 351;
+	compOnlyTeamsCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(compOnlyTeamsCntGroup, numberDefs, 0, 6, NUMBERS_WIDTH, 234, 241);
 	deactivateButtonsFrom(compOnlyTeamsCntGroup, 0);
 	compOnlyTeamsCntGroup->onChange = [&](int btnId)
@@ -1673,8 +1676,8 @@ RandomMapTab::RandomMapTab()
 	const int WIDE_BTN_WIDTH = 85;
 	// Water content
 	waterContentGroup = new CHighlightableButtonsGroup(0);
-	waterContentGroup->pos.y = 419;
-	waterContentGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
+	waterContentGroup->pos.y += 419;
+	waterContentGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	const std::vector<std::string> waterContentBtns = boost::assign::list_of("RANNONE")("RANNORM")("RANISLD");
 	addButtonsWithRandToGroup(waterContentGroup, waterContentBtns, 0, 2, WIDE_BTN_WIDTH, 243, 246);
 	waterContentGroup->onChange = [&](int btnId)
@@ -1684,8 +1687,8 @@ RandomMapTab::RandomMapTab()
 
 	// Monster strength
 	monsterStrengthGroup = new CHighlightableButtonsGroup(0);
-	monsterStrengthGroup->pos.y = 485;
-	monsterStrengthGroup->pos.x = BTNS_GROUP_LEFT_MARGIN;
+	monsterStrengthGroup->pos.y += 485;
+	monsterStrengthGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	const std::vector<std::string> monsterStrengthBtns = boost::assign::list_of("RANWEAK")("RANNORM")("RANSTRG");
 	addButtonsWithRandToGroup(monsterStrengthGroup, monsterStrengthBtns, 0, 2, WIDE_BTN_WIDTH, 248, 251);
 	monsterStrengthGroup->onChange = [&](int btnId)

+ 2 - 2
client/UIFramework/CGuiHandler.cpp

@@ -387,9 +387,9 @@ void CGuiHandler::run()
 			mainFPSmng->framerateDelay(); // holds a constant FPS
 		}
 	}
-	catch(const std::exception & ex)
+	catch(const std::exception & e)
 	{
-		tlog1 << "Error: " << ex.what() << std::endl;
+		tlog1 << "Error: " << e.what() << std::endl;
 		exit(EXIT_FAILURE);
 	}
 }

+ 428 - 0
config/terrainViewPatterns.json

@@ -0,0 +1,428 @@
+// Defines terrain view patterns.
+
+// The following properties are mandatory:
+// data:		the 3x3 pattern
+// mapping:		maps the pattern to a range of terrain view images/frames of the .DEF, e.g. 10-15
+//				for patterns which represent two transitions a comma can be used to distinct between dirt and sand
+//				e.g. 10-15, 25-35 whereas the first value is always dirt and the second sand
+
+// The following properties are optional:
+// flipMode:	should the same be flipped or different images be used(see rock) or is flip not supported at all; allowed values: sameImage | diffImages; default is: sameImage
+// id:			the identifier for the pattern if it's referenced by other patterns
+// minPoints:	the minimum points to reach to validate the pattern successfully
+
+// The following table shows the rules for the 3x3 pattern of all terrain types: 
+// I) normal(e.g. grass, lava, ...):
+// N:		Native type
+// D:		Dirt border
+// S:		Sand border
+// T:		Sand OR dirt border(all Ts in the pattern are replaced by dirt OR sand)
+// ?:		T or N
+// II) dirt:
+// N:		Native type
+// D:		Dirt border
+// S:		Sand border
+// ?:		Any border
+// III) sand:
+// N:		Native type
+// S:		Sand border
+// IV) water:
+// N:		Native type
+// S:		Sand border
+// ?:		Any border
+// V) rock:
+// N:		Native type
+// S:		Sand border
+// ?:		Any border
+
+// Some additional info:
+// Rules can be combined with comma. e.g. T, N which would be the same meaning of ?. It's most useful in combination with pattern chaining.
+// Chaining of patterns is supported. To reference a another pattern you simply add the <Ref Id> to the relevant field of the pattern.
+// Rules can be given points: <[Rule OR Ref Id]-Points> With the property minPoints simple conditions can be built.
+
+{
+	"normal" :
+	[
+		// Standard transitions
+		{
+			"data" :
+			[
+				"?", "?", "T",
+				"?", "N", "N",
+				"T", "N", "N"
+			],
+			"mapping" : "0-3, 20-23"
+		},
+		{
+			"data" :
+			[
+				"?", "N", "N",
+				"T", "N", "N",
+				"?", "N", "N"
+			],
+			"mapping" : "4-7, 24-27"
+		},
+		{
+			"data" :
+			[
+				"?", "T", "?",
+				"N", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "8-11, 28-31"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "N",
+				"N", "N", "T"
+			],
+			"mapping" : "12-15, 32-35"
+		},
+		{
+			"data" :
+			[
+				"T", "T", "a-1,?",
+				"T", "N", "N",
+				"a-1,?", "N", "N"
+			],
+			"mapping" : "16-17, 36-37",
+			"id" : "a",
+			"minPoints" : 1
+		},
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "a-1,N",
+				"N", "a-1,N", "T"
+			],
+			"mapping" : "18-19, 38-39",
+			"minPoints" : 1
+		},
+		// Mixed transitions
+		{
+			"data" :
+			[
+				"T", "N", "N",
+				"N", "N", "N",
+				"N", "N", "T"
+			],
+			"mapping" : "40, 42"
+		},
+		{
+			"data" :
+			[
+				"D", "N", "N",
+				"N", "N", "N",
+				"N", "N", "S"
+			],
+			"mapping" : "41"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "D,N",
+				"N", "N", "D",
+				"S", "D", "D,N"
+			],
+			"mapping" : "43"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "S",
+				"N", "N", "D",
+				"D,N", "D", "D,N"
+			],
+			"mapping" : "44"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "D,N",
+				"N", "N", "D",
+				"N", "N", "S"
+			],
+			"mapping" : "45"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "N",
+				"D,N", "D", "S"
+			],
+			"mapping" : "46"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "D,S,N",
+				"N", "N", "S",
+				"D", "D", "D,S,N"
+			],
+			"mapping" : "47"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "D",
+				"N", "N", "D",
+				"D,S,N", "S", "D,S,N"
+			],
+			"mapping" : "48"
+		},
+		// No transition
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "49-72"
+		}
+	],
+	"dirt" :
+	[
+		// Standard transitions
+		{
+			"data" :
+			[
+				"?", "S", "S",
+				"S", "N", "N",
+				"S", "N", "N"
+			],
+			"mapping" : "0-3"
+		},
+		{
+			"data" :
+			[
+				"?", "D", "D",
+				"S", "N", "N",
+				"?", "D", "D"
+			],
+			"mapping" : "4-7"
+		},
+		{
+			"data" :
+			[
+				"?", "S", "?",
+				"D", "N", "D",
+				"D", "N", "D"
+			],
+			"mapping" : "8-11"
+		},
+		{
+			"data" :
+			[
+				"D", "D", "D",
+				"D", "N", "N",
+				"D", "N", "S"
+			],
+			"mapping" : "12-15"
+		},
+		{
+			"data" :
+			[
+				"S", "S", "D",
+				"S", "N", "b-1,D",
+				"D", "b-1,D", "D"
+			],
+			"mapping" : "16-17",
+			"id" : "a",
+			"minPoints" : 1
+		},
+		{
+			"data" :
+			[
+				"D", "D", "D",
+				"D", "N", "a-1,D",
+				"D", "a-1,D", "S"
+			],
+			"mapping" : "18-19",
+			"id" : "b",
+			"minPoints" : 1
+		},
+		// Mixed transition
+		{
+			"data" :
+			[
+				"S", "D", "D",
+				"D", "N", "D",
+				"D", "D", "S"
+			],
+			"mapping" : "20"
+		},
+		// No transition
+		{
+			"data" :
+			[
+				"D", "D", "D",
+				"D", "N", "D",
+				"D", "D", "D"
+			],
+			"mapping" : "21-44"
+		}
+	],
+	"sand" :
+	[
+		{
+			"data" :
+			[
+				"S", "S", "S",
+				"S", "N", "S",
+				"S", "S", "S"
+			],
+			"mapping" : "0-23"
+		}
+	],
+	"water" :
+	[
+		// Standard transitions
+		{
+			"data" :
+			[
+				"S", "S", "S",
+				"S", "N", "N",
+				"S", "N", "N"
+			],
+			"mapping" : "0-3"
+		},
+		{
+			"data" :
+			[
+				"?", "N", "N",
+				"S", "N", "N",
+				"?", "N", "N"
+			],
+			"mapping" : "4-7"
+		},
+		{
+			"data" :
+			[
+				"?", "S", "?",
+				"N", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "8-11"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "N",
+				"N", "N", "S"
+			],
+			"mapping" : "12-15"
+		},
+		{
+			"data" :
+			[
+				"S", "S", "N",
+				"S", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "16-17",
+			"id" : "a"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "a-1,N",
+				"N", "a-1,N", "S"
+			],
+			"mapping" : "18-19",
+			"minPoints" : 1
+		},
+		// No transition
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "20-32"
+		}
+	],
+	"rock" :
+	[
+		// No transition
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "0-7"
+		},
+		// Standard transitions
+		{
+			"data" :
+			[
+				"?", "S", "?",
+				"S", "N", "N",
+				"?", "N", "N"
+			],
+			"mapping" : "8-15",
+			"flipMode" : "diffImages"
+		},
+		{
+			"data" :
+			[
+				"?", "N", "N",
+				"S", "N", "N",
+				"?", "N", "N"
+			],
+			"mapping" : "16-19",
+			"flipMode" : "diffImages"
+		},
+		{
+			"data" :
+			[
+				"?", "S", "?",
+				"N", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "20-23",
+			"flipMode" : "diffImages"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "N",
+				"N", "N", "S"
+			],
+			"mapping" : "24-31",
+			"flipMode" : "diffImages"
+		},
+		{
+			"data" :
+			[
+				"S", "S", "N",
+				"S", "N", "N",
+				"N", "N", "N"
+			],
+			"mapping" : "32-39",
+			"flipMode" : "diffImages",
+			"id" : "a"
+		},
+		{
+			"data" :
+			[
+				"N", "N", "N",
+				"N", "N", "a-1,N",
+				"N", "a-1,N", "S"
+			],
+			"mapping" : "40-47",
+			"flipMode" : "diffImages",
+			"minPoints" : 1
+		}
+	]
+}

+ 12 - 2
lib/CArtHandler.cpp

@@ -9,6 +9,7 @@
 #include "CSpellHandler.h"
 #include "CObjectHandler.h"
 #include "NetPacks.h"
+#include "GameConstants.h"
 
 using namespace boost::assign;
 
@@ -776,14 +777,23 @@ void CArtHandler::initAllowedArtifactsList(const std::vector<ui8> &allowed)
 			allowedArtifacts.push_back(artifacts[i]);
 		 else //check if active modules allow artifact to be every used
 		 {
-			 if (artifacts[i]->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS ||
-				 artifacts[i]->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT)
+			 if ((artifacts[i]->possibleSlots[ArtBearer::COMMANDER].size() && VLC->modh->modules.COMMANDERS) ||
+				 (artifacts[i]->possibleSlots[ArtBearer::CREATURE].size() && VLC->modh->modules.STACK_ARTIFACT))
 				 allowedArtifacts.push_back(artifacts[i]);
 			 //keep im mind that artifact can be worn by more than one type of bearer
 		 }
 	}
 }
 
+std::vector<ui8> CArtHandler::getDefaultAllowedArtifacts() const
+{
+	std::vector<ui8> allowedArtifacts;
+	allowedArtifacts.resize(127, 1);
+	allowedArtifacts.resize(141, 0);
+	allowedArtifacts.resize(GameConstants::ARTIFACTS_QUANTITY, 1);
+	return allowedArtifacts;
+}
+
 CArtifactInstance::CArtifactInstance()
 {
 	init();

+ 7 - 0
lib/CArtHandler.h

@@ -256,6 +256,13 @@ public:
 	CArtHandler();
 	~CArtHandler();
 
+	/**
+	 * Gets a list of default allowed artifacts.
+	 *
+	 * @return a list of allowed artifacts, the index is the artifact id and the value either 0 for not allowed or 1 for allowed
+	 */
+	std::vector<ui8> getDefaultAllowedArtifacts() const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & artifacts & allowedArtifacts & treasures & minors & majors & relics

+ 41 - 2
lib/CGameState.cpp

@@ -26,6 +26,7 @@
 #include "Filesystem/CResourceLoader.h"
 #include "GameConstants.h"
 #include "RMG/CMapGenOptions.h"
+#include "RMG/CMapGenerator.h"
 
 DLL_LINKAGE boost::rand48 ran;
 class CGObjectInstance;
@@ -872,7 +873,46 @@ void CGameState::init(StartInfo * si)
 			if(scenarioOps->createRandomMap)
 			{
 				tlog0 << "Create random map." << std::endl;
-				//TODO random map
+
+				// Create player settings for RMG
+				std::map<TPlayerColor, CMapGenerator::CPlayerSettings> players;
+				BOOST_FOREACH(auto pInfo, scenarioOps->playerInfos)
+				{
+					const PlayerSettings & startSettings = pInfo.second;
+					CMapGenerator::CPlayerSettings player;
+					player.setColor(startSettings.color);
+					player.setStartingTown(startSettings.castle);
+					if(startSettings.playerID > 0)
+					{
+						player.setPlayerType(CMapGenerator::CPlayerSettings::HUMAN);
+					}
+					else if(startSettings.compOnly)
+					{
+						player.setPlayerType(CMapGenerator::CPlayerSettings::COMP_ONLY);
+					}
+					players[player.getColor()] = player;
+				}
+
+				// Gen map
+				CMapGenerator mapGen(*scenarioOps->mapGenOptions, players, scenarioOps->seedToBeUsed);
+				map = mapGen.generate().release();
+
+				// Update starting options
+				for(auto it = scenarioOps->playerInfos.begin(); it != scenarioOps->playerInfos.end();)
+				{
+					PlayerSettings & pSettings = it->second;
+					if(!(map->players[pSettings.color].canHumanPlay || map->players[pSettings.color].canComputerPlay))
+					{
+						scenarioOps->playerInfos.erase(it++);
+					}
+					else
+					{
+						pSettings.compOnly = !(map->players[pSettings.color].canHumanPlay);
+						pSettings.team = map->players[pSettings.color].team;
+						pSettings.castle = map->players[pSettings.color].defaultCastle();
+						++it;
+					}
+				}
 			}
 			else
 			{
@@ -902,7 +942,6 @@ void CGameState::init(StartInfo * si)
 	VLC->arth->initAllowedArtifactsList(map->allowedArtifact);
 	tlog0 << "Map loaded!" << std::endl;
 
-
 	//tlog0 <<"Reading and detecting map file (together): "<<tmh.getDif()<<std::endl;
 	tlog0 << "\tOur checksum for the map: "<< map->checksum << std::endl;
 	if(scenarioOps->mapfileChecksum)

+ 7 - 0
lib/CHeroHandler.cpp

@@ -498,3 +498,10 @@ std::vector<ui8> CHeroHandler::getDefaultAllowedHeroes() const
 
 	return allowedHeroes;
 }
+
+std::vector<ui8> CHeroHandler::getDefaultAllowedAbilities() const
+{
+	std::vector<ui8> allowedAbilities;
+	allowedAbilities.resize(GameConstants::SKILL_QUANTITY, 1);
+	return allowedAbilities;
+}

+ 8 - 1
lib/CHeroHandler.h

@@ -218,10 +218,17 @@ public:
 	 * create a JSON config file or merge it with a existing config file which describes which heroes can be used for
 	 * random map generation / map editor(default map settings). (Gelu, ... should be excluded)
 	 *
-	 * @return a list of allowed heroes, the index is the hero id and the value either 0 for not allowed and 1 for allowed
+	 * @return a list of allowed heroes, the index is the hero id and the value either 0 for not allowed or 1 for allowed
 	 */
 	std::vector<ui8> getDefaultAllowedHeroes() const;
 
+	/**
+	 * Gets a list of default allowed abilities. OH3 abilities/skills are all allowed by default.
+	 *
+	 * @return a list of allowed abilities, the index is the ability id and the value either 0 for not allowed or 1 for allowed
+	 */
+	std::vector<ui8> getDefaultAllowedAbilities() const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & classes & heroes & expPerLevel & ballistics & terrCosts;

+ 82 - 79
lib/CMakeLists.txt

@@ -1,79 +1,82 @@
-project(libvcmi)
-cmake_minimum_required(VERSION 2.6)
-
-include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/lib)
-include_directories(${Boost_INCLUDE_DIRS} ${SDL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
-
-set(lib_SRCS
-        Filesystem/CBinaryReader.cpp
-        Filesystem/CFilesystemLoader.cpp
-        Filesystem/CMemoryStream.cpp
-        Filesystem/CFileInfo.cpp
-        Filesystem/CLodArchiveLoader.cpp
-        Filesystem/CResourceLoader.cpp
-        Filesystem/CFileInputStream.cpp
-        Filesystem/CCompressedStream.cpp
-        Mapping/CCampaignHandler.cpp
-        Mapping/CMap.cpp
-        Mapping/CMapInfo.cpp
-        Mapping/CMapService.cpp
-   		RMG/CMapGenOptions.cpp
-        BattleAction.cpp
-        BattleHex.cpp
-        BattleState.cpp
-        CArtHandler.cpp
-        CBattleCallback.cpp
-        CBuildingHandler.cpp
-        CConfigHandler.cpp
-        CConsoleHandler.cpp
-        CCreatureHandler.cpp
-        CCreatureSet.cpp
-        CDefObjInfoHandler.cpp
-        CGameInterface.cpp
-        CGameState.cpp
-        CGeneralTextHandler.cpp
-        CHeroHandler.cpp
-        CLogger.cpp
-        CModHandler.cpp
-        CObjectHandler.cpp
-        CObstacleInstance.cpp
-        Connection.cpp
-        CSpellHandler.cpp
-        CThreadHelper.cpp
-        CTownHandler.cpp
-        HeroBonus.cpp
-        IGameCallback.cpp
-        JsonNode.cpp
-        NetPacksLib.cpp
-        ResourceSet.cpp
-        VCMI_Lib.cpp
-)
-
-set(lib_HEADERS
-		Filesystem/CInputStream.h
-		Filesystem/ISimpleResourceLoader.h
-		AI_Base.h
-		CondSh.h
-		ConstTransitivePtr.h
-		CScriptingModule.h
-		CStopWatch.h
-		GameConstants.h
-		StringConstants.h
-		IGameEventsReceiver.h
-		int3.h
-		Interprocess.h
-		NetPacks.h
-		RegisterTypes.h
-		StartInfo.h
-		UnlockGuard.h
-		VCMIDirs.h
-		vcmi_endian.h
-)
-
-add_library(vcmi SHARED ${lib_SRCS} ${lib_HEADERS})
-set_target_properties(vcmi PROPERTIES XCODE_ATTRIBUTE_LD_DYLIB_INSTALL_NAME "@executable_path/libvcmi.dylib")
-target_link_libraries(vcmi ${Boost_LIBRARIES} ${SDL_LIBRARY} ${ZLIB_LIBRARIES})
-
-if (NOT APPLE) # Already inside vcmiclient bundle
-    install(TARGETS vcmi DESTINATION ${LIB_DIR})
-endif()
+project(libvcmi)
+cmake_minimum_required(VERSION 2.6)
+
+include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/lib)
+include_directories(${Boost_INCLUDE_DIRS} ${SDL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR})
+
+set(lib_SRCS
+		Filesystem/CBinaryReader.cpp
+		Filesystem/CFilesystemLoader.cpp
+		Filesystem/CMemoryStream.cpp
+		Filesystem/CFileInfo.cpp
+		Filesystem/CLodArchiveLoader.cpp
+		Filesystem/CResourceLoader.cpp
+		Filesystem/CFileInputStream.cpp
+		Filesystem/CCompressedStream.cpp
+		Mapping/CCampaignHandler.cpp
+		Mapping/CMap.cpp
+		Mapping/CMapEditManager.cpp
+		Mapping/CMapInfo.cpp
+		Mapping/CMapService.cpp
+		RMG/CMapGenOptions.cpp
+		RMG/CMapGenerator.cpp
+		BattleAction.cpp
+		BattleHex.cpp
+		BattleState.cpp
+		CArtHandler.cpp
+		CBattleCallback.cpp
+		CBuildingHandler.cpp
+		CConfigHandler.cpp
+		CConsoleHandler.cpp
+		CCreatureHandler.cpp
+		CCreatureSet.cpp
+		CDefObjInfoHandler.cpp
+		CGameInterface.cpp
+		CGameState.cpp
+		CGeneralTextHandler.cpp
+		CHeroHandler.cpp
+		CLogger.cpp
+		CModHandler.cpp
+		CObjectHandler.cpp
+		CObstacleInstance.cpp
+		Connection.cpp
+		CSpellHandler.cpp
+		CThreadHelper.cpp
+		CTownHandler.cpp
+		HeroBonus.cpp
+		IGameCallback.cpp
+		JsonNode.cpp
+		NetPacksLib.cpp
+		ResourceSet.cpp
+		VCMI_Lib.cpp
+)
+
+set(lib_HEADERS
+		Filesystem/CInputStream.h
+		Filesystem/ISimpleResourceLoader.h
+		AI_Base.h
+		CondSh.h
+		ConstTransitivePtr.h
+		CRandomGenerator.h
+		CScriptingModule.h
+		CStopWatch.h
+		GameConstants.h
+		StringConstants.h
+		IGameEventsReceiver.h
+		int3.h
+		Interprocess.h
+		NetPacks.h
+		RegisterTypes.h
+		StartInfo.h
+		UnlockGuard.h
+		VCMIDirs.h
+		vcmi_endian.h
+)
+
+add_library(vcmi SHARED ${lib_SRCS} ${lib_HEADERS})
+set_target_properties(vcmi PROPERTIES XCODE_ATTRIBUTE_LD_DYLIB_INSTALL_NAME "@executable_path/libvcmi.dylib")
+target_link_libraries(vcmi ${Boost_LIBRARIES} ${SDL_LIBRARY} ${ZLIB_LIBRARIES})
+
+if (NOT APPLE) # Already inside vcmiclient bundle
+    install(TARGETS vcmi DESTINATION ${LIB_DIR})
+endif()

+ 2 - 4
lib/CObjectHandler.cpp

@@ -1971,11 +1971,9 @@ bool CGTownInstance::hasCapitol() const
 	return hasBuilt(EBuilding::CAPITOL);
 }
 CGTownInstance::CGTownInstance()
-	:IShipyard(this), IMarket(this)
+	:IShipyard(this), IMarket(this), town(nullptr), builded(0), destroyed(0), identifier(0), alignment(0xff)
 {
-	builded=-1;
-	destroyed=-1;
-	town=NULL;
+
 }
 
 CGTownInstance::~CGTownInstance()

+ 122 - 0
lib/CRandomGenerator.h

@@ -0,0 +1,122 @@
+
+/*
+ * CRandomGenerator.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 <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int_distribution.hpp>
+#include <boost/random/uniform_real_distribution.hpp>
+#include <boost/random/variate_generator.hpp>
+
+typedef boost::mt19937 TGenerator;
+typedef boost::random::uniform_int_distribution<int> TIntDist;
+typedef boost::random::uniform_real_distribution<double> TRealDist;
+typedef boost::variate_generator<TGenerator &, TIntDist> TRandI;
+typedef boost::variate_generator<TGenerator &, TRealDist> TRand;
+
+/**
+ * The random generator randomly generates integers and real numbers("doubles") between
+ * a given range. This is a header only class and mainly a wrapper for
+ * convenient usage of the boost random API.
+ */
+class CRandomGenerator
+{
+public:
+	/**
+	 * Constructor. Seeds the generator with the current time by default.
+	 */
+	CRandomGenerator() 
+	{
+		gen.seed(std::time(nullptr)); 
+	}
+
+	/**
+	 * Seeds the generator with the given value.
+	 *
+	 * @param value the random seed
+	 */
+	void seed(int value)
+	{
+		gen.seed(value);
+	}
+
+	/**
+	 * Gets a generator which generates integers in the given range.
+	 *
+	 * Example how to use:
+	 * @code
+	 * TRandI rand = getRangeI(0, 10);
+	 * int a = rand(); // with the operator() the next value can be obtained
+	 * int b = rand(); // you can generate more values
+	 * @endcode
+	 *
+	 * @param lower the lower boundary
+	 * @param upper the upper boundary
+	 * @return the generator which can be used to generate integer numbers
+	 */
+	TRandI getRangeI(int lower, int upper)
+	{
+		TIntDist range(lower, upper);
+		return TRandI(gen, range);
+	}
+	
+	/**
+	 * Gets a integer in the given range. In comparison to getRangeI it's
+	 * a convenient method if you want to generate only one value in a given
+	 * range.
+	 *
+	 * @param lower the lower boundary
+	 * @param upper the upper boundary
+	 * @return the generated integer
+	 */
+	int getInteger(int lower, int upper)
+	{
+		return getRangeI(lower, upper)();
+	}
+	
+	/**
+	 * Gets a generator which generates doubles in the given range.
+	 *
+	 * Example how to use:
+	 * @code
+	 * TRand rand = getRange(23.56, 32.10);
+	 * double a = rand(); // with the operator() the next value can be obtained
+	 * double b = rand(); // you can generate more values
+	 * @endcode
+	 *
+	 * @param lower the lower boundary
+	 * @param upper the upper boundary
+	 * @return the generated double
+	 */
+	TRand getRange(double lower, double upper)
+	{
+		TRealDist range(lower, upper);
+		return TRand(gen, range);
+	}
+	
+	/**
+	 * Gets a double in the given range. In comparison to getRange it's
+	 * a convenient method if you want to generate only one value in a given
+	 * range.
+	 *
+	 * @param lower the lower boundary
+	 * @param upper the upper boundary
+	 * @return the generated double
+	 */
+	double getDouble(double lower, double upper)
+	{
+		return getRange(lower, upper)();
+	}
+
+private:
+	/** The actual boost random generator. */
+	TGenerator gen;
+};

+ 7 - 0
lib/CSpellHandler.cpp

@@ -378,3 +378,10 @@ void CSpellHandler::loadSpells()
 	risingSpells += 38, 39, 40;
 	mindSpells += 50, 59, 60, 61, 62;
 }
+
+std::vector<ui8> CSpellHandler::getDefaultAllowedSpells() const
+{
+	std::vector<ui8> allowedSpells;
+	allowedSpells.resize(GameConstants::SPELLS_QUANTITY, 1);
+	return allowedSpells;
+}

+ 7 - 0
lib/CSpellHandler.h

@@ -101,6 +101,13 @@ public:
 	std::set<TSpell> mindSpells;
 	void loadSpells();
 
+	/**
+	 * Gets a list of default allowed spells. OH3 spells are all allowed by default.
+	 *
+	 * @return a list of allowed spells, the index is the spell id and the value either 0 for not allowed or 1 for allowed
+	 */
+	std::vector<ui8> getDefaultAllowedSpells() const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & spells & damageSpells & risingSpells & mindSpells;

+ 28 - 3
lib/Mapping/CMap.cpp

@@ -6,6 +6,7 @@
 #include "../CTownHandler.h"
 #include "../CHeroHandler.h"
 #include "../CDefObjInfoHandler.h"
+#include "../CSpellHandler.h"
 
 SHeroName::SHeroName() : heroId(-1)
 {
@@ -13,8 +14,8 @@ SHeroName::SHeroName() : heroId(-1)
 }
 
 PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false),
-	aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(true),
-	generateHeroAtMainTown(true), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1)
+	aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(false),
+	generateHeroAtMainTown(false), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1)
 {
 	allowedFactions = VLC->townh->getDefaultAllowedFactions();
 }
@@ -129,6 +130,7 @@ CMapHeader::CMapHeader() : version(EMapFormat::SOD), height(72), width(72),
 	twoLevel(true), difficulty(1), levelLimit(0), howManyTeams(0), areAnyPlayers(false)
 {
 	allowedHeroes = VLC->heroh->getDefaultAllowedHeroes();
+	players.resize(GameConstants::PLAYER_LIMIT);
 }
 
 CMapHeader::~CMapHeader()
@@ -138,7 +140,9 @@ CMapHeader::~CMapHeader()
 
 CMap::CMap() : checksum(0), terrain(nullptr), grailRadious(0)
 {
-
+	allowedAbilities = VLC->heroh->getDefaultAllowedAbilities();
+	allowedArtifact = VLC->arth->getDefaultAllowedArtifacts();
+	allowedSpell = VLC->spellh->getDefaultAllowedSpells();
 }
 
 CMap::~CMap()
@@ -186,6 +190,7 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
 		}
 	}
 }
+
 void CMap::addBlockVisTiles(CGObjectInstance * obj)
 {
 	for(int fx=0; fx<8; ++fx)
@@ -286,3 +291,23 @@ void CMap::eraseArtifactInstance(CArtifactInstance * art)
 	assert(artInstances[art->id] == art);
 	artInstances[art->id].dellNull();
 }
+
+void CMap::addQuest(CGObjectInstance * quest)
+{
+	auto q = dynamic_cast<IQuestObject *>(quest);
+	q->quest->qid = quests.size();
+	quests.push_back(q->quest);
+}
+
+void CMap::initTerrain()
+{
+	terrain = new TerrainTile**[width];
+	for(int i = 0; i < width; ++i)
+	{
+		terrain[i] = new TerrainTile*[height];
+		for(int j = 0; j < height; ++j)
+		{
+			terrain[i][j] = new TerrainTile[twoLevel ? 2 : 1];
+		}
+	}
+}

+ 18 - 8
lib/Mapping/CMap.h

@@ -116,10 +116,10 @@ struct DLL_LINKAGE PlayerInfo
 	/** The list of renamed heroes. */
 	std::vector<SHeroName> heroesNames;
 
-	/** True if the player has a main town. The default value is true. */
+	/** True if the player has a main town. The default value is false. */
 	bool hasMainTown;
 
-	/** True if the main hero should be generated at the main town. The default value is true. */
+	/** True if the main hero should be generated at the main town. The default value is false. */
 	bool generateHeroAtMainTown;
 
 	/** The position of the main town. */
@@ -286,11 +286,9 @@ struct DLL_LINKAGE DisposedHero
 	}
 };
 
-/// Class which manages map events.
-
 /**
- * The map event is an event which gives or takes resources for a specific
- * amount of players and can appear regularly or once a time.
+ * The map event is an event which e.g. gives or takes resources of a specific
+ * amount to/from players and can appear regularly or once a time.
  */
 class DLL_LINKAGE CMapEvent
 {
@@ -584,7 +582,7 @@ public:
 	/** Specifies the victory condition. The default value is defeat all enemies. */
 	VictoryCondition victoryCondition;
 
-	/** A list containing information about players. */
+	/** A list containing information about players. The default size of the vector is GameConstants::PLAYER_LIMIT. */
 	std::vector<PlayerInfo> players;
 
 	/** The number of teams. */
@@ -714,10 +712,22 @@ public:
 	 */
 	void addNewArtifactInstance(CArtifactInstance * art);
 
+	/**
+	 * Adds the specified quest instance to the list of quests.
+	 *
+	 * @param quest the quest object which should be added to the list of quests
+	 */
+	void addQuest(CGObjectInstance * quest);
+
+	/**
+	 * Initializes the terrain of the map by allocating memory.
+	 */
+	void initTerrain();
+
 	/** the checksum of the map */
 	ui32 checksum;
 
-	/** a 3-dimensional array of terrain tiles, access is as follows: x, y, level */
+	/** a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground */
 	TerrainTile*** terrain;
 
 	/** list of rumors */

+ 386 - 0
lib/Mapping/CMapEditManager.cpp

@@ -0,0 +1,386 @@
+#include "StdInc.h"
+#include "CMapEditManager.h"
+
+#include "../JsonNode.h"
+#include "../Filesystem/CResourceLoader.h"
+#include "../CDefObjInfoHandler.h"
+
+const std::string TerrainViewPattern::FLIP_MODE_SAME_IMAGE = "sameImage";
+const std::string TerrainViewPattern::FLIP_MODE_DIFF_IMAGES = "diffImages";
+
+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_ANY = "?";
+
+TerrainViewPattern::TerrainViewPattern() : minPoints(0), flipMode(FLIP_MODE_SAME_IMAGE),
+	terGroup(ETerrainGroup::NORMAL)
+{
+
+}
+
+CTerrainViewPatternConfig::CTerrainViewPatternConfig()
+{
+	const JsonNode config(ResourceID("config/terrainViewPatterns.json"));
+	const std::map<std::string, ETerrainGroup::ETerrainGroup> terGroups
+			= boost::assign::map_list_of("normal", ETerrainGroup::NORMAL)("dirt", ETerrainGroup::DIRT)
+			("sand", ETerrainGroup::SAND)("water", ETerrainGroup::WATER)("rock", ETerrainGroup::ROCK);
+	BOOST_FOREACH(auto terMapping, terGroups)
+	{
+		BOOST_FOREACH(const JsonNode & ptrnNode, config[terMapping.first].Vector())
+		{
+			TerrainViewPattern pattern;
+
+			// Read pattern data
+			const JsonVector & data = ptrnNode["data"].Vector();
+			if(data.size() != 9)
+			{
+				throw std::runtime_error("Size of pattern's data vector has to be 9.");
+			}
+			for(int i = 0; i < data.size(); ++i)
+			{
+				std::string cell = data[i].String();
+				boost::algorithm::erase_all(cell, " ");
+				std::vector<std::string> rules;
+				boost::split(rules, cell, boost::is_any_of(","));
+				BOOST_FOREACH(std::string ruleStr, rules)
+				{
+					std::vector<std::string> rule;
+					boost::split(rule, ruleStr, boost::is_any_of("-"));
+					std::pair<std::string, int> pair;
+					pair.first = rule[0];
+					if(rule.size() > 1)
+					{
+						pair.second = boost::lexical_cast<int>(rule[1]);
+					}
+					else
+					{
+						pair.second = 0;
+					}
+					pattern.data[i].push_back(pair);
+				}
+			}
+
+			// Read mapping
+			std::string mappingStr = ptrnNode["mapping"].String();
+			boost::algorithm::erase_all(mappingStr, " ");
+			std::vector<std::string> mappings;
+			boost::split(mappings, mappingStr, boost::is_any_of(","));
+			BOOST_FOREACH(std::string mapping, mappings)
+			{
+				std::vector<std::string> range;
+				boost::split(range, mapping, boost::is_any_of("-"));
+				pattern.mapping.push_back(std::make_pair(boost::lexical_cast<int>(range[0]),
+					boost::lexical_cast<int>(range.size() > 1 ? range[1] : range[0])));
+			}
+
+			// Read optional attributes
+			pattern.id = ptrnNode["id"].String();
+			pattern.minPoints = static_cast<int>(ptrnNode["minPoints"].Float());
+			pattern.flipMode = ptrnNode["flipMode"].String();
+			if(pattern.flipMode.empty())
+			{
+				pattern.flipMode = TerrainViewPattern::FLIP_MODE_SAME_IMAGE;
+			}
+
+			pattern.terGroup = terMapping.second;
+			patterns[terMapping.second].push_back(pattern);
+		}
+	}
+}
+
+const std::vector<TerrainViewPattern> & CTerrainViewPatternConfig::getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const
+{
+	return patterns.find(terGroup)->second;
+}
+
+CMapEditManager::CMapEditManager(const CTerrainViewPatternConfig * terViewPatternConfig, CMap * map, int randomSeed /*= std::time(nullptr)*/)
+	: map(map), terViewPatternConfig(terViewPatternConfig)
+{
+	gen.seed(randomSeed);
+}
+
+void CMapEditManager::clearTerrain()
+{
+	for(int i = 0; i < map->width; ++i)
+	{
+		for(int j = 0; j < map->height; ++j)
+		{
+			map->terrain[i][j][0].terType = ETerrainType::WATER;
+			map->terrain[i][j][0].terView = gen.getInteger(20, 32);
+
+			if(map->twoLevel)
+			{
+				map->terrain[i][j][1].terType = ETerrainType::ROCK;
+				map->terrain[i][j][1].terView = 0;
+			}
+		}
+	}
+}
+
+void CMapEditManager::drawTerrain(ETerrainType::ETerrainType terType, int posx, int posy, int width, int height, bool underground)
+{
+	bool mapLevel = underground ? 1 : 0;
+	for(int i = posx; i < posx + width; ++i)
+	{
+		for(int j = posy; j < posy + height; ++j)
+		{
+			map->terrain[i][j][mapLevel].terType = terType;
+		}
+	}
+
+	//TODO there are situations where more tiles are affected implicitely
+	//TODO add coastal bit to extTileFlags appropriately
+
+	//updateTerrainViews(posx - 1, posy - 1, width + 2, height + 2, mapLevel);
+}
+
+void CMapEditManager::updateTerrainViews(int posx, int posy, int width, int height, int mapLevel)
+{
+	for(int i = posx; i < posx + width; ++i)
+	{
+		for(int j = posy; j < posy + height; ++j)
+		{
+			const std::vector<TerrainViewPattern> & patterns =
+					terViewPatternConfig->getPatternsForGroup(getTerrainGroup(map->terrain[i][j][mapLevel].terType));
+
+			// Detect a pattern which fits best
+			int totalPoints, bestPattern, bestFlip = -1;
+			std::string transitionReplacement;
+			for(int i = 0; i < patterns.size(); ++i)
+			{
+				const TerrainViewPattern & pattern = patterns[i];
+
+				for(int flip = 0; flip < 3; ++flip)
+				{
+					ValidationResult valRslt = validateTerrainView(i, j, mapLevel, flip > 0 ? getFlippedPattern(pattern, flip) : pattern);
+					if(valRslt.result)
+					{
+						if(valRslt.points > totalPoints)
+						{
+							totalPoints = valRslt.points;
+							bestPattern = i;
+							bestFlip = flip;
+							transitionReplacement = valRslt.transitionReplacement;
+						}
+						break;
+					}
+				}
+			}
+			if(bestPattern == -1)
+			{
+				continue;
+			}
+
+			// Get mapping
+			const TerrainViewPattern & pattern = patterns[bestPattern];
+			std::pair<int, int> mapping;
+			if(transitionReplacement.empty())
+			{
+				mapping = pattern.mapping[0];
+			}
+			else
+			{
+				mapping = transitionReplacement == TerrainViewPattern::RULE_DIRT ? pattern.mapping[0] : pattern.mapping[1];
+			}
+
+			// Set terrain view
+			if(pattern.flipMode == TerrainViewPattern::FLIP_MODE_SAME_IMAGE)
+			{
+				map->terrain[i][j][mapLevel].terView = gen.getInteger(mapping.first, mapping.second);
+				map->terrain[i][j][mapLevel].extTileFlags = bestFlip;
+			}
+			else
+			{
+				int range = (mapping.second - mapping.first) / 4;
+				map->terrain[i][j][mapLevel].terView = gen.getInteger(mapping.first + bestFlip * range,
+					mapping.first + (bestFlip + 1) * range - 1);
+				map->terrain[i][j][mapLevel].extTileFlags =	0;
+			}
+		}
+	}
+}
+
+ETerrainGroup::ETerrainGroup CMapEditManager::getTerrainGroup(ETerrainType::ETerrainType terType) const
+{
+	switch(terType)
+	{
+	case ETerrainType::DIRT:
+		return ETerrainGroup::DIRT;
+	case ETerrainType::SAND:
+		return ETerrainGroup::SAND;
+	case ETerrainType::WATER:
+		return ETerrainGroup::WATER;
+	case ETerrainType::ROCK:
+		return ETerrainGroup::ROCK;
+	default:
+		return ETerrainGroup::NORMAL;
+	}
+}
+
+CMapEditManager::ValidationResult CMapEditManager::validateTerrainView(int posx, int posy, int mapLevel, const TerrainViewPattern & pattern) const
+{
+	ETerrainType::ETerrainType centerTerType = map->terrain[posx][posy][mapLevel].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 = posx + (i % 3) - 1;
+		int cy = posy + (i / 3) - 1;
+		bool isAlien = false;
+		ETerrainType::ETerrainType terType;
+		if(cx < 0 || cx >= map->width || cy < 0 || cy >= map->height)
+		{
+			terType = centerTerType;
+		}
+		else
+		{
+			terType = map->terrain[cx][cy][mapLevel].terType;
+			if(terType != centerTerType)
+			{
+				isAlien = true;
+			}
+		}
+
+		// Validate all rules per cell
+		int topPoints = -1;
+		for(int j = 0; j < pattern.data[i].size(); ++j)
+		{
+			const std::pair<std::string, int> & rulePair = pattern.data[i][j];
+			const std::string & rule = rulePair.first;
+			bool isNative = (rule == TerrainViewPattern::RULE_NATIVE || rule == TerrainViewPattern::RULE_ANY) && !isAlien;
+			auto validationRslt = [&](bool rslt)
+			{
+				if(rslt)
+				{
+					topPoints = std::max(topPoints, rulePair.second);
+				}
+				return rslt;
+			};
+
+			// Validate cell with the ruleset of the pattern
+			bool validation;
+			if(pattern.terGroup == ETerrainGroup::NORMAL)
+			{
+				bool isDirt = (rule == TerrainViewPattern::RULE_DIRT
+						|| rule == TerrainViewPattern::RULE_TRANSITION || rule == TerrainViewPattern::RULE_ANY)
+						&& isAlien && !isSandType(terType);
+				bool isSand = (rule == TerrainViewPattern::RULE_SAND || rule == TerrainViewPattern::RULE_TRANSITION
+						|| rule == TerrainViewPattern::RULE_ANY)
+						&& isSandType(terType);
+
+				if(transitionReplacement.empty() && (rule == TerrainViewPattern::RULE_TRANSITION
+					|| rule == TerrainViewPattern::RULE_ANY) && (isDirt || isSand))
+				{
+					transitionReplacement = isDirt ? TerrainViewPattern::RULE_DIRT : TerrainViewPattern::RULE_SAND;
+				}
+				validation = validationRslt((isDirt && transitionReplacement != TerrainViewPattern::RULE_SAND)
+						|| (isSand && transitionReplacement != TerrainViewPattern::RULE_DIRT)
+						|| isNative);
+			}
+			else if(pattern.terGroup == ETerrainGroup::DIRT)
+			{
+				bool isSand = rule == TerrainViewPattern::RULE_SAND && isSandType(terType);
+				bool isDirt = rule == TerrainViewPattern::RULE_DIRT && !isSandType(terType) && !isNative;
+				validation = validationRslt(rule == TerrainViewPattern::RULE_ANY || isSand || isDirt || isNative);
+			}
+			else if(pattern.terGroup == ETerrainGroup::SAND || pattern.terGroup == ETerrainGroup::WATER ||
+					pattern.terGroup == ETerrainGroup::ROCK)
+			{
+				bool isSand = rule == TerrainViewPattern::RULE_SAND && isSandType(terType) && !isNative;
+				validation = validationRslt(rule == TerrainViewPattern::RULE_ANY || isSand || isNative);
+			}
+			if(!validation)
+			{
+				return ValidationResult(false);
+			}
+		}
+
+		if(topPoints == -1)
+		{
+			return ValidationResult(false);
+		}
+		else
+		{
+			totalPoints += topPoints;
+		}
+	}
+
+	if(pattern.minPoints > totalPoints)
+	{
+		return ValidationResult(false);
+	}
+
+	return ValidationResult(true, totalPoints, transitionReplacement);
+}
+
+bool CMapEditManager::isSandType(ETerrainType::ETerrainType terType) const
+{
+	switch(terType)
+	{
+	case ETerrainType::WATER:
+	case ETerrainType::SAND:
+	case ETerrainType::ROCK:
+		return true;
+	default:
+		return false;
+	}
+}
+
+TerrainViewPattern CMapEditManager::getFlippedPattern(const TerrainViewPattern & pattern, int flip) const
+{
+	if(flip == 0)
+	{
+		return pattern;
+	}
+
+	TerrainViewPattern ret = pattern;
+	if(flip == FLIP_PATTERN_HORIZONTAL || flip == FLIP_PATTERN_BOTH)
+	{
+		for(int i = 0; i < 3; ++i)
+		{
+			int y = i * 3;
+			std::swap(ret.data[y], ret.data[y + 2]);
+		}
+	}
+	if(flip == FLIP_PATTERN_VERTICAL || flip == FLIP_PATTERN_BOTH)
+	{
+		for(int i = 0; i < 3; ++i)
+		{
+			std::swap(ret.data[i], ret.data[6 + i]);
+		}
+	}
+
+	return ret;
+}
+
+void CMapEditManager::insertObject(CGObjectInstance * obj, int posx, int posy, bool underground)
+{
+	obj->pos = int3(posx, posy, underground ? 1 : 0);
+	obj->id = map->objects.size();
+	map->objects.push_back(obj);
+	if(obj->ID == Obj::TOWN)
+	{
+		map->towns.push_back(static_cast<CGTownInstance *>(obj));
+	}
+	if(obj->ID == Obj::HERO)
+	{
+		map->heroes.push_back(static_cast<CGHeroInstance*>(obj));
+	}
+	map->addBlockVisTiles(obj);
+}
+
+CMapEditManager::ValidationResult::ValidationResult(bool result, int points /*= 0*/, const std::string & transitionReplacement /*= ""*/)
+	: result(result), points(points), transitionReplacement(transitionReplacement)
+{
+
+}

+ 264 - 0
lib/Mapping/CMapEditManager.h

@@ -0,0 +1,264 @@
+
+/*
+ * CMapEditManager.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 "../CRandomGenerator.h"
+#include "CMap.h"
+
+class CGObjectInstance;
+
+namespace ETerrainGroup
+{
+	/**
+	 * This enumeration lists terrain groups which differ in the terrain view frames alignment.
+	 */
+	enum ETerrainGroup
+	{
+		NORMAL,
+		DIRT,
+		SAND,
+		WATER,
+		ROCK
+	};
+}
+
+/**
+ * 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 TerrainViewPattern
+{
+	/** Constant for the flip mode same image. Pattern will be flipped and the same image will be used(which is given in the mapping). */
+	static const std::string FLIP_MODE_SAME_IMAGE;
+
+	/** 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 type is required. */
+	static const std::string RULE_NATIVE;
+
+	/** Constant for the rule any, meaning a native type, dirty OR sandy border is required. */
+	static const std::string RULE_ANY;
+
+	/**
+	 * Default constructor.
+	 */
+	TerrainViewPattern();
+
+	/**
+	 * The pattern data.
+	 *
+	 * It 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::pair   -> combination of the name of the rule and a optional number of points
+	 */
+	std::array<std::vector<std::pair<std::string, int> >, 9> 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;
+
+	/** The minimum points to reach to to validate the pattern successfully. */
+	int minPoints;
+
+	/** Describes if flipping is required and which mapping should be used. */
+	std::string flipMode;
+
+	/** The terrain group to which the pattern belongs to. */
+	ETerrainGroup::ETerrainGroup terGroup;
+};
+
+/**
+ * The terrain view pattern config loads pattern data from the filesystem.
+ */
+class CTerrainViewPatternConfig
+{
+public:
+	/**
+	 * Constructor. Initializes the patterns data.
+	 */
+	CTerrainViewPatternConfig();
+
+	/**
+	 * Gets the patterns for a specific group of terrain.
+	 *
+	 * @param terGroup the terrain group e.g. normal for grass, lava,... OR dirt OR sand,...
+	 * @return a vector containing patterns
+	 */
+	const std::vector<TerrainViewPattern> & getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const;
+
+private:
+	/** The patterns data. */
+	std::map<ETerrainGroup::ETerrainGroup, std::vector<TerrainViewPattern> > patterns;
+};
+
+/**
+ * The map edit manager provides functionality for drawing terrain and placing
+ * objects on the map.
+ *
+ * TODO add undo / selection functionality for the map editor
+ */
+class CMapEditManager
+{
+public:
+	/**
+	 * Constructor. The map object / terrain data has to be initialized.
+	 *
+	 * @param terViewPatternConfig the terrain view pattern config
+	 * @param map the map object which should be edited
+	 * @param randomSeed optional. the seed which is used for generating randomly terrain views
+	 */
+	CMapEditManager(const CTerrainViewPatternConfig * terViewPatternConfig, CMap * map, int randomSeed = std::time(nullptr));
+
+	/**
+	 * Clears the terrain. The free level is filled with water and the
+	 * underground level with rock.
+	 */
+	void clearTerrain();
+
+	/**
+	 * Draws terrain.
+	 *
+	 * @param terType the type of the terrain to draw
+	 * @param posx the x coordinate
+	 * @param posy the y coordinate
+	 * @param width the height of the terrain to draw
+	 * @param height the width of the terrain to draw
+	 * @param underground true if you want to draw at the underground, false if open
+	 */
+	void drawTerrain(ETerrainType::ETerrainType terType, int posx, int posy, int width, int height, bool underground);
+
+	/**
+	 * Inserts an object.
+	 *
+	 * @param obj the object to insert
+	 * @param posx the x coordinate
+	 * @param posy the y coordinate
+	 * @param underground true if you want to draw at the underground, false if open
+	 */
+	void insertObject(CGObjectInstance * obj, int posx, int posy, bool underground);
+
+private:
+	/**
+	 * The validation result struct represents the result of a pattern validation.
+	 */
+	struct ValidationResult
+	{
+		/**
+		 * Constructor.
+		 *
+		 * @param result the result of the validation either true or false
+		 * @param points optional. the points which were achieved with that pattern
+		 * @param transitionReplacement optional. the replacement of a T rule, either D or S
+		 */
+		ValidationResult(bool result, int points = 0, const std::string & transitionReplacement = "");
+
+		/** The result of the validation. */
+		bool result;
+
+		/** The points which were achieved with that pattern. */
+		int points;
+
+		/** The replacement of a T rule, either D or S. */
+		std::string transitionReplacement;
+	};
+
+	/**
+	 * Updates the terrain view ids in the specified area.
+	 *
+	 * @param posx the x coordinate
+	 * @param posy the y coordinate
+	 * @param width the height of the terrain to update
+	 * @param height the width of the terrain to update
+	 * @param mapLevel the map level, 0 for open and 1 for underground
+	 */
+	void updateTerrainViews(int posx, int posy, int width, int height, int mapLevel);
+
+	/**
+	 * Gets the terrain group by the terrain type number.
+	 *
+	 * @param terType the terrain type
+	 * @return the terrain group
+	 */
+	ETerrainGroup::ETerrainGroup getTerrainGroup(ETerrainType::ETerrainType terType) const;
+
+	/**
+	 * Validates the terrain view of the given position and with the given pattern.
+	 *
+	 * @param posx the x position
+	 * @param posy the y position
+	 * @param mapLevel the map level, 0 for open and 1 for underground
+	 * @param pattern the pattern to validate the terrain view with
+	 * @return a validation result struct
+	 */
+	ValidationResult validateTerrainView(int posx, int posy, int mapLevel, const TerrainViewPattern & pattern) const;
+
+	/**
+	 * Tests whether the given terrain type is a sand type. Sand types are: Water, Sand and Rock
+	 *
+	 * @param terType the terrain type to test
+	 * @return true if the terrain type is a sand type, otherwise false
+	 */
+	bool isSandType(ETerrainType::ETerrainType terType) const;
+
+	/**
+	 * Gets a flipped pattern.
+	 *
+	 * @param pattern the original pattern to flip
+	 * @param flip the flip mode value, see FLIP_PATTERN_* constants for details
+	 * @return the flipped pattern
+	 */
+	TerrainViewPattern getFlippedPattern(const TerrainViewPattern & pattern, int flip) const;
+
+	/** Constant for flipping a pattern horizontally. */
+	static const int FLIP_PATTERN_HORIZONTAL = 1;
+
+	/** Constant for flipping a pattern vertically. */
+	static const int FLIP_PATTERN_VERTICAL = 2;
+
+	/** Constant for flipping a pattern horizontally and vertically. */
+	static const int FLIP_PATTERN_BOTH = 3;
+
+	/** The map object to edit. */
+	CMap * map;
+
+	/** The random number generator. */
+	CRandomGenerator gen;
+
+	/** The terrain view pattern config. */
+	const CTerrainViewPatternConfig * terViewPatternConfig;
+};

+ 1 - 1
lib/Mapping/CMapInfo.h

@@ -40,6 +40,6 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int Version)
 	{
 		h & mapHeader & campaignHeader & scenarioOpts & fileURI & date & playerAmnt & humanPlayers;
-		h & actualHumanPlayers;
+		h & actualHumanPlayers & isRandomMap;
 	}
 };

+ 9 - 68
lib/Mapping/CMapService.cpp

@@ -173,7 +173,7 @@ void CMapLoaderH3M::init()
 	for(int f = 0; f < map->objects.size(); ++f)
 	{
 		if(!map->objects[f]->defInfo) continue;
-		addBlockVisibleTiles(map->objects[f]);
+		map->addBlockVisTiles(map->objects[f]);
 	}
 	times.push_back(MapLoadingTime("blocked/visitable tiles", sw.getDiff()));
 
@@ -231,8 +231,7 @@ void CMapLoaderH3M::readHeader()
 
 void CMapLoaderH3M::readPlayerInfo()
 {
-	mapHeader->players.resize(8);
-	for(int i = 0; i < 8; ++i)
+	for(int i = 0; i < mapHeader->players.size(); ++i)
 	{
 		mapHeader->players[i].canHumanPlay = static_cast<bool>(buffer[pos++]);
 		mapHeader->players[i].canComputerPlay = static_cast<bool>(buffer[pos++]);
@@ -865,7 +864,7 @@ CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/)
 		a = new CArtifactInstance();
 	}
 
-	addNewArtifactInstance(a);
+	map->addNewArtifactInstance(a);
 
 	//TODO make it nicer
 	if(a->artType && a->artType->constituents)
@@ -873,31 +872,16 @@ CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/)
 		CCombinedArtifactInstance * comb = dynamic_cast<CCombinedArtifactInstance *>(a);
 		BOOST_FOREACH(CCombinedArtifactInstance::ConstituentInfo & ci, comb->constituentsInfo)
 		{
-			addNewArtifactInstance(ci.art);
+			map->addNewArtifactInstance(ci.art);
 		}
 	}
 
 	return a;
 }
 
-void CMapLoaderH3M::addNewArtifactInstance(CArtifactInstance * art)
-{
-	art->id = map->artInstances.size();
-	map->artInstances.push_back(art);
-}
-
 void CMapLoaderH3M::readTerrain()
 {
-	// Allocate memory for terrain data
-	map->terrain = new TerrainTile**[map->width];
-	for(int ii = 0; ii < map->width; ii++)
-	{
-		map->terrain[ii] = new TerrainTile*[map->height];
-		for(int jj = 0; jj < map->height; jj++)
-		{
-			map->terrain[ii][jj] = new TerrainTile[map->twoLevel ? 2 : 1];
-		}
-	}
+	map->initTerrain();
 
 	// Read terrain
 	for(int a = 0; a < 2; ++a)
@@ -1276,7 +1260,7 @@ void CMapLoaderH3M::readObjects()
 		case Obj::SEER_HUT:
 			{
 				nobj = readSeerHut();
-				addQuest(nobj);
+				map->addQuest(nobj);
 				break;
 			}
 		case Obj::WITCH_HUT: 
@@ -1584,7 +1568,7 @@ void CMapLoaderH3M::readObjects()
 	case Obj::QUEST_GUARD:
 			{
 				CGQuestGuard * guard = new CGQuestGuard();
-				addQuest(guard);
+				map->addQuest(guard);
 				readQuest(guard);
 				nobj = guard;
 				break;
@@ -1672,13 +1656,13 @@ void CMapLoaderH3M::readObjects()
 		case Obj::BORDERGUARD:
 			{
 				nobj = new CGBorderGuard();
-				addQuest(nobj);
+				map->addQuest(nobj);
 				break;
 			}
 		case Obj::BORDER_GATE:
 			{
 				nobj = new CGBorderGate();
-				addQuest (nobj);
+				map->addQuest (nobj);
 				break;
 			}
 		case Obj::EYE_OF_MAGI: 
@@ -2244,17 +2228,9 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard)
 	guard->quest->isCustomComplete = guard->quest->completedText.size() > 0;
 }
 
-void CMapLoaderH3M::addQuest(CGObjectInstance * quest)
-{
-	auto q = dynamic_cast<IQuestObject *>(quest);
-	q->quest->qid = map->quests.size();
-	map->quests.push_back(q->quest);
-}
-
 CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 {
 	CGTownInstance * nt = new CGTownInstance();
-	nt->identifier = 0;
 	if(map->version > EMapFormat::ROE)
 	{
 		nt->identifier = read_le_u32(buffer + pos);
@@ -2421,16 +2397,8 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 		nt->alignment = buffer[pos];
 		++pos;
 	}
-	else
-	{
-		nt->alignment = 0xff;
-	}
 	pos += 3;
 
-	nt->builded = 0;
-	nt->destroyed = 0;
-	nt->garrisonHero = nullptr;
-
 	return nt;
 }
 
@@ -2549,33 +2517,6 @@ void CMapLoaderH3M::readEvents()
 	}
 }
 
-void CMapLoaderH3M::addBlockVisibleTiles(CGObjectInstance * obj)
-{
-	for(int fx = 0; fx < 8; ++fx)
-	{
-		for(int fy = 0; fy < 6; ++fy)
-		{
-			int xVal = obj->pos.x + fx - 7;
-			int yVal = obj->pos.y + fy - 5;
-			int zVal = obj->pos.z;
-			if(xVal >= 0 && xVal < map->width && yVal >= 0 && yVal < map->height)
-			{
-				TerrainTile & curt = map->terrain[xVal][yVal][zVal];
-				if(((obj->defInfo->visitMap[fy] >> (7 - fx)) & 1))
-				{
-					curt.visitableObjects.push_back(obj);
-					curt.visitable = true;
-				}
-				if(!((obj->defInfo->blockMap[fy] >> (7 - fx)) & 1))
-				{
-					curt.blockingObjects.push_back(obj);
-					curt.blocked = true;
-				}
-			}
-		}
-	}
-}
-
 ui8 CMapLoaderH3M::reverse(ui8 arg)
 {
 	ui8 ret = 0;

+ 0 - 21
lib/Mapping/CMapService.h

@@ -225,13 +225,6 @@ private:
 	 */
 	CArtifactInstance * createArtifact(int aid, int spellID = -1);
 
-	/**
-	 * Adds the specified artifact instance to the list of artifacts of this map.
-	 *
-	 * @param art the artifact which should be added to the list of artifacts
-	 */
-	void addNewArtifactInstance(CArtifactInstance * art);
-
 	/**
 	 * Read rumors.
 	 */
@@ -288,13 +281,6 @@ private:
 	 */
 	void readQuest(IQuestObject * guard);
 
-	/**
-	 * Adds the specified quest instance to the list of quests.
-	 *
-	 * @param quest the quest object which should be added to the list of quests
-	 */
-	void addQuest(CGObjectInstance * quest);
-
 	/**
 	 * Reads a town.
 	 *
@@ -318,13 +304,6 @@ private:
 	 */
 	void readEvents();
 
-	/**
-	 * Adds object instance to block visitable tiles.
-	 *
-	 * @param obj the object to add
-	 */
-	void addBlockVisibleTiles(CGObjectInstance * obj);
-
 	/**
 	 * Reverses the input argument.
 	 *

+ 15 - 12
lib/RMG/CMapGenOptions.cpp

@@ -1,8 +1,10 @@
 #include "StdInc.h"
 #include "CMapGenOptions.h"
 
+#include "../GameConstants.h"
+
 CMapGenOptions::CMapGenOptions() : width(72), height(72), hasTwoLevels(true),
-	playersCnt(-1), teamsCnt(-1), compOnlyPlayersCnt(0), compOnlyTeamsCnt(-1),
+	playersCnt(RANDOM_SIZE), teamsCnt(RANDOM_SIZE), compOnlyPlayersCnt(0), compOnlyTeamsCnt(RANDOM_SIZE),
 	waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM)
 {
 
@@ -13,7 +15,7 @@ si32 CMapGenOptions::getWidth() const
 	return width;
 }
 
-void CMapGenOptions::setWidth(int value)
+void CMapGenOptions::setWidth(si32 value)
 {
 	if(value > 0)
 	{
@@ -21,7 +23,7 @@ void CMapGenOptions::setWidth(int value)
 	}
 	else
 	{
-		throw std::runtime_error("Map width lower than 1 not allowed.");
+		throw std::runtime_error("A map width lower than 1 is not allowed.");
 	}
 }
 
@@ -38,7 +40,7 @@ void CMapGenOptions::setHeight(si32 value)
 	}
 	else
 	{
-		throw std::runtime_error("Map height lower than 1 not allowed.");
+		throw std::runtime_error("A map height lower than 1 is not allowed.");
 	}
 }
 
@@ -59,13 +61,14 @@ si8 CMapGenOptions::getPlayersCnt() const
 
 void CMapGenOptions::setPlayersCnt(si8 value)
 {
-	if((value >= 1 && value <= 8) || value == RANDOM_SIZE)
+	if((value >= 1 && value <= GameConstants::PLAYER_LIMIT) || value == RANDOM_SIZE)
 	{
 		playersCnt = value;
 	}
 	else
 	{
-		throw std::runtime_error("Players count of RMG options should be between 1 and 8 or -1 for random.");
+		throw std::runtime_error("Players count of RMG options should be between 1 and " +
+			boost::lexical_cast<std::string>(GameConstants::PLAYER_LIMIT) + " or CMapGenOptions::RANDOM_SIZE for random.");
 	}
 }
 
@@ -83,7 +86,7 @@ void CMapGenOptions::setTeamsCnt(si8 value)
 	else
 	{
 		throw std::runtime_error("Teams count of RMG options should be between 0 and <" +
-			boost::lexical_cast<std::string>(playersCnt) + "(players count) - 1> or -1 for random.");
+			boost::lexical_cast<std::string>(playersCnt) + "(players count) - 1> or CMapGenOptions::RANDOM_SIZE for random.");
 	}
 }
 
@@ -94,15 +97,15 @@ si8 CMapGenOptions::getCompOnlyPlayersCnt() const
 
 void CMapGenOptions::setCompOnlyPlayersCnt(si8 value)
 {
-	if(value == RANDOM_SIZE || (value >= 0 && value <= 8 - playersCnt))
+	if(value == RANDOM_SIZE || (value >= 0 && value <= GameConstants::PLAYER_LIMIT - playersCnt))
 	{
 		compOnlyPlayersCnt = value;
 	}
 	else
 	{
 		throw std::runtime_error(std::string("Computer only players count of RMG options should be ") +
-			"between 0 and <8 - " + boost::lexical_cast<std::string>(playersCnt) +
-			"(playersCount)> or -1 for random.");
+			"between 0 and <GameConstants::PLAYER_LIMIT - " + boost::lexical_cast<std::string>(playersCnt) +
+			"(playersCount)> or CMapGenOptions::RANDOM_SIZE for random.");
 	}
 }
 
@@ -113,7 +116,7 @@ si8 CMapGenOptions::getCompOnlyTeamsCnt() const
 
 void CMapGenOptions::setCompOnlyTeamsCnt(si8 value)
 {
-	if(value == RANDOM_SIZE || compOnlyPlayersCnt == RANDOM_SIZE || (value >= 0 && value <= compOnlyPlayersCnt - 1))
+	if(value == RANDOM_SIZE || compOnlyPlayersCnt == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayersCnt - 1, 0)))
 	{
 		compOnlyTeamsCnt = value;
 	}
@@ -121,7 +124,7 @@ void CMapGenOptions::setCompOnlyTeamsCnt(si8 value)
 	{
 		throw std::runtime_error(std::string("Computer only teams count of RMG options should be ") +
 			"between 0 and <" + boost::lexical_cast<std::string>(compOnlyPlayersCnt) +
-			"(compOnlyPlayersCnt) - 1> or -1 for random.");
+			"(compOnlyPlayersCnt) - 1> or CMapGenOptions::RANDOM_SIZE for random.");
 	}
 }
 

+ 13 - 11
lib/RMG/CMapGenOptions.h

@@ -89,61 +89,61 @@ public:
 	void setHasTwoLevels(bool value);
 
 	/**
-	 * Gets the count of the players. The default value is -1 representing a random
+	 * Gets the count of the players. The default value is RANDOM_SIZE representing a random
 	 * player count.
 	 *
-	 * @return the count of the players ranging from 1 to 8 or -1 for random
+	 * @return the count of the players ranging from 1 to GameConstants::PLAYER_LIMIT or RANDOM_SIZE for random
 	 */
 	si8 getPlayersCnt() const;
 
 	/**
 	 * Sets the count of the players.
 	 *
-	 * @param value the count of the players ranging from 1 to 8, -1 for random
+	 * @param value the count of the players ranging from 1 to GameConstants::PLAYER_LIMIT, RANDOM_SIZE for random
 	 */
 	void setPlayersCnt(si8 value);
 
 	/**
-	 * Gets the count of the teams. The default value is -1 representing a random
+	 * Gets the count of the teams. The default value is RANDOM_SIZE representing a random
 	 * team count.
 	 *
-	 * @return the count of the teams ranging from 0 to <players count - 1> or -1 for random
+	 * @return the count of the teams ranging from 0 to <players count - 1> or RANDOM_SIZE for random
 	 */
 	si8 getTeamsCnt() const;
 
 	/**
 	 * Sets the count of the teams
 	 *
-	 * @param value the count of the teams ranging from 0 to <players count - 1>, -1 for random
+	 * @param value the count of the teams ranging from 0 to <players count - 1>, RANDOM_SIZE for random
 	 */
 	void setTeamsCnt(si8 value);
 
 	/**
 	 * Gets the count of the computer only players. The default value is 0.
 	 *
-	 * @return the count of the computer only players ranging from 0 to <8 - players count> or -1 for random
+	 * @return the count of the computer only players ranging from 0 to <GameConstants::PLAYER_LIMIT - players count> or RANDOM_SIZE for random
 	 */
 	si8 getCompOnlyPlayersCnt() const;
 
 	/**
 	 * Sets the count of the computer only players.
 	 *
-	 * @param value the count of the computer only players ranging from 0 to <8 - players count>, -1 for random
+	 * @param value the count of the computer only players ranging from 0 to <GameConstants::PLAYER_LIMIT - players count>, RANDOM_SIZE for random
 	 */
 	void setCompOnlyPlayersCnt(si8 value);
 
 	/**
-	 * Gets the count of the computer only teams. The default value is -1 representing
+	 * Gets the count of the computer only teams. The default value is RANDOM_SIZE representing
 	 * a random computer only team count.
 	 *
-	 * @return the count of the computer only teams ranging from 0 to <comp only players - 1> or -1 for random
+	 * @return the count of the computer only teams ranging from 0 to <comp only players - 1> or RANDOM_SIZE for random
 	 */
 	si8 getCompOnlyTeamsCnt() const;
 
 	/**
 	 * Sets the count of the computer only teams.
 	 *
-	 * @param value the count of the computer only teams ranging from 0 to <comp only players - 1>, -1 for random
+	 * @param value the count of the computer only teams ranging from 0 to <comp only players - 1>, RANDOM_SIZE for random
 	 */
 	void setCompOnlyTeamsCnt(si8 value);
 
@@ -213,6 +213,8 @@ public:
 	template <typename Handler>
 	void serialize(Handler & h, const int version)
 	{
+		//FIXME: Enum is not a fixed with data type. Add enum class to both enums
+		// later. For now it is ok.
 		h & width & height & hasTwoLevels & playersCnt & teamsCnt & compOnlyPlayersCnt;
 		h & compOnlyTeamsCnt & waterContent & monsterStrength;
 	}

+ 399 - 0
lib/RMG/CMapGenerator.cpp

@@ -0,0 +1,399 @@
+#include "StdInc.h"
+#include "CMapGenerator.h"
+
+#include "../Mapping/CMap.h"
+#include "../VCMI_Lib.h"
+#include "../CGeneralTextHandler.h"
+#include "../Mapping/CMapEditManager.h"
+#include "../CObjectHandler.h"
+#include "../CDefObjInfoHandler.h"
+#include "../GameConstants.h"
+#include "../CTownHandler.h"
+#include "../StringConstants.h"
+
+CMapGenerator::CMapGenerator(const CMapGenOptions & mapGenOptions, const std::map<TPlayerColor, CPlayerSettings> & players, int randomSeed) :
+	mapGenOptions(mapGenOptions), randomSeed(randomSeed), players(players)
+{
+	gen.seed(randomSeed);
+	validateOptions();
+}
+
+CMapGenerator::~CMapGenerator()
+{
+
+}
+
+std::unique_ptr<CMap> CMapGenerator::generate()
+{
+	finalizeMapGenOptions();
+
+	//TODO select a template based on the map gen options or adapt it if necessary
+
+	map = std::unique_ptr<CMap>(new CMap());
+	addHeaderInfo();
+
+	terViewPatternConfig = std::unique_ptr<CTerrainViewPatternConfig>(new CTerrainViewPatternConfig());
+	mapMgr = std::unique_ptr<CMapEditManager>(new CMapEditManager(terViewPatternConfig.get(), map.get(), randomSeed));
+	genTerrain();
+	genTowns();
+
+	return std::move(map);
+}
+
+void CMapGenerator::validateOptions() const
+{
+	int playersCnt = 0;
+	int compOnlyPlayersCnt = 0;
+	BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
+	{
+		if(pair.second.getPlayerType() == CPlayerSettings::COMP_ONLY)
+		{
+			++compOnlyPlayersCnt;
+		}
+		else
+		{
+			++playersCnt;
+		}
+	}
+	if(mapGenOptions.getPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
+	{
+		if(playersCnt != GameConstants::PLAYER_LIMIT)
+		{
+			throw std::runtime_error(std::string("If the count of players is random size, ")
+				+ "the count of the items in the players map should equal GameConstants::PLAYER_LIMIT.");
+		}
+		if(playersCnt == mapGenOptions.getPlayersCnt())
+		{
+			throw std::runtime_error(std::string("If the count of players is random size, ")
+				+ "all items in the players map should be either of player type AI or HUMAN.");
+		}
+	}
+	else
+	{
+		if(mapGenOptions.getCompOnlyPlayersCnt() != CMapGenOptions::RANDOM_SIZE)
+		{
+			if(playersCnt != mapGenOptions.getPlayersCnt() || compOnlyPlayersCnt != mapGenOptions.getCompOnlyPlayersCnt())
+			{
+				throw std::runtime_error(std::string("The count of players and computer only players in the players map ")
+					+ "doesn't conform with the specified map gen options.");
+			}
+		}
+		else
+		{
+			if(playersCnt != mapGenOptions.getPlayersCnt() || (playersCnt == mapGenOptions.getPlayersCnt()
+				&& compOnlyPlayersCnt != GameConstants::PLAYER_LIMIT - playersCnt))
+			{
+				throw std::runtime_error(std::string("If the count of players is fixed and the count of comp only players random, ")
+					+ "the items in the players map should equal GameConstants::PLAYER_LIMIT.");
+			}
+		}
+	}
+
+	if(countHumanPlayers() < 1)
+	{
+		throw std::runtime_error("1 human player is required at least");
+	}
+
+	BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
+	{
+		if(pair.first != pair.second.getColor())
+		{
+			throw std::runtime_error("The color of an item in player settings and the key of it has to be the same.");
+		}
+	}
+}
+
+void CMapGenerator::finalizeMapGenOptions()
+{
+	if(mapGenOptions.getPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
+	{
+		mapGenOptions.setPlayersCnt(gen.getInteger(countHumanPlayers(), GameConstants::PLAYER_LIMIT));
+
+		// Remove AI players only from the end of the players map if necessary
+		for(auto itrev = players.end(); itrev != players.begin();)
+		{
+			auto it = itrev;
+			--it;
+			if(players.size() == mapGenOptions.getPlayersCnt())
+			{
+				break;
+			}
+			const CPlayerSettings & pSettings = it->second;
+			if(pSettings.getPlayerType() == CPlayerSettings::AI)
+			{
+				players.erase(it);
+			}
+			else
+			{
+				--itrev;
+			}
+		}
+	}
+	if(mapGenOptions.getTeamsCnt() == CMapGenOptions::RANDOM_SIZE)
+	{
+		mapGenOptions.setTeamsCnt(gen.getInteger(0, mapGenOptions.getPlayersCnt() - 1));
+	}
+	if(mapGenOptions.getCompOnlyPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
+	{
+		mapGenOptions.setCompOnlyPlayersCnt(gen.getInteger(0, 8 - mapGenOptions.getPlayersCnt()));
+		int totalPlayersCnt = mapGenOptions.getPlayersCnt() + mapGenOptions.getCompOnlyPlayersCnt();
+
+		// Remove comp only players only from the end of the players map if necessary
+		for(auto itrev = players.end(); itrev != players.begin();)
+		{
+			auto it = itrev;
+			--it;
+			if(players.size() <= totalPlayersCnt)
+			{
+				break;
+			}
+			const CPlayerSettings & pSettings = it->second;
+			if(pSettings.getPlayerType() == CPlayerSettings::COMP_ONLY)
+			{
+				players.erase(it);
+			}
+			else
+			{
+				--itrev;
+			}
+		}
+
+		// Add some comp only players if necessary
+		int compOnlyPlayersToAdd = totalPlayersCnt - players.size();
+		for(int i = 0; i < compOnlyPlayersToAdd; ++i)
+		{
+			CPlayerSettings pSettings;
+			pSettings.setPlayerType(CPlayerSettings::COMP_ONLY);
+			pSettings.setColor(getNextPlayerColor());
+			players[pSettings.getColor()] = pSettings;
+		}
+	}
+	if(mapGenOptions.getCompOnlyTeamsCnt() == CMapGenOptions::RANDOM_SIZE)
+	{
+		mapGenOptions.setCompOnlyTeamsCnt(gen.getInteger(0, std::max(mapGenOptions.getCompOnlyPlayersCnt() - 1, 0)));
+	}
+
+	// There should be at least 2 players (1-player-maps aren't allowed)
+	if(mapGenOptions.getPlayersCnt() + mapGenOptions.getCompOnlyPlayersCnt() < 2)
+	{
+		CPlayerSettings pSettings;
+		pSettings.setPlayerType(CPlayerSettings::AI);
+		pSettings.setColor(getNextPlayerColor());
+		players[pSettings.getColor()] = pSettings;
+		mapGenOptions.setPlayersCnt(2);
+	}
+
+	// 1 team isn't allowed
+	if(mapGenOptions.getTeamsCnt() == 1 && mapGenOptions.getCompOnlyPlayersCnt() == 0)
+	{
+		mapGenOptions.setTeamsCnt(0);
+	}
+
+	if(mapGenOptions.getWaterContent() == EWaterContent::RANDOM)
+	{
+		mapGenOptions.setWaterContent(static_cast<EWaterContent::EWaterContent>(gen.getInteger(0, 2)));
+	}
+	if(mapGenOptions.getMonsterStrength() == EMonsterStrength::RANDOM)
+	{
+		mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(gen.getInteger(0, 2)));
+	}
+}
+
+std::string CMapGenerator::getMapDescription() const
+{
+	const std::string waterContentStr[3] = { "none", "normal", "islands" };
+	const std::string monsterStrengthStr[3] = { "weak", "normal", "strong" };
+
+	std::stringstream ss;
+	ss << "Map created by the Random Map Generator.\nTemplate was <MOCK>, ";
+	ss << "Random seed was " << randomSeed << ", size " << map->width << "x";
+	ss << map->height << ", levels " << (map->twoLevel ? "2" : "1") << ", ";
+	ss << "humans " << static_cast<int>(mapGenOptions.getPlayersCnt()) << ", computers ";
+	ss << static_cast<int>(mapGenOptions.getCompOnlyPlayersCnt()) << ", water " << waterContentStr[mapGenOptions.getWaterContent()];
+	ss << ", monster " << monsterStrengthStr[mapGenOptions.getMonsterStrength()] << ", second expansion map";
+
+	BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
+	{
+		const CPlayerSettings & pSettings = pair.second;
+		if(pSettings.getPlayerType() == CPlayerSettings::HUMAN)
+		{
+			ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor()] << " is human";
+		}
+		if(pSettings.getStartingTown() != CPlayerSettings::RANDOM_TOWN)
+		{
+			ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor()]
+				<< " town choice is " << ETownType::names[pSettings.getStartingTown()];
+		}
+	}
+
+	return ss.str();
+}
+
+void CMapGenerator::addPlayerInfo()
+{
+	// Calculate which team numbers exist
+	std::array<std::list<int>, 2> teamNumbers; // 0= cpu/human, 1= cpu only
+	int teamOffset = 0;
+	for(int i = 0; i < 2; ++i)
+	{
+		int playersCnt = i == 0 ? mapGenOptions.getPlayersCnt() : mapGenOptions.getCompOnlyPlayersCnt();
+		int teamsCnt = i == 0 ? mapGenOptions.getTeamsCnt() : mapGenOptions.getCompOnlyTeamsCnt();
+
+		if(playersCnt == 0)
+		{
+			continue;
+		}
+		int playersPerTeam = playersCnt /
+				(teamsCnt == 0 ? playersCnt : teamsCnt);
+		int teamsCntNorm = teamsCnt;
+		if(teamsCntNorm == 0)
+		{
+			teamsCntNorm = playersCnt;
+		}
+		for(int j = 0; j < teamsCntNorm; ++j)
+		{
+			for(int k = 0; k < playersPerTeam; ++k)
+			{
+				teamNumbers[i].push_back(j + teamOffset);
+			}
+		}
+		for(int j = 0; j < playersCnt - teamsCntNorm * playersPerTeam; ++j)
+		{
+			teamNumbers[i].push_back(j + teamOffset);
+		}
+		teamOffset += teamsCntNorm;
+	}
+
+	// Team numbers are assigned randomly to every player
+	BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
+	{
+		const CPlayerSettings & pSettings = pair.second;
+		PlayerInfo player;
+		player.canComputerPlay = true;
+		int j = pSettings.getPlayerType() == CPlayerSettings::COMP_ONLY ? 1 : 0;
+		if(j == 0)
+		{
+			player.canHumanPlay = true;
+		}
+		auto itTeam = std::next(teamNumbers[j].begin(), gen.getInteger(0, teamNumbers[j].size() - 1));
+		player.team = *itTeam;
+		teamNumbers[j].erase(itTeam);
+		map->players[pSettings.getColor()] = player;
+	}
+
+	map->howManyTeams = (mapGenOptions.getTeamsCnt() == 0 ? mapGenOptions.getPlayersCnt() : mapGenOptions.getTeamsCnt())
+			+ (mapGenOptions.getCompOnlyTeamsCnt() == 0 ? mapGenOptions.getCompOnlyPlayersCnt() : mapGenOptions.getCompOnlyTeamsCnt());
+}
+
+int CMapGenerator::countHumanPlayers() const
+{
+	return static_cast<int>(std::count_if(players.begin(), players.end(), [](const std::pair<TPlayerColor, CPlayerSettings> & pair)
+	{
+		return pair.second.getPlayerType() == CPlayerSettings::HUMAN;
+	}));
+}
+
+void CMapGenerator::genTerrain()
+{
+	map->initTerrain(); //FIXME nicer solution
+	mapMgr->clearTerrain();
+	mapMgr->drawTerrain(ETerrainType::GRASS, 10, 10, 20, 30, false);
+}
+
+void CMapGenerator::genTowns()
+{
+	//FIXME mock gen
+	const int3 townPos[2] = { int3(17, 13, 0), int3(25,13, 0) };
+	const TFaction townTypes[2] = { ETownType::CASTLE, ETownType::DUNGEON };
+
+	for(auto it = players.begin(); it != players.end(); ++it)
+	{
+		TPlayerColor owner = it->first;
+		int pos = std::distance(players.begin(), it);
+		int side = pos % 2;
+		CGTownInstance * town = new CGTownInstance();
+		town->ID = Obj::TOWN;
+		town->subID = townTypes[side];
+		town->tempOwner = owner;
+		town->defInfo = VLC->dobjinfo->gobjs[town->ID][town->subID];
+		town->builtBuildings.insert(EBuilding::FORT);
+		town->builtBuildings.insert(-50);
+		mapMgr->insertObject(town, townPos[side].x, townPos[side].y + (pos / 2) * 5, false);
+		map->players[owner].allowedFactions.clear();
+		map->players[owner].allowedFactions.insert(townTypes[side]);
+	}
+}
+
+void CMapGenerator::addHeaderInfo()
+{
+	map->version = EMapFormat::SOD;
+	map->width = mapGenOptions.getWidth();
+	map->height = mapGenOptions.getHeight();
+	map->twoLevel = mapGenOptions.getHasTwoLevels();
+	map->name = VLC->generaltexth->allTexts[740];
+	map->description = getMapDescription();
+	map->difficulty = 1;
+	addPlayerInfo();
+}
+
+TPlayerColor CMapGenerator::getNextPlayerColor() const
+{
+	for(TPlayerColor i = 0; i < GameConstants::PLAYER_LIMIT; ++i)
+	{
+		if(players.find(i) == players.end())
+		{
+			return i;
+		}
+	}
+	throw std::runtime_error("Shouldn't happen. No free player color exists.");
+}
+
+CMapGenerator::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(AI)
+{
+
+}
+
+int CMapGenerator::CPlayerSettings::getColor() const
+{
+	return color;
+}
+
+
+void CMapGenerator::CPlayerSettings::setColor(int value)
+{
+	if(value >= 0 && value < GameConstants::PLAYER_LIMIT)
+	{
+		color = value;
+	}
+	else
+	{
+		throw std::runtime_error("The color of the player is not in a valid range.");
+	}
+}
+
+int CMapGenerator::CPlayerSettings::getStartingTown() const
+{
+	return startingTown;
+}
+
+void CMapGenerator::CPlayerSettings::setStartingTown(int value)
+{
+	if(value >= -1 && value < static_cast<int>(VLC->townh->towns.size()))
+	{
+		startingTown = value;
+	}
+	else
+	{
+		throw std::runtime_error("The starting town of the player is not in a valid range.");
+	}
+}
+
+CMapGenerator::CPlayerSettings::EPlayerType CMapGenerator::CPlayerSettings::getPlayerType() const
+{
+	return playerType;
+}
+
+void CMapGenerator::CPlayerSettings::setPlayerType(EPlayerType value)
+{
+	playerType = value;
+}

+ 199 - 0
lib/RMG/CMapGenerator.h

@@ -0,0 +1,199 @@
+
+/*
+ * CMapGenerator.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 "../GameConstants.h"
+#include "CMapGenOptions.h"
+#include "../CRandomGenerator.h"
+
+class CMap;
+class CTerrainViewPatternConfig;
+class CMapEditManager;
+
+/**
+ * The map generator creates a map randomly.
+ */
+class CMapGenerator
+{
+public:
+	/**
+	 * The player settings class maps the player color, starting town and human player flag.
+	 */
+	class CPlayerSettings
+	{
+	public:
+		enum EPlayerType
+		{
+			HUMAN,
+			AI,
+			COMP_ONLY
+		};
+
+		/**
+		 * Constructor.
+		 */
+		CPlayerSettings();
+
+		/**
+		 * Gets the color of the player. The default value is 0.
+		 *
+		 * @return the color of the player ranging from 0 to GameConstants::PLAYER_LIMIT - 1
+		 */
+		int getColor() const;
+
+		/**
+		 * Sets the color of the player.
+		 *
+		 * @param value the color of the player ranging from 0 to GameConstants::PLAYER_LIMIT - 1
+		 */
+		void setColor(int value);
+
+		/**
+		 * Gets the starting town of the player. The default value is RANDOM_TOWN.
+		 *
+		 * @return the starting town of the player ranging from 0 to town max count or RANDOM_TOWN
+		 */
+		int getStartingTown() const;
+
+		/**
+		 * Sets the starting town of the player.
+		 *
+		 * @param value the starting town of the player ranging from 0 to town max count or RANDOM_TOWN
+		 */
+		void setStartingTown(int value);
+
+		/**
+		 * Gets the type of the player. The default value is EPlayerType::AI.
+		 *
+		 * @return the type of the player
+		 */
+		EPlayerType getPlayerType() const;
+
+		/**
+		 * Sets the type of the player.
+		 *
+		 * @param playerType the type of the player
+		 */
+		void setPlayerType(EPlayerType value);
+
+		/** Constant for a random town selection. */
+		static const int RANDOM_TOWN = -1;
+
+	private:
+		/** The color of the player. */
+		int color;
+
+		/** The starting town of the player. */
+		int startingTown;
+
+		/** The type of the player e.g. human, comp only,... */
+		EPlayerType playerType;
+	};
+
+	/**
+	 * Constructor.
+	 *
+	 * @param mapGenOptions these options describe how to generate the map.
+	 * @param players the random gen player settings
+	 * @param randomSeed a random seed is required to get random numbers.
+	 */
+	CMapGenerator(const CMapGenOptions & mapGenOptions, const std::map<TPlayerColor, CPlayerSettings> & players, int randomSeed);
+
+	/**
+	 * Destructor.
+	 */
+	~CMapGenerator();
+
+	/**
+	 * Generates a map.
+	 *
+	 * @return the generated map object stored in a unique ptr
+	 */
+	std::unique_ptr<CMap> generate();
+
+private:
+	/**
+	 * Validates map gen options and players options. On errors exceptions will be thrown.
+	 */
+	void validateOptions() const;
+
+	/**
+	 * Finalizes map generation options. Random sizes for various properties are
+	 * converted to fixed values.
+	 */
+	void finalizeMapGenOptions();
+
+	/**
+	 * Gets the map description of the generated map.
+	 *
+	 * @return the map description of the generated map
+	 */
+	std::string getMapDescription() const;
+
+	/**
+	 * Adds player information.(teams, colors, etc...)
+	 */
+	void addPlayerInfo();
+
+	/**
+	 * Counts the amount of human players.
+	 *
+	 * @return the amount of human players ranging from 0 to GameConstants::PLAYER_LIMIT
+	 */
+	int countHumanPlayers() const;
+
+	/**
+	 * Generate terrain.
+	 */
+	void genTerrain();
+
+	/**
+	 * Generate towns.
+	 */
+	void genTowns();
+
+	/**
+	 * Adds header info(size, description, etc...)
+	 */
+	void addHeaderInfo();
+
+	/**
+	 * Gets the next free player color.
+	 *
+	 * @return the next free player color
+	 */
+	TPlayerColor getNextPlayerColor() const;
+
+	/** The map options which describes the size of the map and contain player info. */
+	CMapGenOptions mapGenOptions;
+
+	/** The generated map. */
+	std::unique_ptr<CMap> map;
+
+	/** The random number generator. */
+	CRandomGenerator gen;
+
+	/** The random seed, it is used for the map description. */
+	int randomSeed;
+
+	/** The terrain view pattern config. */
+	std::unique_ptr<CTerrainViewPatternConfig> terViewPatternConfig;
+
+	/** The map edit manager. */
+	std::unique_ptr<CMapEditManager> mapMgr;
+
+	/** The random gen player settings. */
+	std::map<TPlayerColor, CPlayerSettings> players;
+
+	/** Typedef of the players map, so that boost foreach can be used. */
+	typedef std::map<TPlayerColor, CPlayerSettings> tPlayersMap;
+};

+ 4 - 4
lib/StartInfo.h

@@ -42,6 +42,7 @@ struct PlayerSettings
 
 	std::string name;
 	ui8 playerID; //0 - AI, non-0 serves as player id
+	bool compOnly; //true if this player is a computer only player; required for RMG
 	template <typename Handler>
 	void serialize(Handler &h, const int version)
 	{
@@ -55,13 +56,12 @@ struct PlayerSettings
 		h & name;
 		h & playerID;
 		h & team;
+		h & compOnly;
 	}
 
-	PlayerSettings()
+	PlayerSettings() : bonus(RANDOM), castle(NONE), heroPortrait(RANDOM), compOnly(false)
 	{
-		bonus = RANDOM;
-		castle = NONE;
-		heroPortrait = RANDOM;
+		
 	}
 };
 

+ 3 - 0
lib/StringConstants.h

@@ -31,6 +31,9 @@ namespace GameConstants
 	    "demoniac",  "heretic",    "deathknight", "necromancer", "warlock",      "overlord",
 	    "barbarian", "battlemage", "beastmaster", "witch",       "planeswalker", "elementalist"
 	};
+	const std::string PLAYER_COLOR_NAMES [PLAYER_LIMIT] = {
+		"red", "blue", "tan", "green", "orange", "purple", "teal", "pink"
+	};
 }
 
 namespace EAlignment

+ 5 - 0
lib/VCMI_lib.vcxproj

@@ -197,7 +197,9 @@
     <ClCompile Include="Mapping\CMap.cpp" />
     <ClCompile Include="Mapping\CMapInfo.cpp" />
     <ClCompile Include="Mapping\CMapService.cpp" />
+    <ClCompile Include="Mapping\CMapEditManager.cpp" />
     <ClCompile Include="RMG\CMapGenOptions.cpp" />
+    <ClCompile Include="RMG\CMapGenerator.cpp" />
     <ClCompile Include="HeroBonus.cpp" />
     <ClCompile Include="CBattleCallback.cpp" />
     <ClCompile Include="IGameCallback.cpp" />
@@ -239,6 +241,7 @@
     <ClInclude Include="CondSh.h" />
     <ClInclude Include="Connection.h" />
     <ClInclude Include="ConstTransitivePtr.h" />
+    <ClInclude Include="CRandomGenerator.h" />
     <ClInclude Include="CScriptingModule.h" />
     <ClInclude Include="CSpellHandler.h" />
     <ClInclude Include="CStopWatch.h" />
@@ -258,7 +261,9 @@
     <ClInclude Include="Mapping\CMap.h" />
     <ClInclude Include="Mapping\CMapInfo.h" />
     <ClInclude Include="Mapping\CMapService.h" />
+    <ClInclude Include="Mapping\CMapEditManager.h" />
     <ClInclude Include="RMG\CMapGenOptions.h" />
+    <ClInclude Include="RMG\CMapGenerator.h" />
     <ClInclude Include="GameConstants.h" />
     <ClInclude Include="HeroBonus.h" />
     <ClInclude Include="CBattleCallback.h" />