第一次提交
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user