2
0
Эх сурвалжийг харах

- Fixed all unit test failures and a few more bugs - Simplified view generation algorithm

beegee1 12 жил өмнө
parent
commit
0f7d175896

+ 196 - 450
config/terrainViewPatterns.json

@@ -1,467 +1,213 @@
 // 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
+// ?:		D,S 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:
+// No rules
+// IV) water, rock:
 // 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
-		{
-			"id" : "s1",
-			"data" :
-			[
-				"T,N-1", "T,N-2", "T,N-3",
-				"T,N-2", "N", "N",
-				"T", "N", "N"
-			],
-			"maxPoints" : 3,
-			"mapping" : "0-3, 20-23"
-		},
-		{
-			"id" : "s2",
-			"data" :
-			[
-				"D-1,S-2,N", "N", "N",
-				"T,D-2", "N", "N",
-				"D-1,S-2,N", "N", "N"
-			],
-			"maxPoints" : 4,
-			"mapping" : "4-7, 24-27"
-		},
-		{
-			"id" : "s3",
-			"data" :
-			[
-				"D-1,S-2,N", "T,D-2", "D-1,S-2,N",
-				"N", "N", "N",
-				"N", "N", "N"
-			],
-			"maxPoints" : 4,
-			"mapping" : "8-11, 28-31"
-		},
-		{
-			"id" : "s4",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "s3-1",
-				"N", "s2-1", "T"
-			],
-			"mapping" : "12-15, 32-35",
-			"minPoints" : 2
-		},
-		{
-			"id" : "s5",
-			"data" :
-			[
-				"T", "T", "?",
-				"T", "N", "s6-1,m1-1,m2-1,N",
-				"?", "s6-1,m1-1,m2-1,N", "N"
-			],
-			"mapping" : "16-17, 36-37",
-			"minPoints" : 1
-		},
-		{
-			"id" : "s6",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "s5-1,N",
-				"N", "s5-1,N", "T"
-			],
-			"mapping" : "18-19, 38-39",
-			"minPoints" : 1
-		},
-		// Mixed transitions
-		{
-			"id" : "m1",
-			"data" :
-			[
-				"T", "N", "N",
-				"N", "N", "N",
-				"N", "N", "T"
-			],
-			"mapping" : "40, 42"
-		},
-		{
-			"id" : "m2",
-			"data" :
-			[
-				"D", "N", "N",
-				"N", "N", "N",
-				"N", "N", "S"
-			],
-			"mapping" : "41"
-		},
-		{
-			"id" : "m3",
-			"data" :
-			[
-				"N", "N", "D,N",
-				"N", "N", "D",
-				"S", "D,N", "D,N"
-			],
-			"mapping" : "43"
-		},
-		{
-			"id" : "m4",
-			"data" :
-			[
-				"N", "N", "S",
-				"N", "N", "D",
-				"D,N", "D", "D,N"
-			],
-			"mapping" : "44"
-		},
-		{
-			"id" : "m5",
-			"data" :
-			[
-				"N", "N", "D",
-				"N", "N", "D",
-				"N", "N", "S"
-			],
-			"mapping" : "45"
-		},
-		{
-			"id" : "m6",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "N",
-				"D,N", "D", "S"
-			],
-			"mapping" : "46"
-		},
-		{
-			"id" : "m7",
-			"data" :
-			[
-				"N", "N", "?",
-				"N", "N", "S",
-				"D-1,N", "D-1,N", "?"
-			],
-			"minPoints" : 1,
-			"mapping" : "47"
-		},
-		{
-			"id" : "m8",
-			"data" :
-			[
-				"N", "N", "D",
-				"N", "N", "D",
-				"?", "S", "?"
-			],
-			"mapping" : "48"
-		},
-		// No transition
-		{
-			"id" : "n1",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "N",
-				"N", "N", "N"
-			],
-			"mapping" : "49-72"
-		}
-	],
-	"dirt" :
-	[
-		// Standard transitions
-		{
-			"id" : "s1",
-			"data" :
-			[
-				"?", "S", "S",
-				"S", "N", "N",
-				"S", "N", "N"
-			],
-			"mapping" : "0-3"
-		},
-		{
-			"id" : "s2",
-			"data" :
-			[
-				"?", "D", "D",
-				"S", "N", "N",
-				"?", "D", "D"
-			],
-			"mapping" : "4-7"
-		},
-		{
-			"id" : "s3",
-			"data" :
-			[
-				"?", "S", "?",
-				"D", "N", "D",
-				"D", "N", "D"
-			],
-			"mapping" : "8-11"
-		},
-		{
-			"id" : "s4",
-			"data" :
-			[
-				"D", "D", "D",
-				"D", "N", "?",
-				"D", "?", "S"
-			],
-			"mapping" : "12-15"
-		},
-		{
-			"id" : "s5",
-			"data" :
-			[
-				"S", "S", "D",
-				"S", "N", "s6-1,D",
-				"D", "s6-1,D", "D"
-			],
-			"mapping" : "16-17",
-			"minPoints" : 1
-		},
-		{
-			"id" : "s6",
-			"data" :
-			[
-				"D", "D", "D",
-				"D", "N", "s5-1,D",
-				"D", "s5-1,D", "S"
-			],
-			"mapping" : "18-19",
-			"minPoints" : 1
-		},
-		// Mixed transition
-		{
-			"id" : "m1",
-			"data" :
-			[
-				"S", "D", "D",
-				"D", "N", "D",
-				"D", "D", "S"
-			],
-			"mapping" : "20"
-		},
-		// No transition
-		{
-			"id" : "n1",
-			"data" :
-			[
-				"D", "D", "D",
-				"D", "N", "D",
-				"D", "D", "D"
-			],
-			"mapping" : "21-44"
-		}
-	],
-	"sand" :
-	[
-		{
-			"id" : "n1",
-			"data" :
-			[
-				"?", "?", "?",
-				"?", "N", "?",
-				"?", "?", "?"
-			],
-			"mapping" : "0-23"
-		}
-	],
-	"water" :
-	[
-		// Standard transitions
-		{
-			"id" : "s1",
-			"data" :
-			[
-				"S", "S", "S",
-				"S", "N", "N",
-				"S", "N", "N"
-			],
-			"mapping" : "0-3"
-		},
-		{
-			"id" : "s2",
-			"data" :
-			[
-				"?", "N", "N",
-				"S", "N", "N",
-				"?", "N", "N"
-			],
-			"mapping" : "4-7"
-		},
-		{
-			"id" : "s3",
-			"data" :
-			[
-				"?", "S", "?",
-				"N", "N", "N",
-				"N", "N", "N"
-			],
-			"mapping" : "8-11"
-		},
-		{
-			"id" : "s4",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "N",
-				"N", "N", "S"
-			],
-			"mapping" : "12-15"
-		},
-		{
-			"id" : "s5",
-			"data" :
-			[
-				"S", "S", "N,S-1",
-				"S", "N", "N",
-				"N,S-1", "N", "N"
-			],
-			"mapping" : "16-17",
-			"maxPoints" : 1
-		},
-		{
-			"id" : "s6",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "s5-1,N",
-				"N", "s5-1,N", "S"
-			],
-			"mapping" : "18-19",
-			"minPoints" : 1
-		},
-		// No transition
-		{
-			"id" : "n1",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "N",
-				"N", "N", "N"
-			],
-			"mapping" : "20-32"
-		}
-	],
-	"rock" :
-	[
-		// No transition
-		{
-			"id" : "n1",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "N",
-				"N", "N", "N"
-			],
-			"mapping" : "0-7"
-		},
-		// Standard transitions
-		{
-			"id" : "s1",
-			"data" :
-			[
-				"?", "S", "?",
-				"S", "N", "N",
-				"?", "N", "N"
-			],
-			"mapping" : "8-15",
-			"flipMode" : "diffImages"
-		},
-		{
-			"id" : "s2",
-			"data" :
-			[
-				"?", "N", "N",
-				"S", "N", "N",
-				"?", "N", "N"
-			],
-			"mapping" : "16-19",
-			"flipMode" : "diffImages"
-		},
-		{
-			"id" : "s3",
-			"data" :
-			[
-				"?", "S", "?",
-				"N", "N", "N",
-				"N", "N", "N"
-			],
-			"mapping" : "20-23",
-			"flipMode" : "diffImages"
-		},
-		{
-			"id" : "s4",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "N",
-				"N", "N", "S"
-			],
-			"mapping" : "24-31",
-			"flipMode" : "diffImages"
-		},
-		{
-			"id" : "s5",
-			"data" :
-			[
-				"S", "S", "N",
-				"S", "N", "N",
-				"N", "N", "N"
-			],
-			"mapping" : "32-39",
-			"flipMode" : "diffImages"
-		},
-		{
-			"id" : "s6",
-			"data" :
-			[
-				"N", "N", "N",
-				"N", "N", "s5-1,N",
-				"N", "s5-1,N", "S"
-			],
-			"mapping" : "40-47",
-			"flipMode" : "diffImages",
-			"minPoints" : 1
-		}
-	]
-}
+// The order of the patterns is important, do not change!
+[
+	// Extended mixed transitions
+	{
+		"id" : "x1",
+		"data" :
+		[
+			"T", "N", "N",
+			"N", "N", "T",
+			"N", "T", "T"
+		],
+		"mapping" : { "normal" : "73,74", "dirt" : "45" }
+	},
+	{
+		"id" : "x2",
+		"data" :
+		[
+			"D", "D", "N",
+			"D", "N", "N",
+			"N", "N", "S"
+		],
+		"mapping" : { "normal" : "75" }
+	},
+	{
+		"id" : "x3",
+		"data" :
+		[
+			"S", "S", "N",
+			"S", "N", "N",
+			"N", "N", "D"
+		],
+		"mapping" : { "normal" : "76" }
+	},
+	// No transition
+	{
+		"id" : "n1",
+		"data" :
+		[
+			"N", "N", "N",
+			"N", "N", "N",
+			"N", "N", "N"
+		],
+		"mapping" : { "normal" : "49-72", "dirt" : "21-44", "sand" : "0-23", "water" : "20-32", "rock": "0-7" }
+	},
+	// Mixed transitions
+	{
+		"id" : "m1",
+		"data" :
+		[
+			"T", "N", "N",
+			"N", "N", "N",
+			"N", "N", "T"
+		],
+		"mapping" : { "normal" : "40, 42", "dirt" : "20" }
+	},
+	{
+		"id" : "m2",
+		"data" :
+		[
+			"D", "N", "N",
+			"N", "N", "N",
+			"N", "N", "S"
+		],
+		"mapping" : { "normal" : "41" }
+	},
+	{
+		"id" : "m3",
+		"data" :
+		[
+			"N", "N", "D,N",
+			"N", "N", "D",
+			"S", "D,N", "D,N"
+		],
+		"mapping" : { "normal" : "43" }
+	},
+	{
+		"id" : "m4",
+		"data" :
+		[
+			"N", "N", "S",
+			"N", "N", "D",
+			"D,N", "D", "D,N"
+		],
+		"mapping" : { "normal" : "44" }
+	},
+	{
+		"id" : "m5",
+		"data" :
+		[
+			"N", "N", "D",
+			"N", "N", "D",
+			"N", "N", "S"
+		],
+		"mapping" : { "normal" : "45" }
+	},
+	{
+		"id" : "m6",
+		"data" :
+		[
+			"N", "N", "N",
+			"N", "N", "N",
+			"D,N", "D", "S"
+		],
+		"mapping" : { "normal" : "46" }
+	},
+	{
+		"id" : "m7",
+		"data" :
+		[
+			"N", "N", "?",
+			"N", "N", "S",
+			"D-1,N", "D-1,N", "?"
+		],
+		"minPoints" : 1,
+		"mapping" : { "normal" : "47" }
+	},
+	{
+		"id" : "m8",
+		"data" :
+		[
+			"N", "N", "D-1,N",
+			"N", "N", "D-1,N",
+			"?", "S", "?"
+		],
+		"minPoints" : 1,
+		"mapping" : { "normal" : "48" }
+	},
+	// Standard transitions
+	{
+		"id" : "s1",
+		"data" :
+		[
+			"T,N-1", "T,N-2", "T,N-3",
+			"T,N-2", "N", "N",
+			"T", "N", "N"
+		],
+		"maxPoints" : 3,
+		"mapping" : { "normal" : "0-3, 20-23", "dirt" : "0-3", "water" : "0-3", "rock": "4D:8-15" }
+	},
+	{
+		"id" : "s2",
+		"data" :
+		[
+			"?", "N", "N",
+			"T", "N", "N",
+			"?", "N", "N"
+		],
+		"mapping" : { "normal" : "4-7, 24-27", "dirt" : "4-7", "water" : "4-7", "rock": "2D:16-19" }
+	},
+	{
+		"id" : "s3",
+		"data" :
+		[
+			"?", "T", "?",
+			"N", "N", "N",
+			"N", "N", "N"
+		],
+		"mapping" : { "normal" : "8-11, 28-31", "dirt" : "8-11", "water" : "8-11", "rock": "2D:20-23" }
+	},
+	{
+		"id" : "s4",
+		"data" :
+		[
+			"N", "N", "N",
+			"N", "N", "s3-1,m7-1,m8-1",
+			"N", "s2-1,m7-1,m8-1", "T"
+		],
+		"minPoints" : 2,
+		"mapping" : { "normal" : "12-15, 32-35", "dirt" : "12-15", "water" : "12-15", "rock": "4D:24-31" }
+	},
+	{
+		"id" : "s5",
+		"data" :
+		[
+			"T", "T", "?",
+			"T", "N", "s6-1,m1-1,m2-1,N",
+			"?", "s6-1,m1-1,m2-1,N", "N"
+		],
+		"minPoints" : 1,
+		"mapping" : { "normal" : "16-17, 36-37", "dirt" : "16-17", "water" : "16-17", "rock": "4D:32-39" }
+	},
+	{
+		"id" : "s6",
+		"data" :
+		[
+			"N", "N", "N",
+			"N", "N", "s5-1,N",
+			"N", "s5-1,N", "T"
+		],
+		"minPoints" : 1,
+		"mapping" : { "normal" : "18-19, 38-39", "dirt" : "18-19", "water" : "18-19", "rock": "4D:40-47" }
+	}
+]

+ 76 - 82
lib/mapping/CMapEditManager.cpp

@@ -213,8 +213,7 @@ CMapUndoManager & CMapEditManager::getUndoManager()
 	return undoManager;
 }
 
-const std::string TerrainViewPattern::FLIP_MODE_SAME_IMAGE = "sameImage";
-const std::string TerrainViewPattern::FLIP_MODE_DIFF_IMAGES = "diffImages";
+const std::string TerrainViewPattern::FLIP_MODE_DIFF_IMAGES = "D";
 
 const std::string TerrainViewPattern::RULE_DIRT = "D";
 const std::string TerrainViewPattern::RULE_SAND = "S";
@@ -222,10 +221,9 @@ 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)
+TerrainViewPattern::TerrainViewPattern() : diffImages(false), rotationTypesCount(0), minPoints(0), terGroup(ETerrainGroup::NORMAL)
 {
-
+	maxPoints = std::numeric_limits<int>::max();
 }
 
 TerrainViewPattern::WeightedRule::WeightedRule() : points(0)
@@ -252,67 +250,70 @@ CTerrainViewPatternConfig & CTerrainViewPatternConfig::get()
 CTerrainViewPatternConfig::CTerrainViewPatternConfig()
 {
 	const JsonNode config(ResourceID("config/terrainViewPatterns.json"));
-	const auto & groupMap = config.Struct();
-	BOOST_FOREACH(const auto & groupPair, groupMap)
+	const auto & patternsVec = config.Vector();
+	BOOST_FOREACH(const auto & ptrnNode, patternsVec)
 	{
-		auto terGroup = getTerrainGroup(groupPair.first);
-		BOOST_FOREACH(const JsonNode & ptrnNode, groupPair.second.Vector())
-		{
-			TerrainViewPattern pattern;
+		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)
+		// Read pattern data
+		const JsonVector & data = ptrnNode["data"].Vector();
+		assert(data.size() == 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::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> ruleParts;
+				boost::split(ruleParts, ruleStr, boost::is_any_of("-"));
+				TerrainViewPattern::WeightedRule rule;
+				rule.name = ruleParts[0];
+				assert(!rule.name.empty());
+				if(ruleParts.size() > 1)
 				{
-					std::vector<std::string> ruleParts;
-					boost::split(ruleParts, ruleStr, boost::is_any_of("-"));
-					TerrainViewPattern::WeightedRule rule;
-					rule.name = ruleParts[0];
-					if(ruleParts.size() > 1)
-					{
-						rule.points = boost::lexical_cast<int>(ruleParts[1]);
-					}
-					pattern.data[i].push_back(rule);
+					rule.points = boost::lexical_cast<int>(ruleParts[1]);
 				}
+				pattern.data[i].push_back(rule);
 			}
+		}
+
+		// Read various properties
+		pattern.id = ptrnNode["id"].String();
+		assert(!pattern.id.empty());
+		pattern.minPoints = static_cast<int>(ptrnNode["minPoints"].Float());
+		pattern.maxPoints = static_cast<int>(ptrnNode["maxPoints"].Float());
+		if(pattern.maxPoints == 0) pattern.maxPoints = std::numeric_limits<int>::max();
 
-			// Read mapping
-			std::string mappingStr = ptrnNode["mapping"].String();
+		// Read mapping
+		const auto & mappingStruct = ptrnNode["mapping"].Struct();
+		BOOST_FOREACH(const auto & mappingPair, mappingStruct)
+		{
+			TerrainViewPattern terGroupPattern = pattern;
+			auto mappingStr = mappingPair.second.String();
 			boost::algorithm::erase_all(mappingStr, " ");
+			auto colonIndex = mappingStr.find_first_of(":");
+			const auto & flipMode = mappingStr.substr(0, colonIndex);
+			terGroupPattern.diffImages = TerrainViewPattern::FLIP_MODE_DIFF_IMAGES == &(flipMode[flipMode.length() - 1]);
+			if(terGroupPattern.diffImages)
+			{
+				terGroupPattern.rotationTypesCount = boost::lexical_cast<int>(flipMode.substr(0, flipMode.length() - 1));
+				assert(terGroupPattern.rotationTypesCount == 2 || terGroupPattern.rotationTypesCount == 4);
+			}
+			mappingStr = mappingStr.substr(colonIndex + 1);
 			std::vector<std::string> mappings;
 			boost::split(mappings, mappingStr, boost::is_any_of(","));
 			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]),
+				terGroupPattern.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();
-			assert(!pattern.id.empty());
-			pattern.minPoints = static_cast<int>(ptrnNode["minPoints"].Float());
-			pattern.maxPoints = static_cast<int>(ptrnNode["maxPoints"].Float());
-			if(pattern.maxPoints == 0) pattern.maxPoints = std::numeric_limits<int>::max();
-			pattern.flipMode = ptrnNode["flipMode"].String();
-			if(pattern.flipMode.empty())
-			{
-				pattern.flipMode = TerrainViewPattern::FLIP_MODE_SAME_IMAGE;
-			}
-
-			pattern.terGroup = terGroup;
-			patterns[terGroup].push_back(pattern);
+			const auto & terGroup = getTerrainGroup(mappingPair.first);
+			terGroupPattern.terGroup = terGroup;
+			patterns[terGroup].push_back(terGroupPattern);
 		}
 	}
 }
@@ -332,23 +333,22 @@ ETerrainGroup::ETerrainGroup CTerrainViewPatternConfig::getTerrainGroup(const st
 	return it->second;
 }
 
-
 const std::vector<TerrainViewPattern> & CTerrainViewPatternConfig::getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const
 {
 	return patterns.find(terGroup)->second;
 }
 
-const TerrainViewPattern & CTerrainViewPatternConfig::getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const
+boost::optional<const TerrainViewPattern &> CTerrainViewPatternConfig::getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const
 {
 	const std::vector<TerrainViewPattern> & groupPatterns = getPatternsForGroup(terGroup);
 	BOOST_FOREACH(const TerrainViewPattern & pattern, groupPatterns)
 	{
 		if(id == pattern.id)
 		{
-			return pattern;
+			return boost::optional<const TerrainViewPattern &>(pattern);
 		}
 	}
-	throw std::runtime_error("Pattern with ID not found: " + id);
+	return boost::optional<const TerrainViewPattern &>();
 }
 
 CDrawTerrainOperation::CDrawTerrainOperation(CMap * map, const MapRect & rect, ETerrainType terType, CRandomGenerator * gen)
@@ -413,7 +413,7 @@ void CDrawTerrainOperation::updateTerrainViews(const MapRect & rect)
 					break;
 				}
 			}
-			//assert(bestPattern != -1);
+			assert(bestPattern != -1);
 			if(bestPattern == -1)
 			{
 				// This shouldn't be the case
@@ -435,15 +435,16 @@ void CDrawTerrainOperation::updateTerrainViews(const MapRect & rect)
 
 			// Set terrain view
 			auto & tile = map->getTile(int3(x, y, rect.z));
-			if(pattern.flipMode == TerrainViewPattern::FLIP_MODE_SAME_IMAGE)
+			if(!pattern.diffImages)
 			{
 				tile.terView = gen->getInteger(mapping.first, mapping.second);
 				tile.extTileFlags = valRslt.flip;
 			}
 			else
 			{
-				const int framesPerRot = 2;
-				int firstFrame = mapping.first + valRslt.flip * framesPerRot;
+				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;
 			}
@@ -522,12 +523,12 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 			TerrainViewPattern::WeightedRule rule = pattern.data[i][j];
 			if(!rule.isStandardRule())
 			{
-				if(recDepth == 0)
+				if(recDepth == 0 && map->isInTheMap(currentPos))
 				{
-					if(map->isInTheMap(currentPos) && terType == centerTerType)
+					const auto & patternForRule = CTerrainViewPatternConfig::get().getPatternById(getTerrainGroup(terType), rule.name);
+					if(patternForRule)
 					{
-						const auto & patternForRule = CTerrainViewPatternConfig::get().getPatternById(pattern.terGroup, rule.name);
-						auto rslt = validateTerrainView(currentPos, patternForRule, 1);
+						auto rslt = validateTerrainView(currentPos, *patternForRule, 1);
 						if(rslt.result) topPoints = std::max(topPoints, rule.points);
 					}
 					continue;
@@ -538,7 +539,6 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 				}
 			}
 
-			bool nativeTestOk = (rule.name == TerrainViewPattern::RULE_NATIVE || rule.name == TerrainViewPattern::RULE_ANY) && !isAlien;
 			auto applyValidationRslt = [&](bool rslt)
 			{
 				if(rslt)
@@ -550,11 +550,9 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 			// Validate cell with the ruleset of the pattern
 			if(pattern.terGroup == ETerrainGroup::NORMAL)
 			{
-				bool dirtTestOk = (rule.name == TerrainViewPattern::RULE_DIRT
-						|| rule.name == TerrainViewPattern::RULE_TRANSITION || rule.name == TerrainViewPattern::RULE_ANY)
+				bool dirtTestOk = (rule.name == TerrainViewPattern::RULE_DIRT || rule.name == TerrainViewPattern::RULE_TRANSITION)
 						&& isAlien && !isSandType(terType);
-				bool sandTestOk = (rule.name == TerrainViewPattern::RULE_SAND || rule.name == TerrainViewPattern::RULE_TRANSITION
-						|| rule.name == TerrainViewPattern::RULE_ANY)
+				bool sandTestOk = (rule.name == TerrainViewPattern::RULE_SAND || rule.name == TerrainViewPattern::RULE_TRANSITION)
 						&& isSandType(terType);
 
 				if(transitionReplacement.empty() && rule.name == TerrainViewPattern::RULE_TRANSITION
@@ -569,30 +567,26 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 				}
 				else
 				{
-					applyValidationRslt(dirtTestOk || sandTestOk || nativeTestOk);
+					bool nativeTestOk = rule.name == TerrainViewPattern::RULE_NATIVE && !isAlien;
+					applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || dirtTestOk || sandTestOk || nativeTestOk);
 				}
 			}
 			else if(pattern.terGroup == ETerrainGroup::DIRT)
 			{
-				bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && isSandType(terType);
-				bool dirtTestOk = rule.name == TerrainViewPattern::RULE_DIRT && !isSandType(terType) && !nativeTestOk;
-				applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || dirtTestOk || nativeTestOk);
-			}
-			else if(pattern.terGroup == ETerrainGroup::SAND)
-			{
-				bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && isAlien;
+				bool nativeTestOk = rule.name == TerrainViewPattern::RULE_NATIVE && !isSandType(terType);
+				bool sandTestOk = (rule.name == TerrainViewPattern::RULE_SAND || rule.name == TerrainViewPattern::RULE_TRANSITION)
+						&& isSandType(terType);
 				applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || nativeTestOk);
 			}
-			else if(pattern.terGroup == ETerrainGroup::WATER)
+			else if(pattern.terGroup == ETerrainGroup::SAND)
 			{
-				bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && terType != ETerrainType::DIRT
-						&& terType != ETerrainType::WATER;
-				applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || nativeTestOk);
+				applyValidationRslt(true);
 			}
-			else if(pattern.terGroup == ETerrainGroup::ROCK)
+			else if(pattern.terGroup == ETerrainGroup::WATER || pattern.terGroup == ETerrainGroup::ROCK)
 			{
-				bool sandTestOk = rule.name == TerrainViewPattern::RULE_SAND && terType != ETerrainType::DIRT
-						&& terType != ETerrainType::ROCK;
+				bool nativeTestOk = rule.name == TerrainViewPattern::RULE_NATIVE && !isAlien;
+				bool sandTestOk = (rule.name == TerrainViewPattern::RULE_SAND || rule.name == TerrainViewPattern::RULE_TRANSITION)
+						&& isAlien;
 				applyValidationRslt(rule.name == TerrainViewPattern::RULE_ANY || sandTestOk || nativeTestOk);
 			}
 		}

+ 7 - 14
lib/mapping/CMapEditManager.h

@@ -134,35 +134,25 @@ struct DLL_LINKAGE TerrainViewPattern
 	struct WeightedRule
 	{
 		WeightedRule();
-
 		/// Gets true if this rule is a standard rule which means that it has a value of one of the RULE_* constants.
 		bool isStandardRule() const;
 
 		/// The name of the rule. Can be any value of the RULE_* constants or a ID of a another pattern.
 		std::string name;
-
 		/// Optional. A rule can have points. Patterns may have a minimum count of points to reach to be successful.
 		int points;
 	};
 
-	/// 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;
 
@@ -189,13 +179,16 @@ struct DLL_LINKAGE TerrainViewPattern
 	/// std::vector -> size=1: typical, size=2: if this pattern should map to two different types of borders
 	/// std::pair   -> 1st value: lower range, 2nd value: upper range
 	std::vector<std::pair<int, int> > mapping;
+	/// If diffImages is true, different images/frames are used to place a rotated terrain view. If it's false
+	/// the same frame will be used and rotated.
+	bool diffImages;
+	/// The rotationTypesCount is only used if diffImages is true and holds the number how many rotation types(horizontal, etc...)
+	/// are supported.
+	int rotationTypesCount;
 
 	/// The minimum and maximum points to reach to validate the pattern successfully.
 	int minPoints, maxPoints;
 
-	/// Describes if flipping is required and which mapping should be used.
-	std::string flipMode;
-
 	ETerrainGroup::ETerrainGroup terGroup;
 };
 
@@ -206,7 +199,7 @@ public:
 	static CTerrainViewPatternConfig & get();
 
 	const std::vector<TerrainViewPattern> & getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const;
-	const TerrainViewPattern & getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const;
+	boost::optional<const TerrainViewPattern &> getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const;
 	ETerrainGroup::ETerrainGroup getTerrainGroup(const std::string & terGroup) const;
 
 private:

+ 1 - 1
test/CMapEditManagerTest.cpp

@@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain)
 
 			// Get mapping range
 			const auto & pattern = CTerrainViewPatternConfig::get().getPatternById(terGroup, id);
-			const auto & mapping = pattern.mapping;
+			const auto & mapping = (*pattern).mapping;
 
 			const auto & positionsNode = node["pos"].Vector();
 			BOOST_FOREACH(const auto & posNode, positionsNode)

BIN
test/TerrainViewTest.h3m


+ 17 - 5
test/terrainViewMappings.json

@@ -38,7 +38,7 @@
 			"pattern" : "normal.m3"
 		},
 		{
-			"pos" : [ [ 5,34,0 ] ],
+			"pos" : [ [ 5,34,0 ], [ 26,23,0 ] ],
 			"pattern" : "normal.m4"
 		},
 		{
@@ -61,9 +61,17 @@
 			"pos" : [ [ 7,23,0 ] ],
 			"pattern" : "normal.n1"
 		},
+		{
+			"pos" : [ [ 14,6,0 ] ],
+			"pattern" : "normal.x1"
+		},
+		{
+			"pos" : [ [ 26,22,0 ] ],
+			"pattern" : "normal.x2"
+		},
 		// Dirt type
 		{
-			"pos" : [ [ 22,26,0 ], [ 27,35,0 ] ],
+			"pos" : [ [ 22,26,0 ] ],
 			"pattern" : "dirt.s1"
 		},
 		{
@@ -79,7 +87,7 @@
 			"pattern" : "dirt.s4"
 		},
 		{
-			"pos" : [ [ 26,28,1 ] ],
+			"pos" : [ [ 26,28,1 ], [ 27,35,0 ] ],
 			"pattern" : "dirt.s5"
 		},
 		{
@@ -94,6 +102,10 @@
 			"pos" : [ [ 20,27,1 ] ],
 			"pattern" : "dirt.n1"
 		},
+		{
+			"pos" : [ [ 11,24,1 ] ],
+			"pattern" : "dirt.x1"
+		},
 		// Sand type
 		{
 			"pos" : [ [ 22,27,1 ] ],
@@ -134,7 +146,7 @@
 			"pattern" : "rock.n1"
 		},
 		{
-			"pos" : [ [ 11,21,1 ] ],
+			"pos" : [ [ 11,21,1 ], [ 10,22,1 ] ],
 			"pattern" : "rock.s1"
 		},
 		{
@@ -150,7 +162,7 @@
 			"pattern" : "rock.s4"
 		},
 		{
-			"pos" : [ [ 10,22,1 ] ],
+			"pos" : [ [ 12,24,1 ] ],
 			"pattern" : "rock.s5"
 		},
 		{