Program Listing for File GKAsyncTaskCooldownChanged.cpp
↰ Return to documentation for file (Source/Gamekit/Abilities/Blueprint/GKAsyncTaskCooldownChanged.cpp)
// MIT License Copyright(c) 2020 Dan Kestranek
// Include
#include "Gamekit/Abilities/Blueprint/GKAsyncTaskCooldownChanged.h"
// Gamekit
#include "Gamekit/GKLog.h"
#include "Gamekit/Gamekit.h"
UGKAsyncTaskCooldownChanged *UGKAsyncTaskCooldownChanged::ListenForCooldownChange(
UAbilitySystemComponent *AbilitySystemComponent,
FGameplayTagContainer InCooldownTags,
bool InUseServerCooldown)
{
UGKAsyncTaskCooldownChanged *ListenForCooldownChange = NewObject<UGKAsyncTaskCooldownChanged>();
ListenForCooldownChange->ASC = AbilitySystemComponent;
ListenForCooldownChange->CooldownTags = InCooldownTags;
ListenForCooldownChange->UseServerCooldown = InUseServerCooldown;
ListenForCooldownChange->bDestroyed = false;
if (!IsValid(AbilitySystemComponent) || InCooldownTags.Num() < 1)
{
ListenForCooldownChange->EndTask();
return nullptr;
}
TArray<FGameplayTag> CooldownTagArray;
InCooldownTags.GetGameplayTagArray(CooldownTagArray);
ListenForCooldownChange->DelegateHandles.Reserve(CooldownTagArray.Num() + 1);
ListenForCooldownChange->DelegateHandles.Add(
AbilitySystemComponent->OnActiveGameplayEffectAddedDelegateToSelf.AddUObject(
ListenForCooldownChange, &UGKAsyncTaskCooldownChanged::OnActiveGameplayEffectAddedCallback));
for (FGameplayTag CooldownTag: CooldownTagArray)
{
// Invalid tags would make this crash
if (!CooldownTag.IsValid() || CooldownTag == FGameplayTag::EmptyTag)
{
continue;
}
ListenForCooldownChange->DelegateHandles.Add(
AbilitySystemComponent->RegisterGameplayTagEvent(CooldownTag, EGameplayTagEventType::NewOrRemoved)
.AddUObject(ListenForCooldownChange, &UGKAsyncTaskCooldownChanged::CooldownTagChanged));
}
return ListenForCooldownChange;
}
void UGKAsyncTaskCooldownChanged::EndTask()
{
if (bDestroyed)
{
GK_ERROR(TEXT("Destroying object twice"));
return;
}
if (IsValid(this))
{
SetReadyToDestroy();
}
}
void UGKAsyncTaskCooldownChanged::SetReadyToDestroy()
{
if (IsValid(ASC))
{
ASC->OnActiveGameplayEffectAddedDelegateToSelf.Remove(DelegateHandles[0]);
TArray<FGameplayTag> CooldownTagArray;
CooldownTags.GetGameplayTagArray(CooldownTagArray);
int i = 0;
for (FGameplayTag CooldownTag: CooldownTagArray)
{
i += 1;
ASC->RegisterGameplayTagEvent(CooldownTag, EGameplayTagEventType::NewOrRemoved).Remove(DelegateHandles[i]);
}
}
Super::SetReadyToDestroy();
bDestroyed = true;
}
void UGKAsyncTaskCooldownChanged::OnActiveGameplayEffectAddedCallback(UAbilitySystemComponent *Target,
const FGameplayEffectSpec &SpecApplied,
FActiveGameplayEffectHandle ActiveHandle)
{
FGameplayTagContainer AssetTags;
SpecApplied.GetAllAssetTags(AssetTags);
FGameplayTagContainer GrantedTags;
SpecApplied.GetAllGrantedTags(GrantedTags);
TArray<FGameplayTag> CooldownTagArray;
CooldownTags.GetGameplayTagArray(CooldownTagArray);
for (FGameplayTag CooldownTag: CooldownTagArray)
{
if (AssetTags.HasTagExact(CooldownTag) || GrantedTags.HasTagExact(CooldownTag))
{
float TimeRemaining = 0.0f;
float Duration = 0.0f;
// Expecting cooldown tag to always be first tag
FGameplayTagContainer CooldownTagContainer(GrantedTags.GetByIndex(0));
GetCooldownRemainingForTag(CooldownTagContainer, TimeRemaining, Duration);
if (ASC->GetOwnerRole() == ROLE_Authority)
{
// Player is Server
OnCooldownBegin.Broadcast(CooldownTag, TimeRemaining, Duration);
}
else if (!UseServerCooldown && SpecApplied.GetContext().GetAbilityInstance_NotReplicated())
{
// Client using predicted cooldown
OnCooldownBegin.Broadcast(CooldownTag, TimeRemaining, Duration);
}
else if (UseServerCooldown && SpecApplied.GetContext().GetAbilityInstance_NotReplicated() == nullptr)
{
// Client using Server's cooldown. This is Server's corrective cooldown GE.
OnCooldownBegin.Broadcast(CooldownTag, TimeRemaining, Duration);
}
else if (UseServerCooldown && SpecApplied.GetContext().GetAbilityInstance_NotReplicated())
{
// Client using Server's cooldown but this is predicted cooldown GE.
// This can be useful to gray out abilities until Server's cooldown comes in.
OnCooldownBegin.Broadcast(CooldownTag, -1.0f, -1.0f);
}
}
}
}
void UGKAsyncTaskCooldownChanged::CooldownTagChanged(const FGameplayTag CooldownTag, int32 NewCount)
{
if (NewCount == 0)
{
OnCooldownEnd.Broadcast(CooldownTag, -1.0f, -1.0f);
}
}
bool UGKAsyncTaskCooldownChanged::GetCooldownRemainingForTag(FGameplayTagContainer InCooldownTags,
float &TimeRemaining,
float &CooldownDuration)
{
if (IsValid(ASC) && InCooldownTags.Num() > 0)
{
TimeRemaining = 0.f;
CooldownDuration = 0.f;
FGameplayEffectQuery const Query = FGameplayEffectQuery::MakeQuery_MatchAnyOwningTags(InCooldownTags);
TArray<TPair<float, float>> DurationAndTimeRemaining = ASC->GetActiveEffectsTimeRemainingAndDuration(Query);
if (DurationAndTimeRemaining.Num() > 0)
{
int32 BestIdx = 0;
float LongestTime = DurationAndTimeRemaining[0].Key;
for (int32 Idx = 1; Idx < DurationAndTimeRemaining.Num(); ++Idx)
{
if (DurationAndTimeRemaining[Idx].Key > LongestTime)
{
LongestTime = DurationAndTimeRemaining[Idx].Key;
BestIdx = Idx;
}
}
TimeRemaining = DurationAndTimeRemaining[BestIdx].Key;
CooldownDuration = DurationAndTimeRemaining[BestIdx].Value;
return true;
}
}
return false;
}