2019-12-26 15:33:43 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
2016-11-23 15:48:37 -05:00
# include "K2Node_Switch.h"
2022-08-24 22:45:13 -04:00
2016-11-23 15:48:37 -05:00
# include "BPTerminal.h"
2022-08-24 22:45:13 -04:00
# include "BlueprintCompiledStatement.h"
# include "Containers/Array.h"
# include "Containers/EnumAsByte.h"
# include "Containers/IndirectArray.h"
# include "Containers/Map.h"
# include "EdGraph/EdGraphNode.h"
# include "EdGraph/EdGraphNodeUtils.h"
# include "EdGraphSchema_K2.h"
# include "EdGraphUtilities.h"
2014-07-14 13:29:38 -04:00
# include "EditorCategoryUtils.h"
2022-08-24 22:45:13 -04:00
# include "Engine/Blueprint.h"
# include "HAL/PlatformCrt.h"
# include "Internationalization/Internationalization.h"
# include "Kismet2/CompilerResultsLog.h"
# include "KismetCompiledFunctionContext.h"
# include "KismetCompiler.h"
# include "KismetCompilerMisc.h"
2022-05-09 13:29:31 -04:00
# include "Styling/AppStyle.h"
2022-08-24 22:45:13 -04:00
# include "Templates/Casts.h"
# include "Templates/ChooseClass.h"
# include "UObject/Class.h"
# include "UObject/Object.h"
# include "UObject/ObjectPtr.h"
# include "UObject/Script.h"
# include "UObject/UnrealType.h"
# include "UObject/WeakObjectPtrTemplates.h"
2014-03-14 14:13:41 -04:00
# define LOCTEXT_NAMESPACE "K2Node_Switch"
namespace
{
2017-10-25 09:30:36 -04:00
static FName DefaultPinName ( TEXT ( " Default " ) ) ;
static FName SelectionPinName ( TEXT ( " Selection " ) ) ;
2014-03-14 14:13:41 -04:00
}
//////////////////////////////////////////////////////////////////////////
// FKCHandler_Switch
class FKCHandler_Switch : public FNodeHandlingFunctor
{
protected :
TMap < UEdGraphNode * , FBPTerminal * > BoolTermMap ;
public :
2014-09-16 14:26:32 -04:00
FKCHandler_Switch ( FKismetCompilerContext & InCompilerContext )
2014-03-14 14:13:41 -04:00
: FNodeHandlingFunctor ( InCompilerContext )
{
}
2014-06-13 06:14:46 -04:00
virtual void RegisterNets ( FKismetFunctionContext & Context , UEdGraphNode * Node ) override
2014-03-14 14:13:41 -04:00
{
UK2Node_Switch * SwitchNode = Cast < UK2Node_Switch > ( Node ) ;
FNodeHandlingFunctor : : RegisterNets ( Context , Node ) ;
// Create a term to determine if the compare was successful or not
//@TODO: Ideally we just create one ever, not one per switch
2014-09-26 11:32:41 -04:00
FBPTerminal * BoolTerm = Context . CreateLocalTerminal ( ) ;
2017-10-25 09:30:36 -04:00
BoolTerm - > Type . PinCategory = UEdGraphSchema_K2 : : PC_Boolean ;
2014-03-14 14:13:41 -04:00
BoolTerm - > Source = Node ;
2019-02-18 17:37:38 -05:00
BoolTerm - > Name = Context . NetNameMap - > MakeValidName ( Node , TEXT ( " CmpSuccess " ) ) ;
2014-03-14 14:13:41 -04:00
BoolTermMap . Add ( Node , BoolTerm ) ;
}
2014-06-13 06:14:46 -04:00
virtual void Compile ( FKismetFunctionContext & Context , UEdGraphNode * Node ) override
2014-03-14 14:13:41 -04:00
{
UK2Node_Switch * SwitchNode = CastChecked < UK2Node_Switch > ( Node ) ;
2014-09-16 14:26:32 -04:00
FEdGraphPinType ExpectedExecPinType ;
ExpectedExecPinType . PinCategory = UEdGraphSchema_K2 : : PC_Exec ;
2014-03-14 14:13:41 -04:00
// Make sure that the input pin is connected and valid for this block
2014-09-16 14:26:32 -04:00
UEdGraphPin * ExecTriggeringPin = Context . FindRequiredPinByName ( SwitchNode , UEdGraphSchema_K2 : : PN_Execute , EGPD_Input ) ;
if ( ( ExecTriggeringPin = = NULL ) | | ! Context . ValidatePinType ( ExecTriggeringPin , ExpectedExecPinType ) )
2014-03-14 14:13:41 -04:00
{
2017-12-12 18:32:45 -05:00
CompilerContext . MessageLog . Error ( * LOCTEXT ( " NoValidExecutionPinForSwitch_Error " , " @@ must have a valid execution pin @@ " ) . ToString ( ) , SwitchNode , ExecTriggeringPin ) ;
2014-03-14 14:13:41 -04:00
return ;
}
// Make sure that the selection pin is connected and valid for this block
UEdGraphPin * SelectionPin = SwitchNode - > GetSelectionPin ( ) ;
2014-09-16 14:26:32 -04:00
if ( ( SelectionPin = = NULL ) | | ! Context . ValidatePinType ( SelectionPin , SwitchNode - > GetPinType ( ) ) )
2014-03-14 14:13:41 -04:00
{
2017-12-12 18:32:45 -05:00
CompilerContext . MessageLog . Error ( * LOCTEXT ( " NoValidSelectionPinForSwitch_Error " , " @@ must have a valid execution pin @@ " ) . ToString ( ) , SwitchNode , SelectionPin ) ;
2014-03-14 14:13:41 -04:00
return ;
}
// Find the boolean intermediate result term, so we can track whether the compare was successful
FBPTerminal * BoolTerm = BoolTermMap . FindRef ( SwitchNode ) ;
// Generate the output impulse from this node
UEdGraphPin * SwitchSelectionNet = FEdGraphUtilities : : GetNetFromPin ( SelectionPin ) ;
FBPTerminal * SwitchSelectionTerm = Context . NetMap . FindRef ( SwitchSelectionNet ) ;
if ( ( BoolTerm ! = NULL ) & & ( SwitchSelectionTerm ! = NULL ) )
{
UEdGraphPin * FuncPin = SwitchNode - > GetFunctionPin ( ) ;
FBPTerminal * FuncContext = Context . NetMap . FindRef ( FuncPin ) ;
UEdGraphPin * DefaultPin = SwitchNode - > GetDefaultPin ( ) ;
2018-08-24 10:59:28 -04:00
// We don't need to generate if checks if there are no connections to it if there is no default pin or if the default pin is not linked
// If there is a default pin that is linked then it would fall through to that default if we do not generate the cases
const bool bCanSkipUnlinkedCase = ( DefaultPin = = nullptr | | DefaultPin - > LinkedTo . Num ( ) = = 0 ) ;
2014-03-14 14:13:41 -04:00
// Pull out function to use
UClass * FuncClass = Cast < UClass > ( FuncPin - > PinType . PinSubCategoryObject . Get ( ) ) ;
2020-03-15 10:33:45 -04:00
UFunction * FunctionPtr = FindUField < UFunction > ( FuncClass , FuncPin - > PinName ) ;
2014-03-14 14:13:41 -04:00
check ( FunctionPtr ) ;
// Run thru all the output pins except for the default label
for ( auto PinIt = SwitchNode - > Pins . CreateIterator ( ) ; PinIt ; + + PinIt )
{
UEdGraphPin * Pin = * PinIt ;
2018-08-24 10:59:28 -04:00
if ( ( Pin - > Direction = = EGPD_Output ) & & ( Pin ! = DefaultPin ) & & ( ! bCanSkipUnlinkedCase | | Pin - > LinkedTo . Num ( ) > 0 ) )
2014-03-14 14:13:41 -04:00
{
// Create a term for the switch case value
2018-12-03 10:52:48 -05:00
FBPTerminal * CaseValueTerm = new FBPTerminal ( ) ;
Context . Literals . Add ( CaseValueTerm ) ;
2021-04-29 19:32:06 -04:00
CaseValueTerm - > Name = SwitchNode - > GetExportTextForPin ( Pin ) ;
2016-10-27 20:37:57 -04:00
CaseValueTerm - > Type = SwitchNode - > GetInnerCaseType ( ) ;
2016-06-23 19:35:24 -04:00
CaseValueTerm - > SourcePin = Pin ;
2014-03-14 14:13:41 -04:00
CaseValueTerm - > bIsLiteral = true ;
// Call the comparison function associated with this switch node
FBlueprintCompiledStatement & Statement = Context . AppendStatementForNode ( SwitchNode ) ;
Statement . Type = KCST_CallFunction ;
Statement . FunctionToCall = FunctionPtr ;
Statement . FunctionContext = FuncContext ;
Statement . bIsParentContext = false ;
Statement . LHS = BoolTerm ;
Statement . RHS . Add ( SwitchSelectionTerm ) ;
Statement . RHS . Add ( CaseValueTerm ) ;
// Jump to output if strings are actually equal
FBlueprintCompiledStatement & IfFailTest_SucceedAtBeingEqualGoto = Context . AppendStatementForNode ( SwitchNode ) ;
IfFailTest_SucceedAtBeingEqualGoto . Type = KCST_GotoIfNot ;
IfFailTest_SucceedAtBeingEqualGoto . LHS = BoolTerm ;
2014-04-23 17:45:37 -04:00
Context . GotoFixupRequestMap . Add ( & IfFailTest_SucceedAtBeingEqualGoto , Pin ) ;
2014-03-14 14:13:41 -04:00
}
}
// Finally output default pin
GenerateSimpleThenGoto ( Context , * SwitchNode , DefaultPin ) ;
}
else
{
CompilerContext . MessageLog . Error ( * LOCTEXT ( " ResolveTermPassed_Error " , " Failed to resolve term passed into @@ " ) . ToString ( ) , SelectionPin ) ;
}
}
private :
2014-09-16 14:26:32 -04:00
FEdGraphPinType ExpectedSelectionPinType ;
2014-03-14 14:13:41 -04:00
} ;
2014-10-14 10:29:11 -04:00
UK2Node_Switch : : UK2Node_Switch ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
2014-03-14 14:13:41 -04:00
{
bHasDefaultPin = true ;
bHasDefaultPinValueChanged = false ;
}
void UK2Node_Switch : : PostEditChangeProperty ( struct FPropertyChangedEvent & PropertyChangedEvent )
{
FName PropertyName = ( PropertyChangedEvent . Property ! = NULL ) ? PropertyChangedEvent . Property - > GetFName ( ) : NAME_None ;
if ( PropertyName = = TEXT ( " bHasDefaultPin " ) )
{
2014-09-16 10:25:25 -04:00
// Signal to the reconstruction logic that the default pin value has changed
bHasDefaultPinValueChanged = true ;
2014-03-14 14:13:41 -04:00
if ( ! bHasDefaultPin )
{
UEdGraphPin * DefaultPin = GetDefaultPin ( ) ;
if ( DefaultPin )
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
K2Schema - > BreakPinLinks ( * DefaultPin , true ) ;
}
}
ReconstructNode ( ) ;
// Clear the default pin value change flag
bHasDefaultPinValueChanged = false ;
}
Super : : PostEditChangeProperty ( PropertyChangedEvent ) ;
}
2017-10-25 09:30:36 -04:00
FName UK2Node_Switch : : GetSelectionPinName ( )
2014-03-14 14:13:41 -04:00
{
return SelectionPinName ;
}
void UK2Node_Switch : : AllocateDefaultPins ( )
{
// Add default pin
if ( bHasDefaultPin )
{
2017-10-25 09:30:36 -04:00
CreatePin ( EGPD_Output , UEdGraphSchema_K2 : : PC_Exec , DefaultPinName ) ;
2014-03-14 14:13:41 -04:00
}
// Add exec input pin
2017-10-25 09:30:36 -04:00
CreatePin ( EGPD_Input , UEdGraphSchema_K2 : : PC_Exec , UEdGraphSchema_K2 : : PN_Execute ) ;
2014-03-14 14:13:41 -04:00
// Create selection pin based on type
CreateSelectionPin ( ) ;
// Create a new function pin
CreateFunctionPin ( ) ;
// Create any case pins if required
CreateCasePins ( ) ;
}
UK2Node : : ERedirectType UK2Node_Switch : : DoPinsMatchForReconstruction ( const UEdGraphPin * NewPin , int32 NewPinIndex , const UEdGraphPin * OldPin , int32 OldPinIndex ) const
{
// If the default pin setting has changed, return a match for the "execute" input pin (which will have swapped slots), so that we don't have to break any links to it
if ( bHasDefaultPinValueChanged & & ( ( OldPinIndex = = 0 ) | | ( NewPinIndex = = 0 ) ) )
{
if ( ( bHasDefaultPin & & OldPinIndex = = 0 & & NewPinIndex = = 1 )
| | ( ! bHasDefaultPin & & OldPinIndex = = 1 & & NewPinIndex = = 0 ) )
{
return ERedirectType_Name ;
}
}
2017-10-25 09:30:36 -04:00
else if ( NewPin - > PinName = = OldPin - > PinName )
2014-03-14 14:13:41 -04:00
{
// Compare the names, case-sensitively
return ERedirectType_Name ;
}
return ERedirectType_None ;
}
FLinearColor UK2Node_Switch : : GetNodeTitleColor ( ) const
{
// Use yellow for now
return FLinearColor ( 255.0f , 255.0f , 0.0f ) ;
}
2016-05-11 11:05:13 -04:00
FSlateIcon UK2Node_Switch : : GetIconAndTint ( FLinearColor & OutColor ) const
{
2022-05-09 13:12:28 -04:00
static FSlateIcon Icon ( FAppStyle : : GetAppStyleSetName ( ) , " GraphEditor.Switch_16x " ) ;
2016-05-11 11:05:13 -04:00
return Icon ;
}
2014-03-14 14:13:41 -04:00
void UK2Node_Switch : : AddPinToSwitchNode ( )
{
2017-10-25 09:30:36 -04:00
const FName NewPinName = GetUniquePinName ( ) ;
if ( ! NewPinName . IsNone ( ) )
2014-03-14 14:13:41 -04:00
{
2017-10-25 09:30:36 -04:00
CreatePin ( EGPD_Output , UEdGraphSchema_K2 : : PC_Exec , NewPinName ) ;
2014-03-14 14:13:41 -04:00
}
}
void UK2Node_Switch : : RemovePinFromSwitchNode ( UEdGraphPin * TargetPin )
{
// If removing the default pin, we'll need to reconstruct the node, so send a property changed event to handle that
if ( bHasDefaultPin & & TargetPin = = GetDefaultPin ( ) )
{
2020-03-15 10:33:45 -04:00
FProperty * HasDefaultPinProperty = FindFProperty < FProperty > ( GetClass ( ) , " bHasDefaultPin " ) ;
2017-05-09 17:15:32 -04:00
if ( HasDefaultPinProperty )
2014-03-14 14:13:41 -04:00
{
PreEditChange ( HasDefaultPinProperty ) ;
bHasDefaultPin = false ;
FPropertyChangedEvent HasDefaultPinPropertyChangedEvent ( HasDefaultPinProperty ) ;
PostEditChangeProperty ( HasDefaultPinPropertyChangedEvent ) ;
}
}
else
{
2017-03-22 12:57:30 -04:00
RemovePin ( TargetPin ) ;
2021-11-18 14:37:34 -05:00
TargetPin - > MarkAsGarbage ( ) ;
2014-03-14 14:13:41 -04:00
Pins . Remove ( TargetPin ) ;
}
}
2017-05-09 17:15:32 -04:00
bool UK2Node_Switch : : CanRemoveExecutionPin ( UEdGraphPin * TargetPin ) const
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
// Don't allow removing last pin
int32 NumExecPins = 0 ;
for ( int32 i = 0 ; i < Pins . Num ( ) ; + + i )
{
UEdGraphPin * PotentialPin = Pins [ i ] ;
if ( K2Schema - > IsExecPin ( * PotentialPin ) & & ( PotentialPin - > Direction = = EGPD_Output ) )
{
NumExecPins + + ;
}
}
return NumExecPins > 1 ;
}
2014-03-14 14:13:41 -04:00
// Returns the exec output pin name for a given 0-based index
2017-10-25 09:30:36 -04:00
FName UK2Node_Switch : : GetPinNameGivenIndex ( int32 Index ) const
2014-03-14 14:13:41 -04:00
{
2017-10-25 09:30:36 -04:00
return * FString : : Printf ( TEXT ( " %d " ) , Index ) ;
2014-03-14 14:13:41 -04:00
}
void UK2Node_Switch : : CreateFunctionPin ( )
{
// Set properties on the function pin
2017-10-25 09:30:36 -04:00
UEdGraphPin * FunctionPin = CreatePin ( EGPD_Input , UEdGraphSchema_K2 : : PC_Object , FunctionClass , FunctionName ) ;
2014-03-14 14:13:41 -04:00
FunctionPin - > bDefaultValueIsReadOnly = true ;
FunctionPin - > bNotConnectable = true ;
FunctionPin - > bHidden = true ;
2020-03-15 10:33:45 -04:00
UFunction * Function = FindUField < UFunction > ( FunctionClass , FunctionName ) ;
2021-04-29 19:32:06 -04:00
const bool bIsStaticFunc = Function ? Function - > HasAllFunctionFlags ( FUNC_Static ) : false ;
2014-03-14 14:13:41 -04:00
if ( bIsStaticFunc )
{
// Wire up the self to the CDO of the class if it's not us
if ( UBlueprint * BP = GetBlueprint ( ) )
{
UClass * FunctionOwnerClass = Function - > GetOuterUClass ( ) ;
2022-06-02 16:09:18 -04:00
if ( ! BP - > SkeletonGeneratedClass | | ! BP - > SkeletonGeneratedClass - > IsChildOf ( FunctionOwnerClass ) )
2014-03-14 14:13:41 -04:00
{
FunctionPin - > DefaultObject = FunctionOwnerClass - > GetDefaultObject ( ) ;
}
}
}
}
2016-10-27 20:37:57 -04:00
UEdGraphPin * UK2Node_Switch : : GetFunctionPin ( ) const
2014-03-14 14:13:41 -04:00
{
//@TODO: Should probably use a specific index, though FindPin starts at 0, so this won't *currently* conflict with user created pins
2017-10-25 09:30:36 -04:00
return FindPin ( FunctionName ) ;
2014-03-14 14:13:41 -04:00
}
2016-10-27 20:37:57 -04:00
UEdGraphPin * UK2Node_Switch : : GetSelectionPin ( ) const
2014-03-14 14:13:41 -04:00
{
//@TODO: Should probably use a specific index, though FindPin starts at 0, so this won't *currently* conflict with user created pins
return FindPin ( SelectionPinName ) ;
}
2016-10-27 20:37:57 -04:00
UEdGraphPin * UK2Node_Switch : : GetDefaultPin ( ) const
2014-03-14 14:13:41 -04:00
{
return ( bHasDefaultPin )
? Pins [ 0 ]
: NULL ;
}
FNodeHandlingFunctor * UK2Node_Switch : : CreateNodeHandler ( FKismetCompilerContext & CompilerContext ) const
{
2014-09-16 14:26:32 -04:00
return new FKCHandler_Switch ( CompilerContext ) ;
2014-03-14 14:13:41 -04:00
}
2014-07-14 13:29:38 -04:00
FText UK2Node_Switch : : GetMenuCategory ( ) const
{
2014-09-04 13:00:27 -04:00
static FNodeTextCache CachedCategory ;
2015-04-02 11:16:23 -04:00
if ( CachedCategory . IsOutOfDate ( this ) )
2014-09-04 13:00:27 -04:00
{
// FText::Format() is slow, so we cache this to save on performance
2015-04-02 11:16:23 -04:00
CachedCategory . SetCachedText ( FEditorCategoryUtils : : BuildCategoryString ( FCommonEditorCategory : : FlowControl , LOCTEXT ( " ActionMenuCategory " , " Switch " ) ) , this ) ;
2014-09-04 13:00:27 -04:00
}
return CachedCategory ;
2014-07-14 13:29:38 -04:00
}
2021-04-29 19:32:06 -04:00
FString UK2Node_Switch : : GetExportTextForPin ( const UEdGraphPin * Pin ) const
{
return Pin - > PinName . ToString ( ) ;
}
2016-10-27 20:37:57 -04:00
FEdGraphPinType UK2Node_Switch : : GetInnerCaseType ( ) const
{
UEdGraphPin * SelectionPin = GetSelectionPin ( ) ;
if ( ensure ( SelectionPin ) )
{
return SelectionPin - > PinType ;
}
return FEdGraphPinType ( ) ;
}
2014-03-14 14:13:41 -04:00
# undef LOCTEXT_NAMESPACE