2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "BlueprintGraphPrivatePCH.h"
# include "DynamicCastHandler.h"
2014-07-14 16:15:27 -04:00
# include "EditorCategoryUtils.h"
2014-11-20 16:46:25 -05:00
# include "BlueprintEditorSettings.h"
2015-03-02 10:58:01 -05:00
# include "ScopedTransaction.h"
2014-07-14 16:15:27 -04:00
# define LOCTEXT_NAMESPACE "K2Node_DynamicCast"
2014-03-14 14:13:41 -04:00
2014-11-21 14:51:28 -05:00
namespace UK2Node_DynamicCastImpl
{
static const FString CastSuccessPinName ( " bSuccess " ) ;
}
2014-10-14 10:29:11 -04:00
UK2Node_DynamicCast : : UK2Node_DynamicCast ( const FObjectInitializer & ObjectInitializer )
: Super ( ObjectInitializer )
2014-10-27 13:10:00 -04:00
, bIsPureCast ( false )
2014-03-14 14:13:41 -04:00
{
}
void UK2Node_DynamicCast : : AllocateDefaultPins ( )
{
2015-04-08 14:55:57 -04:00
const bool bReferenceObsoleteClass = TargetType & & TargetType - > HasAnyClassFlags ( CLASS_NewerVersionExists ) ;
if ( bReferenceObsoleteClass )
{
Message_Error ( FString : : Printf ( TEXT ( " Node '%s' references obsolete class '%s' " ) , * GetPathName ( ) , * TargetType - > GetPathName ( ) ) ) ;
}
ensure ( ! bReferenceObsoleteClass ) ;
2014-03-14 14:13:41 -04:00
2014-10-27 13:10:00 -04:00
const UEdGraphSchema_K2 * K2Schema = Cast < UEdGraphSchema_K2 > ( GetSchema ( ) ) ;
check ( K2Schema ! = nullptr ) ;
if ( ! K2Schema - > DoesGraphSupportImpureFunctions ( GetGraph ( ) ) )
{
bIsPureCast = true ;
}
2014-03-14 14:13:41 -04:00
2014-10-27 13:10:00 -04:00
if ( ! bIsPureCast )
{
// Input - Execution Pin
CreatePin ( EGPD_Input , K2Schema - > PC_Exec , TEXT ( " " ) , NULL , false , false , K2Schema - > PN_Execute ) ;
// Output - Execution Pins
CreatePin ( EGPD_Output , K2Schema - > PC_Exec , TEXT ( " " ) , NULL , false , false , K2Schema - > PN_CastSucceeded ) ;
CreatePin ( EGPD_Output , K2Schema - > PC_Exec , TEXT ( " " ) , NULL , false , false , K2Schema - > PN_CastFailed ) ;
}
2014-03-14 14:13:41 -04:00
// Input - Source type Pin
2014-12-15 15:30:07 -05:00
CreatePin ( EGPD_Input , K2Schema - > PC_Wildcard , TEXT ( " " ) , UObject : : StaticClass ( ) , false , false , K2Schema - > PN_ObjectToCast ) ;
2014-03-14 14:13:41 -04:00
// Output - Data Pin
if ( TargetType ! = NULL )
{
2014-10-27 13:10:00 -04:00
FString CastResultPinName = K2Schema - > PN_CastedValuePrefix + TargetType - > GetDisplayNameText ( ) . ToString ( ) ;
2014-04-23 17:58:27 -04:00
if ( TargetType - > IsChildOf ( UInterface : : StaticClass ( ) ) )
{
CreatePin ( EGPD_Output , K2Schema - > PC_Interface , TEXT ( " " ) , * TargetType , false , false , CastResultPinName ) ;
}
else
{
CreatePin ( EGPD_Output , K2Schema - > PC_Object , TEXT ( " " ) , * TargetType , false , false , CastResultPinName ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-11-21 14:51:28 -05:00
UEdGraphPin * BoolSuccessPin = CreatePin ( EGPD_Output , K2Schema - > PC_Boolean , TEXT ( " " ) , nullptr , /*bIsArray =*/ false , /*bIsReference =*/ false , UK2Node_DynamicCastImpl : : CastSuccessPinName ) ;
BoolSuccessPin - > bHidden = ! bIsPureCast ;
2014-03-14 14:13:41 -04:00
Super : : AllocateDefaultPins ( ) ;
}
FLinearColor UK2Node_DynamicCast : : GetNodeTitleColor ( ) const
{
return FLinearColor ( 0.0f , 0.55f , 0.62f ) ;
}
2014-04-23 18:30:37 -04:00
FText UK2Node_DynamicCast : : GetNodeTitle ( ENodeTitleType : : Type TitleType ) const
2014-03-14 14:13:41 -04:00
{
2014-09-02 19:08:09 -04:00
if ( TargetType = = nullptr )
{
return NSLOCTEXT ( " K2Node_DynamicCast " , " BadCastNode " , " Bad cast node " ) ;
}
2015-04-02 11:16:23 -04:00
else if ( CachedNodeTitle . IsOutOfDate ( this ) )
2014-04-23 18:30:37 -04:00
{
// If casting to BP class, use BP name not class name (ie. remove the _C)
FString TargetName ;
UBlueprint * CastToBP = UBlueprint : : GetBlueprintFromClass ( TargetType ) ;
2014-09-02 19:08:09 -04:00
if ( CastToBP ! = NULL )
2014-04-23 18:30:37 -04:00
{
TargetName = CastToBP - > GetName ( ) ;
}
else
{
TargetName = TargetType - > GetName ( ) ;
}
FFormatNamedArguments Args ;
Args . Add ( TEXT ( " TargetName " ) , FText : : FromString ( TargetName ) ) ;
2014-09-02 19:08:09 -04:00
// FText::Format() is slow, so we cache this to save on performance
2015-04-02 11:16:23 -04:00
CachedNodeTitle . SetCachedText ( FText : : Format ( NSLOCTEXT ( " K2Node_DynamicCast " , " CastTo " , " Cast To {TargetName} " ) , Args ) , this ) ;
2014-04-23 18:30:37 -04:00
}
2014-09-02 19:08:09 -04:00
return CachedNodeTitle ;
2014-04-23 18:30:37 -04:00
}
2014-10-27 13:10:00 -04:00
void UK2Node_DynamicCast : : GetContextMenuActions ( const FGraphNodeContextMenuBuilder & Context ) const
{
Super : : GetContextMenuActions ( Context ) ;
Context . MenuBuilder - > BeginSection ( " K2NodeDynamicCast " , LOCTEXT ( " DynamicCastHeader " , " Cast " ) ) ;
{
FText MenuEntryTitle = LOCTEXT ( " MakePureTitle " , " Convert to pure cast " ) ;
FText MenuEntryTooltip = LOCTEXT ( " MakePureTooltip " , " Removes the execution pins to make the node more versitile (NOTE: the cast could still, resulting in an invalid output). " ) ;
bool bCanTogglePurity = true ;
2015-03-03 17:20:43 -05:00
auto CanExecutePurityToggle = [ ] ( bool const bInCanTogglePurity ) - > bool
2014-10-27 13:10:00 -04:00
{
2015-03-03 17:20:43 -05:00
return bInCanTogglePurity ;
2014-10-27 13:10:00 -04:00
} ;
if ( bIsPureCast )
{
MenuEntryTitle = LOCTEXT ( " MakeImpureTitle " , " Convert to impure cast " ) ;
MenuEntryTooltip = LOCTEXT ( " MakeImpureTooltip " , " Adds in branching execution pins so that you can separatly handle when the cast fails/succeeds. " ) ;
const UEdGraphSchema_K2 * K2Schema = Cast < UEdGraphSchema_K2 > ( GetSchema ( ) ) ;
check ( K2Schema ! = nullptr ) ;
bCanTogglePurity = K2Schema - > DoesGraphSupportImpureFunctions ( GetGraph ( ) ) ;
if ( ! bCanTogglePurity )
{
MenuEntryTooltip = LOCTEXT ( " CannotMakeImpureTooltip " , " This graph does not support impure calls (and you should therefore test the cast's result for validity). " ) ;
}
}
Context . MenuBuilder - > AddMenuEntry (
MenuEntryTitle ,
MenuEntryTooltip ,
FSlateIcon ( ) ,
FUIAction (
FExecuteAction : : CreateUObject ( this , & UK2Node_DynamicCast : : TogglePurity ) ,
FCanExecuteAction : : CreateStatic ( CanExecutePurityToggle , bCanTogglePurity ) ,
FIsActionChecked ( )
)
) ;
}
Context . MenuBuilder - > EndSection ( ) ;
}
2014-12-15 15:30:07 -05:00
void UK2Node_DynamicCast : : PostReconstructNode ( )
{
Super : : PostReconstructNode ( ) ;
// update the pin name (to "Interface" if an interface is connected)
NotifyPinConnectionListChanged ( GetCastSourcePin ( ) ) ;
}
2014-12-10 18:18:54 -05:00
void UK2Node_DynamicCast : : PostPlacedNewNode ( )
{
Super : : PostPlacedNewNode ( ) ;
const UBlueprintEditorSettings * BlueprintSettings = GetDefault < UBlueprintEditorSettings > ( ) ;
SetPurity ( BlueprintSettings - > bFavorPureCastNodes ) ;
}
2014-03-14 14:13:41 -04:00
UEdGraphPin * UK2Node_DynamicCast : : GetValidCastPin ( ) const
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
UEdGraphPin * Pin = FindPin ( K2Schema - > PN_CastSucceeded ) ;
2014-10-27 13:10:00 -04:00
check ( ( Pin ! = nullptr ) | | bIsPureCast ) ;
check ( ( Pin = = nullptr ) | | ( Pin - > Direction = = EGPD_Output ) ) ;
2014-03-14 14:13:41 -04:00
return Pin ;
}
UEdGraphPin * UK2Node_DynamicCast : : GetInvalidCastPin ( ) const
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
UEdGraphPin * Pin = FindPin ( K2Schema - > PN_CastFailed ) ;
2014-10-27 13:10:00 -04:00
check ( ( Pin ! = nullptr ) | | bIsPureCast ) ;
check ( ( Pin = = nullptr ) | | ( Pin - > Direction = = EGPD_Output ) ) ;
2014-03-14 14:13:41 -04:00
return Pin ;
}
UEdGraphPin * UK2Node_DynamicCast : : GetCastResultPin ( ) const
{
UEdGraphPin * Pin = NULL ;
if ( TargetType ! = NULL )
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
2014-10-27 13:10:00 -04:00
FString PinName = K2Schema - > PN_CastedValuePrefix + TargetType - > GetDisplayNameText ( ) . ToString ( ) ;
2014-03-14 14:13:41 -04:00
Pin = FindPin ( PinName ) ;
}
return Pin ;
}
UEdGraphPin * UK2Node_DynamicCast : : GetCastSourcePin ( ) const
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
UEdGraphPin * Pin = FindPin ( K2Schema - > PN_ObjectToCast ) ;
check ( Pin ! = NULL ) ;
check ( Pin - > Direction = = EGPD_Input ) ;
return Pin ;
}
2014-11-21 14:51:28 -05:00
UEdGraphPin * UK2Node_DynamicCast : : GetBoolSuccessPin ( ) const
{
UEdGraphPin * Pin = FindPin ( UK2Node_DynamicCastImpl : : CastSuccessPinName ) ;
check ( Pin ! = nullptr ) ;
check ( Pin - > Direction = = EGPD_Output ) ;
return Pin ;
}
2014-10-27 13:10:00 -04:00
void UK2Node_DynamicCast : : SetPurity ( bool bNewPurity )
{
if ( bNewPurity ! = bIsPureCast )
{
bIsPureCast = bNewPurity ;
bool const bHasBeenConstructed = ( Pins . Num ( ) > 0 ) ;
if ( bHasBeenConstructed )
{
ReconstructNode ( ) ;
}
}
}
void UK2Node_DynamicCast : : TogglePurity ( )
{
2015-03-02 10:58:01 -05:00
FText TransactionTitle ;
if ( bIsPureCast )
{
TransactionTitle = LOCTEXT ( " TogglePure " , " Convert to Pure Cast " ) ;
}
else
{
TransactionTitle = LOCTEXT ( " ToggleImpure " , " Convert to Impure Cast " ) ;
}
const FScopedTransaction Transaction ( TransactionTitle ) ;
Modify ( ) ;
2014-10-27 13:10:00 -04:00
SetPurity ( ! bIsPureCast ) ;
}
2014-03-14 14:13:41 -04:00
UK2Node : : ERedirectType UK2Node_DynamicCast : : DoPinsMatchForReconstruction ( const UEdGraphPin * NewPin , int32 NewPinIndex , const UEdGraphPin * OldPin , int32 OldPinIndex ) const
{
ERedirectType RedirectType = Super : : DoPinsMatchForReconstruction ( NewPin , NewPinIndex , OldPin , OldPinIndex ) ;
if ( ( ERedirectType_None = = RedirectType ) & & ( NULL ! = NewPin ) & & ( NULL ! = OldPin ) )
{
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
const bool bProperPrefix =
NewPin - > PinName . StartsWith ( K2Schema - > PN_CastedValuePrefix , ESearchCase : : CaseSensitive ) & &
OldPin - > PinName . StartsWith ( K2Schema - > PN_CastedValuePrefix , ESearchCase : : CaseSensitive ) ;
const bool bClassMatch = NewPin - > PinType . PinSubCategoryObject . IsValid ( ) & &
( NewPin - > PinType . PinSubCategoryObject = = OldPin - > PinType . PinSubCategoryObject ) ;
if ( bProperPrefix & & bClassMatch )
{
RedirectType = ERedirectType_Name ;
}
}
return RedirectType ;
}
FNodeHandlingFunctor * UK2Node_DynamicCast : : CreateNodeHandler ( FKismetCompilerContext & CompilerContext ) const
{
return new FKCHandler_DynamicCast ( CompilerContext , KCST_DynamicCast ) ;
}
2014-04-23 20:18:55 -04:00
bool UK2Node_DynamicCast : : HasExternalBlueprintDependencies ( TArray < class UStruct * > * OptionalOutput ) const
{
const UBlueprint * SourceBlueprint = GetBlueprint ( ) ;
UClass * SourceClass = * TargetType ;
const bool bResult = ( SourceClass ! = NULL ) & & ( SourceClass - > ClassGeneratedBy ! = NULL ) & & ( SourceClass - > ClassGeneratedBy ! = SourceBlueprint ) ;
if ( bResult & & OptionalOutput )
{
OptionalOutput - > Add ( SourceClass ) ;
}
return bResult | | Super : : HasExternalBlueprintDependencies ( OptionalOutput ) ;
}
2014-07-14 16:15:27 -04:00
FText UK2Node_DynamicCast : : 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 : : Utilities , LOCTEXT ( " ActionMenuCategory " , " Casting " ) ) , this ) ;
2014-09-04 13:00:27 -04:00
}
return CachedCategory ;
2014-07-14 16:15:27 -04:00
}
2014-09-17 17:07:37 -04:00
FBlueprintNodeSignature UK2Node_DynamicCast : : GetSignature ( ) const
{
FBlueprintNodeSignature NodeSignature = Super : : GetSignature ( ) ;
NodeSignature . AddSubObject ( TargetType ) ;
return NodeSignature ;
}
2014-12-15 15:30:07 -05:00
bool UK2Node_DynamicCast : : IsConnectionDisallowed ( const UEdGraphPin * MyPin , const UEdGraphPin * OtherPin , FString & OutReason ) const
{
bool bIsDisallowed = Super : : IsConnectionDisallowed ( MyPin , OtherPin , OutReason ) ;
if ( MyPin = = GetCastSourcePin ( ) )
{
const FEdGraphPinType & OtherPinType = OtherPin - > PinType ;
const FText OtherPinName = OtherPin - > PinFriendlyName . IsEmpty ( ) ? FText : : FromString ( OtherPin - > PinName ) : OtherPin - > PinFriendlyName ;
if ( OtherPinType . bIsArray )
{
bIsDisallowed = true ;
OutReason = LOCTEXT ( " CannotArrayCast " , " You cannot cast arrays of objects. " ) . ToString ( ) ;
}
else if ( TargetType = = nullptr )
{
bIsDisallowed = true ;
OutReason = LOCTEXT ( " BadCastNode " , " This cast has an invalid target type (was the class deleted without a redirect?). " ) . ToString ( ) ;
}
else if ( ( OtherPinType . PinCategory = = UEdGraphSchema_K2 : : PC_Interface ) | | TargetType - > HasAnyClassFlags ( CLASS_Interface ) )
{
// allow all interface casts
}
else if ( OtherPinType . PinCategory = = UEdGraphSchema_K2 : : PC_Object )
{
UClass * ObjectClass = Cast < UClass > ( OtherPinType . PinSubCategoryObject . Get ( ) ) ;
if ( ( ObjectClass = = nullptr ) & & ( OtherPinType . PinSubCategory = = UEdGraphSchema_K2 : : PSC_Self ) )
{
if ( UK2Node * K2Node = Cast < UK2Node > ( OtherPin - > GetOwningNode ( ) ) )
{
ObjectClass = K2Node - > GetBlueprint ( ) - > GeneratedClass ;
}
}
// if the ObjectClass is still null, assume it is a UObject, which
// will work with everything (so don't disallow it)
if ( ObjectClass ! = nullptr )
{
if ( ObjectClass = = TargetType )
{
bIsDisallowed = true ;
OutReason = FText : : Format ( LOCTEXT ( " EqualObjectCast " , " '{0}' is already a '{1}', you don't need the cast. " ) ,
OtherPinName , TargetType - > GetDisplayNameText ( ) ) . ToString ( ) ;
}
else if ( ObjectClass - > IsChildOf ( TargetType ) )
{
bIsDisallowed = true ;
OutReason = FText : : Format ( LOCTEXT ( " UnneededObjectCast " , " '{0}' is already a '{1}' (which inherits from '{2}'), so you don't need the cast. " ) ,
OtherPinName , ObjectClass - > GetDisplayNameText ( ) , TargetType - > GetDisplayNameText ( ) ) . ToString ( ) ;
}
else if ( ! TargetType - > IsChildOf ( ObjectClass ) )
{
bIsDisallowed = true ;
OutReason = FText : : Format ( LOCTEXT ( " DisallowedObjectCast " , " '{0}' does not inherit from '{1}' (the cast would always fail). " ) ,
TargetType - > GetDisplayNameText ( ) , ObjectClass - > GetDisplayNameText ( ) ) . ToString ( ) ;
}
}
}
else
{
bIsDisallowed = true ;
OutReason = LOCTEXT ( " NonObjectCast " , " You can only cast objects/interfaces. " ) . ToString ( ) ;
}
}
return bIsDisallowed ;
}
void UK2Node_DynamicCast : : NotifyPinConnectionListChanged ( UEdGraphPin * Pin )
{
Super : : NotifyPinConnectionListChanged ( Pin ) ;
if ( Pin = = GetCastSourcePin ( ) )
{
Pin - > PinFriendlyName = FText : : GetEmpty ( ) ;
FEdGraphPinType & InputPinType = Pin - > PinType ;
if ( Pin - > LinkedTo . Num ( ) = = 0 )
{
InputPinType . PinCategory = UEdGraphSchema_K2 : : PC_Wildcard ;
InputPinType . PinSubCategory . Empty ( ) ;
InputPinType . PinSubCategoryObject = nullptr ;
}
else
{
const FEdGraphPinType & ConnectedPinType = Pin - > LinkedTo [ 0 ] - > PinType ;
if ( ConnectedPinType . PinCategory = = UEdGraphSchema_K2 : : PC_Interface )
{
Pin - > PinFriendlyName = LOCTEXT ( " InterfaceInputName " , " Interface " ) ;
InputPinType . PinCategory = UEdGraphSchema_K2 : : PC_Interface ;
InputPinType . PinSubCategoryObject = ConnectedPinType . PinSubCategoryObject ;
}
else if ( ConnectedPinType . PinCategory = = UEdGraphSchema_K2 : : PC_Object )
{
InputPinType . PinCategory = UEdGraphSchema_K2 : : PC_Object ;
InputPinType . PinSubCategoryObject = UObject : : StaticClass ( ) ;
}
}
}
}
2015-01-15 13:54:47 -05:00
void UK2Node_DynamicCast : : ReallocatePinsDuringReconstruction ( TArray < UEdGraphPin * > & OldPins )
{
Super : : ReallocatePinsDuringReconstruction ( OldPins ) ;
// Update exec pins if we converted from impure to pure
ReconnectPureExecPins ( OldPins ) ;
}
bool UK2Node_DynamicCast : : ReconnectPureExecPins ( TArray < UEdGraphPin * > & OldPins )
{
if ( bIsPureCast )
{
// look for an old exec pin
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
UEdGraphPin * PinExec = nullptr ;
for ( UEdGraphPin * Pin : OldPins )
{
if ( Pin - > PinName = = K2Schema - > PN_Execute )
{
PinExec = Pin ;
break ;
}
}
if ( PinExec )
{
// look for old then pin
UEdGraphPin * PinThen = nullptr ;
for ( UEdGraphPin * Pin : OldPins )
{
if ( Pin - > PinName = = K2Schema - > PN_Then )
{
PinThen = Pin ;
break ;
}
}
if ( PinThen )
{
// reconnect all incoming links to old exec pin to the far end of the old then pin.
if ( PinThen - > LinkedTo . Num ( ) > 0 )
{
UEdGraphPin * PinThenLinked = PinThen - > LinkedTo [ 0 ] ;
while ( PinExec - > LinkedTo . Num ( ) > 0 )
{
UEdGraphPin * PinExecLinked = PinExec - > LinkedTo [ 0 ] ;
PinExecLinked - > BreakLinkTo ( PinExec ) ;
PinExecLinked - > MakeLinkTo ( PinThenLinked ) ;
}
return true ;
}
}
}
}
return false ;
}
2014-07-14 16:15:27 -04:00
# undef LOCTEXT_NAMESPACE