// Copyright (c) 2026 Reignance Studios. MIT License, see LICENSE. #include "GKLGameplayAbility.h" #include "AbilitySystemComponent.h" #include "AbilitySystemGlobals.h" #include "GKLAttributeSet.h" #include "GKLCharacter.h" #include "GKLEffects.h" #include "GKLGameplayTags.h" #include "Misc/EngineVersionComparison.h" UGKLGameplayAbility::UGKLGameplayAbility() { InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted; CostGameplayEffectClass = UGKLCostEffect::StaticClass(); CooldownGameplayEffectClass = UGKLCooldownEffect::StaticClass(); ActivationBlockedTags.AddTag(GKLTags::State_Dead); } UGameplayEffect* UGKLGameplayAbility::GetCostGameplayEffect() const { return (StaminaCost > 0.f || ManaCost > 0.f) ? Super::GetCostGameplayEffect() : nullptr; } UGameplayEffect* UGKLGameplayAbility::GetCooldownGameplayEffect() const { return CooldownDuration > 0.f ? Super::GetCooldownGameplayEffect() : nullptr; } const FGameplayTagContainer* UGKLGameplayAbility::GetCooldownTags() const { TempCooldownTags.Reset(); if (const FGameplayTagContainer* EffectTags = Super::GetCooldownTags()) { TempCooldownTags.AppendTags(*EffectTags); } if (CooldownDuration > 0.f) { TempCooldownTags.AppendTags(CooldownTags); } return &TempCooldownTags; } bool UGKLGameplayAbility::CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, FGameplayTagContainer* OptionalRelevantTags) const { if (!GetCostGameplayEffect()) { return true; } const UAbilitySystemComponent* ASC = ActorInfo ? ActorInfo->AbilitySystemComponent.Get() : nullptr; if (!ASC) { return false; } // The cost effect's magnitudes are SetByCaller, so the default check cannot evaluate them. const bool bEnough = ASC->GetNumericAttribute(UGKLAttributeSet::GetStaminaAttribute()) >= StaminaCost && ASC->GetNumericAttribute(UGKLAttributeSet::GetManaAttribute()) >= ManaCost; if (!bEnough && OptionalRelevantTags) { OptionalRelevantTags->AddTag(UAbilitySystemGlobals::Get().ActivateFailCostTag); } return bEnough; } void UGKLGameplayAbility::ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const { const UGameplayEffect* CostEffect = GetCostGameplayEffect(); if (!CostEffect) { return; } const FGameplayEffectSpecHandle Spec = MakeOutgoingGameplayEffectSpec(Handle, ActorInfo, ActivationInfo, CostEffect->GetClass(), GetAbilityLevel(Handle, ActorInfo)); if (Spec.IsValid()) { Spec.Data->SetSetByCallerMagnitude(GKLTags::Data_Cost_Stamina, -StaminaCost); Spec.Data->SetSetByCallerMagnitude(GKLTags::Data_Cost_Mana, -ManaCost); ApplyGameplayEffectSpecToOwner(Handle, ActorInfo, ActivationInfo, Spec); } } void UGKLGameplayAbility::ApplyCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const { const UGameplayEffect* CooldownEffect = GetCooldownGameplayEffect(); if (!CooldownEffect) { return; } const FGameplayEffectSpecHandle Spec = MakeOutgoingGameplayEffectSpec(Handle, ActorInfo, ActivationInfo, CooldownEffect->GetClass(), GetAbilityLevel(Handle, ActorInfo)); if (Spec.IsValid()) { Spec.Data->SetSetByCallerMagnitude(GKLTags::Data_Cooldown, CooldownDuration); Spec.Data->DynamicGrantedTags.AppendTags(CooldownTags); ApplyGameplayEffectSpecToOwner(Handle, ActorInfo, ActivationInfo, Spec); } } AGKLCharacter* UGKLGameplayAbility::GetLiteCharacter() const { return Cast(GetAvatarActorFromActorInfo()); } void UGKLGameplayAbility::AddAbilityTag(const FGameplayTag& Tag) { #if UE_VERSION_NEWER_THAN(5, 4, 99) FGameplayTagContainer Tags = GetAssetTags(); Tags.AddTag(Tag); SetAssetTags(Tags); #else AbilityTags.AddTag(Tag); #endif }