Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_CallFunction.cpp
Michael Schoell d0033ce686 #summary Added a "Go to Definition" menu entry when right clicking on call function nodes in Blueprints
#change UK2Node_CallFunction::GetFunctionGraph now returns the proper graph from whatever Blueprint the function belongs to

#change Cleaned up double clicking on function call nodes

#ttp 316688 - UE4: BLUEPRINTS: Go to definition behavior should be available in the right-click context menu for function calls

[CL 2270980 by Michael Schoell in Main branch]
2014-08-25 15:47:12 -04:00

1584 lines
52 KiB
C++

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "BlueprintGraphPrivatePCH.h"
#include "CompilerResultsLog.h"
#include "CallFunctionHandler.h"
#include "K2Node_SwitchEnum.h"
#define LOCTEXT_NAMESPACE "K2Node"
struct FCustomStructureParamHelper
{
static FName GetCustomStructureParamName()
{
static FName Name(TEXT("CustomStructureParam"));
return Name;
}
static void FillCustomStructureParameterNames(const UFunction* Function, TArray<FString>& OutNames)
{
OutNames.Empty();
if (Function)
{
FString MetaDataValue = Function->GetMetaData(GetCustomStructureParamName());
if (!MetaDataValue.IsEmpty())
{
MetaDataValue.ParseIntoArray(&OutNames, TEXT(","), true);
}
}
}
static void HandleSinglePin(UEdGraphPin* Pin)
{
if (Pin)
{
if (Pin->LinkedTo.Num() > 0)
{
UEdGraphPin* LinkedTo = Pin->LinkedTo[0];
check(LinkedTo);
ensure(!LinkedTo->PinType.bIsArray);
Pin->PinType = LinkedTo->PinType;
}
else
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
Pin->PinType.PinCategory = Schema->PC_Wildcard;
Pin->PinType.PinSubCategory = TEXT("");
Pin->PinType.PinSubCategoryObject = NULL;
}
}
}
static void UpdateCustomStructurePins(const UFunction* Function, UK2Node* Node, UEdGraphPin* SinglePin = NULL)
{
if (Function && Node)
{
TArray<FString> Names;
FCustomStructureParamHelper::FillCustomStructureParameterNames(Function, Names);
if (SinglePin)
{
if (Names.Contains(SinglePin->PinName))
{
HandleSinglePin(SinglePin);
}
}
else
{
for (auto& Name : Names)
{
if (auto Pin = Node->FindPin(Name))
{
HandleSinglePin(Pin);
}
}
}
}
}
};
UK2Node_CallFunction::UK2Node_CallFunction(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
}
bool UK2Node_CallFunction::IsDeprecated() const
{
UFunction* Function = GetTargetFunction();
return (Function != NULL) && Function->HasMetaData(FBlueprintMetadata::MD_DeprecatedFunction);
}
bool UK2Node_CallFunction::ShouldWarnOnDeprecation() const
{
// TEMP: Do not warn in the case of SpawnActor, as we have a special upgrade path for those nodes
return (FunctionReference.GetMemberName() != FName(TEXT("BeginSpawningActorFromBlueprint")));
}
FString UK2Node_CallFunction::GetDeprecationMessage() const
{
UFunction* Function = GetTargetFunction();
if ((Function != NULL) && Function->HasMetaData(FBlueprintMetadata::MD_DeprecationMessage))
{
return FString::Printf(TEXT("%s %s"), *LOCTEXT("CallFunctionDeprecated_Warning", "@@ is deprecated;").ToString(), *Function->GetMetaData(FBlueprintMetadata::MD_DeprecationMessage));
}
return Super::GetDeprecationMessage();
}
FString UK2Node_CallFunction::GetFunctionContextString() const
{
FString 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();
}
FString TargetString = (TrueSelfClass != NULL) ? TrueSelfClass->GetName() : TEXT("None");
// This action won't be necessary once the new name convention is used.
if(TargetString.EndsWith(TEXT("_C")))
{
TargetString = TargetString.LeftChop(2);
}
ContextString = FString(TEXT("\n")) + FString::Printf(*LOCTEXT("CallFunctionOnDifferentContext", "Target is %s").ToString(), *TargetString);
}
return ContextString;
}
FText UK2Node_CallFunction::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
FString FunctionName;
FString ContextString;
FString RPCString;
if (UFunction* Function = GetTargetFunction())
{
RPCString = UK2Node_Event::GetLocalizedNetString(Function->FunctionFlags, true);
FunctionName = UK2Node_CallFunction::GetUserFacingFunctionName(Function);
ContextString = GetFunctionContextString();
}
else
{
FunctionName = FunctionReference.GetMemberName().ToString();
if ((GEditor != NULL) && (GetDefault<UEditorStyleSettings>()->bShowFriendlyNames))
{
FunctionName = FName::NameToDisplayString(FunctionName, false);
}
}
if(TitleType == ENodeTitleType::FullTitle)
{
FFormatNamedArguments Args;
Args.Add(TEXT("FunctionName"), FText::FromString(FunctionName));
Args.Add(TEXT("ContextString"), FText::FromString(ContextString));
Args.Add(TEXT("RPCString"), FText::FromString(RPCString));
return FText::Format(LOCTEXT("CallFunction_FullTitle", "{FunctionName}{ContextString}{RPCString}"), Args);
}
else
{
return FText::FromString(FunctionName);
}
}
void UK2Node_CallFunction::AllocateDefaultPins()
{
UBlueprint* MyBlueprint = GetBlueprint();
UFunction* Function = GetTargetFunction();
// First try remap table
if (Function == NULL)
{
UClass* ParentClass = FunctionReference.GetMemberParentClass(this);
if (ParentClass != NULL)
{
if (UFunction* NewFunction = Cast<UFunction>(FindRemappedField(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 = FindField<UFunction>(TestClass, FunctionReference.GetMemberName());
if (Function != NULL)
{
UClass* OldClass = FunctionReference.GetMemberParentClass(this);
Message_Note( FString::Printf(*LOCTEXT("FixedUpFunctionInLibrary", "UK2Node_CallFunction: Fixed up function '%s', originally in '%s', now in library '%s'.").ToString(),
*FunctionReference.GetMemberName().ToString(),
(OldClass != NULL) ? *OldClass->GetName() : TEXT("(null)"), *TestClass->GetName()) );
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)
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
for(int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++)
{
if(Pins[PinIdx]->PinName == K2Schema->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 = FindField<UFunction>(SelfPinClass, FunctionReference.GetMemberName()))
{
SetFromFunction(NewFunction);
}
}
}
}
// END TEMP
Super::ReallocatePinsDuringReconstruction(OldPins);
}
UEdGraphPin* UK2Node_CallFunction::CreateSelfPin(const UFunction* Function)
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
// Chase up the function's Super chain, the function can be called on any object that is at least that specific
const UFunction* FirstDeclaredFunction = Function;
while (FirstDeclaredFunction->GetSuperFunction() != NULL)
{
FirstDeclaredFunction = FirstDeclaredFunction->GetSuperFunction();
}
// Create the self pin
UClass* FunctionClass = CastChecked<UClass>(FirstDeclaredFunction->GetOuter());
UEdGraphPin* SelfPin = NULL;
if ((FunctionClass == GetBlueprint()->GeneratedClass) || (FunctionClass == GetBlueprint()->SkeletonGeneratedClass))
{
// This means the function is defined within the blueprint, so the pin should be a true "self" pin
SelfPin = CreatePin(EGPD_Input, K2Schema->PC_Object, K2Schema->PSC_Self, NULL, false, false, K2Schema->PN_Self);
}
else if (FunctionClass->IsChildOf(UInterface::StaticClass()))
{
SelfPin = CreatePin(EGPD_Input, K2Schema->PC_Interface, TEXT(""), FunctionClass, false, false, K2Schema->PN_Self);
}
else
{
// This means that the function is declared in an external class, and should reference that class
SelfPin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), FunctionClass, false, false, K2Schema->PN_Self);
}
check(SelfPin != NULL);
return SelfPin;
}
void UK2Node_CallFunction::CreateExecPinsForFunctionCall(const UFunction* Function)
{
bool bCreateSingleExecInputPin = true;
bool bCreateThenPin = true;
// If not pure, create exec pins
if (!bIsPureFunc)
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
// If we want enum->exec expansion, and it is not disabled, do it now
if(bWantsEnumToExecExpansion)
{
const FString& EnumParamName = Function->GetMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs);
UByteProperty* EnumProp = FindField<UByteProperty>(Function, FName(*EnumParamName));
if(EnumProp != NULL && EnumProp->Enum != NULL)
{
const bool bIsFunctionInput = !EnumProp->HasAnyPropertyFlags(CPF_ReturnParm) &&
(!EnumProp->HasAnyPropertyFlags(CPF_OutParm) ||
EnumProp->HasAnyPropertyFlags(CPF_ReferenceParm));
const EEdGraphPinDirection Direction = bIsFunctionInput ? EGPD_Input : EGPD_Output;
// yay, found it! Now create exec pin for each
int32 NumExecs = (EnumProp->Enum->NumEnums() - 1);
for(int32 ExecIdx=0; ExecIdx<NumExecs; ExecIdx++)
{
FString ExecName = EnumProp->Enum->GetEnumName(ExecIdx);
CreatePin(Direction, K2Schema->PC_Exec, TEXT(""), NULL, false, false, ExecName);
}
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, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Execute);
}
if (bCreateThenPin)
{
UEdGraphPin* OutputExecPin = CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Then);
// Use 'completed' name for output pins on latent functions
if(Function->HasMetaData(FBlueprintMetadata::MD_Latent))
{
OutputExecPin->PinFriendlyName = FText::FromString(K2Schema->PN_Completed);
}
}
}
}
void UK2Node_CallFunction::DetermineWantsEnumToExecExpansion(const UFunction* Function)
{
bWantsEnumToExecExpansion = false;
if (Function->HasMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs))
{
const FString& EnumParamName = Function->GetMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs);
UByteProperty* EnumProp = FindField<UByteProperty>(Function, FName(*EnumParamName));
if(EnumProp != NULL && EnumProp->Enum != NULL)
{
bWantsEnumToExecExpansion = true;
}
else
{
if (!bHasCompilerMessage)
{
//put in warning state
bHasCompilerMessage = true;
ErrorType = EMessageSeverity::Warning;
ErrorMsg = FString::Printf(*LOCTEXT("EnumToExecExpansionFailed", "Unable to find enum parameter with name '%s' to expand for @@").ToString(), *EnumParamName);
}
}
}
}
void UK2Node_CallFunction::GeneratePinHoverText(const UEdGraphPin& Pin, FString& HoverTextOut) 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, TEXT(""), HoverTextOut);
return;
}
// figure what tag we should be parsing for (is this a return-val pin, or a parameter?)
FString ParamName;
FString TagStr = TEXT("@param");
if (Pin.PinName == K2Schema->PN_ReturnValue)
{
TagStr = TEXT("@return");
}
else
{
ParamName = Pin.PinName.ToLower();
}
// get the class function object associated with this node
UFunction* Function = GetTargetFunction();
if (Function == NULL)
{
Schema->ConstructBasicPinTooltip(Pin, TEXT(""), HoverTextOut);
return;
}
// grab the the function's comment block for us to parse
FString FunctionToolTipText = GetDefaultTooltipForFunction(Function);
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();
// 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)
while(CurStrPos < FullToolTipLen && FChar::IsWhitespace(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(!FChar::IsLinebreak(FunctionToolTipText[CurStrPos]))
{
ParamDesc.AppendChar(TEXT(' '));
}
}
if (FunctionToolTipText[CurStrPos] != TEXT('@'))
{
ParamDesc.AppendChar(FunctionToolTipText[CurStrPos++]);
}
}
// trim any trailing whitespace from the descriptive text
ParamDesc.TrimTrailing();
// if we came up with a valid description for the param/return-val
if (!ParamDesc.IsEmpty())
{
HoverTextOut += FString(TEXT("\n")) + ParamDesc;
break; // we found a match, so there's no need to continue
}
} while (CurStrPos < FullToolTipLen);
Schema->ConstructBasicPinTooltip(Pin, HoverTextOut, HoverTextOut);
}
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");
// fill out the self-pin's default tool-tip
GeneratePinHoverText(*SelfPin, SelfPin->PinToolTip);
const bool bIsProtectedFunc = Function->GetBoolMetaData(FBlueprintMetadata::MD_Protected);
const bool bIsStaticFunc = Function->HasAllFunctionFlags(FUNC_Static);
//@TODO: Can't strictly speaking always hide the self pin for pure and static functions; it'll still be needed if the function belongs to a class not in the blueprint's class hierarchy
SelfPin->bHidden = ((bIsPureFunc && !bIsConstFunc) || bIsProtectedFunc || bIsStaticFunc);
UBlueprint* BP = GetBlueprint();
ensure(BP);
if (bIsStaticFunc && BP)
{
// Wire up the self to the CDO of the class if it's not us
if (!BP->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass))
{
SelfPin->DefaultObject = FunctionOwnerClass->GetDefaultObject();
}
}
// Build a list of the pins that should be hidden for this function (ones that are automagically filled in by the K2 compiler)
TSet<FString> PinsToHide;
FBlueprintEditorUtils::GetHiddenPinsForFunction(BP, Function, PinsToHide);
const bool bShowHiddenSelfPins = ((PinsToHide.Num() > 0) && BP && BP->ParentClass && BP->ParentClass->HasMetaData(FBlueprintMetadata::MD_ShowHiddenSelfPins));
// Create the inputs and outputs
bool bAllPinsGood = true;
for (TFieldIterator<UProperty> PropIt(Function); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
UProperty* 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;
UEdGraphPin* Pin = CreatePin(Direction, TEXT(""), TEXT(""), NULL, false, bIsRefParam, Param->GetName());
const bool bPinGood = (Pin != NULL) && K2Schema->ConvertPropertyToPinType(Param, /*out*/ Pin->PinType);
if (bPinGood)
{
//Flag pin as read only for const reference property
Pin->bDefaultValueIsIgnored = Param->HasAllPropertyFlags(CPF_ConstParm | CPF_ReferenceParm) && (!Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm) || Pin->PinType.bIsArray);
const bool bAdvancedPin = Param->HasAllPropertyFlags(CPF_AdvancedDisplay);
Pin->bAdvancedView = bAdvancedPin;
if(bAdvancedPin && (ENodeAdvancedPins::NoPins == AdvancedPinDisplay))
{
AdvancedPinDisplay = ENodeAdvancedPins::Hidden;
}
K2Schema->SetPinDefaultValue(Pin, Function, Param);
// setup the default tool-tip text for this pin
GeneratePinHoverText(*Pin, Pin->PinToolTip);
if (PinsToHide.Contains(Pin->PinName))
{
FString const DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
FString const WorldContextMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);
bool bIsSelfPin = ((Pin->PinName == DefaultToSelfMetaValue) || (Pin->PinName == WorldContextMetaValue));
if (!bShowHiddenSelfPins || !bIsSelfPin)
{
Pin->bHidden = true;
K2Schema->SetPinDefaultValueBasedOnType(Pin);
}
}
PostParameterPinCreated(Pin);
}
bAllPinsGood = bAllPinsGood && bPinGood;
}
// If we have an 'enum to exec' parameter, set its default value to something valid so we don't get warnings
if(bWantsEnumToExecExpansion)
{
FString EnumParamName = Function->GetMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs);
UEdGraphPin* EnumParamPin = FindPin(EnumParamName);
if (UEnum* PinEnum = (EnumParamPin ? Cast<UEnum>(EnumParamPin->PinType.PinSubCategoryObject.Get()) : NULL))
{
EnumParamPin->DefaultValue = PinEnum->GetEnumName(0);
}
}
return bAllPinsGood;
}
void UK2Node_CallFunction::PostReconstructNode()
{
Super::PostReconstructNode();
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
// 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(K2Schema->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->GetDefaultObject();
}
}
}
// Set the return type to the right class of component
UActorComponent* TemplateComp = GetTemplateFromNode();
UEdGraphPin* ReturnPin = GetReturnValuePin();
if(TemplateComp && ReturnPin)
{
ReturnPin->PinType.PinSubCategoryObject = TemplateComp->GetClass();
}
}
void UK2Node_CallFunction::DestroyNode()
{
// See if this node has a template
UActorComponent* Template = GetTemplateFromNode();
if (Template != NULL)
{
// Get the blueprint so we can remove it from it
UBlueprint* BlueprintObj = GetBlueprint();
// remove it
BlueprintObj->ComponentTemplates.Remove(Template);
}
Super::DestroyNode();
}
void UK2Node_CallFunction::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{
Super::NotifyPinConnectionListChanged(Pin);
if (Pin)
{
FCustomStructureParamHelper::UpdateCustomStructurePins(GetTargetFunction(), this, Pin);
}
if (bIsBeadFunction)
{
if (Pin->LinkedTo.Num() == 0)
{
// Commit suicide; bead functions must always have an input and output connection
DestroyNode();
}
}
}
UFunction* UK2Node_CallFunction::GetTargetFunction() const
{
UFunction* Function = FunctionReference.ResolveMember<UFunction>(this);
return Function;
}
UEdGraphPin* UK2Node_CallFunction::GetThenPin() const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraphPin* Pin = FindPin(K2Schema->PN_Then);
check(Pin == NULL || Pin->Direction == EGPD_Output); // If pin exists, it must be output
return Pin;
}
UEdGraphPin* UK2Node_CallFunction::GetReturnValuePin() const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraphPin* Pin = FindPin(K2Schema->PN_ReturnValue);
check(Pin == NULL || 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())
{
const bool bHasReturnParams = (Function->GetReturnProperty() != NULL);
return !bHasReturnParams && !IsNodePure() && !IsLatentFunction();
}
return Super::AllowMultipleSelfs(bInputAsArray);
}
FLinearColor UK2Node_CallFunction::GetNodeTitleColor() const
{
if (IsNodePure())
{
return GetDefault<UGraphEditorSettings>()->PureFunctionCallNodeTitleColor;
}
return Super::GetNodeTitleColor();
}
FString UK2Node_CallFunction::GetTooltip() const
{
FString Tooltip;
if (UFunction* Function = GetTargetFunction())
{
Tooltip = GetDefaultTooltipForFunction(Function);
FString ClientString;
if (Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly))
{
ClientString = NSLOCTEXT("K2Node", "ServerFunction", "\n\nAuthority Only. This function will only execute on the server.").ToString();
}
else if (Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic))
{
ClientString = NSLOCTEXT("K2Node", "ClientEvent", "\n\nCosmetic. This event is only for cosmetic, non-gameplay actions.").ToString();
}
Tooltip += ClientString;
}
else
{
Tooltip = LOCTEXT("CallUnknownFunction", "Call unknown function ").ToString() + FunctionReference.GetMemberName().ToString();
}
return Tooltip;
}
FString UK2Node_CallFunction::GetUserFacingFunctionName(const UFunction* Function)
{
FString FunctionName = Function->GetMetaData(TEXT("FriendlyName"));
if (FunctionName.IsEmpty())
{
FunctionName = Function->GetName();
}
if( GEditor && GetDefault<UEditorStyleSettings>()->bShowFriendlyNames )
{
FunctionName = FName::NameToDisplayString(FunctionName, false);
}
return FunctionName;
}
FString UK2Node_CallFunction::GetDefaultTooltipForFunction(const UFunction* Function)
{
const FString Tooltip = Function->GetToolTipText().ToString();
if (!Tooltip.IsEmpty())
{
return Tooltip;
}
else
{
return GetUserFacingFunctionName(Function);
}
}
FString UK2Node_CallFunction::GetDefaultCategoryForFunction(const UFunction* Function, const FString& BaseCategory)
{
FString NodeCategory = BaseCategory;
if( Function->HasMetaData(FBlueprintMetadata::MD_FunctionCategory) )
{
// Add seperator if base category is supplied
if(NodeCategory.Len() > 0)
{
NodeCategory += TEXT("|");
}
// Add category from function
FString FuncCategory = Function->GetMetaData(FBlueprintMetadata::MD_FunctionCategory);
if( GEditor && GetDefault<UEditorStyleSettings>()->bShowFriendlyNames )
{
FuncCategory = FName::NameToDisplayString( FuncCategory, false );
}
NodeCategory += FuncCategory;
}
return NodeCategory;
}
FString 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) )
{
Keywords = Function->GetName();
}
if (ShouldDrawCompact(Function))
{
Keywords.AppendChar(TEXT(' '));
Keywords += GetCompactNodeTitle(Function);
}
FString MetaKeywords = Function->GetMetaData(FBlueprintMetadata::MD_FunctionKeywords);
if (!MetaKeywords.IsEmpty())
{
Keywords.AppendChar(TEXT(' '));
Keywords += MetaKeywords;
}
return Keywords;
}
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, this);
}
}
FString UK2Node_CallFunction::GetDocumentationLink() const
{
UClass* ParentClass = NULL;
if (FunctionReference.IsSelfContext())
{
if (HasValidBlueprint())
{
UFunction* Function = FindField<UFunction>(GetBlueprint()->GeneratedClass, FunctionReference.GetMemberName());
if (Function != NULL)
{
ParentClass = Function->GetOwnerClass();
}
}
}
else
{
ParentClass = FunctionReference.GetMemberParentClass(this);
}
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)
{
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(this);
while (FunctionClass)
{
RedirectPinNames.Add(FString::Printf(TEXT("%s.%s.%s"), *FunctionClass->GetName(), *FunctionReference.GetMemberName().ToString(), *OldPinName));
FunctionClass = FunctionClass->GetSuperClass();
}
}
}
bool UK2Node_CallFunction::IsSelfPinCompatibleWithBlueprintContext(UEdGraphPin *SelfPin, UBlueprint* BlueprintObj) const
{
check(BlueprintObj);
UClass* FunctionClass = FunctionReference.GetMemberParentClass(this);
bool bIsCompatible = (SelfPin != NULL) ? SelfPin->bHidden : true;
if (!bIsCompatible && (BlueprintObj->GeneratedClass != NULL))
{
bIsCompatible |= BlueprintObj->GeneratedClass->IsChildOf(FunctionClass);
}
if (!bIsCompatible && (BlueprintObj->SkeletonGeneratedClass != NULL))
{
bIsCompatible |= BlueprintObj->SkeletonGeneratedClass->IsChildOf(FunctionClass);
}
return bIsCompatible;
}
void UK2Node_CallFunction::EnsureFunctionIsInBlueprint()
{
// Ensure we're calling a function in a context related to our blueprint. If not,
// reassigning the class and then calling ReconstructNodes will re-wire the pins correctly
if (UFunction* Function = GetTargetFunction())
{
UClass* FunctionOwnerClass = Function->GetOuterUClass();
UObject* FunctionGenerator = FunctionOwnerClass ? FunctionOwnerClass->ClassGeneratedBy : NULL;
// If function is generated from a blueprint object then dbl check self pin compatibility
UEdGraphPin* SelfPin = GetDefault<UEdGraphSchema_K2>()->FindSelfPin(*this, EGPD_Input);
if ((FunctionGenerator != NULL) && SelfPin)
{
UBlueprint* BlueprintObj = FBlueprintEditorUtils::FindBlueprintForNode(this);
if ((BlueprintObj != NULL) && !IsSelfPinCompatibleWithBlueprintContext(SelfPin, BlueprintObj))
{
FunctionReference.SetSelfMember(Function->GetFName());
}
}
}
}
void UK2Node_CallFunction::PostPasteNode()
{
Super::PostPasteNode();
EnsureFunctionIsInBlueprint();
UFunction* Function = GetTargetFunction();
if(Function != NULL)
{
// 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<FString> PinsToHide;
FBlueprintEditorUtils::GetHiddenPinsForFunction(GetBlueprint(), Function, PinsToHide);
const bool bShowHiddenSelfPins = ((PinsToHide.Num() > 0) && GetBlueprint()->ParentClass->HasMetaData(FBlueprintMetadata::MD_ShowHiddenSelfPins));
FString const DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
FString const 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];
bool bIsSelfPin = ((Pin->PinName == DefaultToSelfMetaValue) || (Pin->PinName == WorldContextMetaValue));
bool bPinShouldBeHidden = PinsToHide.Contains(Pin->PinName) && (!bShowHiddenSelfPins || !bIsSelfPin);
if (bPinShouldBeHidden && !Pin->bHidden)
{
Pin->BreakAllPinLinks();
K2Schema->SetPinDefaultValueBasedOnType(Pin);
}
Pin->bHidden = bPinShouldBeHidden;
}
}
}
void UK2Node_CallFunction::PostDuplicate(bool bDuplicateForPIE)
{
Super::PostDuplicate(bDuplicateForPIE);
if (!bDuplicateForPIE && (!this->HasAnyFlags(RF_Transient)))
{
FunctionReference.InvalidateSelfScope();
EnsureFunctionIsInBlueprint();
}
}
void UK2Node_CallFunction::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
{
Super::ValidateNodeDuringCompilation(MessageLog);
UFunction *Function = GetTargetFunction();
if (Function == NULL)
{
MessageLog.Warning(*FString::Printf(*LOCTEXT("FunctionNotFound", "Unable to find function with name '%s' for @@").ToString(), *FunctionReference.GetMemberName().ToString()), this);
}
else if (Function->HasMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs) && bWantsEnumToExecExpansion == false)
{
const FString& EnumParamName = Function->GetMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs);
MessageLog.Warning(*FString::Printf(*LOCTEXT("EnumToExecExpansionFailed", "Unable to find enum parameter with name '%s' to expand for @@").ToString(), *EnumParamName), this);
}
// enforce UnsafeDuringActorConstruction keyword
if (Function && Function->HasMetaData(FBlueprintMetadata::MD_UnsafeForConstructionScripts))
{
// emit warning if we are in a construction script
UEdGraph const* const Graph = GetGraph();
UEdGraphSchema_K2 const* const Schema = Cast<const UEdGraphSchema_K2>(GetSchema());
bool bNodeIsInConstructionScript = Schema && Schema->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 = FindField<UFunction>(Node->SignatureClass, Node->SignatureName);
bNodeIsInConstructionScript = SignatureFunction && (SignatureFunction->GetFName() == Schema->FN_UserConstructionScript);
}
}
}
if ( bNodeIsInConstructionScript )
{
MessageLog.Warning(*LOCTEXT("FunctionUnsafeDuringConstruction", "Function '@@' is unsafe to call in a construction script.").ToString(), this);
}
}
}
void UK2Node_CallFunction::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
if (Ar.IsLoading())
{
if (Ar.UE4Ver() < VER_UE4_SWITCH_CALL_NODE_TO_USE_MEMBER_REFERENCE)
{
UFunction* Function = FindField<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);
}
}
}
}
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>(this);
}
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);
if (CompilerContext.bIsFullCompile)
{
const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
UFunction* Function = GetTargetFunction();
// connect DefaultToSelf and WorldContext inside static functions to proper 'self'
const bool bInsideBpFuncLibrary = CompilerContext.Blueprint && (BPTYPE_FunctionLibrary == CompilerContext.Blueprint->BlueprintType);
if (bInsideBpFuncLibrary && SourceGraph && Function)
{
TArray<UK2Node_FunctionEntry*> EntryPoints;
SourceGraph->GetNodesOfClass(EntryPoints);
if (1 != EntryPoints.Num())
{
CompilerContext.MessageLog.Warning(*FString::Printf(*LOCTEXT("WrongEntryPointsNum", "%i entry points found while expanding node @@").ToString(), EntryPoints.Num()), this);
}
else if (auto BetterSelfPin = EntryPoints[0]->GetAutoWorldContextPin())
{
FString const DefaultToSelfMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_DefaultToSelf);
FString const WorldContextMetaValue = Function->GetMetaData(FBlueprintMetadata::MD_WorldContext);
struct FStructConnectHelper
{
static void Connect(const FString& PinName, UK2Node* Node, UEdGraphPin* BetterSelf, const UEdGraphSchema_K2* InSchema, FCompilerResultsLog& MessageLog)
{
auto 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);
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)
{
// Get the metadata that identifies which param is the enum, and try and find it
const FString& EnumParamName = Function->GetMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs);
UByteProperty* EnumProp = FindField<UByteProperty>(Function, FName(*EnumParamName));
UEdGraphPin* EnumParamPin = FindPinChecked(EnumParamName);
if(EnumProp != NULL && EnumProp->Enum != NULL)
{
// Expanded as input execs pins
if (EnumParamPin->Direction == EGPD_Input)
{
// Create normal exec input
UEdGraphPin* ExecutePin = CreatePin(EGPD_Input, Schema->PC_Exec, TEXT(""), NULL, false, false, Schema->PN_Execute);
// Create temp enum variable
UK2Node_TemporaryVariable* TempEnumVarNode = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph);
TempEnumVarNode->VariableType.PinCategory = Schema->PC_Byte;
TempEnumVarNode->VariableType.PinSubCategoryObject = EnumProp->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 == Schema->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;
// Finally remove this 'cosmetic' exec pin
Pins.RemoveAt(PinIdx);
}
}
}
// Expanded as output execs pins
else if (EnumParamPin->Direction == EGPD_Output)
{
// Create normal exec output
UEdGraphPin* ExecutePin = CreatePin(EGPD_Output, Schema->PC_Exec, TEXT(""), NULL, false, false, Schema->PN_Execute);
// 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();
// Hook up execution to the switch node
Schema->TryCreateConnection(ExecutePin, SwitchEnumNode->GetExecPin());
// Connect (hidden) enum pin to switch node's selection pin
Schema->TryCreateConnection(EnumParamPin, SwitchEnumNode->GetSelectionPin());
// Now we want to iterate over other exec outputs
for(int32 PinIdx=Pins.Num()-1; PinIdx>=0; PinIdx--)
{
UEdGraphPin* Pin = Pins[PinIdx];
if( Pin != NULL &&
Pin != ExecutePin &&
Pin->Direction == EGPD_Output &&
Pin->PinType.PinCategory == Schema->PC_Exec )
{
// Move connections from fake 'enum exec' pint to this switch node
CompilerContext.MovePinLinksToIntermediate(*Pin, *SwitchEnumNode->FindPinChecked(Pin->PinName));
// Finally remove this 'cosmetic' exec pin
Pins.RemoveAt(PinIdx);
}
}
}
}
}
}
// 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.bIsArray)
{
const bool bProperInputToExpandForEach =
(1 == MultiSelf->LinkedTo.Num()) &&
(NULL != MultiSelf->LinkedTo[0]) &&
(MultiSelf->LinkedTo[0]->PinType.bIsArray);
if(bProperInputToExpandForEach)
{
CallForEachElementInArrayExpansion(this, MultiSelf, CompilerContext, SourceGraph);
}
}
}
}
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.bIsArray);
ensure(bProperInputToExpandForEach);
UEdGraphPin* ThenPin = Node->FindPinChecked(Schema->PN_Then);
// Create int Iterator
UK2Node_TemporaryVariable* IteratorVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(Node, SourceGraph);
IteratorVar->VariableType.PinCategory = Schema->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(TEXT("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(TEXT("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(TEXT("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(TEXT("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();
}
FName UK2Node_CallFunction::GetPaletteIcon(FLinearColor& OutColor) const
{
static const FName NativeMakeFunc(TEXT("NativeMakeFunc"));
static const FName NativeBrakeFunc(TEXT("NativeBreakFunc"));
auto Function = GetTargetFunction();
if (Function && Function->HasMetaData(NativeMakeFunc))
{
return TEXT("GraphEditor.MakeStruct_16x");
}
else if (Function && Function->HasMetaData(NativeBrakeFunc))
{
return TEXT("GraphEditor.BreakStruct_16x");
}
// 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))
{
return TEXT("GraphEditor.Event_16x");
}
else
{
OutColor = GetNodeTitleColor();
return TEXT("Kismet.AllClasses.FunctionIcon");
}
}
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();
return FText::FromString(GetDefaultCategoryForFunction(TargetFunction, TEXT("")));
}
bool UK2Node_CallFunction::HasExternalBlueprintDependencies(TArray<class UStruct*>* OptionalOutput) const
{
const UClass* SourceClass = FunctionReference.GetMemberParentClass(this);
const UBlueprint* SourceBlueprint = GetBlueprint();
const bool bResult = (SourceClass != NULL) && (SourceClass->ClassGeneratedBy != NULL) && (SourceClass->ClassGeneratedBy != SourceBlueprint);
if (bResult && OptionalOutput)
{
OptionalOutput->Add(GetTargetFunction());
}
return bResult || Super::HasExternalBlueprintDependencies(OptionalOutput);
}
UEdGraph* UK2Node_CallFunction::GetFunctionGraph(const UEdGraphNode*& OutGraphNode) const
{
OutGraphNode = NULL;
// Search for the Blueprint owner of the function graph, climbing up through the Blueprint hierarchy
UClass* MemberParentClass = FunctionReference.GetMemberParentClass(this);
if(MemberParentClass != NULL)
{
UBlueprintGeneratedClass* ParentClass = Cast<UBlueprintGeneratedClass>(MemberParentClass);
if(ParentClass != NULL && ParentClass->ClassGeneratedBy != NULL)
{
UBlueprint* Blueprint = Cast<UBlueprint>(ParentClass->ClassGeneratedBy);
while(Blueprint != NULL)
{
UEdGraph* TargetGraph = FindObject<UEdGraph>(Blueprint, *(FunctionReference.GetMemberName().ToString()));
if((TargetGraph != NULL) && !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 = NULL;
TArray<UK2Node_CustomEvent*> CustomEventNodes;
FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, CustomEventNodes);
for (UK2Node_CustomEvent* CustomEvent : CustomEventNodes)
{
if(CustomEvent->CustomFunctionName == FunctionReference.GetMemberName())
{
OutGraphNode = CustomEvent;
return CustomEvent->GetGraph();
}
}
}
ParentClass = Cast<UBlueprintGeneratedClass>(Blueprint->ParentClass);
Blueprint = ParentClass != NULL ? Cast<UBlueprint>(ParentClass->ClassGeneratedBy) : NULL;
}
}
}
return NULL;
}
bool UK2Node_CallFunction::IsStructureWildcardProperty(const UFunction* Function, const FString& PropertyName)
{
if (Function && !PropertyName.IsEmpty())
{
TArray<FString> Names;
FCustomStructureParamHelper::FillCustomStructureParameterNames(Function, Names);
if (Names.Contains(PropertyName))
{
return true;
}
}
return false;
}
#undef LOCTEXT_NAMESPACE