// Copyright Epic Games, Inc. All Rights Reserved. #include "GameplayEffectExecutionDefinitionDetails.h" #include "IDetailChildrenBuilder.h" #include "DetailWidgetRow.h" #include "IDetailPropertyRow.h" #include "GameplayEffectTypes.h" #include "GameplayEffect.h" #include "GameplayEffectExecutionCalculation.h" #include "IPropertyUtilities.h" #define LOCTEXT_NAMESPACE "GameplayEffectExecutionDefinitionDetailsCustomization" TSharedRef FGameplayEffectExecutionDefinitionDetails::MakeInstance() { return MakeShareable(new FGameplayEffectExecutionDefinitionDetails()); } void FGameplayEffectExecutionDefinitionDetails::CustomizeHeader(TSharedRef StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { HeaderRow .NameContent() [ StructPropertyHandle->CreatePropertyNameWidget() ]; } void FGameplayEffectExecutionDefinitionDetails::CustomizeChildren(TSharedRef StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { bShowCalculationModifiers = false; bShowPassedInTags = false; // @todo: For now, only allow single editing if (StructPropertyHandle->GetNumOuterObjects() == 1) { CalculationClassPropHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGameplayEffectExecutionDefinition, CalculationClass)); TSharedPtr ConditionalEffectsPropHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGameplayEffectExecutionDefinition, ConditionalGameplayEffects)); TSharedPtr CalcModPropHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGameplayEffectExecutionDefinition, CalculationModifiers)); TSharedPtr PassedInTagsHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGameplayEffectExecutionDefinition, PassedInTags)); CalculationModifiersArrayPropHandle = CalcModPropHandle.IsValid() ? CalcModPropHandle->AsArray() : nullptr; if (CalculationClassPropHandle.IsValid()) { CalculationClassPropHandle->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FGameplayEffectExecutionDefinitionDetails::OnCalculationClassChanged)); StructBuilder.AddProperty(CalculationClassPropHandle.ToSharedRef()); StructCustomizationUtils.GetPropertyUtilities()->EnqueueDeferredAction(FSimpleDelegate::CreateSP(this, &FGameplayEffectExecutionDefinitionDetails::UpdateCalculationModifiers)); } if (CalculationModifiersArrayPropHandle.IsValid()) { IDetailPropertyRow& PropRow = StructBuilder.AddProperty(CalcModPropHandle.ToSharedRef()); PropRow.Visibility(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &FGameplayEffectExecutionDefinitionDetails::GetCalculationModifierVisibility))); } if (ConditionalEffectsPropHandle.IsValid()) { StructBuilder.AddProperty(ConditionalEffectsPropHandle.ToSharedRef()); } if (PassedInTagsHandle.IsValid()) { IDetailPropertyRow& PropRow = StructBuilder.AddProperty(PassedInTagsHandle.ToSharedRef()); PropRow.Visibility(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &FGameplayEffectExecutionDefinitionDetails::GetPassedInTagsVisibility))); } } } void FGameplayEffectExecutionDefinitionDetails::OnCalculationClassChanged() { UpdateCalculationModifiers(); } void FGameplayEffectExecutionDefinitionDetails::UpdateCalculationModifiers() { TArray ValidCaptureDefinitions; FGameplayTagContainer ValidTransientAggregatorIdentifiers; // Try to extract the collection of valid capture definitions & transient aggregator identifiers from the execution class CDO, if possible if (CalculationClassPropHandle.IsValid()) { UObject* ObjValue = nullptr; CalculationClassPropHandle->GetValue(ObjValue); UClass* ClassObj = Cast(ObjValue); if (ClassObj) { const UGameplayEffectExecutionCalculation* ExecutionCDO = ClassObj->GetDefaultObject(); if (ExecutionCDO) { ExecutionCDO->GetValidScopedModifierAttributeCaptureDefinitions(ValidCaptureDefinitions); ValidTransientAggregatorIdentifiers = ExecutionCDO->GetValidTransientAggregatorIdentifiers(); // Grab this while we are at it so we know if we should show the 'Passed In Tags' property bShowPassedInTags = ExecutionCDO->DoesRequirePassedInTags(); } } } // Want to hide the calculation modifiers if there are no valid definitions or transient aggregator IDs bShowCalculationModifiers = (ValidCaptureDefinitions.Num() > 0) || (ValidTransientAggregatorIdentifiers.Num() > 0); // Need to prune out any modifiers that aren't supported by the execution class if (CalculationModifiersArrayPropHandle.IsValid()) { uint32 NumChildren = 0; CalculationModifiersArrayPropHandle->GetNumElements(NumChildren); // If there aren't any valid definitions or transient aggregator IDs, just dump the whole array if (ValidCaptureDefinitions.Num() == 0 && ValidTransientAggregatorIdentifiers.Num() == 0 && NumChildren > 0) { CalculationModifiersArrayPropHandle->EmptyArray(); } // There are some valid definitions/IDs, so verify any existing ones to make sure they are in the valid array else { for (int32 ChildIdx = NumChildren - 1; ChildIdx >= 0; --ChildIdx) { TSharedRef ChildPropHandle = CalculationModifiersArrayPropHandle->GetElement(ChildIdx); TArray RawScopedModInfoStructs; ChildPropHandle->AccessRawData(RawScopedModInfoStructs); // @todo: For now, only allow single editing ensure(RawScopedModInfoStructs.Num() == 1); const FGameplayEffectExecutionScopedModifierInfo& CurModInfo = *reinterpret_cast(RawScopedModInfoStructs[0]); const bool bInvalidAttributeAggregator = (CurModInfo.AggregatorType == EGameplayEffectScopedModifierAggregatorType::CapturedAttributeBacked && !ValidCaptureDefinitions.Contains(CurModInfo.CapturedAttribute)); const bool bInvalidTransientAggregator = (CurModInfo.AggregatorType == EGameplayEffectScopedModifierAggregatorType::Transient && !ValidTransientAggregatorIdentifiers.HasTagExact(CurModInfo.TransientAggregatorIdentifier)); if (bInvalidAttributeAggregator || bInvalidTransientAggregator) { CalculationModifiersArrayPropHandle->DeleteItem(ChildIdx); } } } } } EVisibility FGameplayEffectExecutionDefinitionDetails::GetCalculationModifierVisibility() const { return (bShowCalculationModifiers ? EVisibility::Visible : EVisibility::Collapsed); } EVisibility FGameplayEffectExecutionDefinitionDetails::GetPassedInTagsVisibility() const { return (bShowPassedInTags ? EVisibility::Visible : EVisibility::Collapsed); } #undef LOCTEXT_NAMESPACE