Index: binaries/data/mods/public/art/terrains/biome-alpine/alpine_grass.xml
===================================================================
--- binaries/data/mods/public/art/terrains/biome-alpine/alpine_grass.xml	(revision 18211)
+++ binaries/data/mods/public/art/terrains/biome-alpine/alpine_grass.xml	(working copy)
@@ -6,4 +6,5 @@
 		<texture name="specTex" file="types/medit_grass_field_spec.png"/>
 	</textures>
 	<material>terrain_norm_spec.xml</material>
-</terrain>
\ No newline at end of file
+	<props classes="test1, test2"/>
+</terrain>
Index: binaries/data/mods/public/art/terrains/terrain_texture.rnc
===================================================================
--- binaries/data/mods/public/art/terrains/terrain_texture.rnc	(revision 18211)
+++ binaries/data/mods/public/art/terrains/terrain_texture.rnc	(working copy)
@@ -19,6 +19,7 @@
     }? &
     element props {
         attribute groups { text }? & # comma-separated list of groups
+        attribute classes { text }? & # comma-separated list of classes
         attribute mmap {
             list {
                 xsd:integer { minInclusive = "0" }, # R
Index: binaries/data/mods/public/art/terrains/terrain_texture.rng
===================================================================
--- binaries/data/mods/public/art/terrains/terrain_texture.rng	(revision 18211)
+++ binaries/data/mods/public/art/terrains/terrain_texture.rng	(working copy)
@@ -34,6 +34,9 @@
             <attribute name="groups"/>
           </optional>
           <optional>
+            <attribute name="classes"/>
+          </optional>
+          <optional>
             <!-- comma-separated list of groups -->
             <attribute name="mmap">
               <list>
Index: binaries/data/mods/public/simulation/components/BuildRestrictions.js
===================================================================
--- binaries/data/mods/public/simulation/components/BuildRestrictions.js	(revision 18211)
+++ binaries/data/mods/public/simulation/components/BuildRestrictions.js	(working copy)
@@ -314,6 +314,13 @@
 		}
 	}
 
+	var cmpTerrain = Engine.QueryInterface(SYSTEM_ENTITY, IID_Terrain);
+	if (cmpTerrain.GetTerrainClasses(pos.x, pos.y).indexOf("test1") != -1)
+	{
+		result.message = "Get a terrain bonus here";
+		result.success = true;
+		result.bonus = 0.4;
+	}
 	// Success
 	result.success = true;
 	result.message = "";
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(revision 18211)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(working copy)
@@ -1041,6 +1041,8 @@
 
 			if (!result.success)
 				cmpVisual.SetShadingColor(1.4, 0.4, 0.4, 1);
+			else if (result.bonus)
+				cmpVisual.SetShadingColor(1 - result.bonus, 1 + result.bonus, 1, 1);
 			else
 				cmpVisual.SetShadingColor(1, 1, 1, 1);
 		}
Index: source/graphics/Terrain.cpp
===================================================================
--- source/graphics/Terrain.cpp	(revision 18211)
+++ source/graphics/Terrain.cpp	(working copy)
@@ -112,6 +112,16 @@
 	return "default";
 }
 
+std::vector<std::string> CTerrain::GetClasses(fixed x, fixed z) const
+{
+	const ssize_t i = clamp((ssize_t)(x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
+	const ssize_t j = clamp((ssize_t)(z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), (ssize_t)0, m_MapSize-2);
+	CMiniPatch* tile = GetTile(i, j);
+	if (tile && tile->GetTextureEntry())
+		return tile->GetTextureEntry()->GetProperties().GetClasses();
+	return std::vector<std::string>();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // CalcPosition: calculate the world space position of the vertex at (i,j)
 // If i,j is off the map, it acts as if the edges of the terrain are extended
Index: source/graphics/Terrain.h
===================================================================
--- source/graphics/Terrain.h	(revision 18211)
+++ source/graphics/Terrain.h	(working copy)
@@ -81,6 +81,8 @@
 
 	CStr8 GetMovementClass(ssize_t i, ssize_t j) const;
 
+	std::vector<std::string> GetClasses(fixed x, fixed z) const;
+
 	float GetVertexGroundLevel(ssize_t i, ssize_t j) const;
 	fixed GetVertexGroundLevelFixed(ssize_t i, ssize_t j) const;
 	float GetExactGroundLevel(float x, float z) const;
Index: source/graphics/TerrainProperties.cpp
===================================================================
--- source/graphics/TerrainProperties.cpp	(revision 18211)
+++ source/graphics/TerrainProperties.cpp	(working copy)
@@ -96,6 +96,7 @@
 	// Terrain Attribs
 	ATTR(mmap);
 	ATTR(groups);
+	ATTR(classes);
 	ATTR(movementclass);
 	ATTR(angle);
 	ATTR(size);
@@ -115,6 +116,17 @@
 			for(tokenizer::iterator it = tok.begin(); it != tok.end(); ++it)
 				m_Groups.push_back(g_TexMan.FindGroup(*it));
 		}
+		else if (attr.Name == attr_classes)
+		{
+			// Parse a comma-separated list of groups, add the new entry to
+			// each of them
+			m_Classes.clear();
+			boost::char_separator<char> sep(", ");
+			typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+			tokenizer tok(attr.Value, sep);
+			for (const CStr& cl : tok)
+				m_Classes.push_back(cl);
+		}
 		else if (attr.Name == attr_mmap)
 		{
 			CColor col;
Index: source/graphics/TerrainProperties.h
===================================================================
--- source/graphics/TerrainProperties.h	(revision 18211)
+++ source/graphics/TerrainProperties.h	(working copy)
@@ -66,6 +66,9 @@
 	// All terrain type groups we're a member of
 	GroupVector m_Groups;
 
+	// Classes of this terrain
+	std::vector<std::string> m_Classes;
+
 public:
 	CTerrainProperties(CTerrainPropertiesPtr parent);
 
@@ -112,6 +115,11 @@
 	{
 		return m_Groups;
 	}
+
+	std::vector<std::string> GetClasses() const
+	{
+		return m_Classes;
+	}
 };
 
 #endif
Index: source/simulation2/components/CCmpTerrain.cpp
===================================================================
--- source/simulation2/components/CCmpTerrain.cpp	(revision 18211)
+++ source/simulation2/components/CCmpTerrain.cpp	(working copy)
@@ -109,6 +109,11 @@
 		return (u16)vertices;
 	}
 
+	virtual std::vector<std::string> GetTerrainClasses(entity_pos_t x, entity_pos_t z)
+	{
+		return m_Terrain->GetClasses(x, z);
+	}
+
 	virtual CTerrain* GetCTerrain()
 	{
 		return m_Terrain;
Index: source/simulation2/components/ICmpTerrain.cpp
===================================================================
--- source/simulation2/components/ICmpTerrain.cpp	(revision 18211)
+++ source/simulation2/components/ICmpTerrain.cpp	(working copy)
@@ -24,5 +24,6 @@
 BEGIN_INTERFACE_WRAPPER(Terrain)
 DEFINE_INTERFACE_METHOD_2("GetGroundLevel", entity_pos_t, ICmpTerrain, GetGroundLevel, entity_pos_t, entity_pos_t)
 DEFINE_INTERFACE_METHOD_2("CalcNormal", CFixedVector3D, ICmpTerrain, CalcNormal, entity_pos_t, entity_pos_t)
+DEFINE_INTERFACE_METHOD_2("GetTerrainClasses", std::vector<std::string>, ICmpTerrain, GetTerrainClasses, entity_pos_t, entity_pos_t)
 DEFINE_INTERFACE_METHOD_0("GetTilesPerSide", u16, ICmpTerrain, GetTilesPerSide)
 END_INTERFACE_WRAPPER(Terrain)
Index: source/simulation2/components/ICmpTerrain.h
===================================================================
--- source/simulation2/components/ICmpTerrain.h	(revision 18211)
+++ source/simulation2/components/ICmpTerrain.h	(working copy)
@@ -40,6 +41,8 @@
 
 	virtual float GetExactGroundLevel(float x, float z) = 0;
 
+	virtual std::vector<std::string> GetTerrainClasses(entity_pos_t x, entity_pos_t z) = 0;
+
 	/**
 	 * Returns number of tiles per side on the terrain.
 	 * Return value is always non-zero.
Index: source/simulation2/system/ComponentTest.h
===================================================================
--- source/simulation2/system/ComponentTest.h	(revision 18211)
+++ source/simulation2/system/ComponentTest.h	(working copy)
@@ -207,6 +207,11 @@
 		return 50.f;
 	}
 
+	virtual std::vector<std::string> GetTerrainClasses(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z))
+	{
+		return std::vector<std::string>();
+	}
+
 	virtual u16 GetTilesPerSide()
 	{
 		return 16;
