When a move is made where the source and target locations are the same and no actual moving is required, a path of length 0 is returned. When a move cannot be made as there is no valid route, a path of length 0 is also returned. This means Move is unable to tell the difference between no movement required, and no path is possible. Currently it will hit the `hadNoPath` case and report CompleteDestinationBlocked.
To fix the scenario where the source and target location match, track a alreadyAtDestination field. When this scenario triggers, report CompleteDestinationReached instead.
This fixes activities that were using this result to inform their next actions. e.g. MoveOntoAndTurn would previously cancel the Turn portion of the activity, believing that the destination could not be reached. Now, it knows the destination was reached (since we are already there!) and will perform the turn.
MoveWithinRange was only moving the unit far enough away so the center of the target was above the minimum distance. However, the min range checks in the attack code require all positions on the target to be above the minimum distance. For large targets (e.g. buildings) this means some of the target was still too close, and the unit would get stuck in a loop.
Now MoveWithinRange uses the same range checks in order to ensure units are moved the correct distances.