// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action. #include "AbilitySystem/OLSAbilitySystemComponent.h" #include "AbilitySystemGlobals.h" #include "GameplayCueManager.h" #include "OLSLog.h" #include "AbilitySystem/OLSBatchGameplayAbilityInterface.h" #include "AbilitySystem/OLSGlobaAbilitySubsystem.h" #include "AbilitySystem/Abilities/OLSGameplayAbility.h" #include "AnimInstances/OLSBaseLayerAnimInstance.h" #include "DataAssets/OLSAbilityTagRelationshipMappingDataAsset.h" #include "DataAssets/OLSGameDataAsset.h" #include "Systems/OLSAssetManager.h" DEFINE_LOG_CATEGORY(LogOLSAbilitySystemComponent); UE_DEFINE_GAMEPLAY_TAG(TAG_Gameplay_AbilityInputBlocked, "Gameplay.AbilityInputBlocked"); // Sets default values for this component's properties UOLSAbilitySystemComponent::UOLSAbilitySystemComponent() { static constexpr bool isReplicated = true; SetIsReplicatedByDefault(isReplicated); bShouldEnableBatchRPC = true; } void UOLSAbilitySystemComponent::InitAbilityActorInfo(AActor* ownerActor, AActor* avatarActor) { FGameplayAbilityActorInfo* actorInfo = AbilityActorInfo.Get(); check(actorInfo); check(ownerActor); // Guard condition to ensure we should clear/init for this new Avatar Actor. const bool hasAvatarChanged = avatarActor && Cast(avatarActor) && (avatarActor != actorInfo->AvatarActor); Super::InitAbilityActorInfo(ownerActor, avatarActor); // Apply the new defaults obtained from the owner's interface. if (hasAvatarChanged) { if (const TObjectPtr globalAbilitySystem = UWorld::GetSubsystem(GetWorld())) { globalAbilitySystem->RegisterASC(this); } if (const TObjectPtr animInstance = Cast(GetAnimInstanceFromActorInfo())) { animInstance->InitializeWithAbilitySystem(this); } TryActivateAbilitiesOnSpawn(); } } bool UOLSAbilitySystemComponent::ShouldDoServerAbilityRPCBatch() const { return bShouldEnableBatchRPC; } float UOLSAbilitySystemComponent::PlayMontage( UGameplayAbility* animatingAbility, FGameplayAbilityActivationInfo activationInfo, UAnimMontage* montage, float playRate, FName startSectionName, float startTimeSeconds) { if (GEnableDefaultPlayMontage) { // Always useful to still allow the default flow, if there are some meaningful changes in the core system // that were not yet reflect in this custom implementation. Can be enabled with CVar "GEnableDefaultPlayMontage". // return Super::PlayMontage(animatingAbility, activationInfo, montage, playRate, startSectionName, startTimeSeconds); } float duration = -1.f; // This method was re-written just to ensure that the Animation Instance is retrieved from the Actor Info // by default, but also, other scenarios can be supported. Biggest example being an IK Runtime Retarget. // // This virtual "GetAnimInstanceFromActorInfo" provides some flexibility on how the Anim Instance is // retrieved. It can be extended in projects that should support IK Runtime Retargets and also traditional // Anim Instances set in the Actor Info. // const TObjectPtr animInstance = GetAnimInstanceFromActorInfo(); if (animInstance && montage) { duration = animInstance->Montage_Play( montage, playRate, EMontagePlayReturnType::MontageLength, startTimeSeconds); if (duration > 0.f) { if (montage->HasRootMotion() && animInstance->GetOwningActor()) { UE_LOG(LogRootMotion, Log, TEXT("UAbilitySystemComponent::PlayMontage %s, Role: %s") , *GetNameSafe(montage) , *UEnum::GetValueAsString(TEXT("Engine.ENetRole"), animInstance->GetOwningActor()->GetLocalRole()) ); } LocalAnimMontageInfo.AnimMontage = montage; LocalAnimMontageInfo.AnimatingAbility = animatingAbility; LocalAnimMontageInfo.PlayInstanceId = (LocalAnimMontageInfo.PlayInstanceId < UINT8_MAX ? LocalAnimMontageInfo.PlayInstanceId + 1 : 0); if (animatingAbility) { animatingAbility->SetCurrentMontage(montage); } // Start at a given Section. if (startSectionName != NAME_None) { animInstance->Montage_JumpToSection(startSectionName, montage); } // Replicate for non-owners and for replay recordings // The data we set from GetRepAnimMontageInfo_Mutable() is used both by the server to replicate to clients and by clients to record replays. // We need to set this data for recording clients because there exists network configurations where an abilities montage data will not replicate to some clients (for example: if the client is an autonomous proxy.) if (ShouldRecordMontageReplication()) { FGameplayAbilityRepAnimMontage& mutableRepAnimMontageInfo = GetRepAnimMontageInfo_Mutable(); SetReplicatedMontageInfo(mutableRepAnimMontageInfo, montage, startSectionName); // Update parameters that change during Montage lifetime. AnimMontage_UpdateReplicatedData(); } // Replicate to non-owners if (IsOwnerActorAuthoritative()) { // Force net update on our avatar actor. if (AbilityActorInfo->AvatarActor != nullptr) { AbilityActorInfo->AvatarActor->ForceNetUpdate(); } } else { // If this prediction key is rejected, we need to end the preview FPredictionKey predictionKey = GetPredictionKeyForNewAction(); if (predictionKey.IsValidKey()) { predictionKey.NewRejectedDelegate().BindUObject(this, &ThisClass::OnPredictiveMontageRejected, montage); } } } } return duration; } UAnimInstance* UOLSAbilitySystemComponent::GetAnimInstanceFromActorInfo() const { if (!AbilityActorInfo.IsValid()) { return nullptr; } const FGameplayAbilityActorInfo* actorInfo = AbilityActorInfo.Get(); if (actorInfo->AnimInstance.IsValid() && actorInfo->AnimInstance->IsValidLowLevelFast()) { // Return the one that was deliberately set in the Actor Info. return actorInfo->AnimInstance.Get(); } // Otherwise, let the getter method try to figure out the animation instance. return actorInfo->GetAnimInstance(); } FActiveGameplayEffectHandle UOLSAbilitySystemComponent::ApplyGameplayEffectClassToSelf( TSubclassOf effectClass, float level) { FActiveGameplayEffectHandle handle; if (IsValid(effectClass)) { FGameplayEffectContextHandle contextHandle = MakeEffectContext(); contextHandle.AddSourceObject(GetOwner()); const FGameplayEffectSpecHandle specHandle = MakeOutgoingSpec(effectClass, level, contextHandle); if (specHandle.IsValid()) { handle = ApplyGameplayEffectSpecToSelf(*specHandle.Data.Get()); OLS_LOG(LogOLSAbilitySystemComponent, Verbose, TEXT("[%s] Effect '%s' granted at level %f."), GET_UOBJECT_NAME(GetAvatarActor()), GET_UOBJECT_NAME(effectClass), level); } } return handle; } FGameplayAbilitySpecHandle UOLSAbilitySystemComponent::GiveAbilityFromClass( const TSubclassOf abilityClass, int32 level, int32 input) { FGameplayAbilitySpecHandle handle; if (IsValid(abilityClass)) { const FGameplayAbilitySpec newAbilitySpec(FGameplayAbilitySpec(abilityClass, level, input, GetOwner())); handle = GiveAbility(newAbilitySpec); OLS_LOG(LogOLSAbilitySystemComponent, Log, TEXT("[%s] Ability '%s' %s at level %d."), GET_UOBJECT_NAME(GetAvatarActor()), GET_UOBJECT_NAME(abilityClass), handle.IsValid() ? TEXT("successfully granted") : TEXT("failed to be granted"), level); } return handle; } bool UOLSAbilitySystemComponent::TryBatchActivateAbility( FGameplayAbilitySpecHandle abilityHandle, bool shouldEndAbilityImmediately) { bool isAbilityActivated = false; if (abilityHandle.IsValid()) { OLS_LOG(LogAbilitySystemComponent, Warning, TEXT("Ability handle is invalid!")); return isAbilityActivated; } FScopedServerAbilityRPCBatcher batch(this, abilityHandle); isAbilityActivated = TryActivateAbility(abilityHandle, true); if (!shouldEndAbilityImmediately) { const FGameplayAbilitySpec* abilitySpec = FindAbilitySpecFromHandle(abilityHandle); if (abilitySpec != nullptr) { UGameplayAbility* ability = abilitySpec->GetPrimaryInstance(); if (IsValid(ability) && ability->Implements()) { IOLSBatchGameplayAbilityInterface::Execute_EndAbilityFromBatch(ability); } else { OLS_LOG(LogAbilitySystemComponent, Error, TEXT("%s does not implement Batch Gameplay Ability Interface"), GET_UOBJECT_NAME(ability)); } } } return isAbilityActivated; } void UOLSAbilitySystemComponent::CancelAbilitiesByTags( FGameplayTagContainer abilityTags, FGameplayTagContainer cancelFilterTags) { CancelAbilities(&abilityTags, &cancelFilterTags); } void UOLSAbilitySystemComponent::ExecuteGameplayCueLocal( const FGameplayTag gameplayCueTag, const FGameplayCueParameters& gameplayCueParameters) const { const TObjectPtr cueManager = UAbilitySystemGlobals::Get().GetGameplayCueManager(); cueManager->HandleGameplayCue( GetOwner(), gameplayCueTag, EGameplayCueEvent::Type::Executed, gameplayCueParameters); } void UOLSAbilitySystemComponent::AddGameplayCueLocally( const FGameplayTag gameplayCueTag, const FGameplayCueParameters& gameplayCueParameters) const { const TObjectPtr cueManager = UAbilitySystemGlobals::Get().GetGameplayCueManager(); cueManager->HandleGameplayCue( GetOwner(), gameplayCueTag, EGameplayCueEvent::Type::OnActive, gameplayCueParameters); cueManager->HandleGameplayCue( GetOwner(), gameplayCueTag, EGameplayCueEvent::Type::WhileActive, gameplayCueParameters); } void UOLSAbilitySystemComponent::RemoveGameplayCueLocally( const FGameplayTag gameplayCueTag, const FGameplayCueParameters& gameplayCueParameters) const { const TObjectPtr cueManager = UAbilitySystemGlobals::Get().GetGameplayCueManager(); cueManager->HandleGameplayCue(GetOwner(), gameplayCueTag, EGameplayCueEvent::Type::Removed, gameplayCueParameters); } void UOLSAbilitySystemComponent::TryActivateAbilitiesOnSpawn() { ABILITYLIST_SCOPE_LOCK(); for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items) { if (const UOLSGameplayAbility* abilityCDO = Cast(abilitySpec.Ability)) { // @TODO: Implement UOLSGameplayAbility. // abilityCDO->TryActivateAbilityOnSpawn(AbilityActorInfo.Get(), abilitySpec); } } } void UOLSAbilitySystemComponent::AbilitySpecInputPressed(FGameplayAbilitySpec& spec) { Super::AbilitySpecInputPressed(spec); // We don't support UGameplayAbility::bReplicateInputDirectly. // Use replicated events instead so that the WaitInputPress ability task works. if (spec.IsActive()) { PRAGMA_DISABLE_DEPRECATION_WARNINGS const UGameplayAbility* instance = spec.GetPrimaryInstance(); FPredictionKey originalPredictionKey = instance ? instance->GetCurrentActivationInfo().GetActivationPredictionKey() : spec.ActivationInfo.GetActivationPredictionKey(); PRAGMA_ENABLE_DEPRECATION_WARNINGS // Invoke the InputPressed event. This is not replicated here. If someone is listening, they may replicate the InputPressed event to the server. InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputPressed, spec.Handle, originalPredictionKey); } } void UOLSAbilitySystemComponent::AbilitySpecInputReleased(FGameplayAbilitySpec& spec) { Super::AbilitySpecInputReleased(spec); // We don't support UGameplayAbility::bReplicateInputDirectly. // Use replicated events instead so that the WaitInputRelease ability task works. if (spec.IsActive()) { PRAGMA_DISABLE_DEPRECATION_WARNINGS const UGameplayAbility* instance = spec.GetPrimaryInstance(); FPredictionKey originalPredictionKey = instance ? instance->GetCurrentActivationInfo().GetActivationPredictionKey() : spec.ActivationInfo.GetActivationPredictionKey(); PRAGMA_ENABLE_DEPRECATION_WARNINGS // Invoke the InputReleased event. This is not replicated here. If someone is listening, they may replicate the InputReleased event to the server. InvokeReplicatedEvent(EAbilityGenericReplicatedEvent::InputReleased, spec.Handle, originalPredictionKey); } } void UOLSAbilitySystemComponent::NotifyAbilityActivated(const FGameplayAbilitySpecHandle handle, UGameplayAbility* ability) { Super::NotifyAbilityActivated(handle, ability); if (UOLSGameplayAbility* olsAbility = Cast(ability)) { // @TODO: Implement UOLSGameplayAbility. // AddAbilityToActivationGroup(olsAbility->GetActivationGroup(), ability); } } void UOLSAbilitySystemComponent::NotifyAbilityFailed(const FGameplayAbilitySpecHandle handle, UGameplayAbility* ability, const FGameplayTagContainer& failureReason) { Super::NotifyAbilityFailed(handle, ability, failureReason); if (APawn* avatar = Cast(GetAvatarActor())) { if (!avatar->IsLocallyControlled() && ability->IsSupportedForNetworking()) { ClientNotifyAbilityFailed(ability, failureReason); return; } } HandleAbilityFailed(ability, failureReason); } void UOLSAbilitySystemComponent::NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, UGameplayAbility* Ability, bool wasCancelled) { Super::NotifyAbilityEnded(handle, Ability, wasCancelled); if (UOLSGameplayAbility* olsAbility = Cast(Ability)) { // @TODO: Implement UOLSGameplayAbility. // RemoveAbilityFromActivationGroup(olsAbility->GetActivationGroup(), olsAbility); } } void UOLSAbilitySystemComponent::ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& abilityTags, UGameplayAbility* requestingAbility, bool shouldEnableBlockTags, const FGameplayTagContainer& blockTags, bool shouldExecuteCancelTags, const FGameplayTagContainer& cancelTags) { FGameplayTagContainer modifiedBlockTags = blockTags; FGameplayTagContainer modifiedCancelTags = cancelTags; if (TagRelationshipMapping) { // Use the mapping to expand the ability tags into block and cancel tag TagRelationshipMapping->GetAbilityTagsToBlockAndCancel(abilityTags, &modifiedBlockTags, &modifiedCancelTags); } Super::ApplyAbilityBlockAndCancelTags(abilityTags, requestingAbility, shouldEnableBlockTags, modifiedBlockTags, shouldExecuteCancelTags, modifiedCancelTags); //@TODO: Apply any special logic like blocking input or movement } void UOLSAbilitySystemComponent::HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& abilityTags, UGameplayAbility* requestingAbility, bool canBeCanceled) { Super::HandleChangeAbilityCanBeCanceled(abilityTags, requestingAbility, canBeCanceled); //@TODO: Apply any special logic like blocking input or movement } void UOLSAbilitySystemComponent::ClientNotifyAbilityFailed_Implementation(const UGameplayAbility* ability, const FGameplayTagContainer& failureReason) { HandleAbilityFailed(ability, failureReason); } void UOLSAbilitySystemComponent::HandleAbilityFailed(const UGameplayAbility* ability, const FGameplayTagContainer& failureReason) { if (const UOLSGameplayAbility* olsAbility = Cast(ability)) { // @TODO: Implement UOLSGameplayAbility. // olsAbility->OnAbilityFailedToActivate(failureReason); } } void UOLSAbilitySystemComponent::SetReplicatedMontageInfo( FGameplayAbilityRepAnimMontage& mutableRepAnimMontageInfo, UAnimMontage* newMontageToPlay, const FName& startSectionName) { const uint8 playInstanceId = mutableRepAnimMontageInfo.PlayInstanceId < UINT8_MAX ? mutableRepAnimMontageInfo.PlayInstanceId + 1 : 0; const uint8 sectionIdToPlay = newMontageToPlay->GetSectionIndex(startSectionName) + 1; TObjectPtr animation = newMontageToPlay; if (newMontageToPlay->IsDynamicMontage()) { animation = newMontageToPlay->GetFirstAnimReference(); check(!newMontageToPlay->SlotAnimTracks.IsEmpty()); mutableRepAnimMontageInfo.SlotName = newMontageToPlay->SlotAnimTracks[0].SlotName; mutableRepAnimMontageInfo.BlendOutTime = newMontageToPlay->GetDefaultBlendInTime(); } mutableRepAnimMontageInfo.Animation = animation; mutableRepAnimMontageInfo.PlayInstanceId = playInstanceId; mutableRepAnimMontageInfo.SectionIdToPlay = 0; if (mutableRepAnimMontageInfo.Animation && startSectionName != NAME_None) { mutableRepAnimMontageInfo.SectionIdToPlay = sectionIdToPlay; } } void UOLSAbilitySystemComponent::CancelAbilitiesByFunc(TShouldCancelAbilityFunc shouldCancelFunc, bool shouldReplicateCancelAbility) { ABILITYLIST_SCOPE_LOCK(); for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items) { if (!abilitySpec.IsActive()) { continue; } UOLSGameplayAbility* abilityCDO = Cast(abilitySpec.Ability); if (!abilityCDO) { OLS_LOG(LogOLSAbilitySystemComponent, Error, TEXT("CancelAbilitiesByFunc: Non-LyraGameplayAbility %s was Granted to ASC. Skipping."), GET_UOBJECT_NAME(abilitySpec.Ability)); continue; } PRAGMA_DISABLE_DEPRECATION_WARNINGS ensureMsgf(abilitySpec.Ability->GetInstancingPolicy() != EGameplayAbilityInstancingPolicy::NonInstanced, TEXT("CancelAbilitiesByFunc: All Abilities should be Instanced (NonInstanced is being deprecated due to usability issues).")); PRAGMA_ENABLE_DEPRECATION_WARNINGS // Cancel all the spawned instances. TArray Instances = abilitySpec.GetAbilityInstances(); for (UGameplayAbility* AbilityInstance : Instances) { UOLSGameplayAbility* abilityInstance = CastChecked(AbilityInstance); if (shouldCancelFunc(abilityInstance, abilitySpec.Handle)) { if (abilityInstance->CanBeCanceled()) { abilityInstance->CancelAbility(abilitySpec.Handle, AbilityActorInfo.Get(), abilityInstance->GetCurrentActivationInfo(), shouldReplicateCancelAbility); } else { OLS_LOG(LogOLSAbilitySystemComponent, Error, TEXT("CancelAbilitiesByFunc: Can't cancel ability [%s] because CanBeCanceled is false."), GET_UOBJECT_NAME(abilityInstance)); } } } } } void UOLSAbilitySystemComponent::CancelInputActivatedAbilities(bool shouldReplicateCancelAbility) { // @TODO: Implement UOLSGameplayAbility // auto shouldCancelFunc = [this](const UOLSGameplayAbility* ability, FGameplayAbilitySpecHandle handle) // { // const ELyraAbilityActivationPolicy ActivationPolicy = ability->GetActivationPolicy(); // return ((ActivationPolicy == ELyraAbilityActivationPolicy::OnInputTriggered) || (ActivationPolicy == ELyraAbilityActivationPolicy::WhileInputActive)); // }; // // CancelAbilitiesByFunc(shouldCancelFunc, shouldReplicateCancelAbility); } void UOLSAbilitySystemComponent::AbilityInputTagPressed(const FGameplayTag& inputTag) { if (inputTag.IsValid()) { for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items) { if (abilitySpec.Ability && (abilitySpec.GetDynamicSpecSourceTags().HasTagExact(inputTag))) { InputPressedSpecHandles.AddUnique(abilitySpec.Handle); InputHeldSpecHandles.AddUnique(abilitySpec.Handle); } } } } void UOLSAbilitySystemComponent::AbilityInputTagReleased(const FGameplayTag& inputTag) { if (inputTag.IsValid()) { for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items) { if (abilitySpec.Ability && (abilitySpec.GetDynamicSpecSourceTags().HasTagExact(inputTag))) { InputReleasedSpecHandles.AddUnique(abilitySpec.Handle); InputHeldSpecHandles.Remove(abilitySpec.Handle); } } } } void UOLSAbilitySystemComponent::ProcessAbilityInput(float deltaTime, bool shouldGamePaused) { if (HasMatchingGameplayTag(TAG_Gameplay_AbilityInputBlocked)) { ClearAbilityInput(); return; } static TArray abilitiesToActivate; abilitiesToActivate.Reset(); //@TODO: See if we can use FScopedServerAbilityRPCBatcher ScopedRPCBatcher in some of these loops // // Process all abilities that activate when the input is held. // for (const FGameplayAbilitySpecHandle& specHandle : InputHeldSpecHandles) { if (const FGameplayAbilitySpec* abilitySpec = FindAbilitySpecFromHandle(specHandle)) { if (abilitySpec->Ability && !abilitySpec->IsActive()) { const UOLSGameplayAbility* abilityCDO = Cast(abilitySpec->Ability); // @TODO: Implement UOLSGameplayAbility. // if (abilityCDO && abilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::WhileInputActive) // { // AbilitiesToActivate.AddUnique(AbilitySpec->Handle); // } } } } // // Process all abilities that had their input pressed this frame. // for (const FGameplayAbilitySpecHandle& specHandle : InputPressedSpecHandles) { if (FGameplayAbilitySpec* abilitySpec = FindAbilitySpecFromHandle(specHandle)) { if (abilitySpec->Ability) { abilitySpec->InputPressed = true; if (abilitySpec->IsActive()) { // Ability is active so pass along the input event. AbilitySpecInputPressed(*abilitySpec); } else { const UOLSGameplayAbility* abilityCDO = Cast(abilitySpec->Ability); // @TODO: Implement UOLSGameplayAbility. // if (abilityCDO && abilityCDO->GetActivationPolicy() == ELyraAbilityActivationPolicy::OnInputTriggered) // { // abilitiesToActivate.AddUnique(abilitySpec->Handle); // } } } } } // // Try to activate all the abilities that are from presses and holds. // We do it all at once so that held inputs don't activate the ability // and then also send a input event to the ability because of the press. // for (const FGameplayAbilitySpecHandle& abilitySpecHandle : abilitiesToActivate) { TryActivateAbility(abilitySpecHandle); } // // Process all abilities that had their input released this frame. // for (const FGameplayAbilitySpecHandle& specHandle : InputReleasedSpecHandles) { if (FGameplayAbilitySpec* abilitySpec = FindAbilitySpecFromHandle(specHandle)) { if (abilitySpec->Ability) { abilitySpec->InputPressed = false; if (abilitySpec->IsActive()) { // Ability is active so pass along the input event. AbilitySpecInputReleased(*abilitySpec); } } } } // // Clear the cached ability handles. // InputPressedSpecHandles.Reset(); InputReleasedSpecHandles.Reset(); } void UOLSAbilitySystemComponent::ClearAbilityInput() { InputPressedSpecHandles.Reset(); InputReleasedSpecHandles.Reset(); InputHeldSpecHandles.Reset(); } void UOLSAbilitySystemComponent::AddDynamicTagGameplayEffect(const FGameplayTag& tag) { const TSubclassOf dynamicTagGE = UOLSAssetManager::GetSubclass(UOLSGameDataAsset::Get().DynamicTagGameplayEffect); if (!dynamicTagGE) { OLS_LOG(LogOLSAbilitySystemComponent, Warning, TEXT("AddDynamicTagGameplayEffect: Unable to find DynamicTagGameplayEffect [%s]."), *UOLSGameDataAsset::Get().DynamicTagGameplayEffect.GetAssetName()); return; } const FGameplayEffectSpecHandle specHandle = MakeOutgoingSpec(dynamicTagGE, 1.0f, MakeEffectContext()); FGameplayEffectSpec* spec = specHandle.Data.Get(); if (!spec) { OLS_LOG(LogOLSAbilitySystemComponent, Warning, TEXT("AddDynamicTagGameplayEffect: Unable to make outgoing spec for [%s]."), GET_UOBJECT_NAME(dynamicTagGE)); return; } spec->DynamicGrantedTags.AddTag(tag); ApplyGameplayEffectSpecToSelf(*spec); } void UOLSAbilitySystemComponent::RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag) { const TSubclassOf dynamicTagGE = UOLSAssetManager::GetSubclass(UOLSGameDataAsset::Get().DynamicTagGameplayEffect); if (!dynamicTagGE) { OLS_LOG(LogOLSAbilitySystemComponent, Warning, TEXT("RemoveDynamicTagGameplayEffect: Unable to find gameplay effect [%s]."), UOLSGameDataAsset::Get().DynamicTagGameplayEffect.GetAssetName()); return; } FGameplayEffectQuery query = FGameplayEffectQuery::MakeQuery_MatchAnyOwningTags(FGameplayTagContainer(Tag)); query.EffectDefinition = dynamicTagGE; RemoveActiveEffects(query); } void UOLSAbilitySystemComponent::GetAbilityTargetData(const FGameplayAbilitySpecHandle abilityHandle, FGameplayAbilityActivationInfo activationInfo, FGameplayAbilityTargetDataHandle& outTargetDataHandle) { TSharedPtr replicatedData = AbilityTargetDataMap.Find( FGameplayAbilitySpecHandleAndPredictionKey(abilityHandle, activationInfo.GetActivationPredictionKey())); if (replicatedData.IsValid()) { outTargetDataHandle = replicatedData->TargetData; } } void UOLSAbilitySystemComponent::SetTagRelationshipMapping(UOLSAbilityTagRelationshipMappingDataAsset* newMapping) { TagRelationshipMapping = newMapping; } void UOLSAbilitySystemComponent::GetAdditionalActivationTagRequirements(const FGameplayTagContainer& abilityTags, FGameplayTagContainer& outActivationRequired, FGameplayTagContainer& outActivationBlocked) const { if (TagRelationshipMapping) { TagRelationshipMapping->GetRequiredAndBlockedActivationTags(abilityTags, &outActivationRequired, &outActivationBlocked); } }