Implemented OLSPawnExtensionComponent.

Added some functionalities for OLSAbilitySystemComponent
This commit is contained in:
LongLy 2025-01-16 16:04:14 -07:00
parent e0545d6323
commit 51306c57a9
4 changed files with 880 additions and 4 deletions

View File

@ -8,10 +8,16 @@
#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()
{
@ -292,10 +298,142 @@ void UOLSAbilitySystemComponent::RemoveGameplayCueLocally(
void UOLSAbilitySystemComponent::TryActivateAbilitiesOnSpawn()
{
ABILITYLIST_SCOPE_LOCK();
for (const FGameplayAbilitySpec& abilitySpec : GetActivatableAbilities())
for (const FGameplayAbilitySpec& abilitySpec : ActivatableAbilities.Items)
{
// if (const UOLS)
if (const UOLSGameplayAbility* abilityCDO = Cast<UOLSGameplayAbility>(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<UOLSGameplayAbility>(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<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))
{
// @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<const UOLSGameplayAbility>(ability))
{
// @TODO: Implement UOLSGameplayAbility.
// olsAbility->OnAbilityFailedToActivate(failureReason);
}
}
@ -327,4 +465,271 @@ void UOLSAbilitySystemComponent::SetReplicatedMontageInfo(
}
}
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)
{
// @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<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);
// @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<UOLSGameplayAbility>(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<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);
}
}

View File

@ -0,0 +1,304 @@
// © 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 "Components/OLSPawnExtensionComponent.h"
#include "OLSLog.h"
#include "AbilitySystem/OLSAbilitySystemComponent.h"
#include "Components/GameFrameworkComponentManager.h"
#include "DataAssets/OLSPawnDataAsset.h"
DEFINE_LOG_CATEGORY(LogOLSPawnExtensionComponent);
UOLSPawnExtensionComponent::UOLSPawnExtensionComponent(const FObjectInitializer& objectInitializer)
: Super(objectInitializer)
{
PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = false;
SetIsReplicatedByDefault(true);
PawnData = nullptr;
AbilitySystemComponent = nullptr;
}
FName UOLSPawnExtensionComponent::GetFeatureName() const
{
return NAME_ActorFeatureName;
}
bool UOLSPawnExtensionComponent::CanChangeInitState(
UGameFrameworkComponentManager* manager,
FGameplayTag currentState,
FGameplayTag desiredState) const
{
check(manager);
APawn* pawn = GetPawn<APawn>();
// @TODO: Implement LyraGameplayTags::InitState_Spawned.
if (!currentState.IsValid() /* && desiredState == LyraGameplayTags::InitState_Spawned */)
{
// As long as we are on a valid pawn, we count as spawned
if (pawn)
{
return true;
}
}
// @TODO: Implement LyraGameplayTags::InitState_Spawned,
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable,
// @TODO: Implement LyraGameplayTags::InitState_DataInitialized,
// @TODO: Implement LyraGameplayTags::InitState_GameplayReady
// if (currentState == LyraGameplayTags::InitState_Spawned && desiredState == LyraGameplayTags::InitState_DataAvailable)
// {
// // Pawn data is required.
// if (!PawnData)
// {
// return false;
// }
//
// const bool bHasAuthority = pawn->HasAuthority();
// const bool bIsLocallyControlled = pawn->IsLocallyControlled();
//
// if (bHasAuthority || bIsLocallyControlled)
// {
// // Check for being possessed by a controller.
// if (!GetController<AController>())
// {
// return false;
// }
// }
//
// return true;
// }
// else if (currentState == LyraGameplayTags::InitState_DataAvailable && desiredState == LyraGameplayTags::InitState_DataInitialized)
// {
// // Transition to initialize if all features have their data available
// return manager->HaveAllFeaturesReachedInitState(pawn, LyraGameplayTags::InitState_DataAvailable);
// }
// else if (currentState == LyraGameplayTags::InitState_DataInitialized && desiredState == LyraGameplayTags::InitState_GameplayReady)
// {
// return true;
// }
return false;
}
void UOLSPawnExtensionComponent::HandleChangeInitState(
UGameFrameworkComponentManager* manager,
FGameplayTag currentState,
FGameplayTag desiredState)
{
// @TODO: Implement LyraGameplayTags::InitState_Spawned.
// if (desiredState == LyraGameplayTags::InitState_DataInitialized)
// {
// // This is currently all handled by other components listening to this state change
// }
}
void UOLSPawnExtensionComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params)
{
// If another feature is now in DataAvailable, see if we should transition to DataInitialized
if (Params.FeatureName != NAME_ActorFeatureName)
{
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable.
// if (Params.FeatureState == LyraGameplayTags::InitState_DataAvailable)
// {
// CheckDefaultInitialization();
// }
}
}
void UOLSPawnExtensionComponent::CheckDefaultInitialization()
{
// Before checking our progress, try progressing any other features we might depend on
CheckDefaultInitializationForImplementers();
// @TODO: Implement LyraGameplayTags::InitState_Spawned,
// @TODO: Implement LyraGameplayTags::InitState_DataAvailable,
// @TODO: Implement LyraGameplayTags::InitState_DataInitialized,
// @TODO: Implement LyraGameplayTags::InitState_GameplayReady
// static const TArray<FGameplayTag> StateChain = {
// LyraGameplayTags::InitState_Spawned, LyraGameplayTags::InitState_DataAvailable,
// LyraGameplayTags::InitState_DataInitialized, LyraGameplayTags::InitState_GameplayReady
// };
// This will try to progress from spawned (which is only set in BeginPlay) through the data initialization stages until it gets to gameplay ready
// ContinueInitStateChain(StateChain);
}
UOLSPawnExtensionComponent* UOLSPawnExtensionComponent::FindPawnExtensionComponent(const AActor* actor)
{
return (actor ? actor->FindComponentByClass<UOLSPawnExtensionComponent>() : nullptr);
}
UOLSAbilitySystemComponent* UOLSPawnExtensionComponent::GetOLSAbilitySystemComponent() const
{
return AbilitySystemComponent;
}
void UOLSPawnExtensionComponent::SetPawnData(const UOLSPawnDataAsset* pawnData)
{
check(pawnData);
APawn* pawn = GetPawnChecked<APawn>();
if (pawn->GetLocalRole() != ROLE_Authority)
{
return;
}
if (PawnData)
{
OLS_LOG(LogOLSPawnExtensionComponent, Error,
TEXT("Trying to set PawnData [%s] on pawn [%s] that already has valid PawnData [%s]."),
GET_UOBJECT_NAME(pawnData), GET_UOBJECT_NAME(pawn), GET_UOBJECT_NAME(PawnData));
return;
}
PawnData = pawnData;
pawn->ForceNetUpdate();
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::InitializeAbilitySystem(UOLSAbilitySystemComponent* asc, AActor* ownerActor)
{
check(asc);
check(ownerActor);
if (AbilitySystemComponent == asc)
{
// The ability system component hasn't changed.
return;
}
if (AbilitySystemComponent)
{
// Clean up the old ability system component.
UninitializeAbilitySystem();
}
APawn* pawn = GetPawnChecked<APawn>();
AActor* existingAvatar = asc->GetAvatarActor();
OLS_LOG(LogOLSPawnExtensionComponent, Verbose, TEXT("Setting up ASC [%s] on pawn [%s] owner [%s], existing [%s] "),
GET_UOBJECT_NAME(asc), GET_UOBJECT_NAME(pawn), GET_UOBJECT_NAME(ownerActor),
GET_UOBJECT_NAME(existingAvatar));
if ((existingAvatar != nullptr) && (existingAvatar != pawn))
{
OLS_LOG(LogOLSPawnExtensionComponent, Log, TEXT("Existing avatar (authority=%d)"), existingAvatar->HasAuthority() ? 1 : 0);
// There is already a pawn acting as the ASC's avatar, so we need to kick it out
// This can happen on clients if they're lagged: their new pawn is spawned + possessed before the dead one is removed
ensure(!existingAvatar->HasAuthority());
if (UOLSPawnExtensionComponent* OtherExtensionComponent = FindPawnExtensionComponent(existingAvatar))
{
OtherExtensionComponent->UninitializeAbilitySystem();
}
}
AbilitySystemComponent = asc;
AbilitySystemComponent->InitAbilityActorInfo(ownerActor, pawn);
if (ensure(PawnData))
{
asc->SetTagRelationshipMapping(PawnData->TagRelationshipMapping);
}
OnAbilitySystemInitialized.Broadcast();
}
void UOLSPawnExtensionComponent::UninitializeAbilitySystem()
{
if (!AbilitySystemComponent)
{
return;
}
// Uninitialize the ASC if we're still the avatar actor (otherwise another pawn already did it when they became the avatar actor)
if (AbilitySystemComponent->GetAvatarActor() == GetOwner())
{
FGameplayTagContainer abilityTypesToIgnore;
// @TOD:; Implement LyraGameplayTags::Ability_Behavior_SurvivesDeath;
// abilityTypesToIgnore.AddTag(LyraGameplayTags::Ability_Behavior_SurvivesDeath);
AbilitySystemComponent->CancelAbilities(nullptr, &abilityTypesToIgnore);
AbilitySystemComponent->ClearAbilityInput();
AbilitySystemComponent->RemoveAllGameplayCues();
if (AbilitySystemComponent->GetOwnerActor() != nullptr)
{
AbilitySystemComponent->SetAvatarActor(nullptr);
}
else
{
// If the ASC doesn't have a valid owner, we need to clear *all* actor info, not just the avatar pairing
AbilitySystemComponent->ClearActorInfo();
}
OnAbilitySystemUninitialized.Broadcast();
}
AbilitySystemComponent = nullptr;
}
void UOLSPawnExtensionComponent::HandleControllerChanged()
{
if (AbilitySystemComponent && (AbilitySystemComponent->GetAvatarActor() == GetPawnChecked<APawn>()))
{
ensure(AbilitySystemComponent->AbilityActorInfo->OwnerActor == AbilitySystemComponent->GetOwnerActor());
if (AbilitySystemComponent->GetOwnerActor() == nullptr)
{
UninitializeAbilitySystem();
}
else
{
AbilitySystemComponent->RefreshAbilityActorInfo();
}
}
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::HandlePlayerStateReplicated()
{
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::SetupPlayerInputComponent()
{
CheckDefaultInitialization();
}
void UOLSPawnExtensionComponent::OnAbilitySystemInitialized_RegisterAndCall(
FSimpleMulticastDelegate::FDelegate delegate)
{
if (!OnAbilitySystemInitialized.IsBoundToObject(delegate.GetUObject()))
{
OnAbilitySystemInitialized.Add(delegate);
}
if (AbilitySystemComponent)
{
delegate.Execute();
}
}
void UOLSPawnExtensionComponent::OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate delegate)
{
if (!OnAbilitySystemUninitialized.IsBoundToObject(delegate.GetUObject()))
{
OnAbilitySystemUninitialized.Add(delegate);
}
}
void UOLSPawnExtensionComponent::OnRep_PawnData()
{
CheckDefaultInitialization();
}

View File

@ -4,10 +4,13 @@
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "NativeGameplayTags.h"
#include "OLSAbilitySystemComponent.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOLSAbilitySystemComponent, Verbose, All);
OLS_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Gameplay_AbilityInputBlocked);
/**
* CVAR to control the "Play Montage" flow.
* Example: OLS.EnableDefaultPlayMontage true
@ -44,7 +47,6 @@ public:
float startTimeSeconds) override;
// -- End Ability System Component implementation
public:
/**
@ -152,13 +154,79 @@ protected:
*/
void TryActivateAbilitiesOnSpawn();
virtual void AbilitySpecInputPressed(FGameplayAbilitySpec& spec) override;
virtual void AbilitySpecInputReleased(FGameplayAbilitySpec& spec) override;
virtual void NotifyAbilityActivated(const FGameplayAbilitySpecHandle handle, UGameplayAbility* ability) override;
virtual void NotifyAbilityFailed(const FGameplayAbilitySpecHandle handle, UGameplayAbility* ability, const FGameplayTagContainer& failureReason) override;
virtual void NotifyAbilityEnded(FGameplayAbilitySpecHandle handle, UGameplayAbility* Ability, bool bWasCancelled) override;
virtual void ApplyAbilityBlockAndCancelTags(const FGameplayTagContainer& abilityTags, UGameplayAbility* requestingAbility, bool shouldEnableBlockTags, const FGameplayTagContainer& blockTags, bool shouldExecuteCancelTags, const FGameplayTagContainer& cancelTags) override;
virtual void HandleChangeAbilityCanBeCanceled(const FGameplayTagContainer& abilityTags, UGameplayAbility* requestingAbility, bool canBeCanceled) override;
/** Notify client that an ability failed to activate */
UFUNCTION(Client, Unreliable)
void ClientNotifyAbilityFailed(const UGameplayAbility* ability, const FGameplayTagContainer& failureReason);
void HandleAbilityFailed(const UGameplayAbility* ability, const FGameplayTagContainer& failureReason);
/**
* Conveniently separates the code that sets the animation to replicate, so it can be further modified.
*/
virtual void SetReplicatedMontageInfo(FGameplayAbilityRepAnimMontage& mutableRepAnimMontageInfo, UAnimMontage* newMontageToPlay, const FName& startSectionName);
public:
typedef TFunctionRef<bool(const class UOLSGameplayAbility* ability, FGameplayAbilitySpecHandle handle)> TShouldCancelAbilityFunc;
void CancelAbilitiesByFunc(TShouldCancelAbilityFunc shouldCancelFunc, bool shouldReplicateCancelAbility);
void CancelInputActivatedAbilities(bool shouldReplicateCancelAbility);
void AbilityInputTagPressed(const FGameplayTag& inputTag);
void AbilityInputTagReleased(const FGameplayTag& inputTag);
void ProcessAbilityInput(float deltaTime, bool shouldGamePaused);
void ClearAbilityInput();
// @TODO: Implement UOLSGameplayAbility.
// bool IsActivationGroupBlocked(ELyraAbilityActivationGroup Group) const;
// void AddAbilityToActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
// void RemoveAbilityFromActivationGroup(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* LyraAbility);
// void CancelActivationGroupAbilities(ELyraAbilityActivationGroup Group, ULyraGameplayAbility* IgnoreLyraAbility, bool bReplicateCancelAbility);
// Uses a gameplay effect to add the specified dynamic granted tag.
void AddDynamicTagGameplayEffect(const FGameplayTag& tag);
// Removes all active instances of the gameplay effect that was used to add the specified dynamic granted tag.
void RemoveDynamicTagGameplayEffect(const FGameplayTag& Tag);
/** Gets the ability target data associated with the given ability handle and activation info */
void GetAbilityTargetData(const FGameplayAbilitySpecHandle abilityHandle, FGameplayAbilityActivationInfo activationInfo, FGameplayAbilityTargetDataHandle& outTargetDataHandle);
/** Sets the current tag relationship mapping, if null it will clear it out */
void SetTagRelationshipMapping(class UOLSAbilityTagRelationshipMappingDataAsset* newMapping);
/** Looks at ability tags and gathers additional required and blocking tags */
void GetAdditionalActivationTagRequirements(const FGameplayTagContainer& abilityTags, FGameplayTagContainer& outActivationRequired, FGameplayTagContainer& outActivationBlocked) const;
private:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "OLS Ability System", DisplayName = "Enable Ability Batch RPCs", meta = (AllowPrivateAccess = true))
uint8 bShouldEnableBatchRPC : 1 = true;
protected:
// If set, this table is used to look up tag relationships for activate and cancel
UPROPERTY()
TObjectPtr<class UOLSAbilityTagRelationshipMappingDataAsset> TagRelationshipMapping = nullptr;
// Handles to abilities that had their input pressed this frame.
TArray<FGameplayAbilitySpecHandle> InputPressedSpecHandles;
// Handles to abilities that had their input released this frame.
TArray<FGameplayAbilitySpecHandle> InputReleasedSpecHandles;
// Handles to abilities that have their input held.
TArray<FGameplayAbilitySpecHandle> InputHeldSpecHandles;
};

View File

@ -0,0 +1,99 @@
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
#pragma once
#include "CoreMinimal.h"
#include "Components/GameFrameworkInitStateInterface.h"
#include "Components/PawnComponent.h"
#include "OLSPawnExtensionComponent.generated.h"
DECLARE_LOG_CATEGORY_EXTERN(LogOLSPawnExtensionComponent, Verbose, All);
namespace EEndPlayReason { enum Type : int; }
/**
* Component that adds functionality to all Pawn classes so it can be used for characters/vehicles/etc.
* This coordinates the initialization of other components.
*/
UCLASS()
class OLS_API UOLSPawnExtensionComponent : public UPawnComponent, public IGameFrameworkInitStateInterface
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UOLSPawnExtensionComponent(const FObjectInitializer& objectInitializer);
/** The name of this overall feature, this one depends on the other named component features */
static const FName NAME_ActorFeatureName;
//~ Begin IGameFrameworkInitStateInterface interface
virtual FName GetFeatureName() const override;
virtual bool CanChangeInitState(UGameFrameworkComponentManager* manager, FGameplayTag currentState, FGameplayTag desiredState) const override;
virtual void HandleChangeInitState(UGameFrameworkComponentManager* manager, FGameplayTag currentState, FGameplayTag desiredState) override;
virtual void OnActorInitStateChanged(const FActorInitStateChangedParams& Params) override;
virtual void CheckDefaultInitialization() override;
//~ End IGameFrameworkInitStateInterface interface
/** Returns the pawn extension component if one exists on the specified actor. */
UFUNCTION(BlueprintPure, Category = "OLS|Pawn")
static class UOLSPawnExtensionComponent* FindPawnExtensionComponent(const AActor* actor);
/** Gets the current ability system component, which may be owned by a different actor */
UFUNCTION(BlueprintPure, Category = "OLS|Pawn")
class UOLSAbilitySystemComponent* GetOLSAbilitySystemComponent() const ;
public:
/** Gets the pawn data, which is used to specify pawn properties in data */
template <class T>
const T* GetPawnData() const { return Cast<T>(PawnData); }
/** Sets the current pawn data */
void SetPawnData(const class UOLSPawnDataAsset* pawnData);
/** Should be called by the owning pawn to become the avatar of the ability system. */
void InitializeAbilitySystem(class UOLSAbilitySystemComponent* asc, AActor* ownerActor);
/** Should be called by the owning pawn to remove itself as the avatar of the ability system. */
void UninitializeAbilitySystem();
/** Should be called by the owning pawn when the pawn's controller changes. */
void HandleControllerChanged();
/** Should be called by the owning pawn when the player state has been replicated. */
void HandlePlayerStateReplicated();
/** Should be called by the owning pawn when the input component is setup. */
void SetupPlayerInputComponent();
/** Register with the OnAbilitySystemInitialized delegate and broadcast if our pawn has been registered with the ability system component */
void OnAbilitySystemInitialized_RegisterAndCall(FSimpleMulticastDelegate::FDelegate delegate);
/** Register with the OnAbilitySystemUninitialized delegate fired when our pawn is removed as the ability system's avatar actor */
void OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate delegate);
protected:
UFUNCTION()
void OnRep_PawnData();
protected:
/** Delegate fired when our pawn becomes the ability system's avatar actor */
FSimpleMulticastDelegate OnAbilitySystemInitialized;
/** Delegate fired when our pawn is removed as the ability system's avatar actor */
FSimpleMulticastDelegate OnAbilitySystemUninitialized;
protected:
/** Pawn data used to create the pawn. Specified from a spawn function or on a placed instance. */
UPROPERTY(EditInstanceOnly, ReplicatedUsing = OnRep_PawnData, Category = "Lyra|Pawn")
TObjectPtr<const class UOLSPawnDataAsset> PawnData = nullptr;
/** Pointer to the ability system component that is cached for convenience. */
UPROPERTY(Transient)
TObjectPtr<class UOLSAbilitySystemComponent > AbilitySystemComponent = nullptr;
};