第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
using UnrealBuildTool;
public class GenericGameplayAbilitiesEditor : ModuleRules
{
public GenericGameplayAbilitiesEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new[]
{
"Core", "UnrealEd"
}
);
PrivateDependencyModuleNames.AddRange(
new[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"GameplayAbilities",
"GameplayAbilitiesEditor",
"PropertyEditor",
"GameplayTasks",
"GameplayTasksEditor",
"GameplayTags",
"ToolMenus",
"AssetDefinition",
"BlueprintGraph",
"KismetCompiler",
"GameplayTagsEditor", "GenericGameplayAbilities"
}
);
}
}

View File

@@ -0,0 +1,177 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GGA_AttributeGroupNameCustomization.h"
#include "DetailWidgetRow.h"
#include "GGA_AbilitySystemGlobals.h"
#include "GGA_AbilitySystemStructLibrary.h"
#include "PropertyCustomizationHelpers.h"
TSharedRef<IPropertyTypeCustomization> FGGA_AttributeGroupNameCustomization::MakeInstance()
{
return MakeShareable(new FGGA_AttributeGroupNameCustomization());
}
void FGGA_AttributeGroupNameCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
const UGGA_AbilitySystemGlobals* Globals = Cast<UGGA_AbilitySystemGlobals>(IGameplayAbilitiesModule::Get().GetAbilitySystemGlobals());
if (Globals == nullptr)
{
return;
}
FNameMap.Reset();
for (const UCurveTable* CurTable : Globals->GetAttributeDefaultsTables())
{
if (!IsValid(CurTable))
{
continue;
}
for (const TPair<FName, FRealCurve*>& CurveRow : CurTable->GetRowMap())
{
FString RowName = CurveRow.Key.ToString();
TArray<FString> RowParts; //[0]GroupName [1]SetName [2]AttribueName
RowName.ParseIntoArray(RowParts, TEXT("."));
if (RowParts.Num() != 3)
{
continue;
}
TArray<FString> GroupParts; //[0]MainName [1]SubName
RowName.ParseIntoArray(GroupParts, TEXT("->"));
if (GroupParts.Num() != 2)
{
//Add class name as group.
FNameMap.FindOrAdd(FName(*RowParts[0]));
}
else if (GroupParts.Num() == 2)
{
TArray<FName>& Rows = FNameMap.FindOrAdd(FName(*GroupParts[0]));
Rows.AddUnique(FName(*GroupParts[1]));
}
}
}
PropertyUtilities = StructCustomizationUtils.GetPropertyUtilities();
MainNamePropertyHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGGA_AttributeGroupName, MainName));
SubNamePropertyHandle = StructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGGA_AttributeGroupName, SubName));
if (MainNamePropertyHandle.IsValid() && SubNamePropertyHandle.IsValid())
{
HeaderRow
.NameContent()
[
StructPropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
.MinDesiredWidth(600)
.MaxDesiredWidth(4096)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.HAlign(HAlign_Fill)
.Padding(1.f, 0.f, 2.f, 0.f)
[
PropertyCustomizationHelpers::MakePropertyComboBox(MainNamePropertyHandle,
FOnGetPropertyComboBoxStrings::CreateStatic(&FGGA_AttributeGroupNameCustomization::GeneratePrimaryComboboxStrings, true,
false, &FNameMap),
FOnGetPropertyComboBoxValue::CreateSP(this, &FGGA_AttributeGroupNameCustomization::GenerateMainString),
FOnPropertyComboBoxValueSelected::CreateSP(this, &FGGA_AttributeGroupNameCustomization::OnMainValueSelected))
]
+ SVerticalBox::Slot()
.HAlign(HAlign_Fill)
.Padding(2.f, 0.f, 2.f, 0.f)
[
PropertyCustomizationHelpers::MakePropertyComboBox(MainNamePropertyHandle,
FOnGetPropertyComboBoxStrings::CreateStatic(&FGGA_AttributeGroupNameCustomization::GenerateSubComboboxStrings, true,
false, &FNameMap,
MainNamePropertyHandle),
FOnGetPropertyComboBoxValue::CreateSP(this, &FGGA_AttributeGroupNameCustomization::GenerateSubString),
FOnPropertyComboBoxValueSelected::CreateSP(this, &FGGA_AttributeGroupNameCustomization::OnSubValueSelected))
]
]
];
}
}
void FGGA_AttributeGroupNameCustomization::GeneratePrimaryComboboxStrings(TArray<TSharedPtr<FString>>& OutComboBoxStrings, TArray<TSharedPtr<SToolTip>>& OutToolTips, TArray<bool>& OutRestrictedItems,
bool bAllowClear, bool bAllowAll,
TMap<FName, TArray<FName>>* InItems)
{
for (auto Iter = InItems->CreateConstIterator(); Iter; ++Iter)
{
OutComboBoxStrings.Add(MakeShared<FString>(Iter.Key().ToString()));
}
}
void FGGA_AttributeGroupNameCustomization::GenerateSubComboboxStrings(TArray<TSharedPtr<FString>>& OutComboBoxStrings, TArray<TSharedPtr<SToolTip>>& OutToolTips,
TArray<bool>& OutRestrictedItems, bool bAllowClear, bool bAllowAll,
TMap<FName, TArray<FName>>* InItems, TSharedPtr<IPropertyHandle> PrimaryKey)
{
void* TagDataPtr = nullptr;
PrimaryKey->GetValueData(TagDataPtr);
const FName* TagPtr = static_cast<FName*>(TagDataPtr);
if (!TagPtr || TagPtr->IsNone())
{
OutComboBoxStrings.Add(MakeShared<FString>("Invalid"));
}
else
{
const auto Arr = InItems->Find(*TagPtr);
if (Arr)
{
for (auto& Name : *Arr)
{
OutComboBoxStrings.Add(MakeShared<FString>(Name.ToString()));
}
}
}
}
FString FGGA_AttributeGroupNameCustomization::GenerateSubString()
{
void* TagDataPtr = nullptr;
SubNamePropertyHandle->GetValueData(TagDataPtr);
const FName* TagPtr = static_cast<FName*>(TagDataPtr);
FString TagString = TagPtr ? TagPtr->ToString() : "Invalid";
return TagString;
}
void FGGA_AttributeGroupNameCustomization::OnSubValueSelected(const FString& String)
{
SubNamePropertyHandle->SetValue(FName(*String));
}
FString FGGA_AttributeGroupNameCustomization::GenerateMainString()
{
void* TagDataPtr = nullptr;
MainNamePropertyHandle->GetValueData(TagDataPtr);
const FName* TagPtr = static_cast<FName*>(TagDataPtr);
FString TagString = TagPtr ? TagPtr->ToString() : "Invalid";
return TagString;
}
void FGGA_AttributeGroupNameCustomization::OnMainValueSelected(const FString& String)
{
MainNamePropertyHandle->SetValue(FName(*String));
SubNamePropertyHandle->ResetToDefault();
}
void FGGA_AttributeGroupNameCustomization::CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder,
IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
}

View File

@@ -0,0 +1,52 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GGA_K2Node_ContextPayload.h"
#include "EdGraphSchema_K2.h"
#include "BlueprintNodeSpawner.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "Utilities/GGA_GameplayEffectFunctionLibrary.h"
void UGGA_K2Node_ContextPayload::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
Super::GetMenuActions(ActionRegistrar);
UClass* Action = GetClass();
if (ActionRegistrar.IsOpenForRegistration(Action))
{
auto CustomizeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, const FName FunctionName)
{
UGGA_K2Node_ContextPayload* Node = CastChecked<UGGA_K2Node_ContextPayload>(NewNode);
UFunction* Function = UGGA_GameplayEffectFunctionLibrary::StaticClass()->FindFunctionByName(FunctionName);
check(Function);
Node->SetFromFunction(Function);
};
UBlueprintNodeSpawner* SetNodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(SetNodeSpawner != nullptr);
SetNodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(
CustomizeLambda, GET_FUNCTION_NAME_CHECKED(UGGA_GameplayEffectFunctionLibrary, SetContextPayload));
ActionRegistrar.AddBlueprintAction(Action, SetNodeSpawner);
// UBlueprintNodeSpawner* GetNodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
// check(GetNodeSpawner != nullptr);
// GetNodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(
// CustomizeLambda, GET_FUNCTION_NAME_CHECKED(UGGA_GameplayEffectFunctionLibrary, GetContextPayload));
// ActionRegistrar.AddBlueprintAction(Action, GetNodeSpawner);
}
}
bool UGGA_K2Node_ContextPayload::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{
const UEdGraphPin* ValuePin = FindPinChecked(FName(TEXT("Value")));
if (MyPin == ValuePin && MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard)
{
if (OtherPin->PinType.PinCategory != UEdGraphSchema_K2::PC_Struct)
{
OutReason = TEXT("Value must be a struct.");
return true;
}
}
return false;
}

View File

@@ -0,0 +1,309 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GGA_K2Node_EffectContextPayload.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "EdGraphSchema_K2.h"
#include "K2Node_AssignmentStatement.h"
#include "K2Node_CallFunction.h"
#include "K2Node_TemporaryVariable.h"
#include "KismetCompiler.h"
#include "Kismet/BlueprintInstancedStructLibrary.h"
#include "Utilities/GGA_GameplayEffectFunctionLibrary.h"
#define LOCTEXT_NAMESPACE "K2Node"
///////////////////////////////////////////////////////////////////////////
// Node Change events interface implementations
///////////////////////////////////////////////////////////////////////////
void UGGA_K2Node_EffectContextPayload::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
AllocateDefaultPins();
RestoreSplitPins(OldPins);
}
void UGGA_K2Node_EffectContextPayload::PostPlacedNewNode()
{
Super::PostPlacedNewNode();
RefreshOutputType();
}
void UGGA_K2Node_EffectContextPayload::PinConnectionListChanged(UEdGraphPin* Pin)
{
Super::PinConnectionListChanged(Pin);
if (Pin && Pin == GetPayloadTypePin())
{
RefreshOutputType();
}
}
void UGGA_K2Node_EffectContextPayload::PinDefaultValueChanged(UEdGraphPin* Pin)
{
if (Pin == GetPayloadTypePin())
{
if (Pin->LinkedTo.Num() == 0)
{
RefreshOutputType();
}
}
}
void UGGA_K2Node_EffectContextPayload::PostReconstructNode()
{
Super::PostReconstructNode();
RefreshOutputType();
}
void UGGA_K2Node_EffectContextPayload::RefreshOutputType()
{
UEdGraphPin* OutStructPin = GetOutStructPin();
UEdGraphPin* PayloadType = GetPayloadTypePin();
if (PayloadType->DefaultObject != OutStructPin->PinType.PinSubCategoryObject)
{
if (OutStructPin->SubPins.Num() > 0)
{
GetSchema()->RecombinePin(OutStructPin);
}
OutStructPin->PinType.PinSubCategoryObject = PayloadType->DefaultObject;
OutStructPin->PinType.PinCategory = (PayloadType->DefaultObject == nullptr) ? UEdGraphSchema_K2::PC_Wildcard : UEdGraphSchema_K2::PC_Struct;
}
}
////////////////////////////////////////////////////////////////////////////
//UK2_Node and EDGraph Interface Implementations
////////////////////////////////////////////////////////////////////////////
FString UGGA_K2Node_EffectContextPayload::GetPinMetaData(FName InPinName, FName InKey)
{
FString MetaData = Super::GetPinMetaData(InPinName, InKey);
if (MetaData.IsEmpty())
{
//Filters out the abstract classes from the pin search
if (InPinName == GetPayloadTypePin()->GetName() && InKey == FBlueprintMetadata::MD_AllowAbstractClasses)
{
MetaData = TEXT("false");
}
}
return MetaData;
}
FSlateIcon UGGA_K2Node_EffectContextPayload::GetIconAndTint(FLinearColor& OutColor) const
{
OutColor = FLinearColor(FColor::Black);
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "Kismet.AllClasses.FunctionIcon");
}
FLinearColor UGGA_K2Node_EffectContextPayload::GetNodeTitleColor() const
{
return FLinearColor(FColor::Cyan);
}
FText UGGA_K2Node_EffectContextPayload::GetMenuCategory() const
{
return LOCTEXT("K2Node_GetProperty_Category", "GGA|GameplayEffect|Context");
}
///////////////////////////////////////////////////////////////////
// PIN GETTERS
///////////////////////////////////////////////////////////////////
UEdGraphPin* UGGA_K2Node_EffectContextPayload::GetEffectContextPin() const
{
UEdGraphPin* Pin = FindPinChecked(K2Node_ContextPayload_PinNames::EffectContextPinName);
check(Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UGGA_K2Node_EffectContextPayload::GetInstancedStructPin() const
{
UEdGraphPin* Pin = FindPinChecked(K2Node_ContextPayload_PinNames::InstancedStructPinName);
check(Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UGGA_K2Node_EffectContextPayload::GetKeyPin() const
{
UEdGraphPin* Pin = FindPinChecked(K2Node_ContextPayload_PinNames::KeyPinName);
check(Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UGGA_K2Node_EffectContextPayload::GetPayloadTypePin() const
{
UEdGraphPin* Pin = FindPinChecked(K2Node_ContextPayload_PinNames::PayloadTypePinName);
check(Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UGGA_K2Node_EffectContextPayload::GetOutStructPin() const
{
UEdGraphPin* Pin = FindPinChecked(K2Node_ContextPayload_PinNames::OutStructPinName);
check(Pin->Direction == EGPD_Output);
return Pin;
}
UEdGraphPin* UGGA_K2Node_EffectContextPayload::GetValidPin() const
{
UEdGraphPin* Pin = FindPinChecked(K2Node_ContextPayload_PinNames::ValidExecPinName);
check(Pin->Direction == EGPD_Output);
return Pin;
}
UEdGraphPin* UGGA_K2Node_EffectContextPayload::GetInvalidPin() const
{
UEdGraphPin* Pin = FindPinChecked(K2Node_ContextPayload_PinNames::InvalidExecPinName);
check(Pin->Direction == EGPD_Output);
return Pin;
}
void UGGA_K2Node_GetEffectContextPayload::AllocateDefaultPins()
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Struct, FGameplayEffectContextHandle::StaticStruct(), K2Node_ContextPayload_PinNames::EffectContextPinName);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UScriptStruct::StaticClass(), K2Node_ContextPayload_PinNames::PayloadTypePinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, K2Node_ContextPayload_PinNames::ValidExecPinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, K2Node_ContextPayload_PinNames::InvalidExecPinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, K2Node_ContextPayload_PinNames::OutStructPinName);
Super::AllocateDefaultPins();
}
void UGGA_K2Node_GetEffectContextPayload::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
UFunction* GetValidContextPayloadFunc = UGGA_GameplayEffectFunctionLibrary::StaticClass()->FindFunctionByName(
GET_FUNCTION_NAME_CHECKED_ThreeParams(UGGA_GameplayEffectFunctionLibrary, GetValidContextPayload, FGameplayEffectContextHandle, const UScriptStruct*, bool&));
UFunction* GetInstStructValueFunc = UBlueprintInstancedStructLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UBlueprintInstancedStructLibrary, GetInstancedStructValue));
if (!GetValidContextPayloadFunc || !GetInstStructValueFunc)
{
CompilerContext.MessageLog.Error(
*LOCTEXT("InvalidClass", "The GetValidContextPayload or GetInstancedStructValue functions have not been found. Check ExpandNode in GGA_K2Node_EffectContextPayload.cpp").ToString(),
this);
return;
}
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
bool bIsErrorFree = true;
const FEdGraphPinType& PinType = GetOutStructPin()->PinType;
UK2Node_TemporaryVariable* TempVarOutput = CompilerContext.SpawnInternalVariable(
this, PinType.PinCategory, PinType.PinSubCategory, PinType.PinSubCategoryObject.Get(), PinType.ContainerType, PinType.PinValueType);
UK2Node_AssignmentStatement* AssignNode = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
UK2Node_CallFunction* const GetPayloadNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
UK2Node_CallFunction* const GetStructValueNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
GetPayloadNode->SetFromFunction(GetValidContextPayloadFunc);
GetStructValueNode->SetFromFunction(GetInstStructValueFunc);
GetPayloadNode->AllocateDefaultPins();
GetStructValueNode->AllocateDefaultPins();
AssignNode->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(GetPayloadNode, this);
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(GetStructValueNode, this);
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(AssignNode, this);
//Connect input pins to the GetValidContextPayloadNode
//----------------------------------------------------------------------------------------
// UEdGraphPin* EffectContextPin = Schema->FindSelfPin(*GetPayloadNode, EGPD_Input);
UEdGraphPin* EffectContextPin = GetPayloadNode->FindPinChecked(TEXT("EffectContext"));
UEdGraphPin* InterExecLeftPin = GetPayloadNode->GetThenPin();
UEdGraphPin* PayloadTypePin = GetPayloadNode->FindPinChecked(TEXT("PayloadType"));
UEdGraphPin* InterStructLeftPin = GetPayloadNode->GetReturnValuePin();
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*GetEffectContextPin(), *EffectContextPin).CanSafeConnect();
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*GetPayloadTypePin(), *PayloadTypePin).CanSafeConnect();
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
//Connect input Target Pin and the output GetPayloadNode pins to the GetStructValueNode pin
//----------------------------------------------------------------------------------------
UEdGraphPin* InterExecRightPin = GetStructValueNode->GetExecPin();
UEdGraphPin* ValidPin = GetStructValueNode->FindPinChecked(TEXT("Valid"));
UEdGraphPin* InvalidPin = GetStructValueNode->FindPinChecked(TEXT("NotValid"));
UEdGraphPin* OutStructPin = GetStructValueNode->FindPinChecked(TEXT("Value"));
UEdGraphPin* InterStructRightPin = GetStructValueNode->FindPinChecked(TEXT("InstancedStruct"));
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*GetExecPin(), *InterExecRightPin).CanSafeConnect();
bIsErrorFree &= Schema->TryCreateConnection(InterStructLeftPin, InterStructRightPin);
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
//Connect the output of GetStructValueNode to the input of Assign node. The Invalid pin directly goes to Invalid pin
// but the valid pin goes through the Assign node. The temporary variable is assigned a value, and is connected to the
// node's output struct. The order that the connections are made is critical. First the variable needs to connect to the output,
// then the variable should be connected to the Variable pin of Assign so the variable type is updated, and only then the Value pin
// of Assign can be connected to OutStructPin of the GetStructValueNode.
//----------------------------------------------------------------------------------------
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*GetOutStructPin(), *TempVarOutput->GetVariablePin()).CanSafeConnect();
bIsErrorFree &= Schema->TryCreateConnection(AssignNode->GetVariablePin(), TempVarOutput->GetVariablePin());
bIsErrorFree &= Schema->TryCreateConnection(AssignNode->GetValuePin(), OutStructPin);
bIsErrorFree &= Schema->TryCreateConnection(ValidPin, AssignNode->GetExecPin());
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*GetInvalidPin(), *InvalidPin).CanSafeConnect();
bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*GetValidPin(), *AssignNode->GetThenPin()).CanSafeConnect();
//----------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------
if (!bIsErrorFree)
{
CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "Get Context Payload: Internal connection error. @@").ToString(), this);
}
BreakAllNodeLinks();
}
////////////////////////////////////////////////////////////////////////////
//UK2_Node and EDGraph Interface Implementations
////////////////////////////////////////////////////////////////////////////
void UGGA_K2Node_GetEffectContextPayload::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
Super::GetMenuActions(ActionRegistrar);
UClass* Action = GetClass();
if (ActionRegistrar.IsOpenForRegistration(Action))
{
UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create(GetClass());
ActionRegistrar.AddBlueprintAction(Action, Spawner);
}
}
FText UGGA_K2Node_GetEffectContextPayload::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("K2Node_GetProperty_NodeTitle", "Get Context Payload");
}
FText UGGA_K2Node_GetEffectContextPayload::GetTooltipText() const
{
return LOCTEXT("K2Node_GetProperty_TooltipText", "Outputs the context payload based on the selected payload type");
}
FText UGGA_K2Node_GetEffectContextPayload::GetKeywords() const
{
return LOCTEXT("GetEffectContextPayload", "Get Gameplay Effect Context Payload");
}
#undef LOCTEXT_NAMESPACE

View File

@@ -0,0 +1,21 @@
#include "GenericGameplayAbilitiesEditor.h"
#include "GGA_AttributeGroupNameCustomization.h"
#define LOCTEXT_NAMESPACE "FGenericGameplayAbilitiesEditorModule"
void FGenericGameplayAbilitiesEditorModule::StartupModule()
{
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomPropertyTypeLayout("GGA_AttributeGroupName", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FGGA_AttributeGroupNameCustomization::MakeInstance));
}
void FGenericGameplayAbilitiesEditorModule::ShutdownModule()
{
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomPropertyTypeLayout("GGA_AttributeGroupName");
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FGenericGameplayAbilitiesEditorModule, GenericGameplayAbilitiesEditor)

View File

@@ -0,0 +1,41 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "Layout/Visibility.h"
#include "IPropertyTypeCustomization.h"
class FDetailWidgetRow;
class IDetailChildrenBuilder;
class IPropertyHandle;
/** Details customization for FAttributeBasedFloat */
class GENERICGAMEPLAYABILITIESEDITOR_API FGGA_AttributeGroupNameCustomization : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
/** Overridden to provide the property name or hide, if necessary */
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
static void GeneratePrimaryComboboxStrings(TArray<TSharedPtr<FString>>& OutComboBoxStrings, TArray<TSharedPtr<SToolTip>>& OutToolTips, TArray<bool>& OutRestrictedItems, bool bAllowClear, bool bAllowAll,
TMap<FName, TArray<FName>>* InItems);
FString GenerateMainString();
void OnMainValueSelected(const FString& String);
static void GenerateSubComboboxStrings(TArray<TSharedPtr<FString>>& OutComboBoxStrings, TArray<TSharedPtr<SToolTip>>& OutToolTips, TArray<bool>& OutRestrictedItems, bool bAllowClear, bool bAllowAll,
TMap<FName, TArray<FName>>* InItems, TSharedPtr<IPropertyHandle> PrimaryKey);
FString GenerateSubString();
void OnSubValueSelected(const FString& String);
/** Overridden to allow for possibly being hidden */
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> StructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
private:
TMap<FName, TArray<FName>> FNameMap;
TSharedPtr<IPropertyHandle> MainNamePropertyHandle;
TSharedPtr<IPropertyHandle> SubNamePropertyHandle;
TWeakPtr<IPropertyUtilities> PropertyUtilities;
};

View File

@@ -0,0 +1,33 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "K2Node_CallFunction.h"
#include "GGA_K2Node_ContextPayload.generated.h"
class UEdGraphPin;
class FBlueprintActionDatabaseRegistrar;
/**
* Node customization for MakeInstancedStruct(), SetContextPayload(), and GetContextPayload().
*/
UCLASS()
class GENERICGAMEPLAYABILITIESEDITOR_API UGGA_K2Node_ContextPayload : public UK2Node_CallFunction
{
GENERATED_BODY()
//~ Begin UEdGraphNode Interface
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
//~ End UEdGraphNode Interface
//~ Begin K2Node Interface
virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const override;
//~ End K2Node Interface
protected:
//~ UK2Node_CallFunction interface
virtual bool CanToggleNodePurity() const override { return false; }
//~ End UK2Node_CallFunction interface
};

View File

@@ -0,0 +1,73 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "K2Node.h"
#include "GGA_K2Node_EffectContextPayload.generated.h"
namespace K2Node_ContextPayload_PinNames
{
static FName EffectContextPinName = "EffectContext";
static FName InstancedStructPinName = "InstancedStruct";
static FName KeyPinName = "Key";
static FName PayloadTypePinName = "PayloadType";
static FName OutStructPinName = "ReturnValue";
static FName ValidExecPinName = "Valid";
static FName InvalidExecPinName = "Invalid";
};
/**
*
*/
UCLASS()
class GENERICGAMEPLAYABILITIESEDITOR_API UGGA_K2Node_EffectContextPayload : public UK2Node
{
GENERATED_BODY()
protected:
//~ UEdGraphNode Interface.
virtual void PinConnectionListChanged(UEdGraphPin* Pin) override;
virtual void PostPlacedNewNode() override;
virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override;
virtual FString GetPinMetaData(FName InPinName, FName InKey) override;
virtual FSlateIcon GetIconAndTint(FLinearColor& OutColor) const override;
virtual FLinearColor GetNodeTitleColor() const override;
//~ UK2Node Interface
virtual FText GetMenuCategory() const override;
virtual void ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins) override;
virtual bool IsNodeSafeToIgnore() const override { return true; }
virtual bool IsNodePure() const override { return false; }
virtual void PostReconstructNode() override;
//~ Getters
UEdGraphPin* GetEffectContextPin() const;
UEdGraphPin* GetInstancedStructPin() const;
UEdGraphPin* GetKeyPin() const;
UEdGraphPin* GetPayloadTypePin() const;
UEdGraphPin* GetOutStructPin() const;
UEdGraphPin* GetValidPin() const;
UEdGraphPin* GetInvalidPin() const;
//~ Helper Functions
void RefreshOutputType();
};
UCLASS(BlueprintType, Blueprintable)
class GENERICGAMEPLAYABILITIESEDITOR_API UGGA_K2Node_GetEffectContextPayload : public UGGA_K2Node_EffectContextPayload
{
GENERATED_BODY()
//~ UEdGraphNode Interface.
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
virtual FText GetTooltipText() const override;
virtual FText GetKeywords() const override;
virtual void AllocateDefaultPins() override;
//~ UK2Node Interface
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FGenericGameplayAbilitiesEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};