第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Abilities/GGA_AbilityCost.h"
bool UGGA_AbilityCost::CheckCost(const UGameplayAbility* Ability, const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
FGameplayTagContainer* OptionalRelevantTags) const
{
return BlueprintCheckCost(Ability, Handle, *ActorInfo, *OptionalRelevantTags);
}
void UGGA_AbilityCost::ApplyCost(const UGameplayAbility* Ability, const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo)
{
return BlueprintApplyCost(Ability, Handle, *ActorInfo, ActivationInfo);
}

View File

@@ -0,0 +1,350 @@
// 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<UAttributeSet> 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<UAttributeSet>(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<UGameplayAbility> 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<UGameplayAbility>();
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<UGameplayEffect> 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<UGameplayEffect>();
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<UGGA_AbilitySet> 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<FGGA_AbilitySet_GrantedHandles> UGGA_AbilitySet::GiveAbilitySetsToAbilitySystem(TArray<TSoftObjectPtr<UGGA_AbilitySet>> AbilitySets, UAbilitySystemComponent* ASC, UObject* SourceObject,
int32 OverrideLevel)
{
TArray<FGGA_AbilitySet_GrantedHandles> 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<FGGA_AbilitySet_GrantedHandles>& 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<UGameplayAbility> Loaded = Ability.LoadSynchronous())
{
EditorFriendlyName = Loaded->GetDisplayNameText().ToString();
}
}
}
void FGGA_AbilitySet_GameplayEffect::MakeEditorFriendlyName()
{
EditorFriendlyName = "Empty Effect";
if (!GameplayEffect.IsNull())
{
if (TSubclassOf<UGameplayEffect> Loaded = GameplayEffect.LoadSynchronous())
{
EditorFriendlyName = Loaded->GetDisplayNameText().ToString();
}
}
}
void FGGA_AbilitySet_AttributeSet::MakeEditorFriendlyName()
{
EditorFriendlyName = "Empty Attribute Set";
if (!AttributeSet.IsNull())
{
if (TSubclassOf<UAttributeSet> 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

View File

@@ -0,0 +1,651 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Abilities/GGA_GameplayAbility.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemGlobals.h"
#include "AbilitySystemLog.h"
#include "Runtime/Launch/Resources/Version.h"
#include "Abilities/GGA_AbilityCost.h"
#include "GGA_AbilitySystemComponent.h"
#include "GGA_GameplayTags.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/Controller.h"
#include "GameFramework/Pawn.h"
#include "GGA_LogChannels.h"
#include "Misc/DataValidation.h"
#include "Utilities/GGA_GameplayEffectContainerFunctionLibrary.h"
#define ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(FunctionName, ReturnValue) \
{ \
if (!ensure(IsInstantiated())) \
{ \
ABILITY_LOG(Error, TEXT("%s: " #FunctionName " cannot be called on a non-instanced ability. Check the instancing policy."), *GetPathName()); \
return ReturnValue; \
} \
}
UGGA_GameplayAbility::UGGA_GameplayAbility(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ReplicationPolicy = EGameplayAbilityReplicationPolicy::ReplicateNo;
bServerRespectsRemoteAbilityCancellation = false;
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ClientOrServer;
ActivationGroup = EGGA_AbilityActivationGroup::Independent;
bReplicateInputDirectly = false;
bEnableTick = false;
}
void UGGA_GameplayAbility::Tick(float DeltaTime)
{
AbilityTick(DeltaTime);
}
TStatId UGGA_GameplayAbility::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UGGA_GameplayAbility, STATGROUP_GameplayAbility);
}
bool UGGA_GameplayAbility::IsTickable() const
{
return IsInstantiated() && bEnableTick && GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::InstancedPerActor && IsActive();
}
void UGGA_GameplayAbility::AbilityTick_Implementation(float DeltaTime)
{
}
AController* UGGA_GameplayAbility::GetControllerFromActorInfo() const
{
if (CurrentActorInfo)
{
if (AController* PC = CurrentActorInfo->PlayerController.Get())
{
return PC;
}
// Look for a player controller or pawn in the owner chain.
AActor* TestActor = CurrentActorInfo->OwnerActor.Get();
while (TestActor)
{
if (AController* C = Cast<AController>(TestActor))
{
return C;
}
if (APawn* Pawn = Cast<APawn>(TestActor))
{
return Pawn->GetController();
}
TestActor = TestActor->GetOwner();
}
}
return nullptr;
}
void UGGA_GameplayAbility::SetActivationGroup(EGGA_AbilityActivationGroup NewGroup)
{
ActivationGroup = NewGroup;
}
void UGGA_GameplayAbility::TryActivateAbilityOnSpawn(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) const
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
#if ENGINE_MINOR_VERSION > 4
// Fixing this up to use the instance activation, but this function should be deprecated as it cannot work with InstancedPerExecution
UE_CLOG(Spec.Ability->GetInstancingPolicy() == EGameplayAbilityInstancingPolicy::InstancedPerExecution, LogAbilitySystem, Warning,
TEXT("%hs: %s is InstancedPerExecution. This is unreliable for Input as you may only interact with the latest spawned Instance"), __func__, *GetNameSafe(Spec.Ability));
TArray<UGameplayAbility*> Instances = Spec.GetAbilityInstances();
const FGameplayAbilityActivationInfo& ActivationInfo = Instances.IsEmpty() ? Spec.ActivationInfo : Instances.Last()->GetCurrentActivationInfoRef();
const bool bIsPredicting = (ActivationInfo.ActivationMode == EGameplayAbilityActivationMode::Predicting);
#else
const bool bIsPredicting = (Spec.ActivationInfo.ActivationMode == EGameplayAbilityActivationMode::Predicting);
#endif
PRAGMA_ENABLE_DEPRECATION_WARNINGS
// Try to activate if activation policy is on spawn.
#if ENGINE_MINOR_VERSION > 4
if (ActorInfo && !Spec.IsActive() && !bIsPredicting && GetAssetTags().HasTagExact(GGA_AbilityTraitTags::ActivationOnSpawn))
#else
if (ActorInfo && !Spec.IsActive() && !bIsPredicting && AbilityTags.HasTagExact(GGA_AbilityTraitTags::ActivationOnSpawn))
#endif
{
UAbilitySystemComponent* ASC = ActorInfo->AbilitySystemComponent.Get();
const AActor* AvatarActor = ActorInfo->AvatarActor.Get();
// If avatar actor is torn off or about to die, don't try to activate until we get the new one.
if (ASC && AvatarActor && !AvatarActor->GetTearOff() && (AvatarActor->GetLifeSpan() <= 0.0f))
{
const bool bIsLocalExecution = (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::LocalPredicted) || (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::LocalOnly);
const bool bIsServerExecution = (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::ServerOnly) || (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::ServerInitiated);
const bool bClientShouldActivate = ActorInfo->IsLocallyControlled() && bIsLocalExecution;
const bool bServerShouldActivate = ActorInfo->IsNetAuthority() && bIsServerExecution;
if (bClientShouldActivate || bServerShouldActivate)
{
ASC->TryActivateAbility(Spec.Handle);
}
}
}
}
void UGGA_GameplayAbility::HandleActivationFailed(const FGameplayTagContainer& FailedReason) const
{
OnActivationFailed(FailedReason);
}
bool UGGA_GameplayAbility::HasEffectContainer(FGameplayTag ContainerTag)
{
return EffectContainerMap.Contains(ContainerTag);
}
FGGA_GameplayEffectContainerSpec UGGA_GameplayAbility::MakeEffectContainerSpec(FGameplayTag ContainerTag, const FGameplayEventData& EventData, int32 OverrideGameplayLevel)
{
FGGA_GameplayEffectContainer* FoundContainer = EffectContainerMap.Find(ContainerTag);
if (FoundContainer)
{
return UGGA_GameplayEffectContainerFunctionLibrary::MakeEffectContainerSpec(*FoundContainer, EventData, OverrideGameplayLevel, this);
}
return FGGA_GameplayEffectContainerSpec();
}
TArray<FActiveGameplayEffectHandle> UGGA_GameplayAbility::ApplyEffectContainer(FGameplayTag ContainerTag, const FGameplayEventData& EventData, int32 OverrideGameplayLevel)
{
FGGA_GameplayEffectContainer* FoundContainer = EffectContainerMap.Find(ContainerTag);
if (FoundContainer)
{
const FGGA_GameplayEffectContainerSpec Spec = UGGA_GameplayEffectContainerFunctionLibrary::MakeEffectContainerSpec(*FoundContainer, EventData, OverrideGameplayLevel, this);
return UGGA_GameplayEffectContainerFunctionLibrary::ApplyEffectContainerSpec(this, Spec);
}
return TArray<FActiveGameplayEffectHandle>();
}
void UGGA_GameplayAbility::PreActivate(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,
FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate, const FGameplayEventData* TriggerEventData)
{
Super::PreActivate(Handle, ActorInfo, ActivationInfo, OnGameplayAbilityEndedDelegate, TriggerEventData);
UAbilitySystemComponent* Comp = ActorInfo->AbilitySystemComponent.Get();
for (const FGGA_GameplayTagCount& TagCount : ActivationOwnedLooseTags)
{
Comp->AddLooseGameplayTag(TagCount.Tag, TagCount.Count);
}
}
void UGGA_GameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo,
bool bReplicateEndAbility, bool bWasCancelled)
{
if (IsEndAbilityValid(Handle, ActorInfo))
{
if (UAbilitySystemComponent* Comp = ActorInfo->AbilitySystemComponent.Get())
{
for (const FGGA_GameplayTagCount& TagCount : ActivationOwnedLooseTags)
{
Comp->RemoveLooseGameplayTag(TagCount.Tag, TagCount.Count);
}
}
}
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
void UGGA_GameplayAbility::OnActivationFailed_Implementation(const FGameplayTagContainer& FailedReason) const
{
}
bool UGGA_GameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags,
const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const
{
if (!ActorInfo || !ActorInfo->AbilitySystemComponent.IsValid())
{
return false;
}
if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags))
{
return false;
}
//@TODO Possibly remove after setting up tag relationships
UGGA_AbilitySystemComponent* GASC = CastChecked<UGGA_AbilitySystemComponent>(ActorInfo->AbilitySystemComponent.Get());
if (GASC->IsActivationGroupBlocked(ActivationGroup))
{
if (OptionalRelevantTags)
{
OptionalRelevantTags->AddTag(GGA_AbilityActivateFailTags::ActivationGroup);
}
return false;
}
return true;
}
void UGGA_GameplayAbility::SetCanBeCanceled(bool bCanBeCanceled)
{
// The ability can not block canceling if it's replaceable.
if (!bCanBeCanceled && (ActivationGroup == EGGA_AbilityActivationGroup::Exclusive_Replaceable))
{
UE_LOG(LogGGA_Ability, Error, TEXT("SetCanBeCanceled: Ability [%s] can not block canceling because its activation group is replaceable."), *GetName());
return;
}
Super::SetCanBeCanceled(bCanBeCanceled);
}
void UGGA_GameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
Super::OnGiveAbility(ActorInfo, Spec);
K2_OnGiveAbility();
TryActivateAbilityOnSpawn(ActorInfo, Spec);
}
void UGGA_GameplayAbility::OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
K2_OnRemoveAbility();
Super::OnRemoveAbility(ActorInfo, Spec);
}
void UGGA_GameplayAbility::OnAvatarSet(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
Super::OnAvatarSet(ActorInfo, Spec);
K2_OnAvatarSet();
}
bool UGGA_GameplayAbility::ShouldActivateAbility(ENetRole Role) const
{
return K2_ShouldActivateAbility(Role) && Super::ShouldActivateAbility(Role);
// Don't violate security policy if we're not the server
}
bool UGGA_GameplayAbility::K2_ShouldActivateAbility_Implementation(ENetRole Role) const
{
return true;
}
void UGGA_GameplayAbility::InputPressed(const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo)
{
Super::InputPressed(Handle, ActorInfo, ActivationInfo);
K2_OnInputPressed(Handle, *ActorInfo, ActivationInfo);
}
void UGGA_GameplayAbility::InputReleased(const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo)
{
Super::InputReleased(Handle, ActorInfo, ActivationInfo);
K2_OnInputReleased(Handle, *ActorInfo, ActivationInfo);
}
bool UGGA_GameplayAbility::IsInputPressed() const
{
FGameplayAbilitySpec* Spec = GetCurrentAbilitySpec();
return Spec && Spec->InputPressed;
}
bool UGGA_GameplayAbility::BatchRPCTryActivateAbility(FGameplayAbilitySpecHandle InAbilityHandle, bool EndAbilityImmediately)
{
UGGA_AbilitySystemComponent* ASC = Cast<UGGA_AbilitySystemComponent>(GetAbilitySystemComponentFromActorInfo());
if (ASC)
{
return ASC->BatchRPCTryActivateAbility(InAbilityHandle, EndAbilityImmediately);
}
return false;
}
void UGGA_GameplayAbility::ExternalEndAbility()
{
check(CurrentActorInfo);
bool bReplicateEndAbility = true;
bool bWasCancelled = false;
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicateEndAbility, bWasCancelled);
}
bool UGGA_GameplayAbility::CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
OUT FGameplayTagContainer* OptionalRelevantTags) const
{
if (!Super::CheckCost(Handle, ActorInfo, OptionalRelevantTags))
{
return false;
}
if (!K2_OnCheckCost(Handle, *ActorInfo))
{
return false;
}
for (TObjectPtr<UGGA_AbilityCost> AdditionalCost : AdditionalCosts)
{
if (AdditionalCost != nullptr)
{
if (!AdditionalCost->CheckCost(this, Handle, ActorInfo, OptionalRelevantTags))
{
return false;
}
}
}
return true;
}
bool UGGA_GameplayAbility::K2_OnCheckCost_Implementation(const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo& ActorInfo) const
{
return true;
}
void UGGA_GameplayAbility::ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo) const
{
Super::ApplyCost(Handle, ActorInfo, ActivationInfo);
check(ActorInfo);
K2_OnApplyCost(Handle, *ActorInfo, ActivationInfo);
// Used to determine if the ability actually hit a target (as some costs are only spent on successful attempts)
auto DetermineIfAbilityHitTarget = [&]()
{
if (ActorInfo->IsNetAuthority())
{
if (UGGA_AbilitySystemComponent* ASC = Cast<UGGA_AbilitySystemComponent>(ActorInfo->AbilitySystemComponent.Get()))
{
FGameplayAbilityTargetDataHandle TargetData;
ASC->GetAbilityTargetData(Handle, ActivationInfo, TargetData);
for (int32 TargetDataIdx = 0; TargetDataIdx < TargetData.Data.Num(); ++TargetDataIdx)
{
if (UAbilitySystemBlueprintLibrary::TargetDataHasHitResult(TargetData, TargetDataIdx))
{
return true;
}
}
}
}
return false;
};
// Pay any additional costs
bool bAbilityHitTarget = false;
bool bHasDeterminedIfAbilityHitTarget = false;
for (TObjectPtr<UGGA_AbilityCost> AdditionalCost : AdditionalCosts)
{
if (AdditionalCost != nullptr)
{
if (AdditionalCost->ShouldOnlyApplyCostOnHit())
{
if (!bHasDeterminedIfAbilityHitTarget)
{
bAbilityHitTarget = DetermineIfAbilityHitTarget();
bHasDeterminedIfAbilityHitTarget = true;
}
if (!bAbilityHitTarget)
{
continue;
}
}
AdditionalCost->ApplyCost(this, Handle, ActorInfo, ActivationInfo);
}
}
}
UGameplayEffect* UGGA_GameplayAbility::GetCostGameplayEffect() const
{
if (TSubclassOf<UGameplayEffect> GE = K2_GetCostGameplayEffect())
{
if (GE)
{
return GE->GetDefaultObject<UGameplayEffect>();
}
return nullptr;
}
return nullptr;
}
TSubclassOf<UGameplayEffect> UGGA_GameplayAbility::K2_GetCostGameplayEffect_Implementation() const
{
return CostGameplayEffectClass;
}
void UGGA_GameplayAbility::K2_OnApplyCost_Implementation(const FGameplayAbilitySpecHandle Handle,
const FGameplayAbilityActorInfo& ActorInfo,
const FGameplayAbilityActivationInfo ActivationInfo) const
{
}
void UGGA_GameplayAbility::ApplyAbilityTagsToGameplayEffectSpec(FGameplayEffectSpec& Spec, FGameplayAbilitySpec* AbilitySpec) const
{
Super::ApplyAbilityTagsToGameplayEffectSpec(Spec, AbilitySpec);
}
bool UGGA_GameplayAbility::DoesAbilitySatisfyTagRequirements(const UAbilitySystemComponent& AbilitySystemComponent, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags,
FGameplayTagContainer* OptionalRelevantTags) const
{
// Define a common lambda to check for blocked tags
bool bBlocked = false;
auto CheckForBlocked = [&](const FGameplayTagContainer& ContainerA, const FGameplayTagContainer& ContainerB)
{
// Do we not have any tags in common? Then we're not blocked
if (ContainerA.IsEmpty() || ContainerB.IsEmpty() || !ContainerA.HasAny(ContainerB))
{
return;
}
if (OptionalRelevantTags)
{
// Ensure the global blocking tag is only added once
if (!bBlocked)
{
UAbilitySystemGlobals& AbilitySystemGlobals = UAbilitySystemGlobals::Get();
const FGameplayTag& BlockedTag = AbilitySystemGlobals.ActivateFailTagsBlockedTag;
OptionalRelevantTags->AddTag(BlockedTag);
}
// Now append all the blocking tags
OptionalRelevantTags->AppendMatchingTags(ContainerA, ContainerB);
}
bBlocked = true;
};
// Define a common lambda to check for missing required tags
bool bMissing = false;
auto CheckForRequired = [&](const FGameplayTagContainer& TagsToCheck, const FGameplayTagContainer& RequiredTags)
{
// Do we have no requirements, or have met all requirements? Then nothing's missing
if (RequiredTags.IsEmpty() || TagsToCheck.HasAll(RequiredTags))
{
return;
}
if (OptionalRelevantTags)
{
// Ensure the global missing tag is only added once
if (!bMissing)
{
UAbilitySystemGlobals& AbilitySystemGlobals = UAbilitySystemGlobals::Get();
const FGameplayTag& MissingTag = AbilitySystemGlobals.ActivateFailTagsMissingTag;
OptionalRelevantTags->AddTag(MissingTag);
}
FGameplayTagContainer MissingTags = RequiredTags;
MissingTags.RemoveTags(TagsToCheck.GetGameplayTagParents());
OptionalRelevantTags->AppendTags(MissingTags);
}
bMissing = true;
};
const UGGA_AbilitySystemComponent* GASC = Cast<UGGA_AbilitySystemComponent>(&AbilitySystemComponent);
static FGameplayTagContainer AllRequiredTags;
static FGameplayTagContainer AllBlockedTags;
AllRequiredTags = ActivationRequiredTags;
AllBlockedTags = ActivationBlockedTags;
// Expand our ability tags to add additional required/blocked tags
if (GASC)
{
GASC->GetAdditionalActivationTagRequirements(GetAssetTags(), AllRequiredTags, AllBlockedTags);
}
// Start by checking all of the blocked tags first (so OptionalRelevantTags will contain blocked tags first)
CheckForBlocked(GetAssetTags(), AbilitySystemComponent.GetBlockedAbilityTags());
CheckForBlocked(AbilitySystemComponent.GetOwnedGameplayTags(), AllBlockedTags);
// Check to see the required/blocked tags for this ability
if (AllBlockedTags.Num() || AllRequiredTags.Num())
{
static FGameplayTagContainer AbilitySystemComponentTags;
AbilitySystemComponentTags.Reset();
AbilitySystemComponent.GetOwnedGameplayTags(AbilitySystemComponentTags);
if (AbilitySystemComponentTags.HasAny(AllBlockedTags))
{
if (OptionalRelevantTags)
{
OptionalRelevantTags->AppendTags(AllBlockedTags);
}
bBlocked = true;
}
if (!AbilitySystemComponentTags.HasAll(AllRequiredTags))
{
if (OptionalRelevantTags)
{
OptionalRelevantTags->AppendTags(AllRequiredTags);
}
bMissing = true;
}
}
if (SourceTags != nullptr)
{
CheckForBlocked(*SourceTags, SourceBlockedTags);
}
if (TargetTags != nullptr)
{
CheckForBlocked(*TargetTags, TargetBlockedTags);
}
// Now check all required tags
CheckForRequired(AbilitySystemComponent.GetOwnedGameplayTags(), AllRequiredTags);
if (SourceTags != nullptr)
{
CheckForRequired(*SourceTags, SourceRequiredTags);
}
if (TargetTags != nullptr)
{
CheckForRequired(*TargetTags, TargetRequiredTags);
}
if (!bBlocked && !bMissing)
{
// If it's a custom implementation that blocks, we can't specify exactly which tag so just use the generic
bBlocked = AbilitySystemComponent.AreAbilityTagsBlocked(GetAssetTags());
if (bBlocked && OptionalRelevantTags)
{
UAbilitySystemGlobals& AbilitySystemGlobals = UAbilitySystemGlobals::Get();
const FGameplayTag& BlockedTag = AbilitySystemGlobals.ActivateFailTagsBlockedTag;
OptionalRelevantTags->AddTag(BlockedTag);
}
}
// We succeeded if there were no blocked tags and no missing required tags
return !bBlocked && !bMissing;
}
void UGGA_GameplayAbility::SendTargetDataToServer(const FGameplayAbilityTargetDataHandle& TargetData)
{
if (IsPredictingClient())
{
UAbilitySystemComponent* ASC = CurrentActorInfo->AbilitySystemComponent.Get();
check(ASC);
// Create new prediction window for next operation. 为接下来的操作新增一个pk
FScopedPredictionWindow(ASC, true);
FGameplayTag ApplicationTag;
// tell server about it. 告诉服务器设置TargetData,传入技能的uid和激活id,Data和本次操作的id
CurrentActorInfo->AbilitySystemComponent->CallServerSetReplicatedTargetData(
CurrentSpecHandle, CurrentActivationInfo.GetActivationPredictionKey(),
TargetData, ApplicationTag, ASC->ScopedPredictionKey);
}
}
#if WITH_EDITOR
EDataValidationResult UGGA_GameplayAbility::IsDataValid(FDataValidationContext& Context) const
{
if (bReplicateInputDirectly == true)
{
Context.AddError(FText::FromString(TEXT("bReplicateInputDirectly is not recommended to use according to best practices.")));
return EDataValidationResult::Invalid;
}
if (bServerRespectsRemoteAbilityCancellation == true)
{
Context.AddError(FText::FromString(TEXT("bServerRespectsRemoteAbilityCancellation is not recommended to use according to best practices.")));
return EDataValidationResult::Invalid;
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (InstancingPolicy == EGameplayAbilityInstancingPolicy::NonInstanced)
{
Context.AddError(FText::FromString(TEXT("NonInstanced ability is deprecated since UE5.5, Use InstancedPerActor as the default to avoid confusing corner cases")));
return EDataValidationResult::Invalid;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
// if (ReplicationPolicy == EGameplayAbilityReplicationPolicy::Type::ReplicateYes)
// {
// Context.AddError(FText::FromString(TEXT("ReplicationPolicy->ReplicateYes is not acceptable, Pelease use other option!")));
// return EDataValidationResult::Invalid;
// }
// if (!AbilityTriggers.IsEmpty() && NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::Type::ServerInitiated)
// {
// ValidationErrors.Add(FText::FromString(TEXT("Ability with triggers doesn't work with ServerInitiated Net Execution Policy!")));
// return EDataValidationResult::Invalid;
// }
// if (NetExecutionPolicy == EGameplayAbilityNetExecutionPolicy::Type::ServerInitiated)
// {
// ValidationErrors.Add(FText::FromString(TEXT("NetExecutionPolicy->ServerInitiated is not acceptable, Pelease use other option!")));
// return EDataValidationResult::Invalid;
// }
if (bHasBlueprintActivateFromEvent && bHasBlueprintActivate && !AbilityTriggers.IsEmpty())
{
Context.AddError(FText::FromString(TEXT("ActivateAbilityFromEvent will not run! Please remove ActivateAbility node!")));
return EDataValidationResult::Invalid;
}
return Super::IsDataValid(Context);
}
#include "UObject/ObjectSaveContext.h"
void UGGA_GameplayAbility::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
}
#endif

View File

@@ -0,0 +1,7 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Abilities/GGA_GameplayAbilityInterface.h"
// Add default functionality here for any IGGA_GroupedAbilityInterface functions that are not pure virtual.