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 UNavigationPath::SetPathPointsFromPath(FNavigationPath& NativePath)
{
	PathPoints.Reset(NativePath.GetPathPoints().Num());
	for (const auto& PathPoint : NativePath.GetPathPoints())
	{
		PathPoints.Add(PathPoint.Location);
	}
}
void FGameplayDebuggerCategory_AI::CollectPathData(AAIController* DebugAI)
{
	UPathFollowingComponent* PathComp = DebugAI ? DebugAI->GetPathFollowingComponent() : nullptr;
	DataPack.bIsUsingPathFollowing = (PathComp != nullptr);

	if (PathComp)
	{
		TArray<FString> Tokens;
		TArray<EPathFollowingDebugTokens::Type> Flags;
		PathComp->GetDebugStringTokens(Tokens, Flags);

		for (int32 Idx = 0; Idx < Tokens.Num(); Idx++)
		{
			switch (Flags[Idx])
			{
			case EPathFollowingDebugTokens::Description:
				DataPack.PathFollowingInfo += Tokens[Idx];
				break;

			case EPathFollowingDebugTokens::ParamName:
				DataPack.PathFollowingInfo += TEXT(", {yellow}");
				DataPack.PathFollowingInfo += Tokens[Idx];
				DataPack.PathFollowingInfo += TEXT(":");
				break;

			case EPathFollowingDebugTokens::PassedValue:
				DataPack.PathFollowingInfo += TEXT("{yellow}");
				DataPack.PathFollowingInfo += Tokens[Idx];
				break;

			case EPathFollowingDebugTokens::FailedValue:
				DataPack.PathFollowingInfo += TEXT("{orange}");
				DataPack.PathFollowingInfo += Tokens[Idx];
				break;

			default:
				break;
			}
		}

		FNavigationPath* CurrentPath = PathComp->GetPath().Get();
		if (CurrentPath)
		{
			DataPack.bPathHasGoalActor = (CurrentPath->GetGoalActor() != nullptr);
			DataPack.PathGoalLocation = CurrentPath->GetGoalLocation();
			DataPack.NextPathPointIndex = PathComp->GetNextPathIndex();
		}

		if ((CurrentPath != RawLastPath) || (CurrentPath && (CurrentPath->GetLastUpdateTime() != LastPathUpdateTime) ))
		{
			RawLastPath = CurrentPath;
			PathDataPack = FRepDataPath();

			if (CurrentPath)
			{
				LastPathUpdateTime = CurrentPath->GetLastUpdateTime();

				const FNavMeshPath* NavMeshPath = CurrentPath->CastPath<FNavMeshPath>();
				const ARecastNavMesh* NavData = Cast<const ARecastNavMesh>(CurrentPath->GetNavigationDataUsed());
				if (NavMeshPath && NavData)
				{
					for (int32 Idx = 0; Idx < NavMeshPath->PathCorridor.Num(); Idx++)
					{
						FRepDataPath::FPoly PolyData;
						NavData->GetPolyVerts(NavMeshPath->PathCorridor[Idx], PolyData.Points);
						
						const uint32 AreaId = NavData->GetPolyAreaID(NavMeshPath->PathCorridor[Idx]);
						PolyData.Color = NavData->GetAreaIDColor(AreaId);

						PathDataPack.PathCorridor.Add(PolyData);
					}
				}

				for (int32 Idx = 0; Idx < CurrentPath->GetPathPoints().Num(); Idx++)
				{
					PathDataPack.PathPoints.Add(CurrentPath->GetPathPoints()[Idx].Location);
				}
			}
		}
	}
}
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);
	}
}