Files
UnrealEngine/Engine/Source/Editor/BlueprintGraph/Private/K2Node_CommutativeAssociativeBinaryOperator.cpp
2025-05-18 13:04:45 +08:00

326 lines
9.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "K2Node_CommutativeAssociativeBinaryOperator.h"
#include "Containers/Array.h"
#include "Containers/EnumAsByte.h"
#include "Delegates/Delegate.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraphSchema_K2.h"
#include "Framework/Commands/UIAction.h"
#include "HAL/PlatformMath.h"
#include "Internationalization/Internationalization.h"
#include "Internationalization/Text.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/CompilerResultsLog.h"
#include "KismetCompiler.h"
#include "Misc/AssertionMacros.h"
#include "ScopedTransaction.h"
#include "Textures/SlateIcon.h"
#include "ToolMenu.h"
#include "ToolMenuSection.h"
#include "UObject/Class.h"
#include "UObject/NameTypes.h"
#include "UObject/Script.h"
#include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h"
#define LOCTEXT_NAMESPACE "CommutativeAssociativeBinaryOperatorNode"
UK2Node_CommutativeAssociativeBinaryOperator::UK2Node_CommutativeAssociativeBinaryOperator(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
NumAdditionalInputs = 0;
}
UEdGraphPin* UK2Node_CommutativeAssociativeBinaryOperator::FindOutPin() const
{
for(int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++)
{
if(EEdGraphPinDirection::EGPD_Output == Pins[PinIdx]->Direction)
{
return Pins[PinIdx];
}
}
return NULL;
}
UEdGraphPin* UK2Node_CommutativeAssociativeBinaryOperator::FindSelfPin() const
{
for(int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++)
{
if(Pins[PinIdx]->PinName == UEdGraphSchema_K2::PN_Self)
{
return Pins[PinIdx];
}
}
return NULL;
}
bool UK2Node_CommutativeAssociativeBinaryOperator::CanAddPin() const
{
return (NumAdditionalInputs + BinaryOperatorInputsNum) < GetMaxInputPinsNum();
}
bool UK2Node_CommutativeAssociativeBinaryOperator::CanRemovePin(const UEdGraphPin* Pin) const
{
return (
Pin &&
Pin->ParentPin == nullptr &&
NumAdditionalInputs &&
(INDEX_NONE != Pins.IndexOfByKey(Pin)) &&
(EEdGraphPinDirection::EGPD_Input == Pin->Direction)
);
}
UEdGraphPin* UK2Node_CommutativeAssociativeBinaryOperator::GetInputPin(int32 InputPinIndex)
{
const UEdGraphPin* OutPin = FindOutPin();
const UEdGraphPin* SelfPin = FindSelfPin();
int32 CurrentInputIndex = 0;
for(int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++)
{
UEdGraphPin* CurrentPin = Pins[PinIdx];
if((CurrentPin != OutPin) && (CurrentPin != SelfPin))
{
if(CurrentInputIndex == InputPinIndex)
{
return CurrentPin;
}
CurrentInputIndex++;
}
}
return NULL;
}
FEdGraphPinType UK2Node_CommutativeAssociativeBinaryOperator::GetType() const
{
for (int32 PinIt = 0; PinIt < Pins.Num(); PinIt++)
{
if (Pins[PinIt] != FindSelfPin())
{
return Pins[PinIt]->PinType;
}
}
return FEdGraphPinType();
}
void UK2Node_CommutativeAssociativeBinaryOperator::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
if (const UFunction* Function = GetTargetFunction())
{
if (Function->HasAnyFunctionFlags(FUNC_BlueprintPure))
{
UEdGraphPin* SelfPin = FindSelfPin();
const FEdGraphPinType InputType = GetType();
int32 OutPinCount = 0;
bool bConsistentPinType = true;
for (UEdGraphPin* Pin : Pins)
{
if (Pin->Direction == EEdGraphPinDirection::EGPD_Output)
{
++OutPinCount;
}
else if (Pin != SelfPin)
{
if (Pin->PinType != InputType)
{
bConsistentPinType = false;
}
}
}
if (OutPinCount != 1)
{
MessageLog.Error(*LOCTEXT("InvalidOutPin", "@@ - Commutative associative binary operators must have a single output pin.").ToString(), this);
}
if (!bConsistentPinType)
{
MessageLog.Error(*LOCTEXT("InconsistentType", "@@ - Commutative associative binary operator inputs must all be of the same type.").ToString(), this);
}
}
else
{
MessageLog.Error(*LOCTEXT("MustBePure", "@@ - Commutative associative binary operators must be pure functions.").ToString(), this);
}
}
}
void UK2Node_CommutativeAssociativeBinaryOperator::AllocateDefaultPins()
{
Super::AllocateDefaultPins();
for (int32 i = 0; i < NumAdditionalInputs; ++i)
{
AddInputPinInner(i);
}
}
void UK2Node_CommutativeAssociativeBinaryOperator::AddInputPinInner(int32 AdditionalPinIndex)
{
const FEdGraphPinType InputType = GetType();
UEdGraphNode::FCreatePinParams PinParams;
PinParams.ContainerType = InputType.ContainerType;
PinParams.bIsReference = InputType.bIsReference;
PinParams.ValueTerminalType = InputType.PinValueType;
CreatePin(EGPD_Input,
InputType.PinCategory,
InputType.PinSubCategory,
InputType.PinSubCategoryObject.Get(),
GetNameForAdditionalPin(AdditionalPinIndex + BinaryOperatorInputsNum),
PinParams
);
}
void UK2Node_CommutativeAssociativeBinaryOperator::AddInputPin()
{
if(CanAddPin())
{
FScopedTransaction Transaction( LOCTEXT("AddPinTx", "AddPin") );
Modify();
AddInputPinInner(NumAdditionalInputs);
++NumAdditionalInputs;
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}
}
void UK2Node_CommutativeAssociativeBinaryOperator::RemoveInputPin(UEdGraphPin* Pin)
{
if(CanRemovePin(Pin))
{
FScopedTransaction Transaction( LOCTEXT("RemovePinTx", "RemovePin") );
Modify();
if (RemovePin(Pin))
{
--NumAdditionalInputs;
int32 NameIndex = 0;
const UEdGraphPin* OutPin = FindOutPin();
const UEdGraphPin* SelfPin = FindSelfPin();
for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex)
{
UEdGraphPin* LocalPin = Pins[PinIndex];
if(LocalPin && (LocalPin != OutPin) && (LocalPin != SelfPin))
{
const FName PinName = GetNameForAdditionalPin(NameIndex);
if (PinName != LocalPin->PinName)
{
LocalPin->Modify();
LocalPin->PinName = PinName;
}
NameIndex++;
}
}
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}
}
}
void UK2Node_CommutativeAssociativeBinaryOperator::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
{
Super::GetNodeContextMenuActions(Menu, Context);
if (!Context->bIsDebugging)
{
static FName CommutativeAssociativeBinaryOperatorNodeName = FName("CommutativeAssociativeBinaryOperatorNode");
FText CommutativeAssociativeBinaryOperatorStr = LOCTEXT("CommutativeAssociativeBinaryOperatorNode", "Operator Node");
if (Context->Pin != NULL)
{
if(CanRemovePin(Context->Pin))
{
FToolMenuSection& Section = Menu->AddSection(CommutativeAssociativeBinaryOperatorNodeName, CommutativeAssociativeBinaryOperatorStr);
Section.AddMenuEntry(
"RemovePin",
LOCTEXT("RemovePin", "Remove pin"),
LOCTEXT("RemovePinTooltip", "Remove this input pin"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateUObject(const_cast<UK2Node_CommutativeAssociativeBinaryOperator*>(this), &UK2Node_CommutativeAssociativeBinaryOperator::RemoveInputPin, const_cast<UEdGraphPin*>(Context->Pin))
)
);
}
}
else if(CanAddPin())
{
FToolMenuSection& Section = Menu->AddSection(CommutativeAssociativeBinaryOperatorNodeName, CommutativeAssociativeBinaryOperatorStr);
Section.AddMenuEntry(
"AddPin",
LOCTEXT("AddPin", "Add pin"),
LOCTEXT("AddPinTooltip", "Add another input pin"),
FSlateIcon(),
FUIAction(
FExecuteAction::CreateUObject(const_cast<UK2Node_CommutativeAssociativeBinaryOperator*>(this), &UK2Node_CommutativeAssociativeBinaryOperator::AddInputPin)
)
);
}
}
}
void UK2Node_CommutativeAssociativeBinaryOperator::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
if (NumAdditionalInputs > 0)
{
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
UEdGraphPin* LastOutPin = NULL;
const UFunction* const Function = GetTargetFunction();
const UEdGraphPin* SrcOutPin = FindOutPin();
const UEdGraphPin* SrcSelfPin = FindSelfPin();
UEdGraphPin* SrcFirstInput = GetInputPin(0);
check(SrcFirstInput);
for(int32 PinIndex = 0; PinIndex < Pins.Num(); PinIndex++)
{
UEdGraphPin* CurrentPin = Pins[PinIndex];
if( (CurrentPin == SrcFirstInput) || (CurrentPin == SrcOutPin) || (SrcSelfPin == CurrentPin) )
{
continue;
}
UK2Node_CommutativeAssociativeBinaryOperator* NewOperator = SourceGraph->CreateIntermediateNode<UK2Node_CommutativeAssociativeBinaryOperator>();
NewOperator->SetFromFunction(Function);
NewOperator->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(NewOperator, this);
UEdGraphPin* NewOperatorInputA = NewOperator->GetInputPin(0);
check(NewOperatorInputA);
if(LastOutPin)
{
Schema->TryCreateConnection(LastOutPin, NewOperatorInputA);
}
else
{
// handle first created node (SrcFirstInput is skipped, and has no own node).
CompilerContext.MovePinLinksToIntermediate(*SrcFirstInput, *NewOperatorInputA);
}
UEdGraphPin* NewOperatorInputB = NewOperator->GetInputPin(1);
check(NewOperatorInputB);
CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *NewOperatorInputB);
LastOutPin = NewOperator->FindOutPin();
}
check(LastOutPin);
UEdGraphPin* TrueOutPin = FindOutPin();
check(TrueOutPin);
CompilerContext.MovePinLinksToIntermediate(*TrueOutPin, *LastOutPin);
BreakAllNodeLinks();
}
}
#undef LOCTEXT_NAMESPACE