class UKismetAIAsyncTaskProxy* UKismetAIHelperLibrary::CreateMoveToProxyObject(class UObject* WorldContextObject, APawn* Pawn, FVector Destination, AActor* TargetActor, float AcceptanceRadius, bool bStopOnOverlap)
{
	check(WorldContextObject);
	if (!Pawn)
	{
		return NULL;
	}
	UKismetAIAsyncTaskProxy* MyObj = NULL;
	AAIController* AIController = Cast<AAIController>(Pawn->GetController());
	if (AIController)
	{
		MyObj = NewObject<UKismetAIAsyncTaskProxy>();
		FNavPathSharedPtr Path = TargetActor ? AIController->FindPath(TargetActor, true) : AIController->FindPath(Destination, true);
		if (Path.IsValid())
		{
			MyObj->AIController = AIController;
			MyObj->AIController->ReceiveMoveCompleted.AddDynamic(MyObj, &UKismetAIAsyncTaskProxy::OnMoveCompleted);
			MyObj->MoveRequestId = MyObj->AIController->RequestMove(Path, TargetActor, AcceptanceRadius, bStopOnOverlap);
		}
		else
		{
			UWorld* World = GEngine->GetWorldFromContextObject( WorldContextObject );
			World->GetTimerManager().SetTimer(MyObj, &UKismetAIAsyncTaskProxy::OnNoPath, 0.1, false);
		}
	}
	return MyObj;
}
// DEPRECATED FUNCTION SUPPORT
FAIRequestID AAIController::RequestPathAndMove(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query)
{
    FAIRequestID MoveId = FAIRequestID::InvalidRequest;

    FNavPathSharedPtr FoundPath;
    FindPathForMoveRequest(MoveRequest, Query, FoundPath);

    if (FoundPath.IsValid())
    {
        MoveId = RequestMove(MoveRequest, FoundPath);
    }

    return MoveId;
}
void UNavigationPath::SetPath(FNavPathSharedPtr NewSharedPath)
{
	FNavigationPath* NewPath = NewSharedPath.Get();
	if (SharedPath.Get() != NewPath)
	{
		if (SharedPath.IsValid())
		{
			SharedPath->RemoveObserver(PathObserverDelegateHandle);
		}
		SharedPath = NewSharedPath;
		if (NewPath != NULL)
		{
			PathObserverDelegateHandle = NewPath->AddObserver(PathObserver);

			if (RecalculateOnInvalidation != ENavigationOptionFlag::Default)
			{
				NewPath->EnableRecalculationOnInvalidation(RecalculateOnInvalidation == ENavigationOptionFlag::Enable);
			}
			
			SetPathPointsFromPath(*NewPath);
		}
		else
		{
			PathPoints.Reset();
		}

		OnPathEvent(NewPath, NewPath != NULL ? ENavPathEvent::NewPath : ENavPathEvent::Cleared);
	}
}
void UNavigationComponent::SetPath(const FNavPathSharedPtr& ResultPath)
{
	check(ResultPath.Get() != NULL);

	Path = ResultPath;
	Path->SetObserver(PathObserverDelegate);
	bIsPathPartial = Path->IsPartial();
}
EPathFollowingRequestResult::Type AAIController::MoveToActor(class AActor* Goal, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
	SCOPE_CYCLE_COUNTER(STAT_MoveToActor);
	EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed;

	UE_VLOG(this, LogNavigation, Log, TEXT("MoveToActor: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"),
		*GetNameSafe(Goal), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe);

	if (Goal)
	{
		if (PathFollowingComponent && PathFollowingComponent->HasReached(Goal, AcceptanceRadius, !bStopOnOverlap))
		{
			UE_VLOG(this, LogNavigation, Log, TEXT("MoveToActor: already at goal!"));
			PathFollowingComponent->SetLastMoveAtGoal(true);

			OnMoveCompleted(0, EPathFollowingResult::Success);
			Result = EPathFollowingRequestResult::AlreadyAtGoal;
		}
		else 
		{
			FNavPathSharedPtr Path = FindPath(Goal, bUsePathfinding, UNavigationQueryFilter::GetQueryFilter(FilterClass));
			if (Path.IsValid())
			{
				bAllowStrafe = bCanStrafe;

				const uint32 RequestID = RequestMove(Path, Goal, AcceptanceRadius, bStopOnOverlap);
				Result = (RequestID != INVALID_MOVEREQUESTID) ? EPathFollowingRequestResult::RequestSuccessful : EPathFollowingRequestResult::Failed;
			}
		}
	}

	if (Result == EPathFollowingRequestResult::Failed)
	{
		if (PathFollowingComponent)
		{
			PathFollowingComponent->SetLastMoveAtGoal(false);
		}

		OnMoveCompleted(INVALID_MOVEREQUESTID, EPathFollowingResult::Invalid);
	}

	return Result;
}
void UNavigationComponent::AsyncGeneratePath_OnCompleteCallback(uint32 QueryID, ENavigationQueryResult::Type Result, FNavPathSharedPtr ResultPath)
{
	if (QueryID != uint32(AsynPathQueryID))
	{
		UE_VLOG(GetOwner(), LogNavigation, Display, TEXT("Pathfinding query %u finished, but it's different from AsynPathQueryID (%d)"), QueryID, AsynPathQueryID);
		return;
	}

	check(GetOuter() != NULL);
	
	if (Result == ENavigationQueryResult::Success && ResultPath.IsValid() && ResultPath->IsValid())
	{
		const bool bIsSamePath = Path.IsValid() == true && ((const FNavMeshPath*)Path.Get())->ContainsWithSameEnd((const FNavMeshPath*)ResultPath.Get())
			&& FVector::DistSquared(Path->GetDestinationLocation(), ResultPath->GetDestinationLocation()) < KINDA_SMALL_NUMBER;

		if (bIsSamePath == false)
		{
			if (IsFollowing() == true)
			{
				// make sure ticking is enabled (and it shouldn't be enabled before _first_ async path finding)
				SetComponentTickEnabledAsync(true);
			}

			SetPath(ResultPath);
			CurrentGoal = ResultPath->PathPoints[ ResultPath->PathPoints.Num() - 1 ].Location;
			NotifyPathUpdate();
		}
		else
		{
			UE_VLOG(GetOwner(), LogNavigation, Display, TEXT("Skipping newly generated path due to it being the same as current one"));
		}
	}
	else
	{
		ResetTransientData();
		if (MyPathObserver != NULL)
		{
			MyPathObserver->OnPathFailed(this);  
		}
	}
}
EPathFollowingRequestResult::Type AAIController::MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
	SCOPE_CYCLE_COUNTER(STAT_MoveToLocation);

	EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed;
	bool bCanRequestMove = true;

	UE_VLOG(this, LogNavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"),
		*Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe);

	// Check input is valid
	if( Dest.ContainsNaN() )
	{
		UE_VLOG(this, LogNavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"),
			*Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe);
		
		ensure( !Dest.ContainsNaN() );
		bCanRequestMove = false;
	}

	FVector GoalLocation = Dest;

	// fail if projection to navigation is required but it either failed or there's not navigation component
	if (bCanRequestMove && bProjectDestinationToNavigation && (NavComponent == NULL || !NavComponent->ProjectPointToNavigation(Dest, GoalLocation)))
	{
		UE_VLOG_LOCATION(this, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh"));
		bCanRequestMove = false;
	}

	if (bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(GoalLocation, AcceptanceRadius, !bStopOnOverlap))
	{
		UE_VLOG(this, LogNavigation, Log, TEXT("MoveToLocation: already at goal!"));
		PathFollowingComponent->SetLastMoveAtGoal(true);

		OnMoveCompleted(0, EPathFollowingResult::Success);
		Result = EPathFollowingRequestResult::AlreadyAtGoal;
		bCanRequestMove = false;
	}

	if (bCanRequestMove)
	{
		FNavPathSharedPtr Path = FindPath(GoalLocation, bUsePathfinding, UNavigationQueryFilter::GetQueryFilter(FilterClass));
	if (Path.IsValid())
	{
		bAllowStrafe = bCanStrafe;

		const uint32 RequestID = RequestMove(Path, NULL, AcceptanceRadius, bStopOnOverlap);
			Result = (RequestID != INVALID_MOVEREQUESTID) ? EPathFollowingRequestResult::RequestSuccessful : EPathFollowingRequestResult::Failed;
		}
	}

	if (Result == EPathFollowingRequestResult::Failed)
	{
		if (PathFollowingComponent)
		{
			PathFollowingComponent->SetLastMoveAtGoal(false);
		}

		OnMoveCompleted(INVALID_MOVEREQUESTID, EPathFollowingResult::Invalid);
	}

	return Result;
}
FPathFollowingRequestResult AAIController::MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath)
{
    // both MoveToActor and MoveToLocation can be called from blueprints/script and should keep only single movement request at the same time.
    // this function is entry point of all movement mechanics - do NOT abort in here, since movement may be handled by AITasks, which support stacking

    SCOPE_CYCLE_COUNTER(STAT_MoveTo);
    UE_VLOG(this, LogAINavigation, Log, TEXT("MoveTo: %s"), *MoveRequest.ToString());

    FPathFollowingRequestResult ResultData;
    ResultData.Code = EPathFollowingRequestResult::Failed;

    if (MoveRequest.IsValid() == false)
    {
        UE_VLOG(this, LogAINavigation, Error, TEXT("MoveTo request failed due MoveRequest not being valid. Most probably desireg Goal Actor not longer exists"), *MoveRequest.ToString());
        return ResultData;
    }

    if (PathFollowingComponent == nullptr)
    {
        UE_VLOG(this, LogAINavigation, Error, TEXT("MoveTo request failed due missing PathFollowingComponent"));
        return ResultData;
    }

    ensure(MoveRequest.GetNavigationFilter() || !DefaultNavigationFilterClass);

    bool bCanRequestMove = true;
    bool bAlreadyAtGoal = false;

    if (!MoveRequest.IsMoveToActorRequest())
    {
        if (MoveRequest.GetGoalLocation().ContainsNaN() || FAISystem::IsValidLocation(MoveRequest.GetGoalLocation()) == false)
        {
            UE_VLOG(this, LogAINavigation, Error, TEXT("AAIController::MoveTo: Destination is not valid! Goal(%s)"), TEXT_AI_LOCATION(MoveRequest.GetGoalLocation()));
            bCanRequestMove = false;
        }

        // fail if projection to navigation is required but it failed
        if (bCanRequestMove && MoveRequest.IsProjectingGoal())
        {
            UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
            const FNavAgentProperties& AgentProps = GetNavAgentPropertiesRef();
            FNavLocation ProjectedLocation;

            if (NavSys && !NavSys->ProjectPointToNavigation(MoveRequest.GetGoalLocation(), ProjectedLocation, INVALID_NAVEXTENT, &AgentProps))
            {
                UE_VLOG_LOCATION(this, LogAINavigation, Error, MoveRequest.GetGoalLocation(), 30.f, FColor::Red, TEXT("AAIController::MoveTo failed to project destination location to navmesh"));
                bCanRequestMove = false;
            }

            MoveRequest.UpdateGoalLocation(ProjectedLocation.Location);
        }

        bAlreadyAtGoal = bCanRequestMove && PathFollowingComponent->HasReached(MoveRequest);
    }
    else
    {
        bAlreadyAtGoal = bCanRequestMove && PathFollowingComponent->HasReached(MoveRequest);
    }

    if (bAlreadyAtGoal)
    {
        UE_VLOG(this, LogAINavigation, Log, TEXT("MoveTo: already at goal!"));
        ResultData.MoveId = PathFollowingComponent->RequestMoveWithImmediateFinish(EPathFollowingResult::Success);
        ResultData.Code = EPathFollowingRequestResult::AlreadyAtGoal;
    }
    else if (bCanRequestMove)
    {
        FPathFindingQuery PFQuery;

        const bool bValidQuery = BuildPathfindingQuery(MoveRequest, PFQuery);
        if (bValidQuery)
        {
            FNavPathSharedPtr Path;
            FindPathForMoveRequest(MoveRequest, PFQuery, Path);

            const FAIRequestID RequestID = Path.IsValid() ? RequestMove(MoveRequest, Path) : FAIRequestID::InvalidRequest;
            if (RequestID.IsValid())
            {
                bAllowStrafe = MoveRequest.CanStrafe();
                ResultData.MoveId = RequestID;
                ResultData.Code = EPathFollowingRequestResult::RequestSuccessful;

                if (OutPath)
                {
                    *OutPath = Path;
                }
            }
        }
    }

    if (ResultData.Code == EPathFollowingRequestResult::Failed)
    {
        ResultData.MoveId = PathFollowingComponent->RequestMoveWithImmediateFinish(EPathFollowingResult::Invalid);
    }

    return ResultData;
}
void ANavigationData::TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction)
{
	Super::TickActor(DeltaTime, TickType, ThisTickFunction);

	PurgeUnusedPaths();

	INC_DWORD_STAT_BY(STAT_Navigation_ObservedPathsCount, ObservedPaths.Num());

	if (NextObservedPathsTickInSeconds >= 0.f)
	{
		NextObservedPathsTickInSeconds -= DeltaTime;
		if (NextObservedPathsTickInSeconds <= 0.f)
		{
			RepathRequests.Reserve(ObservedPaths.Num());

			for (int32 PathIndex = ObservedPaths.Num() - 1; PathIndex >= 0; --PathIndex)
			{
				if (ObservedPaths[PathIndex].IsValid())
				{
					FNavPathSharedPtr SharedPath = ObservedPaths[PathIndex].Pin();
					FNavigationPath* Path = SharedPath.Get();
					EPathObservationResult::Type Result = Path->TickPathObservation();
					switch (Result)
					{
					case EPathObservationResult::NoLongerObserving:
						ObservedPaths.RemoveAtSwap(PathIndex, 1, /*bAllowShrinking=*/false);
						break;

					case EPathObservationResult::NoChange:
						// do nothing
						break;

					case EPathObservationResult::RequestRepath:
						RepathRequests.Add(FNavPathRecalculationRequest(SharedPath, ENavPathUpdateType::GoalMoved));
						break;
					
					default:
						check(false && "unhandled EPathObservationResult::Type in ANavigationData::TickActor");
						break;
					}
				}
				else
				{
					ObservedPaths.RemoveAtSwap(PathIndex, 1, /*bAllowShrinking=*/false);
				}
			}

			if (ObservedPaths.Num() > 0)
			{
				NextObservedPathsTickInSeconds = ObservedPathsTickInterval;
			}
		}
	}

	if (RepathRequests.Num() > 0)
	{
		float TimeStamp = GetWorldTimeStamp();
		TArray<FNavPathRecalculationRequest> PostponedRequests;
		const UWorld* World = GetWorld();

		// @todo batch-process it!
		for (auto RecalcRequest : RepathRequests)
		{
			// check if it can be updated right now
			FNavPathSharedPtr PinnedPath = RecalcRequest.Path.Pin();
			if (PinnedPath.IsValid() == false)
			{
				continue;
			}

			const UObject* PathQuerier = PinnedPath->GetQuerier();
			const INavAgentInterface* PathNavAgent = Cast<const INavAgentInterface>(PathQuerier);
			if (PathNavAgent && PathNavAgent->ShouldPostponePathUpdates())
			{
				PostponedRequests.Add(RecalcRequest);
				continue;
			}

			FPathFindingQuery Query(PinnedPath.ToSharedRef());
			// @todo consider supplying NavAgentPropertied from path's querier
			const FPathFindingResult Result = FindPath(FNavAgentProperties(), Query.SetPathInstanceToUpdate(PinnedPath));

			// update time stamp to give observers any means of telling if it has changed
			PinnedPath->SetTimeStamp(TimeStamp);

			// partial paths are still valid and can change to full path when moving goal gets back on navmesh
			if (Result.IsSuccessful() || Result.IsPartial())
			{
				PinnedPath->UpdateLastRepathGoalLocation();
				PinnedPath->DoneUpdating(RecalcRequest.Reason);
				if (RecalcRequest.Reason == ENavPathUpdateType::NavigationChanged)
				{
					RegisterActivePath(PinnedPath);
				}
			}
			else
			{
				PinnedPath->RePathFailed();
			}
		}

		RepathRequests.Reset();
		RepathRequests.Append(PostponedRequests);
	}
}