I had AI make an Ability Task in C++ that runs code on a timer. Will computer explode?

Hi, I’m not a programmer. I’m learning GAS, and I need an ability that can run some code, almost on tick. AI says using TimerByEvent in a Gameplay Ability is a No No. It suggested Ability Task, so I requested it. It works, but should I trust it? Here it is:

header

#pragma once

#include "CoreMinimal.h"
#include "Abilities/Tasks/AbilityTask.h"
#include "AbilityTask_OnInterval.generated.h"

/**
 * Delegate fired every interval tick.
 * @param TimeElapsed - Total time elapsed since task start.
 * @param TickCount - Number of elapsed ticks.
 */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnIntervalTick, float, TimeElapsed, int32, TickCount);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnIntervalFinished);

/**
 * Ability Task that triggers repeatedly at a given interval.
 */
UCLASS()
class MYPROJECT_API UAbilityTask_OnInterval : public UAbilityTask
{
	GENERATED_BODY()

public:
	UAbilityTask_OnInterval(const FObjectInitializer& ObjectInitializer);

	/** Creates and starts the task */
	UFUNCTION(BlueprintCallable, meta = (DisplayName = "AbilityTask_OnInterval", DefaultToSelf = "OwningAbility", HidePin = "OwningAbility", BlueprintInternalUseOnly = "TRUE"), Category = "Ability|Tasks")
	static UAbilityTask_OnInterval* OnInterval(UGameplayAbility* OwningAbility, float Interval, int32 MaxTicks = 0);

	/** Called every interval tick */
	UPROPERTY(BlueprintAssignable)
	FOnIntervalTick OnTick;

	UPROPERTY(BlueprintAssignable)
	FOnIntervalFinished OnFinished;


protected:
	virtual void Activate() override;
	virtual void OnDestroy(bool bInOwnerFinished) override;

private:
	FTimerHandle IntervalTimerHandle;

	float Interval;
	int32 MaxTicks;
	int32 TickCount;
	float TimeElapsed;

	void IntervalTick();
	void FinishTask();
};

cpp

#include "AbilityTask_OnInterval.h"
#include "AbilitySystemComponent.h"
#include "TimerManager.h"

UAbilityTask_OnInterval::UAbilityTask_OnInterval(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, Interval(0.f)
	, MaxTicks(0)
	, TickCount(0)
	, TimeElapsed(0.f)
{
}

UAbilityTask_OnInterval* UAbilityTask_OnInterval::OnInterval(UGameplayAbility* OwningAbility, float InInterval, int32 InMaxTicks)
{
	UAbilityTask_OnInterval* Task = NewAbilityTask<UAbilityTask_OnInterval>(OwningAbility);
	Task->Interval = FMath::Max(InInterval, KINDA_SMALL_NUMBER); // avoid zero or negative intervals
	Task->MaxTicks = InMaxTicks;
	return Task;
}

void UAbilityTask_OnInterval::Activate()
{
	TickCount = 0;
	TimeElapsed = 0.f;

	if (!Ability)
	{
		EndTask();
		return;
	}

	if (Ability->GetCurrentActorInfo() && Ability->GetCurrentActorInfo()->AvatarActor.IsValid())
	{
		UWorld* World = Ability->GetCurrentActorInfo()->AvatarActor->GetWorld();
		if (World)
		{
			World->GetTimerManager().SetTimer(IntervalTimerHandle, this, &UAbilityTask_OnInterval::IntervalTick, Interval, true);
			return; // timer set successfully
		}
	}

	// Fallback: if failed to set timer, end task immediately
	EndTask();
}

void UAbilityTask_OnInterval::IntervalTick()
{
	++TickCount;
	TimeElapsed += Interval;

	OnTick.Broadcast(TimeElapsed, TickCount);

	if (MaxTicks > 0 && TickCount >= MaxTicks)
	{
		FinishTask();
	}
}

void UAbilityTask_OnInterval::FinishTask()
{
	if (Ability && Ability->GetCurrentActorInfo() && Ability->GetCurrentActorInfo()->AvatarActor.IsValid())
	{
		UWorld* World = Ability->GetCurrentActorInfo()->AvatarActor->GetWorld();
		if (World)
		{
			World->GetTimerManager().ClearTimer(IntervalTimerHandle);
		}
	}

	OnFinished.Broadcast();
	EndTask();
}

void UAbilityTask_OnInterval::OnDestroy(bool bInOwnerFinished)
{
	if (Ability && Ability->GetCurrentActorInfo() && Ability->GetCurrentActorInfo()->AvatarActor.IsValid())
	{
		UWorld* World = Ability->GetCurrentActorInfo()->AvatarActor->GetWorld();
		if (World)
		{
			World->GetTimerManager().ClearTimer(IntervalTimerHandle);
		}
	}

	Super::OnDestroy(bInOwnerFinished);
}

Any feedback, much appreciate it.

looks fine, the only thing i’d suggest is to cache the World otherwise if its owner is destroyed it cant cancel the timer