UE蓝图 函数调用(CallFunction)节点和源码

系列文章目录

UE蓝图 Get节点和源码
UE蓝图 Set节点和源码
UE蓝图 Cast节点和源码
UE蓝图 分支(Branch)节点和源码
UE蓝图 入口(FunctionEntry)节点和源码
UE蓝图 返回结果(FunctionResult)节点和源码
UE蓝图 函数调用(CallFunction)节点和源码


文章目录

  • 系列文章目录
  • 一、CallFunction节点功能
  • 二、CallFunction节点用法
  • 三、CallFunction使用场景
      • 1. **事件处理**
      • 2. **条件逻辑**
      • 3. **延迟操作**
      • 4. **数据处理**
      • 5. **AI和角色行为**
      • 6. **用户输入处理**
      • 7. **系统交互**
      • 8. **自定义逻辑**
  • 四、实现原理
  • 五、相关源码


一、CallFunction节点功能

在这里插入图片描述

CallFunction节点用于调用指定的函数或方法。这些函数可以是引擎自带的,也可以是用户自定义的。通过CallFunction节点,你可以在蓝图中实现复杂的逻辑和交互。

二、CallFunction节点用法

在Unreal Engine(UE)的蓝图中,CallFunction节点用于在运行时调用一个特定的函数。这个节点通常用于执行那些已经定义好但需要在特定条件下触发的函数。以下是CallFunction节点的基本用法:

  1. 添加CallFunction节点:首先,你需要在你的蓝图中从“右键菜单” -> 在弹出框中输入函数的名字进行检索->选择你要使用的函数,添加一个CallFunction节点。

  2. 传递参数:如果所调用的函数需要参数,你需要将这些参数通过连线的方式传递给CallFunction节点的相应输入引脚。参数的类型和顺序必须与函数定义中的一致。

  3. 执行函数:当蓝图中的逻辑流程到达CallFunction节点时,它将执行所选择的函数。这意味着任何与该函数相关联的逻辑或行为都将被执行。

  4. 处理返回值:如果调用的函数有返回值,你可以在CallFunction节点的“Return Value”引脚处获取这个值。然后,你可以将这个返回值用于蓝图中的其他逻辑或传递给其他节点。

  5. 连接其他节点:你可以将CallFunction节点与其他节点连接起来,以创建更复杂的逻辑流程。例如,你可以使用Sequence节点来确保一系列操作按顺序执行,或者使用Parallel节点来同时执行多个操作。

  6. 编译和测试:完成蓝图编辑后,确保编译你的项目,并在游戏中测试CallFunction节点的行为,以确保它按照预期工作。

三、CallFunction使用场景

UE蓝图中的CallFunction节点有多个使用场景,以下是一些常见的例子:

1. 事件处理

在UE中,事件是蓝图系统的重要组成部分。当某个事件发生时(例如,用户点击按钮、角色受到伤害等),你可以使用CallFunction节点来调用处理该事件的函数。

2. 条件逻辑

根据游戏状态或条件的不同,你可能需要调用不同的函数。CallFunction节点可以与SequenceParallelBranch等节点结合使用,实现复杂的条件逻辑。

3. 延迟操作

使用CallFunction节点与Delay节点结合,可以实现延迟执行某些函数。例如,你可能希望在角色死亡后等待一段时间再触发某些行为。

4. 数据处理

当你需要对数据进行处理或转换时,可以创建自定义函数来处理这些数据,并通过CallFunction节点来调用这些函数。

5. AI和角色行为

在创建AI或角色行为时,你可能需要根据角色的状态或环境来调用不同的函数。CallFunction节点是实现这一目标的重要工具。

6. 用户输入处理

处理用户输入(如键盘、鼠标或手柄输入)时,你可以使用CallFunction节点来调用处理这些输入的函数。

7. 系统交互

与游戏系统(如音频、物理、渲染等)进行交互时,你可能需要调用特定的函数来执行某些操作。CallFunction节点提供了一种方便的方式来调用这些函数。

8. 自定义逻辑

除了上述常见场景外,CallFunction节点还可以用于任何需要调用自定义函数的场景。你可以创建自己的函数来实现特定的逻辑或行为,并通过CallFunction节点来调用它们。

四、实现原理

  • 创建输入引脚
    解析函数的注解获取函数的元数据(输入输出参数和是否执行节点等)创建引脚
    函数定义示例如下
	/** Modulo (A % B) */
	UFUNCTION(BlueprintPure, meta=(DisplayName = "% (integer)", CompactNodeTitle = "%", Keywords = "% modulus"), Category="Math|Integer")
	static int32 Percent_IntInt(int32 A, int32 B = 1);

	/** Addition (A + B) */
	UFUNCTION(BlueprintPure, meta=(DisplayName = "int + int", CompactNodeTitle = "+", Keywords = "+ add plus", CommutativeAssociativeBinaryOperator = "true"), Category="Math|Integer")
	static int32 Add_IntInt(int32 A, int32 B = 1);
  • 调用FKCHandler_CallFunction.RegisterNets注册函数引脚
		for (UEdGraphPin* Pin : Node->Pins)
		{
			const bool bIsConnected = (Pin->LinkedTo.Num() != 0);

			// if this pin could use a default (it doesn't have a connection or default of its own)
			if (!bIsConnected && (Pin->DefaultObject == nullptr))
			{
				if (DefaultToSelfParamNames.Contains(Pin->PinName) && FKismetCompilerUtilities::ValidateSelfCompatibility(Pin, Context))
				{
					ensure(Pin->PinType.PinSubCategoryObject != nullptr);
					ensure((Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object) || (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Interface));

					FBPTerminal* Term = Context.RegisterLiteral(Pin);
					Term->Type.PinSubCategory = UEdGraphSchema_K2::PN_Self;
					Context.NetMap.Add(Pin, Term);
				}
				else if (RequiresSetValue.Contains(Pin->PinName))
				{
					CompilerContext.MessageLog.Error(*NSLOCTEXT("KismetCompiler", "PinMustHaveConnection_Error", "Pin @@ must have a connection").ToString(), Pin);
				}
			}
		}
  • 调用Compile编译创建Statement
FBlueprintCompiledStatement* LatentStatement = nullptr;
//遍历需要调用该函数的所有上下文,并为每个上下文发出一个调用函数语句
for (FBPTerminal* Target : ContextTerms)
{
	FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);
	Statement.FunctionToCall = Function;
	Statement.FunctionContext = Target;
	Statement.Type = KCST_CallFunction;
	Statement.bIsInterfaceContext = IsCalledFunctionFromInterface(Node);
	Statement.bIsParentContext = IsCalledFunctionFinal(Node);

	Statement.LHS = LHSTerm;
	Statement.RHS = RHSTerms;

	if (!bIsLatent)
	{
		// Fixup ubergraph calls
		if (pSrcEventNode)
		{
			UEdGraphPin* ExecOut = CompilerContext.GetSchema()->FindExecutionPin(**pSrcEventNode, EGPD_Output);

			check(CompilerContext.UbergraphContext);
			CompilerContext.UbergraphContext->GotoFixupRequestMap.Add(&Statement, ExecOut);
			Statement.UbergraphCallIndex = 0;
		}
	}
	else
	{
		// Fixup latent functions
		if (LatentTargetNode && (Target == ContextTerms.Last()))
		{
			check(LatentTargetParamIndex != INDEX_NONE);
			Statement.UbergraphCallIndex = LatentTargetParamIndex;
			Context.GotoFixupRequestMap.Add(&Statement, ThenExecPin);
			LatentStatement = &Statement;
		}
	}

	AdditionalCompiledStatementHandling(Context, Node, Statement);

	if(Statement.Type == KCST_CallFunction && Function->HasAnyFunctionFlags(FUNC_Delegate))
	{
		CompilerContext.MessageLog.Error(*LOCTEXT("CallingDelegate_Error", "@@ is trying to call a delegate function - delegates cannot be called directly").ToString(), Node);
		// Sanitize the statement, this would have ideally been detected earlier but we need
		// to run AdditionalCompiledStatementHandling to satisify the DelegateNodeHandler
		// implementation:
		Statement.Type = KCST_CallDelegate;
	}
}

五、相关源码

源码文件:
CallFunctionHandler.h
CallFunctionHandler.cpp
K2Node_CallFunction.h
K2Node_CallFunction.cpp
相关类:
FKCHandler_CallFunction
K2Node_CallFunction


/*******************************************************************************
 *  UK2Node_CallFunction
 ******************************************************************************/

UK2Node_CallFunction::UK2Node_CallFunction(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, bPinTooltipsValid(false)
{
	OrphanedPinSaveMode = ESaveOrphanPinMode::SaveAll;
}


bool UK2Node_CallFunction::HasDeprecatedReference() const
{
	UFunction* Function = GetTargetFunction();
	return (Function && Function->HasMetaData(FBlueprintMetadata::MD_DeprecatedFunction));
}

FEdGraphNodeDeprecationResponse UK2Node_CallFunction::GetDeprecationResponse(EEdGraphNodeDeprecationType DeprecationType) const
{
	FEdGraphNodeDeprecationResponse Response = Super::GetDeprecationResponse(DeprecationType);
	if (DeprecationType == EEdGraphNodeDeprecationType::NodeHasDeprecatedReference)
	{
		// TEMP: Do not warn in the case of SpawnActor, as we have a special upgrade path for those nodes
		if (FunctionReference.GetMemberName() == FName(TEXT("BeginSpawningActorFromBlueprint")))
		{
			Response.MessageType = EEdGraphNodeDeprecationMessageType::None;
		}
		else
		{
			UFunction* Function = GetTargetFunction();
			if (ensureMsgf(Function != nullptr, TEXT("This node should not be able to report having a deprecated reference if the target function cannot be resolved.")))
			{
				FString DetailedMessage = Function->GetMetaData(FBlueprintMetadata::MD_DeprecationMessage);
				Response.MessageText = FBlueprintEditorUtils::GetDeprecatedMemberUsageNodeWarning(GetUserFacingFunctionName(Function), FText::FromString(DetailedMessage));
			}
		}
	}
	
	return Response;
}

FText UK2Node_CallFunction::GetFunctionContextString() const
{
	FText ContextString;

	// Don't show 'target is' if no target pin!
	UEdGraphPin* SelfPin = GetDefault<UEdGraphSchema_K2>()->FindSelfPin(*this, EGPD_Input);
	if(SelfPin != NULL && !SelfPin->bHidden)
	{
		const UFunction* Function = GetTargetFunction();
		UClass* CurrentSelfClass = (Function != NULL) ? Function->GetOwnerClass() : NULL;
		UClass const* TrueSelfClass = CurrentSelfClass;
		if (CurrentSelfClass && CurrentSelfClass->ClassGeneratedBy)
		{
			TrueSelfClass = CurrentSelfClass->GetAuthoritativeClass();
		}

		const FText TargetText = FBlueprintEditorUtils::GetFriendlyClassDisplayName(TrueSelfClass);

		FFormatNamedArguments Args;
		Args.Add(TEXT("TargetName"), TargetText);
		ContextString = FText::Format(LOCTEXT("CallFunctionOnDifferentContext", "Target is {TargetName}"), Args);
	}

	return ContextString;
}


FText UK2Node_CallFunction::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	FText FunctionName;
	FText ContextString;
	FText RPCString;

	if (UFunction* Function = GetTargetFunction())
	{
		RPCString = UK2Node_Event::GetLocalizedNetString(Function->FunctionFlags, true);
		FunctionName = GetUserFacingFunctionName(Function);
		ContextString = GetFunctionContextString();
	}
	else
	{
		FunctionName = FText::FromName(FunctionReference.GetMemberName());
		if ((GEditor != NULL) && (GetDefault<UEditorStyleSettings>()->bShowFriendlyNames))
		{
			FunctionName = FText::FromString(FName::NameToDisplayString(FunctionName.ToString(), false));
		}
	}

	if(TitleType == ENodeTitleType::FullTitle)
	{
		FFormatNamedArguments Args;
		Args.Add(TEXT("FunctionName"), FunctionName);
		Args.Add(TEXT("ContextString"), ContextString);
		Args.Add(TEXT("RPCString"), RPCString);

		if (ContextString.IsEmpty() && RPCString.IsEmpty())
		{
			return FText::Format(LOCTEXT("CallFunction_FullTitle", "{FunctionName}"), Args);
		}
		else if (ContextString.IsEmpty())
		{
			return FText::Format(LOCTEXT("CallFunction_FullTitle_WithRPCString", "{FunctionName}\n{RPCString}"), Args);
		}
		else if (RPCString.IsEmpty())
		{
			return FText::Format(LOCTEXT("CallFunction_FullTitle_WithContextString", "{FunctionName}\n{ContextString}"), Args);
		}
		else
		{
			return FText::Format(LOCTEXT("CallFunction_FullTitle_WithContextRPCString", "{FunctionName}\n{ContextString}\n{RPCString}"), Args);
		}
	}
	else
	{
		return FunctionName;
	}
}

void UK2Node_CallFunction::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) const
{
	if (!bPinTooltipsValid)
	{
		for (UEdGraphPin* P : Pins)
		{
			if (!P->PinToolTip.IsEmpty() && ExpandAsEnumPins.Contains(P))
			{
				continue;
			}

			P->PinToolTip.Reset();
			GeneratePinTooltip(*P);
		}

		bPinTooltipsValid = true;
	}

	return UK2Node::GetPinHoverText(Pin, HoverTextOut);
}

void UK2Node_CallFunction::AllocateDefaultPins()
{
	InvalidatePinTooltips();

	UBlueprint* MyBlueprint = GetBlueprint();
	
	UFunction* Function = GetTargetFunction();
	// favor the skeleton function if possible (in case the signature has 
	// changed, and not yet compiled).
	if (!FunctionReference.IsSelfContext())
	{
		UClass* FunctionClass = FunctionReference.GetMemberParentClass(MyBlueprint->GeneratedClass);
		if (UBlueprintGeneratedClass* BpClassOwner = Cast<UBlueprintGeneratedClass>(FunctionClass))
		{
			// this function could currently only be a part of some skeleton 
			// class (the blueprint has not be compiled with it yet), so let's 
			// check the skeleton class as well, see if we can pull pin data 
			// from there...
			UBlueprint* FunctionBlueprint = CastChecked<UBlueprint>(BpClassOwner->ClassGeneratedBy, ECastCheckedType::NullAllowed);
			if (FunctionBlueprint)
			{
				if (UFunction* SkelFunction = FindUField<UFunction>(FunctionBlueprint->SkeletonGeneratedClass, FunctionReference.GetMemberName()))
				{
					Function = SkelFunction;
				}
			}
		}
	}

	// First try remap table
	if (Function == NULL)
	{
		UClass* ParentClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());

		if (ParentClass != NULL)
		{
			if (UFunction* NewFunction = FMemberReference::FindRemappedField<UFunction>(ParentClass, FunctionReference.GetMemberName()))
			{
				// Found a remapped property, update the node
				Function = NewFunction;
				SetFromFunction(NewFunction);
			}
		}
	}

	if (Function == NULL)
	{
		// The function no longer exists in the stored scope
		// Try searching inside all function libraries, in case the function got refactored into one of them.
		for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
		{
			UClass* TestClass = *ClassIt;
			if (TestClass->IsChildOf(UBlueprintFunctionLibrary::StaticClass()))
			{
				Function = FindUField<UFunction>(TestClass, FunctionReference.GetMemberName());
				if (Function != NULL)
				{
					UClass* OldClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());
					Message_Note(
						FText::Format(LOCTEXT("FixedUpFunctionInLibraryFmt", "UK2Node_CallFunction: Fixed up function '{0}', originally in '{1}', now in library '{2}'."),
							FText::FromString(FunctionReference.GetMemberName().ToString()),
							(OldClass != NULL) ? FText::FromString(*OldClass->GetName()) : LOCTEXT("FixedUpFunctionInLibraryNull", "(null)"),
							FText::FromString(TestClass->GetName())
						).ToString()
					);
					SetFromFunction(Function);
					break;
				}
			}
		}
	}

	// Now create the pins if we ended up with a valid function to call
	if (Function != NULL)
	{
		CreatePinsForFunctionCall(Function);
	}

	FCustomStructureParamHelper::UpdateCustomStructurePins(Function, this);

	Super::AllocateDefaultPins();
}

/** Util to find self pin in an array */
UEdGraphPin* FindSelfPin(TArray<UEdGraphPin*>& Pins)
{
	for(int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++)
	{
		if(Pins[PinIdx]->PinName == UEdGraphSchema_K2::PN_Self)
		{
			return Pins[PinIdx];
		}
	}
	return NULL;
}

void UK2Node_CallFunction::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
	// BEGIN TEMP
	// We had a bug where the class was being messed up by copy/paste, but the self pin class was still ok. This code fixes up those cases.
	UFunction* Function = GetTargetFunction();
	if (Function == NULL)
	{
		if (UEdGraphPin* SelfPin = FindSelfPin(OldPins))
		{
			if (UClass* SelfPinClass = Cast<UClass>(SelfPin->PinType.PinSubCategoryObject.Get()))
			{
				if (UFunction* NewFunction = FindUField<UFunction>(SelfPinClass, FunctionReference.GetMemberName()))
				{
					SetFromFunction(NewFunction);
				}
			}
		}
	}
	// END TEMP

	Super::ReallocatePinsDuringReconstruction(OldPins);

	// Connect Execute and Then pins for functions, which became pure.
	ReconnectPureExecPins(OldPins);
}

UEdGraphPin* UK2Node_CallFunction::CreateSelfPin(const UFunction* Function)
{
	return FBlueprintNodeStatics::CreateSelfPin(this, Function);
}

void UK2Node_CallFunction::CreateExecPinsForFunctionCall(const UFunction* Function)
{
	bool bCreateSingleExecInputPin = true;
	bool bCreateThenPin = true;
	
	ExpandAsEnumPins.Reset();

	// If not pure, create exec pins
	if (!bIsPureFunc)
	{
		// If we want enum->exec expansion, and it is not disabled, do it now
		if(bWantsEnumToExecExpansion)
		{
			TArray<FName> EnumNames;
			GetExpandEnumPinNames(Function, EnumNames);

			FProperty* PreviousInput = nullptr;

			for (const FName& EnumParamName : EnumNames)
			{
				FProperty* Prop = nullptr;
				UEnum* Enum = nullptr;

				if (FByteProperty* ByteProp = FindFProperty<FByteProperty>(Function, EnumParamName))
				{
					Prop = ByteProp;
					Enum = ByteProp->Enum;
				}
				else if (FEnumProperty* EnumProp = FindFProperty<FEnumProperty>(Function, EnumParamName))
				{
					Prop = EnumProp;
					Enum = EnumProp->GetEnum();
				}
				else if (FBoolProperty* BoolProp = FindFProperty<FBoolProperty>(Function, EnumParamName))
				{
					Prop = BoolProp;
				}

				if (Prop != nullptr)
				{
					const bool bIsFunctionInput = !Prop->HasAnyPropertyFlags(CPF_ReturnParm) &&
						(!Prop->HasAnyPropertyFlags(CPF_OutParm) ||
						Prop->HasAnyPropertyFlags(CPF_ReferenceParm));
					const EEdGraphPinDirection Direction = bIsFunctionInput ? EGPD_Input : EGPD_Output;

					if (bIsFunctionInput)
					{
						if (PreviousInput)
						{
							bHasCompilerMessage = true;
							ErrorType = EMessageSeverity::Error;
							ErrorMsg = FString::Printf(TEXT("Parameter '%s' is listed as an ExpandEnumAsExecs input, but %s already was one. Only one is permitted."), *EnumParamName.ToString(), *PreviousInput->GetName());
							break;
						}
						PreviousInput = Prop;
					}

					if (Enum)
					{
						// yay, found it! Now create exec pin for each
						int32 NumExecs = (Enum->NumEnums() - 1);
						for (int32 ExecIdx = 0; ExecIdx < NumExecs; ExecIdx++)
						{
							bool const bShouldBeHidden = Enum->HasMetaData(TEXT("Hidden"), ExecIdx) || Enum->HasMetaData(TEXT("Spacer"), ExecIdx);
							if (!bShouldBeHidden)
							{
								// Can't use Enum->GetNameByIndex here because it doesn't do namespace mangling
								const FString NameStr = Enum->GetNameStringByIndex(ExecIdx);

								UEdGraphPin* CreatedPin = nullptr;

								// todo: really only makes sense if there are multiple outputs
								if (bIsFunctionInput || EnumNames.Num() == 1)
								{
									CreatedPin = CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, *NameStr);
								}
								else
								{
									CreatedPin = CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, *NameStr);
									CreatedPin->PinFriendlyName = FText::FromString(FString::Printf(TEXT("(%s) %s"), *Prop->GetDisplayNameText().ToString(), *NameStr));
								}

								ExpandAsEnumPins.Add(CreatedPin);

								if (Enum->HasMetaData(TEXT("Tooltip"), ExecIdx))
								{
									FString EnumTooltip = Enum->GetMetaData(TEXT("Tooltip"), ExecIdx);

									if (const UEdGraphSchema_K2* const K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema()))
									{
										K2Schema->ConstructBasicPinTooltip(*CreatedPin, FText::FromString(EnumTooltip), CreatedPin->PinToolTip);
									}
									else
									{
										CreatedPin->PinToolTip = EnumTooltip;
									}
								}
							}
						}
					}
					else
					{
						check(Prop->IsA<FBoolProperty>());
						// Create a pin for true and false, note that the order here does not match the
						// numeric order of bool, but it is more natural to put true first (e.g. to match branch node):
						ExpandAsEnumPins.Add(CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, TEXT("True")));
						ExpandAsEnumPins.Add(CreatePin(Direction, UEdGraphSchema_K2::PC_Exec, TEXT("False")));
					}

					if (bIsFunctionInput)
					{
						// If using ExpandEnumAsExec for input, don't want to add a input exec pin
						bCreateSingleExecInputPin = false;
					}
					else
					{
						// If using ExpandEnumAsExec for output, don't want to add a "then" pin
						bCreateThenPin = false;
					}
				}
			}
		}
		
		if (bCreateSingleExecInputPin)
		{
			// Single input exec pin
			CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
		}

		if (bCreateThenPin)
		{
			UEdGraphPin* OutputExecPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
			// Use 'completed' name for output pins on latent functions
			if (Function->HasMetaData(FBlueprintMetadata::MD_Latent))
			{
				OutputExecPin->PinFriendlyName = FText::FromName(UEdGraphSchema_K2::PN_Completed);
			}
		}
	}
}

FName UK2Node_CallFunction::GetFunctionName() const
{
	return FunctionReference.GetMemberName();
}

void UK2Node_CallFunction::DetermineWantsEnumToExecExpansion(const UFunction* Function)
{
	bWantsEnumToExecExpansion = false;

	if (WantsExecPinsForParams(Function))
	{
		TArray<FName> EnumNamesToCheck;
		GetExpandEnumPinNames(Function, EnumNamesToCheck);

		for (int32 i = EnumNamesToCheck.Num() - 1; i >= 0; --i)
		{
			const FName& EnumParamName = EnumNamesToCheck[i];

			FByteProperty* EnumProp = FindFProperty<FByteProperty>(Function, EnumParamName);
			if ((EnumProp != NULL && EnumProp->Enum != NULL) || FindFProperty<FEnumProperty>(Function, EnumParamName))
			{
				bWantsEnumToExecExpansion = true;
				EnumNamesToCheck.RemoveAt(i);
			}
			else
			{
				FBoolProperty* BoolProp = FindFProperty<FBoolProperty>(Function, EnumParamName);
				if (BoolProp)
				{
					bWantsEnumToExecExpansion = true;
					EnumNamesToCheck.RemoveAt(i);
				}
			}
		}
		
		if (bWantsEnumToExecExpansion && EnumNamesToCheck.Num() > 0 && !bHasCompilerMessage)
		{
			bHasCompilerMessage = true;
			ErrorType = EMessageSeverity::Warning;

			if (EnumNamesToCheck.Num() == 1)
			{
				ErrorMsg = FText::Format(LOCTEXT("EnumToExecExpansionFailedFmt", "Unable to find enum parameter with name '{0}' to expand for @@"), FText::FromName(EnumNamesToCheck[0])).ToString();
			}
			else
			{
				FString ParamNames;

				for (const FName& Name : EnumNamesToCheck)
				{
					if (!ParamNames.IsEmpty())
					{
						ParamNames += TEXT(", ");
					}

					ParamNames += Name.ToString();
				}

				ErrorMsg = FText::Format(LOCTEXT("EnumToExecExpansionFailedMultipleFmt", "Unable to find enum parameters for names:\n '{{0}}' \nto expand for @@"), FText::FromString(ParamNames)).ToString();
			}
		}
	}
}

void UK2Node_CallFunction::GetExpandEnumPinNames(const UFunction* Function, TArray<FName>& EnumNamesToCheck)
{ 
	EnumNamesToCheck.Reset();

	// todo: use metadatacache if/when that's accepted.
	const FString EnumParamString = GetAllExecParams(Function);

	TArray<FString> RawGroupings;
	EnumParamString.ParseIntoArray(RawGroupings, TEXT(","), false);
	for (FString& RawGroup : RawGroupings)
	{
		RawGroup.TrimStartAndEndInline();

		TArray<FString> IndividualEntries;
		RawGroup.ParseIntoArray(IndividualEntries, TEXT("|"));

		for (const FString& Entry : IndividualEntries)
		{
			if (Entry.IsEmpty())
			{
				continue;
			}

			EnumNamesToCheck.Add(*Entry);
		}
	}
}

void UK2Node_CallFunction::GeneratePinTooltip(UEdGraphPin& Pin) const
{
	ensure(Pin.GetOwningNode() == this);

	UEdGraphSchema const* Schema = GetSchema();
	check(Schema != NULL);
	UEdGraphSchema_K2 const* const K2Schema = Cast<const UEdGraphSchema_K2>(Schema);

	if (K2Schema == NULL)
	{
		Schema->ConstructBasicPinTooltip(Pin, FText::GetEmpty(), Pin.PinToolTip);
		return;
	}
	
	// get the class function object associated with this node
	UFunction* Function = GetTargetFunction();
	if (Function == NULL)
	{
		Schema->ConstructBasicPinTooltip(Pin, FText::GetEmpty(), Pin.PinToolTip);
		return;
	}


	GeneratePinTooltipFromFunction(Pin, Function);
}

bool UK2Node_CallFunction::CreatePinsForFunctionCall(const UFunction* Function)
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	UClass* FunctionOwnerClass = Function->GetOuterUClass();

	bIsInterfaceCall = FunctionOwnerClass->HasAnyClassFlags(CLASS_Interface);
	bIsPureFunc = (Function->HasAnyFunctionFlags(FUNC_BlueprintPure) != false);
	bIsConstFunc = (Function->HasAnyFunctionFlags(FUNC_Const) != false);
	DetermineWantsEnumToExecExpansion(Function);

	// Create input pins
	CreateExecPinsForFunctionCall(Function);

	UEdGraphPin* SelfPin = CreateSelfPin(Function);

	// Renamed self pin to target
	SelfPin->PinFriendlyName = LOCTEXT("Target", "Target");

	const bool bIsProtectedFunc = Function->GetBoolMetaData(FBlueprintMetadata::MD_Protected);
	const bool bIsStaticFunc = Function->HasAllFunctionFlags(FUNC_Static);

	UEdGraph const* const Graph = GetGraph();
	UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraph(Graph);
	ensure(BP);
	if (BP != nullptr)
	{
		const bool bIsFunctionCompatibleWithSelf = BP->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass);

		if (bIsStaticFunc)
		{
			// For static methods, wire up the self to the CDO of the class if it's not us
			if (!bIsFunctionCompatibleWithSelf)
			{
				UClass* AuthoritativeClass = FunctionOwnerClass->GetAuthoritativeClass();
				SelfPin->DefaultObject = AuthoritativeClass->GetDefaultObject();
			}

			// Purity doesn't matter with a static function, we can always hide the self pin since we know how to call the method
			SelfPin->bHidden = true;
		}
		else
		{
			if (Function->GetBoolMetaData(FBlueprintMetadata::MD_HideSelfPin))
			{
				SelfPin->bHidden = true;
				SelfPin->bNotConnectable = true;
			}
			else
			{
				// Hide the self pin if the function is compatible with the blueprint class and pure (the !bIsConstFunc portion should be going away soon too hopefully)
				SelfPin->bHidden = (bIsFunctionCompatibleWithSelf && bIsPureFunc && !bIsConstFunc);
			}
		}
	}

	// Build a list of the pins that should be hidden for this function (ones that are automagically filled in by the K2 compiler)
	TSet<FName> PinsToHide;
	TSet<FName> InternalPins;
	FBlueprintEditorUtils::GetHiddenPinsForFunction(Graph, Function, PinsToHide, &InternalPins);

	const bool bShowWorldContextPin = ((PinsToHide.Num() > 0) && BP && BP->ParentClass && BP->ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin));

	// Create the inputs and outputs
	bool bAllPinsGood = true;
	for (TFieldIterator<FProperty> PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
	{
		FProperty* Param = *PropIt;
		const bool bIsFunctionInput = !Param->HasAnyPropertyFlags(CPF_ReturnParm) && (!Param->HasAnyPropertyFlags(CPF_OutParm) || Param->HasAnyPropertyFlags(CPF_ReferenceParm));
		const bool bIsRefParam = Param->HasAnyPropertyFlags(CPF_ReferenceParm) && bIsFunctionInput;

		const EEdGraphPinDirection Direction = bIsFunctionInput ? EGPD_Input : EGPD_Output;

		UEdGraphNode::FCreatePinParams PinParams;
		PinParams.bIsReference = bIsRefParam;
		UEdGraphPin* Pin = CreatePin(Direction, NAME_None, Param->GetFName(), PinParams);
		const bool bPinGood = (Pin && K2Schema->ConvertPropertyToPinType(Param, /*out*/ Pin->PinType));

		if (bPinGood)
		{
			// Check for a display name override
			const FString& PinDisplayName = Param->GetMetaData(FBlueprintMetadata::MD_DisplayName);
			if (!PinDisplayName.IsEmpty())
			{
				Pin->PinFriendlyName = FText::FromString(PinDisplayName);
			}
			else if (Function->GetReturnProperty() == Param && Function->HasMetaData(FBlueprintMetadata::MD_ReturnDisplayName))
			{
				Pin->PinFriendlyName = Function->GetMetaDataText(FBlueprintMetadata::MD_ReturnDisplayName);
			}

			//Flag pin as read only for const reference property
			Pin->bDefaultValueIsIgnored = Param->HasAllPropertyFlags(CPF_ConstParm | CPF_ReferenceParm) && (!Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm) || Pin->PinType.IsContainer());

			const bool bAdvancedPin = Param->HasAllPropertyFlags(CPF_AdvancedDisplay);
			Pin->bAdvancedView = bAdvancedPin;
			if(bAdvancedPin && (ENodeAdvancedPins::NoPins == AdvancedPinDisplay))
			{
				AdvancedPinDisplay = ENodeAdvancedPins::Hidden;
			}

			FString ParamValue;
			if (K2Schema->FindFunctionParameterDefaultValue(Function, Param, ParamValue))
			{
				K2Schema->SetPinAutogeneratedDefaultValue(Pin, ParamValue);
			}
			else
			{
				K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(Pin);
			}
			
			if (PinsToHide.Contains(Pin->PinName))
			{
				const FString PinNameStr = Pin->PinName.ToString();
				const FString& DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
				const FString& WorldContextMetaValue  = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);
				bool bIsSelfPin = ((PinNameStr == DefaultToSelfMetaValue) || (PinNameStr == WorldContextMetaValue));

				if (!bShowWorldContextPin || !bIsSelfPin)
				{
					Pin->bHidden = true;
					Pin->bNotConnectable = InternalPins.Contains(Pin->PinName);
				}
			}

			PostParameterPinCreated(Pin);
		}

		bAllPinsGood = bAllPinsGood && bPinGood;
	}

	// If we have 'enum to exec' parameters, set their default value to something valid so we don't get warnings
	if(bWantsEnumToExecExpansion)
	{
		TArray<FName> EnumNamesToCheck;
		GetExpandEnumPinNames(Function, EnumNamesToCheck);

		for (const FName& Name : EnumNamesToCheck)
		{
			UEdGraphPin* EnumParamPin = FindPin(Name);
			if (UEnum* PinEnum = (EnumParamPin ? Cast<UEnum>(EnumParamPin->PinType.PinSubCategoryObject.Get()) : NULL))
			{
				EnumParamPin->DefaultValue = PinEnum->GetNameStringByIndex(0);
			}
		}
	}

	return bAllPinsGood;
}

void UK2Node_CallFunction::PostReconstructNode()
{
	Super::PostReconstructNode();
	InvalidatePinTooltips();

	// conform pins that are marked as SetParam:
	ConformContainerPins();

	FCustomStructureParamHelper::UpdateCustomStructurePins(GetTargetFunction(), this);

	// Fixup self node, may have been overridden from old self node
	UFunction* Function = GetTargetFunction();
	const bool bIsStaticFunc = Function ? Function->HasAllFunctionFlags(FUNC_Static) : false;

	UEdGraphPin* SelfPin = FindPin(UEdGraphSchema_K2::PN_Self);
	if (bIsStaticFunc && SelfPin)
	{
		// Wire up the self to the CDO of the class if it's not us
		if (UBlueprint* BP = GetBlueprint())
		{
			UClass* FunctionOwnerClass = Function->GetOuterUClass();
			if (!BP->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass))
			{
				SelfPin->DefaultObject = FunctionOwnerClass->GetAuthoritativeClass()->GetDefaultObject();
			}
			else
			{
				// In case a non-NULL reference was previously serialized on load, ensure that it's set to NULL here to match what a new node's self pin would be initialized as (see CreatePinsForFunctionCall).
				SelfPin->DefaultObject = nullptr;
			}
		}
	}

	if (UEdGraphPin* TypePickerPin = FDynamicOutputHelper::GetTypePickerPin(this))
	{
		FDynamicOutputHelper(TypePickerPin).ConformOutputType();
	}

	if (IsNodePure())
	{
		// Remove any pre-existing breakpoint on this node since pure nodes cannot have breakpoints
		if (UBreakpoint* ExistingBreakpoint = FKismetDebugUtilities::FindBreakpointForNode(GetBlueprint(), this))
		{
			// Remove the breakpoint
			FKismetDebugUtilities::StartDeletingBreakpoint(ExistingBreakpoint, GetBlueprint());
		}
	}
}

void UK2Node_CallFunction::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{
	Super::NotifyPinConnectionListChanged(Pin);

	// conform pins that are marked as SetParam:
	ConformContainerPins();

	if (!ensure(Pin))
	{
		return;
	}

	FCustomStructureParamHelper::UpdateCustomStructurePins(GetTargetFunction(), this, Pin);

	// Refresh the node to hide internal-only pins once the [invalid] connection has been broken
	if (Pin->bHidden && Pin->bNotConnectable && Pin->LinkedTo.Num() == 0)
	{
		GetGraph()->NotifyGraphChanged();
	}

	if (bIsBeadFunction)
	{
		if (Pin->LinkedTo.Num() == 0)
		{
			// Commit suicide; bead functions must always have an input and output connection
			DestroyNode();
		}
	}

	InvalidatePinTooltips();
	if(!Pin->IsPendingKill())
	{
		FDynamicOutputHelper(Pin).ConformOutputType();
	}
}

void UK2Node_CallFunction::PinDefaultValueChanged(UEdGraphPin* Pin)
{
	Super::PinDefaultValueChanged(Pin);
	InvalidatePinTooltips();
	FDynamicOutputHelper(Pin).ConformOutputType();
}

UFunction* UK2Node_CallFunction::GetTargetFunction() const
{
	if(!FBlueprintCompilationManager::IsGeneratedClassLayoutReady())
	{
		// first look in the skeleton class:
		if(UFunction* SkeletonFn = GetTargetFunctionFromSkeletonClass())
		{
			return SkeletonFn;
		}
	}

	UFunction* Function = FunctionReference.ResolveMember<UFunction>(GetBlueprintClassFromNode());
	return Function;
}

UFunction* UK2Node_CallFunction::GetTargetFunctionFromSkeletonClass() const
{
	UFunction* TargetFunction = nullptr;
	UClass* ParentClass = FunctionReference.GetMemberParentClass( GetBlueprintClassFromNode() );
	UBlueprint* OwningBP = ParentClass ? Cast<UBlueprint>( ParentClass->ClassGeneratedBy ) : nullptr;
	if( UClass* SkeletonClass = OwningBP ? OwningBP->SkeletonGeneratedClass : nullptr )
	{
		TargetFunction = SkeletonClass->FindFunctionByName( FunctionReference.GetMemberName() );
	}
	return TargetFunction;
}

UEdGraphPin* UK2Node_CallFunction::GetThenPin() const
{
	UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_Then);
	check(Pin == nullptr || Pin->Direction == EGPD_Output); // If pin exists, it must be output
	return Pin;
}

UEdGraphPin* UK2Node_CallFunction::GetReturnValuePin() const
{
	UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_ReturnValue);
	check(Pin == nullptr || Pin->Direction == EGPD_Output); // If pin exists, it must be output
	return Pin;
}

bool UK2Node_CallFunction::IsLatentFunction() const
{
	if (UFunction* Function = GetTargetFunction())
	{
		if (Function->HasMetaData(FBlueprintMetadata::MD_Latent))
		{
			return true;
		}
	}

	return false;
}

bool UK2Node_CallFunction::AllowMultipleSelfs(bool bInputAsArray) const
{
	if (UFunction* Function = GetTargetFunction())
	{
		return CanFunctionSupportMultipleTargets(Function);
	}

	return Super::AllowMultipleSelfs(bInputAsArray);
}

bool UK2Node_CallFunction::CanFunctionSupportMultipleTargets(UFunction const* Function)
{
	bool const bIsImpure = !Function->HasAnyFunctionFlags(FUNC_BlueprintPure);
	bool const bIsLatent = Function->HasMetaData(FBlueprintMetadata::MD_Latent);
	bool const bHasReturnParam = (Function->GetReturnProperty() != nullptr);

	return !bHasReturnParam && bIsImpure && !bIsLatent;
}

bool UK2Node_CallFunction::CanPasteHere(const UEdGraph* TargetGraph) const
{
	// Basic check for graph compatibility, etc.
	bool bCanPaste = Super::CanPasteHere(TargetGraph);

	// We check function context for placability only in the base class case; derived classes are typically bound to
	// specific functions that should always be placeable, but may not always be explicitly callable (e.g. InternalUseOnly).
	if(bCanPaste && GetClass() == StaticClass())
	{
		const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
		uint32 AllowedFunctionTypes = UEdGraphSchema_K2::EFunctionType::FT_Pure | UEdGraphSchema_K2::EFunctionType::FT_Const | UEdGraphSchema_K2::EFunctionType::FT_Protected;
		if(K2Schema->DoesGraphSupportImpureFunctions(TargetGraph))
		{
			AllowedFunctionTypes |= UEdGraphSchema_K2::EFunctionType::FT_Imperative;
		}
		UFunction* TargetFunction = GetTargetFunction();
		if( !TargetFunction )
		{
			TargetFunction = GetTargetFunctionFromSkeletonClass();
		}
		if (!TargetFunction)
		{
			// If the function doesn't exist and it is from self context, then it could be created from a CustomEvent node, that was also pasted (but wasn't compiled yet).
			bCanPaste = FunctionReference.IsSelfContext();
		}
		else
		{
			bCanPaste = K2Schema->CanFunctionBeUsedInGraph(FBlueprintEditorUtils::FindBlueprintForGraphChecked(TargetGraph)->GeneratedClass, TargetFunction, TargetGraph, AllowedFunctionTypes, false);
		}
	}
	
	return bCanPaste;
}

bool UK2Node_CallFunction::IsActionFilteredOut(FBlueprintActionFilter const& Filter)
{
	bool bIsFilteredOut = false;
	for(UEdGraph* TargetGraph : Filter.Context.Graphs)
	{
		bIsFilteredOut |= !CanPasteHere(TargetGraph);
	}

	if(const UFunction* TargetFunction = GetTargetFunction())
	{
		const bool bIsProtected = (TargetFunction->FunctionFlags & FUNC_Protected) != 0;
		const bool bIsPrivate = (TargetFunction->FunctionFlags & FUNC_Private) != 0;
		const UClass* OwningClass = TargetFunction->GetOwnerClass();
		if( (bIsProtected || bIsPrivate) && !FBlueprintEditorUtils::IsNativeSignature(TargetFunction) && OwningClass)
		{
			OwningClass = OwningClass->GetAuthoritativeClass();
			// we can filter private and protected blueprints that are unrelated:
			bool bAccessibleInAll = true;
			for (const UBlueprint* Blueprint : Filter.Context.Blueprints)
			{
				UClass* AuthoritativeClass = Blueprint->GeneratedClass;
				if(!AuthoritativeClass)
				{
					continue;
				}

				if(bIsPrivate)
				{
					bAccessibleInAll = bAccessibleInAll && AuthoritativeClass == OwningClass;
				}
				else if(bIsProtected)
				{
					bAccessibleInAll = bAccessibleInAll && AuthoritativeClass->IsChildOf(OwningClass);
				}
			}

			if(!bAccessibleInAll)
			{
				bIsFilteredOut = true;
			}
		}
	}

	return bIsFilteredOut;
}

static FLinearColor GetPalletteIconColor(UFunction const* Function)
{
	bool const bIsPure = (Function != nullptr) && Function->HasAnyFunctionFlags(FUNC_BlueprintPure);
	if (bIsPure)
	{
		return GetDefault<UGraphEditorSettings>()->PureFunctionCallNodeTitleColor;
	}
	return GetDefault<UGraphEditorSettings>()->FunctionCallNodeTitleColor;
}

FSlateIcon UK2Node_CallFunction::GetPaletteIconForFunction(UFunction const* Function, FLinearColor& OutColor)
{
	static const FName NativeMakeFunc(TEXT("NativeMakeFunc"));
	static const FName NativeBrakeFunc(TEXT("NativeBreakFunc"));

	if (Function && Function->HasMetaData(NativeMakeFunc))
	{
		static FSlateIcon Icon("EditorStyle", "GraphEditor.MakeStruct_16x");
		return Icon;
	}
	else if (Function && Function->HasMetaData(NativeBrakeFunc))
	{
		static FSlateIcon Icon("EditorStyle", "GraphEditor.BreakStruct_16x");
		return Icon;
	}
	// Check to see if the function is calling an function that could be an event, display the event icon instead.
	else if (Function && UEdGraphSchema_K2::FunctionCanBePlacedAsEvent(Function))
	{
		static FSlateIcon Icon("EditorStyle", "GraphEditor.Event_16x");
		return Icon;
	}
	else
	{
		OutColor = GetPalletteIconColor(Function);

		static FSlateIcon Icon("EditorStyle", "Kismet.AllClasses.FunctionIcon");
		return Icon;
	}
}

FLinearColor UK2Node_CallFunction::GetNodeTitleColor() const
{
	return GetPalletteIconColor(GetTargetFunction());
}

FText UK2Node_CallFunction::GetTooltipText() const
{
	FText Tooltip;

	UFunction* Function = GetTargetFunction();
	if (Function == nullptr)
	{
		return FText::Format(LOCTEXT("CallUnknownFunction", "Call unknown function {0}"), FText::FromName(FunctionReference.GetMemberName()));
	}
	else if (CachedTooltip.IsOutOfDate(this))
	{
		FText BaseTooltip = FText::FromString(GetDefaultTooltipForFunction(Function));

		FFormatNamedArguments Args;
		Args.Add(TEXT("DefaultTooltip"), BaseTooltip);

		if (Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly))
		{
			Args.Add(
				TEXT("ClientString"),
				NSLOCTEXT("K2Node", "ServerFunction", "Authority Only. This function will only execute on the server.")
			);
			// FText::Format() is slow, so we cache this to save on performance
			CachedTooltip.SetCachedText(FText::Format(LOCTEXT("CallFunction_SubtitledTooltip", "{DefaultTooltip}\n\n{ClientString}"), Args), this);
		}
		else if (Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic))
		{
			Args.Add(
				TEXT("ClientString"),
				NSLOCTEXT("K2Node", "ClientFunction", "Cosmetic. This event is only for cosmetic, non-gameplay actions.")
			);
			// FText::Format() is slow, so we cache this to save on performance
			CachedTooltip.SetCachedText(FText::Format(LOCTEXT("CallFunction_SubtitledTooltip", "{DefaultTooltip}\n\n{ClientString}"), Args), this);
		} 
		else
		{
			CachedTooltip.SetCachedText(BaseTooltip, this);
		}
	}
	return CachedTooltip;
}

void UK2Node_CallFunction::GeneratePinTooltipFromFunction(UEdGraphPin& Pin, const UFunction* Function)
{
	if (Pin.bWasTrashed)
	{
		return;
	}

	// figure what tag we should be parsing for (is this a return-val pin, or a parameter?)
	FString ParamName;
	FString TagStr = TEXT("@param");
	const bool bReturnPin = Pin.PinName == UEdGraphSchema_K2::PN_ReturnValue;
	if (bReturnPin)
	{
		TagStr = TEXT("@return");
	}
	else
	{
		ParamName = Pin.PinName.ToString();
	}

	// grab the the function's comment block for us to parse
	FString FunctionToolTipText = Function->GetToolTipText().ToString();
	
	int32 CurStrPos = INDEX_NONE;
	int32 FullToolTipLen = FunctionToolTipText.Len();
	// parse the full function tooltip text, looking for tag lines
	do 
	{
		CurStrPos = FunctionToolTipText.Find(TagStr, ESearchCase::IgnoreCase, ESearchDir::FromStart, CurStrPos);
		if (CurStrPos == INDEX_NONE) // if the tag wasn't found
		{
			break;
		}

		// advance past the tag
		CurStrPos += TagStr.Len();

		// handle people having done @returns instead of @return
		if (bReturnPin && CurStrPos < FullToolTipLen && FunctionToolTipText[CurStrPos] == TEXT('s'))
		{
			++CurStrPos;
		}

		// advance past whitespace
		while(CurStrPos < FullToolTipLen && FChar::IsWhitespace(FunctionToolTipText[CurStrPos]))
		{
			++CurStrPos;
		}

		// if this is a parameter pin
		if (!ParamName.IsEmpty())
		{
			FString TagParamName;

			// copy the parameter name
			while (CurStrPos < FullToolTipLen && !FChar::IsWhitespace(FunctionToolTipText[CurStrPos]))
			{
				TagParamName.AppendChar(FunctionToolTipText[CurStrPos++]);
			}

			// if this @param tag doesn't match the param we're looking for
			if (TagParamName != ParamName)
			{
				continue;
			}
		}

		// advance past whitespace (get to the meat of the comment)
		// since many doxygen style @param use the format "@param <param name> - <comment>" we also strip - if it is before we get to any other non-whitespace
		while(CurStrPos < FullToolTipLen && (FChar::IsWhitespace(FunctionToolTipText[CurStrPos]) || FunctionToolTipText[CurStrPos] == '-'))
		{
			++CurStrPos;
		}


		FString ParamDesc;
		// collect the param/return-val description
		while (CurStrPos < FullToolTipLen && FunctionToolTipText[CurStrPos] != TEXT('@'))
		{
			// advance past newline
			while(CurStrPos < FullToolTipLen && FChar::IsLinebreak(FunctionToolTipText[CurStrPos]))
			{
				++CurStrPos;

				// advance past whitespace at the start of a new line
				while(CurStrPos < FullToolTipLen && FChar::IsWhitespace(FunctionToolTipText[CurStrPos]))
				{
					++CurStrPos;
				}

				// replace the newline with a single space
				if(CurStrPos < FullToolTipLen && !FChar::IsLinebreak(FunctionToolTipText[CurStrPos]))
				{
					ParamDesc.AppendChar(TEXT(' '));
				}
			}

			if (CurStrPos < FullToolTipLen && FunctionToolTipText[CurStrPos] != TEXT('@'))
			{
				ParamDesc.AppendChar(FunctionToolTipText[CurStrPos++]);
			}
		}

		// trim any trailing whitespace from the descriptive text
		ParamDesc.TrimEndInline();

		// if we came up with a valid description for the param/return-val
		if (!ParamDesc.IsEmpty())
		{
			Pin.PinToolTip += ParamDesc;
			break; // we found a match, so there's no need to continue
		}

	} while (CurStrPos < FullToolTipLen);

	// If we have no parameter or return value descriptions the full description will be relevant in describing the return value:
	if( bReturnPin && 
		Pin.PinToolTip.IsEmpty() && 
		FunctionToolTipText.Find(TEXT("@param")) == INDEX_NONE && 
		FunctionToolTipText.Find(TEXT("@return")) == INDEX_NONE)
	{
		// for the return pin, default to using the function description if no @return tag was provided:
		Pin.PinToolTip = Function->GetToolTipText().ToString();
	}

	GetDefault<UEdGraphSchema_K2>()->ConstructBasicPinTooltip(Pin, FText::FromString(Pin.PinToolTip), Pin.PinToolTip);
}

FText UK2Node_CallFunction::GetUserFacingFunctionName(const UFunction* Function)
{
	FText ReturnDisplayName;

	if (Function != NULL)
	{
		if (GEditor && GetDefault<UEditorStyleSettings>()->bShowFriendlyNames)
		{
			ReturnDisplayName = Function->GetDisplayNameText();
		}
		else
		{
			static const FString Namespace = TEXT("UObjectDisplayNames");
			const FString Key = Function->GetFullGroupName(false);

			ReturnDisplayName = Function->GetMetaDataText(TEXT("DisplayName"), Namespace, Key);
		}
	}
	return ReturnDisplayName;
}

FString UK2Node_CallFunction::GetDefaultTooltipForFunction(const UFunction* Function)
{
	FString Tooltip;

	if (Function != NULL)
	{
		Tooltip = Function->GetToolTipText().ToString();
	}

	if (!Tooltip.IsEmpty())
	{
		// Strip off the doxygen nastiness
		static const FString DoxygenParam(TEXT("@param"));
		static const FString DoxygenReturn(TEXT("@return"));
		static const FString DoxygenSee(TEXT("@see"));
		static const FString TooltipSee(TEXT("See:"));
		static const FString DoxygenNote(TEXT("@note"));
		static const FString TooltipNote(TEXT("Note:"));

		Tooltip.Split(DoxygenParam, &Tooltip, nullptr, ESearchCase::IgnoreCase, ESearchDir::FromStart);
		Tooltip.Split(DoxygenReturn, &Tooltip, nullptr, ESearchCase::IgnoreCase, ESearchDir::FromStart);

		Tooltip.ReplaceInline(*DoxygenSee, *TooltipSee);
		Tooltip.ReplaceInline(*DoxygenNote, *TooltipNote);

		Tooltip.TrimStartAndEndInline();

		UClass* CurrentSelfClass = (Function != NULL) ? Function->GetOwnerClass() : NULL;
		UClass const* TrueSelfClass = CurrentSelfClass;
		if (CurrentSelfClass && CurrentSelfClass->ClassGeneratedBy)
		{
			TrueSelfClass = CurrentSelfClass->GetAuthoritativeClass();
		}

		FText TargetDisplayText = (TrueSelfClass != NULL) ? TrueSelfClass->GetDisplayNameText() : LOCTEXT("None", "None");

		FFormatNamedArguments Args;
		Args.Add(TEXT("TargetName"), TargetDisplayText);
		Args.Add(TEXT("Tooltip"), FText::FromString(Tooltip));
		return FText::Format(LOCTEXT("CallFunction_Tooltip", "{Tooltip}\n\nTarget is {TargetName}"), Args).ToString();
	}
	else
	{
		return GetUserFacingFunctionName(Function).ToString();
	}
}

FText UK2Node_CallFunction::GetDefaultCategoryForFunction(const UFunction* Function, const FText& BaseCategory)
{
	FText NodeCategory = BaseCategory;
	if( Function->HasMetaData(FBlueprintMetadata::MD_FunctionCategory) )
	{
		FText FuncCategory;
		// If we are not showing friendly names, return the metadata stored, without localization
		if( GEditor && !GetDefault<UEditorStyleSettings>()->bShowFriendlyNames )
		{
			FuncCategory = FText::FromString(Function->GetMetaData(FBlueprintMetadata::MD_FunctionCategory));
		}
		else
		{
			// Look for localized metadata
			FuncCategory = FObjectEditorUtils::GetCategoryText(Function);

			// If the result is culture invariant, force it into a display string
			if (FuncCategory.IsCultureInvariant())
			{
				FuncCategory = FText::FromString(FName::NameToDisplayString(FuncCategory.ToString(), false));
			}
		}

		// Combine with the BaseCategory to form the full category, delimited by "|"
		if (!FuncCategory.IsEmpty() && !NodeCategory.IsEmpty())
		{
			NodeCategory = FText::Format(FText::FromString(TEXT("{0}|{1}")), NodeCategory, FuncCategory);
		}
		else if (NodeCategory.IsEmpty())
		{
			NodeCategory = FuncCategory;
		}
	}
	return NodeCategory;
}


FText UK2Node_CallFunction::GetKeywordsForFunction(const UFunction* Function)
{
	// If the friendly name and real function name do not match add the real function name friendly name as a keyword.
	FString Keywords;
	if( Function->GetName() != GetUserFacingFunctionName(Function).ToString() )
	{
		Keywords = Function->GetName();
	}

	if (ShouldDrawCompact(Function))
	{
		Keywords.AppendChar(TEXT(' '));
		Keywords += GetCompactNodeTitle(Function);
	}

	FText MetadataKeywords = Function->GetMetaDataText(FBlueprintMetadata::MD_FunctionKeywords, TEXT("UObjectKeywords"), Function->GetFullGroupName(false));
	FText ResultKeywords;

	if (!MetadataKeywords.IsEmpty())
	{
		FFormatNamedArguments Args;
		Args.Add(TEXT("Name"), FText::FromString(Keywords));
		Args.Add(TEXT("MetadataKeywords"), MetadataKeywords);
		ResultKeywords = FText::Format(FText::FromString("{Name} {MetadataKeywords}"), Args);
	}
	else
	{
		ResultKeywords = FText::FromString(Keywords);
	}

	return ResultKeywords;
}

void UK2Node_CallFunction::SetFromFunction(const UFunction* Function)
{
	if (Function != NULL)
	{
		bIsPureFunc = Function->HasAnyFunctionFlags(FUNC_BlueprintPure);
		bIsConstFunc = Function->HasAnyFunctionFlags(FUNC_Const);
		DetermineWantsEnumToExecExpansion(Function);

		FunctionReference.SetFromField<UFunction>(Function, GetBlueprintClassFromNode());
	}
}

FString UK2Node_CallFunction::GetDocumentationLink() const
{
	UClass* ParentClass = NULL;
	if (FunctionReference.IsSelfContext())
	{
		if (HasValidBlueprint())
		{
			UFunction* Function = FindUField<UFunction>(GetBlueprint()->GeneratedClass, FunctionReference.GetMemberName());
			if (Function != NULL)
			{
				ParentClass = Function->GetOwnerClass();
			}
		}		
	}
	else 
	{
		ParentClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());
	}
	
	if (ParentClass != NULL)
	{
		return FString::Printf(TEXT("Shared/GraphNodes/Blueprint/%s%s"), ParentClass->GetPrefixCPP(), *ParentClass->GetName());
	}

	return FString("Shared/GraphNodes/Blueprint/UK2Node_CallFunction");
}

FString UK2Node_CallFunction::GetDocumentationExcerptName() const
{
	return FunctionReference.GetMemberName().ToString();
}

FString UK2Node_CallFunction::GetDescriptiveCompiledName() const
{
	return FString(TEXT("CallFunc_")) + FunctionReference.GetMemberName().ToString();
}

bool UK2Node_CallFunction::ShouldDrawCompact(const UFunction* Function)
{
	return (Function != NULL) && Function->HasMetaData(FBlueprintMetadata::MD_CompactNodeTitle);
}

bool UK2Node_CallFunction::ShouldDrawCompact() const
{
	UFunction* Function = GetTargetFunction();

	return ShouldDrawCompact(Function);
}

bool UK2Node_CallFunction::ShouldDrawAsBead() const
{
	return bIsBeadFunction;
}

bool UK2Node_CallFunction::ShouldShowNodeProperties() const
{
	// Show node properties if this corresponds to a function graph
	if (FunctionReference.GetMemberName() != NAME_None && HasValidBlueprint())
	{
		return FindObject<UEdGraph>(GetBlueprint(), *(FunctionReference.GetMemberName().ToString())) != NULL;
	}
	return false;
}

FString UK2Node_CallFunction::GetCompactNodeTitle(const UFunction* Function)
{
	static const FString ProgrammerMultiplicationSymbol = TEXT("*");
	static const FString CommonMultiplicationSymbol = TEXT("\xD7");

	static const FString ProgrammerDivisionSymbol = TEXT("/");
	static const FString CommonDivisionSymbol = TEXT("\xF7");

	static const FString ProgrammerConversionSymbol = TEXT("->");
	static const FString CommonConversionSymbol = TEXT("\x2022");

	const FString& OperatorTitle = Function->GetMetaData(FBlueprintMetadata::MD_CompactNodeTitle);
	if (!OperatorTitle.IsEmpty())
	{
		if (OperatorTitle == ProgrammerMultiplicationSymbol)
		{
			return CommonMultiplicationSymbol;
		}
		else if (OperatorTitle == ProgrammerDivisionSymbol)
		{
			return CommonDivisionSymbol;
		}
		else if (OperatorTitle == ProgrammerConversionSymbol)
		{
			return CommonConversionSymbol;
		}
		else
		{
			return OperatorTitle;
		}
	}
	
	return Function->GetName();
}

FText UK2Node_CallFunction::GetCompactNodeTitle() const
{
	UFunction* Function = GetTargetFunction();
	if (Function != NULL)
	{
		return FText::FromString(GetCompactNodeTitle(Function));
	}
	else
	{
		return Super::GetCompactNodeTitle();
	}
}

void UK2Node_CallFunction::GetRedirectPinNames(const UEdGraphPin& Pin, TArray<FString>& RedirectPinNames) const
{
	Super::GetRedirectPinNames(Pin, RedirectPinNames);

	if (RedirectPinNames.Num() > 0)
	{
		const FString OldPinName = RedirectPinNames[0];

		// first add functionname.param
		RedirectPinNames.Add(FString::Printf(TEXT("%s.%s"), *FunctionReference.GetMemberName().ToString(), *OldPinName));

		// if there is class, also add an option for class.functionname.param
		UClass* FunctionClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());
		while (FunctionClass)
		{
			RedirectPinNames.Add(FString::Printf(TEXT("%s.%s.%s"), *FunctionClass->GetName(), *FunctionReference.GetMemberName().ToString(), *OldPinName));
			FunctionClass = FunctionClass->GetSuperClass();
		}
	}
}

void UK2Node_CallFunction::FixupSelfMemberContext()
{
	UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNode(this);
	auto IsBlueprintOfType = [Blueprint](UClass* ClassType)->bool
	{
		bool bIsChildOf  = Blueprint && (Blueprint->GeneratedClass != nullptr) && Blueprint->GeneratedClass->IsChildOf(ClassType);
		if (!bIsChildOf && Blueprint && (Blueprint->SkeletonGeneratedClass))
		{
			bIsChildOf = Blueprint->SkeletonGeneratedClass->IsChildOf(ClassType);
		}
		return bIsChildOf;
	};

	UClass* MemberClass = FunctionReference.GetMemberParentClass();
	if (FunctionReference.IsSelfContext())
	{
		// if there is a function that matches the reference in the new context
		// and there are no connections to the self pin, we just want to call
		// that function
		UEdGraphPin* SelfPin = GetDefault<UEdGraphSchema_K2>()->FindSelfPin(*this, EGPD_Input);
		if (!FunctionReference.ResolveMember<UFunction>(Blueprint) || (SelfPin && SelfPin->HasAnyConnections()))
		{
			if (MemberClass == nullptr)
			{
				// the self pin may have type information stored on it
				if (SelfPin)
				{
					MemberClass = Cast<UClass>(SelfPin->PinType.PinSubCategoryObject.Get());
				}
			}
			// if we happened to retain the ParentClass for a self reference 
			// (unlikely), then we know where this node came from... let's keep it
			// referencing that function
			if (MemberClass != nullptr)
			{
				if (!IsBlueprintOfType(MemberClass))
				{
					FunctionReference.SetExternalMember(FunctionReference.GetMemberName(), MemberClass);
				}
			}
			// else, there is nothing we can do... the node will produce an error later during compilation
		}
	}
	else if (MemberClass != nullptr)
	{
		if (IsBlueprintOfType(MemberClass))
		{
			FunctionReference.SetSelfMember(FunctionReference.GetMemberName());
		}
	}
}

void UK2Node_CallFunction::PostPasteNode()
{
	Super::PostPasteNode();
	FixupSelfMemberContext();

	if (UFunction* Function = GetTargetFunction())
	{
		if (Pins.Num() > 0)
		{
			// After pasting we need to go through and ensure the hidden the self pins is correct in case the source blueprint had different metadata
			TSet<FName> PinsToHide;
			FBlueprintEditorUtils::GetHiddenPinsForFunction(GetGraph(), Function, PinsToHide);

			const bool bShowWorldContextPin = ((PinsToHide.Num() > 0) && GetBlueprint()->ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin));

			const FString& DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
			const FString& WorldContextMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);

			const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
			for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex)
			{
				UEdGraphPin* Pin = Pins[PinIndex];
					const FString PinNameStr = Pin->PinName.ToString();

					const bool bIsSelfPin = ((PinNameStr == DefaultToSelfMetaValue) || (PinNameStr == WorldContextMetaValue));
					const bool bPinShouldBeHidden = ((Pin->SubPins.Num() > 0) || (PinsToHide.Contains(Pin->PinName) && (!bShowWorldContextPin || !bIsSelfPin)));

				if (bPinShouldBeHidden && !Pin->bHidden)
				{
					Pin->BreakAllPinLinks();
					K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(Pin);
				}
				Pin->bHidden = bPinShouldBeHidden;
			}
		}
	}
}

void UK2Node_CallFunction::PostDuplicate(bool bDuplicateForPIE)
{
	Super::PostDuplicate(bDuplicateForPIE);
	if (!bDuplicateForPIE && (!this->HasAnyFlags(RF_Transient)))
	{
		FixupSelfMemberContext();
	}
}

void UK2Node_CallFunction::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
	Super::ValidateNodeDuringCompilation(MessageLog);

	const UBlueprint* Blueprint = GetBlueprint();
	UFunction *Function = GetTargetFunction();
	if (Function == NULL)
	{
		FString OwnerName;

		if (Blueprint != nullptr)
		{
			OwnerName = Blueprint->GetName();
			if (UClass* FuncOwnerClass = FunctionReference.GetMemberParentClass(Blueprint->GeneratedClass))
			{
				OwnerName = FuncOwnerClass->GetName();
			}
		}
		FString const FunctName = FunctionReference.GetMemberName().ToString();

		FText const WarningFormat = LOCTEXT("FunctionNotFoundFmt", "Could not find a function named \"{0}\" in '{1}'.\nMake sure '{2}' has been compiled for @@");
		MessageLog.Error(*FText::Format(WarningFormat, FText::FromString(FunctName), FText::FromString(OwnerName), FText::FromString(OwnerName)).ToString(), this);
	}
	else if (WantsExecPinsForParams(Function) && bWantsEnumToExecExpansion == false)
	{
		// will technically not have a properly formatted output for multiple params... but /shrug. 
		const FString EnumParamName = GetAllExecParams(Function);
		MessageLog.Warning(*FText::Format(LOCTEXT("EnumToExecExpansionFailedFmt", "Unable to find enum parameter with name '{0}' to expand for @@"), FText::FromString(EnumParamName)).ToString(), this);
	}

	const UClass* BlueprintClass = Blueprint ? Blueprint->ParentClass : nullptr;
	const bool bIsEditorOnlyBlueprintBaseClass = !BlueprintClass || IsEditorOnlyObject(BlueprintClass);

	// This error is disabled while we figure out how we can identify uncooked only
	// blueprints that want to make use of uncooked only APIs:
	#if 0
	const bool bIsUncookedOnlyFunction = Function && Function->GetOutermost()->HasAllPackagesFlags(PKG_UncookedOnly);
	if (	bIsUncookedOnlyFunction &&
			// Only allow calls to uncooked only functions from editor only/uncooked only
			// contexts:
			!(	GetOutermost()->HasAnyPackageFlags(PKG_UncookedOnly|PKG_EditorOnly) ||
				bIsEditorOnlyBlueprintBaseClass ))
	{
		MessageLog.Error(*LOCTEXT("UncookedOnlyError", "Attempting to call uncooked only function @@ in runtime blueprint").ToString(), this);
	}
	#endif //0
	
	// Ensure that editor module BP exposed UFunctions can only be called in blueprints for which the base class is also part of an editor module
	// Also check for functions wrapped in WITH_EDITOR 
	if (Function && Blueprint &&
		(IsEditorOnlyObject(Function) || Function->HasAnyFunctionFlags(FUNC_EditorOnly)))
	{	
		if (!bIsEditorOnlyBlueprintBaseClass)
		{
			FString const FunctName = Function->GetName();
			FText const WarningFormat = LOCTEXT("EditorFunctionFmt", "Cannot use the editor function \"{0}\" in this runtime Blueprint. Only for use in Editor Utility Blueprints and Blutilities.");
			MessageLog.Error(*FText::Format(WarningFormat, FText::FromString(FunctName)).ToString(), this);
		}
	}

	if (Function)
	{
		// enforce UnsafeDuringActorConstruction keyword
		if (Function->HasMetaData(FBlueprintMetadata::MD_UnsafeForConstructionScripts))
		{
			// emit warning if we are in a construction script
			UEdGraph const* const Graph = GetGraph();
			bool bNodeIsInConstructionScript = UEdGraphSchema_K2::IsConstructionScript(Graph);

			if (bNodeIsInConstructionScript == false)
			{
				// IsConstructionScript() can return false if graph was cloned from the construction script
				// in that case, check the function entry
				TArray<const UK2Node_FunctionEntry*> EntryPoints;
				Graph->GetNodesOfClass(EntryPoints);

				if (EntryPoints.Num() == 1)
				{
					UK2Node_FunctionEntry const* const Node = EntryPoints[0];
					if (Node)
					{
						UFunction* const SignatureFunction = Node->FunctionReference.ResolveMember<UFunction>(Node->GetBlueprintClassFromNode());
						bNodeIsInConstructionScript = SignatureFunction && (SignatureFunction->GetFName() == UEdGraphSchema_K2::FN_UserConstructionScript);
					}
				}
			}

			if ( bNodeIsInConstructionScript )
			{
				MessageLog.Warning(*LOCTEXT("FunctionUnsafeDuringConstruction", "Function '@@' is unsafe to call in a construction script.").ToString(), this);
			}
		}

		// enforce WorldContext restrictions
		const bool bInsideBpFuncLibrary = Blueprint && (BPTYPE_FunctionLibrary == Blueprint->BlueprintType);
		if (!bInsideBpFuncLibrary && 
			Function->HasMetaData(FBlueprintMetadata::MD_WorldContext) && 
			!Function->HasMetaData(FBlueprintMetadata::MD_CallableWithoutWorldContext))
		{
			check(Blueprint);
			UClass* ParentClass = Blueprint->ParentClass;
			check(ParentClass);
			if (ParentClass && !FBlueprintEditorUtils::ImplementsGetWorld(Blueprint) && !ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin))
			{
				MessageLog.Warning(*LOCTEXT("FunctionUnsafeInContext", "Function '@@' is unsafe to call from blueprints of class '@@'.").ToString(), this, ParentClass);
			}
		}

		if(Blueprint && !FBlueprintEditorUtils::IsNativeSignature(Function))
		{
			// enforce protected function restriction
			const bool bCanTreatAsError = Blueprint->GetLinkerCustomVersion(FFrameworkObjectVersion::GUID) >= FFrameworkObjectVersion::EnforceBlueprintFunctionVisibility;

			const bool bIsProtected = (Function->FunctionFlags & FUNC_Protected) != 0;
			const bool bFuncBelongsToSubClass = Blueprint->SkeletonGeneratedClass->IsChildOf(Function->GetOuterUClass());
			if (bIsProtected && !bFuncBelongsToSubClass)
			{
				if(bCanTreatAsError)
				{
					MessageLog.Error(*LOCTEXT("FunctionProtectedAccessed", "Function '@@' is protected and can't be accessed outside of its hierarchy.").ToString(), this);
				}
				else
				{
					MessageLog.Note(*LOCTEXT("FunctionProtectedAccessedNote", "Function '@@' is protected and can't be accessed outside of its hierarchy - this will be an error if the asset is resaved.").ToString(), this);
				}
			}

			// enforce private function restriction
			const bool bIsPrivate = (Function->FunctionFlags & FUNC_Private) != 0;
			const bool bFuncBelongsToClass = bFuncBelongsToSubClass && (Blueprint->SkeletonGeneratedClass == Function->GetOuterUClass());
			if (bIsPrivate && !bFuncBelongsToClass)
			{
				if(bCanTreatAsError)
				{
					MessageLog.Error(*LOCTEXT("FunctionPrivateAccessed", "Function '@@' is private and can't be accessed outside of its defined class '@@'.").ToString(), this, Function->GetOuterUClass());
				}
				else
				{
					MessageLog.Note(*LOCTEXT("FunctionPrivateAccessedNote", "Function '@@' is private and can't be accessed outside of its defined class '@@' - this will be an error if the asset is resaved.").ToString(), this, Function->GetOuterUClass());
				}
			}
		}
	}

	FDynamicOutputHelper::VerifyNode(this, MessageLog);

	for (UEdGraphPin* Pin : Pins)
	{
		if (Pin && Pin->PinType.bIsWeakPointer && !Pin->PinType.IsContainer())
		{
			const FString ErrorString = FText::Format(
				LOCTEXT("WeakPtrNotSupportedErrorFmt", "Weak pointers are not supported as function parameters. Pin '{0}' @@"),
				FText::FromString(Pin->GetName())
			).ToString();
			MessageLog.Error(*ErrorString, this);
		}
	}
}

void UK2Node_CallFunction::Serialize(FArchive& Ar)
{
	Super::Serialize(Ar);

	Ar.UsingCustomVersion(FReleaseObjectVersion::GUID);

	if (Ar.IsLoading())
	{
		if (Ar.UE4Ver() < VER_UE4_SWITCH_CALL_NODE_TO_USE_MEMBER_REFERENCE)
		{
			UFunction* Function = FindUField<UFunction>(CallFunctionClass_DEPRECATED, CallFunctionName_DEPRECATED);
			const bool bProbablySelfCall = (CallFunctionClass_DEPRECATED == NULL) || ((Function != NULL) && (Function->GetOuterUClass()->ClassGeneratedBy == GetBlueprint()));

			FunctionReference.SetDirect(CallFunctionName_DEPRECATED, FGuid(), CallFunctionClass_DEPRECATED, bProbablySelfCall);
		}

		if(Ar.UE4Ver() < VER_UE4_K2NODE_REFERENCEGUIDS)
		{
			FGuid FunctionGuid;

			if (UBlueprint::GetGuidFromClassByFieldName<UFunction>(GetBlueprint()->GeneratedClass, FunctionReference.GetMemberName(), FunctionGuid))
			{
				const bool bSelf = FunctionReference.IsSelfContext();
				FunctionReference.SetDirect(FunctionReference.GetMemberName(), FunctionGuid, (bSelf ? NULL : FunctionReference.GetMemberParentClass((UClass*)NULL)), bSelf);
			}
		}

		// Consider the 'CPF_UObjectWrapper' flag on native function call parameters and return values.
		if (Ar.CustomVer(FReleaseObjectVersion::GUID) < FReleaseObjectVersion::PinTypeIncludesUObjectWrapperFlag)
		{
			if (UFunction* TargetFunction = GetTargetFunction())
			{
				if (TargetFunction->IsNative())
				{
					for (TFieldIterator<FProperty> PropIt(TargetFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
					{
						if (UEdGraphPin* Pin = FindPin(PropIt->GetFName()))
						{
							if (const FMapProperty* MapProperty = CastField<FMapProperty>(*PropIt))
							{
								if (MapProperty->KeyProp && MapProperty->KeyProp->HasAllPropertyFlags(CPF_UObjectWrapper))
								{
									Pin->PinType.bIsUObjectWrapper = 1;
								}

								if (MapProperty->ValueProp && MapProperty->ValueProp->HasAllPropertyFlags(CPF_UObjectWrapper))
								{
									Pin->PinType.PinValueType.bTerminalIsUObjectWrapper = true;
								}
							}
							else if (const FSetProperty* SetProperty = CastField<FSetProperty>(*PropIt))
							{
								if (SetProperty->ElementProp && SetProperty->ElementProp->HasAllPropertyFlags(CPF_UObjectWrapper))
								{
									Pin->PinType.PinValueType.bTerminalIsUObjectWrapper = true;
								}
							}
							else if(const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(*PropIt))
							{
								if(ArrayProperty->Inner && ArrayProperty->Inner->HasAllPropertyFlags(CPF_UObjectWrapper))
								{
									Pin->PinType.PinValueType.bTerminalIsUObjectWrapper = true;
								}
							}
							else if (PropIt->HasAllPropertyFlags(CPF_UObjectWrapper))
							{
								Pin->PinType.bIsUObjectWrapper = 1;
							}
						}
					}
				}
			}
		}

		if (!Ar.IsObjectReferenceCollector())
		{
			// Don't validate the enabled state if the user has explicitly set it. Also skip validation if we're just duplicating this node.
			const bool bIsDuplicating = (Ar.GetPortFlags() & PPF_Duplicate) != 0;
			if (!bIsDuplicating && !HasUserSetTheEnabledState())
			{
				if (const UFunction* Function = GetTargetFunction())
				{
					// Enable as development-only if specified in metadata. This way existing functions that have the metadata added to them will get their enabled state fixed up on load.
					if (GetDesiredEnabledState() == ENodeEnabledState::Enabled && Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly))
					{
						SetEnabledState(ENodeEnabledState::DevelopmentOnly, /*bUserAction=*/ false);
					}
					// Ensure that if the metadata is removed, we also fix up the enabled state to avoid leaving it set as development-only in that case.
					else if (GetDesiredEnabledState() == ENodeEnabledState::DevelopmentOnly && !Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly))
					{
						SetEnabledState(ENodeEnabledState::Enabled, /*bUserAction=*/ false);
					}
				}
			}
		}
	}
}

void UK2Node_CallFunction::PostPlacedNewNode()
{
	Super::PostPlacedNewNode();

	// Try re-setting the function given our new parent scope, in case it turns an external to an internal, or vis versa
	FunctionReference.RefreshGivenNewSelfScope<UFunction>(GetBlueprintClassFromNode());

	// Set the node to development only if the function specifies that
	check(!HasUserSetTheEnabledState());
	if (const UFunction* Function = GetTargetFunction())
	{
		if (Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly))
		{
			SetEnabledState(ENodeEnabledState::DevelopmentOnly, /*bUserAction=*/ false);
		}
	}
}

FNodeHandlingFunctor* UK2Node_CallFunction::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{
	return new FKCHandler_CallFunction(CompilerContext);
}

void UK2Node_CallFunction::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
	Super::ExpandNode(CompilerContext, SourceGraph);

	const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
	UFunction* Function = GetTargetFunction();

	// connect DefaultToSelf and WorldContext inside static functions to proper 'self'  
	if (SourceGraph && Schema->IsStaticFunctionGraph(SourceGraph) && Function)
	{
		TArray<UK2Node_FunctionEntry*> EntryPoints;
		SourceGraph->GetNodesOfClass(EntryPoints);
		if (1 != EntryPoints.Num())
		{
			CompilerContext.MessageLog.Warning(*FText::Format(LOCTEXT("WrongEntryPointsNumFmt", "{0} entry points found while expanding node @@"), EntryPoints.Num()).ToString(), this);
		}
		else if (UEdGraphPin* BetterSelfPin = EntryPoints[0]->GetAutoWorldContextPin())
		{
			const FString& DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
			const FString& WorldContextMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);

			struct FStructConnectHelper
			{
				static void Connect(const FString& PinName, UK2Node* Node, UEdGraphPin* BetterSelf, const UEdGraphSchema_K2* InSchema, FCompilerResultsLog& MessageLog)
				{
					UEdGraphPin* Pin = Node->FindPin(PinName);
					if (!PinName.IsEmpty() && Pin && !Pin->LinkedTo.Num())
					{
						const bool bConnected = InSchema->TryCreateConnection(Pin, BetterSelf);
						if (!bConnected)
						{
							MessageLog.Warning(*LOCTEXT("DefaultToSelfNotConnected", "DefaultToSelf pin @@ from node @@ cannot be connected to @@").ToString(), Pin, Node, BetterSelf);
						}
					}
				}
			};
			FStructConnectHelper::Connect(DefaultToSelfMetaValue, this, BetterSelfPin, Schema, CompilerContext.MessageLog);
			if (!Function->HasMetaData(FBlueprintMetadata::MD_CallableWithoutWorldContext))
			{
				FStructConnectHelper::Connect(WorldContextMetaValue, this, BetterSelfPin, Schema, CompilerContext.MessageLog);
			}
		}
	}

	// If we have an enum param that is expanded, we handle that first
	if(bWantsEnumToExecExpansion)
	{
		if(Function)
		{
			TArray<FName> EnumNamesToCheck;
			GetExpandEnumPinNames(Function, EnumNamesToCheck);

			bool bAlreadyHandleInput = false;

			UEdGraphPin* OutMainExecutePin = nullptr;
			UK2Node_ExecutionSequence* SpawnedSequenceNode = nullptr;
			int32 OutSequenceIndex = 0;

			const auto LinkIntoOutputChain = [&OutMainExecutePin, &SpawnedSequenceNode, &OutSequenceIndex, &CompilerContext, this, SourceGraph, Schema](UK2Node* Node)
			{
				if (!OutMainExecutePin)
				{
					// Create normal exec output -- only once though.
					OutMainExecutePin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
				}
				else
				{
					// set up a sequence so we can call one after another.
					if (!SpawnedSequenceNode)
					{
						SpawnedSequenceNode = CompilerContext.SpawnIntermediateNode<UK2Node_ExecutionSequence>(this, SourceGraph);
						SpawnedSequenceNode->AllocateDefaultPins();
						CompilerContext.MovePinLinksToIntermediate(*OutMainExecutePin, *SpawnedSequenceNode->GetThenPinGivenIndex(OutSequenceIndex++));
						Schema->TryCreateConnection(OutMainExecutePin, SpawnedSequenceNode->Pins[0]);
					}
				}

				// Hook up execution to the branch node
				if (!SpawnedSequenceNode)
				{
					Schema->TryCreateConnection(OutMainExecutePin, Node->GetExecPin());
				}
				else
				{
					UEdGraphPin* SequenceOutput = SpawnedSequenceNode->GetThenPinGivenIndex(OutSequenceIndex);

					if (!SequenceOutput)
					{
						SpawnedSequenceNode->AddInputPin();
						SequenceOutput = SpawnedSequenceNode->GetThenPinGivenIndex(OutSequenceIndex);
					}

					Schema->TryCreateConnection(SequenceOutput, Node->GetExecPin());
					OutSequenceIndex++;
				}
			};

			for (const FName& EnumParamName : EnumNamesToCheck)
			{
				UEnum* Enum = nullptr;

				if (FByteProperty* ByteProp = FindFProperty<FByteProperty>(Function, EnumParamName))
				{
					Enum = ByteProp->Enum;
				}
				else if (FEnumProperty* EnumProp = FindFProperty<FEnumProperty>(Function, EnumParamName))
				{
					Enum = EnumProp->GetEnum();
				}

				UEdGraphPin* EnumParamPin = FindPin(EnumParamName);
				if (Enum && EnumParamPin)
				{
					// Expanded as input execs pins
					if (EnumParamPin->Direction == EGPD_Input)
					{
						if (bAlreadyHandleInput)
						{
							CompilerContext.MessageLog.Error(TEXT("@@ Already provided an input enum parameter for ExpandEnumAsExecs. Only one is permitted."), this);
							return;
						}

						bAlreadyHandleInput = true;

						// Create normal exec input
						UEdGraphPin* ExecutePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);

						// Create temp enum variable
						UK2Node_TemporaryVariable* TempEnumVarNode = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
						TempEnumVarNode->VariableType.PinCategory = UEdGraphSchema_K2::PC_Byte;
						TempEnumVarNode->VariableType.PinSubCategoryObject = Enum;
						TempEnumVarNode->AllocateDefaultPins();
						// Get the output pin
						UEdGraphPin* TempEnumVarOutput = TempEnumVarNode->GetVariablePin();

						// Connect temp enum variable to (hidden) enum pin
						Schema->TryCreateConnection(TempEnumVarOutput, EnumParamPin);

						// Now we want to iterate over other exec inputs...
						for (int32 PinIdx = Pins.Num() - 1; PinIdx >= 0; PinIdx--)
						{
							UEdGraphPin* Pin = Pins[PinIdx];
							if (Pin != NULL &&
								Pin != ExecutePin &&
								Pin->Direction == EGPD_Input &&
								Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
							{
								// Create node to set the temp enum var
								UK2Node_AssignmentStatement* AssignNode = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
								AssignNode->AllocateDefaultPins();

								// Move connections from fake 'enum exec' pint to this assignment node
								CompilerContext.MovePinLinksToIntermediate(*Pin, *AssignNode->GetExecPin());

								// Connect this to out temp enum var
								Schema->TryCreateConnection(AssignNode->GetVariablePin(), TempEnumVarOutput);

								// Connect exec output to 'real' exec pin
								Schema->TryCreateConnection(AssignNode->GetThenPin(), ExecutePin);

								// set the literal enum value to set to
								AssignNode->GetValuePin()->DefaultValue = Pin->PinName.ToString();

								// Finally remove this 'cosmetic' exec pin
								Pins[PinIdx]->MarkPendingKill();
								Pins.RemoveAt(PinIdx);
							}
						}
					}
					// Expanded as output execs pins
					else if (EnumParamPin->Direction == EGPD_Output)
					{
						// Create a SwitchEnum node to switch on the output enum
						UK2Node_SwitchEnum* SwitchEnumNode = CompilerContext.SpawnIntermediateNode<UK2Node_SwitchEnum>(this, SourceGraph);
						UEnum* EnumObject = Cast<UEnum>(EnumParamPin->PinType.PinSubCategoryObject.Get());
						SwitchEnumNode->SetEnum(EnumObject);
						SwitchEnumNode->AllocateDefaultPins();

						LinkIntoOutputChain(SwitchEnumNode);

						// Connect (hidden) enum pin to switch node's selection pin
						Schema->TryCreateConnection(EnumParamPin, SwitchEnumNode->GetSelectionPin());

						// Now we want to iterate over other exec outputs corresponding to the enum.
						// the first pins created are the ExpandEnumAsExecs pins, and they're all made at the same time.
						for (int32 PinIdx = Enum->NumEnums() - 2; PinIdx >= 0; PinIdx--)
						{
							UEdGraphPin* Pin = Pins[PinIdx];

							if (Pin &&
								Pin != OutMainExecutePin &&
								Pin->Direction == EGPD_Output &&
								Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
							{
								if (UEdGraphPin* FoundPin = SwitchEnumNode->FindPin(Pin->PinName))
								{
									if (!FoundPin->LinkedTo.Contains(Pin))
									{
										// Move connections from fake 'enum exec' pin to this switch node
										CompilerContext.MovePinLinksToIntermediate(*Pin, *FoundPin);

										// Finally remove this 'cosmetic' exec pin
										Pins[PinIdx]->MarkPendingKill();
										Pins.RemoveAt(PinIdx);
									}
								}
								// Have passed the relevant entries... no more work to do here.
								else
								{
									break;
								}
							}
						}
					}
				}
				else if(EnumParamPin && !EnumParamPin->PinType.IsContainer() && EnumParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Boolean)
				{
					if (EnumParamPin->Direction == EGPD_Input)
					{
						// Create normal exec input
						UEdGraphPin* ExecutePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
						// Create temp bool variable
						UK2Node_TemporaryVariable* TempBoolVarNode = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
						TempBoolVarNode->VariableType.PinCategory = UEdGraphSchema_K2::PC_Boolean;
						TempBoolVarNode->AllocateDefaultPins();
						// Get the output pin
						UEdGraphPin* TempBoolVarOutput = TempBoolVarNode->GetVariablePin();
						// Connect temp enum variable to (hidden) bool pin
						Schema->TryCreateConnection(TempBoolVarOutput, EnumParamPin);
						
						// create a true entry and a false:
						const auto CreateAssignNode = [Schema, &CompilerContext, this, SourceGraph, TempBoolVarOutput, ExecutePin](UEdGraphPin* FakePin, const TCHAR* DefaultValue)
						{
							UK2Node_AssignmentStatement* AssignNode = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph);
							AssignNode->AllocateDefaultPins();

							// Move connections from fake 'enum exec' pint to this assignment node
							CompilerContext.MovePinLinksToIntermediate(*FakePin, *AssignNode->GetExecPin());

							// Connect this to out temp enum var
							Schema->TryCreateConnection(AssignNode->GetVariablePin(), TempBoolVarOutput);

							// Connect exec output to 'real' exec pin
							Schema->TryCreateConnection(AssignNode->GetThenPin(), ExecutePin);

							// set the literal enum value to set to
							AssignNode->GetValuePin()->DefaultValue = DefaultValue;
						};

						UEdGraphPin* TruePin = FindPinChecked(TEXT("True"), EEdGraphPinDirection::EGPD_Input);
						UEdGraphPin* FalsePin = FindPinChecked(TEXT("False"), EEdGraphPinDirection::EGPD_Input);

						CreateAssignNode(TruePin, TEXT("True"));
						CreateAssignNode(FalsePin, TEXT("False"));

						// remove fake false/true nodes:
						RemovePin(TruePin);
						RemovePin(FalsePin);
					}
					else if (EnumParamPin->Direction == EGPD_Output)
					{
						// Create a Branch node to switch on the output bool:
						UK2Node_IfThenElse* IfElseNode = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(this, SourceGraph);
						IfElseNode->AllocateDefaultPins();

						LinkIntoOutputChain(IfElseNode);

						// Connect (hidden) bool pin to branch node
						Schema->TryCreateConnection(EnumParamPin, IfElseNode->GetConditionPin());

						UEdGraphPin* TruePin = FindPinChecked(TEXT("True"), EEdGraphPinDirection::EGPD_Output);
						UEdGraphPin* FalsePin = FindPinChecked(TEXT("False"), EEdGraphPinDirection::EGPD_Output);

						// move true connection to branch node:
						CompilerContext.MovePinLinksToIntermediate(*TruePin, *IfElseNode->GetThenPin());
						// move false connection to branch node:
						CompilerContext.MovePinLinksToIntermediate(*FalsePin, *IfElseNode->GetElsePin());

						// remove fake false/true nodes:
						RemovePin(TruePin);
						RemovePin(FalsePin);
					}
				}
			}
		}
	}

	// AUTO CREATED REFS
	{
		if ( Function )
		{
			TArray<FString> AutoCreateRefTermPinNames;
			const bool bHasAutoCreateRefTerms = Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm);
			if ( bHasAutoCreateRefTerms )
			{
				CompilerContext.GetSchema()->GetAutoEmitTermParameters(Function, AutoCreateRefTermPinNames);
			}

			for (UEdGraphPin* Pin : Pins)
			{
				const bool bIsRefInputParam = Pin && Pin->PinType.bIsReference && (Pin->Direction == EGPD_Input) && !CompilerContext.GetSchema()->IsMetaPin(*Pin);
				if (!bIsRefInputParam)
				{
					continue;
				}

				const bool bHasConnections = Pin->LinkedTo.Num() > 0;
				const bool bCreateDefaultValRefTerm = bHasAutoCreateRefTerms && 
					!bHasConnections && AutoCreateRefTermPinNames.Contains(Pin->PinName.ToString());

				if (bCreateDefaultValRefTerm)
				{
					const bool bHasDefaultValue = !Pin->DefaultValue.IsEmpty() || Pin->DefaultObject || !Pin->DefaultTextValue.IsEmpty();

					// copy defaults as default values can be reset when the pin is connected
					const FString DefaultValue = Pin->DefaultValue;
					UObject* DefaultObject = Pin->DefaultObject;
					const FText DefaultTextValue = Pin->DefaultTextValue;
					bool bMatchesDefaults = Pin->DoesDefaultValueMatchAutogenerated();

					UEdGraphPin* ValuePin = InnerHandleAutoCreateRef(this, Pin, CompilerContext, SourceGraph, bHasDefaultValue);
					if ( ValuePin )
					{
						if (bMatchesDefaults)
						{
							// Use the latest code to set default value
							Schema->SetPinAutogeneratedDefaultValueBasedOnType(ValuePin);
						}
						else
						{
							ValuePin->DefaultValue = DefaultValue;
							ValuePin->DefaultObject = DefaultObject;
							ValuePin->DefaultTextValue = DefaultTextValue;
						}
					}
				}
				// since EX_Self does not produce an addressable (referenceable) FProperty, we need to shim
				// in a "auto-ref" term in its place (this emulates how UHT generates a local value for 
				// native functions; hence the IsNative() check)
				else if (bHasConnections && Pin->LinkedTo[0]->PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self && Pin->PinType.bIsConst && !Function->IsNative())
				{
					InnerHandleAutoCreateRef(this, Pin, CompilerContext, SourceGraph, /*bForceAssignment =*/true);
				}
			}
		}
	}

	// Then we go through and expand out array iteration if necessary
	const bool bAllowMultipleSelfs = AllowMultipleSelfs(true);
	UEdGraphPin* MultiSelf = Schema->FindSelfPin(*this, EEdGraphPinDirection::EGPD_Input);
	if(bAllowMultipleSelfs && MultiSelf && !MultiSelf->PinType.IsArray())
	{
		const bool bProperInputToExpandForEach = 
			(1 == MultiSelf->LinkedTo.Num()) && 
			(nullptr != MultiSelf->LinkedTo[0]) && 
			(MultiSelf->LinkedTo[0]->PinType.IsArray());
		if(bProperInputToExpandForEach)
		{
			CallForEachElementInArrayExpansion(this, MultiSelf, CompilerContext, SourceGraph);
		}
	}
}

UEdGraphPin* UK2Node_CallFunction::InnerHandleAutoCreateRef(UK2Node* Node, UEdGraphPin* Pin, FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph, bool bForceAssignment)
{
	const bool bAddAssigment = !Pin->PinType.IsContainer() && bForceAssignment;

	// ADD LOCAL VARIABLE
	UK2Node_TemporaryVariable* LocalVariable = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(Node, SourceGraph);
	LocalVariable->VariableType = Pin->PinType;
	LocalVariable->VariableType.bIsReference = false;
	LocalVariable->AllocateDefaultPins();
	if (!bAddAssigment)
	{
		if (!CompilerContext.GetSchema()->TryCreateConnection(LocalVariable->GetVariablePin(), Pin))
		{
			CompilerContext.MessageLog.Error(*LOCTEXT("AutoCreateRefTermPin_NotConnected", "AutoCreateRefTerm Expansion: Pin @@ cannot be connected to @@").ToString(), LocalVariable->GetVariablePin(), Pin);
			return nullptr;
		}
	}
	// ADD ASSIGMENT
	else
	{
		// TODO connect to dest..
		UK2Node_PureAssignmentStatement* AssignDefaultValue = CompilerContext.SpawnIntermediateNode<UK2Node_PureAssignmentStatement>(Node, SourceGraph);
		AssignDefaultValue->AllocateDefaultPins();
		const bool bVariableConnected = CompilerContext.GetSchema()->TryCreateConnection(AssignDefaultValue->GetVariablePin(), LocalVariable->GetVariablePin());
		UEdGraphPin* AssignInputPit = AssignDefaultValue->GetValuePin();
		const bool bPreviousInputSaved = AssignInputPit && CompilerContext.MovePinLinksToIntermediate(*Pin, *AssignInputPit).CanSafeConnect();
		const bool bOutputConnected = CompilerContext.GetSchema()->TryCreateConnection(AssignDefaultValue->GetOutputPin(), Pin);
		if (!bVariableConnected || !bOutputConnected || !bPreviousInputSaved)
		{
			CompilerContext.MessageLog.Error(*LOCTEXT("AutoCreateRefTermPin_AssignmentError", "AutoCreateRefTerm Expansion: Assignment Error @@").ToString(), AssignDefaultValue);
			return nullptr;
		}
		CompilerContext.GetSchema()->SetPinAutogeneratedDefaultValueBasedOnType(AssignDefaultValue->GetValuePin());
		return AssignInputPit;
	}
	return nullptr;
}

void UK2Node_CallFunction::CallForEachElementInArrayExpansion(UK2Node* Node, UEdGraphPin* MultiSelf, FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
	const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
	check(Node && MultiSelf && SourceGraph && Schema);
	const bool bProperInputToExpandForEach = 
		(1 == MultiSelf->LinkedTo.Num()) && 
		(NULL != MultiSelf->LinkedTo[0]) && 
		(MultiSelf->LinkedTo[0]->PinType.IsArray());
	ensure(bProperInputToExpandForEach);

	UEdGraphPin* ThenPin = Node->FindPinChecked(UEdGraphSchema_K2::PN_Then);

	// Create int Iterator
	UK2Node_TemporaryVariable* IteratorVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(Node, SourceGraph);
	IteratorVar->VariableType.PinCategory = UEdGraphSchema_K2::PC_Int;
	IteratorVar->AllocateDefaultPins();

	// Initialize iterator
	UK2Node_AssignmentStatement* InteratorInitialize = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(Node, SourceGraph);
	InteratorInitialize->AllocateDefaultPins();
	InteratorInitialize->GetValuePin()->DefaultValue = TEXT("0");
	Schema->TryCreateConnection(IteratorVar->GetVariablePin(), InteratorInitialize->GetVariablePin());
	CompilerContext.MovePinLinksToIntermediate(*Node->GetExecPin(), *InteratorInitialize->GetExecPin());

	// Do loop branch
	UK2Node_IfThenElse* Branch = CompilerContext.SpawnIntermediateNode<UK2Node_IfThenElse>(Node, SourceGraph);
	Branch->AllocateDefaultPins();
	Schema->TryCreateConnection(InteratorInitialize->GetThenPin(), Branch->GetExecPin());
	CompilerContext.MovePinLinksToIntermediate(*ThenPin, *Branch->GetElsePin());

	// Do loop condition
	UK2Node_CallFunction* Condition = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(Node, SourceGraph); 
	Condition->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Less_IntInt)));
	Condition->AllocateDefaultPins();
	Schema->TryCreateConnection(Condition->GetReturnValuePin(), Branch->GetConditionPin());
	Schema->TryCreateConnection(Condition->FindPinChecked(TEXT("A")), IteratorVar->GetVariablePin());

	// Array size
	UK2Node_CallArrayFunction* ArrayLength = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(Node, SourceGraph); 
	ArrayLength->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetArrayLibrary, Array_Length)));
	ArrayLength->AllocateDefaultPins();
	CompilerContext.CopyPinLinksToIntermediate(*MultiSelf, *ArrayLength->GetTargetArrayPin());
	ArrayLength->PinConnectionListChanged(ArrayLength->GetTargetArrayPin());
	Schema->TryCreateConnection(Condition->FindPinChecked(TEXT("B")), ArrayLength->GetReturnValuePin());

	// Get Element
	UK2Node_CallArrayFunction* GetElement = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(Node, SourceGraph); 
	GetElement->SetFromFunction(UKismetArrayLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetArrayLibrary, Array_Get)));
	GetElement->AllocateDefaultPins();
	CompilerContext.CopyPinLinksToIntermediate(*MultiSelf, *GetElement->GetTargetArrayPin());
	GetElement->PinConnectionListChanged(GetElement->GetTargetArrayPin());
	Schema->TryCreateConnection(GetElement->FindPinChecked(TEXT("Index")), IteratorVar->GetVariablePin());

	// Iterator increment
	UK2Node_CallFunction* Increment = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(Node, SourceGraph); 
	Increment->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Add_IntInt)));
	Increment->AllocateDefaultPins();
	Schema->TryCreateConnection(Increment->FindPinChecked(TEXT("A")), IteratorVar->GetVariablePin());
	Increment->FindPinChecked(TEXT("B"))->DefaultValue = TEXT("1");

	// Iterator assigned
	UK2Node_AssignmentStatement* IteratorAssign = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(Node, SourceGraph);
	IteratorAssign->AllocateDefaultPins();
	Schema->TryCreateConnection(IteratorAssign->GetVariablePin(), IteratorVar->GetVariablePin());
	Schema->TryCreateConnection(IteratorAssign->GetValuePin(), Increment->GetReturnValuePin());
	Schema->TryCreateConnection(IteratorAssign->GetThenPin(), Branch->GetExecPin());

	// Connect pins from intermediate nodes back in to the original node
	Schema->TryCreateConnection(Branch->GetThenPin(), Node->GetExecPin());
	Schema->TryCreateConnection(ThenPin, IteratorAssign->GetExecPin());
	Schema->TryCreateConnection(GetElement->FindPinChecked(TEXT("Item")), MultiSelf);
}

FName UK2Node_CallFunction::GetCornerIcon() const
{
	if (const UFunction* Function = GetTargetFunction())
	{
		if (Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly))
		{
			return TEXT("Graph.Replication.AuthorityOnly");		
		}
		else if (Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic))
		{
			return TEXT("Graph.Replication.ClientEvent");
		}
		else if(Function->HasMetaData(FBlueprintMetadata::MD_Latent))
		{
			return TEXT("Graph.Latent.LatentIcon");
		}
	}
	return Super::GetCornerIcon();
}

FSlateIcon UK2Node_CallFunction::GetIconAndTint(FLinearColor& OutColor) const
{
	return GetPaletteIconForFunction(GetTargetFunction(), OutColor);
}

bool UK2Node_CallFunction::ReconnectPureExecPins(TArray<UEdGraphPin*>& OldPins)
{
	if (IsNodePure())
	{
		// look for an old exec pin
		UEdGraphPin* PinExec = nullptr;
		for (int32 PinIdx = 0; PinIdx < OldPins.Num(); PinIdx++)
		{
			if (OldPins[PinIdx]->PinName == UEdGraphSchema_K2::PN_Execute)
			{
				PinExec = OldPins[PinIdx];
				break;
			}
		}
		if (PinExec)
		{
			PinExec->SetSavePinIfOrphaned(false); 

			// look for old then pin
			UEdGraphPin* PinThen = nullptr;
			for (int32 PinIdx = 0; PinIdx < OldPins.Num(); PinIdx++)
			{
				if (OldPins[PinIdx]->PinName == UEdGraphSchema_K2::PN_Then)
				{
					PinThen = OldPins[PinIdx];
					break;
				}
			}
			if (PinThen)
			{
				PinThen->SetSavePinIfOrphaned(false);

				// reconnect all incoming links to old exec pin to the far end of the old then pin.
				if (PinThen->LinkedTo.Num() > 0)
				{
					UEdGraphPin* PinThenLinked = PinThen->LinkedTo[0];
					while (PinExec->LinkedTo.Num() > 0)
					{
						UEdGraphPin* PinExecLinked = PinExec->LinkedTo[0];
						PinExecLinked->BreakLinkTo(PinExec);
						PinExecLinked->MakeLinkTo(PinThenLinked);
					}
					return true;
				}
			}
		}
	}
	return false;
}

void UK2Node_CallFunction::InvalidatePinTooltips()
{
	bPinTooltipsValid = false;
}

void UK2Node_CallFunction::ConformContainerPins()
{
	// helper functions for type propagation:
	const auto TryReadTypeToPropagate = [](UEdGraphPin* Pin, bool& bOutPropagated, FEdGraphTerminalType& TypeToPropagete)
	{
		if (Pin && !bOutPropagated)
		{
			if (Pin->HasAnyConnections() || !Pin->DoesDefaultValueMatchAutogenerated() )
			{
				bOutPropagated = true;
				if (Pin->LinkedTo.Num() != 0)
				{
					TypeToPropagete = Pin->LinkedTo[0]->GetPrimaryTerminalType();
				}
				else
				{
					TypeToPropagete = Pin->GetPrimaryTerminalType();
				}
			}
		}
	};

	const auto TryReadValueTypeToPropagate = [](UEdGraphPin* Pin, bool& bOutPropagated, FEdGraphTerminalType& TypeToPropagete)
	{
		if (Pin && !bOutPropagated)
		{
			if (Pin->LinkedTo.Num() != 0 || !Pin->DoesDefaultValueMatchAutogenerated())
			{
				bOutPropagated = true;
				if (Pin->LinkedTo.Num() != 0)
				{
					TypeToPropagete = Pin->LinkedTo[0]->PinType.PinValueType;
				}
				else
				{
					TypeToPropagete = Pin->PinType.PinValueType;
				}
			}
		}
	};
	
	const UEdGraphSchema_K2* Schema = CastChecked<UEdGraphSchema_K2>(GetSchema());

	const auto TryPropagateType = [Schema](UEdGraphPin* Pin, const FEdGraphTerminalType& TerminalType, bool bTypeIsAvailable)
	{
		if(Pin)
		{
			if(bTypeIsAvailable)
			{
				const FEdGraphTerminalType PrimaryType = Pin->GetPrimaryTerminalType();
				if( PrimaryType.TerminalCategory != TerminalType.TerminalCategory ||
					PrimaryType.TerminalSubCategory != TerminalType.TerminalSubCategory ||
					PrimaryType.TerminalSubCategoryObject != TerminalType.TerminalSubCategoryObject)
				{
					// terminal type changed:
					if (Pin->SubPins.Num() > 0 && Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard)
					{
						Schema->RecombinePin(Pin->SubPins[0]);
					}

					Pin->PinType.PinCategory = TerminalType.TerminalCategory;
					Pin->PinType.PinSubCategory = TerminalType.TerminalSubCategory;
					Pin->PinType.PinSubCategoryObject = TerminalType.TerminalSubCategoryObject;

					// Also propagate the CPF_UObjectWrapper flag, which will be set for "wrapped" object ptr types (e.g. TSubclassOf).
					Pin->PinType.bIsUObjectWrapper = TerminalType.bTerminalIsUObjectWrapper;
				
					// Reset default values
					if (!Schema->IsPinDefaultValid(Pin, Pin->DefaultValue, Pin->DefaultObject, Pin->DefaultTextValue).IsEmpty())
					{
						Schema->ResetPinToAutogeneratedDefaultValue(Pin, false);
					}
				}
			}
			else
			{
				// reset to wildcard:
				if (Pin->SubPins.Num() > 0)
				{
					Schema->RecombinePin(Pin->SubPins[0]);
				}

				Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
				Pin->PinType.PinSubCategory = NAME_None;
				Pin->PinType.PinSubCategoryObject = nullptr;
				Pin->PinType.bIsUObjectWrapper = false;
				Schema->ResetPinToAutogeneratedDefaultValue(Pin, false);
			}
		}
	};

	const auto TryPropagateValueType = [](UEdGraphPin* Pin, const FEdGraphTerminalType& TerminalType, bool bTypeIsAvailable)
	{
		if (Pin)
		{
			if (bTypeIsAvailable)
			{
				Pin->PinType.PinValueType.TerminalCategory = TerminalType.TerminalCategory;
				Pin->PinType.PinValueType.TerminalSubCategory = TerminalType.TerminalSubCategory;
				Pin->PinType.PinValueType.TerminalSubCategoryObject = TerminalType.TerminalSubCategoryObject;
			}
			else
			{
				Pin->PinType.PinValueType.TerminalCategory = UEdGraphSchema_K2::PC_Wildcard;
				Pin->PinType.PinValueType.TerminalSubCategory = NAME_None;
				Pin->PinType.PinValueType.TerminalSubCategoryObject = nullptr;
			}
		}
	};
		
	const UFunction* TargetFunction = GetTargetFunction();
	if (TargetFunction == nullptr)
	{
		return;
	}

	// find any pins marked as SetParam
	const FString& SetPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_SetParam);

	// useless copies/allocates in this code, could be an optimization target...
	TArray<FString> SetParamPinGroups;
	{
		SetPinMetaData.ParseIntoArray(SetParamPinGroups, TEXT(","), true);
	}

	for (FString& Entry : SetParamPinGroups)
	{
		// split the group:
		TArray<FString> GroupEntries;
		Entry.ParseIntoArray(GroupEntries, TEXT("|"), true);
		// resolve pins
		TArray<UEdGraphPin*> ResolvedPins;
		for(UEdGraphPin* Pin : Pins)
		{
			if (GroupEntries.Contains(Pin->GetName()))
			{
				ResolvedPins.Add(Pin);
			}
		}

		// if nothing is connected (or non-default), reset to wildcard
		// else, find the first type and propagate to everyone else::
		bool bReadyToPropagatSetType = false;
		FEdGraphTerminalType TypeToPropagate;
		for (UEdGraphPin* Pin : ResolvedPins)
		{
			TryReadTypeToPropagate(Pin, bReadyToPropagatSetType, TypeToPropagate);
			if(bReadyToPropagatSetType)
			{
				break;
			}
		}

		for (UEdGraphPin* Pin : ResolvedPins)
		{
			TryPropagateType( Pin, TypeToPropagate, bReadyToPropagatSetType );
		}
	}

	const FString& MapPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapParam);
	const FString& MapKeyPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapKeyParam);
	const FString& MapValuePinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapValueParam);

	if(!MapPinMetaData.IsEmpty() || !MapKeyPinMetaData.IsEmpty() || !MapValuePinMetaData.IsEmpty() )
	{
		// if the map pin has a connection infer from that, otherwise use the information on the key param and value param:
		bool bReadyToPropagateKeyType = false;
		FEdGraphTerminalType KeyTypeToPropagate;
		bool bReadyToPropagateValueType = false;
		FEdGraphTerminalType ValueTypeToPropagate;

		UEdGraphPin* MapPin = MapPinMetaData.IsEmpty() ? nullptr : FindPin(MapPinMetaData);
		UEdGraphPin* MapKeyPin = MapKeyPinMetaData.IsEmpty() ? nullptr : FindPin(MapKeyPinMetaData);
		UEdGraphPin* MapValuePin = MapValuePinMetaData.IsEmpty() ? nullptr : FindPin(MapValuePinMetaData);

		TryReadTypeToPropagate(MapPin, bReadyToPropagateKeyType, KeyTypeToPropagate);
		TryReadValueTypeToPropagate(MapPin, bReadyToPropagateValueType, ValueTypeToPropagate);
		TryReadTypeToPropagate(MapKeyPin, bReadyToPropagateKeyType, KeyTypeToPropagate);
		TryReadTypeToPropagate(MapValuePin, bReadyToPropagateValueType, ValueTypeToPropagate);

		TryPropagateType(MapPin, KeyTypeToPropagate, bReadyToPropagateKeyType);
		TryPropagateType(MapKeyPin, KeyTypeToPropagate, bReadyToPropagateKeyType);

		TryPropagateValueType(MapPin, ValueTypeToPropagate, bReadyToPropagateValueType);
		TryPropagateType(MapValuePin, ValueTypeToPropagate, bReadyToPropagateValueType);
	}
}

FText UK2Node_CallFunction::GetToolTipHeading() const
{
	FText Heading = Super::GetToolTipHeading();

	struct FHeadingBuilder
	{
		FHeadingBuilder(FText InitialHeading) : ConstructedHeading(InitialHeading) {}

		void Append(FText HeadingAddOn)
		{
			if (ConstructedHeading.IsEmpty())
			{
				ConstructedHeading = HeadingAddOn;
			}
			else 
			{
				ConstructedHeading = FText::Format(FText::FromString("{0}\n{1}"), HeadingAddOn, ConstructedHeading);
			}
		}

		FText ConstructedHeading;
	};
	FHeadingBuilder HeadingBuilder(Super::GetToolTipHeading());

	if (const UFunction* Function = GetTargetFunction())
	{
		if (Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly))
		{
			HeadingBuilder.Append(LOCTEXT("ServerOnlyFunc", "Server Only"));	
		}
		if (Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic))
		{
			HeadingBuilder.Append(LOCTEXT("ClientOnlyFunc", "Client Only"));
		}
		if(Function->HasMetaData(FBlueprintMetadata::MD_Latent))
		{
			HeadingBuilder.Append(LOCTEXT("LatentFunc", "Latent"));
		}
	}

	return HeadingBuilder.ConstructedHeading;
}

void UK2Node_CallFunction::GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const
{
	UFunction* TargetFunction = GetTargetFunction();
	const FString TargetFunctionName = TargetFunction ? TargetFunction->GetName() : TEXT( "InvalidFunction" );
	OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Type" ), TEXT( "Function" ) ));
	OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Class" ), GetClass()->GetName() ));
	OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Name" ), TargetFunctionName ));
}

FText UK2Node_CallFunction::GetMenuCategory() const
{
	UFunction* TargetFunction = GetTargetFunction();
	if (TargetFunction != nullptr)
	{
		return GetDefaultCategoryForFunction(TargetFunction, FText::GetEmpty());
	}
	return FText::GetEmpty();
}

bool UK2Node_CallFunction::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const
{
	UFunction* Function = GetTargetFunction();
	const UClass* SourceClass = Function ? Function->GetOwnerClass() : nullptr;
	const UBlueprint* SourceBlueprint = GetBlueprint();
	bool bResult = (SourceClass != nullptr) && (SourceClass->ClassGeneratedBy != SourceBlueprint);
	if (bResult && OptionalOutput)
	{
		OptionalOutput->AddUnique(Function);
	}

	// All structures, that are required for the BP compilation, should be gathered
	for (UEdGraphPin* Pin : Pins)
	{
		UStruct* DepStruct = Pin ? Cast<UStruct>(Pin->PinType.PinSubCategoryObject.Get()) : nullptr;

		UClass* DepClass = Cast<UClass>(DepStruct);
		if (DepClass && (DepClass->ClassGeneratedBy == SourceBlueprint))
		{
			//Don't include self
			continue;
		}

		if (DepStruct && !DepStruct->IsNative())
		{
			if (OptionalOutput)
			{
				OptionalOutput->AddUnique(DepStruct);
			}
			bResult = true;
		}
	}

	const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);
	return bSuperResult || bResult;
}

UEdGraph* UK2Node_CallFunction::GetFunctionGraph(const UEdGraphNode*& OutGraphNode) const
{
	OutGraphNode = nullptr;

	// Search for the Blueprint owner of the function graph, climbing up through the Blueprint hierarchy
	UClass* MemberParentClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());
	if(MemberParentClass != nullptr)
	{
		UBlueprintGeneratedClass* ParentClass = Cast<UBlueprintGeneratedClass>(MemberParentClass);
		if(ParentClass != nullptr && ParentClass->ClassGeneratedBy != nullptr)
		{
			UBlueprint* Blueprint = Cast<UBlueprint>(ParentClass->ClassGeneratedBy);
			while(Blueprint != nullptr)
			{
				UEdGraph* TargetGraph = nullptr;
				const FName FunctionName = FunctionReference.GetMemberName();
				for (UEdGraph* const Graph : Blueprint->FunctionGraphs) 
				{
					if (Graph->GetFName() == FunctionName)
					{
						TargetGraph = Graph;
						break;
					}
				}

				if (!TargetGraph)
				{
					for (const FBPInterfaceDescription& Interface : Blueprint->ImplementedInterfaces)
					{
						for (UEdGraph* const Graph : Interface.Graphs)
						{
							if (Graph->GetFName() == FunctionName)
							{
								TargetGraph = Graph;
								break;
							}
						}

						if (TargetGraph)
						{
							break;
						}
					}
				}

				if((TargetGraph != nullptr) && !TargetGraph->HasAnyFlags(RF_Transient))
				{
					// Found the function graph in a Blueprint, return that graph
					return TargetGraph;
				}
				else
				{
					// Did not find the function call as a graph, it may be a custom event
					UK2Node_CustomEvent* CustomEventNode = nullptr;

					TArray<UK2Node_CustomEvent*> CustomEventNodes;
					FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, CustomEventNodes);

					for (UK2Node_CustomEvent* const CustomEvent : CustomEventNodes)
					{
						if(CustomEvent->CustomFunctionName == FunctionReference.GetMemberName())
						{
							OutGraphNode = CustomEvent;
							return CustomEvent->GetGraph();
						}
					}
				}

				ParentClass = Cast<UBlueprintGeneratedClass>(Blueprint->ParentClass);
				Blueprint = ParentClass != nullptr ? Cast<UBlueprint>(ParentClass->ClassGeneratedBy) : nullptr;
			}
		}
	}
	return nullptr;
}

bool UK2Node_CallFunction::IsStructureWildcardProperty(const UFunction* Function, const FName PropertyName)
{
	if (Function && !PropertyName.IsNone())
	{
		TArray<FString> Names;
		FCustomStructureParamHelper::FillCustomStructureParameterNames(Function, Names);
		if (Names.Contains(PropertyName.ToString()))
		{
			return true;
		}
	}
	return false;
}

bool UK2Node_CallFunction::IsWildcardProperty(const UFunction* InFunction, const FProperty* InProperty)
{
	if (InProperty)
	{
		return FEdGraphUtilities::IsSetParam(InFunction, InProperty->GetFName()) || FEdGraphUtilities::IsMapParam(InFunction, InProperty->GetFName());
	}
	return false;
}

void UK2Node_CallFunction::AddSearchMetaDataInfo(TArray<struct FSearchTagDataPair>& OutTaggedMetaData) const
{
	Super::AddSearchMetaDataInfo(OutTaggedMetaData);

	if (UFunction* TargetFunction = GetTargetFunction())
	{
		OutTaggedMetaData.Add(FSearchTagDataPair(FFindInBlueprintSearchTags::FiB_NativeName, FText::FromString(TargetFunction->GetName())));
	}
}

TSharedPtr<SWidget> UK2Node_CallFunction::CreateNodeImage() const
{
	// For set, map and array functions we have a cool icon. This helps users quickly
	// identify container types:
	if (UFunction* TargetFunction = GetTargetFunction())
	{
		UEdGraphPin* NodeImagePin = FEdGraphUtilities::FindArrayParamPin(TargetFunction, this);
		NodeImagePin = NodeImagePin ? NodeImagePin : FEdGraphUtilities::FindSetParamPin(TargetFunction, this);
		NodeImagePin = NodeImagePin ? NodeImagePin : FEdGraphUtilities::FindMapParamPin(TargetFunction, this);
		if(NodeImagePin)
		{
			// Find the first array param pin and bind that to our array image:
			return SPinTypeSelector::ConstructPinTypeImage(NodeImagePin);
		}
	}

	return TSharedPtr<SWidget>();
}

UObject* UK2Node_CallFunction::GetJumpTargetForDoubleClick() const
{
	// If there is an event node, jump to it, otherwise jump to the function graph
	const UEdGraphNode* ResultEventNode = nullptr;
	UEdGraph* FunctionGraph = GetFunctionGraph(/*out*/ ResultEventNode);
	if (ResultEventNode != nullptr)
	{
		return const_cast<UEdGraphNode*>(ResultEventNode);
	}
	else
	{
		return FunctionGraph;
	}
}

bool UK2Node_CallFunction::CanJumpToDefinition() const
{
	const UFunction* TargetFunction = GetTargetFunction();
	const bool bNativeFunction = (TargetFunction != nullptr) && (TargetFunction->IsNative());
	return bNativeFunction || (GetJumpTargetForDoubleClick() != nullptr);
}

void UK2Node_CallFunction::JumpToDefinition() const
{
	// For native functions, try going to the function definition in C++ if available
	if (UFunction* TargetFunction = GetTargetFunction())
	{
		if (TargetFunction->IsNative())
		{
			// First try the nice way that will get to the right line number
			bool bSucceeded = false;
			const bool bNavigateToNativeFunctions = GetDefault<UBlueprintEditorSettings>()->bNavigateToNativeFunctionsFromCallNodes;
			
			if(bNavigateToNativeFunctions) 
			{
				if(FSourceCodeNavigation::CanNavigateToFunction(TargetFunction))
				{
					bSucceeded = FSourceCodeNavigation::NavigateToFunction(TargetFunction);
				}

				// Failing that, fall back to the older method which will still get the file open assuming it exists
				if (!bSucceeded)
				{
					FString NativeParentClassHeaderPath;
					const bool bFileFound = FSourceCodeNavigation::FindClassHeaderPath(TargetFunction, NativeParentClassHeaderPath) && (IFileManager::Get().FileSize(*NativeParentClassHeaderPath) != INDEX_NONE);
					if (bFileFound)
					{
						const FString AbsNativeParentClassHeaderPath = FPaths::ConvertRelativePathToFull(NativeParentClassHeaderPath);
						bSucceeded = FSourceCodeNavigation::OpenSourceFile(AbsNativeParentClassHeaderPath);
					}
				}
			}
			else
			{	
				// Inform user that the function is native, give them opportunity to enable navigation to native
				// functions:
				FNotificationInfo Info(LOCTEXT("NavigateToNativeDisabled", "Navigation to Native (c++) Functions Disabled"));
				Info.ExpireDuration = 10.0f;
				Info.CheckBoxState = bNavigateToNativeFunctions ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
			
				Info.CheckBoxStateChanged = FOnCheckStateChanged::CreateStatic(
					[](ECheckBoxState NewState)
					{
						const FScopedTransaction Transaction(LOCTEXT("ChangeNavigateToNativeFunctionsFromCallNodes", "Change Navigate to Native Functions from Call Nodes Setting"));
	
						UBlueprintEditorSettings* MutableEditorSetings = GetMutableDefault<UBlueprintEditorSettings>();
						MutableEditorSetings->Modify();
						MutableEditorSetings->bNavigateToNativeFunctionsFromCallNodes = (NewState == ECheckBoxState::Checked) ? true : false;
						MutableEditorSetings->SaveConfig();
					}
				);
				Info.CheckBoxText = LOCTEXT("EnableNavigationToNative", "Navigate to Native Functions from Blueprint Call Nodes?");
			
				FSlateNotificationManager::Get().AddNotification(Info);
			}

			return;
		}
	}

	// Otherwise, fall back to the inherited behavior which should go to the function entry node
	Super::JumpToDefinition();
}

FString UK2Node_CallFunction::GetPinMetaData(FName InPinName, FName InKey)
{
	FString MetaData = Super::GetPinMetaData(InPinName, InKey);

	// If there's no metadata directly on the pin then check for metadata on the function
	if (MetaData.IsEmpty())
	{
		if (UFunction* Function = GetTargetFunction())
		{
			// Find the corresponding property for the pin
			if (FProperty* Property = Function->FindPropertyByName(InPinName))
			{
				MetaData = Property->GetMetaData(InKey);
			}
		}
	}

	return MetaData;
}

bool UK2Node_CallFunction::IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const
{
	bool bIsDisallowed = Super::IsConnectionDisallowed(MyPin, OtherPin, OutReason);
	if (!bIsDisallowed && MyPin != nullptr)
	{
		if (MyPin->bNotConnectable)
		{
			bIsDisallowed = true;
			OutReason = LOCTEXT("PinConnectionDisallowed", "This parameter is for internal use only.").ToString();
		}
		else if (UFunction* TargetFunction = GetTargetFunction())
		{
			const bool bIsObjectType = (MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object ||
				MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject) &&
				(OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object ||
				OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_SoftObject);

			if (// Strictly speaking this first check is not needed, but by not disabling the connection here we get a better reason later:
				(	OtherPin->PinType.IsContainer() 
					// make sure we don't allow connections of mismatched container types (e.g. maps to arrays)
					&& (OtherPin->PinType.ContainerType != MyPin->PinType.ContainerType)
					&& (
						(FEdGraphUtilities::IsSetParam(TargetFunction, MyPin->PinName) && !MyPin->PinType.IsSet()) ||
						(FEdGraphUtilities::IsMapParam(TargetFunction, MyPin->PinName) && !MyPin->PinType.IsMap()) ||
						(FEdGraphUtilities::IsArrayDependentParam(TargetFunction, MyPin->PinName) && !MyPin->PinType.IsArray())
					)
				)
			)
			{
				bIsDisallowed = true;
				OutReason = LOCTEXT("PinSetConnectionDisallowed", "Containers of containers are not supported - consider wrapping a container in a Structure object").ToString();
			}
			// Do not allow exec pins to be connected to a wildcard if this is a container function
			else if(MyPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Wildcard && OtherPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
			{
				bIsDisallowed = true;
				OutReason = LOCTEXT("PinExecConnectionDisallowed", "Cannot create a container of Exec pins.").ToString();
			}
			else if (bIsObjectType && MyPin->Direction == EGPD_Input && MyPin->PinType.IsContainer() && OtherPin->PinType.IsContainer())
			{
				// Check that we can actually connect the dependent pins to this new array
				const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(GetSchema());

				// Gather all pins that would be dependent on on the container type
				TArray<UEdGraphPin*> DependentPins;
				{
					for (UEdGraphPin* Pin : Pins)
					{
						if (Pin->Direction == EGPD_Input && Pin != MyPin && FEdGraphUtilities::IsDynamicContainerParam(TargetFunction, Pin->PinName))
						{
							DependentPins.Add(Pin);
						}
					}
				}

				for (UEdGraphPin* Pin : DependentPins)
				{
					// If the pins are both containers, then ArePinTypesCompatible will fail incorrectly.
					if (OtherPin->PinType.ContainerType != Pin->PinType.ContainerType)
					{
						continue;
					}

					UClass* Context = nullptr;
					UBlueprint* Blueprint = GetBlueprint();
					if (Blueprint)
					{
						Context = Blueprint->GeneratedClass;
					}

					const bool ConnectResponse = K2Schema->ArePinTypesCompatible(Pin->PinType, OtherPin->PinType, Context, /* bIgnoreArray = */ true);

					if (!ConnectResponse)
					{
						// For sets, we have to check if the other pin is a valid child that can actually 
						// be connected in cases like the "Union" function
						UStruct const* OutputObject = (OtherPin->PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? Context : Cast<UStruct>(OtherPin->PinType.PinSubCategoryObject.Get());
						UStruct const* InputObject = (Pin->PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self) ? Context : Cast<UStruct>(Pin->PinType.PinSubCategoryObject.Get());

						if (OtherPin->PinType.IsSet() && OutputObject && InputObject && OutputObject->IsChildOf(InputObject))
						{
							bIsDisallowed = false;
						}
						else
						{
							// Display the necessary tooltip on the pin hover, and log it if we are compiling
							FFormatNamedArguments MessageArgs;
							MessageArgs.Add(TEXT("PinAType"), UEdGraphSchema_K2::TypeToText(Pin->PinType));
							MessageArgs.Add(TEXT("PinBType"), UEdGraphSchema_K2::TypeToText(OtherPin->PinType));
							UBlueprint* BP = GetBlueprint();
							UEdGraph* OwningGraph = GetGraph();

							OutReason = FText::Format(LOCTEXT("DefaultPinIncompatibilityMessage", "{PinAType} is not compatible with {PinBType}."), MessageArgs).ToString();
							return true;
						}
					}
				}
			}
		}
	}

	return bIsDisallowed;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/407820.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Vuforia+Unity】AR06-空间环境识别功能(AreaTargets)

Vuforia原理:把被识别的物体转成图、立体图、柱形图,3D模型、环境模型,然后模型生成Vuforia数据库-导入Unity-参考模型位置开始摆放数字内容,然后参考模型自动隐藏-发布APP-识别生活中实物-数字内容叠加上去! 不论你是否曾有过相关经验,只要跟随本文的步骤,你就可以成功…

uni-app vue3 setup nvue中webview层级覆盖问题

核心就是这两行&#xff0c;&#x1f923;发现设置后不能点击了&#xff0c;这个玩意可能只能弹窗打开的时候动态的修改 position: static, zindex: 0onLoad(options > {loadWebview()})function loadWebview() {let pageInfo uni.getSystemInfoSync();width.value pageI…

强大到怀疑人生!AI视频生成必备的工具推荐!

刚发现的超牛逼AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频——播放量随便破百万。地址&#xff1a;跳转提示 - 3MW短网址https://3mw.cn/3ed5 这个Ai漫画推文软件的优势&#xff1a; 1、无需本地部署&#xff0c;对电脑…

Visual Studio:Entity设置表之间的关联关系

1、选择表并右键-》新增-》关联 2、设置关联的表及关联关系并“确定”即可

机器学习模型的过拟合与欠拟合

机器学习模型的训练过程中&#xff0c;可能会出现3种情况&#xff1a;模型欠拟合、模型正常拟合与模型过拟合。其中模型欠拟合与模型过拟合都是不好的情况。下面将会从不同的角度介绍如何判断模型属于哪种拟合情况。 &#xff08;1&#xff09;欠拟合与过拟合表现方式 欠拟合…

phtread_cancel函数用于取消线程,但不是实时的

如上图所示&#xff0c;线程函数中没有取消点&#xff08;一般是一些系统调用----man 7 pthreads查看&#xff0c;自定义函数是无效的&#xff09;&#xff0c;则使用pthread_cancle函数不生效。 解决方法&#xff1a;可以添加pthread_testcancle(); 通过pthread_join回收的…

广联达Linkworks GetAllData 信息泄露漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

MATLAB练习题:计算中国式排名

​讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 下表给出了两种不同的排名结果&#xff0c;成绩越高排名越靠前…

基于Springboot的校园求职招聘系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园求职招聘系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

剪辑视频调色怎么让画质变得清晰 视频剪辑调色技巧有哪些方面 剪辑视频免费的软件有哪些 会声会影调色在哪里 会声会影模板素材

视频调色的作用有很多&#xff0c;除了进行风格化剪辑以外&#xff0c;还可以让作品的画质变得清晰。通过调色来增强画面的清晰度&#xff0c;在观感上也会显得十分自然。视频调色的技巧有很多&#xff0c;并且原理大都十分简单。有关剪辑视频调色怎么让画质变得清晰&#xff0…

神经网络系列---感知机(Neuron)

文章目录 感知机(Neuron)感知机(Neuron)的决策函数可以表示为&#xff1a;感知机(Neuron)的学习算法主要包括以下步骤&#xff1a;感知机可以实现逻辑运算中的AND、OR、NOT和异或(XOR)运算。 感知机(Neuron) 感知机(Neuron)是一种简单而有效的二分类算法&#xff0c;用于将输入…

MATLAB环境下基于NLEO的算法的脑电EEG信号自发活动瞬态检测

自发脑电信号是一种非平稳性很强的随机信号。在传统的脑电信号处理中&#xff0c;较公认的处理方法大多是建立在假设脑电图是准平稳信号的基础上&#xff0c;即认为它可以分成若干段&#xff0c;每一段的过程基本平稳&#xff0c;但段上叠加着瞬态。瞬态信号是有别于背景节率&a…

leetcode:491.递增子序列

1.误区&#xff1a;不能直接对数组排序再求解子集&#xff0c;因为那样就改变了原有数组的顺序 2.树形结构&#xff1a;一个一个取数&#xff0c;然后保证是递增序列&#xff0c;且不能重复。&#xff08;数层上不可以重复取&#xff0c;树枝上可以重复取&#xff09;收集的结…

使用PM2实现高效的应用监控与管理

微信搜索“好朋友乐平”关注公众号。 1. pm2 PM2 是一个流行的进程管理器&#xff0c;用于 Node.js 应用程序。它支持应用程序的负载均衡、自动重启、日志管理、监控以及多环境管理等功能。PM2让开发者能够以守护进程的方式运行和管理 Node.js 应用&#xff0c;即使在应用崩溃…

This dependency was not found解决方法

问题如上(前端代码)&#xff0c;我是引用js文件出的问题&#xff0c;无法找到api/userManage模块。 解决&#xff1a;没感觉哪有问题&#xff0c;把后面加了个/&#xff0c;就解决了&#xff0c;代表src目录&#xff0c;应该是目录和目录之间应该有/作为分割&#xff1a;

计算机网络-局域网

文章目录 局域网局域网拓扑结构以太网以太网传输介质以太网时隙提高传统以太网带宽的途径以太网帧格式 局域网协议IEEE 802参考模型IEEE802.2协议LLC帧格式及其控制字段LLC提供的三种服务 IEEE 802.3协议IEEE 802.4协议IEEE 802.5协议 高速局域网100M以太网千兆以太网万兆以太网…

数理统计的基本概念

文章目录 前提概念性质常用的统计量 前提概念 与概率论的区别&#xff0c;他是基于实际数据的&#xff0c;但是概率是理论计算的结果。 总体&#xff1a;与所研究问题相关的对象的全体。 样本&#xff1a;按照一定的规定&#xff08;每个个体被抽中的概率相同&#xff09;&…

C/C++暴力/枚举/穷举题目(刷蓝桥杯基础题的进!)

目录 前言 一、百钱买百鸡 二、百元兑钞 三、门牌号码&#xff08;蓝桥杯真题&#xff09; 四、相乘&#xff08;蓝桥杯真题&#xff09; 五、卡片拼数字&#xff08;蓝桥杯真题&#xff09; 六、货物摆放&#xff08;蓝桥杯真题&#xff09; 七、最短路径&#xff08;蓝…

人工智能 — 特征选择、特征提取、PCA

目录 一、特征选择1、定义2、原因3、做法4、生成过程5、停止条件 二、特征提取三、PCA 算法1、零均值化&#xff08;中心化&#xff09;2、方差3、协方差4、协方差矩阵5、对协方差矩阵求特征值、特征矩阵6、对特征值进行排序7、评价模型8、代码实现9、sklearn 库10、鸢尾花实例…

MySQL死锁产生的原因和解决方法

一.什么是死锁 要想知道MYSQL死锁产生的原因,就要知道什么是死锁?在了解什么是死锁之前,先来看一个概念:线程安全问题 1.线程安全问题 1.1什么是线程安全问题 线程安全问题&#xff0c;指的是在多线程环境当中&#xff0c;线程并发访问某个资源&#xff0c;从而导致的原子性&a…