// Copyright 2025 https://yuewu.dev/en All Rights Reserved. #include "Abilities/GGA_AbilitySet.h" #include "Abilities/GameplayAbility.h" #include "Runtime/Launch/Resources/Version.h" #include "AbilitySystemComponent.h" #include "Utilities/GGA_AbilitySystemFunctionLibrary.h" DEFINE_LOG_CATEGORY(LogGGA_AbilitySet) #include UE_INLINE_GENERATED_CPP_BY_NAME(GGA_AbilitySet) void FGGA_AbilitySet_GrantedHandles::AddAbilitySpecHandle(const FGameplayAbilitySpecHandle& Handle) { if (Handle.IsValid()) { AbilitySpecHandles.Add(Handle); } } void FGGA_AbilitySet_GrantedHandles::AddGameplayEffectHandle(const FActiveGameplayEffectHandle& Handle) { if (Handle.IsValid()) { GameplayEffectHandles.Add(Handle); } } void FGGA_AbilitySet_GrantedHandles::AddAttributeSet(UAttributeSet* Set) { GrantedAttributeSets.Add(Set); } void FGGA_AbilitySet_GrantedHandles::TakeFromAbilitySystem(UAbilitySystemComponent* ASC) { check(ASC); if (!ASC->IsOwnerActorAuthoritative()) { // Must be authoritative to give or take ability sets. return; } for (const FGameplayAbilitySpecHandle& Handle : AbilitySpecHandles) { if (Handle.IsValid()) { ASC->ClearAbility(Handle); } } for (const FActiveGameplayEffectHandle& Handle : GameplayEffectHandles) { if (Handle.IsValid()) { ASC->RemoveActiveGameplayEffect(Handle); } } for (UAttributeSet* Set : GrantedAttributeSets) { ASC->RemoveSpawnedAttribute(Set); } AbilitySpecHandles.Reset(); GameplayEffectHandles.Reset(); GrantedAttributeSets.Reset(); } UGGA_AbilitySet::UGGA_AbilitySet(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } void UGGA_AbilitySet::GiveToAbilitySystem(UAbilitySystemComponent* ASC, FGGA_AbilitySet_GrantedHandles* OutGrantedHandles, UObject* SourceObject, int32 OverrideLevel) const { check(ASC); if (!ASC->IsOwnerActorAuthoritative()) { // Must be authoritative to give or take ability sets. return; } // Grant the attribute sets. for (int32 SetIndex = 0; SetIndex < GrantedAttributes.Num(); ++SetIndex) { const FGGA_AbilitySet_AttributeSet& SetToGrant = GrantedAttributes[SetIndex]; const TSubclassOf AttributeSetClass = SetToGrant.AttributeSet.LoadSynchronous(); if (!AttributeSetClass) { UE_LOG(LogGGA_AbilitySet, Error, TEXT("GrantedAttributes[%d] on ability set [%s]: AttributeSet is not valid"), SetIndex, *GetNameSafe(this)); continue; } if (UAttributeSet* ExistingOne = UGGA_AbilitySystemFunctionLibrary::GetAttributeSetByClass(ASC, AttributeSetClass)) { UE_LOG(LogGGA_AbilitySet, Error, TEXT("GrantedAttributes[%d] on ability set [%s]: AttributeSet already exists."), SetIndex, *GetNameSafe(this)); continue; } #if WITH_EDITORONLY_DATA if (!SetToGrant.bAttributeSetEnabled) { UE_LOG(LogGGA_AbilitySet, Display, TEXT( "GrantedAttributes[%d] on ability set [%s]:skipped for debugging."), SetIndex, *GetNameSafe(this)); continue; } #endif UAttributeSet* NewSet = NewObject(ASC->GetOwner(), AttributeSetClass); ASC->AddAttributeSetSubobject(NewSet); if (OutGrantedHandles) { OutGrantedHandles->AddAttributeSet(NewSet); } } // Grant the gameplay abilities. for (int32 AbilityIndex = 0; AbilityIndex < GrantedGameplayAbilities.Num(); ++AbilityIndex) { const FGGA_AbilitySet_GameplayAbility& AbilityToGrant = GrantedGameplayAbilities[AbilityIndex]; const TSubclassOf AbilityClass = AbilityToGrant.Ability.LoadSynchronous(); if (!AbilityClass) { UE_LOG(LogGGA_AbilitySet, Error, TEXT("GrantedGameplayAbilities[%d] on ability set [%s]: Ability class is not valid."), AbilityIndex, *GetNameSafe(this)); continue; } #if WITH_EDITORONLY_DATA if (!AbilityToGrant.bAbilityEnabled) { UE_LOG(LogGGA_AbilitySet, Display, TEXT( "GrantedGameplayAbilities[%d] on ability set [%s]: Skipped for debugging."), AbilityIndex, *GetNameSafe(this)); continue; } #endif UGameplayAbility* AbilityCDO = AbilityClass->GetDefaultObject(); FGameplayAbilitySpec AbilitySpec(AbilityCDO, OverrideLevel > 0 ? OverrideLevel : AbilityToGrant.AbilityLevel); AbilitySpec.SourceObject = SourceObject; if (AbilityToGrant.InputID > 0) { AbilitySpec.InputID = AbilityToGrant.InputID; } if (!AbilityToGrant.DynamicTags.IsEmpty()) { #if ENGINE_MINOR_VERSION > 4 AbilitySpec.GetDynamicSpecSourceTags().AppendTags(AbilityToGrant.DynamicTags); #else AbilitySpec.DynamicAbilityTags.AppendTags(AbilityToGrant.DynamicTags); #endif } const FGameplayAbilitySpecHandle AbilitySpecHandle = ASC->GiveAbility(AbilitySpec); if (OutGrantedHandles) { OutGrantedHandles->AddAbilitySpecHandle(AbilitySpecHandle); } } // Grant the gameplay effects. for (int32 EffectIndex = 0; EffectIndex < GrantedGameplayEffects.Num(); ++EffectIndex) { const FGGA_AbilitySet_GameplayEffect& EffectToGrant = GrantedGameplayEffects[EffectIndex]; const TSubclassOf EffectClass = EffectToGrant.GameplayEffect.LoadSynchronous(); if (!EffectClass) { UE_LOG(LogGGA_AbilitySet, Error, TEXT("GrantedGameplayEffects[%d] on ability set [%s]:Effect Class is not valid"), EffectIndex, *GetNameSafe(this)); continue; } #if WITH_EDITORONLY_DATA if (!EffectToGrant.bEffectEnabled) { UE_LOG(LogGGA_AbilitySet, Display, TEXT( "GrantedGameplayEffects[%d] on ability set [%s]:Skipped for debugging."), EffectIndex, *GetNameSafe(this)); continue; } #endif const UGameplayEffect* GameplayEffectCDO = EffectClass->GetDefaultObject(); const FActiveGameplayEffectHandle GameplayEffectHandle = ASC-> ApplyGameplayEffectToSelf(GameplayEffectCDO, OverrideLevel > 0 ? OverrideLevel : EffectToGrant.EffectLevel, ASC->MakeEffectContext()); if (OutGrantedHandles) { OutGrantedHandles->AddGameplayEffectHandle(GameplayEffectHandle); if (GameplayEffectCDO->DurationPolicy == EGameplayEffectDurationType::Infinite && !GameplayEffectHandle.IsValid()) { UE_LOG(LogGGA_AbilitySet, Warning, TEXT("Granted Infinite GameplayEffects[%d] on ability set [%s] failed to apply"), EffectIndex, *GetNameSafe(this)); } } } } FGGA_AbilitySet_GrantedHandles UGGA_AbilitySet::GiveAbilitySetToAbilitySystem(TSoftObjectPtr AbilitySet, UAbilitySystemComponent* ASC, UObject* SourceObject, int32 OverrideLevel) { FGGA_AbilitySet_GrantedHandles GrantedHandles; if (IsValid(ASC) && !AbilitySet.IsNull()) { if (!AbilitySet.IsValid()) { AbilitySet.LoadSynchronous(); } AbilitySet->GiveToAbilitySystem(ASC, &GrantedHandles, SourceObject, OverrideLevel); } return GrantedHandles; } TArray UGGA_AbilitySet::GiveAbilitySetsToAbilitySystem(TArray> AbilitySets, UAbilitySystemComponent* ASC, UObject* SourceObject, int32 OverrideLevel) { TArray Handles; for (auto& AbilitySet : AbilitySets) { Handles.Add(GiveAbilitySetToAbilitySystem(AbilitySet, ASC, SourceObject, OverrideLevel)); } return Handles; } void UGGA_AbilitySet::TakeAbilitySetFromAbilitySystem(FGGA_AbilitySet_GrantedHandles& GrantedHandles, UAbilitySystemComponent* ASC) { if (IsValid(ASC)) { GrantedHandles.TakeFromAbilitySystem(ASC); } } void UGGA_AbilitySet::TakeAbilitySetsFromAbilitySystem(TArray& GrantedHandles, UAbilitySystemComponent* ASC) { if (IsValid(ASC)) { for (FGGA_AbilitySet_GrantedHandles& Handle : GrantedHandles) { Handle.TakeFromAbilitySystem(ASC); } } } #if WITH_EDITOR #include "UObject/ObjectSaveContext.h" void FGGA_AbilitySet_GameplayAbility::MakeEditorFriendlyName() { EditorFriendlyName = "Empty Ability"; if (!Ability.IsNull()) { if (TSubclassOf Loaded = Ability.LoadSynchronous()) { EditorFriendlyName = Loaded->GetDisplayNameText().ToString(); } } } void FGGA_AbilitySet_GameplayEffect::MakeEditorFriendlyName() { EditorFriendlyName = "Empty Effect"; if (!GameplayEffect.IsNull()) { if (TSubclassOf Loaded = GameplayEffect.LoadSynchronous()) { EditorFriendlyName = Loaded->GetDisplayNameText().ToString(); } } } void FGGA_AbilitySet_AttributeSet::MakeEditorFriendlyName() { EditorFriendlyName = "Empty Attribute Set"; if (!AttributeSet.IsNull()) { if (TSubclassOf Loaded = AttributeSet.LoadSynchronous()) { EditorFriendlyName = Loaded->GetDisplayNameText().ToString(); } } } void UGGA_AbilitySet::PreSave(FObjectPreSaveContext SaveContext) { Super::PreSave(SaveContext); if (!IsRunningCommandlet()) { for (auto& Ability : GrantedGameplayAbilities) { Ability.MakeEditorFriendlyName(); } for (auto& Effect : GrantedGameplayEffects) { Effect.MakeEditorFriendlyName(); } for (auto& Attribute : GrantedAttributes) { Attribute.MakeEditorFriendlyName(); } } } void UGGA_AbilitySet::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); } void UGGA_AbilitySet::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) { FName MemberName = PropertyChangedEvent.PropertyChain.GetActiveMemberNode()->GetValue()->GetFName(); if (PropertyChangedEvent.GetPropertyName() == TEXT("Ability") && MemberName == GET_MEMBER_NAME_CHECKED(UGGA_AbilitySet, GrantedGameplayAbilities)) { const int32 Index = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_CHECKED(UGGA_AbilitySet, GrantedGameplayAbilities).ToString()); if (Index != INDEX_NONE) { GrantedGameplayAbilities[Index].MakeEditorFriendlyName(); } } if (PropertyChangedEvent.GetPropertyName() == TEXT("GameplayEffect") && MemberName == GET_MEMBER_NAME_CHECKED(UGGA_AbilitySet, GrantedGameplayEffects)) { const int32 Index = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_CHECKED(UGGA_AbilitySet, GrantedGameplayEffects).ToString()); if (Index != INDEX_NONE) { GrantedGameplayEffects[Index].MakeEditorFriendlyName(); } } if (PropertyChangedEvent.GetPropertyName() == TEXT("AttributeSet") && MemberName == GET_MEMBER_NAME_CHECKED(UGGA_AbilitySet, GrantedAttributes)) { const int32 Index = PropertyChangedEvent.GetArrayIndex(GET_MEMBER_NAME_CHECKED(UGGA_AbilitySet, GrantedGameplayEffects).ToString()); if (Index != INDEX_NONE) { GrantedAttributes[Index].MakeEditorFriendlyName(); } } Super::PostEditChangeChainProperty(PropertyChangedEvent); } #endif