// Copyright Epic Games, Inc. All Rights Reserved. #include "GameplayTask.h" #include "UObject/Package.h" #include "GameFramework/Actor.h" #include "VisualLogger/VisualLogger.h" #include "GameplayTaskResource.h" #include "GameplayTasksComponent.h" #if UE_WITH_IRIS #include "Iris/ReplicationSystem/ReplicationFragmentUtil.h" #endif // UE_WITH_IRIS #include UE_INLINE_GENERATED_CPP_BY_NAME(GameplayTask) UGameplayTask::UGameplayTask(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bTickingTask = false; bSimulatedTask = false; bIsSimulating = false; bOwnedByTasksComponent = false; bClaimRequiredResources = true; bOwnerFinished = false; TaskState = EGameplayTaskState::Uninitialized; ResourceOverlapPolicy = ETaskResourceOverlapPolicy::StartOnTop; Priority = FGameplayTasks::DefaultPriority; SetFlags(RF_StrongRefOnFrame); } IGameplayTaskOwnerInterface* UGameplayTask::ConvertToTaskOwner(UObject& OwnerObject) { IGameplayTaskOwnerInterface* OwnerInterface = Cast(&OwnerObject); if (OwnerInterface == nullptr) { AActor* AsActor = Cast(&OwnerObject); if (AsActor) { OwnerInterface = AsActor->FindComponentByClass(); } } return OwnerInterface; } IGameplayTaskOwnerInterface* UGameplayTask::ConvertToTaskOwner(AActor& OwnerActor) { IGameplayTaskOwnerInterface* OwnerInterface = Cast(&OwnerActor); if (OwnerInterface == nullptr) { OwnerInterface = OwnerActor.FindComponentByClass(); } return OwnerInterface; } void UGameplayTask::ReadyForActivation() { if (UGameplayTasksComponent* TasksPtr = TasksComponent.Get()) { if (RequiresPriorityOrResourceManagement() == false) { PerformActivation(); } else { TasksPtr->AddTaskReadyForActivation(*this); } } else { EndTask(); } } void UGameplayTask::InitTask(IGameplayTaskOwnerInterface& InTaskOwner, uint8 InPriority) { Priority = InPriority; TaskOwner = &InTaskOwner; TaskState = EGameplayTaskState::AwaitingActivation; if (bClaimRequiredResources) { ClaimedResources.AddSet(RequiredResources); } // call owner.OnGameplayTaskInitialized before accessing owner.GetGameplayTasksComponent, this is required for child tasks InTaskOwner.OnGameplayTaskInitialized(*this); UGameplayTasksComponent* GTComponent = InTaskOwner.GetGameplayTasksComponent(*this); TasksComponent = GTComponent; bOwnedByTasksComponent = (TaskOwner.GetObject() == GTComponent); // make sure that task component knows about new task if (GTComponent && !bOwnedByTasksComponent) { GTComponent->OnGameplayTaskInitialized(*this); } } void UGameplayTask::InitSimulatedTask(UGameplayTasksComponent& InGameplayTasksComponent) { TasksComponent = &InGameplayTasksComponent; bIsSimulating = true; } UWorld* UGameplayTask::GetWorld() const { if (UGameplayTasksComponent* TasksPtr = TasksComponent.Get()) { return TasksPtr->GetWorld(); } return nullptr; } AActor* UGameplayTask::GetOwnerActor() const { if (TaskOwner.IsValid()) { return TaskOwner->GetGameplayTaskOwner(this); } else if (UGameplayTasksComponent* TasksPtr = TasksComponent.Get()) { return TasksPtr->GetGameplayTaskOwner(this); } return nullptr; } AActor* UGameplayTask::GetAvatarActor() const { if (TaskOwner.IsValid()) { return TaskOwner->GetGameplayTaskAvatar(this); } else if (UGameplayTasksComponent* TasksPtr = TasksComponent.Get()) { return TasksPtr->GetGameplayTaskAvatar(this); } return nullptr; } void UGameplayTask::TaskOwnerEnded() { UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose , TEXT("%s TaskOwnerEnded called, current State: %s") , *GetName(), *GetTaskStateName()); if (TaskState != EGameplayTaskState::Finished) { bOwnerFinished = true; if (IsValidChecked(this)) { OnDestroy(true); } else { // mark as finished, just to be on the safe side TaskState = EGameplayTaskState::Finished; } } } void UGameplayTask::EndTask() { UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose , TEXT("%s EndTask called, current State: %s") , *GetName(), *GetTaskStateName()); if (TaskState != EGameplayTaskState::Finished) { if (IsValidChecked(this)) { OnDestroy(false); } else { // mark as finished, just to be on the safe side TaskState = EGameplayTaskState::Finished; } } } 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::ExternalCancel() { UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose , TEXT("%s ExternalCancel called, current State: %s") , *GetName(), *GetTaskStateName()); EndTask(); } void UGameplayTask::OnDestroy(bool bInOwnerFinished) { if (ensureMsgf(IsValidChecked(this), TEXT("OnDestroy called on invalid gameplay task"))) { const FString OwnerName = TaskOwner.IsValid() ? TaskOwner.GetObject()->GetName() : TEXT("Invalid GameplayTask Owner"); ensureMsgf(TaskState != EGameplayTaskState::Finished, TEXT("%s OnDestroy called, current state: %i, owner name: %s"), *GetName(), TaskState, *OwnerName); } TaskState = EGameplayTaskState::Finished; if (UGameplayTasksComponent* TasksPtr = TasksComponent.Get()) { TasksPtr->OnGameplayTaskDeactivated(*this); } MarkAsGarbage(); } FString UGameplayTask::GetDebugString() const { return FString::Printf(TEXT("%s (%s)"), *GetName(), *InstanceName.ToString()); } void UGameplayTask::AddRequiredResource(TSubclassOf RequiredResource) { check(RequiredResource); const uint8 ResourceID = UGameplayTaskResource::GetResourceID(RequiredResource); RequiredResources.AddID(ResourceID); } void UGameplayTask::AddRequiredResourceSet(const TArray >& RequiredResourceSet) { for (auto Resource : RequiredResourceSet) { if (Resource) { const uint8 ResourceID = UGameplayTaskResource::GetResourceID(Resource); RequiredResources.AddID(ResourceID); } } } void UGameplayTask::AddRequiredResourceSet(FGameplayResourceSet RequiredResourceSet) { RequiredResources.AddSet(RequiredResourceSet); } void UGameplayTask::AddClaimedResource(TSubclassOf ClaimedResource) { check(ClaimedResource); const uint8 ResourceID = UGameplayTaskResource::GetResourceID(ClaimedResource); ClaimedResources.AddID(ResourceID); } void UGameplayTask::AddClaimedResourceSet(const TArray >& AdditionalResourcesToClaim) { for (auto ResourceClass : AdditionalResourcesToClaim) { if (ResourceClass) { ClaimedResources.AddID(UGameplayTaskResource::GetResourceID(ResourceClass)); } } } void UGameplayTask::AddClaimedResourceSet(FGameplayResourceSet AdditionalResourcesToClaim) { ClaimedResources.AddSet(AdditionalResourcesToClaim); } 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(); // Activate call may result in the task actually "instantly" finishing. // If this happens we don't want to bother the TaskComponent // with information on this task if (IsFinished() == false) { TasksComponent->OnGameplayTaskActivated(*this); } } void UGameplayTask::Activate() { UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose , TEXT("%s Activate called, current State: %s") , *GetName(), *GetTaskStateName()); } 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::Resume() { UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose , TEXT("%s Resume called, current State: %s") , *GetName(), *GetTaskStateName()); TaskState = EGameplayTaskState::Active; UE_CLOG(TasksComponent.IsValid() == false, LogGameplayTasks, Error , TEXT("%s Resume called while TasksComponent is no longer valid"), *GetName()); if (TasksComponent.IsValid()) { TasksComponent->OnGameplayTaskActivated(*this); } } //----------------------------------------------------------------------// // 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::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; } } #if WITH_GAMEPLAYTASK_DEBUG //----------------------------------------------------------------------// // debug //----------------------------------------------------------------------// FString UGameplayTask::GenerateDebugDescription() const { if (RequiresPriorityOrResourceManagement()) { UObject* OwnerOb = Cast(GetTaskOwner()); return FString::Printf(TEXT("%s:%s Pri:%d Owner:%s Res:%s"), *GetName(), InstanceName != NAME_None ? *InstanceName.ToString() : TEXT("-"), (int32)Priority, *GetNameSafe(OwnerOb), *RequiredResources.GetDebugDescription()); } return GetName(); } FString UGameplayTask::GetTaskStateName() const { static const UEnum* Enum = StaticEnum(); check(Enum); return Enum->GetNameStringByValue(int64(TaskState)); } #endif // WITH_GAMEPLAYTASK_DEBUG ////////////////////////////////////////////////////////////////////////// // Child tasks UGameplayTasksComponent* UGameplayTask::GetGameplayTasksComponent(const UGameplayTask& Task) const { return ((&Task == ChildTask) || (&Task == this)) ? GetGameplayTasksComponent() : nullptr; } AActor* UGameplayTask::GetGameplayTaskOwner(const UGameplayTask* Task) const { return ((Task == ChildTask) || (Task == this)) ? UGameplayTask::GetOwnerActor() : nullptr; } AActor* UGameplayTask::GetGameplayTaskAvatar(const UGameplayTask* Task) const { return ((Task == ChildTask) || (Task == this)) ? UGameplayTask::GetAvatarActor() : nullptr; } uint8 UGameplayTask::GetGameplayTaskDefaultPriority() const { return GetPriority(); } 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::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; } #if UE_WITH_IRIS void UGameplayTask::RegisterReplicationFragments(UE::Net::FFragmentRegistrationContext& Context, UE::Net::EFragmentRegistrationFlags RegistrationFlags) { // Build descriptors and allocate PropertyReplicationFragments for this object. UE::Net::FReplicationFragmentUtil::CreateAndRegisterFragmentsForObject(this, Context, RegistrationFlags); } #endif // UE_WITH_IRIS