310 lines
12 KiB
C++
310 lines
12 KiB
C++
// 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
|