2020-08-27 18:38:23 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "K2Node_PromotableOperator.h"
2022-08-30 23:03:03 -04:00
# include "BlueprintEditorSettings.h"
2020-08-27 18:38:23 -04:00
# include "BlueprintTypePromotion.h"
2022-08-30 23:03:03 -04:00
# include "Containers/EnumAsByte.h"
# include "Containers/Set.h"
# include "Containers/UnrealString.h"
# include "CoreTypes.h"
# include "Delegates/Delegate.h"
# include "EdGraph/EdGraph.h"
# include "EdGraph/EdGraphNode.h"
# include "EdGraph/EdGraphNodeUtils.h"
# include "EdGraph/EdGraphPin.h"
# include "EdGraph/EdGraphSchema.h"
2020-08-27 18:38:23 -04:00
# include "EdGraphSchema_K2.h"
2022-08-30 23:03:03 -04:00
# include "Engine/MemberReference.h"
# include "EngineLogs.h"
2020-08-27 18:38:23 -04:00
# include "Framework/Commands/UIAction.h"
2022-08-30 23:03:03 -04:00
# include "HAL/PlatformCrt.h"
# include "Internationalization/Internationalization.h"
# include "K2Node.h"
2022-10-31 17:18:29 -04:00
# include "Kismet/BlueprintTypeConversions.h"
2020-08-27 18:38:23 -04:00
# include "Kismet2/BlueprintEditorUtils.h"
2020-10-12 17:44:44 -04:00
# include "Kismet2/CompilerResultsLog.h"
2022-08-30 23:03:03 -04:00
# include "Kismet2/WildcardNodeUtils.h"
# include "KismetCompiler.h"
# include "Logging/LogCategory.h"
# include "Logging/LogMacros.h"
# include "Misc/AssertionMacros.h"
# include "ScopedTransaction.h"
# include "Templates/SubclassOf.h"
# include "Templates/UnrealTemplate.h"
# include "Textures/SlateIcon.h"
# include "ToolMenu.h"
# include "ToolMenuDelegates.h"
# include "ToolMenuSection.h"
# include "Trace/Detail/Channel.h"
# include "UObject/Class.h"
# include "UObject/UnrealType.h"
# include "UObject/WeakObjectPtr.h"
# include "UObject/WeakObjectPtrTemplates.h"
2022-09-13 12:13:50 -04:00
# include "EdGraphSchema_K2_Actions.h"
2020-08-27 18:38:23 -04:00
# define LOCTEXT_NAMESPACE "PromotableOperatorNode"
2023-08-11 10:30:15 -04:00
static TAutoConsoleVariable < bool > CVarAllowConversionOfComparisonOps (
TEXT ( " BP.bAllowConversionOfComparisonOps " ) ,
true ,
TEXT ( " If true, then allow the user to convert between comparison operators on the UK2Node_PromotableOperator " ) ,
ECVF_Default ) ;
2020-08-27 18:38:23 -04:00
///////////////////////////////////////////////////////////
// Pin names for default construction
static const FName InputPinA_Name = FName ( TEXT ( " A " ) ) ;
static const FName InputPinB_Name = FName ( TEXT ( " B " ) ) ;
2020-12-05 12:00:19 -04:00
static const FName TolerancePin_Name = FName ( TEXT ( " ErrorTolerance " ) ) ;
2020-10-12 17:44:44 -04:00
static const int32 NumFunctionInputs = 2 ;
2020-08-27 18:38:23 -04:00
///////////////////////////////////////////////////////////
// UK2Node_PromotableOperator
UK2Node_PromotableOperator : : UK2Node_PromotableOperator ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
{
UpdateOpName ( ) ;
OrphanedPinSaveMode = ESaveOrphanPinMode : : SaveAllButExec ;
2020-10-12 17:44:44 -04:00
NumAdditionalInputs = 0 ;
2020-08-27 18:38:23 -04:00
}
///////////////////////////////////////////////////////////
// UEdGraphNode interface
2020-12-05 12:00:19 -04:00
namespace PromotableOpUtils
{
bool FindTolerancePinType ( const UFunction * Func , FEdGraphPinType & OutPinType )
{
const UEdGraphSchema_K2 * Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
// Check if there is a tolerance field that we need to add
// If UFunction has a third input param
int32 InputArgsFound = 0 ;
for ( TFieldIterator < FProperty > PropIt ( Func ) ; PropIt & & ( PropIt - > PropertyFlags & CPF_Parm ) ; + + PropIt )
{
const FProperty * Param = * PropIt ;
// We don't care about the return property, and we are looking for
// any additional input param that isn't in the normal function input range
if ( Param - > HasAnyPropertyFlags ( CPF_ReturnParm ) | | ( InputArgsFound + + < NumFunctionInputs ) )
{
continue ;
}
// Found the tolerance type!
FEdGraphPinType ParamType ;
if ( Schema - > ConvertPropertyToPinType ( Param , /* out */ ParamType ) )
{
OutPinType = ParamType ;
return true ;
}
}
return false ;
}
}
2020-08-27 18:38:23 -04:00
void UK2Node_PromotableOperator : : AllocateDefaultPins ( )
{
FWildcardNodeUtils : : CreateWildcardPin ( this , InputPinA_Name , EGPD_Input ) ;
FWildcardNodeUtils : : CreateWildcardPin ( this , InputPinB_Name , EGPD_Input ) ;
2020-10-19 22:02:12 -04:00
UEdGraphPin * OutPin = FWildcardNodeUtils : : CreateWildcardPin ( this , UEdGraphSchema_K2 : : PN_ReturnValue , EGPD_Output ) ;
2020-12-05 12:00:19 -04:00
const UFunction * Func = GetTargetFunction ( ) ;
2020-10-19 22:02:12 -04:00
// For comparison functions we always want a bool output, so make it visually so
2020-12-05 12:00:19 -04:00
if ( Func & & FTypePromotion : : IsComparisonFunc ( Func ) )
2020-10-19 22:02:12 -04:00
{
2020-10-20 13:25:04 -04:00
OutPin - > PinType . PinCategory = UEdGraphSchema_K2 : : PC_Boolean ;
2020-12-05 12:00:19 -04:00
// If we need a tolerance pin then we need to add it here, but not if we are in a wildcard state
FEdGraphPinType ToleranceType ;
if ( PromotableOpUtils : : FindTolerancePinType ( Func , ToleranceType ) )
{
UEdGraphPin * TolerancePin = CreatePin ( EGPD_Input , ToleranceType , TolerancePin_Name ) ;
}
2020-10-19 22:02:12 -04:00
}
2020-10-12 17:44:44 -04:00
2020-12-07 21:02:36 -04:00
// Update the op name so that if there is a blank wildcard node left on the graph we
// can ensure that it is correct
UpdateOpName ( ) ;
ensureMsgf ( ( OperationName ! = TEXT ( " NO_OP " ) ) , TEXT ( " Invalid operation name on Promotable Operator node! " ) ) ;
2020-10-12 17:44:44 -04:00
// Create any additional input pin. Their appropriate type is determined in ReallocatePinsDuringReconstruction
// because we cannot get a promoted type with no links to the pin.
for ( int32 i = NumFunctionInputs ; i < ( NumAdditionalInputs + NumFunctionInputs ) ; + + i )
{
AddInputPinImpl ( i ) ;
}
2020-08-27 18:38:23 -04:00
}
void UK2Node_PromotableOperator : : GetNodeContextMenuActions ( UToolMenu * Menu , UGraphNodeContextMenuContext * Context ) const
{
Super : : GetNodeContextMenuActions ( Menu , Context ) ;
2020-10-12 17:44:44 -04:00
static const FName PromotableOperatorNodeName = FName ( " PromotableOperator " ) ;
static const FText PromotableOperatorStr = LOCTEXT ( " PromotableOperatorNode " , " Operator Node " ) ;
2023-08-11 10:30:15 -04:00
if ( CVarAllowConversionOfComparisonOps . GetValueOnAnyThread ( ) & & CanConvertComparisonOperatorNodeType ( Context - > Node ) )
{
static const FName ConvertComparisonOpName = FName ( " ConvertComparisonOp " ) ;
static const FText ConvertComparisonOpStr = LOCTEXT ( " ConvertComparisonOp " , " Convert Operator " ) ;
FToolMenuSection & Section = Menu - > AddSection ( ConvertComparisonOpName , ConvertComparisonOpStr ) ;
for ( const FName & PossibleConversionOpName : FTypePromotion : : GetComparisonOpNames ( ) )
{
// Don't display our current operator type
if ( PossibleConversionOpName = = OperationName )
{
continue ;
}
2020-10-12 17:44:44 -04:00
2023-08-11 10:30:15 -04:00
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " NewOpName " ) , FTypePromotion : : GetUserFacingOperatorName ( PossibleConversionOpName ) ) ;
Args . Add ( TEXT ( " CurrentOpName " ) , FTypePromotion : : GetUserFacingOperatorName ( OperationName ) ) ;
2023-08-14 10:30:07 -04:00
const FText PinConversionName = FText : : Format ( LOCTEXT ( " ConvertOpName_Tooltip " , " Convert to {NewOpName} " ) , Args ) ;
2023-08-11 10:30:15 -04:00
Section . AddMenuEntry (
FName ( PinConversionName . ToString ( ) ) ,
PinConversionName ,
2023-08-14 10:30:07 -04:00
FText : : Format ( LOCTEXT ( " ConvertOperator_ToType_Tooltip " , " Convert this node operation from '{CurrentOpName}' to '{NewOpName}' " ) , Args ) ,
2023-08-11 10:30:15 -04:00
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateStatic ( & UK2Node_PromotableOperator : : ConvertComparisonOperatorNode , const_cast < UEdGraphNode * > ( Context - > Node . Get ( ) ) , PossibleConversionOpName )
)
) ;
}
}
2020-10-12 17:44:44 -04:00
// Add the option to remove a pin via the context menu
if ( CanRemovePin ( Context - > Pin ) )
{
FToolMenuSection & Section = Menu - > AddSection ( PromotableOperatorNodeName , PromotableOperatorStr ) ;
Section . AddMenuEntry (
" RemovePin " ,
LOCTEXT ( " RemovePin " , " Remove pin " ) ,
LOCTEXT ( " RemovePinTooltip " , " Remove this input pin " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateUObject ( const_cast < UK2Node_PromotableOperator * > ( this ) , & UK2Node_PromotableOperator : : RemoveInputPin , const_cast < UEdGraphPin * > ( Context - > Pin ) )
)
) ;
}
else if ( CanAddPin ( ) )
{
FToolMenuSection & Section = Menu - > AddSection ( PromotableOperatorNodeName , PromotableOperatorStr ) ;
Section . AddMenuEntry (
" AddPin " ,
LOCTEXT ( " AddPin " , " Add pin " ) ,
LOCTEXT ( " AddPinTooltip " , " Add another input pin " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateUObject ( const_cast < UK2Node_PromotableOperator * > ( this ) , & UK2Node_PromotableOperator : : AddInputPin )
)
) ;
}
2021-02-01 19:47:28 -04:00
// Add the pin conversion sub menu
2021-02-22 15:43:31 -04:00
if ( CanConvertPinType ( Context - > Pin ) )
2021-02-01 19:47:28 -04:00
{
2022-11-04 12:12:44 -04:00
static const FName ConvNodeName = FName ( " PromotableOperatorPinConvs " ) ;
static const FText ConvNodeStr = LOCTEXT ( " PromotableOperatorPinConvs " , " Pin Conversions " ) ;
FToolMenuSection & ConversionSection = Menu - > AddSection ( ConvNodeName , ConvNodeStr ) ;
2021-02-22 15:43:31 -04:00
// Give the user an option to reset this node to wildcard
if ( ! FWildcardNodeUtils : : IsWildcardPin ( Context - > Pin ) )
{
2022-11-04 12:12:44 -04:00
const FText ResetName = LOCTEXT ( " ResetFunction " , " To Wildcard " ) ;
2021-02-22 15:43:31 -04:00
2022-11-04 12:12:44 -04:00
ConversionSection . AddMenuEntry (
2021-02-22 15:43:31 -04:00
FName ( ResetName . ToString ( ) ) ,
ResetName ,
2022-11-04 12:12:44 -04:00
LOCTEXT ( " ResetToWildcardTooltip " , " Break all connections and reset this node to a wildcard state. " ) ,
2021-02-22 15:43:31 -04:00
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateUObject ( const_cast < UK2Node_PromotableOperator * > ( this ) , & UK2Node_PromotableOperator : : ConvertPinType , const_cast < UEdGraphPin * > ( Context - > Pin ) , FWildcardNodeUtils : : GetDefaultWildcardPinType ( ) )
)
) ;
}
2022-11-04 12:12:44 -04:00
CreateConversionMenu ( ConversionSection , ( UEdGraphPin * ) Context - > Pin ) ;
2021-02-01 19:47:28 -04:00
}
}
2022-11-04 12:12:44 -04:00
void UK2Node_PromotableOperator : : CreateConversionMenu ( FToolMenuSection & ConversionSection , UEdGraphPin * PinToConvert ) const
2021-02-01 19:47:28 -04:00
{
2022-11-04 12:12:44 -04:00
check ( PinToConvert ) ;
2021-02-01 19:47:28 -04:00
2021-02-22 15:43:31 -04:00
// Gather what pin types could possibly be used for a conversion with this operator
TArray < UFunction * > AvailableFunctions ;
FTypePromotion : : GetAllFuncsForOp ( OperationName , AvailableFunctions ) ;
TArray < FEdGraphPinType > PossiblePromos ;
const UEdGraphSchema_K2 * Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
2021-02-01 19:47:28 -04:00
2021-02-22 15:43:31 -04:00
// If this is a split pin, then we need to convert the parent pin, not the child.
2022-11-04 12:12:44 -04:00
if ( PinToConvert - > ParentPin ! = nullptr )
2021-02-22 15:43:31 -04:00
{
2022-11-04 12:12:44 -04:00
PinToConvert = PinToConvert - > ParentPin ;
2021-02-01 19:47:28 -04:00
}
2021-02-22 15:43:31 -04:00
// If we have a pin that matches our current type, then we can use it to see if we can still get a valid function
2022-11-04 12:12:44 -04:00
FEdGraphPinType OriginalContextType = PinToConvert - > PinType ;
2021-02-22 15:43:31 -04:00
for ( const UFunction * Func : AvailableFunctions )
{
for ( TFieldIterator < FProperty > PropIt ( Func ) ; PropIt & & ( PropIt - > PropertyFlags & CPF_Parm ) ; + + PropIt )
2021-02-01 19:47:28 -04:00
{
2021-02-22 15:43:31 -04:00
const FProperty * Param = * PropIt ;
FEdGraphPinType ParamType ;
if ( Schema - > ConvertPropertyToPinType ( Param , /* out */ ParamType ) )
2021-02-01 19:47:28 -04:00
{
2022-11-04 12:12:44 -04:00
if ( FWildcardNodeUtils : : IsWildcardPin ( PinToConvert ) | | FTypePromotion : : IsValidPromotion ( ParamType , PinToConvert - > PinType ) | | FTypePromotion : : IsValidPromotion ( PinToConvert - > PinType , ParamType ) )
2021-02-01 19:47:28 -04:00
{
PossiblePromos . AddUnique ( ParamType ) ;
}
}
}
2021-02-22 15:43:31 -04:00
}
2021-02-01 19:47:28 -04:00
2021-02-22 15:43:31 -04:00
// Don't display the conversion menu if there are no valid conversions
if ( AvailableFunctions . Num ( ) = = 0 )
{
return ;
}
2021-02-01 19:47:28 -04:00
2021-02-22 15:43:31 -04:00
// Add the options to the context menu
for ( const FEdGraphPinType & PinType : PossiblePromos )
{
FFormatNamedArguments Args ;
2022-11-04 12:12:44 -04:00
Args . Add ( TEXT ( " NewPinType " ) , Schema - > TypeToText ( PinType ) ) ;
Args . Add ( TEXT ( " CurrentPinType " ) , Schema - > TypeToText ( OriginalContextType ) ) ;
2021-02-01 19:47:28 -04:00
2023-08-14 10:30:07 -04:00
const FText PinConversionName = FText : : Format ( LOCTEXT ( " Convert_Pin_To_Type_Tooltip " , " To {NewPinType} " ) , Args ) ;
2021-02-01 19:47:28 -04:00
2022-11-04 12:12:44 -04:00
ConversionSection . AddMenuEntry (
2021-02-22 15:43:31 -04:00
FName ( PinConversionName . ToString ( ) ) ,
PinConversionName ,
2022-11-04 12:12:44 -04:00
FText : : Format ( LOCTEXT ( " ConvertPinTypeTooltip " , " Convert this pin type from '{CurrentPinType}' to '{NewPinType}' " ) , Args ) ,
2021-02-22 15:43:31 -04:00
FSlateIcon ( ) ,
FUIAction (
2022-11-04 12:12:44 -04:00
FExecuteAction : : CreateUObject ( const_cast < UK2Node_PromotableOperator * > ( this ) , & UK2Node_PromotableOperator : : ConvertPinType , const_cast < UEdGraphPin * > ( PinToConvert ) , PinType )
2021-02-22 15:43:31 -04:00
)
) ;
2021-02-01 19:47:28 -04:00
}
2020-08-27 18:38:23 -04:00
}
2021-02-04 15:10:41 -04:00
bool UK2Node_PromotableOperator : : CanConvertPinType ( const UEdGraphPin * Pin ) const
{
2021-02-22 15:43:31 -04:00
// You can convert any pin except for output pins on comparison functions and the tolerance pin
return
Pin & &
! ( Pin - > Direction = = EGPD_Output & &
FTypePromotion : : IsComparisonFunc ( GetTargetFunction ( ) ) ) & &
2023-01-27 11:23:35 -05:00
! IsTolerancePin ( * Pin ) ;
2021-02-04 15:10:41 -04:00
}
2023-08-11 10:30:15 -04:00
bool UK2Node_PromotableOperator : : CanConvertComparisonOperatorNodeType ( const UEdGraphNode * Node )
{
if ( ! Node )
{
return false ;
}
if ( const UK2Node_PromotableOperator * OpNode = Cast < UK2Node_PromotableOperator > ( Node ) )
{
// Only allow conversions between simple comparison types. If we have a tolerance pin, don't allow conversion
// because not all operators for those types have a tolerance.
const UEdGraphPin * TolerancePin = OpNode - > FindTolerancePin ( ) ;
const bool bTolerancePinAcceptable = ! TolerancePin | | TolerancePin - > bHidden ;
const UFunction * TargetFunc = OpNode - > GetTargetFunction ( ) ;
// If the target function is null then it's a wildcard node and we can convert it
return ( TargetFunc = = nullptr | | FTypePromotion : : IsComparisonFunc ( TargetFunc ) ) & & bTolerancePinAcceptable ;
}
return false ;
}
void UK2Node_PromotableOperator : : ConvertComparisonOperatorNode ( UEdGraphNode * Node , const FName NewOpName )
{
UK2Node_PromotableOperator * OpNode = Cast < UK2Node_PromotableOperator > ( Node ) ;
// You can only convert to comparison operators, nothing else, because then we can be sure the number of pins to consider will be correct
// when finding the best matching function. Other math ops may behave in undefined ways if we allowed it.
if ( ! OpNode | | ! FTypePromotion : : IsComparisonOpName ( NewOpName ) )
{
return ;
}
TArray < UEdGraphPin * > PinsToConsider ;
OpNode - > GetPinsToConsider ( PinsToConsider ) ;
2023-08-14 12:22:51 -04:00
// In the case of this node having no pins to consider (it is a wildcard, with no default values or connections)
// we will just use the boolean output pin to ensure that we get a valid UFunction to use.
if ( PinsToConsider . IsEmpty ( ) )
{
UEdGraphPin * OutPin = OpNode - > GetOutputPin ( ) ;
// Because we only allow this conversion to happen on comparison operators, we can be sure the output pin will be a
// simple boolean output and that it is there
ensure ( OutPin & & OutPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Boolean ) ;
PinsToConsider . Add ( OutPin ) ;
}
// For nodes with connections we have to find the best function again that matches for them
2024-04-22 09:42:07 -04:00
if ( const UFunction * BestMatchingFunc = FTypePromotion : : FindBestMatchingFunc ( NewOpName , PinsToConsider ) )
2023-08-11 10:30:15 -04:00
{
2024-04-22 09:42:07 -04:00
FScopedTransaction Transaction ( LOCTEXT ( " PromotableOperatorComparisonOpConversion " , " Convert operator node " ) ) ;
OpNode - > Modify ( ) ;
OpNode - > OperationName = NewOpName ;
2023-08-11 10:30:15 -04:00
// Only allow this with comparison functions
ensure ( FTypePromotion : : IsComparisonFunc ( BestMatchingFunc ) ) ;
UE_LOG ( LogBlueprint , Verbose , TEXT ( " Converting node '%s' from '%s' to '%s'... " ) , * GetNameSafe ( OpNode ) , * OpNode - > OperationName . ToString ( ) , * NewOpName . ToString ( ) ) ;
OpNode - > SetFromFunction ( BestMatchingFunc ) ;
OpNode - > ReconstructNode ( ) ;
}
else
{
UE_LOG ( LogBlueprint , Error , TEXT ( " Failed to convert Convert node from '%s' to '%s' " ) , * OpNode - > OperationName . ToString ( ) , * NewOpName . ToString ( ) ) ;
}
}
2020-08-27 18:38:23 -04:00
FText UK2Node_PromotableOperator : : GetTooltipText ( ) const
{
2022-11-04 12:12:44 -04:00
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " FunctionTooltip " ) , ! HasAnyConnectionsOrDefaults ( ) ? FTypePromotion : : GetUserFacingOperatorName ( OperationName ) : Super : : GetTooltipText ( ) ) ;
2020-08-27 18:38:23 -04:00
2022-11-04 12:12:44 -04:00
return FText : : Format ( LOCTEXT ( " PromotableOpTooltipText " , " {FunctionTooltip} \n \n To go back to old math nodes, uncheck 'Enable Type Promotion' in the Blueprint Editor Settings. Both node types are supported. " ) , Args ) ;
2020-08-27 18:38:23 -04:00
}
2020-11-04 20:26:25 -04:00
void UK2Node_PromotableOperator : : PinDefaultValueChanged ( UEdGraphPin * Pin )
{
Super : : PinDefaultValueChanged ( Pin ) ;
if ( bDefaultValueReentranceGuard )
{
return ;
}
// Re-entrance Guard just in case this function gets called from any notify triggers in the schema
// to prevent possible recursive calls from ResetPinToAutogeneratedDefaultValue when breaking
// all links to this node
TGuardValue < bool > ReentranceGuard ( bDefaultValueReentranceGuard , true ) ;
// If this default value resets to the default one on the pin, and there are no other
// connections or default values, then we should just reset the whole node to a wildcard
if ( ! HasAnyConnectionsOrDefaults ( ) )
{
ResetNodeToWildcard ( ) ;
}
}
void UK2Node_PromotableOperator : : NodeConnectionListChanged ( )
{
Super : : NodeConnectionListChanged ( ) ;
// This will handle the case of dragging off of this node, and connecting to a node via typing
// in the context menu. Without updating in this case, our pins would be left as wildcards!
if ( HasAnyConnectionsOrDefaults ( ) )
{
UpdateOpName ( ) ;
UpdateFromBestMatchingFunction ( ) ;
// Get correct default value boxes
2023-03-10 20:03:26 -05:00
GetGraph ( ) - > NotifyNodeChanged ( this ) ;
2020-11-04 20:26:25 -04:00
}
}
2020-12-03 16:28:38 -04:00
void UK2Node_PromotableOperator : : PostPasteNode ( )
{
Super : : PostPasteNode ( ) ;
// If we have copied a node with additional pins then we need to make sure
// they get reset to wildcard as well, otherwise their type will persist
if ( ! HasAnyConnectionsOrDefaults ( ) )
{
ResetNodeToWildcard ( ) ;
}
}
2020-08-27 18:38:23 -04:00
///////////////////////////////////////////////////////////
// UK2Node interface
void UK2Node_PromotableOperator : : ExpandNode ( FKismetCompilerContext & CompilerContext , UEdGraph * SourceGraph )
{
Super : : ExpandNode ( CompilerContext , SourceGraph ) ;
const bool bValidOpName = UpdateOpName ( ) ;
if ( ! bValidOpName )
{
UE_LOG ( LogBlueprint , Error , TEXT ( " Could not find matching operation name for this function! " ) ) ;
CompilerContext . MessageLog . Error ( TEXT ( " Could not find matching operation on '@@'! " ) , this ) ;
return ;
}
2020-10-12 17:44:44 -04:00
UEdGraphPin * OriginalOutputPin = GetOutputPin ( ) ;
TArray < UEdGraphPin * > OriginalInputPins = GetInputPins ( ) ;
2020-08-27 18:38:23 -04:00
// Our operator function has been determined on pin connection change
2020-10-12 17:44:44 -04:00
UFunction * OpFunction = GetTargetFunction ( ) ;
2020-08-27 18:38:23 -04:00
if ( ! OpFunction )
{
UE_LOG ( LogBlueprint , Error , TEXT ( " Could not find matching op function during expansion! " ) ) ;
CompilerContext . MessageLog . Error ( TEXT ( " Could not find matching op function during expansion on '@@'! " ) , this ) ;
return ;
}
2020-10-12 17:44:44 -04:00
const UEdGraphSchema_K2 * Schema = CompilerContext . GetSchema ( ) ;
2020-08-27 18:38:23 -04:00
2020-10-12 17:44:44 -04:00
/** Helper struct to gather the necessary pins we need to create redirections */
struct FIntermediateCastPinHelper
2020-08-27 18:38:23 -04:00
{
2020-10-12 17:44:44 -04:00
UEdGraphPin * InputA = nullptr ;
UEdGraphPin * InputB = nullptr ;
UEdGraphPin * OutputPin = nullptr ;
UEdGraphPin * SelfPin = nullptr ;
2020-12-05 12:00:19 -04:00
UEdGraphPin * TolerancePin = nullptr ;
2020-08-27 18:38:23 -04:00
2020-10-12 17:44:44 -04:00
explicit FIntermediateCastPinHelper ( UK2Node_CallFunction * NewOperator )
2020-08-27 18:38:23 -04:00
{
2020-10-12 17:44:44 -04:00
check ( NewOperator ) ;
SelfPin = NewOperator - > FindPin ( UEdGraphSchema_K2 : : PN_Self ) ;
2020-12-05 12:00:19 -04:00
TolerancePin = NewOperator - > FindPin ( TolerancePin_Name , EGPD_Input ) ;
2020-10-12 17:44:44 -04:00
// Find inputs and outputs
for ( UEdGraphPin * Pin : NewOperator - > Pins )
2020-08-27 18:38:23 -04:00
{
2020-12-05 12:00:19 -04:00
if ( Pin = = SelfPin | | Pin = = TolerancePin )
2020-10-12 17:44:44 -04:00
{
continue ;
}
if ( Pin - > Direction = = EEdGraphPinDirection : : EGPD_Input )
{
if ( ! InputA )
{
InputA = Pin ;
}
else if ( ! InputB )
{
InputB = Pin ;
}
}
else if ( Pin - > Direction = = EEdGraphPinDirection : : EGPD_Output )
{
OutputPin = Pin ;
}
2020-08-27 18:38:23 -04:00
}
}
2020-10-12 17:44:44 -04:00
~ FIntermediateCastPinHelper ( ) = default ;
} ;
UK2Node_CallFunction * PrevIntermediateNode = nullptr ;
UEdGraphPin * PrevOutputPin = nullptr ;
2020-12-05 12:00:19 -04:00
UEdGraphPin * MyTolerancePin = FindTolerancePin ( ) ;
2020-10-12 17:44:44 -04:00
// Create cast from original 2 inputs to the first intermediate node
{
UFunction * BestFunc = OpFunction ;
2021-01-13 21:01:37 -04:00
// Look for a best matching function if we possibly don't have one
if ( ! BestFunc )
2020-08-27 18:38:23 -04:00
{
2020-10-12 17:44:44 -04:00
TArray < UEdGraphPin * > PinsToConsider =
{
OriginalInputPins [ 0 ] ,
OriginalInputPins [ 1 ] ,
OriginalOutputPin
} ;
if ( UFunction * Func = FTypePromotion : : FindBestMatchingFunc ( OperationName , PinsToConsider ) )
{
BestFunc = Func ;
}
}
PrevIntermediateNode = CreateIntermediateNode ( this , BestFunc , CompilerContext , SourceGraph ) ;
FIntermediateCastPinHelper NewOpHelper ( PrevIntermediateNode ) ;
PrevOutputPin = PrevIntermediateNode - > FindPin ( UEdGraphSchema_K2 : : PN_ReturnValue , EGPD_Output ) ;
const bool bPinASuccess = UK2Node_PromotableOperator : : CreateIntermediateCast ( this , CompilerContext , SourceGraph , OriginalInputPins [ 0 ] , NewOpHelper . InputA ) ;
const bool bPinBSuccess = UK2Node_PromotableOperator : : CreateIntermediateCast ( this , CompilerContext , SourceGraph , OriginalInputPins [ 1 ] , NewOpHelper . InputB ) ;
2020-12-05 12:00:19 -04:00
// Attempt to connect the tolerance pin if both nodes have one
const bool bToleranceSuccess = MyTolerancePin & & NewOpHelper . TolerancePin ? UK2Node_PromotableOperator : : CreateIntermediateCast ( this , CompilerContext , SourceGraph , MyTolerancePin , NewOpHelper . TolerancePin ) : true ;
2020-10-12 17:44:44 -04:00
2020-12-05 12:00:19 -04:00
if ( ! bPinASuccess | | ! bPinBSuccess | | ! bToleranceSuccess )
2020-10-12 17:44:44 -04:00
{
CompilerContext . MessageLog . Error ( TEXT ( " '@@' could not successfuly expand pins! " ) , PrevIntermediateNode ) ;
2020-08-27 18:38:23 -04:00
}
}
2020-10-25 18:06:25 -04:00
2020-10-12 17:44:44 -04:00
// Loop through all the additional inputs, create a new node of this function and connecting inputs as necessary
for ( int32 i = NumFunctionInputs ; i < NumAdditionalInputs + NumFunctionInputs ; + + i )
2020-08-27 18:38:23 -04:00
{
2020-10-12 17:44:44 -04:00
check ( i > 0 & & i < OriginalInputPins . Num ( ) ) ;
FIntermediateCastPinHelper PrevNodeHelper ( PrevIntermediateNode ) ;
// Find the best matching function that this intermediate node should use
// so that we can avoid unnecessary conversion nodes and casts
UFunction * BestMatchingFunc = OpFunction ;
{
2020-10-25 18:06:25 -04:00
TArray < UEdGraphPin * > PinsToConsider =
2020-10-12 17:44:44 -04:00
{
PrevNodeHelper . OutputPin ,
OriginalInputPins [ i ] ,
OriginalOutputPin
} ;
2020-10-25 18:06:25 -04:00
2020-10-12 17:44:44 -04:00
if ( UFunction * Func = FTypePromotion : : FindBestMatchingFunc ( OperationName , PinsToConsider ) )
{
BestMatchingFunc = Func ;
}
}
UK2Node_CallFunction * NewIntermediateNode = CreateIntermediateNode ( PrevIntermediateNode , BestMatchingFunc , CompilerContext , SourceGraph ) ;
FIntermediateCastPinHelper NewOpHelper ( NewIntermediateNode ) ;
// Connect the output pin of the previous intermediate node, to the input of the new one
const bool bPinASuccess = CreateIntermediateCast ( PrevIntermediateNode , CompilerContext , SourceGraph , NewOpHelper . InputA , PrevOutputPin ) ;
2020-10-25 18:06:25 -04:00
2020-10-12 17:44:44 -04:00
// Connect the original node's pin to the newly created intermediate node's B Pin
const bool bPinBSuccess = CreateIntermediateCast ( this , CompilerContext , SourceGraph , OriginalInputPins [ i ] , NewOpHelper . InputB ) ;
2020-12-05 12:00:19 -04:00
// Make a connection to a tolerance pin if both nodes have one
const bool bToleranceSuccess = MyTolerancePin & & NewOpHelper . TolerancePin ? UK2Node_PromotableOperator : : CreateIntermediateCast ( this , CompilerContext , SourceGraph , MyTolerancePin , NewOpHelper . TolerancePin ) : true ;
2020-10-25 18:06:25 -04:00
2020-12-05 12:00:19 -04:00
if ( ! bPinASuccess | | ! bPinBSuccess | | ! bToleranceSuccess )
2020-10-12 17:44:44 -04:00
{
CompilerContext . MessageLog . Error ( TEXT ( " '@@' could not successfuly expand additional pins! " ) , PrevIntermediateNode ) ;
}
// Track what the previous node is so that we can connect it's output appropriately
PrevOutputPin = NewOpHelper . OutputPin ;
PrevIntermediateNode = NewIntermediateNode ;
2020-08-27 18:38:23 -04:00
}
2020-10-12 17:44:44 -04:00
// Make the final output connection that we need
if ( OriginalOutputPin & & PrevOutputPin )
{
CompilerContext . MovePinLinksToIntermediate ( * OriginalOutputPin , * PrevOutputPin ) ;
2021-01-26 16:55:11 -04:00
// If there is no link to the output pin then the connection response called for a conversion node,
// but one was not available. This can occur when there is a connection to the output pin that
// is smaller than an input and cannot be casted. We throw a compiler error instead of auto-breaking
// pins because that can be confusing to the user.
if ( PrevOutputPin - > LinkedTo . Num ( ) = = 0 )
{
CompilerContext . MessageLog . Error (
* FText : : Format ( LOCTEXT ( " FailedOutputConnection_ErrorFmt " ,
" Output pin type '{1}' is not compatible with input type of '{0}' on '@@' " ) ,
Schema - > TypeToText ( PrevOutputPin - > PinType ) ,
Schema - > TypeToText ( OriginalOutputPin - > PinType ) ) . ToString ( ) ,
PrevIntermediateNode
) ;
}
2020-10-12 17:44:44 -04:00
}
2020-08-27 18:38:23 -04:00
}
void UK2Node_PromotableOperator : : NotifyPinConnectionListChanged ( UEdGraphPin * ChangedPin )
{
Super : : NotifyPinConnectionListChanged ( ChangedPin ) ;
2020-10-13 12:13:55 -04:00
EvaluatePinsFromChange ( ChangedPin ) ;
2020-08-27 18:38:23 -04:00
}
void UK2Node_PromotableOperator : : PostReconstructNode ( )
{
Super : : PostReconstructNode ( ) ;
// We only need to set the function if we have connections, otherwise we should stick in a wildcard state
if ( HasAnyConnectionsOrDefaults ( ) )
{
2020-12-05 12:00:19 -04:00
if ( UEdGraphPin * TolerancePin = FindTolerancePin ( ) )
{
FEdGraphPinType ToleranceType ;
TolerancePin - > bHidden = ! PromotableOpUtils : : FindTolerancePinType ( GetTargetFunction ( ) , ToleranceType ) ;
}
2020-08-27 18:38:23 -04:00
// Allocate default pins will have been called before this, which means we are reset to wildcard state
// We need to Update the pins to be the proper function again
UpdatePinsFromFunction ( GetTargetFunction ( ) ) ;
2020-10-12 17:44:44 -04:00
for ( UEdGraphPin * AddPin : Pins )
{
2023-01-27 11:23:35 -05:00
if ( ensure ( AddPin ) & & IsAdditionalPin ( * AddPin ) & & AddPin - > LinkedTo . Num ( ) > 0 )
2020-10-12 17:44:44 -04:00
{
FEdGraphPinType TypeToSet = FTypePromotion : : GetPromotedType ( AddPin - > LinkedTo ) ;
AddPin - > PinType = TypeToSet ;
}
}
2020-08-27 18:38:23 -04:00
}
2020-12-05 12:00:19 -04:00
else if ( UEdGraphPin * TolerancePin = FindTolerancePin ( ) )
{
TolerancePin - > bHidden = true ;
}
2020-08-27 18:38:23 -04:00
}
bool UK2Node_PromotableOperator : : IsConnectionDisallowed ( const UEdGraphPin * MyPin , const UEdGraphPin * OtherPin , FString & OutReason ) const
{
check ( MyPin & & OtherPin ) ;
2020-12-10 13:39:52 -04:00
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
2021-05-04 11:03:15 -04:00
// Container types cannot be promotable operators as they have their own special case wildcard propagation
2020-12-10 13:39:52 -04:00
if ( OtherPin - > PinType . IsContainer ( ) )
2020-08-27 18:38:23 -04:00
{
OutReason = LOCTEXT ( " NoExecPinsAllowed " , " Promotable Operator nodes cannot have containers or references. " ) . ToString ( ) ;
return true ;
}
2020-11-18 15:19:46 -04:00
// The output pin on comparison operators is always a boolean, and cannot have its type changed
// so we need to just treat it normally as a regular K2CallFunction node would
2020-08-27 18:38:23 -04:00
else if ( MyPin = = GetOutputPin ( ) & & FTypePromotion : : IsComparisonFunc ( GetTargetFunction ( ) ) & & OtherPin - > PinType . PinCategory ! = UEdGraphSchema_K2 : : PC_Boolean )
{
2020-11-18 15:19:46 -04:00
return Super : : IsConnectionDisallowed ( MyPin , OtherPin , OutReason ) ;
2020-08-27 18:38:23 -04:00
}
2020-12-07 21:02:36 -04:00
// If the pins are the same type then there is no reason to check for a promotion
else if ( MyPin - > PinType = = OtherPin - > PinType )
{
return Super : : IsConnectionDisallowed ( MyPin , OtherPin , OutReason ) ;
}
2020-12-15 17:53:40 -04:00
// Enums need to be casted to a byte manually before we can do math with them, like in C++
else if ( ! FWildcardNodeUtils : : IsWildcardPin ( MyPin ) & & MyPin - > Direction = = EGPD_Input & & OtherPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Byte & & OtherPin - > PinType . PinSubCategoryObject ! = nullptr )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " OtherPinType " ) , K2Schema - > TypeToText ( OtherPin - > PinType ) ) ;
OutReason = FText : : Format ( LOCTEXT ( " NoCompatibleEnumConv " , " '{OtherPinType}' must be converted to a numeric type before being connected " ) , Args ) . ToString ( ) ;
return true ;
}
2020-12-10 13:39:52 -04:00
else if ( FWildcardNodeUtils : : IsWildcardPin ( MyPin ) & & ! FWildcardNodeUtils : : IsWildcardPin ( OtherPin ) )
{
TArray < UEdGraphPin * > PinsToConsider ;
PinsToConsider . Add ( const_cast < UEdGraphPin * > ( OtherPin ) ) ;
if ( ! FTypePromotion : : FindBestMatchingFunc ( OperationName , PinsToConsider ) )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " OtherPinType " ) , K2Schema - > TypeToText ( OtherPin - > PinType ) ) ;
Args . Add ( TEXT ( " OpName " ) , FText : : FromName ( OperationName ) ) ;
2021-10-12 21:21:22 -04:00
const TSet < FName > & DenyList = GetDefault < UBlueprintEditorSettings > ( ) - > TypePromotionPinDenyList ;
if ( DenyList . Contains ( OtherPin - > PinType . PinCategory ) )
2021-09-17 17:51:07 -04:00
{
2021-10-12 21:21:22 -04:00
OutReason = FText : : Format ( LOCTEXT ( " NoCompatibleStructConv_Denied " , " No matching '{OpName}' function for '{OtherPinType}'. This type is listed as denied by TypePromotionPinDenyList in the blueprint editor settings. " ) , Args ) . ToString ( ) ;
2021-09-17 17:51:07 -04:00
}
else
{
OutReason = FText : : Format ( LOCTEXT ( " NoCompatibleStructConv " , " No matching '{OpName}' function for '{OtherPinType}' " ) , Args ) . ToString ( ) ;
}
2020-12-10 13:39:52 -04:00
return true ;
}
}
2020-08-27 18:38:23 -04:00
const bool bHasStructPin = MyPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Struct | | OtherPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Struct ;
// If the other pin can be promoted to my pin type, than allow the connection
if ( FTypePromotion : : IsValidPromotion ( OtherPin - > PinType , MyPin - > PinType ) )
{
if ( bHasStructPin )
{
// Compare the directions
const UEdGraphPin * InputPin = nullptr ;
const UEdGraphPin * OutputPin = nullptr ;
if ( ! K2Schema - > CategorizePinsByDirection ( MyPin , OtherPin , /*out*/ InputPin , /*out*/ OutputPin ) )
{
OutReason = LOCTEXT ( " DirectionsIncompatible " , " Pin directions are not compatible! " ) . ToString ( ) ;
return true ;
}
if ( ! FTypePromotion : : HasStructConversion ( InputPin , OutputPin ) )
{
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " MyPinType " ) , K2Schema - > TypeToText ( MyPin - > PinType ) ) ;
Args . Add ( TEXT ( " OtherPinType " ) , K2Schema - > TypeToText ( OtherPin - > PinType ) ) ;
2022-03-04 16:10:23 -05:00
OutReason = FText : : Format ( LOCTEXT ( " NoCompatibleOperatorConv " , " No compatible operator functions between '{MyPinType}' and '{OtherPinType}' " ) , Args ) . ToString ( ) ;
2020-08-27 18:38:23 -04:00
return true ;
}
}
return false ;
}
return Super : : IsConnectionDisallowed ( MyPin , OtherPin , OutReason ) ;
}
void UK2Node_PromotableOperator : : ReallocatePinsDuringReconstruction ( TArray < UEdGraphPin * > & OldPins )
{
Super : : ReallocatePinsDuringReconstruction ( OldPins ) ;
2020-10-12 17:44:44 -04:00
// We need to fix up any additional pins that may have been created as a wildcard pin
int32 AdditionalPinsFixed = 0 ;
// Additional Pin creation here? Check for orphan pins here and see if we can re-create them
for ( UEdGraphPin * OldPin : OldPins )
{
2023-01-27 11:23:35 -05:00
if ( ensure ( OldPin ) )
2020-10-12 17:44:44 -04:00
{
2023-01-27 11:23:35 -05:00
if ( IsAdditionalPin ( * OldPin ) )
2020-10-12 17:44:44 -04:00
{
2023-01-27 11:23:35 -05:00
if ( UEdGraphPin * AddPin = GetAdditionalPin ( AdditionalPinsFixed + NumFunctionInputs ) )
{
AddPin - > PinType = OldPin - > PinType ;
AddPin - > DefaultValue = OldPin - > DefaultValue ;
+ + AdditionalPinsFixed ;
}
2020-10-25 18:06:25 -04:00
}
2021-02-01 19:47:28 -04:00
2023-01-27 11:23:35 -05:00
// Copy the default value and pin type for pins without any links to preseve it
UEdGraphPin * CurrentPin = FindPin ( OldPin - > GetFName ( ) , OldPin - > Direction ) ;
if ( CurrentPin & & OldPin - > LinkedTo . Num ( ) = = 0 & & ! OldPin - > DoesDefaultValueMatchAutogenerated ( ) )
{
CurrentPin - > PinType = OldPin - > PinType ;
CurrentPin - > DefaultValue = OldPin - > DefaultValue ;
}
2021-02-01 19:47:28 -04:00
}
2020-10-12 17:44:44 -04:00
}
2020-08-27 18:38:23 -04:00
}
void UK2Node_PromotableOperator : : AutowireNewNode ( UEdGraphPin * ChangedPin )
{
Super : : AutowireNewNode ( ChangedPin ) ;
2020-10-13 12:13:55 -04:00
EvaluatePinsFromChange ( ChangedPin ) ;
2020-08-27 18:38:23 -04:00
}
2021-11-07 23:43:01 -05:00
void UK2Node_PromotableOperator : : GetPinHoverText ( const UEdGraphPin & Pin , FString & HoverTextOut ) const
{
Super : : GetPinHoverText ( Pin , HoverTextOut ) ;
static const FText RightClickToConv = LOCTEXT ( " RightClickToConvTooltip " , " \n \n Right click this pin to convert its type. " ) ;
HoverTextOut + = RightClickToConv . ToString ( ) ;
}
2020-10-12 17:44:44 -04:00
///////////////////////////////////////////////////////////
// IK2Node_AddPinInterface
void UK2Node_PromotableOperator : : AddInputPin ( )
2020-08-27 18:38:23 -04:00
{
2020-10-12 17:44:44 -04:00
if ( CanAddPin ( ) )
{
FScopedTransaction Transaction ( LOCTEXT ( " AddPinPromotableOperator " , " AddPin " ) ) ;
Modify ( ) ;
AddInputPinImpl ( NumFunctionInputs + NumAdditionalInputs ) ;
+ + NumAdditionalInputs ;
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( GetBlueprint ( ) ) ;
}
}
bool UK2Node_PromotableOperator : : CanAddPin ( ) const
{
return ( ( NumAdditionalInputs + NumFunctionInputs ) < GetMaxInputPinsNum ( ) ) & &
2020-11-11 12:19:54 -04:00
! FTypePromotion : : IsComparisonFunc ( GetTargetFunction ( ) ) ;
2020-10-12 17:44:44 -04:00
}
bool UK2Node_PromotableOperator : : CanRemovePin ( const UEdGraphPin * Pin ) const
{
return (
2020-12-02 17:56:14 -04:00
Pin & & Pin - > ParentPin = = nullptr & &
2020-10-12 17:44:44 -04:00
NumAdditionalInputs > 0 & &
INDEX_NONE ! = Pins . IndexOfByKey ( Pin ) & &
Pin - > Direction = = EEdGraphPinDirection : : EGPD_Input
2020-10-25 18:06:25 -04:00
) ;
2020-10-12 17:44:44 -04:00
}
void UK2Node_PromotableOperator : : RemoveInputPin ( UEdGraphPin * Pin )
{
if ( CanRemovePin ( Pin ) )
{
FScopedTransaction Transaction ( LOCTEXT ( " RemovePinPromotableOperator " , " RemovePin " ) ) ;
Modify ( ) ;
if ( RemovePin ( Pin ) )
{
- - NumAdditionalInputs ;
int32 NameIndex = 0 ;
const UEdGraphPin * SelfPin = FindPin ( UEdGraphSchema_K2 : : PN_Self ) ;
for ( int32 PinIndex = 0 ; PinIndex < Pins . Num ( ) ; + + PinIndex )
{
UEdGraphPin * LocalPin = Pins [ PinIndex ] ;
2020-12-02 17:56:14 -04:00
if ( LocalPin & & ( LocalPin - > Direction ! = EGPD_Output ) & & ( LocalPin ! = SelfPin ) & & LocalPin - > ParentPin = = nullptr )
2020-10-12 17:44:44 -04:00
{
const FName PinName = GetNameForAdditionalPin ( NameIndex ) ;
if ( PinName ! = LocalPin - > PinName )
{
LocalPin - > Modify ( ) ;
LocalPin - > PinName = PinName ;
}
NameIndex + + ;
}
}
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( GetBlueprint ( ) ) ;
}
}
2020-12-02 17:56:14 -04:00
// If the pin that was removed makes this node empty then we can just reset it
// and have no need to reevaluate any pins
if ( ! HasAnyConnectionsOrDefaults ( ) )
{
ResetNodeToWildcard ( ) ;
}
2020-10-12 17:44:44 -04:00
}
UEdGraphPin * UK2Node_PromotableOperator : : GetAdditionalPin ( int32 PinIndex ) const
{
const FName PinToFind = GetNameForAdditionalPin ( PinIndex ) ;
for ( UEdGraphPin * Pin : Pins )
{
if ( Pin & & Pin - > PinName = = PinToFind )
{
return Pin ;
}
}
return nullptr ;
2020-08-27 18:38:23 -04:00
}
2020-12-04 12:11:27 -04:00
UEdGraphPin * UK2Node_PromotableOperator : : FindTolerancePin ( ) const
{
return FindPin ( TolerancePin_Name , EGPD_Input ) ;
}
2020-11-11 17:54:13 -04:00
///////////////////////////////////////////////////////////
// UK2Node_CallFunction interface
void UK2Node_PromotableOperator : : SetFromFunction ( const UFunction * Function )
{
if ( Function )
{
OperationName = FTypePromotion : : GetOpNameFromFunction ( Function ) ;
}
2021-05-20 03:38:47 -04:00
// During compilation of an Anim BP, if this node gets pruned then the outer will be the /Engine/Transient/
// package which will result in this node not having a valid SelfContext, so there is no need
// to set any further information based on this function as it will not be used.
if ( GetBlueprintClassFromNode ( ) )
{
Super : : SetFromFunction ( Function ) ;
}
2020-11-11 17:54:13 -04:00
}
2020-08-27 18:38:23 -04:00
///////////////////////////////////////////////////////////
// UK2Node_PromotableOperator
2023-01-27 11:23:35 -05:00
bool UK2Node_PromotableOperator : : IsTolerancePin ( const UEdGraphPin & Pin ) const
2020-11-19 12:30:03 -04:00
{
2023-01-27 11:23:35 -05:00
return Pin . PinName = = TolerancePin_Name & & Pin . Direction = = EGPD_Input ;
2020-11-19 12:30:03 -04:00
}
2020-10-12 17:44:44 -04:00
UEdGraphPin * UK2Node_PromotableOperator : : AddInputPinImpl ( int32 PinIndex )
{
const FName NewPinName = GetNameForAdditionalPin ( PinIndex ) ;
UEdGraphPin * NewPin = FWildcardNodeUtils : : CreateWildcardPin ( this , NewPinName , EGPD_Input ) ;
check ( NewPin ) ;
// Determine a default type for this pin if we have other input connections
const TArray < UEdGraphPin * > InputPins = GetInputPins ( /* bIncludeLinks = */ true ) ;
check ( InputPins . Num ( ) ) ;
FEdGraphPinType PromotedType = FTypePromotion : : GetPromotedType ( InputPins ) ;
NewPin - > PinType = PromotedType ;
return NewPin ;
}
2023-01-27 11:23:35 -05:00
bool UK2Node_PromotableOperator : : IsAdditionalPin ( const UEdGraphPin & Pin ) const
2020-10-12 17:44:44 -04:00
{
// Quickly check if this input pin is one of the two default input pins
2023-01-25 12:40:58 -05:00
bool bIsDefaultPinA =
2023-01-27 11:23:35 -05:00
( Pin . PinName = = InputPinA_Name ) | |
( Pin . ParentPin & & ( Pin . ParentPin - > PinName = = InputPinA_Name ) ) ;
2023-01-25 12:40:58 -05:00
bool bIsDefaultPinB =
2023-01-27 11:23:35 -05:00
( Pin . PinName = = InputPinB_Name ) | |
( Pin . ParentPin & & ( Pin . ParentPin - > PinName = = InputPinB_Name ) ) ;
2023-01-25 12:40:58 -05:00
2023-01-27 11:23:35 -05:00
return ( Pin . Direction = = EGPD_Input ) & & ! bIsDefaultPinA & & ! bIsDefaultPinB & & ! IsTolerancePin ( Pin ) ;
2020-10-12 17:44:44 -04:00
}
2020-08-27 18:38:23 -04:00
bool UK2Node_PromotableOperator : : HasAnyConnectionsOrDefaults ( ) const
{
2021-01-29 16:08:52 -04:00
if ( FTypePromotion : : IsComparisonOpName ( OperationName ) )
{
return HasDeterminingComparisonTypes ( ) ;
}
2020-08-27 18:38:23 -04:00
for ( UEdGraphPin * Pin : Pins )
{
if ( Pin - > LinkedTo . Num ( ) > 0 | | ! Pin - > DoesDefaultValueMatchAutogenerated ( ) )
{
return true ;
}
}
return false ;
}
2021-01-29 16:08:52 -04:00
bool UK2Node_PromotableOperator : : HasDeterminingComparisonTypes ( ) const
{
if ( ! FTypePromotion : : IsComparisonOpName ( OperationName ) )
{
return false ;
}
// For comparison ops, we only want to check the input pins as the output
// will always be a bool
for ( const UEdGraphPin * Pin : Pins )
{
if ( Pin & & Pin - > Direction = = EGPD_Input & & ( Pin - > LinkedTo . Num ( ) > 0 | | ! Pin - > DoesDefaultValueMatchAutogenerated ( ) ) )
{
return true ;
}
}
return false ;
}
2021-02-01 19:47:28 -04:00
void UK2Node_PromotableOperator : : EvaluatePinsFromChange ( UEdGraphPin * ChangedPin , const bool bFromConversion /* = false*/ )
2020-10-13 12:13:55 -04:00
{
UpdateOpName ( ) ;
2022-03-22 11:31:01 -04:00
if ( ! ensureMsgf ( ChangedPin , TEXT ( " UK2Node_PromotableOperator::EvaluatePinsFromChange failed to evaluate on a null pin! " ) ) )
{
return ;
}
2020-10-13 12:13:55 -04:00
// True if the pin that has changed now has zero connections
2021-02-01 19:47:28 -04:00
const bool bWasAFullDisconnect = ( ChangedPin - > LinkedTo . Num ( ) = = 0 ) & & ! HasAnyConnectionsOrDefaults ( ) ;
2021-01-26 17:48:50 -04:00
const bool bIsComparisonOp = FTypePromotion : : IsComparisonOpName ( OperationName ) ;
2020-10-13 12:13:55 -04:00
// If we have been totally disconnected and don't have any non-default inputs,
// than we just reset the node to be a regular wildcard
2021-02-01 19:47:28 -04:00
if ( ! bFromConversion & & ( bWasAFullDisconnect | | ( bIsComparisonOp & & ! HasDeterminingComparisonTypes ( ) ) ) )
2020-10-13 12:13:55 -04:00
{
ResetNodeToWildcard ( ) ;
return ;
}
// If the pin that was connected is linked to a wildcard pin, then we should make it a wildcard
// and do nothing else.
2022-03-22 11:31:01 -04:00
else if ( ChangedPin - > GetOwningNodeUnchecked ( ) = = this & & FWildcardNodeUtils : : IsLinkedToWildcard ( ChangedPin ) )
2020-10-13 12:13:55 -04:00
{
return ;
}
2020-12-05 12:00:19 -04:00
// Changing the tolerance pin doesn't effect the rest of the function signature, so don't attempt to update the func
2023-01-27 11:23:35 -05:00
else if ( IsTolerancePin ( * ChangedPin ) )
2020-12-05 12:00:19 -04:00
{
return ;
}
// If we have connected the output of our comparison operator (which is always a bool) then
// there is no need to update the other pins
2021-01-26 17:48:50 -04:00
else if ( bIsComparisonOp & & ChangedPin = = GetOutputPin ( ) )
2020-12-05 12:00:19 -04:00
{
return ;
}
2020-10-13 12:13:55 -04:00
2021-03-30 16:00:00 -04:00
// If the newest connection to this this pin is a different type,
// then we need to break all other type connections that are not the same
if ( ChangedPin - > LinkedTo . Num ( ) > 1 )
{
const FEdGraphPinType & MostRecentConnection = ChangedPin - > LinkedTo . Last ( ) - > PinType ;
for ( int32 i = ChangedPin - > LinkedTo . Num ( ) - 1 ; i > = 0 ; - - i )
{
UEdGraphPin * Link = ChangedPin - > LinkedTo [ i ] ;
2022-02-25 09:44:01 -05:00
const bool bIsRealNumberConnection =
( Link - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Real ) & &
( MostRecentConnection . PinCategory = = UEdGraphSchema_K2 : : PC_Real ) ;
// Real numbers are an exception to the normal link breaking rules.
// Even if the subcategories don't match, they're still valid connections
// since we implicitly cast between float and double types.
if ( bIsRealNumberConnection )
{
continue ;
}
2021-04-02 20:18:12 -04:00
if ( Link - > PinType . PinCategory ! = MostRecentConnection . PinCategory | |
Link - > PinType . PinSubCategory ! = MostRecentConnection . PinSubCategory | |
Link - > PinType . PinSubCategoryObject ! = MostRecentConnection . PinSubCategoryObject
)
2021-03-30 16:00:00 -04:00
{
ChangedPin - > BreakLinkTo ( Link ) ;
}
}
}
2020-12-05 12:00:19 -04:00
// Gather all pins and their links so we can determine the highest type
2020-10-19 22:02:12 -04:00
TArray < UEdGraphPin * > PinsToConsider ;
2020-11-19 12:30:03 -04:00
GetPinsToConsider ( PinsToConsider ) ;
2020-10-13 12:13:55 -04:00
2021-02-01 19:47:28 -04:00
if ( bFromConversion )
{
2021-02-25 14:04:06 -04:00
PinsToConsider . AddUnique ( ChangedPin ) ;
2021-02-01 19:47:28 -04:00
}
2020-10-20 13:25:04 -04:00
const UFunction * BestMatchingFunc = FTypePromotion : : FindBestMatchingFunc ( OperationName , PinsToConsider ) ;
2020-10-13 12:13:55 -04:00
2020-10-20 13:25:04 -04:00
// Store these other function options for later so that the user can convert to them later
2021-02-22 15:43:31 -04:00
UpdatePinsFromFunction ( BestMatchingFunc , ChangedPin , bFromConversion ) ;
2020-10-13 12:13:55 -04:00
}
2020-08-27 18:38:23 -04:00
bool UK2Node_PromotableOperator : : UpdateOpName ( )
{
2020-10-25 18:06:25 -04:00
2020-08-27 18:38:23 -04:00
// If the function is null then return false, because we did not successfully update it.
// This could be possible during node reconstruction/refresh, and we don't want to set the
// op name to "Empty" incorrectly.
2020-10-25 18:06:25 -04:00
if ( const UFunction * Func = GetTargetFunction ( ) )
{
OperationName = FTypePromotion : : GetOpNameFromFunction ( Func ) ;
return true ;
}
return false ;
2020-08-27 18:38:23 -04:00
}
2020-10-12 17:44:44 -04:00
UK2Node_CallFunction * UK2Node_PromotableOperator : : CreateIntermediateNode ( UK2Node_CallFunction * PreviousNode , const UFunction * const OpFunction , FKismetCompilerContext & CompilerContext , UEdGraph * SourceGraph )
{
// Spawn an intermediate UK2Node_CallFunction node of the function type we need
UK2Node_CallFunction * NewOperator = SourceGraph - > CreateIntermediateNode < UK2Node_CallFunction > ( ) ;
NewOperator - > SetFromFunction ( OpFunction ) ;
NewOperator - > AllocateDefaultPins ( ) ;
// Move this node next to the thing it was linked to
NewOperator - > NodePosY = PreviousNode - > NodePosY + 50 ;
NewOperator - > NodePosX = PreviousNode - > NodePosX + 8 ;
CompilerContext . MessageLog . NotifyIntermediateObjectCreation ( NewOperator , this ) ;
return NewOperator ;
}
bool UK2Node_PromotableOperator : : CreateIntermediateCast ( UK2Node_CallFunction * SourceNode , FKismetCompilerContext & CompilerContext , UEdGraph * SourceGraph , UEdGraphPin * InputPin , UEdGraphPin * OutputPin )
2020-08-27 18:38:23 -04:00
{
check ( InputPin & & OutputPin ) ;
2020-10-12 17:44:44 -04:00
const UEdGraphSchema_K2 * Schema = CompilerContext . GetSchema ( ) ;
2020-08-27 18:38:23 -04:00
2022-11-30 16:03:28 -05:00
// If the pin types are the same, than no casts are needed and we can just connect.
// This includes real number types that may have a different subcategory, which we implicitly convert.
const bool bPinTypesMatch =
( InputPin - > PinType = = OutputPin - > PinType ) | |
( ( InputPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Real ) & & ( OutputPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Real ) ) ;
if ( bPinTypesMatch )
2020-08-27 18:38:23 -04:00
{
2020-10-12 17:44:44 -04:00
// If SourceNode is 'this' then we need to move the pin links instead of just
// creating the connection because the output is not another new node, but
// just the intermediate expansion node.
if ( SourceNode = = this )
{
return ! CompilerContext . MovePinLinksToIntermediate ( * InputPin , * OutputPin ) . IsFatal ( ) ;
}
else
2020-10-25 18:06:25 -04:00
{
2020-10-12 17:44:44 -04:00
return Schema - > TryCreateConnection ( InputPin , OutputPin ) ;
}
2020-08-27 18:38:23 -04:00
}
UK2Node * TemplateConversionNode = nullptr ;
TSubclassOf < UK2Node > ConversionNodeClass ;
2022-12-12 15:50:51 -05:00
if ( TOptional < UEdGraphSchema_K2 : : FSearchForAutocastFunctionResults > AutoCastResults = Schema - > SearchForAutocastFunction ( InputPin - > PinType , OutputPin - > PinType ) )
2020-08-27 18:38:23 -04:00
{
// Create a new call function node for the casting operator
UK2Node_CallFunction * TemplateNode = SourceGraph - > CreateIntermediateNode < UK2Node_CallFunction > ( ) ;
2022-12-12 15:50:51 -05:00
TemplateNode - > FunctionReference . SetExternalMember ( AutoCastResults - > TargetFunction , AutoCastResults - > FunctionOwner ) ;
2020-08-27 18:38:23 -04:00
TemplateConversionNode = TemplateNode ;
TemplateNode - > AllocateDefaultPins ( ) ;
CompilerContext . MessageLog . NotifyIntermediateObjectCreation ( TemplateNode , this ) ;
}
2022-12-13 12:36:45 -05:00
else if ( TOptional < UEdGraphSchema_K2 : : FFindSpecializedConversionNodeResults > ConversionNodeResults = Schema - > FindSpecializedConversionNode ( InputPin - > PinType , * OutputPin , true ) )
2020-08-27 18:38:23 -04:00
{
2022-09-13 12:13:50 -04:00
FVector2D AverageLocation = UEdGraphSchema_K2 : : CalculateAveragePositionBetweenNodes ( InputPin , OutputPin ) ;
2022-12-12 15:50:51 -05:00
TemplateConversionNode = FEdGraphSchemaAction_K2NewNode : : SpawnNodeFromTemplate < UK2Node > ( SourceGraph , ConversionNodeResults - > TargetNode , AverageLocation ) ;
2022-09-13 12:13:50 -04:00
CompilerContext . MessageLog . NotifyIntermediateObjectCreation ( TemplateConversionNode , this ) ;
2020-08-27 18:38:23 -04:00
}
bool bInputSuccessful = false ;
bool bOutputSuccessful = false ;
if ( TemplateConversionNode )
{
UEdGraphPin * ConversionInput = nullptr ;
2022-09-13 12:13:50 -04:00
UEdGraphPin * ConversionOutput = TemplateConversionNode - > FindPin ( UEdGraphSchema_K2 : : PN_ReturnValue , EGPD_Output ) ;
2020-08-27 18:38:23 -04:00
for ( UEdGraphPin * ConvPin : TemplateConversionNode - > Pins )
{
2022-09-13 12:13:50 -04:00
if ( ConvPin )
2020-08-27 18:38:23 -04:00
{
2022-09-13 12:13:50 -04:00
if ( ! ConversionInput & & ConvPin - > Direction = = EGPD_Input & & ConvPin - > PinName ! = UEdGraphSchema_K2 : : PSC_Self )
{
ConversionInput = ConvPin ;
}
else if ( ! ConversionOutput & & ConvPin - > Direction = = EGPD_Output )
{
ConversionOutput = ConvPin ;
}
2020-08-27 18:38:23 -04:00
}
}
2022-09-13 12:13:50 -04:00
ensure ( ConversionInput & & ConversionOutput ) ;
2020-08-27 18:38:23 -04:00
2020-10-19 22:02:12 -04:00
// Connect my input to the conversion node directly if we have links, otherwise we need to move the intermediate version of it
2020-08-27 18:38:23 -04:00
if ( InputPin - > LinkedTo . Num ( ) > 0 )
{
bInputSuccessful = Schema - > TryCreateConnection ( InputPin - > LinkedTo [ 0 ] , ConversionInput ) ;
}
2020-10-25 18:06:25 -04:00
else if ( InputPin & & ConversionInput )
2020-10-19 22:02:12 -04:00
{
bInputSuccessful = ! CompilerContext . MovePinLinksToIntermediate ( * InputPin , * ConversionInput ) . IsFatal ( ) ;
}
2020-08-27 18:38:23 -04:00
// Connect conversion node output to the input of the new operator
bOutputSuccessful = Schema - > TryCreateConnection ( ConversionOutput , OutputPin ) ;
// Move this node next to the thing it was linked to
2020-10-12 17:44:44 -04:00
TemplateConversionNode - > NodePosY = SourceNode - > NodePosY ;
TemplateConversionNode - > NodePosX = SourceNode - > NodePosX + 4 ;
2020-08-27 18:38:23 -04:00
}
else
{
CompilerContext . MessageLog . Error ( * FText : : Format ( LOCTEXT ( " NoValidPromotion " , " Cannot find appropriate promotion from '{0}' to '{1}' on '@@' " ) ,
Schema - > TypeToText ( InputPin - > PinType ) ,
Schema - > TypeToText ( OutputPin - > PinType ) ) . ToString ( ) ,
2020-10-12 17:44:44 -04:00
SourceNode
2020-08-27 18:38:23 -04:00
) ;
}
return bInputSuccessful & & bOutputSuccessful ;
}
void UK2Node_PromotableOperator : : ResetNodeToWildcard ( )
{
RecombineAllSplitPins ( ) ;
// Reset type to wildcard
const FEdGraphPinType & WildType = FWildcardNodeUtils : : GetDefaultWildcardPinType ( ) ;
2020-10-26 12:56:25 -04:00
const UEdGraphSchema * Schema = GetSchema ( ) ;
2020-08-27 18:38:23 -04:00
for ( UEdGraphPin * Pin : Pins )
{
// Ensure this pin is not a split pin
if ( Pin & & Pin - > ParentPin = = nullptr )
{
Pin - > PinType = WildType ;
2020-10-26 12:56:25 -04:00
Schema - > ResetPinToAutogeneratedDefaultValue ( Pin ) ;
2020-08-27 18:38:23 -04:00
}
}
2020-12-05 12:00:19 -04:00
const UFunction * Func = GetTargetFunction ( ) ;
2020-10-19 22:02:12 -04:00
2020-12-05 12:00:19 -04:00
if ( Func & & FTypePromotion : : IsComparisonFunc ( Func ) )
{
// Set output pins to have a bool output flag by default
if ( UEdGraphPin * OutPin = GetOutputPin ( ) )
2020-10-19 22:02:12 -04:00
{
OutPin - > PinType . PinCategory = UEdGraphSchema_K2 : : PC_Boolean ;
}
2020-12-05 12:00:19 -04:00
// If we have a tolerance pin, and we reset to wildcard then we should hide it
if ( UEdGraphPin * TolerancePin = FindTolerancePin ( ) )
{
TolerancePin - > bHidden = true ;
}
2020-10-19 22:02:12 -04:00
}
2020-10-25 18:06:25 -04:00
2023-03-10 20:03:26 -05:00
GetGraph ( ) - > NotifyNodeChanged ( this ) ;
2020-08-27 18:38:23 -04:00
}
void UK2Node_PromotableOperator : : RecombineAllSplitPins ( )
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
// Gather what pins need to be recombined from a split pin
2020-10-25 18:06:25 -04:00
for ( int32 Index = 0 ; Index < Pins . Num ( ) ; + + Index )
2020-08-27 18:38:23 -04:00
{
if ( Pins [ Index ] & & Pins [ Index ] - > SubPins . Num ( ) > 0 )
{
K2Schema - > RecombinePin ( Pins [ Index ] ) ;
}
}
}
2020-11-04 20:26:25 -04:00
void UK2Node_PromotableOperator : : UpdateFromBestMatchingFunction ( )
{
// Gather all pins and their links so we can determine the highest type that the user could want
TArray < UEdGraphPin * > PinsToConsider ;
2020-11-19 12:30:03 -04:00
GetPinsToConsider ( PinsToConsider ) ;
2020-11-04 20:26:25 -04:00
// We need to update the pins from our function if have a new connection
2020-12-05 12:00:19 -04:00
if ( const UFunction * BestMatchingFunc = FTypePromotion : : FindBestMatchingFunc ( OperationName , PinsToConsider ) )
{
UpdatePinsFromFunction ( BestMatchingFunc ) ;
}
2020-11-04 20:26:25 -04:00
}
2020-08-27 18:38:23 -04:00
TArray < UEdGraphPin * > UK2Node_PromotableOperator : : GetInputPins ( bool bIncludeLinks /** = false */ ) const
{
TArray < UEdGraphPin * > InputPins ;
for ( UEdGraphPin * Pin : Pins )
{
// Exclude split pins from this
if ( Pin & & Pin - > Direction = = EEdGraphPinDirection : : EGPD_Input & & Pin - > ParentPin = = nullptr )
{
InputPins . Add ( Pin ) ;
if ( bIncludeLinks )
{
for ( UEdGraphPin * Link : Pin - > LinkedTo )
{
InputPins . Emplace ( Link ) ;
}
}
}
}
return InputPins ;
}
2020-12-03 11:44:58 -04:00
void UK2Node_PromotableOperator : : GetPinsToConsider ( TArray < UEdGraphPin * > & OutArray ) const
2020-11-19 12:30:03 -04:00
{
2020-12-05 12:00:19 -04:00
const bool bIsComparisonOp = FTypePromotion : : IsComparisonOpName ( OperationName ) ;
2020-11-19 12:30:03 -04:00
for ( UEdGraphPin * Pin : Pins )
{
2023-01-27 11:23:35 -05:00
if ( ensure ( Pin ) )
2020-11-19 12:30:03 -04:00
{
2023-01-27 11:23:35 -05:00
// Tolerance pins don't factor into what types we should be matching
// for the function, so we should not consider them
if ( IsTolerancePin ( * Pin ) )
2020-12-02 17:56:14 -04:00
{
2023-01-27 11:23:35 -05:00
continue ;
2020-12-02 17:56:14 -04:00
}
2023-01-27 11:23:35 -05:00
// If this is a comparison operator then we don't need to consider the boolean output
// pin because every comparison function will have a boolean output!
if ( bIsComparisonOp & & Pin - > Direction = = EGPD_Output )
2020-11-19 12:30:03 -04:00
{
2023-01-27 11:23:35 -05:00
continue ;
2020-11-19 12:30:03 -04:00
}
2023-01-27 11:23:35 -05:00
// If this pin has links, then use those instead of the actual pin because we could be process of changing it
// which means that it would still have it's old pin type, and could be inaccurate
if ( Pin - > LinkedTo . Num ( ) > 0 )
2020-12-02 17:56:14 -04:00
{
2023-01-27 11:23:35 -05:00
// If this is from a split pin, then we care about the parent type
if ( Pin - > ParentPin ! = nullptr )
{
OutArray . Add ( Pin - > ParentPin ) ;
}
// as well as all the links to it
for ( UEdGraphPin * Link : Pin - > LinkedTo )
{
OutArray . Emplace ( Link ) ;
}
2020-12-02 17:56:14 -04:00
}
2023-01-27 11:23:35 -05:00
else if ( ! FWildcardNodeUtils : : IsWildcardPin ( Pin ) )
2020-12-02 17:56:14 -04:00
{
2023-01-27 11:23:35 -05:00
// If this is from a split pin, then we care about the type of the parent, not this pin
if ( Pin - > ParentPin ! = nullptr )
{
OutArray . Add ( Pin - > ParentPin ) ;
}
else
{
OutArray . Add ( Pin ) ;
}
2020-12-02 17:56:14 -04:00
}
2020-11-19 12:30:03 -04:00
}
}
}
2021-02-22 15:43:31 -04:00
void UK2Node_PromotableOperator : : UpdatePinsFromFunction ( const UFunction * Function , UEdGraphPin * ChangedPin /* = nullptr */ , bool bIsFromConversion /*= false*/ )
2020-08-27 18:38:23 -04:00
{
if ( ! Function )
{
return ;
}
const UEdGraphSchema_K2 * Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
2020-11-02 14:54:18 -04:00
// Gather the pin types of the properties on the function we want to convert to
FEdGraphPinType FunctionReturnType ;
FEdGraphPinType HighestFuncInputType ;
2020-12-05 12:00:19 -04:00
FEdGraphPinType ToleranceType ;
2020-11-02 14:54:18 -04:00
TArray < FEdGraphPinType > FunctionInputTypes ;
2020-08-27 18:38:23 -04:00
{
2020-11-02 14:54:18 -04:00
for ( TFieldIterator < FProperty > PropIt ( Function ) ; PropIt & & ( PropIt - > PropertyFlags & CPF_Parm ) ; + + PropIt )
2020-08-27 18:38:23 -04:00
{
2020-11-02 14:54:18 -04:00
FProperty * Param = * PropIt ;
FEdGraphPinType ParamType ;
2020-08-27 18:38:23 -04:00
2020-11-02 14:54:18 -04:00
if ( Schema - > ConvertPropertyToPinType ( Param , /* out */ ParamType ) )
2020-08-27 18:38:23 -04:00
{
2020-11-02 14:54:18 -04:00
if ( Param - > HasAnyPropertyFlags ( CPF_ReturnParm ) )
2020-08-27 18:38:23 -04:00
{
2020-11-02 14:54:18 -04:00
FunctionReturnType = ParamType ;
}
else
{
// Track the highest input pin type that we have
if ( FTypePromotion : : GetHigherType ( HighestFuncInputType , ParamType ) = = FTypePromotion : : ETypeComparisonResult : : TypeBHigher )
2020-08-27 18:38:23 -04:00
{
2020-11-02 14:54:18 -04:00
HighestFuncInputType = ParamType ;
2020-08-27 18:38:23 -04:00
}
2020-11-02 14:54:18 -04:00
FunctionInputTypes . Add ( ParamType ) ;
2020-08-27 18:38:23 -04:00
}
}
2020-11-02 14:54:18 -04:00
}
}
2020-08-27 18:38:23 -04:00
2021-02-22 15:43:31 -04:00
auto ConformPinLambda = [ & ChangedPin , bIsFromConversion ] ( const FEdGraphPinType & FunctionPinType , UEdGraphPin * NodePin )
2020-11-02 14:54:18 -04:00
{
2022-10-31 17:18:29 -04:00
using namespace UE : : Kismet : : BlueprintTypeConversions ;
2020-11-02 14:54:18 -04:00
// If the pin types are already equal, then we don't have to do any work
// If this is linked to wildcard pins, then we can just ignore it and handle it on expansion
2020-11-09 12:10:59 -04:00
if ( ! NodePin | | FWildcardNodeUtils : : IsLinkedToWildcard ( NodePin ) )
2020-11-02 14:54:18 -04:00
{
return ;
2020-08-27 18:38:23 -04:00
}
2021-02-22 15:43:31 -04:00
// Pins that are underdoing a conversion will have already had their types changed
if ( NodePin = = ChangedPin & & bIsFromConversion )
{
return ;
}
2020-11-02 14:54:18 -04:00
// By default, conform to the type of the function param
FEdGraphPinType ConformingType = FunctionPinType ;
const FEdGraphPinType HighestLinkedType = NodePin - > LinkedTo . Num ( ) > 0 ? FTypePromotion : : GetPromotedType ( NodePin - > LinkedTo ) : NodePin - > PinType ;
2022-10-31 17:18:29 -04:00
const UScriptStruct * NodePinStruct = Cast < UScriptStruct > ( HighestLinkedType . PinSubCategoryObject ) ;
const UScriptStruct * FunctionPinStruct = Cast < UScriptStruct > ( FunctionPinType . PinSubCategoryObject ) ;
const bool bHasImplicitConversionFunction = FStructConversionTable : : Get ( ) . GetConversionFunction ( NodePinStruct , FunctionPinStruct ) . IsSet ( ) ;
const bool bDifferingStructs =
2020-12-08 16:35:52 -04:00
HighestLinkedType . PinCategory = = UEdGraphSchema_K2 : : PC_Struct & &
2022-10-31 17:18:29 -04:00
FunctionPinType . PinCategory = = UEdGraphSchema_K2 : : PC_Struct & &
HighestLinkedType . PinSubCategoryObject ! = FunctionPinType . PinSubCategoryObject & &
! bHasImplicitConversionFunction ;
2020-11-02 14:54:18 -04:00
2021-02-01 19:47:28 -04:00
const bool bHasDeterminingType = NodePin - > LinkedTo . Num ( ) > 0 | | ! NodePin - > DoesDefaultValueMatchAutogenerated ( ) ;
2020-11-02 14:54:18 -04:00
// If the highest type is the same as the function type, just continue on with life
2021-02-01 19:47:28 -04:00
if ( bHasDeterminingType & & ( HighestLinkedType . PinCategory ! = FunctionPinType . PinCategory | | bDifferingStructs ) )
2020-11-02 14:54:18 -04:00
{
2020-12-10 13:39:52 -04:00
NodePin - > PinType = FunctionPinType ;
const bool bValidPromo =
FTypePromotion : : IsValidPromotion ( HighestLinkedType , FunctionPinType ) | |
2021-02-08 17:05:40 -04:00
( NodePin - > LinkedTo . Num ( ) > 0 & & FTypePromotion : : HasStructConversion ( NodePin , NodePin - > LinkedTo [ 0 ] ) ) ;
2020-12-10 13:39:52 -04:00
2020-11-02 14:54:18 -04:00
// If the links cannot be promoted to the function type, then we need to break them
2020-11-09 12:10:59 -04:00
// We don't want to break the pin if it is the one that the user has dragged on to though,
// because that would result in the node breaking connection as soon as the user lets go
2020-12-10 13:39:52 -04:00
if ( ( NodePin ! = ChangedPin ) & & ! FWildcardNodeUtils : : IsWildcardPin ( NodePin ) & & ! bValidPromo )
2020-11-02 14:54:18 -04:00
{
NodePin - > BreakAllPinLinks ( ) ;
}
else
{
ConformingType = HighestLinkedType ;
}
}
// Conform the pin type appropriately
NodePin - > PinType = ConformingType ;
} ;
2020-12-05 12:00:19 -04:00
// Check if we need to add a tolerance pin or not with this new function
2021-02-23 17:51:47 -04:00
if ( FTypePromotion : : IsComparisonFunc ( Function ) )
2020-12-05 12:00:19 -04:00
{
2021-02-23 17:51:47 -04:00
UEdGraphPin * ExistingTolPin = FindTolerancePin ( ) ;
const bool bHasTolerancePin = PromotableOpUtils : : FindTolerancePinType ( Function , ToleranceType ) ;
if ( ! ExistingTolPin )
{
ExistingTolPin = CreatePin ( EGPD_Input , ToleranceType , TolerancePin_Name ) ;
}
2020-12-05 12:00:19 -04:00
// Set the tolerance pin to visible if it is currently on the node
2021-02-23 17:51:47 -04:00
ExistingTolPin - > bHidden = ! bHasTolerancePin ;
2020-12-05 12:00:19 -04:00
}
2020-11-02 14:54:18 -04:00
int32 CurPinIndex = 0 ;
for ( UEdGraphPin * CurPin : Pins )
{
2023-01-27 11:23:35 -05:00
if ( ensure ( CurPin ) )
2020-11-02 14:54:18 -04:00
{
2023-01-27 11:23:35 -05:00
// We don't want to try and conform split pin, because we will already have conformed the parent pin
if ( CurPin - > ParentPin ! = nullptr )
{
continue ;
}
2020-11-02 14:54:18 -04:00
2023-01-27 11:23:35 -05:00
if ( IsAdditionalPin ( * CurPin ) )
{
// Conform to the highest input pin on the function
ConformPinLambda ( HighestFuncInputType , CurPin ) ;
}
else if ( CurPin - > Direction = = EGPD_Output )
{
// Match to the output pin
ConformPinLambda ( FunctionReturnType , CurPin ) ;
}
// Creation and conformation of the tolerance pin is handled before all others to
// ensure that connections are not broken accidentally
else if ( IsTolerancePin ( * CurPin ) )
{
ConformPinLambda ( ToleranceType , CurPin ) ;
}
else
{
// Match to the appropriate function input type
ConformPinLambda ( FunctionInputTypes [ CurPinIndex ] , CurPin ) ;
+ + CurPinIndex ;
}
2020-11-02 14:54:18 -04:00
}
2020-08-27 18:38:23 -04:00
}
// Update the function reference and the FUNC_BlueprintPure/FUNC_Const appropriately
SetFromFunction ( Function ) ;
2020-11-02 14:54:18 -04:00
2020-11-03 15:52:04 -04:00
// Invalidate the tooltips
CachedTooltip . MarkDirty ( ) ;
2020-11-02 14:54:18 -04:00
// We need to notify the graph that the node has changed to get
// the correct default value text boxes on the node
2023-03-10 20:03:26 -05:00
GetGraph ( ) - > NotifyNodeChanged ( this ) ;
2020-08-27 18:38:23 -04:00
}
UEdGraphPin * UK2Node_PromotableOperator : : GetOutputPin ( ) const
{
for ( UEdGraphPin * Pin : Pins )
{
if ( Pin & & Pin - > Direction = = EGPD_Output )
{
return Pin ;
}
}
return nullptr ;
}
2021-02-01 19:47:28 -04:00
void UK2Node_PromotableOperator : : ConvertPinType ( UEdGraphPin * PinToChange , const FEdGraphPinType NewPinType )
{
// No work to be done here!
if ( ! PinToChange | | PinToChange - > PinType = = NewPinType )
{
return ;
}
FScopedTransaction Transaction ( LOCTEXT ( " PromotableOperatorPinConvert " , " Convert pin type " ) ) ;
Modify ( ) ;
if ( NewPinType . PinCategory = = UEdGraphSchema_K2 : : PC_Wildcard )
{
2021-02-05 15:06:50 -04:00
const bool bIsComparison = FTypePromotion : : IsComparisonFunc ( GetTargetFunction ( ) ) ;
// Break all input connections, but only break an output if it is not a comparison
// because the bool pin type will not change.
for ( UEdGraphPin * Pin : Pins )
{
if ( Pin - > Direction = = EGPD_Input | | ! bIsComparison )
{
Pin - > BreakAllPinLinks ( ) ;
}
}
2021-02-01 19:47:28 -04:00
ResetNodeToWildcard ( ) ;
return ;
}
// Break any pin links to this node because the type will be different
PinToChange - > BreakAllPinLinks ( ) ;
2021-02-22 15:43:31 -04:00
// Recombine any split pins that this type has before changing its type
const UEdGraphSchema_K2 * Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
if ( PinToChange - > SubPins . Num ( ) > 0 )
{
Schema - > RecombinePin ( PinToChange ) ;
}
2021-02-01 19:47:28 -04:00
PinToChange - > PinType = NewPinType ;
EvaluatePinsFromChange ( PinToChange , true ) ;
InvalidatePinTooltips ( ) ;
2021-02-22 15:43:31 -04:00
// Reset the default value on pins that have been converted
Schema - > ResetPinToAutogeneratedDefaultValue ( PinToChange , false ) ;
2021-02-01 19:47:28 -04:00
}
2020-08-27 18:38:23 -04:00
# undef LOCTEXT_NAMESPACE // "PromotableOperatorNode"