Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(revision 14441)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(working copy)
@@ -1706,31 +1706,32 @@
 					// Check the target is still alive and attackable
 					if (this.TargetIsAlive(target) && this.CanAttack(target, this.order.data.forceResponse || null))
 					{
-						// Check we can still reach the target
-						if (this.CheckTargetAttackRange(target, IID_Attack, this.order.data.attackType))
+						// If we are hunting, first update the target position of the gather order so we know where will be the killed animal
+						if (this.order.data.hunting && this.orderQueue[1] && this.orderQueue[1].data.lastPos)
 						{
-							// If we are hunting, first update the target position of the gather order so we know where will be the killed animal
-							if (this.order.data.hunting && this.orderQueue[1] && this.orderQueue[1].data.lastPos)
+							var cmpPosition = Engine.QueryInterface(this.order.data.target, IID_Position);
+							if (cmpPosition && cmpPosition.IsInWorld())
 							{
-								var cmpPosition = Engine.QueryInterface(this.order.data.target, IID_Position);
-								if (cmpPosition && cmpPosition.IsInWorld())
-								{
-									// Store the initial position, so that we can find the rest of the herd later
-									if (!this.orderQueue[1].data.initPos)
-										this.orderQueue[1].data.initPos = this.orderQueue[1].data.lastPos;
-									this.orderQueue[1].data.lastPos = cmpPosition.GetPosition();
-									// We still know where the animal is, so we shouldn't give up before going there
-									this.orderQueue[1].data.secondTry = undefined;
-								}
+								// Store the initial position, so that we can find the rest of the herd later
+								if (!this.orderQueue[1].data.initPos)
+									this.orderQueue[1].data.initPos = this.orderQueue[1].data.lastPos;
+								this.orderQueue[1].data.lastPos = cmpPosition.GetPosition();
+								// We still know where the animal is, so we shouldn't give up before going there
+								this.orderQueue[1].data.secondTry = undefined;
 							}
+						}
 
-							var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
-							this.lastAttacked = cmpTimer.GetTime() - msg.lateness;
+						var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+						this.lastAttacked = cmpTimer.GetTime() - msg.lateness;
 
-							this.FaceTowardsTarget(target);
-							var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
-							cmpAttack.PerformAttack(this.order.data.attackType, target);
+						this.FaceTowardsTarget(target);
+						var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
+						cmpAttack.PerformAttack(this.order.data.attackType, target);
 
+						
+						// Check we can still reach the target for the next attack
+						if (this.CheckTargetAttackRange(target, IID_Attack, this.order.data.attackType))
+						{
 							if (this.resyncAnimation)
 							{
 								this.SetAnimationSync(this.attackTimers.repeat, this.attackTimers.repeat);
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged.xml	(revision 14441)
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged.xml	(working copy)
@@ -11,7 +11,7 @@
       <Pierce>10.0</Pierce>
       <Crush>0.0</Crush>
       <MaxRange>16.0</MaxRange>
-      <MinRange>0.0</MinRange>
+      <MinRange>20.0</MinRange>
       <ProjectileSpeed>25.0</ProjectileSpeed>
       <PrepareTime>900</PrepareTime>
       <RepeatTime>1500</RepeatTime>
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_archer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_archer.xml	(revision 14441)
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_archer.xml	(working copy)
@@ -6,7 +6,6 @@
       <Pierce>20.0</Pierce>
       <Crush>0.0</Crush>
       <MaxRange>56.0</MaxRange>
-      <MinRange>0.0</MinRange>
       <ProjectileSpeed>75.0</ProjectileSpeed>
       <PrepareTime>1200</PrepareTime>
       <RepeatTime>2000</RepeatTime>
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_javelinist.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_javelinist.xml	(revision 14441)
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry_ranged_javelinist.xml	(working copy)
@@ -6,7 +6,6 @@
       <Pierce>25.0</Pierce>
       <Crush>0.0</Crush>
       <MaxRange>44</MaxRange>
-      <MinRange>0.0</MinRange>
       <ProjectileSpeed>50.0</ProjectileSpeed>
       <PrepareTime>1200</PrepareTime>
       <RepeatTime>2000</RepeatTime>
Index: source/simulation2/components/CCmpObstruction.cpp
===================================================================
--- source/simulation2/components/CCmpObstruction.cpp	(revision 14441)
+++ source/simulation2/components/CCmpObstruction.cpp	(working copy)
@@ -442,8 +442,18 @@
 		return m_Tag;
 	}
 
+	virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out)
+	{
+		return GetObstructionSquare(out, true);
+	}
+
 	virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out)
 	{
+		return GetObstructionSquare(out, false);
+	}
+
+	virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out, bool previousPosition)
+	{
 		CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
 		if (!cmpPosition)
 			return false; // error
@@ -455,7 +465,11 @@
 		if (!cmpPosition->IsInWorld())
 			return false; // no obstruction square
 
-		CFixedVector2D pos = cmpPosition->GetPosition2D();
+		CFixedVector2D pos;
+		if (previousPosition)
+			pos = cmpPosition->GetPreviousPosition2D();
+		else
+			pos = cmpPosition->GetPosition2D();
 		if (m_Type == STATIC)
 			out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1);
 		else if (m_Type == UNIT)
Index: source/simulation2/components/CCmpUnitMotion.cpp
===================================================================
--- source/simulation2/components/CCmpUnitMotion.cpp	(revision 14441)
+++ source/simulation2/components/CCmpUnitMotion.cpp	(working copy)
@@ -107,7 +107,7 @@
 static const CColor OVERLAY_COLOUR_LONG_PATH(1, 1, 1, 1);
 static const CColor OVERLAY_COLOUR_SHORT_PATH(1, 0, 0, 1);
 
-static const entity_pos_t g_GoalDelta = entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/4; // for extending the goal outwards/inwards a little bit
+static const entity_pos_t g_GoalDelta = entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/2; // for extending the goal outwards/inwards a little bit
 
 class CCmpUnitMotion : public ICmpUnitMotion
 {
@@ -995,11 +995,21 @@
 						}
 						else
 						{
+							// check if target was reached in case of a moving target
+							CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), m_TargetEntity);
+							if 
+							(
+								cmpUnitMotion && 
+								cmpUnitMotion->IsMoving() &&
+								MoveToTargetRange(m_TargetEntity, m_TargetMinRange, m_TargetMaxRange)
+							)
+								return;
+
 							// Not in formation, so just finish moving
-
 							StopMoving();
+							m_State = STATE_IDLE;
+							MoveSucceeded();
 
-
 							if (m_FacePointAfterMove)
 								FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z);
 							// TODO: if the goal was a square building, we ought to point towards the
@@ -1508,7 +1518,12 @@
 
 		entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize);
 
-		if (distance < minRange)
+		// compare with previous obstruction
+		ICmpObstructionManager::ObstructionSquare previousObstruction;
+		cmpObstruction->GetPreviousObstructionSquare(previousObstruction);
+		entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize);
+
+		if (distance < minRange && previousDistance < minRange)
 		{
 			// Too close to the square - need to move away
 
@@ -1523,7 +1538,7 @@
 			goal.hw = obstruction.hw + delta;
 			goal.hh = obstruction.hh + delta;
 		}
-		else if (maxRange < entity_pos_t::Zero() || distance < maxRange)
+		else if (maxRange < entity_pos_t::Zero() || distance < maxRange || previousDistance < maxRange)
 		{
 			// We're already in range - no need to move anywhere
 			if (m_FacePointAfterMove)
@@ -1554,6 +1569,17 @@
 					return false;
 				}
 
+				entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius;
+
+				if (previousCircleDistance < maxRange)
+				{
+					// We're already in range - no need to move anywhere
+					if (m_FacePointAfterMove)
+						FaceTowardsPointFromPos(pos, goal.x, goal.z);
+					return false;
+				}
+
+
 				entity_pos_t goalDistance = maxRange - g_GoalDelta;
 
 				goal.type = ICmpPathfinder::Goal::CIRCLE;
@@ -1626,12 +1652,17 @@
 		CFixedVector2D halfSize(obstruction.hw, obstruction.hh);
 		entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize);
 
+		// compare with previous obstruction
+		ICmpObstructionManager::ObstructionSquare previousObstruction;
+		cmpObstruction->GetPreviousObstructionSquare(previousObstruction);
+		entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize);
+		
 		// See if we're too close to the target square
-		if (distance < minRange)
+		if (distance < minRange && previousDistance < minRange)
 			return false;
 
 		// See if we're close enough to the target square
-		if (maxRange < entity_pos_t::Zero() || distance <= maxRange)
+		if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange)
 			return true;
 
 		entity_pos_t circleRadius = halfSize.Length();
@@ -1645,6 +1676,11 @@
 
 			if (circleDistance <= maxRange)
 				return true;
+			// also check circle around previous position
+			circleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius;
+
+			if (circleDistance <= maxRange)
+				return true;
 		}
 
 		return false;
@@ -1655,14 +1691,12 @@
 		if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld())
 			return false;
 
-		CFixedVector2D targetPos = cmpTargetPosition->GetPosition2D();
+		CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D();
 
 		entity_pos_t distance = (pos - targetPos).Length();
 
-		if (minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange))
-			return true;
-
-		return false;
+		return minRange <= distance && 
+			(maxRange < entity_pos_t::Zero() || distance <= maxRange);
 	}
 }
 
Index: source/simulation2/components/ICmpObstruction.h
===================================================================
--- source/simulation2/components/ICmpObstruction.h	(revision 14441)
+++ source/simulation2/components/ICmpObstruction.h	(working copy)
@@ -47,6 +47,11 @@
 	 */
 	virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0;
 
+	/**
+	 * Same as the method above, but returns an obstruction shape for the previous turn
+	 */
+	virtual bool GetPreviousObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) = 0;
+
 	virtual entity_pos_t GetUnitRadius() = 0;
 
 	virtual bool IsControlPersistent() = 0;
