Gameplay Ability Set’s

Gameplay Ability Sets allow you define a set of abilities and effects which are given to the target ASC, I use these to apply the default generic abilities a player has, and default generic effects each player will have. These can also be used for things like perks, etc. The other benefit is, they can be added and removed via a simple handle.

Here is the base class:

// Fill out your copyright notice in the Description page of Project Settings.


#include "AbilitySystem/Core/KaosAbilitySet.h"
#include "AbilitySystem/Abilities/KaosGameplayAbility.h"
#include "KaosLogging.h"
#include "KaosAbilitySystemComponent.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(KaosAbilitySet)

namespace KaosAbilitySetHandle_Impl
{
	static int32 LastHandleId = 0;
	static int32 GetNextQueuedHandleIdForUse() { return ++LastHandleId; }
}

UKaosAbilitySet::UKaosAbilitySet(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

FKaosAbilitySetHandle UKaosAbilitySet::GiveAbilitySetTo(UKaosAbilitySystemComponent* ASC, UObject* OverrideSourceObject) const
{
	check(ASC);

	if (!ASC->IsOwnerActorAuthoritative())
	{
		// Must be authoritative to give or take ability sets.
		return FKaosAbilitySetHandle();
	}

	FKaosAbilitySetHandle OutHandle;
	OutHandle.HandleId = KaosAbilitySetHandle_Impl::GetNextQueuedHandleIdForUse();
	OutHandle.AbilitySystemComponent = ASC;
	
	// Grant the gameplay abilities.
	for (int32 AbilityIndex = 0; AbilityIndex < GrantedGameplayAbilities.Num(); ++AbilityIndex)
	{
		const FKaosAbilitySet_GameplayAbility& AbilityToGrant = GrantedGameplayAbilities[AbilityIndex];

		if (!IsValid(AbilityToGrant.Ability))
		{
			UE_LOG(LogKaosAbilitySystem, Error, TEXT("GrantedGameplayAbilities[%d] on ability set [%s] is not valid."), AbilityIndex, *GetNameSafe(this));
			continue;
		}

		UKaosGameplayAbility* AbilityCDO = AbilityToGrant.Ability->GetDefaultObject<UKaosGameplayAbility>();

		FGameplayAbilitySpec AbilitySpec(AbilityCDO, AbilityToGrant.AbilityLevel);
		AbilitySpec.SourceObject = OverrideSourceObject;
		AbilitySpec.DynamicAbilityTags.AddTag(AbilityToGrant.InputTag);

		const FGameplayAbilitySpecHandle AbilitySpecHandle = ASC->GiveAbility(AbilitySpec);
		OutHandle.AddAbilitySpecHandle(AbilitySpecHandle);
	}

	// Grant the gameplay effects.
	for (int32 EffectIndex = 0; EffectIndex < GrantedGameplayEffects.Num(); ++EffectIndex)
	{
		const FKaosAbilitySet_GameplayEffect& EffectToGrant = GrantedGameplayEffects[EffectIndex];

		if (!IsValid(EffectToGrant.GameplayEffect))
		{
			UE_LOG(LogKaosAbilitySystem, Error, TEXT("GrantedGameplayEffects[%d] on ability set [%s] is not valid"), EffectIndex, *GetNameSafe(this));
			continue;
		}

		const UGameplayEffect* GameplayEffect = EffectToGrant.GameplayEffect->GetDefaultObject<UGameplayEffect>();
		const FActiveGameplayEffectHandle GameplayEffectHandle = ASC->ApplyGameplayEffectToSelf(GameplayEffect, EffectToGrant.EffectLevel, ASC->MakeEffectContext());
		OutHandle.AddGameplayEffectHandle(GameplayEffectHandle);
	}

	// Grant the attribute sets.
	for (int32 SetIndex = 0; SetIndex < GrantedAttributes.Num(); ++SetIndex)
	{
		const FKaosAbilitySet_AttributeSet& SetToGrant = GrantedAttributes[SetIndex];

		if (!IsValid(SetToGrant.AttributeSet))
		{
			UE_LOG(LogKaosAbilitySystem, Error, TEXT("GrantedAttributes[%d] on ability set [%s] is not valid"), SetIndex, *GetNameSafe(this));
			continue;
		}

		UAttributeSet* NewSet = NewObject<UAttributeSet>(ASC->GetOwner(), SetToGrant.AttributeSet);
		ASC->AddAttributeSetSubobject(NewSet);
		OutHandle.AddAttributeSet(NewSet);
	}

	return OutHandle;
}

FKaosAbilitySetHandle UKaosAbilitySet::GiveAbilitySetToInterface(TScriptInterface<IKaosAbilitySystemInterface> AbilitySystemInterface, UObject* OverrideSourceObject) const
{
	UKaosAbilitySystemComponent* KaosASC = Cast<UKaosAbilitySystemComponent>(AbilitySystemInterface.GetObject());
	return GiveAbilitySetTo(KaosASC, OverrideSourceObject);
}

void UKaosAbilitySet::TakeAbilitySet(FKaosAbilitySetHandle& AbilitySetHandle)
{
	if (!AbilitySetHandle.IsValid())
	{
		return;
	}
	
	if (!AbilitySetHandle.AbilitySystemComponent->IsOwnerActorAuthoritative())
	{
		// Must be authoritative to give or take ability sets.
		return;
	}

	for (const FGameplayAbilitySpecHandle& Handle : AbilitySetHandle.AbilitySpecHandles)
	{
		if (Handle.IsValid())
		{
			AbilitySetHandle.AbilitySystemComponent->ClearAbility(Handle);
		}
	}

	for (const FActiveGameplayEffectHandle& Handle : AbilitySetHandle.GameplayEffectHandles)
	{
		if (Handle.IsValid())
		{
			AbilitySetHandle.AbilitySystemComponent->RemoveActiveGameplayEffect(Handle);
		}
	}

	for (UAttributeSet* Set : AbilitySetHandle.GrantedAttributeSets)
	{
		AbilitySetHandle.AbilitySystemComponent->GetSpawnedAttributes_Mutable().Remove(Set);
	}

	AbilitySetHandle.Reset();
}

and the corresponding header file

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "KaosAbilitySystemInterface.h"
#include "KaosAbilityTypes.h"
#include "Engine/DataAsset.h"
#include "KaosAbilitySet.generated.h"

class UKaosAbilitySystemComponent;

/**
 * FKaosAbilitySet_GameplayAbility
 *
 *	Data used by the ability set to grant gameplay abilities.
 */
USTRUCT(BlueprintType)
struct FKaosAbilitySet_GameplayAbility
{
	GENERATED_BODY()

public:
	// Gameplay ability to grant.
	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<UKaosGameplayAbility> Ability = nullptr;

	// Level of ability to grant.
	UPROPERTY(EditDefaultsOnly)
	int32 AbilityLevel = 1;
};


/**
 * FKaosAbilitySet_GameplayEffect
 *
 *	Data used by the ability set to grant gameplay effects.
 */
USTRUCT(BlueprintType)
struct FKaosAbilitySet_GameplayEffect
{
	GENERATED_BODY()

public:
	// Gameplay effect to grant.
	UPROPERTY(EditDefaultsOnly)
	TSubclassOf<UGameplayEffect> GameplayEffect = nullptr;

	// Level of gameplay effect to grant.
	UPROPERTY(EditDefaultsOnly)
	float EffectLevel = 1.0f;
};

/**
 * FKaosAbilitySetHandle
 *
 *	Data used to store handles to what has been granted by the ability set.
 */
USTRUCT(BlueprintType)
struct FKaosAbilitySetHandle
{
	GENERATED_BODY()

	bool IsValid() const
	{
		return AbilitySystemComponent.IsValid() && HandleId != 0;
	}

private:
	friend class UKaosAbilitySet;

	void AddAbilitySpecHandle(const FGameplayAbilitySpecHandle& Handle);
	void AddGameplayEffectHandle(const FActiveGameplayEffectHandle& Handle);

	// Handles to the granted abilities.
	UPROPERTY()
	TArray<FGameplayAbilitySpecHandle> AbilitySpecHandles;

	// Handles to the granted gameplay effects.
	UPROPERTY()
	TArray<FActiveGameplayEffectHandle> GameplayEffectHandles;

	int32 HandleId = 0;

	TWeakObjectPtr<UKaosAbilitySystemComponent> AbilitySystemComponent = nullptr;

	void Reset()
	{
		AbilitySpecHandles.Reset();
		GameplayEffectHandles.Reset();
		AbilitySystemComponent.Reset();
		HandleId = 0;
	}
};

/**
 * 
 */
UCLASS(BlueprintType, Const)
class KAOSGAME_API UKaosAbilitySet : public UPrimaryDataAsset
{
	GENERATED_BODY()
public:

	UKaosAbilitySet(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

	const TArray<FKaosAbilitySet_GameplayAbility>& GetGrantedGameplayAbilities() const { return GrantedGameplayAbilities; }
	const TArray<FKaosAbilitySet_GameplayEffect>& GetGrantedGameplayEffects() const { return GrantedGameplayEffects; }
	const TArray<FKaosAbilitySet_AttributeSet>& GetGrantedAttributes() const { return GrantedAttributes; }

	FKaosAbilitySetHandle GiveAbilitySetTo(UKaosAbilitySystemComponent* ASC, UObject* OverrideSourceObject = nullptr) const;
	FKaosAbilitySetHandle GiveAbilitySetToInterface(TScriptInterface<IKaosAbilitySystemInterface> AbilitySystemInterface, UObject* OverrideSourceObject = nullptr) const;
	static void TakeAbilitySet(FKaosAbilitySetHandle& AbilitySetHandle);
protected:

	// Gameplay abilities to grant when this ability set is granted.
	UPROPERTY(EditDefaultsOnly, Category = "Gameplay Abilities", meta=(TitleProperty=Ability))
	TArray<FKaosAbilitySet_GameplayAbility> GrantedGameplayAbilities;

	// Gameplay effects to grant when this ability set is granted.
	UPROPERTY(EditDefaultsOnly, Category = "Gameplay Effects", meta=(TitleProperty=GameplayEffect))
	TArray<FKaosAbilitySet_GameplayEffect> GrantedGameplayEffects;

	// Attribute sets to grant when this ability set is granted.
	UPROPERTY(EditDefaultsOnly, Category = "Attribute Sets", meta=(TitleProperty=AttributeSet))
	TArray<FKaosAbilitySet_AttributeSet> GrantedAttributes;
};

The Ability Sets can be granted by issuing the AbilitySet->GiveAbilitySetTo or you can make some BP statics to do that like exampled below:

void UKaosAbilityStatics::UnequipKaosAbilitySet(FKaosAbilitySetHandle& AbilitySetHandle)
{
	if (AbilitySetHandle.IsValid())
	{
		UKaosAbilitySet::TakeAbilitySet(AbilitySetHandle);
	}
}

FKaosAbilitySetHandle UKaosAbilityStatics::EquipKaosAbilitySet(TScriptInterface<IAbilitySystemInterface> AbilitySystemInterface, const UKaosAbilitySet* AbilitySet, UObject* OverrideSourceObject)
{
	return AbilitySet->GiveAbilitySetToInterface(AbilitySystemInterface, OverrideSourceObject);
}