RealtimeStyleTransferRuntime/Source/LyraGame/GameFeatures/GameFeatureAction_AddAbilit...

296 lines
11 KiB
C++

// 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)
{
AbilitySystemComponent->RemoveSpawnedAttribute(AttribSetInstance);
}
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