// Copyright 2025 https://yuewu.dev/en All Rights Reserved. #include "Utilities/GGA_GameplayEffectContainerFunctionLibrary.h" #include "AbilitySystemBlueprintLibrary.h" #include "AbilitySystemComponent.h" #include "GameplayAbilitySpec.h" #include "AbilitySystemGlobals.h" #include "GGA_LogChannels.h" #include "TargetingSystem/TargetingSubsystem.h" bool UGGA_GameplayEffectContainerFunctionLibrary::IsValidContainer(const FGGA_GameplayEffectContainer& Container) { return !Container.TargetGameplayEffectClasses.IsEmpty() && Container.TargetingPreset != nullptr; } bool UGGA_GameplayEffectContainerFunctionLibrary::HasValidEffects(const FGGA_GameplayEffectContainerSpec& ContainerSpec) { return ContainerSpec.HasValidEffects(); } bool UGGA_GameplayEffectContainerFunctionLibrary::HasValidTargets(const FGGA_GameplayEffectContainerSpec& ContainerSpec) { return ContainerSpec.HasValidTargets(); } FGGA_GameplayEffectContainerSpec UGGA_GameplayEffectContainerFunctionLibrary::AddTargets(const FGGA_GameplayEffectContainerSpec& ContainerSpec, const TArray& HitResults, const TArray& TargetActors) { FGGA_GameplayEffectContainerSpec NewSpec = ContainerSpec; NewSpec.AddTargets(HitResults, TargetActors); return NewSpec; } FGGA_GameplayEffectContainerSpec UGGA_GameplayEffectContainerFunctionLibrary::MakeEffectContainerSpec(const FGGA_GameplayEffectContainer& Container, const FGameplayEventData& EventData, int32 OverrideGameplayLevel, UGameplayAbility* SourceAbility) { FGGA_GameplayEffectContainerSpec ReturnSpec; if (SourceAbility== nullptr && EventData.Instigator == nullptr) { UE_LOG(LogGGA_AbilitySystem, Warning, TEXT("Missing instigator within EventData, It's required to look for Source AbilitySystemComponent!")) return ReturnSpec; } // First figure out our actor info UAbilitySystemComponent* SourceASC = SourceAbility?SourceAbility->GetAbilitySystemComponentFromActorInfo():UAbilitySystemGlobals::GetAbilitySystemComponentFromActor(EventData.Instigator); if (SourceASC && SourceASC->GetAvatarActor()) { if (Container.TargetingPreset.Get()) { if (UTargetingSubsystem* TargetingSubsystem = UTargetingSubsystem::Get(SourceASC->GetWorld())) { FTargetingSourceContext SourceContext; SourceContext.SourceActor = SourceASC->GetAvatarActor(); FTargetingRequestHandle TargetingHandle = UTargetingSubsystem::MakeTargetRequestHandle(Container.TargetingPreset, SourceContext); FTargetingRequestDelegate Delegate = FTargetingRequestDelegate::CreateLambda([&](FTargetingRequestHandle InTargetingHandle) { TArray HitResults; TArray TargetActors; TargetingSubsystem->GetTargetingResults(InTargetingHandle, HitResults); ReturnSpec.AddTargets(HitResults, TargetActors); }); UE_LOG(LogGGA_AbilitySystem, VeryVerbose, TEXT("Starting immediate targeting task for EffectContainer.")); FTargetingImmediateTaskData& ImmediateTaskData = FTargetingImmediateTaskData::FindOrAdd(TargetingHandle); ImmediateTaskData.bReleaseOnCompletion = true; TargetingSubsystem->ExecuteTargetingRequestWithHandle(TargetingHandle, Delegate); } } int32 Level = OverrideGameplayLevel >= 0 ? OverrideGameplayLevel : EventData.EventMagnitude; // Build GameplayEffectSpecs for each applied effect for (const TSubclassOf& EffectClass : Container.TargetGameplayEffectClasses) { FGameplayEffectSpecHandle SpecHandle = SourceAbility?SourceAbility->MakeOutgoingGameplayEffectSpec(EffectClass, Level):SourceASC->MakeOutgoingSpec(EffectClass, Level, SourceASC->MakeEffectContext()); FGameplayEffectContextHandle ContextHandle = UAbilitySystemBlueprintLibrary::GetEffectContext(SpecHandle); if (SourceAbility==nullptr && EventData.OptionalObject) { ContextHandle.AddSourceObject(EventData.OptionalObject); } ReturnSpec.TargetGameplayEffectSpecs.Add(SpecHandle); } if (EventData.TargetData.Num() > 0) { ReturnSpec.TargetData.Append(EventData.TargetData); } } return ReturnSpec; } TArray UGGA_GameplayEffectContainerFunctionLibrary::ApplyEffectContainerSpec(UGameplayAbility* ExecutingAbility, const FGGA_GameplayEffectContainerSpec& ContainerSpec) { TArray AllEffects; if (!IsValid(ExecutingAbility) || !ExecutingAbility->IsInstantiated()) { UE_LOG(LogGGA_Ability, Error, TEXT("Requires \"Executing ability\" to apply effect container spec.")) return AllEffects; } const FGameplayAbilityActorInfo* ActorInfo = ExecutingAbility->GetCurrentActorInfo(); const FGameplayAbilityActivationInfo& ActivationInfo = ExecutingAbility->GetCurrentActivationInfoRef(); // Iterate list of effect specs and apply them to their target data for (const FGameplayEffectSpecHandle& SpecHandle : ContainerSpec.TargetGameplayEffectSpecs) { TArray EffectHandles; if (SpecHandle.IsValid() && ExecutingAbility->HasAuthorityOrPredictionKey(ActorInfo, &ActivationInfo)) { FScopedTargetListLock ActiveScopeLock(*ActorInfo->AbilitySystemComponent, *ExecutingAbility); for (TSharedPtr Data : ContainerSpec.TargetData.Data) { if (Data.IsValid()) { AllEffects.Append(Data->ApplyGameplayEffectSpec(*SpecHandle.Data.Get(), ActorInfo->AbilitySystemComponent->GetPredictionKeyForNewAction())); } else { UE_LOG(LogGGA_Ability, Warning, TEXT("ApplyGameplayEffectSpecToTarget invalid target data passed in. Ability: %s"), *ExecutingAbility->GetPathName()); } } } } return AllEffects; } TArray UGGA_GameplayEffectContainerFunctionLibrary::ApplyExternalEffectContainerSpec(const FGGA_GameplayEffectContainerSpec& ContainerSpec) { TArray AllEffects; // Iterate list of gameplay effects for (const FGameplayEffectSpecHandle& SpecHandle : ContainerSpec.TargetGameplayEffectSpecs) { if (SpecHandle.IsValid()) { // If effect is valid, iterate list of targets and apply to all for (TSharedPtr Data : ContainerSpec.TargetData.Data) { AllEffects.Append(Data->ApplyGameplayEffectSpec(*SpecHandle.Data.Get())); } } } return AllEffects; }