// 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(CurrentActorInfo->AbilitySystemComponent.Get()) : nullptr); } ALyraPlayerController* ULyraGameplayAbility::GetLyraPlayerControllerFromActorInfo() const { return (CurrentActorInfo ? Cast(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(TestActor)) { return C; } if (APawn* Pawn = Cast(TestActor)) { return Pawn->GetController(); } TestActor = TestActor->GetOwner(); } } return nullptr; } ALyraCharacter* ULyraGameplayAbility::GetLyraCharacterFromActorInfo() const { return (CurrentActorInfo ? Cast(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(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 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(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 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(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(&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(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 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; } }