Files
ProjectEleri/Plugins/DirectiveUtilities/Source/DirectiveUtilitiesRuntime/Private/Tasks/DirectiveUtilTask_Flow.cpp

287 lines
7.0 KiB
C++

// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
#include "Tasks/DirectiveUtilTask_Flow.h"
#include "DirectiveUtilLogChannels.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "TimerManager.h"
UDirectiveUtilTask_UpdateForDuration* UDirectiveUtilTask_UpdateForDuration::UpdateForDuration(
UObject* WorldContextObject,
const float Duration,
const float UpdateInterval)
{
UDirectiveUtilTask_UpdateForDuration* Action = NewObject<UDirectiveUtilTask_UpdateForDuration>();
Action->WorldContextObject = WorldContextObject;
Action->Duration = Duration;
Action->UpdateInterval = UpdateInterval;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_UpdateForDuration::Activate()
{
UWorld* World = WorldContextObject && GEngine
? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
: nullptr;
if (!World)
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Update for Duration failed to activate. World is null."));
bFinished = true;
SetReadyToDestroy();
return;
}
FTimerManager& TimerManager = World->GetTimerManager();
if (!FMath::IsFinite(Duration) || Duration <= 0.0f)
{
CompletionTimerHandle = TimerManager.SetTimerForNextTick(this, &UDirectiveUtilTask_UpdateForDuration::OnComplete);
return;
}
TimerManager.SetTimer(
CompletionTimerHandle,
this,
&UDirectiveUtilTask_UpdateForDuration::OnComplete,
Duration,
false);
if (!FMath::IsFinite(UpdateInterval) || UpdateInterval <= 0.0f)
{
UpdateTimerHandle = TimerManager.SetTimerForNextTick(this, &UDirectiveUtilTask_UpdateForDuration::OnUpdate);
return;
}
FTimerManagerTimerParameters UpdateParameters;
UpdateParameters.bLoop = true;
UpdateParameters.bMaxOncePerFrame = true;
UpdateParameters.FirstDelay = UpdateInterval;
TimerManager.SetTimer(
UpdateTimerHandle,
this,
&UDirectiveUtilTask_UpdateForDuration::OnUpdate,
UpdateInterval,
UpdateParameters);
}
void UDirectiveUtilTask_UpdateForDuration::Cancel()
{
bFinished = true;
ClearTimers();
Updated.Clear();
Completed.Clear();
Super::Cancel();
}
bool UDirectiveUtilTask_UpdateForDuration::IsActive() const
{
return !bFinished && Super::IsActive();
}
bool UDirectiveUtilTask_UpdateForDuration::ShouldBroadcastDelegates() const
{
return !bFinished && Super::ShouldBroadcastDelegates();
}
void UDirectiveUtilTask_UpdateForDuration::OnUpdate()
{
if (!ShouldBroadcastDelegates())
{
ClearTimers();
return;
}
FTimerManager* TimerManager = GetTimerManager();
if (!TimerManager)
{
Cancel();
return;
}
const float RemainingTime = TimerManager->GetTimerRemaining(CompletionTimerHandle);
const float ElapsedTime = RemainingTime >= 0.0f
? FMath::Clamp(Duration - RemainingTime, 0.0f, Duration)
: Duration;
BroadcastUpdate(ElapsedTime, ElapsedTime / Duration);
if (ShouldBroadcastDelegates() && (!FMath::IsFinite(UpdateInterval) || UpdateInterval <= 0.0f))
{
UpdateTimerHandle = TimerManager->SetTimerForNextTick(this, &UDirectiveUtilTask_UpdateForDuration::OnUpdate);
}
}
void UDirectiveUtilTask_UpdateForDuration::OnComplete()
{
if (!ShouldBroadcastDelegates())
{
ClearTimers();
return;
}
if (!bHasUpdated || LastElapsedTime < Duration)
{
const float FinalElapsedTime = FMath::IsFinite(Duration) && Duration > 0.0f ? Duration : 0.0f;
BroadcastUpdate(FinalElapsedTime, 1.0f);
}
if (!ShouldBroadcastDelegates())
{
return;
}
ClearTimers();
Completed.Broadcast();
bFinished = true;
SetReadyToDestroy();
}
void UDirectiveUtilTask_UpdateForDuration::BroadcastUpdate(const float ElapsedTime, const float Alpha)
{
const float DeltaTime = bHasUpdated ? FMath::Max(ElapsedTime - LastElapsedTime, 0.0f) : ElapsedTime;
LastElapsedTime = ElapsedTime;
bHasUpdated = true;
Updated.Broadcast(ElapsedTime, DeltaTime, Alpha);
}
void UDirectiveUtilTask_UpdateForDuration::ClearTimers()
{
if (FTimerManager* TimerManager = GetTimerManager())
{
TimerManager->ClearTimer(UpdateTimerHandle);
TimerManager->ClearTimer(CompletionTimerHandle);
}
}
UDirectiveUtilTask_RepeatWithInterval* UDirectiveUtilTask_RepeatWithInterval::RepeatWithInterval(
UObject* WorldContextObject,
const int32 Count,
const float Interval,
const float InitialDelay)
{
UDirectiveUtilTask_RepeatWithInterval* Action = NewObject<UDirectiveUtilTask_RepeatWithInterval>();
Action->WorldContextObject = WorldContextObject;
Action->Count = Count;
Action->Interval = Interval;
Action->InitialDelay = InitialDelay;
if (WorldContextObject)
{
Action->RegisterWithGameInstance(WorldContextObject);
}
return Action;
}
void UDirectiveUtilTask_RepeatWithInterval::Activate()
{
UWorld* World = WorldContextObject && GEngine
? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
: nullptr;
if (!World)
{
UE_LOG(LogDirectiveUtil, Warning, TEXT("Repeat with Interval failed to activate. World is null."));
bFinished = true;
SetReadyToDestroy();
return;
}
if (Count == 0 || Count < -1)
{
TimerHandle = World->GetTimerManager().SetTimerForNextTick(this, &UDirectiveUtilTask_RepeatWithInterval::Complete);
return;
}
const float SafeInitialDelay = FMath::IsFinite(InitialDelay) && InitialDelay > 0.0f
? InitialDelay
: 0.0f;
Schedule(SafeInitialDelay);
}
void UDirectiveUtilTask_RepeatWithInterval::Cancel()
{
bFinished = true;
ClearTimer();
Iteration.Clear();
Completed.Clear();
Super::Cancel();
}
bool UDirectiveUtilTask_RepeatWithInterval::IsActive() const
{
return !bFinished && Super::IsActive();
}
bool UDirectiveUtilTask_RepeatWithInterval::ShouldBroadcastDelegates() const
{
return !bFinished && Super::ShouldBroadcastDelegates();
}
void UDirectiveUtilTask_RepeatWithInterval::Schedule(const float Delay)
{
FTimerManager* TimerManager = GetTimerManager();
if (!TimerManager)
{
Cancel();
return;
}
if (Delay > 0.0f)
{
TimerManager->SetTimer(TimerHandle, this, &UDirectiveUtilTask_RepeatWithInterval::OnIteration, Delay, false);
}
else
{
TimerHandle = TimerManager->SetTimerForNextTick(this, &UDirectiveUtilTask_RepeatWithInterval::OnIteration);
}
}
void UDirectiveUtilTask_RepeatWithInterval::OnIteration()
{
if (!ShouldBroadcastDelegates())
{
ClearTimer();
return;
}
const int32 CurrentIndex = NextIndex;
NextIndex = NextIndex == MAX_int32 ? 0 : NextIndex + 1;
const int32 Remaining = Count == -1 ? -1 : Count - NextIndex;
Iteration.Broadcast(CurrentIndex, Remaining);
if (!ShouldBroadcastDelegates())
{
return;
}
if (Count != -1 && NextIndex >= Count)
{
Complete();
return;
}
const float SafeInterval = FMath::IsFinite(Interval) && Interval > 0.0f
? Interval
: 0.0f;
Schedule(SafeInterval);
}
void UDirectiveUtilTask_RepeatWithInterval::Complete()
{
if (!ShouldBroadcastDelegates())
{
ClearTimer();
return;
}
ClearTimer();
Completed.Broadcast();
bFinished = true;
SetReadyToDestroy();
}
void UDirectiveUtilTask_RepeatWithInterval::ClearTimer()
{
if (FTimerManager* TimerManager = GetTimerManager())
{
TimerManager->ClearTimer(TimerHandle);
}
}