A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
DialogueGraphNode.cpp
Go to the documentation of this file.
1// Copyright Csaba Molnar, Daniel Butum. All Rights Reserved.
2#include "DialogueGraphNode.h"
3
4#include "Editor/EditorEngine.h"
5#include "Framework/Commands/GenericCommands.h"
6#include "EdGraph/EdGraphNode.h"
7#include "Engine/Font.h"
8#include "Framework/MultiBox/MultiBoxBuilder.h"
9
10#if ENGINE_MINOR_VERSION >= 24
11#include "ToolMenu.h"
12#endif
13
15#include "DlgDialogue.h"
17#include "DialogueCommands.h"
18#include "DlgSystemSettings.h"
19
20#define LOCTEXT_NAMESPACE "DialogueGraphNode"
21
23// Begin UObject interface
25{
26 Super::PostLoad();
27
28 // Fixup any DialogueNode back pointers that may be out of date
29 if (DialogueNode)
30 {
31 DialogueNode->SetGraphNode(this);
32 DialogueNode->SetFlags(RF_Transactional);
33 }
34}
35
37{
39
40 // Make sure this DialogueNode is owned by the Dialogue it's being pasted into.
41 // The paste can come from another Dialogue
43}
44
45void UDialogueGraphNode::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
46{
47 Super::PostEditChangeProperty(PropertyChangedEvent);
48 if (!PropertyChangedEvent.Property)
49 {
50 return;
51 }
52
53 CheckAll();
55}
56
57void UDialogueGraphNode::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent)
58{
59 Super::PostEditChangeChainProperty(PropertyChangedEvent);
60}
61
62bool UDialogueGraphNode::Modify(bool bAlwaysMarkDirty)
63{
64 if (!CanModify())
65 {
66 return false;
67 }
68
69 bool bWasModified = Super::Modify(bAlwaysMarkDirty);
70
71 // Notify the Dialogue structure and the edge nodes of modification
72 if (DialogueNode)
73 {
74 bWasModified = bWasModified && DialogueNode->Modify(bAlwaysMarkDirty);
75 }
76
77 // Special case when this method is called when the engine is starting
78 if (HasOutputPin())
79 {
80 // Can happen when copy pasting nodes
81 constexpr bool bCheckParent = false;
82 for (UDialogueGraphNode_Edge* EdgeNode : GetChildEdgeNodes(bCheckParent))
83 {
84 bWasModified = bWasModified && EdgeNode->SuperModify();
85 }
86 }
87
88 return bWasModified;
89}
90// End UObject interface
92
94// Begin UEdGraphNode interface
95FText UDialogueGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
96{
97 if (NodeIndex == INDEX_NONE)
98 {
99 return Super::GetNodeTitle(TitleType);
100 }
101
102 const FString FullString = DialogueNode->GetNodeParticipantName().ToString();
103 // Display the full title
104 return FText::FromString(FullString);
105}
106
108{
109 Super::PrepareForCopying();
110
111 // Temporarily take ownership of the DialogueNode, so that it is not deleted when cutting
112 if (DialogueNode)
113 {
114 DialogueNode->Rename(nullptr, this, REN_DontCreateRedirectors);
115 }
116}
117
119{
120 Super::PostCopyNode();
121 // Make sure the DialogueNode goes back to being owned by the Dialogue after copying.
123}
124
126{
127 // Default the node to searching for an excerpt named for the C++ node class name, including the U prefix.
128 // This is done so that the excerpt name in the doc file can be found by find-in-files when searching for the full class name.
129// UClass* MyClass = DialogueNode != nullptr ? DialogueNode->GetClass() : this->GetClass();
130// return FString::Printf(TEXT("%s%s"), MyClass->GetPrefixCPP(), *MyClass->GetName());
131 return "";
132}
133
135{
136 FFormatNamedArguments Args;
137 Args.Add(TEXT("ParticipantName"), DialogueNode ? FText::FromName(DialogueNode->GetNodeParticipantName()) : FText::GetEmpty());
138 Args.Add(TEXT("NodeType"), DialogueNode ? FText::FromString(DialogueNode->GetNodeTypeString()) : FText::GetEmpty());
139 Args.Add(TEXT("Description"), DialogueNode ? FText::FromString(DialogueNode->GetDesc()) : FText::GetEmpty());
140 return FText::Format(LOCTEXT("GraphNodeTooltip", "ParticipantName = {ParticipantName}\nType = {NodeType}\n{Description}"), Args);
141}
142
144{
145 // NOTE: GraphNode.OutputPin.LinkedTo are kept in sync with the DialogueNode.Children
146 check(Pin->GetOwningNode() == this);
147 check(DialogueNode->GetNodeOpenChildren_DEPRECATED().Num() == 0);
148
149 // Input pins are ignored, as they are not reliable source of information, each node should only work with its output pins
150 if (Pin->Direction == EGPD_Input)
151 {
152 return;
153 }
154
155 // Handle Output Pin (this node)
156 check(Pin->Direction == EGPD_Output);
157 const UEdGraphPin* OutputPin = GetOutputPin();
158 // Only one Pin, and that pin should be our output pinOutputPin
159 check(Pin == OutputPin);
160
161 const int32 DialogueNodeChildrenNum = DialogueNode->GetNodeChildren().Num();
162 const int32 GraphNodeChildrenNum = OutputPin->LinkedTo.Num();
163
164 // Nothing added/removed, maybe something replaced?
165 if (DialogueNodeChildrenNum == GraphNodeChildrenNum)
166 {
167#if DO_CHECK
169 check(DiffResult.Index == INDEX_NONE);
171#endif
172 }
173 // Some link was added/removed
174 else if (DialogueNodeChildrenNum < GraphNodeChildrenNum)
175 {
176 // Output link added, extend the number of children
177 // Not handled here, as everytime we just add at the end of the array.
178 // See UDialogueGraphNode_Edge::CreateConnections
179 }
180 else
181 {
182 // One Output link removed, reduce the number of linked children
183 check(DialogueNodeChildrenNum > GraphNodeChildrenNum);
184 if (GraphNodeChildrenNum == 0)
185 {
186 // All nodes were removed, node is most likely going to be removed
188 }
189 else
190 {
191 // Only one
195 DialogueNode->RemoveChildAt(DiffResult.Index);
196 }
197 }
198}
199
200#if ENGINE_MINOR_VERSION >= 24
201void UDialogueGraphNode::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
202{
203 // These actions (commands) are handled and registered in the FDialogueEditor class
204 if (Context->Node && !Context->bIsDebugging && !IsRootNode())
205 {
206 // Menu for right clicking on node
207 FToolMenuSection& Section = Menu->AddSection("DialogueGraphNode_BaseNodeEditCRUD");
209 {
210 Section.AddMenuEntry(FDialogueCommands::Get().ConvertSpeechSequenceNodeToSpeechNodes);
211 }
212 if (IsSpeechNode()
214 {
215 Section.AddMenuEntry(FDialogueCommands::Get().ConvertSpeechNodesToSpeechSequence);
216 }
217
218 Section.AddMenuEntry(FGenericCommands::Get().Delete);
219// Section.AddMenuEntry(FGenericCommands::Get().Cut);
220 Section.AddMenuEntry(FGenericCommands::Get().Copy);
221// Section.AddMenuEntry(FGenericCommands::Get().Duplicate);
222
223 }
224}
225
226#else
227
228void UDialogueGraphNode::GetContextMenuActions(const FGraphNodeContextMenuBuilder& Context) const
229{
230 // These actions (commands) are handled and registered in the FDialogueEditor class
231 if (Context.Node && !Context.bIsDebugging && !IsRootNode())
232 {
233 // Menu for right clicking on node
234 Context.MenuBuilder->BeginSection("DialogueGraphNode_BaseNodeEditCRUD");
235 {
237 {
238 Context.MenuBuilder->AddMenuEntry(FDialogueCommands::Get().ConvertSpeechSequenceNodeToSpeechNodes);
239 }
240 if (IsSpeechNode()
242 {
243 Context.MenuBuilder->AddMenuEntry(FDialogueCommands::Get().ConvertSpeechNodesToSpeechSequence);
244 }
245
246 Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Delete);
247// Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Cut);
248 Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Copy);
249// Context.MenuBuilder->AddMenuEntry(FGenericCommands::Get().Duplicate);
250 }
251 Context.MenuBuilder->EndSection();
252 }
253}
254#endif // ENGINE_MINOR_VERSION >= 24
255
256void UDialogueGraphNode::AutowireNewNode(UEdGraphPin* FromPin)
257{
258 // No context given, simply return
259 if (FromPin == nullptr)
260 {
261 return;
262 }
263
264 // FromPin should not belong to this node but to the node that spawned this node.
265 check(FromPin->GetOwningNode() != this);
266 check(FromPin->Direction == EGPD_Output);
267
268 UEdGraphPin* InputpIn = GetInputPin();
270
271 // auto-connect from dragged pin to first compatible pin on the new node
272 verify(Schema->TryCreateConnection(FromPin, InputpIn));
273 FromPin->GetOwningNode()->NodeConnectionListChanged();
274}
275// End UEdGraphNode interface
277
279// Begin UDialogueGraphNode_Base interface
281{
282 if (NodeIndex == INDEX_NONE)
283 {
284 return FLinearColor::Black;
285 }
286
287 const UDlgSystemSettings* Settings = GetDefault<UDlgSystemSettings>();
288 if (IsSpeechNode())
289 {
291 {
292 return Settings->VirtualParentNodeColor;
293 }
294
295 return Settings->SpeechNodeColor;
296 }
297
298 if (IsSelectorNode())
299 {
301 {
302 return Settings->SelectorFirstNodeColor;
303 }
304
306 {
307 return Settings->SelectorRandomNodeColor;
308 }
309
310 // should not reach here, like never
311 checkNoEntry();
312 }
313
315 {
316 return Settings->SpeechSequenceNodeColor;
317 }
318
319 if (IsEndNode())
320 {
321 return Settings->EndNodeColor;
322 }
323
324 return FLinearColor::Black;
325}
326
328{
329 for (UDialogueGraphNode* ChildNode : GetChildNodes())
330 {
331 if (ChildNode == TargetNode)
332 {
333 return true;
334 }
335 }
336
337 return false;;
338}
339
345// End UDialogueGraphNode_Base interface
347
349// Begin own functions
351{
352 if (!IsValid(DialogueNode))
353 {
354 return false;
355 }
356
357 // Try simple node
359 {
360 return true;
361 }
362
363 // Speech sequence node
365 {
366 for (const FDlgSpeechSequenceEntry & Sequence : GetDialogueNode<UDlgNode_SpeechSequence>().GetNodeSpeechSequence())
367 {
368 if (Sequence.VoiceSoundWave != nullptr || Sequence.VoiceDialogueWave != nullptr)
369 {
370 return true;
371 }
372 }
373 }
374
375 return false;
376}
377
379{
380 if (!IsValid(DialogueNode))
381 {
382 return false;
383 }
384
385 // Try simple node
386 if (DialogueNode->GetNodeGenericData() != nullptr)
387 {
388 return true;
389 }
390
391 // Speech sequence node
393 {
394 for (const FDlgSpeechSequenceEntry & Sequence : GetDialogueNode<UDlgNode_SpeechSequence>().GetNodeSpeechSequence())
395 {
396 if (Sequence.GenericData != nullptr)
397 {
398 return true;
399 }
400 }
401 }
402
403 return false;
404}
405
407{
409 checkf(Dialogue->GetNodes()[InIndex] == InNode, TEXT("The selected index = %d and with the Node provided does not match the Node from the Dialogue"), InIndex);
410
411 SetDialogueNodeIndex(InIndex);
412 SetDialogueNode(InNode);
413}
414
416{
417 const TArray<UDialogueGraphNode_Edge*> GraphNodeEdges = GetChildEdgeNodes();
418 for (int32 EdgeIndex = 0; EdgeIndex < GraphNodeEdges.Num(); EdgeIndex++)
419 {
420 const UDialogueGraphNode_Edge* GraphEdge = GraphNodeEdges[EdgeIndex];
421 if (!GraphEdge->HasChildNode())
422 {
423 continue;
424 }
425
426 if (GraphEdge->GetChildNode()->GetDialogueNodeIndex() == ChildNodeIndex)
427 {
428 return EdgeIndex;
429 }
430 }
431
432 return INDEX_NONE;
433}
434
435void UDialogueGraphNode::SetEdgeTargetIndexAt(int32 EdgeIndex, int32 NewTargetIndex)
436{
437 check(NewTargetIndex > INDEX_NONE);
438 const TArray<UDialogueGraphNode_Edge*> GraphNodeEdges = GetChildEdgeNodes();
439 check(DialogueNode->GetNodeChildren().Num() == GraphNodeEdges.Num());
440 check(GraphNodeEdges.IsValidIndex(EdgeIndex));
441
442 DialogueNode->GetSafeMutableNodeChildAt(EdgeIndex)->TargetIndex = NewTargetIndex;
443 GraphNodeEdges[EdgeIndex]->SetDialogueEdgeTargetIndex(NewTargetIndex);
444}
445
446void UDialogueGraphNode::SetEdgeTextAt(int32 EdgeIndex, const FText& NewText)
447{
448 const TArray<UDialogueGraphNode_Edge*> GraphNodeEdges = GetChildEdgeNodes();
449 check(DialogueNode->GetNodeChildren().Num() == GraphNodeEdges.Num());
450 check(GraphNodeEdges.IsValidIndex(EdgeIndex));
451
453 Edge->SetText(NewText);
454 GraphNodeEdges[EdgeIndex]->SetDialogueEdgeText(NewText);
455}
456
457void UDialogueGraphNode::SetEdges(const TArray<FDlgEdge>& InEdges)
458{
459 const TArray<UDialogueGraphNode_Edge*> GraphNodeEdges = GetChildEdgeNodes();
460 check(InEdges.Num() == GraphNodeEdges.Num());
461
462 // Set the edges on the Dialogue
464
465 // Set the edges on the edge nodes
466 for (int32 EdgeIndex = 0, EdgesNum = InEdges.Num(); EdgeIndex < EdgesNum; EdgeIndex++)
467 {
468 GraphNodeEdges[EdgeIndex]->SetDialogueEdge(InEdges[EdgeIndex]);
469 }
470}
471
473{
474 const TArray<UDialogueGraphNode_Edge*> GraphNodeEdges = GetChildEdgeNodes();
475 const int32 NumEdges = DialogueNode->GetNumNodeChildren();
476 check(NumEdges == GraphNodeEdges.Num());
477
478 for (int32 EdgeIndex = 0; EdgeIndex < NumEdges; EdgeIndex++)
479 {
480 GraphNodeEdges[EdgeIndex]->SetDialogueEdge(DialogueNode->GetNodeChildAt(EdgeIndex));
481 }
482}
483
485{
487
488 // Is Orphan node
489 if (!IsRootNode() && GetInputPin()->LinkedTo.Num() == 0)
490 {
491 SetCompilerWarningMessage(TEXT("Node has no input connections (orphan). It will not be accessible from anywhere"));
492 }
493 else if (DialogueNode->GetNodeOpenChildren_DEPRECATED().Num() > 0)
494 {
495 // Has open children :O
496 SetCompilerWarningMessage(TEXT("Node has invalid (open) edges in its DialogueNode"));
497 }
498}
499
501{
502 constexpr int32 EstimatedCharWidth = 6;
503 // Check which is bigger, and use that
504 const FString NodeTitle = GetNodeTitle(ENodeTitleType::FullTitle).ToString();
505 const FString NodeText = DialogueNode->GetNodeText().ToString();
506
507 int32 Result;
508 FString ResultFromString;
509 if (NodeTitle.Len() > NodeText.Len())
510 {
511 Result = NodeTitle.Len() * EstimatedCharWidth;
512 ResultFromString = NodeTitle;
513 }
514 else
515 {
516 Result = NodeText.Len() * EstimatedCharWidth;
517 ResultFromString = NodeText;
518 }
519
520 if (const UFont* Font = GetDefault<UEditorEngine>()->EditorFont)
521 {
522 Result = Font->GetStringSize(*ResultFromString);
523 }
524
525 return Result;
526}
527
529{
530#if DO_CHECK
531 if (!IsRootNode())
532 {
534 checkf(Dialogue->GetNodes()[NodeIndex] == DialogueNode, TEXT("The NodeIndex = %d with DialogueNode does not match the Node from the Dialogue at the same index"), NodeIndex);
535 }
536#endif
537}
538
540{
541#if DO_CHECK
542 const UEdGraphPin* OutputPin = GetOutputPin();
543 const TArray<FDlgEdge>& NodeEdges = DialogueNode->GetNodeChildren();
544 const int32 EdgesNum = NodeEdges.Num();
545 const int32 PinsLinkedToNum = OutputPin->LinkedTo.Num();
546 check(PinsLinkedToNum == GetChildNodes().Num());
547 checkf(
548 EdgesNum == PinsLinkedToNum,
549 TEXT("The node with OLD index = %d does not have the number of PinsLinkedToNum (%d) equal to the EdgesNum (%d) in it's FDlgNode (DialogueNode)"),
550 NodeIndex, PinsLinkedToNum, EdgesNum
551 );
552
553 const int32 OpenEdgesNum = DialogueNode->GetNodeOpenChildren_DEPRECATED().Num();
554 checkf(
555 OpenEdgesNum == 0,
556 TEXT("The node with OLD index = %d has open output pins (%d), this should not be allowed"),
557 NodeIndex, OpenEdgesNum
558 );
559
560 checkf(
561 DialogueNode->GetGraphNode() == this,
562 TEXT("The node with OLD index = %d does not have the DialogueNode.GraphNode == this (graph node)"),
564 );
565
566 // Check if Edges have the same openness as the output pins
567 if (bStrictCheck)
568 {
569 for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
570 {
571 FString OutMessage;
572 const FDlgEdge& Edge = NodeEdges[EdgeIndex];
573 if (!DoesEdgeMatchEdgeIndex(Edge, EdgeIndex, OutMessage))
574 {
575 checkf(
576 false,
577 TEXT("The node with OLD index = %d does not have the dialogue edge at index = %d matching the Edge in the GraphNode. Error message = `%s`"),
578 NodeIndex, EdgeIndex, *OutMessage
579 );
580 }
581 }
582 }
583#endif
584}
585
586TArray<UDialogueGraphNode*> UDialogueGraphNode::GetParentNodes() const
587{
588 // (input pin) ParentNode (output pin) -> (input pin) EdgeNode aka ParentEdgeConnection (output pin) -> (input pin) ThisNode (output pin)
589 TArray<UDialogueGraphNode*> ParentNodes;
590 for (const UDialogueGraphNode_Edge* ParentEdgeConnection : GetParentEdgeNodes())
591 {
592 ParentNodes.Add(ParentEdgeConnection->GetParentNode());
593 }
594
595 return ParentNodes;
596}
597
598TArray<UDialogueGraphNode*> UDialogueGraphNode::GetChildNodes() const
599{
600 // (input pin) ThisNode (output pin) -> (input pin) EdgeNode aka ChildEdgeConnection (output pin) -> (input pin) ChildNode (output pin)
601 TArray<UDialogueGraphNode*> ChildNodes;
602 for (const UDialogueGraphNode_Edge* ChildEdgeConnection : GetChildEdgeNodes())
603 {
604 ChildNodes.Add(ChildEdgeConnection->GetChildNode());
605 }
606
607 return ChildNodes;
608}
609
610TArray<UDialogueGraphNode_Edge*> UDialogueGraphNode::GetParentEdgeNodes(bool bCheckChild /*= true*/) const
611{
612 // (input pin) ParentNode (output pin) -> (input pin) EdgeNode aka ParentEdgeConnection (EdgeOutputPin) -> (input pin) ThisNode (output pin)
613 TArray<UDialogueGraphNode_Edge*> ParentEdgeNodes;
614 for (const UEdGraphPin* EdgeOutputPin : GetInputPin()->LinkedTo)
615 {
616 UDialogueGraphNode_Edge* ParentEdgeConnection = CastChecked<UDialogueGraphNode_Edge>(EdgeOutputPin->GetOwningNode());
617
618#if DO_CHECK
619 if (bCheckChild)
620 {
621 check(ParentEdgeConnection->GetChildNode() == this);
622 }
623#endif
624
625 ParentEdgeNodes.Add(ParentEdgeConnection);
626 }
627
628 return ParentEdgeNodes;
629}
630
631TArray<UDialogueGraphNode_Edge*> UDialogueGraphNode::GetChildEdgeNodes(bool bCheckParent /*= true*/) const
632{
633 // (input pin) ThisNode (output pin) -> (EdgeInputPin) EdgeNode aka ChildEdgeConnection (output pin) -> (input pin) ChildNode (output pin)
634 TArray<UDialogueGraphNode_Edge*> ChildEdgeNodes;
635 for (const UEdGraphPin* EdgeInputPin : GetOutputPin()->LinkedTo)
636 {
637 UDialogueGraphNode_Edge* ChildEdgeConnection = CastChecked<UDialogueGraphNode_Edge>(EdgeInputPin->GetOwningNode());
638
639#if DO_CHECK
640 if (bCheckParent)
641 {
642 check(ChildEdgeConnection->GetParentNode() == this);
643 }
644#endif
645
646 ChildEdgeNodes.Add(ChildEdgeConnection);
647 }
648
649 return ChildEdgeNodes;
650}
651
653{
654 if (!HasOutputPin())
655 {
656 return false;
657 }
658
659 // (input pin) ThisNode (output pin) -> (EdgeInputPin) EdgeNode aka ChildEdgeConnection (output pin) -> (input pin) ChildNode (output pin)
660 for (const UEdGraphPin* EdgeInputPin : GetOutputPin()->LinkedTo)
661 {
662 if (const UDialogueGraphNode_Edge* ChildEdgeConnection = Cast<UDialogueGraphNode_Edge>(EdgeInputPin->GetOwningNodeUnchecked()))
663 {
664 if (ChildEdgeToFind == ChildEdgeConnection)
665 {
666 return true;
667 }
668 }
669 }
670
671 return false;
672}
673
675{
676 if (!HasInputPin())
677 {
678 return false;
679 }
680
681 // (input pin) ParentNode (output pin) -> (input pin) EdgeNode aka ParentEdgeConnection (EdgeOutputPin) -> (input pin) ThisNode (output pin)
682 for (const UEdGraphPin* EdgeOutputPin : GetInputPin()->LinkedTo)
683 {
684 if (const UDialogueGraphNode_Edge* ParentEdgeConnection = Cast<UDialogueGraphNode_Edge>(EdgeOutputPin->GetOwningNodeUnchecked()))
685 {
686 if (ParentEdgeToFind == ParentEdgeConnection)
687 {
688 return true;
689 }
690 }
691 }
692
693 return false;
694}
695
697{
698 FORCEINLINE bool operator()(const TPair<UEdGraphPin*, FDlgEdge>& A, const TPair<UEdGraphPin*, FDlgEdge>& B) const
699 {
700 const UEdGraphNode* NodeA = A.Key->GetOwningNode();
701 const UEdGraphNode* NodeB = B.Key->GetOwningNode();
702 return NodeA->NodePosX != NodeB->NodePosX ? NodeA->NodePosX < NodeB->NodePosX : NodeA->NodePosY < NodeB->NodePosY;
703 }
704};
705
707{
708 // Holds an array of synced pairs, each pair corresponds to a linked to output pin and corresponding dialogue edge
709 TArray<TPair<UEdGraphPin*, FDlgEdge>> SyncedArray;
710
711 UEdGraphPin* OutputPin = GetOutputPin();
712 const TArray<UEdGraphPin*> ChildPins = OutputPin->LinkedTo;
713 const TArray<FDlgEdge>& ChildDialogueNodeEdges = DialogueNode->GetNodeChildren();
714 check(ChildPins.Num() == ChildDialogueNodeEdges.Num());
715
716 // Step 1. Construct the synced array
717 const int32 ChildrenNum = ChildPins.Num();
718 SyncedArray.Reserve(ChildrenNum);
719 for (int32 ChildIndex = 0; ChildIndex < ChildrenNum; ChildIndex++)
720 {
721 SyncedArray.Emplace(ChildPins[ChildIndex], ChildDialogueNodeEdges[ChildIndex]);
722 }
723
724 // Step 2. Sort the synced array
725 SyncedArray.Sort(FCompareNodeXLocation());
726
727 // Step 3. Reconstruct the output pins/edges from the sorted synced array
728 OutputPin->LinkedTo.Empty();
730 for (const TPair<UEdGraphPin*, FDlgEdge>& SyncedPair : SyncedArray)
731 {
732 OutputPin->LinkedTo.Add(SyncedPair.Key);
733 DialogueNode->AddNodeChild(SyncedPair.Value);
734 }
735}
736
737void UDialogueGraphNode::OnDialogueNodePropertyChanged(const FPropertyChangedEvent& PropertyChangedEvent, int32 EdgeIndexChanged)
738{
739 if (!PropertyChangedEvent.Property)
740 {
741 return;
742 }
743
744 // Keep in sync the Edge Graph Node with the modified Edge from the Details Panel of the Parent Node
745 const TArray<FDlgEdge>& DialogueEdges = DialogueNode->GetNodeChildren();
746 if (EdgeIndexChanged != INDEX_NONE)
747 {
748 // We know what edge changed, update that, most likelye and add/insert/modify
749 const TArray<UDialogueGraphNode_Edge*> ChildEdgeNodes = GetChildEdgeNodes();
750 check(ChildEdgeNodes.IsValidIndex(EdgeIndexChanged))
751
752 // Update the proxy edge node with the new edge
753 ChildEdgeNodes[EdgeIndexChanged]->SetDialogueEdge(DialogueEdges[EdgeIndexChanged]);
754 }
755 else
756 {
757 // We do not know that edge, update all, most likely remove
758 const TArray<UDialogueGraphNode_Edge*> ChildEdgeNodes = GetChildEdgeNodes();
759 for (int32 EdgeIndex = 0, EdgesNum = ChildEdgeNodes.Num(); EdgeIndex < EdgesNum; EdgeIndex++)
760 {
761 // Update the proxy edge node with the new edge
762 ChildEdgeNodes[EdgeIndex]->SetDialogueEdge(DialogueEdges[EdgeIndex]);
763 }
764 }
765}
766
768{
769 if (!DialogueNode)
770 {
771 return;
772 }
773
775
776 // Ensures DialogueNode is owned by the Dialogue
777 if (DialogueNode->GetOuter() != Dialogue)
778 {
779 DialogueNode->Rename(nullptr, Dialogue, REN_DontCreateRedirectors);
780 }
781
782 // Set up the back pointer for newly created dialogue node nodes
783 DialogueNode->SetGraphNode(this);
784 DialogueNode->SetFlags(RF_Transactional);
785}
786
788{
790 const UEdGraphPin* OutputPin = GetOutputPin();
791 const TArray<FDlgEdge>& NodeEdges = GetDialogueNode().GetNodeChildren();
792 const int32 EdgesNum = NodeEdges.Num();
793 const int32 LinkedToNum = OutputPin->LinkedTo.Num();
794 const bool bAreLengthsEqual = EdgesNum == LinkedToNum;
795
796 // If EdgesNum > LinkedToNum, we are finding the (only) edge that is different from the linked pins
797 // If LinkedToNum > EdgesNum, we are finding the (only) linked pin that is different from the edges
798 if (!bAreLengthsEqual && FMath::Abs(EdgesNum - LinkedToNum) != 1)
799 {
800 UE_LOG(LogDlgSystemEditor, Error,
801 TEXT("FindDifferenceBetweenNodeEdgesAndLinkedToPins: EdgesNum (%d) != LinkedToNum (%d), but function was called in the wrong context"),
802 EdgesNum, LinkedToNum);
804 return Result;
805 }
806
807 // Try to find difference in common areas of both arrays
808 for (int32 Index = 0; Index < EdgesNum && Index < LinkedToNum; Index++)
809 {
810 const FDlgEdge& Edge = NodeEdges[Index];
811 if (!DoesEdgeMatchEdgeIndex(Edge, Index, Result.Message))
812 {
813 Result.Index = Index;
815 // Assume next Edge matches index
816#if DO_CHECK
817 if (Index + 1 < EdgesNum && Index + 1 < LinkedToNum)
818 {
819 check(DoesEdgeMatchEdgeIndex(NodeEdges[Index + 1], Index, Result.Message));
820 }
821#endif
822
823 return Result;
824 }
825 }
826
827 // Did not find any result in the common indices area :(
828 if (!bAreLengthsEqual && Result.Index == INDEX_NONE)
829 {
830 if (LinkedToNum > EdgesNum)
831 {
832 // Because LinkedToNum > EdgesNum, it means that the last edge was removed OR a pin connection was added
833 Result.Index = LinkedToNum - 1;
835 }
836 else
837 {
838 check(EdgesNum > LinkedToNum);
839 // Because EdgesNum > LinkedToNum, it means that the last linked to pin connection was removed OR an edge was added
840 Result.Index = EdgesNum - 1;
842 }
843 }
844
845 return Result;
846}
847
848bool UDialogueGraphNode::DoesEdgeMatchEdgeIndex(const FDlgEdge& Edge, int32 EdgeIndex, FString& OutMessage) const
849{
850 // (input pin) ThisNode (ThisOutputPin) -> (EdgeInputPin) ChildEdgeConnection (output pin) -> (input pin) ChildNode (output pin)
851 check(Edge.IsValid());
852 UEdGraphPin* ThisOutputPin = GetOutputPin();
853 if (!ThisOutputPin->LinkedTo.IsValidIndex(EdgeIndex))
854 {
855 OutMessage = FString::Printf(TEXT("The provided EdgeIndex = %d is not a valid index in the ThisOutputPin->LinkedTo Array"), EdgeIndex);
856 return false;
857 }
858
859 // Check if Edge.TargetIndex matches the NodeIndex of the ChildNode
860 const UEdGraphPin* EdgeInputPin = ThisOutputPin->LinkedTo[EdgeIndex];
861 check(EdgeInputPin->Direction == EGPD_Input);
862
863 // Walk to child
864 const UDialogueGraphNode_Edge* ChildEdgeConnection = CastChecked<UDialogueGraphNode_Edge>(EdgeInputPin->GetOwningNode());
865 const UDialogueGraphNode* ChildNode = ChildEdgeConnection->GetChildNode();
866 check(ChildNode != this);
867
868 // Edge differs :(
869 if (Edge != ChildEdgeConnection->GetDialogueEdge())
870 {
871 OutMessage = TEXT("The provided Edge does not match the ChildEdgeConnection.Edge");
872 return false;
873 }
874
875 // Target node Index differs :(
876 if (Edge.TargetIndex != ChildNode->GetDialogueNodeIndex())
877 {
878 OutMessage = FString::Printf(
879 TEXT("The provided Edge.TargetIndex = %d is DIFFERENT from the ChildNode.NodeIndex = %d"),
880 Edge.TargetIndex, ChildNode->GetDialogueNodeIndex()
881 );
882 return false;
883 }
884
885 return true;
886}
887// End own functions
889
890#undef LOCTEXT_NAMESPACE
static bool CanConvertSpeechNodesToSpeechSequence(const TSet< UObject * > &SelectedNodes, TArray< UDialogueGraphNode * > &OutSelectedGraphNodes)
static const TSet< UObject * > GetSelectedNodes(const UEdGraph *Graph)
void SetCompilerWarningMessage(FString Message)
UDlgDialogue * GetDialogue() const
UEdGraphPin * GetOutputPin() const
const UDialogueGraphSchema * GetDialogueGraphSchema() const
UEdGraphPin * GetInputPin() const
UDialogueGraphNode * GetChildNode() const
const FDlgEdge & GetDialogueEdge() const
UDialogueGraphNode * GetParentNode() const
void CheckDialogueNodeIndexMatchesNode() const
int32 EstimateNodeWidth() const
bool HasParentEdgeNode(const UDialogueGraphNode_Edge *ParentEdgeToFind) const
bool DoesEdgeMatchEdgeIndex(const FDlgEdge &Edge, int32 EdgeIndex, FString &OutMessage) const
void PinConnectionListChanged(UEdGraphPin *Pin) override
void SetDialogueNodeDataChecked(int32 InIndex, UDlgNode *InNode)
virtual int32 GetDialogueNodeIndex() const
bool IsSelectorNode() const
void CheckDialogueNodeSyncWithGraphNode(bool bStrictCheck=false) const
void AutowireNewNode(UEdGraphPin *FromPin) override
virtual void SetDialogueNode(UDlgNode *InNode)
int32 GetChildEdgeIndexForChildNodeIndex(int32 ChildNodeIndex) const
void SetEdgeTextAt(int32 EdgeIndex, const FText &NewText)
TArray< UDialogueGraphNode * > GetChildNodes() const
bool HasVoicePropertiesSet() const
bool IsSelectorFirstNode() const
void OnDialogueNodePropertyChanged(const FPropertyChangedEvent &PropertyChangedEvent, int32 EdgeIndexChanged)
bool HasChildEdgeNode(const UDialogueGraphNode_Edge *ChildEdgeToFind) const
TArray< UDialogueGraphNode * > GetParentNodes() const
virtual void SetDialogueNodeIndex(int32 InIndex)
FLinearColor GetNodeBackgroundColor() const override
int32 NodeIndex
UPROPERTY(VisibleAnywhere, Category = DialogueGraphNode)
bool HasOutputConnectionToNode(const UEdGraphNode *TargetNode) const override
bool IsVirtualParentNode() const
const DlgNodeType & GetDialogueNode() const
void CheckAll() const override
void PostEditChangeProperty(FPropertyChangedEvent &PropertyChangedEvent) override
void RegisterListeners() override
void PostCopyNode() override
virtual bool IsRootNode() const
TArray< UDialogueGraphNode_Edge * > GetChildEdgeNodes(bool bCheckParent=true) const
void PostEditImport() override
bool IsSelectorRandomNode() const
TArray< UDialogueGraphNode_Edge * > GetParentEdgeNodes(bool bCheckChild=true) const
void PostLoad() override
FText GetNodeTitle(ENodeTitleType::Type TitleType) const override
void GetContextMenuActions(const FGraphNodeContextMenuBuilder &Context) const override
void PostEditChangeChainProperty(struct FPropertyChangedChainEvent &PropertyChangedEvent) override
const FDiffNodeEdgeLinkedToPinResult FindDifferenceBetweenNodeEdgesAndLinkedToPins() const
UDlgNode * DialogueNode
UPROPERTY(EditAnywhere, Instanced, Category = DialogueGraphNode, Meta = (ShowOnlyInnerProperties))
bool IsSpeechSequenceNode() const
bool Modify(bool bAlwaysMarkDirty=true) override
FString GetDocumentationExcerptName() const override
void PrepareForCopying() override
FText GetTooltipText() const override
void SetEdgeTargetIndexAt(int32 EdgeIndex, int32 NewTargetIndex)
void SetEdges(const TArray< FDlgEdge > &InEdges)
bool TryCreateConnection(UEdGraphPin *PinA, UEdGraphPin *PinB) const override
UCLASS(BlueprintType, Meta = (DisplayThumbnail = "true"))
Definition DlgDialogue.h:85
UCLASS(BlueprintType, Abstract, EditInlineNew, ClassGroup = "Dialogue")
Definition DlgNode.h:40
virtual const FText & GetNodeText() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:265
USoundWave * GetNodeVoiceSoundWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.cpp:360
virtual FDlgEdge * GetSafeMutableNodeChildAt(int32 EdgeIndex)
Definition DlgNode.h:213
const TArray< int32 > GetNodeOpenChildren_DEPRECATED() const
Definition DlgNode.cpp:242
FDialogueNodePropertyChanged OnDialogueNodePropertyChanged
Definition DlgNode.h:87
virtual const TArray< FDlgEdge > & GetNodeChildren() const
Gets this nodes children (edges) as a const/mutable array.
Definition DlgNode.h:184
virtual int32 GetNumNodeChildren() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:191
virtual FName GetNodeParticipantName() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:127
virtual const FDlgEdge & GetNodeChildAt(int32 EdgeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:197
virtual void RemoveChildAt(int32 EdgeIndex)
Definition DlgNode.h:203
virtual UObject * GetNodeGenericData() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:317
FString GetDesc() override
Definition DlgNode.h:49
virtual UDialogueWave * GetNodeVoiceDialogueWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:302
virtual void AddNodeChild(const FDlgEdge &InChild)
Definition DlgNode.h:200
virtual void SetNodeChildren(const TArray< FDlgEdge > &InChildren)
Definition DlgNode.h:185
virtual void RemoveAllChildren()
Definition DlgNode.h:210
UCLASS(Config = Engine, DefaultConfig, meta = (DisplayName = "Dialogue System Settings"))
FLinearColor SelectorFirstNodeColor
UPROPERTY(Category = "Graph Node Color", Config, EditAnywhere)
FLinearColor EndNodeColor
UPROPERTY(Category = "Graph Node Color", Config, EditAnywhere)
FLinearColor SpeechSequenceNodeColor
UPROPERTY(Category = "Graph Node Color", Config, EditAnywhere)
FLinearColor VirtualParentNodeColor
UPROPERTY(Category = "Graph Node Color", Config, EditAnywhere)
FLinearColor SelectorRandomNodeColor
UPROPERTY(Category = "Graph Node Color", Config, EditAnywhere)
FLinearColor SpeechNodeColor
UPROPERTY(Category = "Graph Node Color", Config, EditAnywhere)
FORCEINLINE bool operator()(const TPair< UEdGraphPin *, FDlgEdge > &A, const TPair< UEdGraphPin *, FDlgEdge > &B) const
USTRUCT(BlueprintType)
Definition DlgEdge.h:25
int32 TargetIndex
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Dialogue|Edge", Meta = (ClampMin = -1))
Definition DlgEdge.h:126
void SetText(const FText &NewText)
Definition DlgEdge.h:79
bool IsValid() const
Definition DlgEdge.h:107
USTRUCT(BlueprintType)