You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3228282 on 2016/12/08 by Aaron.McLeran Adding ability to fix up existing sound classes - Utility "soundclassfixup" console command renames sound classes which are packaged inside other sound classes accidentally as new uniquely named packages - Also removes code which was allowing "NewSoundClass" behavior in sound class graphs to populate with existing sound classes. Instead, it *always* creates a new sound class and warns if the sound class already exists. Connecting existing sound classes is instead going to be done through dragging them into the graph from the content browser or from the sound class node itself. Change 3228774 on 2016/12/09 by Ori.Cohen Fix multi select being very slow in phat #JIRA UE-39559 Change 3229036 on 2016/12/09 by Marc.Audy Remove trivial overrides Change 3229130 on 2016/12/09 by Aaron.McLeran Fixing build error. Moving new code from CL 3228282 into WITH_EDITOR block since it's an editor-only operation Change 3229412 on 2016/12/09 by Aaron.McLeran Fixing 7.1 surround sound systems on PC by forcing them to load as 5.1. - We don't support 7.1 but 7.1 systems should at least behave as good as 5.1 Change 3229782 on 2016/12/09 by Marc.Audy Fixed crash when seamless travelling in PIE from levels other than the current editor level with a streaming sublevel shared with the current editor level (4.15) #jira UE-39407 Change 3229842 on 2016/12/09 by Marc.Audy Missing files for CL# 3229782 Change 3229905 on 2016/12/09 by Marc.Audy Check Owner has a valid world before tryign to access Scene (4.14.2) #jira UE-39560 Change 3229961 on 2016/12/09 by Aaron.McLeran UE-39650 Implementing CL 3229894 in Dev-Framework Change 3229964 on 2016/12/09 by Aaron.McLeran Removing redundant loop introduced from integration Change 3230722 on 2016/12/12 by Lukasz.Furman fixed vislog macros for recording thick segments #ue4 Change 3230864 on 2016/12/12 by Lina.Halper Fix crash with deleting pose #jira:UE-39584 Change3230893on 2016/12/12 by Marc.Audy Support more default values in UHT for FVector: ForwardVector, RightVector, and single float FVector constructor Change 3231189 on 2016/12/12 by Ori.Cohen Added bone name to the physics invalid operation warnings. Change3231420on 2016/12/12 by James.Golding Support per-component skel mesh weight override #jira UEFW-240 Change 3231422 on 2016/12/12 by James.Golding Test map for per-component skin weights Change 3231491 on 2016/12/12 by James.Golding Move , FPositionVertexBuffer and FStaticMeshVertexDataInterface into their own headers Move FStaticMeshVertexBuffer implementation into its own cpp Change 3231590 on 2016/12/12 by mason.seay Changed to box collision Change 3231900 on 2016/12/12 by Aaron.McLeran Switching to creating new master submixes rather than loading them Change 3231909 on 2016/12/12 by James.Golding Fix Mac CIS in StaticMeshVertexBuffer.h Change 3232157 on 2016/12/13 by Mieszko.Zielinski Fixed a silly bug in FBlackboardKeySelector::InitSelection resulting in the key selector picking first "ok-ish" value, even if it wasn't matching type filter #UE4 Change 3232162 on 2016/12/13 by Mieszko.Zielinski Fixed UNavigationSystem::bNavigationAutoUpdateEnabled getting ignored by recent addition to related condition in UNavigationSystem #UE4 Change 3232314 on 2016/12/13 by James.Golding Another attempt at fixing Mac CIS Change 3232322 on 2016/12/13 by Lukasz.Furman fixed order of nav area application and low area filter #ue4 Change 3232364 on 2016/12/13 by Thomas.Sarkanen Spline IK node Added new runtime & graph node to deform bones along a spline. Added edit mode to work with in the BP editor. Spline is specified within the node using control points. External spline could come later. Currently very expensive to evaluate as it regenerates the transformed spline and PWLA each frame. #jira UEFW-249 - Add spline IK node Change 3232589 on 2016/12/13 by Thomas.Sarkanen Fixed non-editor builds Change 3232654 on 2016/12/13 by Marc.Audy Don't rerun construction scripts when an actor has seamless traveled from another level (4.15) #jira UE-39699 Change 3232690 on 2016/12/13 by Martin.Wilson Remove unused member Change 3232691 on 2016/12/13 by Martin.Wilson Virtual bone additions: 1) Rename support 2) Ability to chain virtual bones (Have a virtual bone that is a child of another virtual bone) #jira UE-39710 Change 3232782 on 2016/12/13 by Danny.Bouimad Adding Test Content Change 3232843 on 2016/12/13 by danny.bouimad More Updates Change 3232981 on 2016/12/13 by Marc.Audy Fix CIS issues Change 3233075 on 2016/12/13 by mason.seay SplineIK asset for bug report Change 3233124 on 2016/12/13 by Ori.Cohen Added mass automation tests. Change 3233265 on 2016/12/13 by Ben.Marsh Build: Add support for building Orion and Fortnite precompiled binaries from Dev-Framework. Change 3233365 on 2016/12/13 by mason.seay Resaving with non-empty engine version Change 3233532 on 2016/12/13 by mason.seay Level blueprint clean up Change 3233571 on 2016/12/13 by Ben.Marsh Set up paths for precompiled binaries. Change 3233601 on 2016/12/13 by Ben.Marsh Build: Use the code CL rather than latest CL for precompiled binaries. Change 3234402 on 2016/12/14 by Ori.Cohen Physics: Fixed line traces not working properly in editor worlds when physics substepping was enabled (UE-36408) - Substepping relies on interpolating transforms over frames, but only game worlds will be ticked, so we now disallow this feature in non-game worlds. #jira UE-36408 Change 3234415 on 2016/12/14 by Ori.Cohen Fix CIS Change 3234574 on 2016/12/14 by Thomas.Sarkanen Fix crash when IK chain is inverted #jira UE-39720 - Crash compiling animation blueprint with Spline IK node Change 3234882 on 2016/12/14 by Ori.Cohen Fixed teleport not working for physical animation component Change 3234971 on 2016/12/14 by Aaron.McLeran Fix for omni-directional sounds in audio mixer Change 3235251 on 2016/12/14 by mason.seay Assets for proposed functional testing Change 3235492 on 2016/12/14 by Ori.Cohen Undo previous bad normal fix and remove wheel width compensation. This leads to bad normals when thick tires roll over the edge leading to instability. #JIRA UE-38710 Change 3236398 on 2016/12/15 by Marc.Audy (4.15) Add new object flag RF_NeedInitialization to indicate that ~FObjectInitalizer and PostInitProperties have not been executed for the object Do not allow Modify calls on Objects that have not been initialized #jira UE-39731 Change 3236413 on 2016/12/15 by Lukasz.Furman added EQS profiler #ue4 Change 3236418 on 2016/12/15 by Lukasz.Furman changed log verbosity in navmesh geometry export function #jira UE-39809 #3039 Change 3236508 on 2016/12/15 by Ori.Cohen Allow vehicles to override inertia tensor after any mass properties have changed #JIRA UE-39566 Change 3236573 on 2016/12/15 by Ori.Cohen Fix manipulation tool not working properly with welded components Change 3236577 on 2016/12/15 by Ori.Cohen Improve physics asset body creation so that it merges small bones and turns off collision between initially overlapping bodies. Change 3236580 on 2016/12/15 by Ori.Cohen Improve mass computation for physics shapes (ignore trimesh which introduces error) Change 3236581 on 2016/12/15 by Ori.Cohen Fix incorrect inertia tensor computation for cubes (was being doubled by mistake). Change 3236809 on 2016/12/15 by Lukasz.Furman compilation fix: missing headers in EnvQueryManager Change 3237187 on 2016/12/15 by Lukasz.Furman compilation fix: missing defines in EnvQueryInstance Change 3237423 on 2016/12/15 by Aaron.McLeran Audio mixer: Allow center channel panning as a project setting. - To better support previous audio engine behavior, allow audio mixer to pan audio to center channel via audio settings. Change 3237639 on 2016/12/15 by Aaron.McLeran Audio mixer stat tracking Change 3237646 on 2016/12/15 by dan.reynolds MIDI Test Assets: General MIDITestBP MPKmini2 Child BP MPKmini2 Wrap Map Change 3238148 on 2016/12/16 by Lukasz.Furman fixed crash in EQS profiler copy of CL# 3238145 Change 3238708 on 2016/12/16 by Marc.Audy (4.15) Don't unload and then reload streaming levels that are marked to be hidden. #jira UE-39883 Change 3238799 on 2016/12/16 by Lina.Halper Potential fix + more info on crash on copying curve for WEX Change 3239559 on 2016/12/19 by Ori.Cohen Guard against infinitely thin geometry which fixes some nans Change 3239728 on 2016/12/19 by Marc.Audy Merging //UE4/Dev-Main to Dev-Framework (//UE4/Dev-Framework) @ 3239536 Change 3239735 on 2016/12/19 by Jon.Nabozny Set 'p.MoveIgnoreFirstBlockingOverlap' to be enabled by default (3158732). This causes collision behavior to remain unchanged unless people opt in to the new behavior. Adjust Bot_RandomLocations default health to 100 from 0. This prevents death by hits from non-projectiles. 4.15 #jira UE-39387 Change 3239765 on 2016/12/19 by Jon.Nabozny Fix FPredictProjectilePathParams to use a valid default value for TraceChannel. This requires the use of a new bool bTraceWithChannel which is enabled by default. 4.15 #JIRA UE-39726 Change 3239810 on 2016/12/19 by Marc.Audy Avoid duplicate GetWorldSettings call Change 3239826 on 2016/12/19 by Lukasz.Furman fixed crashes in gameplay debugger's draw delegate handling copy of 3234768, 3239819 #ue4 Change 3239894 on 2016/12/19 by Richard.Hinckley Improving UInterface template files for "New C++ Class" feature. We now use GENERATED_BODY macros and don't need an empty constructor in the .cpp file. Change 3239957 on 2016/12/19 by Aaron.McLeran UE-39924 Fix for crash when duplicating sound cue assets in content browser Checking for null before casting Change 3239983 on 2016/12/19 by Mieszko.Zielinski Fixed injecting dynamic BTs not as expected when there's more than one injection point #UE4 Change 3240177 on 2016/12/19 by Mieszko.Zielinski Fix for AI agents hand-placed on levels not getting their PathFollowingComponent.MyNavData set properly #UE4 Change 3240488 on 2016/12/19 by Aaron.McLeran UE-39924 Fix for crash when duplicating sound cue assets in content browser More fixes! Change 3240512 on 2016/12/19 by dan.reynolds AEOverview Update: - Created support for single level loads (sub-maps now auto generate lights and a staging platform when loaded individually vs. via AEOverviewMain) This will allow developers to load single levels functionally without adding lights or other assets to make them work. Change 3240518 on 2016/12/19 by dan.reynolds AEOverview Update: - Added test for Multichannel 2D Reverb Change 3240875 on 2016/12/20 by mason.seay Gameplay Tag Functional Tests Change 3240876 on 2016/12/20 by dan.reynolds AEOverview Fix - Fixed miss targeted menu items (updated prefixes) Change 3240923 on 2016/12/20 by Lukasz.Furman fixed memory corruption in template A* solver copy of CL# 3240898 #ue4 Change 3241661 on 2016/12/21 by Thomas.Sarkanen Fix mesh-customized sockets not showing up by default in 'Active' socket filter mode #jira UE-39938 - Cannot edit mesh sockets Change 3241964 on 2016/12/21 by Wes.Hunt Remove QoSReporter from CrashReportClient #tests editor debug gpf and verify crash is sent. Change 3241996 on 2016/12/21 by Wes.Hunt Add @Owner tags to all analytics events in all our games #jira AN-805 * Added default owners to most events. Tracked down authors of some events. * Added skeleton docs for many missing locations (just added @Name and @Owner so analytics folks can see the name and who to talk to in the doc webpage). * verified this checkin contains changes to comments ONLY. #tests compiled Orion and QAGame. Change 3242825 on 2016/12/22 by Lukasz.Furman fixed order of behavior tree execution indices for PIE debugging #jira UE-39922 Change 3242860 on 2016/12/22 by mason.seay Functional tests for timer Change 3243188 on 2016/12/22 by dan.reynolds AEOverview Update - Created viewport bookmarks on each sub-map for individual testing consistency - Updated EQ and Reverb effect parameters to work with new Audio Mixer Effects Change 3243192 on 2016/12/22 by dan.reynolds AEOverview Lighting Fix Change 3243507 on 2016/12/23 by dan.reynolds AEOverview Moved to Maps\Framework\Audio\ + redirector clean up, resaves, etc. Change 3243553 on 2016/12/24 by Aaron.McLeran Bringing fixes to dev-framework from odin 3240517 3240476 3240473 3240412 3240315 3240220 3240194 Change 3243567 on 2016/12/24 by Aaron.McLeran Fixing build. Adding #include for FConfigCacheIni Change 3244466 on 2017/01/01 by Mieszko.Zielinski Removed FGameplayDebuggerDebugDrawDelegateHelper::InitDelegateHelper implementation that was failing a check without any explanation or comment #UE4 #jira UE-40069 Change 3244471 on 2017/01/01 by Aaron.McLeran Bringing fixes to dev-framework from odin 3244469 3244467 3243743 Change 3244639 on 2017/01/03 by Jurre.deBaare CIS error fix Change 3244748 on 2017/01/03 by Jurre.deBaare Crash while using the Delete Button in the HLOD Outliner while a Generated Proxy Mesh is opened in the Static Mesh Editor #fix Unify path for both delete cluster options in the outliner UI #jira UE-40066 Change 3245338 on 2017/01/03 by Aaron.McLeran Getting rid of shadowed variable. Change 3245816 on 2017/01/03 by Aaron.McLeran Synth component and DSP objects - New synth component wraps an audio component and procedural sound wave to make generating synthesis much much easier - Bunch of changes and improvements to DSP objects for real-time synthesis. - New polyphonic virtual analog synthesizer Change 3246146 on 2017/01/04 by Ben.Marsh Move precompiled binaries into the Private-Binaries stream. Change 3246283 on 2017/01/04 by Marc.Audy Fix CIS warnings Change 3246457 on 2017/01/04 by Aaron.McLeran Fixing static analysis warnings Change 3246519 on 2017/01/04 by Benn.Gallagher Fix for serialization mismatch on skeletal mesh source model. Change 3247193 on 2017/01/04 by Dan.Reynolds Adding new DSP utility Change3247769on 2017/01/05 by Marc.Audy Remove inaccurate comment Change 3248068 on 2017/01/05 by dan.reynolds AEOverview Fix - Shortening long path name (Multichannel sub-directories) and fixing up redirectors Change 3248251 on 2017/01/05 by Jon.Nabozny Fix uninitialized PropertyColor in BillboardComponent. Change 3249305 on 2017/01/06 by James.Golding Fix FColorVertexBuffer copy constructor if source buffer is not initialised #jira UE-40242 Change 3249639 on 2017/01/06 by Jon.Nabozny Fix K2Node_CallFunction tool tip generation crash. #JIRA UE-40307 Change 3249716 on 2017/01/06 by Aaron.McLeran Minor changes to DSP objects Deciding on a method to pass parameters from BP to synth components. Change 3249909 on 2017/01/06 by James.Golding Change USkinnedMeshComponent::GetSkinWeightBuffer to not require a MeshObject to return valid weight buffer Make VertInfluencedByActiveBoneTyped not crash if weight buffer is null #jira UE-40289 Change 3249931 on 2017/01/06 by Aaron.McLeran Bring CL 3244528 from Odin to Dev-Framework Change 3250012 on 2017/01/06 by Aaron.McLeran Changing how synth params work - Removing base-class parameter getters/setters, removing OnParameterChange virtual function - Added SynthCommand function to help setting synth params on audio render thread from game thread - Refactored Synth1Component to use new system Change 3250084 on 2017/01/06 by Aaron.McLeran Adding preset struct and adding noise to oscillator Change 3250257 on 2017/01/07 by Aaron.McLeran Checking in stub for new synthesis plugin to put synthesis instances. Change 3250264 on 2017/01/07 by Aaron.McLeran Moving synthesis code to new synthesis plugin Change 3250313 on 2017/01/07 by Aaron.McLeran Fixing CIS static analysis warning on include cycle Change 3250353 on 2017/01/08 by Aaron.McLeran Various audio mixer/dsp refinements -Simplying envelope code to just be a straightforward case statement -Added sample value lerping code for Amp object to avoid zippering when running at control-rate sample rates -Changed source manager wrapping code to always set NextFrameIndex to -1 in the edge case of the next being out of range, but current not being out of range. It should always be -1. -Added a console var to toggle enabling sample checks for tracking down sample bugs -Added data table row subclass to EpicSynth1Component preset struct Change 3250382 on 2017/01/08 by Aaron.McLeran Bringing ODIN-3977 fix to dev-framework Change 3250435 on 2017/01/08 by Aaron.McLeran Adding ability to set note durations for synth component Removing OnNoteOn/OnNoteOff events since derived synth components may or may not deal with notes. Change 3250443 on 2017/01/08 by Aaron.McLeran Fixing CIS, removing console variable code. Change 3250445 on 2017/01/08 by Aaron.McLeran Attempted fix for crash on existing PIE Change 3250446 on 2017/01/08 by dan.reynolds Updated MidiSynthTestBP for new Note On Note Off functions Change 3250447 on 2017/01/08 by dan.reynolds MidiListener and MidiSynthTestBP Updated to use Duration argument (MidiListener set default value to -1.0f ) Change 3250455 on 2017/01/08 by Aaron.McLeran Adding critical section so stopping a source voice and processing source voice can't happen at same time. Change 3250465 on 2017/01/08 by Aaron.McLeran Fixing NaNs in sine approximations Change 3250466 on 2017/01/08 by Aaron.McLeran Adding new music utility. - Changing scale indicies to be 1-based (music oriented) - Adding new function to get chord note from a mode Change 3250467 on 2017/01/08 by Aaron.McLeran Undoing change to FastSin parabolic sine approximation - was not dividing by zero! Change 3250468 on 2017/01/08 by Aaron.McLeran Adding ability to get a direct virtual function callback for procedural sound waves -Using the UE4 delegate function was not safe in the audio rendering thread and would sometimes not actually get called. Switched to a more direct and simple override, avoids some buffer copies and is more simple. -Updated synth component code to use the new method. Change 3250470 on 2017/01/08 by Aaron.McLeran Fixing note on duration Change 3250479 on 2017/01/08 by Aaron.McLeran Fixing pan in the amp dsp object Change 3252179 on 2017/01/10 by Mieszko.Zielinski Fallout fix after removal of BlackboardKeyUtils::CalculateComparisonResult declaration from the AIModule #UE4 Change 3252498 on 2017/01/10 by Marc.Audy Fix non-unity compile errors [CL 3252563 by Marc Audy in Main branch]
2741 lines
91 KiB
C++
2741 lines
91 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "K2Node_CallFunction.h"
|
|
#include "UObject/UObjectHash.h"
|
|
#include "UObject/Interface.h"
|
|
#include "UObject/PropertyPortFlags.h"
|
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
|
#include "Engine/BlueprintGeneratedClass.h"
|
|
#include "GraphEditorSettings.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "K2Node_Event.h"
|
|
#include "K2Node_AssignmentStatement.h"
|
|
#include "K2Node_CallArrayFunction.h"
|
|
#include "K2Node_CustomEvent.h"
|
|
#include "K2Node_FunctionEntry.h"
|
|
#include "K2Node_IfThenElse.h"
|
|
#include "K2Node_TemporaryVariable.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "EditorStyleSettings.h"
|
|
#include "Editor.h"
|
|
#include "EdGraphUtilities.h"
|
|
|
|
#include "KismetCompiler.h"
|
|
#include "CallFunctionHandler.h"
|
|
#include "K2Node_SwitchEnum.h"
|
|
#include "Kismet/KismetMathLibrary.h"
|
|
#include "Kismet/KismetArrayLibrary.h"
|
|
#include "Kismet2/KismetDebugUtilities.h"
|
|
#include "K2Node_PureAssignmentStatement.h"
|
|
#include "BlueprintActionFilter.h"
|
|
#include "FindInBlueprintManager.h"
|
|
#include "SPinTypeSelector.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "K2Node"
|
|
|
|
/*******************************************************************************
|
|
* FCustomStructureParamHelper
|
|
******************************************************************************/
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* FDynamicOutputUtils
|
|
******************************************************************************/
|
|
|
|
struct FDynamicOutputHelper
|
|
{
|
|
public:
|
|
FDynamicOutputHelper(UEdGraphPin* InAlteredPin)
|
|
: MutatingPin(InAlteredPin)
|
|
{}
|
|
|
|
/**
|
|
* Attempts to change the output pin's type so that it reflects the class
|
|
* specified by the input class pin.
|
|
*/
|
|
void ConformOutputType() const;
|
|
|
|
/**
|
|
* Retrieves the class pin that is used to determine the function's output type.
|
|
*
|
|
* @return Null if the "DeterminesOutputType" metadata targets an invalid
|
|
* param (or if the metadata isn't present), otherwise a class picker pin.
|
|
*/
|
|
static UEdGraphPin* GetTypePickerPin(const UK2Node_CallFunction* FuncNode);
|
|
|
|
/**
|
|
* Attempts to pull out class info from the supplied pin. Starts with the
|
|
* pin's default, and then falls back onto the pin's native type. Will poll
|
|
* any connections that the pin may have.
|
|
*
|
|
* @param Pin The pin you want a class from.
|
|
* @return A class that the pin represents (could be null if the pin isn't a class pin).
|
|
*/
|
|
static UClass* GetPinClass(UEdGraphPin* Pin);
|
|
|
|
/**
|
|
* Intended to be used by ValidateNodeDuringCompilation(). Will check to
|
|
* make sure the dynamic output's connections are still valid (they could
|
|
* become invalid as the the pin's type changes).
|
|
*
|
|
* @param FuncNode The node you wish to validate.
|
|
* @param MessageLog The log to post errors/warnings to.
|
|
*/
|
|
static void VerifyNode(const UK2Node_CallFunction* FuncNode, FCompilerResultsLog& MessageLog);
|
|
|
|
private:
|
|
/**
|
|
*
|
|
*
|
|
* @return
|
|
*/
|
|
UK2Node_CallFunction* GetFunctionNode() const;
|
|
|
|
/**
|
|
*
|
|
*
|
|
* @return
|
|
*/
|
|
UFunction* GetTargetFunction() const;
|
|
|
|
/**
|
|
* Checks if the supplied pin is the class picker that governs the
|
|
* function's output type.
|
|
*
|
|
* @param Pin The pin to test.
|
|
* @return True if the pin corresponds to the param that was flagged by the "DeterminesOutputType" metadata.
|
|
*/
|
|
bool IsTypePickerPin(UEdGraphPin* Pin) const;
|
|
|
|
/**
|
|
* Retrieves the object output pin that is altered as the class input is
|
|
* changed (favors params flagged by "DynamicOutputParam" metadata).
|
|
*
|
|
* @return Null if the output param cannot be altered from the class input,
|
|
* otherwise a output pin that will mutate type as the class input is changed.
|
|
*/
|
|
static UEdGraphPin* GetDynamicOutPin(const UK2Node_CallFunction* FuncNode);
|
|
|
|
/**
|
|
* Checks if the specified type is an object type that reflects the picker
|
|
* pin's class.
|
|
*
|
|
* @param TypeToTest The type you want to check.
|
|
* @return True if the type is likely the output governed by the class picker pin, otherwise false.
|
|
*/
|
|
static bool CanConformPinType(const UK2Node_CallFunction* FuncNode, const FEdGraphPinType& TypeToTest);
|
|
|
|
private:
|
|
UEdGraphPin* MutatingPin;
|
|
};
|
|
|
|
void FDynamicOutputHelper::ConformOutputType() const
|
|
{
|
|
if (IsTypePickerPin(MutatingPin))
|
|
{
|
|
UClass* PickedClass = GetPinClass(MutatingPin);
|
|
UK2Node_CallFunction* FuncNode = GetFunctionNode();
|
|
|
|
if (UEdGraphPin* DynamicOutPin = GetDynamicOutPin(FuncNode))
|
|
{
|
|
DynamicOutPin->PinType.PinSubCategoryObject = PickedClass;
|
|
|
|
// leave the connection, and instead bring the user's attention to
|
|
// it via a ValidateNodeDuringCompilation() error
|
|
// const UEdGraphSchema* Schema = FuncNode->GetSchema();
|
|
// for (int32 LinkIndex = 0; LinkIndex < DynamicOutPin->LinkedTo.Num();)
|
|
// {
|
|
// UEdGraphPin* Link = DynamicOutPin->LinkedTo[LinkIndex];
|
|
// // if this can no longer be linked to the other pin, then we
|
|
// // should disconnect it (because the pin's type just changed)
|
|
// if (Schema->CanCreateConnection(DynamicOutPin, Link).Response == CONNECT_RESPONSE_DISALLOW)
|
|
// {
|
|
// DynamicOutPin->BreakLinkTo(Link);
|
|
// // @TODO: warn/notify somehow
|
|
// }
|
|
// else
|
|
// {
|
|
// ++LinkIndex;
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
|
|
UEdGraphPin* FDynamicOutputHelper::GetTypePickerPin(const UK2Node_CallFunction* FuncNode)
|
|
{
|
|
UEdGraphPin* TypePickerPin = nullptr;
|
|
|
|
if (UFunction* Function = FuncNode->GetTargetFunction())
|
|
{
|
|
FString TypeDeterminingPinName = Function->GetMetaData(FBlueprintMetadata::MD_DynamicOutputType);
|
|
if (!TypeDeterminingPinName.IsEmpty())
|
|
{
|
|
TypePickerPin = FuncNode->FindPin(TypeDeterminingPinName);
|
|
}
|
|
}
|
|
|
|
if (TypePickerPin && !ensure(TypePickerPin->Direction == EGPD_Input))
|
|
{
|
|
TypePickerPin = nullptr;
|
|
}
|
|
|
|
return TypePickerPin;
|
|
}
|
|
|
|
UClass* FDynamicOutputHelper::GetPinClass(UEdGraphPin* Pin)
|
|
{
|
|
UClass* PinClass = UObject::StaticClass();
|
|
|
|
bool const bIsClassOrObjectPin = (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class || Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object);
|
|
if (bIsClassOrObjectPin)
|
|
{
|
|
if (UClass* DefaultClass = Cast<UClass>(Pin->DefaultObject))
|
|
{
|
|
PinClass = DefaultClass;
|
|
}
|
|
else if (UClass* BaseClass = Cast<UClass>(Pin->PinType.PinSubCategoryObject.Get()))
|
|
{
|
|
PinClass = BaseClass;
|
|
}
|
|
|
|
if (Pin->LinkedTo.Num() > 0)
|
|
{
|
|
UClass* CommonInputClass = nullptr;
|
|
for (UEdGraphPin* LinkedPin : Pin->LinkedTo)
|
|
{
|
|
const FEdGraphPinType& LinkedPinType = LinkedPin->PinType;
|
|
|
|
UClass* LinkClass = Cast<UClass>(LinkedPinType.PinSubCategoryObject.Get());
|
|
if (LinkClass == nullptr && LinkedPinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self)
|
|
{
|
|
if (UK2Node* K2Node = Cast<UK2Node>(LinkedPin->GetOwningNode()))
|
|
{
|
|
LinkClass = K2Node->GetBlueprint()->GeneratedClass;
|
|
}
|
|
}
|
|
|
|
if (LinkClass != nullptr)
|
|
{
|
|
if (CommonInputClass != nullptr)
|
|
{
|
|
while (!LinkClass->IsChildOf(CommonInputClass))
|
|
{
|
|
CommonInputClass = CommonInputClass->GetSuperClass();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CommonInputClass = LinkClass;
|
|
}
|
|
}
|
|
}
|
|
|
|
PinClass = CommonInputClass;
|
|
}
|
|
}
|
|
return PinClass;
|
|
}
|
|
|
|
void FDynamicOutputHelper::VerifyNode(const UK2Node_CallFunction* FuncNode, FCompilerResultsLog& MessageLog)
|
|
{
|
|
if (UEdGraphPin* DynamicOutPin = GetDynamicOutPin(FuncNode))
|
|
{
|
|
const UEdGraphSchema* Schema = FuncNode->GetSchema();
|
|
for (UEdGraphPin* Link : DynamicOutPin->LinkedTo)
|
|
{
|
|
if (Schema->CanCreateConnection(DynamicOutPin, Link).Response == CONNECT_RESPONSE_DISALLOW)
|
|
{
|
|
FText const ErrorFormat = LOCTEXT("BadConnection", "Invalid pin connection from '@@' to '@@'. You may have changed the type after the connections were made.");
|
|
MessageLog.Error(*ErrorFormat.ToString(), DynamicOutPin, Link);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UK2Node_CallFunction* FDynamicOutputHelper::GetFunctionNode() const
|
|
{
|
|
return CastChecked<UK2Node_CallFunction>(MutatingPin->GetOwningNode());
|
|
}
|
|
|
|
UFunction* FDynamicOutputHelper::GetTargetFunction() const
|
|
{
|
|
return GetFunctionNode()->GetTargetFunction();
|
|
}
|
|
|
|
bool FDynamicOutputHelper::IsTypePickerPin(UEdGraphPin* Pin) const
|
|
{
|
|
bool bIsTypeDeterminingPin = false;
|
|
|
|
if (UFunction* Function = GetTargetFunction())
|
|
{
|
|
FString TypeDeterminingPinName = Function->GetMetaData(FBlueprintMetadata::MD_DynamicOutputType);
|
|
if (!TypeDeterminingPinName.IsEmpty())
|
|
{
|
|
bIsTypeDeterminingPin = (Pin->PinName == TypeDeterminingPinName);
|
|
}
|
|
}
|
|
|
|
bool const bPinIsClassPicker = (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Class);
|
|
bool const bPinIsObjectPicker = (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object);
|
|
return bIsTypeDeterminingPin && (bPinIsClassPicker || bPinIsObjectPicker) && (Pin->Direction == EGPD_Input);
|
|
}
|
|
|
|
UEdGraphPin* FDynamicOutputHelper::GetDynamicOutPin(const UK2Node_CallFunction* FuncNode)
|
|
{
|
|
UProperty* TaggedOutputParam = nullptr;
|
|
if (UFunction* Function = FuncNode->GetTargetFunction())
|
|
{
|
|
const FString& OutputPinName = Function->GetMetaData(FBlueprintMetadata::MD_DynamicOutputParam);
|
|
// we sort through properties, instead of pins, because the pin's type
|
|
// could already be modified to some other class (for when we check CanConformPinType)
|
|
for (TFieldIterator<UProperty> ParamIt(Function); ParamIt && (ParamIt->PropertyFlags & CPF_Parm); ++ParamIt)
|
|
{
|
|
if (OutputPinName.IsEmpty() && ParamIt->HasAnyPropertyFlags(CPF_ReturnParm))
|
|
{
|
|
TaggedOutputParam = *ParamIt;
|
|
break;
|
|
}
|
|
else if (OutputPinName == ParamIt->GetName())
|
|
{
|
|
TaggedOutputParam = *ParamIt;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (TaggedOutputParam != nullptr)
|
|
{
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
FEdGraphPinType PropertyPinType;
|
|
|
|
if (!K2Schema->ConvertPropertyToPinType(TaggedOutputParam, /*out*/PropertyPinType) || !CanConformPinType(FuncNode, PropertyPinType))
|
|
{
|
|
TaggedOutputParam = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
UEdGraphPin* DynamicOutPin = nullptr;
|
|
if (TaggedOutputParam != nullptr)
|
|
{
|
|
DynamicOutPin = FuncNode->FindPin(TaggedOutputParam->GetName());
|
|
if (DynamicOutPin && (DynamicOutPin->Direction != EGPD_Output))
|
|
{
|
|
DynamicOutPin = nullptr;
|
|
}
|
|
}
|
|
return DynamicOutPin;
|
|
}
|
|
|
|
bool FDynamicOutputHelper::CanConformPinType(const UK2Node_CallFunction* FuncNode, const FEdGraphPinType& TypeToTest)
|
|
{
|
|
bool bIsProperType = false;
|
|
if (UEdGraphPin* TypePickerPin = GetTypePickerPin(FuncNode))
|
|
{
|
|
UClass* BasePickerClass = CastChecked<UClass>(TypePickerPin->PinType.PinSubCategoryObject.Get());
|
|
|
|
const FString& PinCategory = TypeToTest.PinCategory;
|
|
if ((PinCategory == UEdGraphSchema_K2::PC_Object) ||
|
|
(PinCategory == UEdGraphSchema_K2::PC_Interface) ||
|
|
(PinCategory == UEdGraphSchema_K2::PC_Class))
|
|
{
|
|
if (UClass* TypeClass = Cast<UClass>(TypeToTest.PinSubCategoryObject.Get()))
|
|
{
|
|
bIsProperType = BasePickerClass->IsChildOf(TypeClass);
|
|
}
|
|
}
|
|
}
|
|
return bIsProperType;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* UK2Node_CallFunction
|
|
******************************************************************************/
|
|
|
|
UK2Node_CallFunction::UK2Node_CallFunction(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
, bPinTooltipsValid(false)
|
|
{
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
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 (auto& P : Pins)
|
|
{
|
|
P->PinToolTip.Empty();
|
|
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 = FindField<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 = Cast<UFunction>(FMemberReference::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(GetBlueprintClassFromNode());
|
|
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);
|
|
|
|
// Connect Execute and Then pins for functions, which became pure.
|
|
ReconnectPureExecPins(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());
|
|
// we don't want blueprint-function target pins to be formed from the
|
|
// skeleton class (otherwise, they could be incompatible with other pins
|
|
// that represent the same type)... this here could lead to a compiler
|
|
// warning (the GeneratedClass could not have the function yet), but in
|
|
// that, the user would be reminded to compile the other blueprint
|
|
if (FunctionClass->ClassGeneratedBy)
|
|
{
|
|
FunctionClass = FunctionClass->GetAuthoritativeClass();
|
|
}
|
|
|
|
UEdGraphPin* SelfPin = NULL;
|
|
if (FunctionClass == GetBlueprint()->GeneratedClass)
|
|
{
|
|
// 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);
|
|
|
|
UProperty* Prop = nullptr;
|
|
UEnum* Enum = nullptr;
|
|
|
|
if(UByteProperty* ByteProp = FindField<UByteProperty>(Function, FName(*EnumParamName)))
|
|
{
|
|
Prop = ByteProp;
|
|
Enum = ByteProp->Enum;
|
|
}
|
|
else if(UEnumProperty* EnumProp = FindField<UEnumProperty>(Function, FName(*EnumParamName)))
|
|
{
|
|
Prop = EnumProp;
|
|
Enum = EnumProp->GetEnum();
|
|
}
|
|
|
|
if(Prop != nullptr && Enum != nullptr)
|
|
{
|
|
const bool bIsFunctionInput = !Prop->HasAnyPropertyFlags(CPF_ReturnParm) &&
|
|
(!Prop->HasAnyPropertyFlags(CPF_OutParm) ||
|
|
Prop->HasAnyPropertyFlags(CPF_ReferenceParm));
|
|
const EEdGraphPinDirection Direction = bIsFunctionInput ? EGPD_Input : EGPD_Output;
|
|
|
|
// 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)
|
|
{
|
|
FString ExecName = 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) || FindField<UEnumProperty>(Function, FName(*EnumParamName)))
|
|
{
|
|
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::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<FString> PinsToHide;
|
|
TSet<FString> 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<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)
|
|
{
|
|
// Check for a display name override
|
|
const FString PinDisplayName = Param->GetMetaData(FBlueprintMetadata::MD_DisplayName);
|
|
if (!PinDisplayName.IsEmpty())
|
|
{
|
|
Pin->PinFriendlyName = FText::FromString(PinDisplayName);
|
|
}
|
|
|
|
//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);
|
|
|
|
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 (!bShowWorldContextPin || !bIsSelfPin)
|
|
{
|
|
Pin->bHidden = true;
|
|
Pin->bNotConnectable = InternalPins.Contains(Pin->PinName);
|
|
}
|
|
}
|
|
|
|
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();
|
|
InvalidatePinTooltips();
|
|
|
|
// conform pins that are marked as SetParam:
|
|
ConformContainerPins();
|
|
|
|
FCustomStructureParamHelper::UpdateCustomStructurePins(GetTargetFunction(), this);
|
|
|
|
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();
|
|
}
|
|
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();
|
|
FDynamicOutputHelper(Pin).ConformOutputType();
|
|
}
|
|
|
|
void UK2Node_CallFunction::PinDefaultValueChanged(UEdGraphPin* Pin)
|
|
{
|
|
Super::PinDefaultValueChanged(Pin);
|
|
InvalidatePinTooltips();
|
|
FDynamicOutputHelper(Pin).ConformOutputType();
|
|
}
|
|
|
|
UFunction* UK2Node_CallFunction::GetTargetFunction() const
|
|
{
|
|
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
|
|
{
|
|
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())
|
|
{
|
|
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);
|
|
}
|
|
|
|
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.ToLower();
|
|
}
|
|
|
|
// 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.TrimTrailing();
|
|
|
|
// 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);
|
|
|
|
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.Trim();
|
|
Tooltip.TrimTrailing();
|
|
|
|
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 = Function->GetMetaDataText(FBlueprintMetadata::MD_FunctionCategory, TEXT("UObjectCategory"), Function->GetFullGroupName(false));
|
|
|
|
// 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 = FindField<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)
|
|
{
|
|
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();
|
|
const bool bIsLocalMacro = Blueprint && Blueprint->MacroGraphs.Contains(GetGraph());
|
|
ensureMsgf(FunctionReference.IsSelfContext() || (MemberClass != nullptr) || bIsLocalMacro, TEXT("Unknown member class in %s"), *GetPathName());
|
|
if (FunctionReference.IsSelfContext())
|
|
{
|
|
if (MemberClass == nullptr)
|
|
{
|
|
// the self pin may have type information stored on it
|
|
if (UEdGraphPin* SelfPin = GetDefault<UEdGraphSchema_K2>()->FindSelfPin(*this, EGPD_Input))
|
|
{
|
|
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... if there is an function matching
|
|
// the member name in this Blueprint, then it will reference that
|
|
// function (even if it came from a different Blueprint, one with an
|
|
// identically named function)... if there is no function matching this
|
|
// reference, then 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();
|
|
|
|
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(GetGraph(), Function, PinsToHide);
|
|
|
|
const bool bShowWorldContextPin = ((PinsToHide.Num() > 0) && GetBlueprint()->ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin));
|
|
|
|
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) && (!bShowWorldContextPin || !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)))
|
|
{
|
|
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("FunctionNotFound", "Could not find a function named \"%s\" in '%s'.\nMake sure '%s' has been compiled for @@");
|
|
MessageLog.Error(*FString::Printf(*WarningFormat.ToString(), *FunctName, *OwnerName, *OwnerName), 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);
|
|
}
|
|
|
|
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();
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 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 && !ParentClass->GetDefaultObject()->ImplementsGetWorld() && !ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin))
|
|
{
|
|
MessageLog.Warning(*LOCTEXT("FunctionUnsafeInContext", "Function '@@' is unsafe to call from blueprints of class '@@'.").ToString(), this, ParentClass);
|
|
}
|
|
}
|
|
}
|
|
|
|
FDynamicOutputHelper::VerifyNode(this, MessageLog);
|
|
|
|
for (UEdGraphPin* Pin : Pins)
|
|
{
|
|
if (Pin && Pin->PinType.bIsWeakPointer && !Pin->PinType.IsContainer())
|
|
{
|
|
const FString ErrorString = FString::Printf(
|
|
*LOCTEXT("WeakPtrNotSupportedError", "Weak prointer is not supported as function parameter. Pin '%s' @@").ToString(),
|
|
*Pin->GetName());
|
|
MessageLog.Error(*ErrorString, 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);
|
|
}
|
|
}
|
|
|
|
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 && !bUserSetEnabledState)
|
|
{
|
|
UClass* SelfScope = GetBlueprintClassFromNode();
|
|
if (!FunctionReference.IsSelfContext() || SelfScope != nullptr)
|
|
{
|
|
if (const UFunction* Function = FunctionReference.ResolveMember<UFunction>(SelfScope))
|
|
{
|
|
// 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 (EnabledState == ENodeEnabledState::Enabled && Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly))
|
|
{
|
|
EnabledState = ENodeEnabledState::DevelopmentOnly;
|
|
}
|
|
// 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 (EnabledState == ENodeEnabledState::DevelopmentOnly && !Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly))
|
|
{
|
|
EnabledState = ENodeEnabledState::Enabled;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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());
|
|
|
|
// Re-enable for development only if specified in metadata.
|
|
if(EnabledState == ENodeEnabledState::Enabled && !bUserSetEnabledState)
|
|
{
|
|
const UFunction* Function = GetTargetFunction();
|
|
if (Function && Function->HasMetaData(FBlueprintMetadata::MD_DevelopmentOnly))
|
|
{
|
|
EnabledState = ENodeEnabledState::DevelopmentOnly;
|
|
}
|
|
}
|
|
}
|
|
|
|
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(*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);
|
|
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)
|
|
{
|
|
// Get the metadata that identifies which param is the enum, and try and find it
|
|
const FString& EnumParamName = Function->GetMetaData(FBlueprintMetadata::MD_ExpandEnumAsExecs);
|
|
|
|
UEnum* Enum = nullptr;
|
|
|
|
if (UByteProperty* ByteProp = FindField<UByteProperty>(Function, FName(*EnumParamName)))
|
|
{
|
|
Enum = ByteProp->Enum;
|
|
}
|
|
else if (UEnumProperty* EnumProp = FindField<UEnumProperty>(Function, FName(*EnumParamName)))
|
|
{
|
|
Enum = EnumProp->GetEnum();
|
|
}
|
|
|
|
UEdGraphPin* EnumParamPin = FindPinChecked(EnumParamName);
|
|
if(Enum != nullptr)
|
|
{
|
|
// 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 = 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[PinIdx]->MarkPendingKill();
|
|
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' pin to this switch node
|
|
CompilerContext.MovePinLinksToIntermediate(*Pin, *SwitchEnumNode->FindPinChecked(Pin->PinName));
|
|
|
|
// Finally remove this 'cosmetic' exec pin
|
|
Pins[PinIdx]->MarkPendingKill();
|
|
Pins.RemoveAt(PinIdx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// AUTO CREATED REFS
|
|
{
|
|
if ( Function )
|
|
{
|
|
TArray<FString> AutoCreateRefTermPinNames;
|
|
const bool bHasAutoCreateRefTerms = Function->HasMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm);
|
|
if ( bHasAutoCreateRefTerms )
|
|
{
|
|
CompilerContext.GetSchema()->GetAutoEmitTermParameters(Function, AutoCreateRefTermPinNames);
|
|
}
|
|
|
|
for ( auto Pin : Pins )
|
|
{
|
|
if ( Pin && bHasAutoCreateRefTerms && AutoCreateRefTermPinNames.Contains(Pin->PinName) )
|
|
{
|
|
const bool bValidAutoRefPin = Pin->PinType.bIsReference
|
|
&& !CompilerContext.GetSchema()->IsMetaPin(*Pin)
|
|
&& ( Pin->Direction == EGPD_Input )
|
|
&& !Pin->LinkedTo.Num();
|
|
if ( bValidAutoRefPin )
|
|
{
|
|
const bool bHasDefaultValue = !Pin->DefaultValue.IsEmpty() || Pin->DefaultObject || !Pin->DefaultTextValue.IsEmpty();
|
|
|
|
//default values can be reset when the pin is connected
|
|
const auto DefaultValue = Pin->DefaultValue;
|
|
const auto DefaultObject = Pin->DefaultObject;
|
|
const auto DefaultTextValue = Pin->DefaultTextValue;
|
|
const auto AutogeneratedDefaultValue = Pin->AutogeneratedDefaultValue;
|
|
|
|
auto ValuePin = InnerHandleAutoCreateRef(this, Pin, CompilerContext, SourceGraph, bHasDefaultValue);
|
|
if ( ValuePin )
|
|
{
|
|
if (!DefaultObject && DefaultTextValue.IsEmpty() && DefaultValue.Equals(AutogeneratedDefaultValue, ESearchCase::CaseSensitive))
|
|
{
|
|
// Use the latest code to set default value
|
|
Schema->SetPinDefaultValueBasedOnType(ValuePin);
|
|
}
|
|
else
|
|
{
|
|
ValuePin->DefaultValue = DefaultValue;
|
|
ValuePin->DefaultObject = DefaultObject;
|
|
ValuePin->DefaultTextValue = DefaultTextValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
UEdGraphPin* UK2Node_CallFunction::InnerHandleAutoCreateRef(UK2Node* Node, UEdGraphPin* Pin, FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph, bool bForceAssignment)
|
|
{
|
|
const bool bAddAssigment = !Pin->PinType.bIsArray && 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 NULL;
|
|
}
|
|
}
|
|
// 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());
|
|
auto 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 NULL;
|
|
}
|
|
CompilerContext.GetSchema()->SetPinDefaultValueBasedOnType(AssignDefaultValue->GetValuePin());
|
|
return AssignInputPit;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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(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
|
|
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
|
|
UEdGraphPin* PinExec = nullptr;
|
|
for (int32 PinIdx = 0; PinIdx < OldPins.Num(); PinIdx++)
|
|
{
|
|
if (OldPins[PinIdx]->PinName == K2Schema->PN_Execute)
|
|
{
|
|
PinExec = OldPins[PinIdx];
|
|
break;
|
|
}
|
|
}
|
|
if (PinExec)
|
|
{
|
|
// look for old then pin
|
|
UEdGraphPin* PinThen = nullptr;
|
|
for (int32 PinIdx = 0; PinIdx < OldPins.Num(); PinIdx++)
|
|
{
|
|
if (OldPins[PinIdx]->PinName == K2Schema->PN_Then)
|
|
{
|
|
PinThen = OldPins[PinIdx];
|
|
break;
|
|
}
|
|
}
|
|
if (PinThen)
|
|
{
|
|
// 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->LinkedTo.Num() != 0 || Pin->DefaultValue != Pin->AutogeneratedDefaultValue)
|
|
{
|
|
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->DefaultValue != Pin->AutogeneratedDefaultValue)
|
|
{
|
|
bOutPropagated = true;
|
|
if (Pin->LinkedTo.Num() != 0)
|
|
{
|
|
TypeToPropagete = Pin->LinkedTo[0]->PinType.PinValueType;
|
|
}
|
|
else
|
|
{
|
|
TypeToPropagete = Pin->PinType.PinValueType;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const auto TryPropagateType = [](UEdGraphPin* Pin, const FEdGraphTerminalType& TerminalType, bool bTypeIsAvailable)
|
|
{
|
|
if(Pin)
|
|
{
|
|
if(bTypeIsAvailable)
|
|
{
|
|
Pin->PinType.PinCategory = TerminalType.TerminalCategory;
|
|
Pin->PinType.PinSubCategory = TerminalType.TerminalSubCategory;
|
|
Pin->PinType.PinSubCategoryObject = TerminalType.TerminalSubCategoryObject;
|
|
}
|
|
else
|
|
{
|
|
// reset to wildcard:
|
|
Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
|
|
Pin->PinType.PinSubCategory.Empty();
|
|
Pin->PinType.PinSubCategoryObject = nullptr;
|
|
Pin->DefaultValue = TEXT("");
|
|
}
|
|
}
|
|
};
|
|
|
|
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.Empty();
|
|
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(auto 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 = NULL;
|
|
|
|
// Search for the Blueprint owner of the function graph, climbing up through the Blueprint hierarchy
|
|
UClass* MemberParentClass = FunctionReference.GetMemberParentClass(GetBlueprintClassFromNode());
|
|
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 = NULL;
|
|
FName FunctionName = FunctionReference.GetMemberName();
|
|
for (UEdGraph* Graph : Blueprint->FunctionGraphs)
|
|
{
|
|
CA_SUPPRESS(28182); // warning C28182: Dereferencing NULL pointer. 'Graph' contains the same NULL value as 'TargetGraph' did.
|
|
if (Graph->GetFName() == FunctionName)
|
|
{
|
|
TargetGraph = Graph;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool UK2Node_CallFunction::IsWildcardProperty(const UFunction* InFunction, const UProperty* InProperty)
|
|
{
|
|
if (InProperty)
|
|
{
|
|
return FEdGraphUtilities::IsSetParam(InFunction, InProperty->GetName()) || FEdGraphUtilities::IsMapParam(InFunction, InProperty->GetName());
|
|
}
|
|
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>();
|
|
}
|
|
|
|
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())
|
|
{
|
|
if(
|
|
// Strictly speaking this first check is not needed, but by not disabling the connection here we get a better reason later:
|
|
( ( FEdGraphUtilities::IsSetParam(TargetFunction, MyPin->PinName) &&
|
|
(OtherPin->PinType.IsContainer() && !MyPin->PinType.bIsSet) ) ||
|
|
( FEdGraphUtilities::IsMapParam(TargetFunction, MyPin->PinName) &&
|
|
(OtherPin->PinType.IsContainer() && !MyPin->PinType.bIsMap) ) ||
|
|
( FEdGraphUtilities::IsArrayDependentParam(TargetFunction, MyPin->PinName) &&
|
|
(OtherPin->PinType.IsContainer() && !MyPin->PinType.bIsArray) )
|
|
)
|
|
&&
|
|
// make sure we don't allow connections of mismatched container types (e.g. maps to arrays)
|
|
(
|
|
OtherPin->PinType.bIsMap != MyPin->PinType.bIsMap ||
|
|
OtherPin->PinType.bIsSet != MyPin->PinType.bIsSet ||
|
|
OtherPin->PinType.bIsArray != MyPin->PinType.bIsArray
|
|
)
|
|
)
|
|
{
|
|
bIsDisallowed = true;
|
|
OutReason = LOCTEXT("PinSetConnectionDisallowed", "Containers of containers are not supported - consider wrapping a container in a Structure object").ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
return bIsDisallowed;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|