// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. #include "K2Node.h" #include "UObject/UnrealType.h" #include "UObject/CoreRedirects.h" #include "EdGraph/EdGraphPin.h" #include "UObject/Interface.h" #include "Engine/Blueprint.h" #include "Engine/MemberReference.h" #include "GraphEditorSettings.h" #include "EdGraph/EdGraphSchema.h" #include "EdGraphSchema_K2.h" #include "K2Node_CallFunction.h" #include "K2Node_MacroInstance.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Editor/EditorEngine.h" #include "Misc/OutputDeviceNull.h" #include "Engine/Breakpoint.h" #include "Kismet2/KismetDebugUtilities.h" #include "KismetCompiler.h" #include "PropertyCustomizationHelpers.h" #include "ObjectEditorUtils.h" #define LOCTEXT_NAMESPACE "K2Node" // File-Scoped Globals static const uint32 MaxArrayPinTooltipLineCount = 10; ///////////////////////////////////////////////////// // UK2Node UK2Node::UK2Node(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { bAllowSplitPins_DEPRECATED = true; } void UK2Node::PostLoad() { #if WITH_EDITORONLY_DATA // Clean up win watches for any deprecated pins we are about to remove in Super::PostLoad if (DeprecatedPins.Num() && HasValidBlueprint()) { UBlueprint* BP = GetBlueprint(); check(BP); // patch DeprecatedPinWatches to WatchedPins: for (int32 WatchIdx = BP->DeprecatedPinWatches.Num() - 1; WatchIdx >= 0; --WatchIdx) { UEdGraphPin_Deprecated* WatchedPin = BP->DeprecatedPinWatches[WatchIdx]; if (DeprecatedPins.Contains(WatchedPin)) { if (UEdGraphPin* NewPin = UEdGraphPin::FindPinCreatedFromDeprecatedPin(WatchedPin)) { BP->WatchedPins.Add(NewPin); } BP->DeprecatedPinWatches.RemoveAt(WatchIdx); } } } #endif // WITH_EDITORONLY_DATA Super::PostLoad(); // fix up pin default values FixupPinDefaultValues(); } void UK2Node::Serialize(FArchive& Ar) { Super::Serialize(Ar); if (Ar.IsObjectReferenceCollector() && Ar.IsSaving()) { // If looking for references during save, expand any default values on the pins for (const UEdGraphPin* Pin : Pins) { if (!Pin->bDefaultValueIsIgnored && !Pin->DefaultValue.IsEmpty() && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct && Pin->PinType.PinSubCategoryObject.IsValid()) { UScriptStruct* Struct = Cast(Pin->PinType.PinSubCategoryObject.Get()); if (Struct) { int32 StructSize = Struct->GetStructureSize(); uint8* StructData = (uint8*)FMemory::Malloc(StructSize); // Import the literal text to a dummy struct and then serialize that FOutputDeviceNull NullOutput; Struct->InitializeStruct(StructData); Struct->ImportText(*Pin->DefaultValue, StructData, nullptr, PPF_None, &NullOutput, Pin->PinName); Struct->SerializeItem(Ar, StructData, nullptr); Struct->DestroyStruct(StructData); FMemory::Free(StructData); } } } } } void UK2Node::FixupPinDefaultValues() { const int32 LinkerUE4Version = GetLinkerUE4Version(); const UEdGraphSchema_K2* K2Schema = GetDefault(); // Swap "new" default error tolerance value with zero on vector/rotator equality nodes, in order to preserve current behavior in existing blueprints. if(LinkerUE4Version < VER_UE4_BP_MATH_VECTOR_EQUALITY_USES_EPSILON) { static const FString VectorsEqualFunctionEpsilonPinName = TEXT("KismetMathLibrary.EqualEqual_VectorVector.ErrorTolerance"); static const FString VectorsNotEqualFunctionEpsilonPinName = TEXT("KismetMathLibrary.NotEqual_VectorVector.ErrorTolerance"); static const FString RotatorsEqualFunctionEpsilonPinName = TEXT("KismetMathLibrary.EqualEqual_RotatorRotator.ErrorTolerance"); static const FString RotatorsNotEqualFunctionEpsilonPinName = TEXT("KismetMathLibrary.NotEqual_RotatorRotator.ErrorTolerance"); bool bFoundPin = false; for(int32 i = 0; i < Pins.Num() && !bFoundPin; ++i) { UEdGraphPin* Pin = Pins[i]; check(Pin); TArray RedirectPinNames; GetRedirectPinNames(*Pin, RedirectPinNames); for(auto PinNameIter = RedirectPinNames.CreateConstIterator(); PinNameIter && !bFoundPin; ++PinNameIter) { if((Pin->DefaultValue == Pin->AutogeneratedDefaultValue) && (*PinNameIter == VectorsEqualFunctionEpsilonPinName || *PinNameIter == VectorsNotEqualFunctionEpsilonPinName || *PinNameIter == RotatorsEqualFunctionEpsilonPinName || *PinNameIter == RotatorsNotEqualFunctionEpsilonPinName)) { bFoundPin = true; K2Schema->TrySetDefaultValue(*Pin, TEXT("0.0")); } } } } } FText UK2Node::GetToolTipHeading() const { FText Heading = FText::GetEmpty(); if (UBreakpoint* ExistingBreakpoint = FKismetDebugUtilities::FindBreakpointForNode(GetBlueprint(), this)) { if (ExistingBreakpoint->IsEnabled()) { Heading = LOCTEXT("EnabledBreakpoint", "Active Breakpoint"); FText ActiveBreakpointToolTipText = GetActiveBreakpointToolTipText(); if (!ActiveBreakpointToolTipText.IsEmpty()) { Heading = FText::Format(FText::FromString("{0} - {1}"), Heading, ActiveBreakpointToolTipText); } } else { Heading = LOCTEXT("DisabledBreakpoint", "Disabled Breakpoint"); } } return Heading; } FText UK2Node::GetActiveBreakpointToolTipText() const { return LOCTEXT("ActiveBreakpointToolTip", "Execution will break at this location."); } bool UK2Node::CreatePinsForFunctionEntryExit(const UFunction* Function, bool bForFunctionEntry) { const UEdGraphSchema_K2* K2Schema = GetDefault(); // Create the inputs and outputs bool bAllPinsGood = true; for (TFieldIterator PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt) { UProperty* Param = *PropIt; const bool bIsFunctionInput = !Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm); if (bIsFunctionInput == bForFunctionEntry) { const EEdGraphPinDirection Direction = bForFunctionEntry ? EGPD_Output : EGPD_Input; UEdGraphPin* Pin = CreatePin(Direction, TEXT(""), TEXT(""), NULL, false, false, Param->GetName()); const bool bPinGood = K2Schema->ConvertPropertyToPinType(Param, /*out*/ Pin->PinType); K2Schema->SetPinDefaultValueBasedOnType(Pin); UK2Node_CallFunction::GeneratePinTooltipFromFunction(*Pin, Function); bAllPinsGood = bAllPinsGood && bPinGood; } } return bAllPinsGood; } void UK2Node::AutowireNewNode(UEdGraphPin* FromPin) { const UEdGraphSchema_K2* K2Schema = CastChecked(GetSchema()); // Do some auto-connection if (FromPin != NULL) { TSet NodeList; // sometimes we don't always find an ideal connection, but we want to exhaust // all our options first... this stores a secondary less-ideal pin to connect to, if nothing better was found UEdGraphPin* BackupConnection = NULL; // If not dragging an exec pin, auto-connect from dragged pin to first compatible pin on the new node for (int32 i=0; i