#Playing a failure sound if insufficient Mana to activate ability

3 messages · Page 1 of 1 (latest)

dusk jacinth
#

In UAuraAbilitySystemComponent::AbilityActorInfoSet(), bind to OnActivationFailed.

AbilityFailedCallbacks.AddUObject(this, &UAuraAbilitySystemComponent::OnActivationFailed);```
This passes in the failed ability and tags describing the reason:
```c++
void UAuraAbilitySystemComponent::OnActivationFailed(const UGameplayAbility* FailedAbility, const FGameplayTagContainer& GameplayTags)
{
    UE_LOGFMT(LogHerb64, Warning, "{0}: Ability Activation failed for {1}, Tags: {2}", __FUNCTION__, FailedAbility->GetName(), GameplayTags.ToStringSimple(true));
    if (GameplayTags.HasTag(FAuraGameplayTags::Get().AbilityFail_Cost))
    {
        if (UAuraDamageGameplayAbility* Ability = const_cast<UAuraDamageGameplayAbility*>(Cast<UAuraDamageGameplayAbility>(FailedAbility)))
        {
            FScopedAbilityListLock ActiveScopeLock(*this);
            for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
            {
                if (AbilitySpec.Ability == FailedAbility)
                {
                    AbilitySpec.DynamicAbilityTags.AddTag(FAuraGameplayTags::Get().AbilityFail_Cost);
                    break;
                }
            }
            if (Ability->ActivationFailSound) UGameplayStatics::PlaySound2D(this, Ability->ActivationFailSound);
        }
    }
}```
Note that I also add a dynamic tag to the spec. This is used to avoid multiple triggers in AbilityInputTagHeld().
dusk jacinth
#

This requires tags to be defined, that are passed with that delegate.
I added native tags for the available reasons (see /Engine/Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AbilitySystemGlobals.h)

FGameplayTag AbilityFail_TagsBlocked;
FGameplayTag AbilityFail_TagsMissing;
FGameplayTag AbilityFail_Networking;
FGameplayTag AbilityFail_CoolDown;
FGameplayTag AbilityFail_IsDead;```
In addition, these need to be defined in DefaultGame.ini to be able to be passed along with the delegate:
```[/Script/GameplayAbilities.AbilitySystemGlobals]
AbilitySystemGlobalsClassName="/Script/Aura.AuraAbilitySystemGlobals"
+GameplayCueNotifyPaths=/Game/Blueprints/AbilitySystem/GameplayCueNotifies
ActivateFailCostName=AbilityFail.Cost
ActivateFailTagsBlockedName=AbilityFail.TagsBlocked
ActivateFailTagsMissingName=AbilityFail.TagsMissing
ActivateFailNetworkingName=AbilityFail.Networking
ActivateFailCooldownName=AbilityFail.CoolDown
ActivateFailIsDeadName=AbilityFail.IsDead```
#

Avoid triggering the callback this on each TryActivateAbility for each tick, so only try activate if the fail tag is not present. c++ void UAuraAbilitySystemComponent::AbilityInputTagHeld(const FGameplayTag& InputTag) { if (!InputTag.IsValid()) return; FScopedAbilityListLock ActiveScopeLock(*this); for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()) { if (AbilitySpec.DynamicAbilityTags.HasTagExact(InputTag)) { bool bIsFailedCost = AbilitySpec.DynamicAbilityTags.HasTag(FAuraGameplayTags::Get().AbilityFail_Cost); AbilitySpecInputPressed(AbilitySpec); if (!bIsFailedCost && !AbilitySpec.IsActive()) { TryActivateAbility(AbilitySpec.Handle); } } } }
That fail tag is removed from the dynamic ability tags in AbilityInputTagReleased().