// Copyright Epic Games, Inc. All Rights Reserved. #include "EdGraphSchema_K2_Actions.h" #include "ComponentAssetBroker.h" #include "Components/ActorComponent.h" #include "Containers/EnumAsByte.h" #include "EdGraph/EdGraphNode.h" #include "EdGraphNode_Comment.h" #include "EdGraphSchema_K2.h" #include "EdGraphUtilities.h" #include "Engine/Blueprint.h" #include "Engine/BlueprintGeneratedClass.h" #include "Engine/MemberReference.h" #include "GameFramework/Actor.h" #include "K2Node.h" #include "K2Node_AddComponent.h" #include "K2Node_AddDelegate.h" #include "K2Node_BaseMCDelegate.h" #include "K2Node_CustomEvent.h" #include "K2Node_Event.h" #include "K2Node_Literal.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/KismetEditorUtilities.h" #include "Layout/SlateRect.h" #include "Math/UnrealMathSSE.h" #include "ScopedTransaction.h" #include "Settings/EditorStyleSettings.h" #include "UObject/Field.h" #include "UObject/ObjectHandle.h" namespace { // Maximum distance a drag can be off a node edge to require 'push off' from node const int32 NodeDistance = 60; // The amount to offset a literal reference (to an actor) from the function node it is being connected to const float FunctionNodeLiteralReferencesXOffset = 224.0f; // The height of a literal reference node const float NodeLiteralHeight = 48.0f; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_BlueprintVariableBase void FEdGraphSchemaAction_BlueprintVariableBase::MovePersistentItemToCategory(const FText& NewCategoryName) { FBlueprintEditorUtils::SetBlueprintVariableCategory(GetSourceBlueprint(), VarName, Cast(GetVariableScope()), NewCategoryName); } int32 FEdGraphSchemaAction_BlueprintVariableBase::GetReorderIndexInContainer() const { if (UBlueprint* SourceBlueprint = GetSourceBlueprint()) { return FBlueprintEditorUtils::FindNewVariableIndex(SourceBlueprint, VarName); } return INDEX_NONE; } bool FEdGraphSchemaAction_BlueprintVariableBase::ReorderToBeforeAction(TSharedRef OtherAction) { if (OtherAction->GetPersistentItemDefiningObject() == GetPersistentItemDefiningObject()) { FEdGraphSchemaAction_BlueprintVariableBase* VarAction = (FEdGraphSchemaAction_BlueprintVariableBase*)&OtherAction.Get(); // Only let you drag and drop if variables are from same BP class, and not onto itself UBlueprint* BP = GetSourceBlueprint(); FName TargetVarName = VarAction->GetVariableName(); if ((BP != nullptr) && (VarName != TargetVarName) && (VariableSource == VarAction->GetVariableScope())) { if (FBlueprintEditorUtils::MoveVariableBeforeVariable(BP, Cast(VarAction->GetVariableScope()), VarName, TargetVarName, true)) { // Change category of var to match the one we dragged on to as well FText TargetVarCategory = FBlueprintEditorUtils::GetBlueprintVariableCategory(BP, TargetVarName, Cast(GetVariableScope())); MovePersistentItemToCategory(TargetVarCategory); // Update Blueprint after changes so they reflect in My Blueprint tab. FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP); return true; } } } return false; } FEdGraphSchemaActionDefiningObject FEdGraphSchemaAction_BlueprintVariableBase::GetPersistentItemDefiningObject() const { UObject* DefiningObject = GetSourceBlueprint(); if (FProperty* Prop = GetProperty()) { DefiningObject = Prop->GetOwnerStruct(); } return FEdGraphSchemaActionDefiningObject(DefiningObject); } UBlueprint* FEdGraphSchemaAction_BlueprintVariableBase::GetSourceBlueprint() const { UClass* ClassToCheck = GetVariableClass(); if (ClassToCheck == nullptr) { if (UFunction* Function = Cast(GetVariableScope())) { ClassToCheck = Function->GetOuterUClass(); } } return UBlueprint::GetBlueprintFromClass(ClassToCheck); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2LocalVar int32 FEdGraphSchemaAction_K2LocalVar::GetReorderIndexInContainer() const { return FBlueprintEditorUtils::FindLocalVariableIndex(GetSourceBlueprint(), Cast(GetVariableScope()), GetVariableName()); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2Graph void FEdGraphSchemaAction_K2Graph::MovePersistentItemToCategory(const FText& NewCategoryName) { if ((GraphType == EEdGraphSchemaAction_K2Graph::Function) || (GraphType == EEdGraphSchemaAction_K2Graph::Macro)) { if(EdGraph->GetSchema()->GetClass()->GetFName() == TEXT("AnimationGraphSchema")) { FBlueprintEditorUtils::SetAnimationGraphLayerGroup(EdGraph, NewCategoryName); } else { FBlueprintEditorUtils::SetBlueprintFunctionOrMacroCategory(EdGraph, NewCategoryName); } } } int32 FEdGraphSchemaAction_K2Graph::GetReorderIndexInContainer() const { return FBlueprintEditorUtils::FindIndexOfGraphInParent(EdGraph); } bool FEdGraphSchemaAction_K2Graph::ReorderToBeforeAction(TSharedRef OtherAction) { if (OtherAction->GetTypeId() == GetTypeId()) { const int32 OldIndex = GetReorderIndexInContainer(); const int32 NewIndexToGoBefore = OtherAction->GetReorderIndexInContainer(); if ((OldIndex != INDEX_NONE) && (OldIndex != NewIndexToGoBefore)) { if (FBlueprintEditorUtils::MoveGraphBeforeOtherGraph(EdGraph, NewIndexToGoBefore, true)) { // Change category to match the one we dragged on to as well MovePersistentItemToCategory(OtherAction->GetCategory()); return true; } } } return false; } FEdGraphSchemaActionDefiningObject FEdGraphSchemaAction_K2Graph::GetPersistentItemDefiningObject() const { UObject* DefiningObject = GetSourceBlueprint(); if (UFunction* Func = GetFunction()) { // Use the class where the function was initially introduced as the defining object while (Func->GetSuperFunction()) { Func = Func->GetSuperFunction(); } DefiningObject = Func->GetOuterUClassUnchecked(); } return FEdGraphSchemaActionDefiningObject(DefiningObject, (void*)GraphType); } UBlueprint* FEdGraphSchemaAction_K2Graph::GetSourceBlueprint() const { return FBlueprintEditorUtils::FindBlueprintForGraph(EdGraph); } UFunction* FEdGraphSchemaAction_K2Graph::GetFunction() const { if (GraphType == EEdGraphSchemaAction_K2Graph::Function) { if (UBlueprint* SourceBlueprint = GetSourceBlueprint()) { if (FuncName != NAME_None) { return FindUField(SourceBlueprint->SkeletonGeneratedClass, FuncName); } } } return nullptr; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2Delegate ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2ViewNode UEdGraphNode* FEdGraphSchemaAction_K2NewNode::CreateNode( UEdGraph* ParentGraph, TArrayView FromPins, const FVector2D Location, UK2Node* NodeTemplate, EK2NewNodeFlags Options) { if (NodeTemplate) { return CreateNode( ParentGraph, FromPins, Location, [NodeTemplate](UEdGraph* InParentGraph)->UK2Node* { return DuplicateObject(NodeTemplate, InParentGraph); }, [](UK2Node*) {}, Options ); } return nullptr; } UEdGraphNode* FEdGraphSchemaAction_K2NewNode::CreateNode( UEdGraph* ParentGraph, TArrayView FromPins, const FVector2D Location, TFunctionRef ConstructionFn, TFunctionRef InitializerFn, EK2NewNodeFlags Options) { const bool bSelectNewNode = (Options & EK2NewNodeFlags::SelectNewNode) != EK2NewNodeFlags::None; const bool bGotoNode = (Options & EK2NewNodeFlags::GotoNewNode) != EK2NewNodeFlags::None; const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "K2_AddNode", "Add Node"), !IsInAsyncLoadingThread()); ParentGraph->Modify(); for(UEdGraphPin* FromPin : FromPins) { if (FromPin != nullptr) { FromPin->Modify(); } } // Smart pointer that handles fixup after potential node reconstruction FWeakGraphPinPtr FromPinPtr = FromPins.Num() > 0 ? FromPins[0] : nullptr; // Duplicate template node to create new node UK2Node* ResultNode = ConstructionFn(ParentGraph); InitializerFn(ResultNode); if (ParentGraph->HasAnyFlags(RF_Transactional)) { ResultNode->SetFlags(RF_Transactional); } ParentGraph->AddNode(ResultNode, true, bSelectNewNode); ResultNode->CreateNewGuid(); ResultNode->PostPlacedNewNode(); ResultNode->AllocateDefaultPins(); // For input pins, new node will generally overlap node being dragged off // Work out if we want to visually push away from connected node int32 XLocation = static_cast(Location.X); if (FromPinPtr.IsValid() && FromPinPtr->Direction == EGPD_Input) { UEdGraphNode* PinNode = FromPinPtr->GetOwningNode(); const double XDelta = FMath::Abs(PinNode->NodePosX - Location.X); if (XDelta < NodeDistance) { // Set location to edge of current node minus the max move distance // to force node to push off from connect node enough to give selection handle XLocation = PinNode->NodePosX - NodeDistance; } } ResultNode->NodePosX = XLocation; ResultNode->NodePosY = static_cast(Location.Y); ResultNode->SnapToGrid(GetDefault()->GridSnapSize); // make sure to auto-wire after we position the new node (in case the // auto-wire creates a conversion node to put between them) for (UEdGraphPin* FromPin : FromPins) { ResultNode->AutowireNewNode(FromPin); } // Update Analytics for the new nodes FBlueprintEditorUtils::AnalyticsTrackNewNode(ResultNode); // NOTE: At this point the node may have been reconstructed, depending on node type! UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); // See if we need to recompile skeleton after adding this node, or just mark dirty if (ResultNode->NodeCausesStructuralBlueprintChange()) { FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); } else { FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); } // Clear any error messages resulting from placing a node. They'll be flagged on the next compile ResultNode->ErrorMsg.Empty(); ResultNode->bHasCompilerMessage = false; if (bGotoNode) { // Select existing node FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ResultNode); } return ResultNode; } UEdGraphNode* FEdGraphSchemaAction_K2NewNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { EK2NewNodeFlags Options = EK2NewNodeFlags::None; if (bSelectNewNode) { Options |= EK2NewNodeFlags::SelectNewNode; } if (bGotoNode) { Options |= EK2NewNodeFlags::GotoNewNode; } return CreateNode(ParentGraph, MakeArrayView(&FromPin, 1), FDeprecateSlateVector2D(Location), NodeTemplate, Options); } UEdGraphNode* FEdGraphSchemaAction_K2NewNode::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2f& Location, bool bSelectNewNode/* = true*/) { EK2NewNodeFlags Options = EK2NewNodeFlags::None; if (bSelectNewNode) { Options |= EK2NewNodeFlags::SelectNewNode; } if (bGotoNode) { Options |= EK2NewNodeFlags::GotoNewNode; } return CreateNode(ParentGraph, FromPins, FDeprecateSlateVector2D(Location), NodeTemplate, Options); } void FEdGraphSchemaAction_K2NewNode::AddReferencedObjects( FReferenceCollector& Collector ) { FEdGraphSchemaAction::AddReferencedObjects( Collector ); // These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around Collector.AddReferencedObject( NodeTemplate ); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2ViewNode UEdGraphNode* FEdGraphSchemaAction_K2ViewNode::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { // If the node is valid, select it if (NodePtr) { // Select existing node FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(NodePtr); } return nullptr; } UEdGraphNode* FEdGraphSchemaAction_K2ViewNode::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2f& Location, bool bSelectNewNode/* = true*/) { PerformAction(ParentGraph, nullptr, Location, bSelectNewNode); return nullptr; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AssignDelegate UEdGraphNode* FEdGraphSchemaAction_K2AssignDelegate::AssignDelegate(class UK2Node* NodeTemplate, class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2D Location, bool bSelectNewNode) { UK2Node_AddDelegate* BindNode = nullptr; if (UK2Node_AddDelegate* AddDelegateTemplate = Cast(NodeTemplate)) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_AddNode", "Add Node") ); ParentGraph->Modify(); if (FromPin) { FromPin->Modify(); } BindNode = Cast(CreateNode(ParentGraph, MakeArrayView(&FromPin, 1), Location, NodeTemplate, bSelectNewNode ? EK2NewNodeFlags::SelectNewNode : EK2NewNodeFlags::None)); FMulticastDelegateProperty* DelegateProperty = BindNode ? CastField(BindNode->GetProperty()) : nullptr; if(DelegateProperty) { const FString FunctionName = FString::Printf(TEXT("%s_Event"), *DelegateProperty->GetName()); UK2Node_CustomEvent* EventNode = UK2Node_CustomEvent::CreateFromFunction( FVector2D(Location.X - 150, Location.Y + 150), ParentGraph, FunctionName, DelegateProperty->SignatureFunction, bSelectNewNode); if(EventNode) { const UEdGraphSchema_K2* K2Schema = GetDefault(); UEdGraphPin* OutDelegatePin = EventNode->FindPinChecked(UK2Node_CustomEvent::DelegateOutputName); UEdGraphPin* InDelegatePin = BindNode->GetDelegatePin(); K2Schema->TryCreateConnection(OutDelegatePin, InDelegatePin); } } UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); } return BindNode; } UEdGraphNode* FEdGraphSchemaAction_K2AssignDelegate::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { return AssignDelegate(NodeTemplate, ParentGraph, FromPin, FDeprecateSlateVector2D(Location), bSelectNewNode); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_EventFromFunction UEdGraphNode* FEdGraphSchemaAction_EventFromFunction::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { UK2Node_CustomEvent* EventNode = nullptr; if (SignatureFunction) { if (FromPin) { // Make sure, that function is latest, so the names of parameters are proper. UK2Node_BaseMCDelegate* MCDelegateNode = Cast(FromPin->GetOwningNode()); UEdGraphPin* InputDelegatePin = MCDelegateNode ? MCDelegateNode->GetDelegatePin() : nullptr; UFunction* OriginalFunction = MCDelegateNode ? MCDelegateNode->GetDelegateSignature() : nullptr; if (OriginalFunction && (OriginalFunction != SignatureFunction) && (FromPin == InputDelegatePin) && SignatureFunction->IsSignatureCompatibleWith(OriginalFunction)) { SignatureFunction = OriginalFunction; } } const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_AddNode", "Add Node") ); ParentGraph->Modify(); if (FromPin) { FromPin->Modify(); } EventNode = UK2Node_CustomEvent::CreateFromFunction(FDeprecateSlateVector2D(Location), ParentGraph, SignatureFunction->GetName() + TEXT("_Event"), SignatureFunction, bSelectNewNode); EventNode->AutowireNewNode(FromPin); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); } return EventNode; } UEdGraphNode* FEdGraphSchemaAction_EventFromFunction::PerformAction(class UEdGraph* ParentGraph, TArray& FromPins, const FVector2f& Location, bool bSelectNewNode/* = true*/) { UEdGraphNode* ResultNode = nullptr; if (FromPins.Num() > 0) { ResultNode = PerformAction(ParentGraph, FromPins[0], Location, bSelectNewNode); // Try autowiring the rest of the pins for (int32 Index = 1; Index < FromPins.Num(); ++Index) { ResultNode->AutowireNewNode(FromPins[Index]); } } else { ResultNode = PerformAction(ParentGraph, nullptr, Location, bSelectNewNode); } return ResultNode; } void FEdGraphSchemaAction_EventFromFunction::AddReferencedObjects(FReferenceCollector& Collector) { FEdGraphSchemaAction::AddReferencedObjects(Collector); // These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around Collector.AddReferencedObject(SignatureFunction); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddComponent UEdGraphNode* FEdGraphSchemaAction_K2AddComponent::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { if (ComponentClass == nullptr) { return nullptr; } UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); UEdGraphNode* NewNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, Location, bSelectNewNode); if ((NewNode != nullptr) && (Blueprint != nullptr)) { UK2Node_AddComponent* AddCompNode = CastChecked(NewNode); ensure(nullptr != Cast(Blueprint->GeneratedClass)); // Then create a new template object, and add to array in UActorComponent* NewTemplate = NewObject(Blueprint->GeneratedClass, ComponentClass, NAME_None, RF_ArchetypeObject | RF_Public); Blueprint->ComponentTemplates.Add(NewTemplate); // Set the name of the template as the default for the TemplateName param UEdGraphPin* TemplateNamePin = AddCompNode->GetTemplateNamePinChecked(); if (TemplateNamePin) { TemplateNamePin->DefaultValue = NewTemplate->GetName(); } // Set the return type to be the type of the template UEdGraphPin* ReturnPin = AddCompNode->GetReturnValuePin(); if (ReturnPin) { ReturnPin->PinType.PinSubCategoryObject = *ComponentClass; } // Set the asset if(ComponentAsset != nullptr) { FComponentAssetBrokerage::AssignAssetToComponent(NewTemplate, ComponentAsset); } AddCompNode->ReconstructNode(); } FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); return NewNode; } void FEdGraphSchemaAction_K2AddComponent::AddReferencedObjects( FReferenceCollector& Collector ) { FEdGraphSchemaAction_K2NewNode::AddReferencedObjects( Collector ); // These don't get saved to disk, but we want to make sure the objects don't get GC'd while the action array is around Collector.AddReferencedObject( ComponentAsset ); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddEvent UEdGraphNode* FEdGraphSchemaAction_K2AddEvent::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { UEdGraphNode* NewNode = nullptr; const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_Event", "Add Event") ); UBlueprint const* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); UK2Node_Event const* ExistingEvent = nullptr; if (EventHasAlreadyBeenPlaced(Blueprint, &ExistingEvent)) { check(ExistingEvent != nullptr); FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(ExistingEvent); } else { NewNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, Location, bSelectNewNode); } return NewNode; } bool FEdGraphSchemaAction_K2AddEvent::EventHasAlreadyBeenPlaced(UBlueprint const* Blueprint, UK2Node_Event const** FoundEventOut /*= nullptr*/) const { UK2Node_Event* ExistingEvent = nullptr; if (Blueprint != nullptr) { UK2Node_Event const* EventTemplate = Cast(NodeTemplate); ExistingEvent = FBlueprintEditorUtils::FindOverrideForFunction(Blueprint, EventTemplate->EventReference.GetMemberParentClass(EventTemplate->GetBlueprintClassFromNode()), EventTemplate->EventReference.GetMemberName()); } if (FoundEventOut != nullptr) { *FoundEventOut = ExistingEvent; } return (ExistingEvent != nullptr); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddCustomEvent UEdGraphNode* FEdGraphSchemaAction_K2AddCustomEvent::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "K2_CustomEvent", "Add Custom Event") ); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(ParentGraph); UEdGraphNode* NewNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, Location, bSelectNewNode); // Set the name for the template to be a unique custom event name, so the generated node will have a default name that is already validated UK2Node_CustomEvent* CustomEventNode = CastChecked(NewNode); CustomEventNode->CustomFunctionName = FBlueprintEditorUtils::FindUniqueCustomEventName(Blueprint); return NewNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddCallOnActor UEdGraphNode* FEdGraphSchemaAction_K2AddCallOnActor::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { // Snap the node placement location to the grid, ensures calculations later match up better const uint32 GridSnapSize = GetDefault()->GridSnapSize; FVector2f LocalLocation; LocalLocation.X = FMath::GridSnap(Location.X, GridSnapSize); LocalLocation.Y = FMath::GridSnap(Location.Y, GridSnapSize); // First use the base functionality to spawn the 'call function' node UEdGraphNode* CallNode = FEdGraphSchemaAction_K2NewNode::PerformAction(ParentGraph, FromPin, LocalLocation); const float FunctionNodeHeightUnsnapped = UEdGraphSchema_K2::EstimateNodeHeight( CallNode ); // this is the guesstimate of the function node's height, snapped to grid units const float FunctionNodeHeight = FMath::GridSnap( FunctionNodeHeightUnsnapped, GridSnapSize ); // this is roughly the middle of the function node height const float FunctionNodeMidY = static_cast(LocalLocation.Y + FunctionNodeHeight * 0.5f); // this is the offset up from the mid point at which we start placing nodes const float StartYOffset = (static_cast((LevelActors.Num() > 0) ? LevelActors.Num() - 1 : 0) * -NodeLiteralHeight) * 0.5f; // The Y location we start placing nodes from const float ReferencedNodesPlacementYLocation = FunctionNodeMidY + StartYOffset; // Now we need to create the actor literal to wire up for ( int32 ActorIndex = 0; ActorIndex < LevelActors.Num(); ActorIndex++ ) { AActor* LevelActor = LevelActors[ActorIndex]; if(LevelActor != nullptr) { UK2Node_Literal* LiteralNode = NewObject(ParentGraph); ParentGraph->AddNode(LiteralNode, false, bSelectNewNode); LiteralNode->SetFlags(RF_Transactional); LiteralNode->SetObjectRef(LevelActor); LiteralNode->AllocateDefaultPins(); LiteralNode->NodePosX = static_cast(LocalLocation.X - FunctionNodeLiteralReferencesXOffset); // this is the current offset down from the Y start location to place the next node at float CurrentNodeOffset = NodeLiteralHeight * static_cast(ActorIndex); LiteralNode->NodePosY = static_cast(ReferencedNodesPlacementYLocation + CurrentNodeOffset); LiteralNode->SnapToGrid(GridSnapSize); // Connect the literal out to the self of the call UEdGraphPin* LiteralOutput = LiteralNode->GetValuePin(); UEdGraphPin* CallSelfInput = CallNode->FindPin(UEdGraphSchema_K2::PN_Self); if(LiteralOutput != nullptr && CallSelfInput != nullptr) { LiteralOutput->MakeLinkTo(CallSelfInput); } } } return CallNode; } void FEdGraphSchemaAction_K2AddCallOnActor::AddReferencedObjects( FReferenceCollector& Collector ) { FEdGraphSchemaAction_K2NewNode::AddReferencedObjects( Collector ); Collector.AddReferencedObjects(LevelActors); } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2AddComment UEdGraphNode* FEdGraphSchemaAction_K2AddComment::PerformAction(class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/) { // Add menu item for creating comment boxes UEdGraphNode_Comment* CommentTemplate = NewObject(); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(ParentGraph); FVector2f SpawnLocation = Location; FSlateRect Bounds; if ((Blueprint != nullptr) && FKismetEditorUtilities::GetBoundsForSelectedNodes(Blueprint, Bounds, 50.0f)) { CommentTemplate->SetBounds(Bounds); SpawnLocation.X = CommentTemplate->NodePosX; SpawnLocation.Y = CommentTemplate->NodePosY; } UEdGraphNode* NewNode = FEdGraphSchemaAction_NewNode::SpawnNodeFromTemplate(ParentGraph, CommentTemplate, SpawnLocation, bSelectNewNode); // Update Analytics for these nodes FBlueprintEditorUtils::AnalyticsTrackNewNode( NewNode ); // Mark Blueprint as structurally modified since // UK2Node_Comment::NodeCausesStructuralBlueprintChange used to return true FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint); return NewNode; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2TargetNode UEdGraphNode* FEdGraphSchemaAction_K2TargetNode::PerformAction( class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/ ) { FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(NodeTemplate); return nullptr; } ///////////////////////////////////////////////////// // FEdGraphSchemaAction_K2PasteHere UEdGraphNode* FEdGraphSchemaAction_K2PasteHere::PerformAction( class UEdGraph* ParentGraph, UEdGraphPin* FromPin, const FVector2f& Location, bool bSelectNewNode/* = true*/ ) { FKismetEditorUtilities::PasteNodesHere(ParentGraph, FDeprecateSlateVector2D(Location)); return nullptr; }