2020-08-27 18:38:23 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "K2Node_PromotableOperator.h"
# include "BlueprintTypePromotion.h"
# include "EdGraphSchema_K2.h"
# include "EdGraphUtilities.h"
# include "K2Node_CommutativeAssociativeBinaryOperator.h"
# include "KismetCompiler.h"
# include "ScopedTransaction.h"
# include "ToolMenus.h"
# include "Framework/Commands/UIAction.h"
# include "Kismet2/BlueprintEditorUtils.h"
# include "Kismet2/WildcardNodeUtils.h"
2020-10-12 17:44:44 -04:00
# include "Kismet2/CompilerResultsLog.h"
2020-08-27 18:38:23 -04:00
# define LOCTEXT_NAMESPACE "PromotableOperatorNode"
///////////////////////////////////////////////////////////
// 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 " ) ;
// 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
{
2021-02-22 15:43:31 -04:00
// Give the user an option to reset this node to wildcard
if ( ! FWildcardNodeUtils : : IsWildcardPin ( Context - > Pin ) )
{
static const FName ResetWildcardName = FName ( " PromotableOperatorResetWildcardPinConvs " ) ;
static const FText ResetWildcardStr = LOCTEXT ( " PromotableOperatorResetWildcardPinConvs " , " Reset To Wildcard " ) ;
FToolMenuSection & Section = Menu - > AddSection ( ResetWildcardName , ResetWildcardStr ) ;
const FText ResetName = LOCTEXT ( " ResetFunction_Tooltip " , " Reset this node to wildcard " ) ;
Section . AddMenuEntry (
FName ( ResetName . ToString ( ) ) ,
ResetName ,
LOCTEXT ( " ResetToWildcardTooltip " , " Reset this node to a wildcard state. " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateUObject ( const_cast < UK2Node_PromotableOperator * > ( this ) , & UK2Node_PromotableOperator : : ConvertPinType , const_cast < UEdGraphPin * > ( Context - > Pin ) , FWildcardNodeUtils : : GetDefaultWildcardPinType ( ) )
)
) ;
}
2021-02-01 19:47:28 -04:00
FToolMenuSection & Section = Menu - > AddSection ( " K2NodePromoOpConversionGraphNode " ) ;
Section . AddSubMenu (
" ConvertPin " ,
LOCTEXT ( " ConvertPin " , " Convert Pin... " ) ,
LOCTEXT ( " ConvertPinTooltip " , " Convert the selected pin to another type " ) ,
FNewToolMenuDelegate : : CreateUObject ( this , & UK2Node_PromotableOperator : : CreateConversionSubMenu , ( UEdGraphPin * ) Context - > Pin )
) ;
}
}
void UK2Node_PromotableOperator : : CreateConversionSubMenu ( UToolMenu * Menu , UEdGraphPin * ContextPin ) const
{
check ( ContextPin ) ;
static const FName ConvNodeName = FName ( " PromotableOperatorPinConvs " ) ;
static const FText ConvNodeStr = LOCTEXT ( " PromotableOperatorPinConvs " , " Pin Conversions " ) ;
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.
if ( ContextPin - > ParentPin ! = nullptr )
{
ContextPin = ContextPin - > 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
FEdGraphPinType OriginalContextType = ContextPin - > PinType ;
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
{
2021-02-22 15:43:31 -04:00
if ( FWildcardNodeUtils : : IsWildcardPin ( ContextPin ) | | FTypePromotion : : IsValidPromotion ( ParamType , ContextPin - > PinType ) | | FTypePromotion : : IsValidPromotion ( ContextPin - > 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 )
{
FToolMenuSection & Section = Menu - > AddSection ( ConvNodeName , ConvNodeStr ) ;
2021-02-01 19:47:28 -04:00
2021-02-22 15:43:31 -04:00
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " PinType " ) , Schema - > TypeToText ( PinType ) ) ;
2021-02-01 19:47:28 -04:00
2021-02-22 15:43:31 -04:00
const FText PinConversionName = FText : : Format ( LOCTEXT ( " CallFunction_Tooltip " , " {PinType} " ) , Args ) ;
2021-02-01 19:47:28 -04:00
2021-02-22 15:43:31 -04:00
Section . AddMenuEntry (
FName ( PinConversionName . ToString ( ) ) ,
PinConversionName ,
LOCTEXT ( " ConvertPinTypeTooltip " , " Convert this pin type to the selected type " ) ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateUObject ( const_cast < UK2Node_PromotableOperator * > ( this ) , & UK2Node_PromotableOperator : : ConvertPinType , const_cast < UEdGraphPin * > ( ContextPin ) , PinType )
)
) ;
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 ( ) ) ) & &
! IsTolerancePin ( Pin ) ;
2021-02-04 15:10:41 -04:00
}
2020-08-27 18:38:23 -04:00
FText UK2Node_PromotableOperator : : GetTooltipText ( ) const
{
// If there are no connections then just display the op name
if ( ! HasAnyConnectionsOrDefaults ( ) )
{
2020-12-05 12:00:19 -04:00
return FTypePromotion : : GetUserFacingOperatorName ( OperationName ) ;
2020-08-27 18:38:23 -04:00
}
// Otherwise use the default one (a more specific function tooltip)
return Super : : GetTooltipText ( ) ;
}
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
GetGraph ( ) - > NotifyGraphChanged ( ) ;
}
}
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 )
{
if ( IsAdditionalPin ( AddPin ) & & AddPin - > LinkedTo . Num ( ) > 0 )
{
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 )
{
if ( IsAdditionalPin ( OldPin ) )
{
if ( UEdGraphPin * AddPin = GetAdditionalPin ( AdditionalPinsFixed + NumFunctionInputs ) )
{
AddPin - > PinType = OldPin - > PinType ;
AddPin - > DefaultValue = OldPin - > DefaultValue ;
+ + AdditionalPinsFixed ;
2020-10-25 18:06:25 -04:00
}
2020-10-12 17:44:44 -04:00
}
2021-02-01 19:47:28 -04: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 ;
}
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
2020-11-19 12:30:03 -04:00
bool UK2Node_PromotableOperator : : IsTolerancePin ( const UEdGraphPin * Pin ) const
{
return Pin & & Pin - > PinName = = TolerancePin_Name & & Pin - > Direction = = EGPD_Input ;
}
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 ;
}
bool UK2Node_PromotableOperator : : IsAdditionalPin ( const UEdGraphPin * Pin ) const
{
// Quickly check if this input pin is one of the two default input pins
2020-12-05 12:00:19 -04:00
return Pin & & Pin - > Direction = = EGPD_Input & & Pin - > PinName ! = InputPinA_Name & & Pin - > PinName ! = InputPinB_Name & & ! 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
else if ( IsTolerancePin ( ChangedPin ) )
{
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
// If the pin types are the same, than no casts are needed and we can just connect
if ( InputPin - > PinType = = OutputPin - > PinType )
{
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 ;
FName TargetFunctionName ;
UClass * ClassContainingConversionFunction = nullptr ;
TSubclassOf < UK2Node > ConversionNodeClass ;
if ( Schema - > SearchForAutocastFunction ( InputPin - > PinType , OutputPin - > PinType , /*out*/ TargetFunctionName , /*out*/ ClassContainingConversionFunction ) )
{
// Create a new call function node for the casting operator
UK2Node_CallFunction * TemplateNode = SourceGraph - > CreateIntermediateNode < UK2Node_CallFunction > ( ) ;
TemplateNode - > FunctionReference . SetExternalMember ( TargetFunctionName , ClassContainingConversionFunction ) ;
TemplateConversionNode = TemplateNode ;
TemplateNode - > AllocateDefaultPins ( ) ;
CompilerContext . MessageLog . NotifyIntermediateObjectCreation ( TemplateNode , this ) ;
}
else
{
Schema - > FindSpecializedConversionNode ( InputPin , OutputPin , true , /*out*/ TemplateConversionNode ) ;
}
bool bInputSuccessful = false ;
bool bOutputSuccessful = false ;
if ( TemplateConversionNode )
{
UEdGraphPin * ConversionInput = nullptr ;
for ( UEdGraphPin * ConvPin : TemplateConversionNode - > Pins )
{
if ( ConvPin & & ConvPin - > Direction = = EGPD_Input & & ConvPin - > PinName ! = UEdGraphSchema_K2 : : PSC_Self )
{
ConversionInput = ConvPin ;
break ;
}
}
UEdGraphPin * ConversionOutput = TemplateConversionNode - > FindPin ( UEdGraphSchema_K2 : : PN_ReturnValue , EGPD_Output ) ;
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
2020-10-26 12:56:25 -04:00
GetGraph ( ) - > NotifyGraphChanged ( ) ;
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 )
{
// Tolerance pins don't factor into what types we should be matching
// for the function, so we should not consider them
if ( IsTolerancePin ( Pin ) )
{
continue ;
}
2020-12-05 12:00:19 -04: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 )
{
continue ;
}
2020-11-19 12:30:03 -04: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
// 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
2020-11-19 12:30:03 -04:00
for ( UEdGraphPin * Link : Pin - > LinkedTo )
{
OutArray . Emplace ( Link ) ;
}
}
2021-02-25 14:04:06 -04:00
else if ( ! FWildcardNodeUtils : : IsWildcardPin ( Pin ) )
2020-11-19 12:30:03 -04:00
{
2020-12-02 17:56:14 -04: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-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
{
// 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 ;
2020-12-08 16:35:52 -04:00
const bool bDifferingStructs =
HighestLinkedType . PinCategory = = UEdGraphSchema_K2 : : PC_Struct & &
FunctionPinType . PinCategory = = UEdGraphSchema_K2 : : PC_Struct & &
HighestLinkedType . PinSubCategoryObject ! = FunctionPinType . PinSubCategoryObject ;
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 )
{
// We don't want to try and conform split pin, because we will already have conformed the parent pin
if ( CurPin - > ParentPin ! = nullptr )
{
continue ;
}
if ( IsAdditionalPin ( CurPin ) )
{
// Conform to the highest input pin on the function
2020-12-05 12:00:19 -04:00
ConformPinLambda ( HighestFuncInputType , CurPin ) ;
2020-11-02 14:54:18 -04:00
}
else if ( CurPin - > Direction = = EGPD_Output )
{
// Match to the output pin
2020-12-05 12:00:19 -04:00
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 ) ;
2020-11-02 14:54:18 -04:00
}
else
{
// Match to the appropriate function input type
2020-12-05 12:00:19 -04:00
ConformPinLambda ( FunctionInputTypes [ CurPinIndex ] , CurPin ) ;
2020-11-02 14:54:18 -04:00
+ + CurPinIndex ;
}
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
GetGraph ( ) - > NotifyGraphChanged ( ) ;
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"