RealtimeStyleTransferRuntime/Source/LyraGame/AbilitySystem/Abilities/LyraGameplayAbility.cpp

533 lines
17 KiB
C++
Raw Normal View History

2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraGameplayAbility.h"
#include "LyraLogChannels.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "Player/LyraPlayerController.h"
#include "Character/LyraCharacter.h"
#include "LyraGameplayTags.h"
#include "LyraAbilityCost.h"
#include "Character/LyraHeroComponent.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemGlobals.h"
#include "LyraAbilitySimpleFailureMessage.h"
#include "GameFramework/GameplayMessageSubsystem.h"
#include "AbilitySystem/LyraAbilitySourceInterface.h"
#include "AbilitySystem/LyraGameplayEffectContext.h"
#include "Physics/PhysicalMaterialWithTags.h"
UE_DEFINE_GAMEPLAY_TAG(TAG_ABILITY_SIMPLE_FAILURE_MESSAGE, "Ability.UserFacingSimpleActivateFail.Message");
UE_DEFINE_GAMEPLAY_TAG(TAG_ABILITY_PLAY_MONTAGE_FAILURE_MESSAGE, "Ability.PlayMontageOnActivateFail.Message");
ULyraGameplayAbility::ULyraGameplayAbility(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
ReplicationPolicy = EGameplayAbilityReplicationPolicy::ReplicateNo;
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted;
NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ClientOrServer;
ActivationPolicy = ELyraAbilityActivationPolicy::OnInputTriggered;
ActivationGroup = ELyraAbilityActivationGroup::Independent;
ActiveCameraMode = nullptr;
}
ULyraAbilitySystemComponent* ULyraGameplayAbility::GetLyraAbilitySystemComponentFromActorInfo() const
{
return (CurrentActorInfo ? Cast<ULyraAbilitySystemComponent>(CurrentActorInfo->AbilitySystemComponent.Get()) : nullptr);
}
ALyraPlayerController* ULyraGameplayAbility::GetLyraPlayerControllerFromActorInfo() const
{
return (CurrentActorInfo ? Cast<ALyraPlayerController>(CurrentActorInfo->PlayerController.Get()) : nullptr);
}
AController* ULyraGameplayAbility::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;
}
ALyraCharacter* ULyraGameplayAbility::GetLyraCharacterFromActorInfo() const
{
return (CurrentActorInfo ? Cast<ALyraCharacter>(CurrentActorInfo->AvatarActor.Get()) : nullptr);
}
ULyraHeroComponent* ULyraGameplayAbility::GetHeroComponentFromActorInfo() const
{
return (CurrentActorInfo ? ULyraHeroComponent::FindHeroComponent(CurrentActorInfo->AvatarActor.Get()) : nullptr);
}
void ULyraGameplayAbility::NativeOnAbilityFailedToActivate(const FGameplayTagContainer& FailedReason) const
{
bool bSimpleFailureFound = false;
for (FGameplayTag Reason : FailedReason)
{
if (!bSimpleFailureFound)
{
if (const FText* pUserFacingMessage = FailureTagToUserFacingMessages.Find(Reason))
{
FLyraAbilitySimpleFailureMessage Message;
Message.PlayerController = GetActorInfo().PlayerController.Get();
Message.FailureTags = FailedReason;
Message.UserFacingReason = *pUserFacingMessage;
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld());
MessageSystem.BroadcastMessage(TAG_ABILITY_SIMPLE_FAILURE_MESSAGE, Message);
bSimpleFailureFound = true;
}
}
if (UAnimMontage* pMontage = FailureTagToAnimMontage.FindRef(Reason))
{
FLyraAbilityMontageFailureMessage Message;
Message.PlayerController = GetActorInfo().PlayerController.Get();
Message.FailureTags = FailedReason;
Message.FailureMontage = pMontage;
UGameplayMessageSubsystem& MessageSystem = UGameplayMessageSubsystem::Get(GetWorld());
MessageSystem.BroadcastMessage(TAG_ABILITY_PLAY_MONTAGE_FAILURE_MESSAGE, Message);
}
}
}
bool ULyraGameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const
{
if (!ActorInfo || !ActorInfo->AbilitySystemComponent.IsValid())
{
return false;
}
ULyraAbilitySystemComponent* LyraASC = CastChecked<ULyraAbilitySystemComponent>(ActorInfo->AbilitySystemComponent.Get());
const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get();
if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags))
{
return false;
}
//@TODO Possibly remove after setting up tag relationships
if (LyraASC->IsActivationGroupBlocked(ActivationGroup))
{
if (OptionalRelevantTags)
{
OptionalRelevantTags->AddTag(GameplayTags.Ability_ActivateFail_ActivationGroup);
}
return false;
}
return true;
}
void ULyraGameplayAbility::SetCanBeCanceled(bool bCanBeCanceled)
{
// The ability can not block canceling if it's replaceable.
if (!bCanBeCanceled && (ActivationGroup == ELyraAbilityActivationGroup::Exclusive_Replaceable))
{
UE_LOG(LogLyraAbilitySystem, Error, TEXT("SetCanBeCanceled: Ability [%s] can not block canceling because its activation group is replaceable."), *GetName());
return;
}
Super::SetCanBeCanceled(bCanBeCanceled);
}
void ULyraGameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
Super::OnGiveAbility(ActorInfo, Spec);
K2_OnAbilityAdded();
TryActivateAbilityOnSpawn(ActorInfo, Spec);
}
void ULyraGameplayAbility::OnRemoveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec)
{
K2_OnAbilityRemoved();
Super::OnRemoveAbility(ActorInfo, Spec);
}
void ULyraGameplayAbility::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
}
void ULyraGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled)
{
ClearCameraMode();
Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled);
}
bool ULyraGameplayAbility::CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, OUT FGameplayTagContainer* OptionalRelevantTags) const
{
if (!Super::CheckCost(Handle, ActorInfo, OptionalRelevantTags) || !ActorInfo)
{
return false;
}
// Verify we can afford any additional costs
for (TObjectPtr<ULyraAbilityCost> AdditionalCost : AdditionalCosts)
{
if (AdditionalCost != nullptr)
{
if (!AdditionalCost->CheckCost(this, Handle, ActorInfo, /*inout*/ OptionalRelevantTags))
{
return false;
}
}
}
return true;
}
void ULyraGameplayAbility::ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const
{
Super::ApplyCost(Handle, ActorInfo, ActivationInfo);
check(ActorInfo);
// 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 (ULyraAbilitySystemComponent* ASC = Cast<ULyraAbilitySystemComponent>(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<ULyraAbilityCost> AdditionalCost : AdditionalCosts)
{
if (AdditionalCost != nullptr)
{
if (AdditionalCost->ShouldOnlyApplyCostOnHit())
{
if (!bHasDeterminedIfAbilityHitTarget)
{
bAbilityHitTarget = DetermineIfAbilityHitTarget();
bHasDeterminedIfAbilityHitTarget = true;
}
if (!bAbilityHitTarget)
{
continue;
}
}
AdditionalCost->ApplyCost(this, Handle, ActorInfo, ActivationInfo);
}
}
}
FGameplayEffectContextHandle ULyraGameplayAbility::MakeEffectContext(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo) const
{
FGameplayEffectContextHandle ContextHandle = Super::MakeEffectContext(Handle, ActorInfo);
FLyraGameplayEffectContext* EffectContext = FLyraGameplayEffectContext::ExtractEffectContext(ContextHandle);
check(EffectContext);
check(ActorInfo);
AActor* EffectCauser = nullptr;
const ILyraAbilitySourceInterface* AbilitySource = nullptr;
float SourceLevel = 0.0f;
GetAbilitySource(Handle, ActorInfo, /*out*/ SourceLevel, /*out*/ AbilitySource, /*out*/ EffectCauser);
UObject* SourceObject = GetSourceObject(Handle, ActorInfo);
AActor* Instigator = ActorInfo ? ActorInfo->OwnerActor.Get() : nullptr;
EffectContext->SetAbilitySource(AbilitySource, SourceLevel);
EffectContext->AddInstigator(Instigator, EffectCauser);
EffectContext->AddSourceObject(SourceObject);
return ContextHandle;
}
void ULyraGameplayAbility::ApplyAbilityTagsToGameplayEffectSpec(FGameplayEffectSpec& Spec, FGameplayAbilitySpec* AbilitySpec) const
{
Super::ApplyAbilityTagsToGameplayEffectSpec(Spec, AbilitySpec);
if (const FHitResult* HitResult = Spec.GetContext().GetHitResult())
{
if (const UPhysicalMaterialWithTags* PhysMatWithTags = Cast<const UPhysicalMaterialWithTags>(HitResult->PhysMaterial.Get()))
{
Spec.CapturedTargetTags.GetSpecTags().AppendTags(PhysMatWithTags->Tags);
}
}
}
bool ULyraGameplayAbility::DoesAbilitySatisfyTagRequirements(const UAbilitySystemComponent& AbilitySystemComponent, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const
{
// Specialized version to handle death exclusion and AbilityTags expansion via ASC
bool bBlocked = false;
bool bMissing = false;
UAbilitySystemGlobals& AbilitySystemGlobals = UAbilitySystemGlobals::Get();
const FGameplayTag& BlockedTag = AbilitySystemGlobals.ActivateFailTagsBlockedTag;
const FGameplayTag& MissingTag = AbilitySystemGlobals.ActivateFailTagsMissingTag;
// Check if any of this ability's tags are currently blocked
if (AbilitySystemComponent.AreAbilityTagsBlocked(AbilityTags))
{
bBlocked = true;
}
const ULyraAbilitySystemComponent* LyraASC = Cast<ULyraAbilitySystemComponent>(&AbilitySystemComponent);
static FGameplayTagContainer AllRequiredTags;
static FGameplayTagContainer AllBlockedTags;
AllRequiredTags = ActivationRequiredTags;
AllBlockedTags = ActivationBlockedTags;
// Expand our ability tags to add additional required/blocked tags
if (LyraASC)
{
LyraASC->GetAdditionalActivationTagRequirements(AbilityTags, AllRequiredTags, 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))
{
const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get();
if (OptionalRelevantTags && AbilitySystemComponentTags.HasTag(GameplayTags.Status_Death))
{
// If player is dead and was rejected due to blocking tags, give that feedback
OptionalRelevantTags->AddTag(GameplayTags.Ability_ActivateFail_IsDead);
}
bBlocked = true;
}
if (!AbilitySystemComponentTags.HasAll(AllRequiredTags))
{
bMissing = true;
}
}
if (SourceTags != nullptr)
{
if (SourceBlockedTags.Num() || SourceRequiredTags.Num())
{
if (SourceTags->HasAny(SourceBlockedTags))
{
bBlocked = true;
}
if (!SourceTags->HasAll(SourceRequiredTags))
{
bMissing = true;
}
}
}
if (TargetTags != nullptr)
{
if (TargetBlockedTags.Num() || TargetRequiredTags.Num())
{
if (TargetTags->HasAny(TargetBlockedTags))
{
bBlocked = true;
}
if (!TargetTags->HasAll(TargetRequiredTags))
{
bMissing = true;
}
}
}
if (bBlocked)
{
if (OptionalRelevantTags && BlockedTag.IsValid())
{
OptionalRelevantTags->AddTag(BlockedTag);
}
return false;
}
if (bMissing)
{
if (OptionalRelevantTags && MissingTag.IsValid())
{
OptionalRelevantTags->AddTag(MissingTag);
}
return false;
}
return true;
}
void ULyraGameplayAbility::OnPawnAvatarSet()
{
K2_OnPawnAvatarSet();
}
void ULyraGameplayAbility::GetAbilitySource(FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, float& OutSourceLevel, const ILyraAbilitySourceInterface*& OutAbilitySource, AActor*& OutEffectCauser) const
{
OutSourceLevel = 0.0f;
OutAbilitySource = nullptr;
OutEffectCauser = nullptr;
OutEffectCauser = ActorInfo->AvatarActor.Get();
// If we were added by something that's an ability info source, use it
UObject* SourceObject = GetSourceObject(Handle, ActorInfo);
OutAbilitySource = Cast<ILyraAbilitySourceInterface>(SourceObject);
}
void ULyraGameplayAbility::TryActivateAbilityOnSpawn(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) const
{
const bool bIsPredicting = (Spec.ActivationInfo.ActivationMode == EGameplayAbilityActivationMode::Predicting);
// Try to activate if activation policy is on spawn.
if (ActorInfo && !Spec.IsActive() && !bIsPredicting && (ActivationPolicy == ELyraAbilityActivationPolicy::OnSpawn))
{
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);
}
}
}
}
bool ULyraGameplayAbility::CanChangeActivationGroup(ELyraAbilityActivationGroup NewGroup) const
{
if (!IsInstantiated() || !IsActive())
{
return false;
}
if (ActivationGroup == NewGroup)
{
return true;
}
ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponentFromActorInfo();
check(LyraASC);
if ((ActivationGroup != ELyraAbilityActivationGroup::Exclusive_Blocking) && LyraASC->IsActivationGroupBlocked(NewGroup))
{
// This ability can't change groups if it's blocked (unless it is the one doing the blocking).
return false;
}
if ((NewGroup == ELyraAbilityActivationGroup::Exclusive_Replaceable) && !CanBeCanceled())
{
// This ability can't become replaceable if it can't be canceled.
return false;
}
return true;
}
bool ULyraGameplayAbility::ChangeActivationGroup(ELyraAbilityActivationGroup NewGroup)
{
ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(ChangeActivationGroup, false);
if (!CanChangeActivationGroup(NewGroup))
{
return false;
}
if (ActivationGroup != NewGroup)
{
ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponentFromActorInfo();
check(LyraASC);
LyraASC->RemoveAbilityFromActivationGroup(ActivationGroup, this);
LyraASC->AddAbilityToActivationGroup(NewGroup, this);
ActivationGroup = NewGroup;
}
return true;
}
void ULyraGameplayAbility::SetCameraMode(TSubclassOf<ULyraCameraMode> CameraMode)
{
ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(SetCameraMode, );
if (ULyraHeroComponent* HeroComponent = GetHeroComponentFromActorInfo())
{
HeroComponent->SetAbilityCameraMode(CameraMode, CurrentSpecHandle);
ActiveCameraMode = CameraMode;
}
}
void ULyraGameplayAbility::ClearCameraMode()
{
ENSURE_ABILITY_IS_INSTANTIATED_OR_RETURN(ClearCameraMode, );
if (ActiveCameraMode)
{
if (ULyraHeroComponent* HeroComponent = GetHeroComponentFromActorInfo())
{
HeroComponent->ClearAbilityCameraMode(CurrentSpecHandle);
}
ActiveCameraMode = nullptr;
}
}