438 lines
15 KiB
C++
438 lines
15 KiB
C++
// ©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<FString, uint32> LinkTitleCount;
|
|
|
|
FToolMenuSection &Section = Menu->FindOrAddSection("ComboActionGraphAssetGraphSchemaPinActions");
|
|
|
|
// Add all the links we could break from
|
|
for (TArray<class UEdGraphPin*>::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<UEdGraphPin*>(InGraphPin), *Links)));
|
|
}
|
|
}
|
|
|
|
void UComboActionGraphSchema::GetGraphContextActions(FGraphContextMenuBuilder &ContextMenuBuilder) const
|
|
{
|
|
UComboActionGraph *Graph = CastChecked<UComboActionGraph>(ContextMenuBuilder.CurrentGraph->GetOuter());
|
|
|
|
if (Graph->NodeType == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const bool bNoParent = (ContextMenuBuilder.FromPin == NULL);
|
|
|
|
FText AddToolTip = LOCTEXT("NewMoutneaDialogueGraphNodeTooltip", "Add Dialogue Node here");
|
|
|
|
TSet<TSubclassOf<UComboActionGraphNode>> 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<FAssetSchemaAction_ComboActionGraphSchema_NewNode> NewNodeAction(new FAssetSchemaAction_ComboActionGraphSchema_NewNode(NodeCategory, Desc, AddToolTip, 0));
|
|
NewNodeAction->NodeTemplate = NewObject<UEdComboActionGraphNode>(ContextMenuBuilder.OwnerOfTemporaries);
|
|
NewNodeAction->NodeTemplate->ComboActionGraphNode = NewObject<UComboActionGraphNode>(NewNodeAction->NodeTemplate, Graph->NodeType);
|
|
NewNodeAction->NodeTemplate->ComboActionGraphNode->Graph = Graph;
|
|
ContextMenuBuilder.AddAction(NewNodeAction);
|
|
|
|
Visited.Add(Graph->NodeType);
|
|
}
|
|
|
|
for (TObjectIterator<UClass> It; It; ++It)
|
|
{
|
|
if (It->IsChildOf(Graph->NodeType) && !It->HasAnyClassFlags(CLASS_Abstract) && !Visited.Contains(*It))
|
|
{
|
|
TSubclassOf<UComboActionGraphNode> 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<FAssetSchemaAction_ComboActionGraphSchema_NewNode> Action(new FAssetSchemaAction_ComboActionGraphSchema_NewNode(NodeCategory, Desc, AddToolTip, 0));
|
|
Action->NodeTemplate = NewObject<UEdComboActionGraphNode>(ContextMenuBuilder.OwnerOfTemporaries);
|
|
Action->NodeTemplate->ComboActionGraphNode = NewObject<UComboActionGraphNode>(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<UEdGraphPin*>(Context->Pin)));
|
|
}
|
|
else
|
|
{
|
|
((UComboActionGraphSchema *const)this)->GetBreakLinkToSubMenuActions(Menu, const_cast<UEdGraphPin*>(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<UEdComboActionGraphNode>(A->GetOwningNode());
|
|
UEdComboActionGraphNode* EdNode_B = Cast<UEdComboActionGraphNode>(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<UEdComboActionGraphNode>(A->GetOwningNode());
|
|
UEdComboActionGraphNode *NodeB = Cast<UEdComboActionGraphNode>(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<UEdComboActionGraphEdge>(NodeA->GetGraph());
|
|
Action.NodeTemplate->SetEdge(NewObject<UComboActionGraphEdge>(Action.NodeTemplate, Graph->EdgeType));
|
|
UEdComboActionGraphEdge *EdgeNode = Cast<UEdComboActionGraphEdge>(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<UMounteaDialogueGraphEditorSettings>())
|
|
{
|
|
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<UEdComboActionGraphNode>(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<UEdComboActionGraphNode>(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
|