Index: binaries/data/mods/public/simulation/components/TurretHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/TurretHolder.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/TurretHolder.js	(working copy)
@@ -0,0 +1,148 @@
+function TurretHolder() {}
+
+TurretHolder.prototype.Schema =
+	"<zeroOrMore>" +
+		"<element>" +
+			"<anyName/>" +
+			"<interleave>" +
+				"<optional>" +
+					"<element name='Template'>" +
+						"<text/>" +
+					"</element>" +
+				"</optional>" +
+				"<element name='Offset'>" +
+					"<interleave>" +
+						"<element name='X'>" +
+							"<data type='decimal'/>" +
+						"</element>" +
+						"<element name='Y'>" +
+							"<data type='decimal'/>" +
+						"</element>" +
+						"<element name='Z'>" +
+							"<data type='decimal'/>" +
+						"</element>" +
+					"</interleave>" +
+				"</element>" +
+			"</interleave>" +
+		"</element>" +
+	"</zeroOrMore>";
+
+TurretHolder.prototype.Init = function()
+{
+	this.turrets = {};
+	var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+	cmpTimer.SetTimeout(this.entity, IID_TurretHolder, "InitReal", 0, null);
+	
+};
+
+/**
+ * The real init function. But this should not be loaded in Atlas,
+ * unless a simulation is started.
+ */
+TurretHolder.prototype.InitReal = function(msg)
+{
+	var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+	for (var positionCode in this.template)
+	{
+		if (!this.template[positionCode].Template)
+			continue;
+		var ent = Engine.AddEntity(this.template[positionCode].Template);
+		var cmpTurretOwnership = Engine.QueryInterface(ent, IID_Ownership);
+		cmpTurretOwnership.SetOwner(cmpOwnership.GetOwner());
+		this.AddTurret(positionCode, ent);
+	}
+};
+
+/**
+ * Bind an existing entity to a positionCode of this turretHolder
+ * Some components will be notified that this entity shouldn't move on its own anymore
+ */
+TurretHolder.prototype.AddTurret = function(positionCode, ent)
+{
+	if (!this.template[positionCode])
+	{
+		warn("TurretHolder.JS: entity assigned to unknown turret positionCode");
+		return;
+	}
+	if (this.turrets[positionCode])
+	{
+		warn("TurretHolder.JS: entity assigned to occupied turret positionCode");
+		return;
+	}
+
+	var cmpPosition = Engine.QueryInterface(ent, IID_Position);
+	if (cmpPosition)
+	{
+		var offset = this.template[positionCode].Offset;
+		var pos = new Vector3D(+offset.X, +offset.Y, +offset.Z);
+		cmpPosition.SetTurretParent(this.entity, pos);
+	}
+
+	var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+	if (cmpUnitAI)
+		cmpUnitAI.SetTurret(true);
+	this.turrets[positionCode] = ent;
+};
+
+/**
+ * Remove a turret from the turret holder
+ * This does not reset its position, nor does it kill the turret
+ * It just unbinds it from its turret holder
+ */
+TurretHolder.prototype.RemoveTurret = function(positionCode)
+{
+	var ent = this.turrets[positionCode];
+	if (!ent)
+		return;
+	var cmpPosition = Engine.QueryInterface(ent, IID_Position);
+	if (cmpPosition)
+		cmpPosition.SetTurretParent(INVALID_ENTITY, new Vector3D());
+	var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+	if (cmpUnitAI)
+		cmpUnitAI.SetTurret(false);
+	delete this.turrets[positionCode];
+};
+
+TurretHolder.prototype.GetAllTurretPositions = function()
+{
+	return Object.keys(this.template);
+};
+
+TurretHolder.prototype.GetOccupiedTurretPositions = function()
+{
+	return Object.keys(this.turrets);
+};
+
+TurretHolder.prototype.GetFreeTurretPositions = function()
+{
+	var r = [];
+	for (var positionCode in this.template)
+		if (!(positionCode in this.turrets))
+			r.push(positionCode);
+	return r;
+};
+
+TurretHolder.prototype.GetTurretOnPosition = function(positionCode)
+{
+	return this.turrets[positionCode];
+};
+
+TurretHolder.prototype.OnOwnershipChanged = function(msg)
+{
+	for each (var ent in this.turrets)
+	{
+		var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
+		cmpOwnership.SetOwner(msg.to);
+	}
+}
+
+/**
+ * Also destroy all turrets
+ */
+TurretHolder.prototype.OnDestroy = function(msg)
+{
+	for each (var ent in this.turrets)
+		Engine.DestroyEntity(ent);
+}
+
+Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder);
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(revision 15170)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(working copy)
@@ -179,7 +179,7 @@
 	// Called when being told to walk as part of a formation
 	"Order.FormationWalk": function(msg) {
 		// Let players move captured domestic animals around
-		if (this.IsAnimal() && !this.IsDomestic())
+		if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
 		{
 			this.FinishOrder();
 			return;
@@ -207,7 +207,7 @@
 	"Order.LeaveFoundation": function(msg) {
 		// If foundation is not ally of entity, or if entity is unpacked siege,
 		// ignore the order
-		if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack())
+		if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack() || this.IsTurret())
 		{
 			this.FinishOrder();
 			return;
@@ -252,7 +252,7 @@
 
 	"Order.Walk": function(msg) {
 		// Let players move captured domestic animals around
-		if (this.IsAnimal() && !this.IsDomestic())
+		if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
 		{
 			this.FinishOrder();
 			return;
@@ -278,7 +278,7 @@
 
 	"Order.WalkAndFight": function(msg) {
 		// Let players move captured domestic animals around
-		if (this.IsAnimal() && !this.IsDomestic())
+		if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
 		{
 			this.FinishOrder();
 			return;
@@ -305,7 +305,7 @@
 
 	"Order.WalkToTarget": function(msg) {
 		// Let players move captured domestic animals around
-		if (this.IsAnimal() && !this.IsDomestic())
+		if (this.IsAnimal() && !this.IsDomestic() || this.IsTurret())
 		{
 			this.FinishOrder();
 			return;
@@ -486,7 +486,7 @@
 
 		// If we can't reach the target, but are standing ground, then abandon this attack order.
 		// Unless we're hunting, that's a special case where we should continue attacking our target.
-		if (this.GetStance().respondStandGround && !this.order.data.force && !this.order.data.hunting)
+		if (this.GetStance().respondStandGround && !this.order.data.force && !this.order.data.hunting || this.IsTurret())
 		{
 			this.FinishOrder();
 			return;
@@ -685,6 +685,11 @@
 	},
 
 	"Order.Garrison": function(msg) {
+		if (this.IsTurret())
+		{
+			this.FinishOrder();
+			return;
+		}
 		// For packable units:
 		// 1. If packed, we can move to the garrison target.
 		// 2. If unpacked, we first need to pack, then follow case 1.
@@ -1242,7 +1247,7 @@
 		"Order.LeaveFoundation": function(msg) {
 			// If foundation is not ally of entity, or if entity is unpacked siege,
 			// ignore the order
-			if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack())
+			if (!IsOwnedByAllyOfEntity(this.entity, msg.data.target) || this.IsPacking() || this.CanPack() || this.IsTurret())
 			{
 				this.FinishOrder();
 				return;
@@ -3119,8 +3124,23 @@
 	this.lastHealed = undefined;
 
 	this.SetStance(this.template.DefaultStance);
+	this.SetTurret(false);
 };
 
+/**
+ * Set the flag to true to use this unit as a turret
+ * This means no moving is allowed, only turning
+ */
+UnitAI.prototype.SetTurret = function(flag)
+{
+	this.isTurret = flag;
+};
+
+UnitAI.prototype.IsTurret = function()
+{
+	return this.isTurret;
+};
+
 UnitAI.prototype.ReactsToAlert = function(level)
 {
 	return this.template.AlertReactiveLevel <= level;
@@ -4152,7 +4172,7 @@
 
 UnitAI.prototype.MoveToTargetRange = function(target, iid, type)
 {
-	if (!this.CheckTargetVisible(target))
+	if (!this.CheckTargetVisible(target) || this.IsTurret())
 		return false;
 
 	var cmpRanged = Engine.QueryInterface(this.entity, iid);
@@ -4583,6 +4603,9 @@
  */
 UnitAI.prototype.ShouldChaseTargetedEntity = function(target, force)
 {
+	if (this.IsTurret())
+		return false;
+
 	// TODO: use special stances instead?
 	var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
 	if (cmpPack)
Index: binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js	(revision 0)
+++ binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js	(working copy)
@@ -0,0 +1,2 @@
+Engine.RegisterInterface("TurretHolder");
+
Index: binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml	(revision 15170)
+++ binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml	(working copy)
@@ -69,6 +69,16 @@
     <Radius>20</Radius>
     <Weight>40000</Weight>
   </TerritoryInfluence>
+  <TurretHolder>
+    <tree>
+      <Template>units/athen_infantry_archer_a</Template>
+      <Offset>
+        <X>5</X>
+        <Y>5</Y>
+        <Z>5</Z>
+      </Offset>
+    </tree>
+  </TurretHolder>
   <Vision>
     <Range>20</Range>
   </Vision>
Index: source/simulation2/components/CCmpPosition.cpp
===================================================================
--- source/simulation2/components/CCmpPosition.cpp	(revision 15170)
+++ source/simulation2/components/CCmpPosition.cpp	(working copy)
@@ -75,10 +75,16 @@
 	entity_pos_t m_YOffset, m_LastYOffset;
 	bool m_RelativeToGround; // whether m_YOffset is relative to terrain/water plane, or an absolute height
 
+	// when the entity is a turret, only m_RotY is used, and this is the rotation
+	// relative to the parent entity
 	entity_angle_t m_RotX, m_RotY, m_RotZ;
 
 	player_id_t m_Territory;
 
+	entity_id_t m_TurretParent;
+	CFixedVector3D m_TurretPosition;
+	std::set<entity_id_t> m_Turrets;
+
 	// not serialized:
 	float m_InterpolatedRotX, m_InterpolatedRotY, m_InterpolatedRotZ;
 	float m_LastInterpolatedRotX, m_LastInterpolatedRotZ; // not serialized
@@ -140,6 +146,8 @@
 		m_Territory = INVALID_PLAYER;
 
 		m_NeedInitialXZRotation = false;
+		m_TurretParent = INVALID_ENTITY;
+		m_TurretPosition = CFixedVector3D();
 	}
 
 	virtual void Deinit()
@@ -217,6 +225,59 @@
 			UpdateXZRotation();
 	}
 
+	virtual void UpdateTurretPosition()
+	{
+		if (m_TurretParent == INVALID_ENTITY)
+			return;
+		CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
+		if (!cmpPosition)
+		{
+			LOGERROR(L"Turret with parent without position component");
+			return;
+		}
+		if (!cmpPosition->IsInWorld())
+			MoveOutOfWorld();
+		else
+		{
+			CFixedVector2D rotatedPosition = CFixedVector2D(m_TurretPosition.X, m_TurretPosition.Z);
+			rotatedPosition = rotatedPosition.Rotate(cmpPosition->GetRotation().Y);
+			CFixedVector2D rootPosition = cmpPosition->GetPosition2D();
+			entity_pos_t x = rootPosition.X + rotatedPosition.X;
+			entity_pos_t z = rootPosition.Y + rotatedPosition.Y;
+			if (!m_InWorld || m_X != x || m_Z != z)
+				MoveTo(x, z);
+			entity_pos_t y = cmpPosition->GetHeightOffset() + m_TurretPosition.Y;
+			if (!m_InWorld || m_YOffset != y)
+				SetHeightOffset(y);
+		}
+	}
+
+	virtual std::set<entity_id_t>* GetTurrets()
+	{
+		return &m_Turrets;
+	}
+
+	virtual void SetTurretParent(entity_id_t id, CFixedVector3D offset)
+	{
+		if (m_TurretParent != INVALID_ENTITY)
+		{
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
+			if (cmpPosition)
+				cmpPosition->GetTurrets()->erase(GetEntityId());
+		}
+
+		m_TurretParent = id;
+		m_TurretPosition = offset;
+
+		if (m_TurretParent != INVALID_ENTITY)
+		{
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
+			if (cmpPosition)
+				cmpPosition->GetTurrets()->insert(GetEntityId());
+		}
+		UpdateTurretPosition();
+	}
+
 	virtual bool IsInWorld()
 	{
 		return m_InWorld;
@@ -249,7 +310,6 @@
 	{
 		m_X = x;
 		m_Z = z;
-		m_RotY = ry;
 		
 		if (!m_InWorld)
 		{
@@ -259,7 +319,8 @@
 			m_LastYOffset = m_YOffset;
 		}
 		
-		AdvertisePositionChanges();
+		// TurnTo will advertise the position changes
+		TurnTo(ry);
 	}
 
 	virtual void JumpTo(entity_pos_t x, entity_pos_t z)
@@ -392,6 +453,12 @@
 
 	virtual void TurnTo(entity_angle_t y)
 	{
+		if (m_TurretParent != INVALID_ENTITY)
+		{
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
+			if (cmpPosition)
+				y -= cmpPosition->GetRotation().Y;
+		}
 		m_RotY = y;
 
 		AdvertisePositionChanges();
@@ -399,6 +466,12 @@
 
 	virtual void SetYRotation(entity_angle_t y)
 	{
+		if (m_TurretParent != INVALID_ENTITY)
+		{
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
+			if (cmpPosition)
+				y -= cmpPosition->GetRotation().Y;
+		}
 		m_RotY = y;
 		m_InterpolatedRotY = m_RotY.ToFloat();
 
@@ -431,6 +504,13 @@
 
 	virtual CFixedVector3D GetRotation()
 	{
+		entity_angle_t y = m_RotY;
+		if (m_TurretParent != INVALID_ENTITY)
+		{
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
+			if (cmpPosition)
+				y += cmpPosition->GetRotation().Y;
+		}
 		return CFixedVector3D(m_RotX, m_RotY, m_RotZ);
 	}
 
@@ -461,6 +541,32 @@
 
 	virtual CMatrix3D GetInterpolatedTransform(float frameOffset, bool forceFloating)
 	{
+		if (m_TurretParent != INVALID_ENTITY)
+		{
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent);
+			if (!cmpPosition)
+			{
+				LOGERROR(L"Turret with parent without position component");
+				CMatrix3D m;
+				m.SetIdentity();
+				return m;
+			}
+			if (!cmpPosition->IsInWorld())
+			{
+				LOGERROR(L"CCmpPosition::GetInterpolatedTransform called on turret entity when IsInWorld is false");
+				CMatrix3D m;
+				m.SetIdentity();
+				return m;
+			}
+			else
+			{
+				CMatrix3D parentTransformMatrix = cmpPosition->GetInterpolatedTransform(frameOffset, forceFloating);
+				CMatrix3D ownTransformation = CMatrix3D();
+				ownTransformation.SetYRotation(m_InterpolatedRotY);
+				ownTransformation.Translate(-m_TurretPosition.X.ToFloat(), m_TurretPosition.Y.ToFloat(), -m_TurretPosition.Z.ToFloat());
+				return parentTransformMatrix * ownTransformation;
+			}
+		}
 		if (!m_InWorld)
 		{
 			LOGERROR(L"CCmpPosition::GetInterpolatedTransform called on entity when IsInWorld is false");
@@ -548,6 +654,7 @@
 		}
 		case MT_TurnStart:
 		{
+			
 			m_LastInterpolatedRotX = m_InterpolatedRotX;
 			m_LastInterpolatedRotZ = m_InterpolatedRotZ;
 
@@ -592,6 +699,12 @@
 private:
 	void AdvertisePositionChanges()
 	{
+		for (std::set<entity_id_t>::const_iterator it = m_Turrets.begin(); it != m_Turrets.end(); ++it)
+		{
+			CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *it);
+			if (cmpPosition)
+				cmpPosition->UpdateTurretPosition();
+		}
 		if (m_InWorld)
 		{
 			CMessagePositionChanged msg(GetEntityId(), true, m_X, m_Z, m_RotY);
Index: source/simulation2/components/ICmpPosition.cpp
===================================================================
--- source/simulation2/components/ICmpPosition.cpp	(revision 15170)
+++ source/simulation2/components/ICmpPosition.cpp	(working copy)
@@ -22,6 +22,7 @@
 #include "simulation2/system/InterfaceScripted.h"
 
 BEGIN_INTERFACE_WRAPPER(Position)
+DEFINE_INTERFACE_METHOD_2("SetTurretParent", void, ICmpPosition, SetTurretParent, entity_id_t, CFixedVector3D)
 DEFINE_INTERFACE_METHOD_0("IsInWorld", bool, ICmpPosition, IsInWorld)
 DEFINE_INTERFACE_METHOD_0("MoveOutOfWorld", void, ICmpPosition, MoveOutOfWorld)
 DEFINE_INTERFACE_METHOD_2("MoveTo", void, ICmpPosition, MoveTo, entity_pos_t, entity_pos_t)
Index: source/simulation2/components/ICmpPosition.h
===================================================================
--- source/simulation2/components/ICmpPosition.h	(revision 15170)
+++ source/simulation2/components/ICmpPosition.h	(working copy)
@@ -59,6 +59,21 @@
 {
 public:
 	/**
+	 * Set this as a turret of an other entity
+	 */
+	virtual void SetTurretParent(entity_id_t parent, CFixedVector3D offset) = 0;
+
+	/**
+	 * Has to be called to update the simulation position of the turret
+	 */
+	virtual void UpdateTurretPosition() = 0;
+
+	/**
+	 * Get the list of turrets to read or edit
+	 */
+	virtual std::set<entity_id_t>* GetTurrets() = 0;
+
+	/**
 	 * Returns true if the entity currently exists at a defined position in the world.
 	 */
 	virtual bool IsInWorld() = 0;
Index: source/simulation2/components/tests/test_RangeManager.h
===================================================================
--- source/simulation2/components/tests/test_RangeManager.h	(revision 15170)
+++ source/simulation2/components/tests/test_RangeManager.h	(working copy)
@@ -40,6 +40,9 @@
 public:
 	DEFAULT_MOCK_COMPONENT()
 
+	virtual void SetTurretParent(entity_id_t UNUSED(id), CFixedVector3D UNUSED(pos)) {}
+	virtual void UpdateTurretPosition() {}
+	virtual std::set<entity_id_t>* GetTurrets() { return NULL; }
 	virtual bool IsInWorld() { return true; }
 	virtual void MoveOutOfWorld() { }
 	virtual void MoveTo(entity_pos_t UNUSED(x), entity_pos_t UNUSED(z)) { }
