Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(revision 14302)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(working copy)
@@ -1661,7 +1661,10 @@
 					var attackName = "attack_" + this.order.data.attackType.toLowerCase();
 					this.SelectAnimation(attackName, false, 1.0, "attack");
 					this.SetAnimationSync(prepare, this.attackTimers.repeat);
-					this.StartTimer(prepare, this.attackTimers.repeat);
+					// repeat timer faster than repeat time for the attack
+					// as targets out-of-range also get detected in the timer loop
+					// and the unit should respond fast to it to stop the animation in time
+					this.StartTimer(prepare, Math.min(250,this.attackTimers.repeat));
 					// TODO: we should probably only bother syncing projectile attacks, not melee
 
 					// If using a non-default prepare time, re-sync the animation when the timer runs.
Index: source/simulation2/components/CCmpUnitMotion.cpp
===================================================================
--- source/simulation2/components/CCmpUnitMotion.cpp	(revision 14302)
+++ source/simulation2/components/CCmpUnitMotion.cpp	(working copy)
@@ -132,6 +132,7 @@
 	bool m_FormationController;
 	fixed m_WalkSpeed, m_OriginalWalkSpeed; // in metres per second
 	fixed m_RunSpeed, m_OriginalRunSpeed;
+	fixed m_TurnLength;
 	ICmpPathfinder::pass_class_t m_PassClass;
 	ICmpPathfinder::cost_class_t m_CostClass;
 
@@ -297,6 +298,7 @@
 		m_WalkSpeed = m_OriginalWalkSpeed = paramNode.GetChild("WalkSpeed").ToFixed();
 		m_Speed = m_WalkSpeed;
 		m_CurSpeed = fixed::Zero();
+		m_TurnLength = fixed::Zero();
 
 		if (paramNode.GetChild("Run").IsOk())
 			m_RunSpeed = m_OriginalRunSpeed = paramNode.GetChild("Run").GetChild("Speed").ToFixed();
@@ -350,6 +352,8 @@
 
 		serialize.NumberFixed_Unbounded("speed", m_Speed);
 
+		serialize.NumberFixed_Unbounded("turnLength", m_TurnLength);
+
 		serialize.Bool("moving", m_Moving);
 		serialize.Bool("facePointAfterMove", m_FacePointAfterMove);
 
@@ -379,8 +383,8 @@
 		{
 			if (m_FormationController)
 			{
-				fixed dt = static_cast<const CMessageUpdate_MotionFormation&> (msg).turnLength;
-				Move(dt);
+				m_TurnLength = static_cast<const CMessageUpdate_MotionFormation&> (msg).turnLength;
+				Move(m_TurnLength);
 			}
 			break;
 		}
@@ -388,8 +392,8 @@
 		{
 			if (!m_FormationController)
 			{
-				fixed dt = static_cast<const CMessageUpdate_MotionUnit&> (msg).turnLength;
-				Move(dt);
+				m_TurnLength = static_cast<const CMessageUpdate_MotionFormation&> (msg).turnLength;
+				Move(m_TurnLength);
 			}
 			break;
 		}
@@ -535,6 +539,8 @@
 
 	void MoveSucceeded()
 	{
+		// No longer moving, so speed is 0.
+		m_CurSpeed = fixed::Zero();
 		m_Moving = false;
 
 		CmpPtr<ICmpObstruction> cmpObstruction(GetEntityHandle());
@@ -541,9 +547,6 @@
 		if (cmpObstruction)
 			cmpObstruction->SetMovingFlag(false);
 
-		// No longer moving, so speed is 0.
-		m_CurSpeed = fixed::Zero();
-
 		CMessageMotionChanged msg(false, false);
 		GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg);
 	}
@@ -995,11 +998,19 @@
 						}
 						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();
 
-
 							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
@@ -1313,6 +1324,22 @@
 
 	CFixedVector2D pos = cmpPosition->GetPosition2D();
 
+	entity_pos_t overShoot = fixed::Zero();
+
+	// if the target is also moving, take an overshoot into account
+	CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), target);
+	if (cmpUnitMotion && cmpUnitMotion->IsMoving())
+		overShoot = m_CurSpeed.Multiply(m_TurnLength);
+
+	entity_pos_t origMaxRange = maxRange;
+	entity_pos_t origMinRange = minRange;
+	maxRange -= overShoot;
+	minRange += overShoot;
+	if (maxRange < fixed::Zero() && origMaxRange >= fixed::Zero())
+		maxRange = fixed::Zero();
+	if (maxRange > fixed::Zero() && minRange > maxRange)
+		minRange = maxRange;	
+
 	ICmpPathfinder::Goal goal;
 
 	if (minRange.IsZero() && maxRange.IsZero())
@@ -1381,8 +1408,8 @@
 	m_State = STATE_INDIVIDUAL_PATH;
 	m_TargetEntity = target;
 	m_TargetOffset = CFixedVector2D();
-	m_TargetMinRange = minRange;
-	m_TargetMaxRange = maxRange;
+	m_TargetMinRange = origMinRange;
+	m_TargetMaxRange = origMaxRange;
 	m_FinalGoal = goal;
 
 	BeginPathing(pos, goal);
@@ -1457,7 +1484,22 @@
 bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange)
 {
 	PROFILE("MoveToTargetRange");
+	entity_pos_t overShoot = fixed::Zero();
 
+	// if the target is als moving, take an overshoot into account
+	CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), target);
+	if (cmpUnitMotion && cmpUnitMotion->IsMoving())
+		overShoot = m_CurSpeed.Multiply(m_TurnLength);
+
+	entity_pos_t origMaxRange = maxRange;
+	entity_pos_t origMinRange = minRange;
+	maxRange -= overShoot;
+	minRange += overShoot;
+	if (maxRange < fixed::Zero() && origMaxRange >= fixed::Zero())
+		maxRange = fixed::Zero();
+	if (maxRange > fixed::Zero() && minRange > maxRange)
+		minRange = maxRange;	
+
 	CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
 	if (!cmpPosition || !cmpPosition->IsInWorld())
 		return false;
@@ -1578,8 +1620,8 @@
 		m_State = STATE_INDIVIDUAL_PATH;
 		m_TargetEntity = target;
 		m_TargetOffset = CFixedVector2D();
-		m_TargetMinRange = minRange;
-		m_TargetMaxRange = maxRange;
+		m_TargetMinRange = origMinRange;
+		m_TargetMaxRange = origMaxRange;
 		m_FinalGoal = goal;
 
 		BeginPathing(pos, goal);
@@ -1596,7 +1638,7 @@
 
 		CFixedVector2D targetPos = cmpTargetPosition->GetPosition2D();
 
-		return MoveToPointRange(targetPos.X, targetPos.Y, minRange, maxRange, target);
+		return MoveToPointRange(targetPos.X, targetPos.Y, origMinRange, origMaxRange, target);
 	}
 }
 
