void UAITask_MoveTo::ConditionalUpdatePath()
{
	// mark this path as waiting for repath so that PathFollowingComponent doesn't abort the move while we 
	// micro manage repathing moment
	// note that this flag fill get cleared upon repathing end
	if (Path.IsValid())
	{
		Path->SetManualRepathWaiting(true);
	}

	if (MoveRequest.IsUsingPathfinding() && OwnerController && OwnerController->ShouldPostponePathUpdates())
	{
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT("%s> can't path right now, waiting..."), *GetName());
		OwnerController->GetWorldTimerManager().SetTimer(PathRetryTimerHandle, this, &UAITask_MoveTo::ConditionalUpdatePath, 0.2f, false);
	}
	else
	{
		PathRetryTimerHandle.Invalidate();
		
		ANavigationData* NavData = Path.IsValid() ? Path->GetNavigationDataUsed() : nullptr;
		if (NavData)
		{
			NavData->RequestRePath(Path, ENavPathUpdateType::NavigationChanged);
		}
		else
		{
			UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT("%s> unable to repath, aborting!"), *GetName());
			FinishMoveTask(EPathFollowingResult::Aborted);
		}
	}
}
void UGameplayTask::PauseInTaskQueue()
{
	switch (TaskState)
	{
	case EGameplayTaskState::Uninitialized:
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Error
			, TEXT("UGameplayTask::PauseInTaskQueue Task %s passed for pausing withouth having InitTask called on it!")
			, *GetName());
		break;
	case EGameplayTaskState::AwaitingActivation:
		// nothing to do here. Don't change the state to indicate this task has never been run before
		break;
	case EGameplayTaskState::Paused:
		// nothing to do here. Already paused
		break;
	case EGameplayTaskState::Active:
		// pause!
		Pause();
		break;
	case EGameplayTaskState::Finished:
		// nothing to do here. But sounds odd, so let's log this, just in case
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log
			, TEXT("UGameplayTask::PauseInTaskQueue Task %s being pause while already marked as Finished")
			, *GetName());
		break;
	default:
		checkNoEntry(); // looks like unhandled value! Probably a new enum entry has been added
		break;
	}
}
void UGameplayTask::OnGameplayTaskInitialized(UGameplayTask& Task)
{
    UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose, TEXT("%s> Child task initialized: %s"), *GetName(), *Task.GetName());

    // only one child task is allowed
    if (ChildTask)
    {
        UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose, TEXT(">> terminating previous child task: %s"), *ChildTask->GetName());
        ChildTask->EndTask();
    }

    ChildTask = &Task;
}
//----------------------------------------------------------------------//
// GameplayTasksComponent-related functions
//----------------------------------------------------------------------//
void UGameplayTask::ActivateInTaskQueue()
{
	switch(TaskState)
	{
	case EGameplayTaskState::Uninitialized:
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Error
			, TEXT("UGameplayTask::ActivateInTaskQueue Task %s passed for activation withouth having InitTask called on it!")
			, *GetName());
		break;
	case EGameplayTaskState::AwaitingActivation:
		PerformActivation();
		break;
	case EGameplayTaskState::Paused:
		// resume
		Resume();
		break;
	case EGameplayTaskState::Active:
		// nothing to do here
		break;
	case EGameplayTaskState::Finished:
		// If a task has finished, and it's being revived let's just treat the same as AwaitingActivation
		PerformActivation();
		break;
	default:
		checkNoEntry(); // looks like unhandled value! Probably a new enum entry has been added
		break;
	}
}
void UGameplayTask::ExternalCancel()
{
	UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
		, TEXT("%s ExternalCancel called, current State: %s")
		, *GetName(), *GetTaskStateName());

	EndTask();
}
void UAITask_MoveTo::Resume()
{
	Super::Resume();

	if (!MoveRequestID.IsValid() || !OwnerController->ResumeMove(MoveRequestID))
	{
		UE_CVLOG(MoveRequestID.IsValid(), GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT("%s> Resume move failed, starting new one."), *GetName());
		ConditionalPerformMove();
	}
}
void UGameplayTask::Resume()
{
    UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
            , TEXT("%s Resume called, current State: %s")
            , *GetName(), *GetTaskStateName());

    TaskState = EGameplayTaskState::Active;

    TasksComponent->OnGameplayTaskActivated(*this);
}
void UGameplayTask::Pause()
{
    UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
            , TEXT("%s Pause called, current State: %s")
            , *GetName(), *GetTaskStateName());

    TaskState = EGameplayTaskState::Paused;

    TasksComponent->OnGameplayTaskDeactivated(*this);
}
void UGameplayTask::EndTask()
{
	UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
		, TEXT("%s EndTask called, current State: %s")
		, *GetName(), *GetTaskStateName());

	if (TaskState != EGameplayTaskState::Finished && !IsPendingKill())
	{
		OnDestroy(false);
	}
}
void UAITask_MoveTo::OnRequestFinished(FAIRequestID RequestID, const FPathFollowingResult& Result)
{
	if (RequestID == MoveRequestID)
	{
		if (Result.HasFlag(FPathFollowingResultFlags::UserAbort) && Result.HasFlag(FPathFollowingResultFlags::NewRequest) && !Result.HasFlag(FPathFollowingResultFlags::ForcedScript))
		{
			UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT("%s> ignoring OnRequestFinished, move was aborted by new request"), *GetName());
		}
		else
		{
			// reset request Id, FinishMoveTask doesn't need to update path following's state
			MoveRequestID = FAIRequestID::InvalidRequest;
			FinishMoveTask(Result.Code);
		}
	}
	else if (IsActive())
	{
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Warning, TEXT("%s> received OnRequestFinished with not matching RequestID!"), *GetName());
	}
}
void UGameplayTask::ExternalConfirm(bool bEndTask)
{
	UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
		, TEXT("%s ExternalConfirm called, bEndTask = %s, State : %s")
		, *GetName(), bEndTask ? TEXT("TRUE") : TEXT("FALSE"), *GetTaskStateName());

	if (bEndTask)
	{
		EndTask();
	}
}
void UGameplayTask::OnGameplayTaskDeactivated(UGameplayTask& Task)
{
    // cleanup after deactivation
    if (&Task == ChildTask)
    {
        UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose, TEXT("%s> Child task deactivated: %s (state: %s)"), *GetName(), *Task.GetName(), *Task.GetTaskStateName());
        if (Task.IsFinished())
        {
            ChildTask = nullptr;
        }
    }
}
void UGameplayTask::TaskOwnerEnded()
{
    UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
            , TEXT("%s TaskOwnerEnded called, current State: %s")
            , *GetName(), *GetTaskStateName());

    if (TaskState != EGameplayTaskState::Finished && !IsPendingKill())
    {
        bOwnerFinished = true;
        OnDestroy(true);
    }
}
void UAITask_MoveTo::ConditionalPerformMove()
{
	if (MoveRequest.IsUsingPathfinding() && OwnerController && OwnerController->ShouldPostponePathUpdates())
	{
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT("%s> can't path right now, waiting..."), *GetName());
		OwnerController->GetWorldTimerManager().SetTimer(MoveRetryTimerHandle, this, &UAITask_MoveTo::ConditionalPerformMove, 0.2f, false);
	}
	else
	{
		MoveRetryTimerHandle.Invalidate();
		PerformMove();
	}
}
void UAITask_MoveTo::OnPathEvent(FNavigationPath* InPath, ENavPathEvent::Type Event)
{
	const static UEnum* NavPathEventEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ENavPathEvent"));
	UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT("%s> Path event: %s"), *GetName(), *NavPathEventEnum->GetEnumName(Event));

	switch (Event)
	{
	case ENavPathEvent::NewPath:
	case ENavPathEvent::UpdatedDueToGoalMoved:
	case ENavPathEvent::UpdatedDueToNavigationChanged:
		if (InPath && InPath->IsPartial() && !MoveRequest.IsUsingPartialPaths())
		{
			UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT(">> partial path is not allowed, aborting"));
			UPathFollowingComponent::LogPathHelper(OwnerController, InPath, MoveRequest.GetGoalActor());
			FinishMoveTask(EPathFollowingResult::Aborted);
		}
#if ENABLE_VISUAL_LOG
		else if (!IsActive())
		{
			UPathFollowingComponent::LogPathHelper(OwnerController, InPath, MoveRequest.GetGoalActor());
		}
#endif // ENABLE_VISUAL_LOG
		break;

	case ENavPathEvent::Invalidated:
		ConditionalUpdatePath();
		break;

	case ENavPathEvent::Cleared:
	case ENavPathEvent::RePathFailed:
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Log, TEXT(">> no path, aborting!"));
		FinishMoveTask(EPathFollowingResult::Aborted);
		break;

	case ENavPathEvent::MetaPathUpdate:
	default:
		break;
	}
}
void UGameplayTask::Resume()
{
	UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
		, TEXT("%s Resume called, current State: %s")
		, *GetName(), *GetTaskStateName());

	TaskState = EGameplayTaskState::Active;

	TasksComponent->OnTaskActivated(*this);

	if (bOwnedByTasksComponent == false && TaskOwner.IsValid())
	{
		TaskOwner->OnTaskActivated(*this);
	}
}
void UGameplayTask::PerformActivation()
{
    if (TaskState == EGameplayTaskState::Active)
    {
        UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Warning
                , TEXT("%s PerformActivation called while TaskState is already Active. Bailing out.")
                , *GetName());
        return;
    }

    TaskState = EGameplayTaskState::Active;

    Activate();

    TasksComponent->OnGameplayTaskActivated(*this);
}
void UAITask_MoveTo::PerformMove()
{
	UPathFollowingComponent* PFComp = OwnerController ? OwnerController->GetPathFollowingComponent() : nullptr;
	if (PFComp == nullptr)
	{
		FinishMoveTask(EPathFollowingResult::Invalid);
		return;
	}

	ResetObservers();
	ResetTimers();

	// start new move request
	FNavPathSharedPtr FollowedPath;
	const FPathFollowingRequestResult ResultData = OwnerController->MoveTo(MoveRequest, &FollowedPath);

	switch (ResultData.Code)
	{
	case EPathFollowingRequestResult::Failed:
		FinishMoveTask(EPathFollowingResult::Invalid);
		break;

	case EPathFollowingRequestResult::AlreadyAtGoal:
		MoveRequestID = ResultData.MoveId;
		OnRequestFinished(ResultData.MoveId, FPathFollowingResult(EPathFollowingResult::Success, FPathFollowingResultFlags::AlreadyAtGoal));
		break;

	case EPathFollowingRequestResult::RequestSuccessful:
		MoveRequestID = ResultData.MoveId;
		PathFinishDelegateHandle = PFComp->OnRequestFinished.AddUObject(this, &UAITask_MoveTo::OnRequestFinished);
		SetObservedPath(FollowedPath);

		if (TaskState == EGameplayTaskState::Finished)
		{
			UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Error, TEXT("%s> re-Activating Finished task!"), *GetName());
		}
		break;

	default:
		checkNoEntry();
		break;
	}
}
Esempio n. 19
0
void UAITask_MoveTo::HandleMoveFinished(FAIRequestID RequestID, EPathFollowingResult::Type Result)
{
	if (RequestID == MoveRequestID)
	{
		if (Result == EPathFollowingResult::Success)
		{
			EndTask();
			OnMoveFinished.Broadcast(Result);
		}
		else
		{
			Pause();
		}
	}
	else
	{
		// @todo report issue to the owner component
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Warning, TEXT("%s got movement-finished-notification but RequestID doesn't match. Possible task leak"), *GetName());
	}
}
UGameplayTasksComponent* UGameplayTask::GetGameplayTasksComponent(const UGameplayTask& Task) const
{
    return ((&Task == ChildTask) || (&Task == this)) ? GetGameplayTasksComponent() : nullptr;
}
void UGameplayTask::Activate()
{
	UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
		, TEXT("%s Activate called, current State: %s")
		, *GetName(), *GetTaskStateName());
}