Import helper plugin

This commit is contained in:
2026-07-15 19:17:47 +02:00
parent fd992725a2
commit 3c084d9669
72 changed files with 11853 additions and 1 deletions

View File

@@ -0,0 +1,245 @@
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
#include "Tasks/DirectiveUtilTask_AsyncLoadAsset.h"
#include "DirectiveUtilLogChannels.h"
#include "Engine/AssetManager.h"
#include "Engine/StreamableManager.h"
UDirectiveUtilTask_AsyncLoadAsset* UDirectiveUtilTask_AsyncLoadAsset::AsyncLoadAsset(UObject* WorldContextObject, const TSoftObjectPtr<UObject> Asset)
{
UDirectiveUtilTask_AsyncLoadAsset* Action = NewObject<UDirectiveUtilTask_AsyncLoadAsset>();
Action->SoftAsset = Asset;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_AsyncLoadAsset::Activate()
{
if (SoftAsset.IsNull())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset failed to activate. The soft object reference is null."));
Failed.Broadcast(nullptr);
SetReadyToDestroy();
return;
}
if (!UAssetManager::GetIfInitialized())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset failed to activate. The Asset Manager is not initialized."));
Failed.Broadcast(nullptr);
SetReadyToDestroy();
return;
}
StreamableHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(
SoftAsset.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadAsset::OnLoaded),
FStreamableManager::DefaultAsyncLoadPriority);
if (!StreamableHandle.IsValid())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset failed to start the load request."));
Failed.Broadcast(nullptr);
SetReadyToDestroy();
}
}
void UDirectiveUtilTask_AsyncLoadAsset::OnLoaded()
{
UObject* LoadedAsset = StreamableHandle.IsValid() ? StreamableHandle->GetLoadedAsset() : nullptr;
if (LoadedAsset)
{
Completed.Broadcast(LoadedAsset);
}
else
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset completed but the asset could not be resolved."));
Failed.Broadcast(nullptr);
}
SetReadyToDestroy();
}
void UDirectiveUtilTask_AsyncLoadAsset::Cancel()
{
if (StreamableHandle.IsValid() && StreamableHandle->IsActive())
{
StreamableHandle->CancelHandle();
}
SetReadyToDestroy();
}
UDirectiveUtilTask_AsyncLoadClass* UDirectiveUtilTask_AsyncLoadClass::AsyncLoadClass(UObject* WorldContextObject, const TSoftClassPtr<UObject> AssetClass)
{
UDirectiveUtilTask_AsyncLoadClass* Action = NewObject<UDirectiveUtilTask_AsyncLoadClass>();
Action->SoftClass = AssetClass;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_AsyncLoadClass::Activate()
{
if (SoftClass.IsNull())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class failed to activate. The soft class reference is null."));
Failed.Broadcast(nullptr);
SetReadyToDestroy();
return;
}
if (!UAssetManager::GetIfInitialized())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class failed to activate. The Asset Manager is not initialized."));
Failed.Broadcast(nullptr);
SetReadyToDestroy();
return;
}
StreamableHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(
SoftClass.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadClass::OnLoaded),
FStreamableManager::DefaultAsyncLoadPriority);
if (!StreamableHandle.IsValid())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class failed to start the load request."));
Failed.Broadcast(nullptr);
SetReadyToDestroy();
}
}
void UDirectiveUtilTask_AsyncLoadClass::OnLoaded()
{
UClass* LoadedClass = StreamableHandle.IsValid() ? Cast<UClass>(StreamableHandle->GetLoadedAsset()) : nullptr;
if (LoadedClass)
{
Completed.Broadcast(LoadedClass);
}
else
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class completed but the class could not be resolved."));
Failed.Broadcast(nullptr);
}
SetReadyToDestroy();
}
void UDirectiveUtilTask_AsyncLoadClass::Cancel()
{
if (StreamableHandle.IsValid() && StreamableHandle->IsActive())
{
StreamableHandle->CancelHandle();
}
SetReadyToDestroy();
}
UDirectiveUtilTask_AsyncLoadAssets* UDirectiveUtilTask_AsyncLoadAssets::AsyncLoadAssets(UObject* WorldContextObject, const TArray<TSoftObjectPtr<UObject>>& Assets)
{
UDirectiveUtilTask_AsyncLoadAssets* Action = NewObject<UDirectiveUtilTask_AsyncLoadAssets>();
Action->SoftAssets = Assets;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_AsyncLoadAssets::Activate()
{
// Unset references are filtered out of the request but keep their null slots in the output;
// duplicates are requested once and resolved per slot.
TArray<FSoftObjectPath> PathsToLoad;
for (const TSoftObjectPtr<UObject>& SoftAsset : SoftAssets)
{
if (!SoftAsset.IsNull())
{
PathsToLoad.AddUnique(SoftAsset.ToSoftObjectPath());
}
}
if (PathsToLoad.Num() == 0)
{
// Nothing to request; an empty request list is an error path in the streamable manager.
OnLoaded();
return;
}
if (!UAssetManager::GetIfInitialized())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Assets failed to activate. The Asset Manager is not initialized."));
OnLoaded();
return;
}
StreamableHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(
MoveTemp(PathsToLoad),
FStreamableDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadAssets::OnLoaded),
FStreamableManager::DefaultAsyncLoadPriority);
if (!StreamableHandle.IsValid())
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Assets failed to start the load request."));
OnLoaded();
return;
}
// Binding fails when the load already finished (all assets were in memory); complete directly,
// with the guard keeping the broadcast exactly once.
if (!StreamableHandle->BindUpdateDelegate(FStreamableUpdateDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadAssets::OnUpdate)))
{
OnLoaded();
}
}
void UDirectiveUtilTask_AsyncLoadAssets::OnLoaded()
{
if (bHasCompleted)
{
return;
}
bHasCompleted = true;
TArray<UObject*> LoadedAssets;
LoadedAssets.Reserve(SoftAssets.Num());
for (const TSoftObjectPtr<UObject>& SoftAsset : SoftAssets)
{
LoadedAssets.Add(SoftAsset.Get());
}
Completed.Broadcast(LoadedAssets);
SetReadyToDestroy();
}
void UDirectiveUtilTask_AsyncLoadAssets::OnUpdate(TSharedRef<FStreamableHandle> Handle)
{
if (bHasCompleted)
{
return;
}
int32 LoadedCount = 0;
int32 RequestedCount = 0;
Handle->GetLoadedCount(LoadedCount, RequestedCount);
Progress.Broadcast(LoadedCount, RequestedCount);
}
void UDirectiveUtilTask_AsyncLoadAssets::Cancel()
{
bHasCompleted = true;
if (StreamableHandle.IsValid() && StreamableHandle->IsActive())
{
StreamableHandle->CancelHandle();
}
SetReadyToDestroy();
}

View File

@@ -0,0 +1,139 @@
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
#include "Tasks/DirectiveUtilTask_AsyncTrace.h"
#include "DirectiveUtilLogChannels.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "WorldCollision.h"
#include "CollisionShape.h"
#include "CollisionQueryParams.h"
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncLineTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
{
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
Action->WorldContextObject = WorldContextObject;
Action->Start = Start;
Action->End = End;
Action->TraceChannel = TraceChannel;
Action->bMultiTrace = bMultiTrace;
Action->Shape = EDirectiveUtilTraceShape::Line;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncSphereTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const float Radius, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
{
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
Action->WorldContextObject = WorldContextObject;
Action->Start = Start;
Action->End = End;
Action->Radius = Radius;
Action->TraceChannel = TraceChannel;
Action->bMultiTrace = bMultiTrace;
Action->Shape = EDirectiveUtilTraceShape::Sphere;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncBoxTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const FVector HalfSize, const FRotator Orientation, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
{
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
Action->WorldContextObject = WorldContextObject;
Action->Start = Start;
Action->End = End;
Action->HalfSize = HalfSize;
Action->Orientation = Orientation.Quaternion();
Action->TraceChannel = TraceChannel;
Action->bMultiTrace = bMultiTrace;
Action->Shape = EDirectiveUtilTraceShape::Box;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncCapsuleTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const float Radius, const float HalfHeight, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
{
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
Action->WorldContextObject = WorldContextObject;
Action->Start = Start;
Action->End = End;
Action->Radius = Radius;
Action->HalfHeight = HalfHeight;
Action->TraceChannel = TraceChannel;
Action->bMultiTrace = bMultiTrace;
Action->Shape = EDirectiveUtilTraceShape::Capsule;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_AsyncTrace::Activate()
{
UWorld* World = WorldContextObject && GEngine
? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
: nullptr;
if (!World)
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Trace failed to activate. World is null."));
Completed.Broadcast(TArray<FHitResult>());
SetReadyToDestroy();
return;
}
const ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel);
const EAsyncTraceType AsyncType = bMultiTrace ? EAsyncTraceType::Multi : EAsyncTraceType::Single;
FTraceDelegate TraceDelegate;
TraceDelegate.BindUObject(this, &UDirectiveUtilTask_AsyncTrace::OnTraceComplete);
FCollisionQueryParams Params(FName(TEXT("DirectiveUtilAsyncTrace")), false);
if (Shape == EDirectiveUtilTraceShape::Line)
{
World->AsyncLineTraceByChannel(AsyncType, Start, End, CollisionChannel, Params, FCollisionResponseParams::DefaultResponseParam, &TraceDelegate);
}
else
{
FCollisionShape CollisionShape;
switch (Shape)
{
case EDirectiveUtilTraceShape::Sphere:
CollisionShape = FCollisionShape::MakeSphere(Radius);
break;
case EDirectiveUtilTraceShape::Box:
CollisionShape = FCollisionShape::MakeBox(HalfSize);
break;
case EDirectiveUtilTraceShape::Capsule:
CollisionShape = FCollisionShape::MakeCapsule(Radius, HalfHeight);
break;
default:
break;
}
World->AsyncSweepByChannel(AsyncType, Start, End, Orientation, CollisionChannel, CollisionShape, Params, FCollisionResponseParams::DefaultResponseParam, &TraceDelegate);
}
}
void UDirectiveUtilTask_AsyncTrace::OnTraceComplete(const FTraceHandle& Handle, FTraceDatum& Datum)
{
Completed.Broadcast(Datum.OutHits);
SetReadyToDestroy();
}

View File

@@ -0,0 +1,57 @@
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
#include "Tasks/DirectiveUtilTask_Delay.h"
#include "DirectiveUtilLogChannels.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "TimerManager.h"
UDirectiveUtilTask_Delay* UDirectiveUtilTask_Delay::CancellableDelay(UObject* WorldContextObject, const float Duration)
{
UDirectiveUtilTask_Delay* Action = NewObject<UDirectiveUtilTask_Delay>();
Action->WorldContextObject = WorldContextObject;
Action->Duration = Duration;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_Delay::EndTask()
{
if (UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) : nullptr)
{
World->GetTimerManager().ClearTimer(TimerHandle);
}
SetReadyToDestroy();
}
void UDirectiveUtilTask_Delay::Activate()
{
UWorld* World = WorldContextObject && GEngine
? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
: nullptr;
if (!World)
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Cancellable Delay failed to activate. World is null."));
SetReadyToDestroy();
return;
}
const float ClampedDuration = FMath::Max(Duration, KINDA_SMALL_NUMBER);
World->GetTimerManager().SetTimer(TimerHandle, this, &UDirectiveUtilTask_Delay::OnDelayComplete, ClampedDuration, false);
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Cancellable Delay started for %f seconds."), ClampedDuration);
}
void UDirectiveUtilTask_Delay::OnDelayComplete()
{
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Cancellable Delay completed."));
Completed.Broadcast();
SetReadyToDestroy();
}

View File

@@ -0,0 +1,280 @@
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
#include "Tasks/DirectiveUtilTask_MoveToLocation.h"
#include "DirectiveUtilLogChannels.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "Engine/World.h"
#include "DrawDebugHelpers.h"
#include "Navigation/PathFollowingComponent.h"
#include "TimerManager.h"
UDirectiveUtilTask_MoveToLocation* UDirectiveUtilTask_MoveToLocation::MoveToLocation(
UObject* WorldContextObject,
AController* Controller,
const FVector Destination,
const float AcceptanceRadius,
const bool bCheckStuckMovement,
const float StuckThreshold,
const bool bDebugLineTrace)
{
UDirectiveUtilTask_MoveToLocation* Action = NewObject<UDirectiveUtilTask_MoveToLocation>();
Action->Controller = Controller;
Action->Destination = Destination;
Action->AcceptanceRadius = AcceptanceRadius;
Action->bDebugLineTrace = bDebugLineTrace;
Action->StuckThreshold = StuckThreshold;
Action->bCheckStuckMovement = bCheckStuckMovement;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_MoveToLocation::EndTask()
{
ExecuteCompleted(false);
}
void UDirectiveUtilTask_MoveToLocation::Activate()
{
if(!Controller || !Controller->GetPawn())
{
ExecuteCompleted(false);
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to location. Aborting."));
return;
}
StartLocation = Controller->GetPawn()->GetActorLocation();
LastCheckedLocation = StartLocation;
CurrentLocation = StartLocation;
Controller->GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &UDirectiveUtilTask_MoveToLocation::CheckMoveToLocation, 0.1f, true);
if (bCheckStuckMovement)
{
Controller->GetWorld()->GetTimerManager().SetTimer(StuckTimerHandle, this, &UDirectiveUtilTask_MoveToLocation::CheckStuckMovement, 3.f, true);
}
UAIBlueprintHelperLibrary::SimpleMoveToLocation(Controller, Destination);
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Moving controller to location (%s)."), *Destination.ToString());
if (bDebugLineTrace)
{
DrawDebugLine(
Controller->GetWorld(),
Destination + FVector(0, 0, 100),
Destination,
FColor::Green,
false,
5.0f,
0,
1.0f
);
}
}
void UDirectiveUtilTask_MoveToLocation::CheckMoveToLocation()
{
if(!Controller || !Controller->GetPawn())
{
ExecuteCompleted(false);
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to location. Aborting."));
return;
}
CurrentLocation = Controller->GetPawn()->GetActorLocation();
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller is moving to location (%s). Current distance: %f."), *Destination.ToString(), FVector::Dist(CurrentLocation, Destination));
if (FVector::Dist(CurrentLocation, Destination) < AcceptanceRadius)
{
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller has moved to location."));
ExecuteCompleted(true);
return;
}
const UPathFollowingComponent* PathFollowing = Controller->FindComponentByClass<UPathFollowingComponent>();
if (!PathFollowing || PathFollowing->GetStatus() == EPathFollowingStatus::Idle)
{
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Path following has stopped. Completing move to location."));
ExecuteCompleted(FVector::Dist(CurrentLocation, Destination) < AcceptanceRadius);
}
}
void UDirectiveUtilTask_MoveToLocation::CheckStuckMovement()
{
if(!Controller || !Controller->GetPawn())
{
ExecuteCompleted(false);
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to location. Aborting."));
return;
}
if (FVector::Dist(CurrentLocation, LastCheckedLocation) < StuckThreshold)
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller is stuck while moving to location. Aborting"));
ExecuteCompleted(false);
}
LastCheckedLocation = CurrentLocation;
}
void UDirectiveUtilTask_MoveToLocation::ExecuteCompleted(const bool bSuccess)
{
if (bHasCompleted)
{
return;
}
bHasCompleted = true;
UE_LOG(LogDirectiveUtil, Log, TEXT("Movement to location completed. Success: %s."), bSuccess ? TEXT("true") : TEXT("false"));
if (Controller)
{
Controller->GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
Controller->GetWorld()->GetTimerManager().ClearTimer(StuckTimerHandle);
}
Completed.Broadcast(bSuccess);
Controller = nullptr;
Destination = FVector::ZeroVector;
SetReadyToDestroy();
}
UDirectiveUtilTask_MoveToActor* UDirectiveUtilTask_MoveToActor::MoveToActor(
UObject* WorldContextObject,
AController* Controller,
AActor* Goal,
const float AcceptanceRadius,
const bool bCheckStuckMovement,
const float StuckThreshold)
{
UDirectiveUtilTask_MoveToActor* Action = NewObject<UDirectiveUtilTask_MoveToActor>();
Action->Controller = Controller;
Action->Goal = Goal;
Action->AcceptanceRadius = AcceptanceRadius;
Action->StuckThreshold = StuckThreshold;
Action->bCheckStuckMovement = bCheckStuckMovement;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_MoveToActor::EndTask()
{
ExecuteCompleted(false);
}
void UDirectiveUtilTask_MoveToActor::Activate()
{
if(!Controller || !Controller->GetPawn() || !IsValid(Goal))
{
ExecuteCompleted(false);
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller, pawn, or goal has been destroyed while moving to actor. Aborting."));
return;
}
StartLocation = Controller->GetPawn()->GetActorLocation();
LastCheckedLocation = StartLocation;
CurrentLocation = StartLocation;
Controller->GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &UDirectiveUtilTask_MoveToActor::CheckMoveToActor, 0.1f, true);
if (bCheckStuckMovement)
{
Controller->GetWorld()->GetTimerManager().SetTimer(StuckTimerHandle, this, &UDirectiveUtilTask_MoveToActor::CheckStuckMovement, 3.f, true);
}
UAIBlueprintHelperLibrary::SimpleMoveToActor(Controller, Goal);
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Moving controller to actor (%s)."), *GetNameSafe(Goal));
}
void UDirectiveUtilTask_MoveToActor::CheckMoveToActor()
{
if(!Controller || !Controller->GetPawn())
{
ExecuteCompleted(false);
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to actor. Aborting."));
return;
}
if (!IsValid(Goal))
{
ExecuteCompleted(false);
UE_LOG(LogDirectiveUtil, Warning, TEXT("Goal actor has been destroyed while moving to actor. Aborting."));
return;
}
// The goal can move, so its location is re-read every poll.
const FVector GoalLocation = Goal->GetActorLocation();
CurrentLocation = Controller->GetPawn()->GetActorLocation();
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller is moving to actor (%s). Current distance: %f."), *GetNameSafe(Goal), FVector::Dist(CurrentLocation, GoalLocation));
if (FVector::Dist(CurrentLocation, GoalLocation) < AcceptanceRadius)
{
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller has moved to actor."));
ExecuteCompleted(true);
return;
}
const UPathFollowingComponent* PathFollowing = Controller->FindComponentByClass<UPathFollowingComponent>();
if (!PathFollowing || PathFollowing->GetStatus() == EPathFollowingStatus::Idle)
{
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Path following has stopped. Completing move to actor."));
ExecuteCompleted(FVector::Dist(CurrentLocation, GoalLocation) < AcceptanceRadius);
}
}
void UDirectiveUtilTask_MoveToActor::CheckStuckMovement()
{
if(!Controller || !Controller->GetPawn())
{
ExecuteCompleted(false);
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to actor. Aborting."));
return;
}
if (FVector::Dist(CurrentLocation, LastCheckedLocation) < StuckThreshold)
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller is stuck while moving to actor. Aborting"));
ExecuteCompleted(false);
}
LastCheckedLocation = CurrentLocation;
}
void UDirectiveUtilTask_MoveToActor::ExecuteCompleted(const bool bSuccess)
{
if (bHasCompleted)
{
return;
}
bHasCompleted = true;
UE_LOG(LogDirectiveUtil, Log, TEXT("Movement to actor completed. Success: %s."), bSuccess ? TEXT("true") : TEXT("false"));
if (Controller)
{
Controller->GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
Controller->GetWorld()->GetTimerManager().ClearTimer(StuckTimerHandle);
}
Completed.Broadcast(bSuccess);
Controller = nullptr;
Goal = nullptr;
SetReadyToDestroy();
}