OLS/Source/ols/Private/AbilitySystem/OLSAbilitySystemComponent.cpp
LongLy 57b53b9c0c Implemented OLSCameraMode.
Addressed @TODOs related to custom logs and OLSCameraMode
2025-01-20 14:08:07 -07:00

815 lines
28 KiB
C++

// © 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<APawn>(avatarActor) && (avatarActor != actorInfo->AvatarActor);
Super::InitAbilityActorInfo(ownerActor, avatarActor);
// Apply the new defaults obtained from the owner's interface.
if (hasAvatarChanged)
{
if (const TObjectPtr<UOLSGlobaAbilitySubsystem> globalAbilitySystem = UWorld::GetSubsystem<UOLSGlobaAbilitySubsystem>(GetWorld()))
{
globalAbilitySystem->RegisterASC(this);
}
if (const TObjectPtr<UOLSBaseLayerAnimInstance> animInstance = Cast<UOLSBaseLayerAnimInstance>(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<UAnimInstance> 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<UGameplayEffect> 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<UGameplayAbility> 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<UOLSBatchGameplayAbilityInterface>())
{
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<UGameplayCueManager> cueManager = UAbilitySystemGlobals::Get().GetGameplayCueManager();
cueManager->HandleGameplayCue(
GetOwner(),
gameplayCueTag,
EGameplayCueEvent::Type::Executed,
gameplayCueParameters);
}
void UOLSAbilitySystemComponent::AddGameplayCueLocally(
const FGameplayTag gameplayCueTag,
const FGameplayCueParameters& gameplayCueParameters) const
{
const TObjectPtr<UGameplayCueManager> 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<UGameplayCueManager> 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<UOLSGameplayAbility>(abilitySpec.Ability))
{
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<UOLSGameplayAbility>(ability))
{
AddAbilityToActivationGroup(olsAbility->GetActivationGroup(), olsAbility);
}
}
void UOLSAbilitySystemComponent::NotifyAbilityFailed(const FGameplayAbilitySpecHandle handle,
UGameplayAbility* ability,
const FGameplayTagContainer& failureReason)
{
Super::NotifyAbilityFailed(handle, ability, failureReason);
if (APawn* avatar = Cast<APawn>(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<UOLSGameplayAbility>(ability))
{
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<const UOLSGameplayAbility>(ability))
{
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<UAnimSequenceBase> 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<UOLSGameplayAbility>(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<UGameplayAbility*> Instances = abilitySpec.GetAbilityInstances();
for (UGameplayAbility* AbilityInstance : Instances)
{
UOLSGameplayAbility* abilityInstance = CastChecked<UOLSGameplayAbility>(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)
{
auto shouldCancelFunc = [this](const UOLSGameplayAbility* ability, FGameplayAbilitySpecHandle handle)
{
const EOLSAbilityActivationPolicy activationPolicy = ability->GetActivationPolicy();
return ((activationPolicy == EOLSAbilityActivationPolicy::OnInputTriggered) || (activationPolicy == EOLSAbilityActivationPolicy::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<FGameplayAbilitySpecHandle> 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<UOLSGameplayAbility>(abilitySpec->Ability);
if (abilityCDO && abilityCDO->GetActivationPolicy() == EOLSAbilityActivationPolicy::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<UOLSGameplayAbility>(abilitySpec->Ability);
if (abilityCDO && abilityCDO->GetActivationPolicy() == EOLSAbilityActivationPolicy::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();
}
bool UOLSAbilitySystemComponent::IsActivationGroupBlocked(EOLSAbilityActivationGroup Group) const
{
bool isBlocked = false;
switch (Group)
{
case EOLSAbilityActivationGroup::Independent:
// Independent abilities are never blocked.
isBlocked = false;
break;
case EOLSAbilityActivationGroup::Exclusive_Replaceable:
case EOLSAbilityActivationGroup::Exclusive_Blocking:
// Exclusive abilities can activate if nothing is blocking.
isBlocked = (ActivationGroupCounts[(uint8)EOLSAbilityActivationGroup::Exclusive_Blocking] > 0);
break;
default:
checkf(false, TEXT("IsActivationGroupBlocked: Invalid ActivationGroup [%d]\n"), (uint8)Group);
break;
}
return isBlocked;
}
void UOLSAbilitySystemComponent::AddAbilityToActivationGroup(EOLSAbilityActivationGroup group,
UOLSGameplayAbility* ability)
{
check(ability);
check(ActivationGroupCounts[(uint8)group] < INT32_MAX);
ActivationGroupCounts[(uint8)group]++;
constexpr bool shouldReplicateCancelAbility = false;
switch (group)
{
case EOLSAbilityActivationGroup::Independent:
// Independent abilities do not cancel any other abilities.
break;
case EOLSAbilityActivationGroup::Exclusive_Replaceable:
case EOLSAbilityActivationGroup::Exclusive_Blocking:
CancelActivationGroupAbilities(EOLSAbilityActivationGroup::Exclusive_Replaceable, ability, shouldReplicateCancelAbility);
break;
default:
checkf(false, TEXT("AddAbilityToActivationGroup: Invalid ActivationGroup [%d]\n"), (uint8)group);
break;
}
const int32 exclusiveCount = ActivationGroupCounts[static_cast<uint8>(
EOLSAbilityActivationGroup::Exclusive_Replaceable)] + ActivationGroupCounts[static_cast<uint8>(
EOLSAbilityActivationGroup::Exclusive_Blocking)];
if (!ensure(exclusiveCount <= 1))
{
OLS_LOG(LogOLSAbilitySystemComponent, Error,
TEXT("Multiple exclusive abilities are running."));
}
}
void UOLSAbilitySystemComponent::RemoveAbilityFromActivationGroup(EOLSAbilityActivationGroup group,
UOLSGameplayAbility* ability)
{
check(ability);
check(ActivationGroupCounts[(uint8)group] > 0);
ActivationGroupCounts[(uint8)group]--;
}
void UOLSAbilitySystemComponent::CancelActivationGroupAbilities(EOLSAbilityActivationGroup group,
UOLSGameplayAbility* ignoreAbility,
bool shouldReplicateCancelAbility)
{
auto shouldCancelFunc = [this, group, ignoreAbility](const UOLSGameplayAbility* ability,
FGameplayAbilitySpecHandle handle)
{
return ((ability->GetActivationGroup() == group) && (ability != ignoreAbility));
};
CancelAbilitiesByFunc(shouldCancelFunc, shouldReplicateCancelAbility);
}
void UOLSAbilitySystemComponent::AddDynamicTagGameplayEffect(const FGameplayTag& tag)
{
const TSubclassOf<UGameplayEffect> 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<UGameplayEffect> 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<FAbilityReplicatedDataCache> 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);
}
}