Index: source/simulation2/components/ICmpTerrainModifier.h
===================================================================
--- source/simulation2/components/ICmpTerrainModifier.h	(revision 0)
+++ source/simulation2/components/ICmpTerrainModifier.h	(working copy)
@@ -0,0 +1,35 @@
+/* Copyright (C) 2014 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef INCLUDED_ICMPTERRAINMODIFIER
+#define INCLUDED_ICMPTERRAINMODIFIER
+
+#include "simulation2/system/Interface.h"
+
+/**
+ * Handles terrain changes around an entity
+ * Currently only handles flattening on building placement.
+ */
+class ICmpTerrainModifier : public IComponent
+{
+public:
+	virtual void ApplyTerrainModification() = 0;
+	
+	DECLARE_INTERFACE_TYPE(TerrainModifier)
+};
+
+#endif // INCLUDED_ICMPTERRAINMODIFIER

Property changes on: source/simulation2/components/ICmpTerrainModifier.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: source/simulation2/components/CCmpTerrainModifier.cpp
===================================================================
--- source/simulation2/components/CCmpTerrainModifier.cpp	(revision 0)
+++ source/simulation2/components/CCmpTerrainModifier.cpp	(working copy)
@@ -0,0 +1,304 @@
+/* Copyright (C) 2013 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "precompiled.h"
+
+#include "simulation2/system/Component.h"
+#include "ICmpTerrainModifier.h"
+
+#include "graphics/Terrain.h"
+#include "ps/CLogger.h"
+#include "simulation2/MessageTypes.h"
+#include "simulation2/serialization/SerializeTemplates.h"
+
+#include "simulation2/components/ICmpFootprint.h"
+#include "simulation2/components/ICmpPosition.h"
+#include "simulation2/components/ICmpTerrain.h"
+#include "simulation2/helpers/Geometry.h"
+
+
+/**
+ * Handles terrain changes around an entity
+ * Currently only handles flattening on building placement.
+ */
+class CCmpTerrainModifier : public ICmpTerrainModifier
+{
+private:
+	// used to expand outwards.
+	struct Point {
+		Point() : X(0), Y(0), parentX(0), parentY(0), generation(1), originalHeight(0)
+		{ }
+		Point(int x, int y, u16 height) : X(x), Y(y), parentX(x), parentY(y), generation(1), originalHeight(height)
+		{}
+		Point(int x, int y, int px, int py, int Generation, u16 height) : X(x), Y(y), parentX(px), parentY(py), generation(Generation), originalHeight(height)
+		{}
+
+		int X,Y;
+		int parentX,parentY;
+		int generation;
+		u16 originalHeight;
+	};
+	
+public:
+	static void ClassInit(CComponentManager& componentManager)
+	{
+		componentManager.SubscribeToMessageType(MT_Destroy);
+	}
+
+	DEFAULT_COMPONENT_ALLOCATOR(TerrainModifier)
+
+	// Dynamic state:
+
+	static std::string GetSchema()
+	{
+		return
+			"<a:example/>"
+			"<a:help>Causes the entity to flatten the terrain when placed on the map, if possible.</a:help>"
+			"<empty/>";
+	}
+
+	virtual void Init(const CParamNode& UNUSED(paramNode))
+	{
+	}
+
+	virtual void Deinit()
+	{
+	}
+
+	template<typename S>
+	void SerializeCommon(S& UNUSED(serialize))
+	{
+	}
+
+	virtual void Serialize(ISerializer& serialize)
+	{
+		SerializeCommon(serialize);
+	}
+
+	virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
+	{
+		Init(paramNode);
+
+		SerializeCommon(deserialize);
+	}
+
+	virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
+	{
+		switch (msg.GetType())
+		{
+		case MT_Destroy:
+		{
+			break;
+		}
+		}
+	}
+	
+	// Try to modifiy the terrain around us.
+	virtual void ApplyTerrainModification()
+	{
+		FlattenTerrainAroundCenter();
+	}
+	
+	// Flatten the terrain around us.
+	void FlattenTerrainAroundCenter()
+	{
+		CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
+		if (!cmpPosition)
+			return;
+
+		CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
+		if (!cmpTerrain)
+			return;
+
+		CmpPtr<ICmpFootprint> cmpFootprint(GetEntityHandle());
+		if (!cmpFootprint)
+			return;
+		
+		// grab the shape and dimensions of the footprint
+		entity_pos_t footprintSize0, footprintSize1, footprintHeight;
+		ICmpFootprint::EShape footprintShape;
+		cmpFootprint->GetShape(footprintShape, footprintSize0, footprintSize1, footprintHeight);
+				
+		CFixedVector2D position = cmpPosition->GetPosition2D();
+		
+		fixed originX = (position.X/4);
+		fixed originY = (position.Y/4);
+		int posX = originX.ToInt_RoundToZero();
+		int posY = originY.ToInt_RoundToZero();
+				
+		// copy the heightmap
+		CTerrain* terrain = cmpTerrain->GetCTerrain();
+		ssize_t width = terrain->GetVerticesPerSide();
+		u16* heightmap = terrain->GetHeightMap();
+		
+		// we'll be hotmodifying the heightmap.
+		
+		/* Algorithm is somewhat simple:
+		 * We start from the 4 vertices surrounding the center point
+		 * For each vertice
+		 *		If we've already been added to the closed list, we remove us from the open list
+		 *		Depending on the quadrant (of a circle around the center point) they are in, we add 2 points that go away from the center
+		 */
+#define tou16(a) (a*(int)HEIGHT_UNITS_PER_METRE).ToInt_RoundToNearest();
+
+		u16 originalHeight = tou16(terrain->GetExactGroundLevelFixed(position.X,position.Y));
+
+		// let's actually flatten the terrain.
+		// So what we actually need here is to be quite sure we don't make a passable tile impassable.
+		// because that has all sorts of weird gameplay side-effects which we do not want.
+		// so we need to refrain from moving vertices too much.
+		
+		// define the 4 corners as base pixels. I'll assume we can't be exactly on a point here, which probably holds true.
+		std::vector<Point> openList;
+		
+		std::stack<Point> lastPoints;
+		
+		// acts as a global list of points I've studied.
+		std::map<std::pair<int,int>, Point> closedList;
+		
+		Point basePointA(posX,posY, originalHeight);
+		Point basePointB(posX,posY+1, originalHeight);
+		Point basePointC(posX+1,posY, originalHeight);
+		Point basePointD(posX+1,posY+1, originalHeight);
+		openList.push_back(basePointA);
+		openList.push_back(basePointB);
+		openList.push_back(basePointC);
+		openList.push_back(basePointD);
+		
+		
+		// for each base pixel on the openList, update
+		while (openList.size() > 0)
+		{
+			Point curPoint = openList[0];
+			// disregard already moved points or points not on the map (includes the edges, so we don't need to clamp)
+			if (closedList.find(std::make_pair(curPoint.X,curPoint.Y)) != closedList.end())
+			{
+				openList.erase(openList.begin());
+				continue;
+			}
+			if (curPoint.X <= 0 || curPoint.X >= width-1
+				|| curPoint.Y <= 0 || curPoint.Y >= width-1)
+			{
+				lastPoints.push(curPoint);
+				closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
+				openList.erase(openList.begin());
+				continue;
+			}
+
+			CFixedVector2D path(fixed::FromInt(curPoint.X) - originX,fixed::FromInt(curPoint.Y) - originY);
+			fixed distance = path.Length();
+			
+			// if we're too far away
+			if (distance > footprintSize0/2)
+			{
+				lastPoints.push(curPoint);
+				closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
+				openList.erase(openList.begin());
+				continue;
+			}
+			else if(distance >= footprintSize0/5 && abs(heightmap[curPoint.X + curPoint.Y*width] - originalHeight) < 175)
+			{
+				// here we're not under the foundation and it's still flat, so disregard going further.
+				lastPoints.push(curPoint);
+				closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
+				openList.erase(openList.begin());
+				continue;
+
+			}
+			
+			path.Normalize();
+
+#define checkHeight(X,Y,changeX,changeY) abs(heightmap[X+changeX + (Y+changeY)*width]-heightmap[X + Y*width]) < 4000
+#define addPoint(id, X,Y,changeX,changeY, generation) id = Point(X+changeX,Y+changeY,X,Y,generation, heightmap[X+changeX + (Y+changeY)*width]);\
+++addedPoints;\
+openList.push_back(id);
+			
+			int newSquares[2][2] = { {0,0}, {0,0}};
+
+			if (path.X <= fixed::Zero() && path.Y <= fixed::Zero())
+			{
+				// lower left quadrant
+				newSquares[0][0] = -1;
+				newSquares[1][1] = -1;
+			}
+			else if (path.X <= fixed::Zero() && path.Y >= fixed::Zero())
+			{
+				// upper left quadrant
+				newSquares[0][0] = -1;
+				newSquares[1][1] = +1;
+			}
+			else if (path.X >= fixed::Zero() && path.Y <= fixed::Zero())
+			{
+				// lower right quadrant
+				newSquares[0][0] = +1;
+				newSquares[1][1] = -1;
+			}
+			else if (path.X >= fixed::Zero() && path.Y >= fixed::Zero())
+			{
+				// upper right quadrant
+				newSquares[0][0] = +1;
+				newSquares[1][1] = +1;
+			}
+			int generation = 1;
+			if(distance >= footprintSize0/5 && distance <= footprintSize0/2)
+				generation = curPoint.generation+1;
+			
+			int addedPoints = 0;
+			Point newA;
+			Point newB;
+			// let's check that the slope isn't too big in that direction.
+			if (checkHeight(curPoint.X,curPoint.Y,newSquares[0][0],newSquares[0][1]))
+				addPoint(newA,curPoint.X,curPoint.Y,newSquares[0][0],newSquares[0][1], generation);
+			if (checkHeight(curPoint.X,curPoint.Y,newSquares[1][0],newSquares[1][1]))
+				addPoint(newB,curPoint.X,curPoint.Y,newSquares[1][0],newSquares[1][1], generation);
+
+#undef addPoint
+#undef checkHeight
+			if (addedPoints == 0)
+				lastPoints.push(curPoint);
+			
+			closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
+			openList.erase(openList.begin());
+		}
+		// Now for each point, we'll move towards the last parent, flattening a bit each time.
+		// we'll do it linearly for now, since we now of many points we'll go through till the center.
+		while (lastPoints.size() > 0)
+		{
+			Point curPoint = lastPoints.top();
+			lastPoints.pop();
+			int startGeneration = curPoint.generation;
+			do
+			{
+				u16 myHeight = curPoint.originalHeight;
+				// target height is original height
+				fixed coeff = fixed::FromInt(curPoint.generation) / startGeneration;
+				fixed height = coeff * (int)myHeight;
+				height += (fixed::FromInt(1) - coeff) * (int)originalHeight;
+				heightmap[width*curPoint.Y + curPoint.X] = height.ToInt_RoundToNearest();
+				
+				curPoint = closedList.find(std::make_pair(curPoint.parentX,curPoint.parentY))->second;
+			} while(curPoint.X != curPoint.parentX || curPoint.Y != curPoint.parentY);
+		}
+
+		// upload the modified heightmap.
+		terrain->SetHeightMap(heightmap);
+		cmpTerrain->MakeDirty(position.X.ToInt_RoundToZero(), position.X.ToInt_RoundToInfinity(),
+							  position.Y.ToInt_RoundToZero(), position.Y.ToInt_RoundToInfinity());
+	}
+};
+
+REGISTER_COMPONENT_TYPE(TerrainModifier)

Property changes on: source/simulation2/components/CCmpTerrainModifier.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: source/simulation2/components/ICmpTerrainModifier.cpp
===================================================================
--- source/simulation2/components/ICmpTerrainModifier.cpp	(revision 0)
+++ source/simulation2/components/ICmpTerrainModifier.cpp	(working copy)
@@ -0,0 +1,37 @@
+/* Copyright (C) 2014 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "precompiled.h"
+
+#include "ICmpTerrainModifier.h"
+
+#include "simulation2/system/InterfaceScripted.h"
+#include "simulation2/system/SimContext.h"
+
+BEGIN_INTERFACE_WRAPPER(TerrainModifier)
+DEFINE_INTERFACE_METHOD_0("ApplyTerrainModification", void, ICmpTerrainModifier, ApplyTerrainModification)
+/*DEFINE_INTERFACE_METHOD_2("CheckFoundation", std::string, ICmpObstruction, CheckFoundation_wrapper, std::string, bool)
+DEFINE_INTERFACE_METHOD_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation)
+DEFINE_INTERFACE_METHOD_2("GetEntityCollisions", std::vector<entity_id_t>, ICmpObstruction, GetEntityCollisions, bool, bool)
+DEFINE_INTERFACE_METHOD_1("SetActive", void, ICmpObstruction, SetActive, bool)
+DEFINE_INTERFACE_METHOD_3("SetDisableBlockMovementPathfinding", void, ICmpObstruction, SetDisableBlockMovementPathfinding, bool, bool, int32_t)
+DEFINE_INTERFACE_METHOD_0("GetBlockMovementFlag", bool, ICmpObstruction, GetBlockMovementFlag)
+DEFINE_INTERFACE_METHOD_1("SetControlGroup", void, ICmpObstruction, SetControlGroup, entity_id_t)
+DEFINE_INTERFACE_METHOD_0("GetControlGroup", entity_id_t, ICmpObstruction, GetControlGroup)
+DEFINE_INTERFACE_METHOD_1("SetControlGroup2", void, ICmpObstruction, SetControlGroup2, entity_id_t)
+DEFINE_INTERFACE_METHOD_0("GetControlGroup2", entity_id_t, ICmpObstruction, GetControlGroup2)
+*/END_INTERFACE_WRAPPER(TerrainModifier)

Property changes on: source/simulation2/components/ICmpTerrainModifier.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js	(revision 14730)
+++ binaries/data/mods/public/simulation/helpers/Commands.js	(working copy)
@@ -876,6 +876,12 @@
 	var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation);
 	cmpFoundation.InitialiseConstruction(player, cmd.template);
 
+
+	// If it modifies the terrain, apply the changes
+	var CmpTerrainModifier = Engine.QueryInterface(ent, IID_TerrainModifier);
+	if (CmpTerrainModifier)
+		CmpTerrainModifier.ApplyTerrainModification();
+
 	// send Metadata info if any
 	if (cmd.metadata)
 		Engine.PostMessage(ent, MT_AIMetadata, { "id": ent, "metadata" : cmd.metadata, "owner" : player } );
Index: binaries/data/mods/public/simulation/templates/template_structure.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure.xml	(revision 14730)
+++ binaries/data/mods/public/simulation/templates/template_structure.xml	(working copy)
@@ -97,6 +97,7 @@
     <BarHeight>0.6</BarHeight>
     <HeightOffset>12.0</HeightOffset>
   </StatusBars>
+  <TerrainModifier/>
   <TerritoryDecay>
     <HealthDecayRate>5</HealthDecayRate>
   </TerritoryDecay>
Index: source/simulation2/TypeList.h
===================================================================
--- source/simulation2/TypeList.h	(revision 14730)
+++ source/simulation2/TypeList.h	(working copy)
@@ -149,6 +149,9 @@
 INTERFACE(Terrain)
 COMPONENT(Terrain)
 
+INTERFACE(TerrainModifier)
+COMPONENT(TerrainModifier)
+
 INTERFACE(TerritoryInfluence)
 COMPONENT(TerritoryInfluence)
 
Index: source/simulation2/components/CCmpTemplateManager.cpp
===================================================================
--- source/simulation2/components/CCmpTemplateManager.cpp	(revision 14730)
+++ source/simulation2/components/CCmpTemplateManager.cpp	(working copy)
@@ -572,6 +572,7 @@
 	permittedComponentTypes.insert("AIProxy");
 	permittedComponentTypes.insert("RallyPoint");
 	permittedComponentTypes.insert("RallyPointRenderer");
+	permittedComponentTypes.insert("TerrainModifier");
 
 	CParamNode::LoadXMLString(out, "<Entity/>");
 	out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
Index: source/simulation2/components/CCmpTerrain.cpp
===================================================================
--- source/simulation2/components/CCmpTerrain.cpp	(revision 14730)
+++ source/simulation2/components/CCmpTerrain.cpp	(working copy)
@@ -25,6 +25,9 @@
 #include "simulation2/MessageTypes.h"
 
 #include "graphics/Terrain.h"
+#include "graphics/RenderableObject.h"
+#include "ps/Game.h"
+#include "ps/World.h"
 #include "renderer/Renderer.h"
 #include "renderer/WaterManager.h"
 #include "maths/Vector3D.h"
@@ -140,7 +143,7 @@
 
 		MakeDirty(0, 0, tiles+1, tiles+1);
 	}
-
+	
 	virtual void MakeDirty(i32 i0, i32 j0, i32 i1, i32 j1)
 	{
 		CMessageTerrainChanged msg(i0, j0, i1, j1);
