2022-05-23 18:41:30 +00:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "GameFeatureAction_AddAbilities.h"
# include "GameFeaturesSubsystem.h"
# include "Components/GameFrameworkComponentManager.h"
# include "GameFeaturesSubsystemSettings.h"
# include "Engine/AssetManager.h"
# include "AbilitySystemComponent.h"
# include "AttributeSet.h"
# include "AbilitySystem/LyraAbilitySystemComponent.h"
# include "Player/LyraPlayerState.h" //@TODO: For the fname
# define LOCTEXT_NAMESPACE "GameFeatures"
//////////////////////////////////////////////////////////////////////
// UGameFeatureAction_AddAbilities
void UGameFeatureAction_AddAbilities : : OnGameFeatureActivating ( FGameFeatureActivatingContext & Context )
{
FPerContextData & ActiveData = ContextData . FindOrAdd ( Context ) ;
if ( ! ensureAlways ( ActiveData . ActiveExtensions . IsEmpty ( ) ) | |
! ensureAlways ( ActiveData . ComponentRequests . IsEmpty ( ) ) )
{
Reset ( ActiveData ) ;
}
Super : : OnGameFeatureActivating ( Context ) ;
}
void UGameFeatureAction_AddAbilities : : OnGameFeatureDeactivating ( FGameFeatureDeactivatingContext & Context )
{
Super : : OnGameFeatureDeactivating ( Context ) ;
FPerContextData * ActiveData = ContextData . Find ( Context ) ;
if ( ensure ( ActiveData ) )
{
Reset ( * ActiveData ) ;
}
}
# if WITH_EDITOR
EDataValidationResult UGameFeatureAction_AddAbilities : : IsDataValid ( TArray < FText > & ValidationErrors )
{
EDataValidationResult Result = CombineDataValidationResults ( Super : : IsDataValid ( ValidationErrors ) , EDataValidationResult : : Valid ) ;
int32 EntryIndex = 0 ;
for ( const FGameFeatureAbilitiesEntry & Entry : AbilitiesList )
{
if ( Entry . ActorClass . IsNull ( ) )
{
Result = EDataValidationResult : : Invalid ;
ValidationErrors . Add ( FText : : Format ( LOCTEXT ( " EntryHasNullActor " , " Null ActorClass at index {0} in AbilitiesList " ) , FText : : AsNumber ( EntryIndex ) ) ) ;
}
if ( Entry . GrantedAbilities . IsEmpty ( ) & & Entry . GrantedAttributes . IsEmpty ( ) & & Entry . GrantedAbilitySets . IsEmpty ( ) )
{
Result = EDataValidationResult : : Invalid ;
ValidationErrors . Add ( FText : : Format ( LOCTEXT ( " EntryHasNoAddOns " , " Index {0} in AbilitiesList will do nothing (no granted abilities, attributes, or ability sets) " ) , FText : : AsNumber ( EntryIndex ) ) ) ;
}
int32 AbilityIndex = 0 ;
for ( const FLyraAbilityGrant & Ability : Entry . GrantedAbilities )
{
if ( Ability . AbilityType . IsNull ( ) )
{
Result = EDataValidationResult : : Invalid ;
ValidationErrors . Add ( FText : : Format ( LOCTEXT ( " EntryHasNullAbility " , " Null AbilityType at index {0} in AbilitiesList[{1}].GrantedAbilities " ) , FText : : AsNumber ( AbilityIndex ) , FText : : AsNumber ( EntryIndex ) ) ) ;
}
+ + AbilityIndex ;
}
int32 AttributesIndex = 0 ;
for ( const FLyraAttributeSetGrant & Attributes : Entry . GrantedAttributes )
{
if ( Attributes . AttributeSetType . IsNull ( ) )
{
Result = EDataValidationResult : : Invalid ;
ValidationErrors . Add ( FText : : Format ( LOCTEXT ( " EntryHasNullAttributeSet " , " Null AttributeSetType at index {0} in AbilitiesList[{1}].GrantedAttributes " ) , FText : : AsNumber ( AttributesIndex ) , FText : : AsNumber ( EntryIndex ) ) ) ;
}
+ + AttributesIndex ;
}
int32 AttributeSetIndex = 0 ;
for ( const TSoftObjectPtr < const ULyraAbilitySet > & AttributeSetPtr : Entry . GrantedAbilitySets )
{
if ( AttributeSetPtr . IsNull ( ) )
{
Result = EDataValidationResult : : Invalid ;
ValidationErrors . Add ( FText : : Format ( LOCTEXT ( " EntryHasNullAttributeSet " , " Null AbilitySet at index {0} in AbilitiesList[{1}].GrantedAbilitySets " ) , FText : : AsNumber ( AttributeSetIndex ) , FText : : AsNumber ( EntryIndex ) ) ) ;
}
+ + AttributeSetIndex ;
}
+ + EntryIndex ;
}
return Result ;
return EDataValidationResult : : NotValidated ;
}
# endif
void UGameFeatureAction_AddAbilities : : AddToWorld ( const FWorldContext & WorldContext , const FGameFeatureStateChangeContext & ChangeContext )
{
UWorld * World = WorldContext . World ( ) ;
UGameInstance * GameInstance = WorldContext . OwningGameInstance ;
FPerContextData & ActiveData = ContextData . FindOrAdd ( ChangeContext ) ;
if ( ( GameInstance ! = nullptr ) & & ( World ! = nullptr ) & & World - > IsGameWorld ( ) )
{
if ( UGameFrameworkComponentManager * ComponentMan = UGameInstance : : GetSubsystem < UGameFrameworkComponentManager > ( GameInstance ) )
{
int32 EntryIndex = 0 ;
for ( const FGameFeatureAbilitiesEntry & Entry : AbilitiesList )
{
if ( ! Entry . ActorClass . IsNull ( ) )
{
UGameFrameworkComponentManager : : FExtensionHandlerDelegate AddAbilitiesDelegate = UGameFrameworkComponentManager : : FExtensionHandlerDelegate : : CreateUObject (
this , & UGameFeatureAction_AddAbilities : : HandleActorExtension , EntryIndex , ChangeContext ) ;
TSharedPtr < FComponentRequestHandle > ExtensionRequestHandle = ComponentMan - > AddExtensionHandler ( Entry . ActorClass , AddAbilitiesDelegate ) ;
ActiveData . ComponentRequests . Add ( ExtensionRequestHandle ) ;
EntryIndex + + ;
}
}
}
}
}
void UGameFeatureAction_AddAbilities : : Reset ( FPerContextData & ActiveData )
{
while ( ! ActiveData . ActiveExtensions . IsEmpty ( ) )
{
auto ExtensionIt = ActiveData . ActiveExtensions . CreateIterator ( ) ;
RemoveActorAbilities ( ExtensionIt - > Key , ActiveData ) ;
}
ActiveData . ComponentRequests . Empty ( ) ;
}
void UGameFeatureAction_AddAbilities : : HandleActorExtension ( AActor * Actor , FName EventName , int32 EntryIndex , FGameFeatureStateChangeContext ChangeContext )
{
FPerContextData * ActiveData = ContextData . Find ( ChangeContext ) ;
if ( AbilitiesList . IsValidIndex ( EntryIndex ) & & ActiveData )
{
const FGameFeatureAbilitiesEntry & Entry = AbilitiesList [ EntryIndex ] ;
if ( ( EventName = = UGameFrameworkComponentManager : : NAME_ExtensionRemoved ) | | ( EventName = = UGameFrameworkComponentManager : : NAME_ReceiverRemoved ) )
{
RemoveActorAbilities ( Actor , * ActiveData ) ;
}
else if ( ( EventName = = UGameFrameworkComponentManager : : NAME_ExtensionAdded ) | | ( EventName = = ALyraPlayerState : : NAME_LyraAbilityReady ) )
{
AddActorAbilities ( Actor , Entry , * ActiveData ) ;
}
}
}
void UGameFeatureAction_AddAbilities : : AddActorAbilities ( AActor * Actor , const FGameFeatureAbilitiesEntry & AbilitiesEntry , FPerContextData & ActiveData )
{
check ( Actor ) ;
if ( ! Actor - > HasAuthority ( ) )
{
return ;
}
// early out if Actor already has ability extensions applied
if ( ActiveData . ActiveExtensions . Find ( Actor ) ! = nullptr )
{
return ;
}
if ( UAbilitySystemComponent * AbilitySystemComponent = FindOrAddComponentForActor < UAbilitySystemComponent > ( Actor , AbilitiesEntry , ActiveData ) )
{
FActorExtensions AddedExtensions ;
AddedExtensions . Abilities . Reserve ( AbilitiesEntry . GrantedAbilities . Num ( ) ) ;
AddedExtensions . Attributes . Reserve ( AbilitiesEntry . GrantedAttributes . Num ( ) ) ;
AddedExtensions . AbilitySetHandles . Reserve ( AbilitiesEntry . GrantedAbilitySets . Num ( ) ) ;
for ( const FLyraAbilityGrant & Ability : AbilitiesEntry . GrantedAbilities )
{
if ( ! Ability . AbilityType . IsNull ( ) )
{
FGameplayAbilitySpec NewAbilitySpec ( Ability . AbilityType . LoadSynchronous ( ) ) ;
FGameplayAbilitySpecHandle AbilityHandle = AbilitySystemComponent - > GiveAbility ( NewAbilitySpec ) ;
AddedExtensions . Abilities . Add ( AbilityHandle ) ;
}
}
for ( const FLyraAttributeSetGrant & Attributes : AbilitiesEntry . GrantedAttributes )
{
if ( ! Attributes . AttributeSetType . IsNull ( ) )
{
TSubclassOf < UAttributeSet > SetType = Attributes . AttributeSetType . LoadSynchronous ( ) ;
if ( SetType )
{
UAttributeSet * NewSet = NewObject < UAttributeSet > ( AbilitySystemComponent - > GetOwner ( ) , SetType ) ;
if ( ! Attributes . InitializationData . IsNull ( ) )
{
UDataTable * InitData = Attributes . InitializationData . LoadSynchronous ( ) ;
if ( InitData )
{
NewSet - > InitFromMetaDataTable ( InitData ) ;
}
}
AddedExtensions . Attributes . Add ( NewSet ) ;
AbilitySystemComponent - > AddAttributeSetSubobject ( NewSet ) ;
}
}
}
ULyraAbilitySystemComponent * LyraASC = CastChecked < ULyraAbilitySystemComponent > ( AbilitySystemComponent ) ;
for ( const TSoftObjectPtr < const ULyraAbilitySet > & SetPtr : AbilitiesEntry . GrantedAbilitySets )
{
if ( const ULyraAbilitySet * Set = SetPtr . Get ( ) )
{
Set - > GiveToAbilitySystem ( LyraASC , & AddedExtensions . AbilitySetHandles . AddDefaulted_GetRef ( ) ) ;
}
}
ActiveData . ActiveExtensions . Add ( Actor , AddedExtensions ) ;
}
else
{
UE_LOG ( LogGameFeatures , Error , TEXT ( " Failed to find/add an ability component to '%s'. Abilities will not be granted. " ) , * Actor - > GetPathName ( ) ) ;
}
}
void UGameFeatureAction_AddAbilities : : RemoveActorAbilities ( AActor * Actor , FPerContextData & ActiveData )
{
if ( FActorExtensions * ActorExtensions = ActiveData . ActiveExtensions . Find ( Actor ) )
{
if ( UAbilitySystemComponent * AbilitySystemComponent = Actor - > FindComponentByClass < UAbilitySystemComponent > ( ) )
{
for ( UAttributeSet * AttribSetInstance : ActorExtensions - > Attributes )
{
2022-09-13 07:18:28 +00:00
AbilitySystemComponent - > RemoveSpawnedAttribute ( AttribSetInstance ) ;
2022-05-23 18:41:30 +00:00
}
for ( FGameplayAbilitySpecHandle AbilityHandle : ActorExtensions - > Abilities )
{
AbilitySystemComponent - > SetRemoveAbilityOnEnd ( AbilityHandle ) ;
}
ULyraAbilitySystemComponent * LyraASC = CastChecked < ULyraAbilitySystemComponent > ( AbilitySystemComponent ) ;
for ( FLyraAbilitySet_GrantedHandles & SetHandle : ActorExtensions - > AbilitySetHandles )
{
SetHandle . TakeFromAbilitySystem ( LyraASC ) ;
}
}
ActiveData . ActiveExtensions . Remove ( Actor ) ;
}
}
UActorComponent * UGameFeatureAction_AddAbilities : : FindOrAddComponentForActor ( UClass * ComponentType , AActor * Actor , const FGameFeatureAbilitiesEntry & AbilitiesEntry , FPerContextData & ActiveData )
{
UActorComponent * Component = Actor - > FindComponentByClass ( ComponentType ) ;
bool bMakeComponentRequest = ( Component = = nullptr ) ;
if ( Component )
{
// Check to see if this component was created from a different `UGameFrameworkComponentManager` request.
// `Native` is what `CreationMethod` defaults to for dynamically added components.
if ( Component - > CreationMethod = = EComponentCreationMethod : : Native )
{
// Attempt to tell the difference between a true native component and one created by the GameFrameworkComponent system.
// If it is from the UGameFrameworkComponentManager, then we need to make another request (requests are ref counted).
UObject * ComponentArchetype = Component - > GetArchetype ( ) ;
bMakeComponentRequest = ComponentArchetype - > HasAnyFlags ( RF_ClassDefaultObject ) ;
}
}
if ( bMakeComponentRequest )
{
UWorld * World = Actor - > GetWorld ( ) ;
UGameInstance * GameInstance = World - > GetGameInstance ( ) ;
if ( UGameFrameworkComponentManager * ComponentMan = UGameInstance : : GetSubsystem < UGameFrameworkComponentManager > ( GameInstance ) )
{
TSharedPtr < FComponentRequestHandle > RequestHandle = ComponentMan - > AddComponentRequest ( AbilitiesEntry . ActorClass , ComponentType ) ;
ActiveData . ComponentRequests . Add ( RequestHandle ) ;
}
if ( ! Component )
{
Component = Actor - > FindComponentByClass ( ComponentType ) ;
ensureAlways ( Component ) ;
}
}
return Component ;
}
# undef LOCTEXT_NAMESPACE