// ©2023 Batty Bovine Productions, LLC. All Rights Reserved. #include "ComboActionGraphSchema.h" #include "ComboActionGraph.h" //#include "FConnectionDrawingPolicy_ComboActionGraph.h" #include "GraphEditorActions.h" #include "Ed/EdComboActionGraphEdge.h" #include "Ed/EdComboActionGraphNode.h" #include "Framework/Commands/GenericCommands.h" #include "Nodes/ComboActionGraphEdge.h" #include "Nodes/ComboActionGraphNode.h" #define LOCTEXT_NAMESPACE "ComboActionGraph" int32 UComboActionGraphSchema::CurrentCacheRefreshID = 0; UEdGraphNode *FAssetSchemaAction_ComboActionGraphSchema_NewNode::PerformAction(UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode) { UEdGraphNode *ResultNode = nullptr; if (NodeTemplate != nullptr) { const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorNewNode", "Combo Action Graph Editor: New Node")); ParentGraph->Modify(); if (FromPin != nullptr) FromPin->Modify(); NodeTemplate->Rename(nullptr, ParentGraph); ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode); NodeTemplate->CreateNewGuid(); NodeTemplate->PostPlacedNewNode(); NodeTemplate->AllocateDefaultPins(); NodeTemplate->AutowireNewNode(FromPin); NodeTemplate->NodePosX = Location.X; NodeTemplate->NodePosY = Location.Y; NodeTemplate->ComboActionGraphNode->SetFlags(EObjectFlags::RF_Transactional); NodeTemplate->SetFlags(EObjectFlags::RF_Transactional); ResultNode = NodeTemplate; } return ResultNode; } void FAssetSchemaAction_ComboActionGraphSchema_NewNode::AddReferencedObjects(FReferenceCollector &Collector) { FEdGraphSchemaAction::AddReferencedObjects(Collector); Collector.AddReferencedObject(NodeTemplate); } UEdGraphNode *FAssetSchemaAction_ComboActionGraphSchema_NewEdge::PerformAction(UEdGraph *ParentGraph, UEdGraphPin *FromPin, const FVector2D Location, bool bSelectNewNode) { UEdGraphNode *ResultNode = nullptr; if (NodeTemplate != nullptr) { const FScopedTransaction Transaction(LOCTEXT("ComboActionGraphEditorNewEdge", "Combo Action Graph Editor: New Edge")); ParentGraph->Modify(); if (FromPin != nullptr) FromPin->Modify(); NodeTemplate->Rename(nullptr, ParentGraph); ParentGraph->AddNode(NodeTemplate, true, bSelectNewNode); NodeTemplate->CreateNewGuid(); NodeTemplate->PostPlacedNewNode(); NodeTemplate->AllocateDefaultPins(); NodeTemplate->AutowireNewNode(FromPin); NodeTemplate->NodePosX = Location.X; NodeTemplate->NodePosY = Location.Y; NodeTemplate->ComboActionGraphEdge->SetFlags(EObjectFlags::RF_Transactional); NodeTemplate->SetFlags(EObjectFlags::RF_Transactional); ResultNode = NodeTemplate; } return ResultNode; } void FAssetSchemaAction_ComboActionGraphSchema_NewEdge::AddReferencedObjects(FReferenceCollector &Collector) { FEdGraphSchemaAction::AddReferencedObjects(Collector); Collector.AddReferencedObject(NodeTemplate); } void UComboActionGraphSchema::GetBreakLinkToSubMenuActions(UToolMenu *Menu, UEdGraphPin *InGraphPin) { // Make sure we have a unique name for every entry in the list TMap LinkTitleCount; FToolMenuSection &Section = Menu->FindOrAddSection("ComboActionGraphAssetGraphSchemaPinActions"); // Add all the links we could break from for (TArray::TConstIterator Links(InGraphPin->LinkedTo); Links; ++Links) { UEdGraphPin* Pin = *Links; FString TitleString = Pin->GetOwningNode()->GetNodeTitle(ENodeTitleType::ListView).ToString(); FText Title = FText::FromString(TitleString); if (Pin->PinName != TEXT("")) { TitleString = FString::Printf(TEXT("%s (%s)"), *TitleString, *Pin->PinName.ToString()); // Add name of connection if possible FFormatNamedArguments Args; Args.Add(TEXT("NodeTitle"), Title); Args.Add(TEXT("PinName"), Pin->GetDisplayName()); Title = FText::Format(LOCTEXT("BreakDescPin", "{NodeTitle} ({PinName})"), Args); } uint32& Count = LinkTitleCount.FindOrAdd(TitleString); FText Description; FFormatNamedArguments Args; Args.Add(TEXT("NodeTitle"), Title); Args.Add(TEXT("NumberOfNodes"), Count); if (Count == 0) { Description = FText::Format(LOCTEXT("BreakDesc", "Break link to {NodeTitle}"), Args); } else { Description = FText::Format(LOCTEXT("BreakDescMulti", "Break link to {NodeTitle} ({NumberOfNodes})"), Args); } ++Count; Section.AddMenuEntry(NAME_None, Description, Description, FSlateIcon(), FUIAction( FExecuteAction::CreateUObject(this, &UComboActionGraphSchema::BreakSinglePinLink, const_cast(InGraphPin), *Links))); } } void UComboActionGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder &ContextMenuBuilder) const { UComboActionGraph *Graph = CastChecked(ContextMenuBuilder.CurrentGraph->GetOuter()); if (Graph->NodeType == nullptr) { return; } const bool bNoParent = (ContextMenuBuilder.FromPin == NULL); FText AddToolTip = LOCTEXT("NewMoutneaDialogueGraphNodeTooltip", "Add Dialogue Node here"); TSet> Visited; FText Desc = Graph->NodeType.GetDefaultObject()->ContextMenuName; FText NodeCategory = Graph->NodeType.GetDefaultObject()->GetNodeCategory(); if (Desc.IsEmpty()) { FString Title = Graph->NodeType->GetName(); Title.RemoveFromEnd("_C"); Desc = FText::FromString(Title); } if (!Graph->NodeType->HasAnyClassFlags(CLASS_Abstract)) { TSharedPtr NewNodeAction(new FAssetSchemaAction_ComboActionGraphSchema_NewNode(NodeCategory, Desc, AddToolTip, 0)); NewNodeAction->NodeTemplate = NewObject(ContextMenuBuilder.OwnerOfTemporaries); NewNodeAction->NodeTemplate->ComboActionGraphNode = NewObject(NewNodeAction->NodeTemplate, Graph->NodeType); NewNodeAction->NodeTemplate->ComboActionGraphNode->Graph = Graph; ContextMenuBuilder.AddAction(NewNodeAction); Visited.Add(Graph->NodeType); } for (TObjectIterator It; It; ++It) { if (It->IsChildOf(Graph->NodeType) && !It->HasAnyClassFlags(CLASS_Abstract) && !Visited.Contains(*It)) { TSubclassOf NodeType = *It; if (It->GetName().StartsWith("REINST") || It->GetName().StartsWith("SKEL")) continue; if (!Graph->GetClass()->IsChildOf(NodeType.GetDefaultObject()->CompatibleGraphType)) continue; if (!NodeType.GetDefaultObject()->bAllowManualCreate) continue; Desc = NodeType.GetDefaultObject()->ContextMenuName; AddToolTip = NodeType.GetDefaultObject()->GetDescription(); NodeCategory = NodeType.GetDefaultObject()->GetNodeCategory(); if (Desc.IsEmpty()) { FString Title = NodeType->GetName(); Title.RemoveFromEnd("_C"); Desc = FText::FromString(Title); } TSharedPtr Action(new FAssetSchemaAction_ComboActionGraphSchema_NewNode(NodeCategory, Desc, AddToolTip, 0)); Action->NodeTemplate = NewObject(ContextMenuBuilder.OwnerOfTemporaries); Action->NodeTemplate->ComboActionGraphNode = NewObject(Action->NodeTemplate, NodeType); Action->NodeTemplate->ComboActionGraphNode->Graph = Graph; ContextMenuBuilder.AddAction(Action); Visited.Add(NodeType); } } } void UComboActionGraphSchema::GetContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const { if (Context->Pin) { { FToolMenuSection& Section = Menu->AddSection("ComboActionGraphAssetGraphSchemaNodeActions", LOCTEXT("PinActionsMenuHeader", "Pin Actions")); // Only display the 'Break Links' option if there is a link to break! if (Context->Pin->LinkedTo.Num() > 0) { Section.AddMenuEntry(FGraphEditorCommands::Get().BreakPinLinks); // add sub menu for break link to if (Context->Pin->LinkedTo.Num() > 1) { Section.AddSubMenu( "BreakLinkTo", LOCTEXT("BreakLinkTo", "Break Link To..."), LOCTEXT("BreakSpecificLinks", "Break a specific link..."), FNewToolMenuDelegate::CreateUObject((UComboActionGraphSchema *const)this, &UComboActionGraphSchema::GetBreakLinkToSubMenuActions, const_cast(Context->Pin))); } else { ((UComboActionGraphSchema *const)this)->GetBreakLinkToSubMenuActions(Menu, const_cast(Context->Pin)); } } } } else if (Context->Node) { { FToolMenuSection& Section = Menu->AddSection("ComboActionGraphAssetGraphSchemaNodeActions", LOCTEXT("ClassActionsMenuHeader", "Node Actions")); Section.AddSeparator(FName(TEXT("Main Options"))); Section.AddMenuEntry(FGenericCommands::Get().Rename); Section.AddSeparator(FName(TEXT("Other Options"))); Section.AddMenuEntry(FGenericCommands::Get().Delete); Section.AddMenuEntry(FGenericCommands::Get().Cut); Section.AddMenuEntry(FGenericCommands::Get().Copy); Section.AddMenuEntry(FGenericCommands::Get().Duplicate); Section.AddMenuEntry(FGraphEditorCommands::Get().BreakNodeLinks); } } Super::GetContextMenuActions(Menu, Context); } const FPinConnectionResponse UComboActionGraphSchema::CanCreateConnection(const UEdGraphPin* A, const UEdGraphPin* B) const { // Make sure the pins are not on the same node if (A->GetOwningNode() == B->GetOwningNode()) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorSameNode", "Both are on the same node")); } // Compare the directions if ((A->Direction == EGPD_Input) && (B->Direction == EGPD_Input)) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorInput", "Can't connect input node to input node")); } else if ((A->Direction == EGPD_Output) && (B->Direction == EGPD_Output)) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorOutput", "Can't connect output node to output node")); } // check for cycles FComboActionNodeVisitorCycleChecker CycleChecker; if (!CycleChecker.CheckForLoop(A->GetOwningNode(), B->GetOwningNode())) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorCycle", "Can't create a graph cycle")); } if (!CycleChecker.CheckForLoop(B->GetOwningNode(), A->GetOwningNode())) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinErrorCycle", "Can't create a graph cycle")); } UEdComboActionGraphNode* EdNode_A = Cast(A->GetOwningNode()); UEdComboActionGraphNode* EdNode_B = Cast(B->GetOwningNode()); if (EdNode_A == nullptr || EdNode_B == nullptr) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, LOCTEXT("PinError", "Not a valid UComboActionGraphEdNode")); } FText ErrorMessage; if (A->Direction == EGPD_Input) { if (!EdNode_A->ComboActionGraphNode->CanCreateConnection(EdNode_B->ComboActionGraphNode, EGPD_Input, ErrorMessage)) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage); } } else { if (!EdNode_B->ComboActionGraphNode->CanCreateConnection(EdNode_A->ComboActionGraphNode, EGPD_Output, ErrorMessage)) { return FPinConnectionResponse(CONNECT_RESPONSE_DISALLOW, ErrorMessage); } } if (EdNode_A->ComboActionGraphNode->GetGraph()->bEdgeEnabled) { return FPinConnectionResponse(CONNECT_RESPONSE_MAKE_WITH_CONVERSION_NODE, LOCTEXT("PinConnect", "Connect nodes with edge")); } else { return FPinConnectionResponse(CONNECT_RESPONSE_MAKE, LOCTEXT("PinConnect", "Connect nodes")); } } bool UComboActionGraphSchema::CreateAutomaticConversionNodeAndConnections(UEdGraphPin* A, UEdGraphPin* B) const { UEdComboActionGraphNode *NodeA = Cast(A->GetOwningNode()); UEdComboActionGraphNode *NodeB = Cast(B->GetOwningNode()); if (NodeA == nullptr || NodeB == nullptr) return false; if (NodeA->GetInputPin() == nullptr || NodeA->GetOutputPin() == nullptr || NodeB->GetInputPin() == nullptr || NodeB->GetOutputPin() == nullptr) return false; UComboActionGraph* Graph = NodeA->ComboActionGraphNode->GetGraph(); FVector2D InitPos((NodeA->NodePosX + NodeB->NodePosX) / 2, (NodeA->NodePosY + NodeB->NodePosY) / 2); FAssetSchemaAction_ComboActionGraphSchema_NewEdge Action; Action.NodeTemplate = NewObject(NodeA->GetGraph()); Action.NodeTemplate->SetEdge(NewObject(Action.NodeTemplate, Graph->EdgeType)); UEdComboActionGraphEdge *EdgeNode = Cast(Action.PerformAction(NodeA->GetGraph(), nullptr, InitPos, false)); if (A->Direction == EEdGraphPinDirection::EGPD_Output) { EdgeNode->CreateConnections(NodeA, NodeB); } else { EdgeNode->CreateConnections(NodeB, NodeA); } return true; } FConnectionDrawingPolicy *UComboActionGraphSchema::CreateConnectionDrawingPolicy(int32 InBackLayerID, int32 InFrontLayerID, float InZoomFactor, const FSlateRect& InClippingRect, FSlateWindowElementList& InDrawElements, UEdGraph* InGraphObj) const { /* if (const UMounteaDialogueGraphEditorSettings* MounteaDialogueGraphEditorSettings = GetMutableDefault()) { if (MounteaDialogueGraphEditorSettings->AllowAdvancedWiring()) { return new FConnectionDrawingPolicy_AdvancedMounteaDialogueGraph(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj); } } */ //return new FConnectionDrawingPolicy_ComboActionGraph(InBackLayerID, InFrontLayerID, InZoomFactor, InClippingRect, InDrawElements, InGraphObj); return nullptr; } FLinearColor UComboActionGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const { return FColor::White; } void UComboActionGraphSchema::BreakNodeLinks(UEdGraphNode& TargetNode) const { const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakNodeLinks", "Break Node Links")); Super::BreakNodeLinks(TargetNode); } void UComboActionGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const { const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakPinLinks", "Break Pin Links")); Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation); } void UComboActionGraphSchema::BreakSinglePinLink(UEdGraphPin* SourcePin, UEdGraphPin* TargetPin) const { const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "GraphEd_BreakSinglePinLink", "Break Pin Link")); Super::BreakSinglePinLink(SourcePin, TargetPin); } UEdGraphPin *UComboActionGraphSchema::DropPinOnNode(UEdGraphNode* InTargetNode, const FName& InSourcePinName, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection) const { UEdComboActionGraphNode* EdNode = Cast(InTargetNode); switch (InSourcePinDirection) { case EEdGraphPinDirection::EGPD_Input: return EdNode->GetOutputPin(); case EEdGraphPinDirection::EGPD_Output: return EdNode->GetInputPin(); default: return nullptr; } } bool UComboActionGraphSchema::SupportsDropPinOnNode(UEdGraphNode* InTargetNode, const FEdGraphPinType& InSourcePinType, EEdGraphPinDirection InSourcePinDirection, FText& OutErrorMessage) const { return Cast(InTargetNode) != nullptr; } bool UComboActionGraphSchema::IsCacheVisualizationOutOfDate(int32 InVisualizationCacheID) const { return CurrentCacheRefreshID != InVisualizationCacheID; } int32 UComboActionGraphSchema::GetCurrentVisualizationCacheID() const { return CurrentCacheRefreshID; } void UComboActionGraphSchema::ForceVisualizationCacheClear() const { CurrentCacheRefreshID++; } void UComboActionGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const { Super::CreateDefaultNodesForGraph(Graph); } #undef LOCTEXT_NAMESPACE