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,381 @@
+/* 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.
+	// this uses a  weird "Expand and come back" algorithm to flatten terrain.
+	void FlattenTerrainAroundCenter()
+	{
+		CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
+		if (!cmpPosition)
+			return;
+
+		CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
+		if (!cmpTerrain)
+			return;
+
+		CmpPtr<ICmpFootprint> cmpFootprint(GetEntityHandle());
+		if (!cmpFootprint)
+			return;
+		
+		//CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
+		//StationaryOnlyObstructionFilter obstructionFilter;
+
+		// grab the shape and dimensions of the footprint
+		entity_pos_t footprintSize0, footprintSize1, footprintHeight;
+		ICmpFootprint::EShape footprintShape;
+		cmpFootprint->GetShape(footprintShape, footprintSize0, footprintSize1, footprintHeight);
+		
+		// original position
+		CFixedVector2D position = cmpPosition->GetPosition2D();
+		// scaled down
+		CFixedVector2D mapPosition(position.X/4, position.Y/4);
+		
+		fixed& originX = mapPosition.X;
+		fixed& originY = mapPosition.Y;
+		
+		int posX = originX.ToInt_RoundToZero();
+		int posY = originY.ToInt_RoundToZero();
+		
+		// get the terrain map, we will modify it directly.
+		CTerrain* terrain = cmpTerrain->GetCTerrain();
+		ssize_t width = terrain->GetVerticesPerSide();
+		u16* heightmap = terrain->GetHeightMap();
+		
+		// we'll be hotmodifying the heightmap.
+		
+		/* Okay so let's start flattening.
+		 * Algorithm is "expand and retract".
+		 * Basically we'll start from the 4 vertices around the center of the building
+		 * And we'll expand outwards until we're quite sure we'll be able to flatten terrain nicely.
+		 * Then in a second step we'll start from the outermost point and work our way back to the center, actually flattening
+		 * Then we'll smooth the result.
+		 */
+		
+		// get the height we'll try to flatten to.
+		int averageHeight = 0;
+		int divider = 0;
+		for (int YY = posY-2; YY <= posY+2; ++YY)
+			for (int XX = posX-2; XX <= posX+2; ++XX)
+				if (terrain->IsOnMap(XX*4,YY*4))
+				{
+					averageHeight += heightmap[XX + YY*width];
+					++divider;
+				}
+		u16 originalHeight = (u16)(averageHeight/divider);
+
+		
+		// points to consider
+		std::vector<Point> openList;
+		// the outermost points from which we'll start flattening
+		std::stack<Point> lastPoints;
+		// acts as a global list of points I've studied.
+		std::map<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);
+		
+		// some macros to make the code more readable below.
+#define checkHeight(X,Y,changeX,changeY) abs(heightmap[X+changeX + (Y+changeY)*width]-heightmap[X + Y*width]) < 4000
+#define checkCollision(X,Y,changeX,changeY) (!cmpObstructionManager || !cmpObstructionManager->TestLine(obstructionFilter, entity_pos_t::FromInt(X*4),entity_pos_t::FromInt(Y*4), entity_pos_t::FromInt(X*4+changeX*4),entity_pos_t::FromInt(Y*4+changeY*4), entity_pos_t::FromInt(1)))
+#define addPoint(id, X,Y,changeX,changeY, generation) id = Point(X+changeX,Y+changeY,X,Y,generation, heightmap[X+changeX + (Y+changeY)*width]);\
+openList.push_back(id);
+
+		
+		// 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(curPoint.X + curPoint.Y*width) != closedList.end())
+			{
+				openList.erase(openList.begin());
+				continue;
+			}
+			if (curPoint.X <= 0 || curPoint.X >= width-2
+				|| curPoint.Y <= 0 || curPoint.Y >= width-2)
+			{
+				lastPoints.push(curPoint);
+				closedList[curPoint.X + curPoint.Y*width] = curPoint;
+				openList.erase(openList.begin());
+				continue;
+			}
+
+			bool inFoundation = false;
+			if (footprintShape == ICmpFootprint::SQUARE)
+			{
+					fixed s, c; // sine and cosine of the Y axis rotation angle (aka the yaw)
+					fixed a = cmpPosition->GetRotation().Y;
+					sincos_approx(a, s, c);
+					CFixedVector2D u(c, -s); // unit vector along the rotated X axis
+					CFixedVector2D v(s, c); // unit vector along the rotated Z axis
+					CFixedVector2D halfSize(footprintSize0/8, footprintSize1/8);
+					if (Geometry::PointIsInSquare(CFixedVector2D(fixed::FromInt(curPoint.X), fixed::FromInt(curPoint.Y)) - CFixedVector2D(originX,originY), u, v, halfSize))
+						inFoundation = true;
+			}
+			
+			CFixedVector2D path(fixed::FromInt(curPoint.X) - originX,fixed::FromInt(curPoint.Y) - originY);
+			
+			if (curPoint.generation > 20)
+			{
+				// failed, do not save us and add the first not-in foundation point to the last point list
+				while (curPoint.generation > 2)
+					curPoint = closedList.find(curPoint.parentX + curPoint.parentY*width)->second;
+
+				lastPoints.push(curPoint);
+				closedList[curPoint.X + curPoint.Y*width] = curPoint;
+				openList.erase(openList.begin());
+				continue;
+			}
+			else if(!inFoundation && curPoint.generation == 2 && abs(heightmap[curPoint.X + curPoint.Y*width] - originalHeight) < 100)
+			{
+				// it appears this is flat, so do nothing.
+				openList.erase(openList.begin());
+				continue;
+			}
+			else if(!inFoundation && abs(heightmap[curPoint.X + curPoint.Y*width] - originalHeight) < 1000*(curPoint.generation-1))
+			{
+				// here we're not under the foundation and we know we'll be able to flatten from here, so stop.
+				lastPoints.push(curPoint);
+				closedList[curPoint.X + curPoint.Y*width] = curPoint;
+				openList.erase(openList.begin());
+				continue;
+			}
+						
+			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(!inFoundation)
+				generation = curPoint.generation+1;
+			
+			// let's check if we actually add the points, or if something prevents us from going further in that direction.
+			Point newA;
+			Point newB;
+			
+			// let's check that the slope isn't too big in that direction (meaning it's already impassable, so we won't try to flatten there)
+			// or if we run into a building, then we won't try to flatten under it either).
+			if (checkHeight(curPoint.X,curPoint.Y,newSquares[0][0],newSquares[0][1]))
+				//	&& checkCollision(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]))
+				//	&& checkCollision(curPoint.X,curPoint.Y,newSquares[1][0],newSquares[1][1]))
+				addPoint(newB,curPoint.X,curPoint.Y,newSquares[1][0],newSquares[1][1], generation);
+						
+			closedList[curPoint.X + curPoint.Y*width] = curPoint;
+			openList.erase(openList.begin());
+		}
+#undef addPoint
+#undef checkCollision
+#undef checkHeight
+
+		// 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.
+
+		int32_t minX = width, maxX = 0;
+		int32_t minY = width, maxY = 0;
+
+		while (lastPoints.size() > 0)
+		{
+			Point curPoint = lastPoints.top();
+			lastPoints.pop();
+			int startGeneration = curPoint.generation - 1;
+			u16 startHeight = curPoint.originalHeight;
+			u16 myHeight = curPoint.originalHeight;
+			bool carryOn = true;
+			while (carryOn)
+			{
+				if (curPoint.X == curPoint.parentX && curPoint.Y == curPoint.parentY)
+					carryOn = false;
+				
+				// target height is original height
+				fixed coeff = startGeneration == 0 ? fixed::Zero() : fixed::FromInt(curPoint.generation-1) / startGeneration;
+				fixed height = coeff * (int)startHeight;
+				height += (fixed::FromInt(1) - coeff) * (int)originalHeight;
+				if (myHeight-height.ToInt_RoundToNearest() > 1000)
+					height = fixed::FromInt(myHeight - 1000);
+				else if (myHeight-height.ToInt_RoundToNearest() < -1000)
+					height = fixed::FromInt(myHeight + 1000);
+					
+				myHeight = heightmap[width*curPoint.Y + curPoint.X] = height.ToInt_RoundToNearest();
+				minX =  curPoint.X < minX ? curPoint.X : minX;
+				minY =  curPoint.Y < minY ? curPoint.Y : minY;
+				maxX =  curPoint.X > maxX ? curPoint.X : maxX;
+				maxY =  curPoint.Y > maxY ? curPoint.Y : maxY;
+				curPoint = closedList.find(curPoint.parentX + curPoint.parentY*width)->second;
+			}
+		}
+		int height;
+		
+#define addHeightUnlessdiff(X,Y) height = (int)heightmap[X+(Y)*width]; \
+if (height-averageHeight < 2000 && averageHeight-height > -2000) { \
+	averageHeight += height; \
+	++divider; \
+}
+		/// smooth out of the foundations
+		for (std::map<int, Point>::iterator itr = closedList.begin(); itr != closedList.end(); itr++)
+		{
+			Point& pt = itr->second;
+			if (pt.generation == 1)
+				continue;
+			divider = 2;
+			averageHeight = (int)(heightmap[pt.X + pt.Y*width])*2;
+			addHeightUnlessdiff(pt.X+1,pt.Y);
+			addHeightUnlessdiff(pt.X,pt.Y+1);
+			addHeightUnlessdiff(pt.X,pt.Y-1);
+			addHeightUnlessdiff(pt.X-1,pt.Y);
+			heightmap[pt.X + pt.Y*width] = (u16)(averageHeight/divider);
+		}
+#undef addHeightUnlessdiff
+		
+		if (minX < maxX && minY < maxY)
+		{
+			// upload the modified heightmap.
+			terrain->SetHeightMap(heightmap);
+			cmpTerrain->MakeDirty(minX,minY,maxX,maxY);
+		}
+	}
+};
+
+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: binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml	(revision 14730)
+++ binaries/data/mods/public/simulation/templates/template_structure_resource_field.xml	(working copy)
@@ -62,6 +62,7 @@
   <StatusBars>
     <HeightOffset>8.0</HeightOffset>
   </StatusBars>
+  <TerrainModifier disable=""/>
   <TerritoryDecay disable=""/>
   <Vision>
     <Range>0</Range>
Index: binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml	(revision 14730)
+++ binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml	(working copy)
@@ -52,6 +52,7 @@
       <death>attack/destruction/building_collapse_large.xml</death>
     </SoundGroups>
   </Sound>
+  <TerrainModifier disable=""/>
   <TerritoryDecay disable=""/>
   <ProductionQueue>
     <BatchTimeModifier>0.8</BatchTimeModifier>
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);
