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

295 lines
9.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "K2Node_Copy.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "Containers/Array.h"
#include "Containers/Map.h"
#include "Containers/UnrealString.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "EdGraphUtilities.h"
#include "EditorCategoryUtils.h"
#include "Engine/Blueprint.h"
#include "HAL/PlatformMath.h"
#include "Internationalization/Internationalization.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "KismetCompiledFunctionContext.h"
#include "KismetCompilerMisc.h"
#include "Misc/AssertionMacros.h"
#include "Templates/Casts.h"
#include "Templates/SubclassOf.h"
#include "UObject/Class.h"
#include "UObject/NameTypes.h"
#include "UObject/Object.h"
#include "UObject/WeakObjectPtrTemplates.h"
class FKismetCompilerContext;
struct FBPTerminal;
#define LOCTEXT_NAMESPACE "K2Node_Copy"
//////////////////////////////////////////////////////////////////////////
// FKCHandler_Select
class FKCHandler_Copy : public FNodeHandlingFunctor
{
protected:
TMap<UEdGraphNode*, FBPTerminal*> BoolTermMap;
public:
FKCHandler_Copy(FKismetCompilerContext& InCompilerContext)
: FNodeHandlingFunctor(InCompilerContext)
{
}
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
FNodeHandlingFunctor::RegisterNets(Context, Node);
// Create the net for the return value manually as it's a special case Output Direction pin
UK2Node_Copy* CopyNode = Cast<UK2Node_Copy>(Node);
UEdGraphPin* CopyResultPin = CopyNode->GetCopyResultPin();
FBPTerminal* Term = Context.CreateLocalTerminalFromPinAutoChooseScope(CopyResultPin, Context.NetNameMap->MakeValidName(CopyResultPin));
Context.NetMap.Add(CopyResultPin, Term);
}
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
// Cast the node and get all the input pins
UK2Node_Copy* CopyNode = Cast<UK2Node_Copy>(Node);
// Get the kismet term for the copy result
UEdGraphPin* CopyResultPin = CopyNode->GetCopyResultPin();
FBPTerminal** CopyResultTerm = Context.NetMap.Find(CopyResultPin);
// Get the kismet term from the option pin
UEdGraphPin* InputReferencePin = FEdGraphUtilities::GetNetFromPin(Node->Pins[0]);
FBPTerminal** InputReferenceTerm = Context.NetMap.Find(InputReferencePin);
// Copy the reference value into the output pin so the value will be returned as a copy.
FKismetCompilerUtilities::CreateObjectAssignmentStatement(Context, Node, *InputReferenceTerm, *CopyResultTerm);
}
};
UK2Node_Copy::UK2Node_Copy(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UK2Node_Copy::AllocateDefaultPins()
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, UEdGraphSchema_K2::PN_Item);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, UEdGraphSchema_K2::PN_ReturnValue);
Super::AllocateDefaultPins();
}
FText UK2Node_Copy::GetTooltipText() const
{
return LOCTEXT("CopyNodeTooltip", "Outputs a copy of the value passed into it.");
}
FText UK2Node_Copy::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("Copy", "Copy");
}
void UK2Node_Copy::PostReconstructNode()
{
if (GetInputReferencePin()->LinkedTo.Num() > 0)
{
PropagatePinType(GetInputReferencePin()->LinkedTo[0]->PinType);
}
else if (GetCopyResultPin()->LinkedTo.Num() > 0)
{
PropagatePinType(GetCopyResultPin()->LinkedTo[0]->PinType);
}
}
/** Determine if any pins are connected, if so make all the other pins the same type, if not, make sure pins are switched back to wildcards */
void UK2Node_Copy::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{
Super::NotifyPinConnectionListChanged(Pin);
if (Pin->ParentPin == nullptr)
{
UEdGraphPin* InputPin = GetInputReferencePin();
UEdGraphPin* ResultPin = GetCopyResultPin();
// If a pin connection is made, then we need to propagate the change to the other pins and validate all connections, otherwise reset them to Wildcard pins
if (Pin->LinkedTo.Num() > 0)
{
PropagatePinType(Pin->LinkedTo[0]->PinType);
}
else if (InputPin->LinkedTo.Num() == 0 && ResultPin->LinkedTo.Num() == 0)
{
InputPin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
InputPin->PinType.PinSubCategory = TEXT("");
InputPin->PinType.PinSubCategoryObject = NULL;
ResultPin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
ResultPin->PinType.PinSubCategory = TEXT("");
ResultPin->PinType.PinSubCategoryObject = NULL;
InputPin->BreakAllPinLinks();
ResultPin->BreakAllPinLinks();
}
}
}
UEdGraphPin* UK2Node_Copy::GetInputReferencePin() const
{
UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_Item);
check(Pin);
return Pin;
}
UEdGraphPin* UK2Node_Copy::GetCopyResultPin() const
{
UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_ReturnValue);
check(Pin);
return Pin;
}
void UK2Node_Copy::PinTypeChanged(UEdGraphPin* Pin)
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
{
// Set the return value
UEdGraphPin* ReturnPin = GetCopyResultPin();
if (ReturnPin->PinType != Pin->PinType)
{
// Recombine the sub pins back into the ReturnPin
if (ReturnPin->SubPins.Num() > 0)
{
Schema->RecombinePin(ReturnPin->SubPins[0]);
}
ReturnPin->PinType = Pin->PinType;
Schema->SetPinAutogeneratedDefaultValueBasedOnType(ReturnPin);
}
}
// Let the graph know to refresh
GetGraph()->NotifyGraphChanged();
UBlueprint* Blueprint = GetBlueprint();
if(!Blueprint->bBeingCompiled)
{
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
Blueprint->BroadcastChanged();
}
}
bool UK2Node_Copy::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{
if (OtherPin)
{
if (OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
{
OutReason = LOCTEXT("ExecConnectionDisallowed", "Cannot connect with Exec pin.").ToString();
return true;
}
else if (OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object || OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class
|| OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject || OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftClass
|| OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Interface)
{
OutReason = FText::Format(LOCTEXT("ObjectConnectionDisallowed", "Cannot connect with {0} pin."), FText::FromName(OtherPin->PinType.PinCategory)).ToString();
return true;
}
else if (OtherPin->PinType.IsContainer())
{
OutReason = LOCTEXT("ArrayConnectionDisallowed", "Cannot connect with container pin.").ToString();
return true;
}
}
return Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);
}
FNodeHandlingFunctor* UK2Node_Copy::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
return static_cast<FNodeHandlingFunctor*>(new FKCHandler_Copy(CompilerContext));
}
void UK2Node_Copy::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
// actions get registered under specific object-keys; the idea is that
// actions might have to be updated (or deleted) if their object-key is
// mutated (or removed)... here we use the node's class (so if the node
// type disappears, then the action should go with it)
UClass* ActionKey = GetClass();
// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
// check to make sure that the registrar is looking for actions of this type
// (could be regenerating actions for a specific asset, and therefore the
// registrar would only accept actions corresponding to that asset)
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
}
}
FText UK2Node_Copy::GetMenuCategory() const
{
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Utilities);
}
void UK2Node_Copy::PropagatePinType(FEdGraphPinType& InType)
{
UClass const* CallingContext = NULL;
if (UBlueprint const* Blueprint = GetBlueprint())
{
CallingContext = Blueprint->GeneratedClass;
if (CallingContext == NULL)
{
CallingContext = Blueprint->ParentClass;
}
}
UEdGraphPin* InputPin = GetInputReferencePin();
UEdGraphPin* ResultPin = GetCopyResultPin();
InputPin->PinType = InType;
ResultPin->PinType = InType;
InputPin->PinType.ContainerType = EPinContainerType::None;
InputPin->PinType.bIsReference = false;
ResultPin->PinType.ContainerType = EPinContainerType::None;
ResultPin->PinType.bIsReference = false;
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
// Verify that all previous connections to this pin are still valid with the new type
for (TArray<UEdGraphPin*>::TIterator ConnectionIt(InputPin->LinkedTo); ConnectionIt; ++ConnectionIt)
{
UEdGraphPin* ConnectedPin = *ConnectionIt;
if (!Schema->ArePinsCompatible(InputPin, ConnectedPin, CallingContext))
{
InputPin->BreakLinkTo(ConnectedPin);
}
}
// Verify that all previous connections to this pin are still valid with the new type
for (TArray<UEdGraphPin*>::TIterator ConnectionIt(ResultPin->LinkedTo); ConnectionIt; ++ConnectionIt)
{
UEdGraphPin* ConnectedPin = *ConnectionIt;
if (!Schema->ArePinsCompatible(ResultPin, ConnectedPin, CallingContext))
{
ResultPin->BreakLinkTo(ConnectedPin);
}
}
}
#undef LOCTEXT_NAMESPACE