A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
DlgContext.cpp
Go to the documentation of this file.
1// Copyright Csaba Molnar, Daniel Butum. All Rights Reserved.
2#include "DlgContext.h"
3
4#include "DlgConstants.h"
5#include "Nodes/DlgNode.h"
6#include "Nodes/DlgNode_End.h"
9#include "DlgMemory.h"
10#include "Engine/Texture2D.h"
11#include "Logging/DlgLogger.h"
12#include "Net/UnrealNetwork.h"
13
14
15UDlgContext::UDlgContext(const FObjectInitializer& ObjectInitializer)
16 : UDlgObject(ObjectInitializer)
17{
18 //UObject.bReplicates = true;
19}
20
21void UDlgContext::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
22{
23 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
24 DOREPLIFETIME(ThisClass, Dialogue);
25 DOREPLIFETIME(ThisClass, SerializedParticipants);
26}
27
29{
31 for (const auto& KeyValue : Participants)
32 {
33 SerializedParticipants.Add(KeyValue.Value);
34 }
35}
36
38{
41 {
42 if (IsValid(Participant))
43 {
44 Participants.Add(IDlgDialogueParticipant::Execute_GetParticipantName(Participant), Participant);
45 }
46 }
47}
48
49bool UDlgContext::ChooseOption(int32 OptionIndex)
50{
51 check(Dialogue);
52 if (UDlgNode* Node = GetMutableActiveNode())
53 {
54 if (Node->OptionSelected(OptionIndex, *this))
55 {
56 return true;
57 }
58 }
59
60 bDialogueEnded = true;
61 return false;
62}
63
65{
66 check(Dialogue);
68 {
69 if (Node->OptionSelectedFromReplicated(OptionIndex, *this))
70 {
71 return true;
72 }
73 }
74
75 bDialogueEnded = true;
76 return false;
77}
78
80{
81 if (!AllChildren.IsValidIndex(Index))
82 {
83 LogErrorWithContext(FString::Printf(TEXT("ChooseOptionBasedOnAllOptionIndex - INVALID given Index = %d"), Index));
84 bDialogueEnded = true;
85 return false;
86 }
87
88 if (!AllChildren[Index].IsSatisfied())
89 {
90 LogErrorWithContext(FString::Printf(TEXT("ChooseOptionBasedOnAllOptionIndex - given Index = %d is an unsatisfied edge"), Index));
91 bDialogueEnded = true;
92 return false;
93 }
94
95 for (int32 i = 0; i < AvailableChildren.Num(); ++i)
96 {
97 if (AvailableChildren[i] == AllChildren[Index].GetEdge())
98 {
99 return ChooseOption(i);
100 }
101 }
102
103 ensure(false);
104 bDialogueEnded = true;
105 return false;
106}
107
109{
110 check(Dialogue);
112 if (!IsValid(Node))
113 {
114 LogErrorWithContext(TEXT("ReevaluateOptions - Failed to update dialogue options"));
115 return false;
116 }
117
118 return Node->ReevaluateChildren(*this, {});
119}
120
121const FText& UDlgContext::GetOptionText(int32 OptionIndex) const
122{
123 check(Dialogue);
124 if (!AvailableChildren.IsValidIndex(OptionIndex))
125 {
126 LogErrorWithContext(FString::Printf(TEXT("GetOptionText - INVALID given OptionIndex = %d"), OptionIndex));
127 return FText::GetEmpty();
128 }
129
130 return AvailableChildren[OptionIndex].GetText();
131}
132
133FName UDlgContext::GetOptionSpeakerState(int32 OptionIndex) const
134{
135 check(Dialogue);
136 if (!AvailableChildren.IsValidIndex(OptionIndex))
137 {
138 LogErrorWithContext(FString::Printf(TEXT("GetOptionSpeakerState - INVALID given OptionIndex = %d"), OptionIndex));
139 return NAME_None;
140 }
141
142 return AvailableChildren[OptionIndex].SpeakerState;
143}
144
145const TArray<FDlgCondition>& UDlgContext::GetOptionEnterConditions(int32 OptionIndex) const
146{
147 check(Dialogue);
148 if (!AvailableChildren.IsValidIndex(OptionIndex))
149 {
150 LogErrorWithContext(FString::Printf(TEXT("GetOptionEnterConditions - INVALID given OptionIndex = %d"), OptionIndex));
151 static TArray<FDlgCondition> EmptyArray;
152 return EmptyArray;
153 }
154
155 return AvailableChildren[OptionIndex].Conditions;
156}
157
158const FDlgEdge& UDlgContext::GetOption(int32 OptionIndex) const
159{
160 check(Dialogue);
161 if (!AvailableChildren.IsValidIndex(OptionIndex))
162 {
163 LogErrorWithContext(FString::Printf(TEXT("GetOption - INVALID given OptionIndex = %d"), OptionIndex));
165 }
166
167 return AvailableChildren[OptionIndex];
168}
169
171{
172 check(Dialogue);
173 if (!AllChildren.IsValidIndex(Index))
174 {
175 LogErrorWithContext(FString::Printf(TEXT("GetOptionTextFromAll - INVALID given Index = %d"), Index));
176 return FText::GetEmpty();
177 }
178
179 return AllChildren[Index].GetEdge().GetText();
180}
181
182bool UDlgContext::IsOptionSatisfied(int32 Index) const
183{
184 check(Dialogue);
185 if (!AllChildren.IsValidIndex(Index))
186 {
187 LogErrorWithContext(FString::Printf(TEXT("IsOptionSatisfied - INVALID given Index = %d"), Index));
188 return false;
189 }
190
191 return AllChildren[Index].IsSatisfied();
192}
193
195{
196 check(Dialogue);
197 if (!AllChildren.IsValidIndex(Index))
198 {
199 LogErrorWithContext(FString::Printf(TEXT("GetOptionSpeakerStateFromAll - INVALID given Index = %d"), Index));
200 return NAME_None;
201 }
202
203 return AllChildren[Index].GetEdge().SpeakerState;
204}
205
207{
208 check(Dialogue);
209 if (!AvailableChildren.IsValidIndex(Index))
210 {
211 LogErrorWithContext(FString::Printf(TEXT("GetOptionFromAll - INVALID given Index = %d"), Index));
213 }
214
215 return AllChildren[Index];
216}
217
219{
220 const UDlgNode* Node = GetActiveNode();
221 if (!IsValid(Node))
222 {
223 LogErrorWithContext(TEXT("GetActiveNodeText - INVALID Active Node"));
224 return FText::GetEmpty();
225 }
226
227 return Node->GetNodeText();
228}
229
231{
232 const UDlgNode* Node = GetActiveNode();
233 if (!IsValid(Node))
234 {
235 LogErrorWithContext(TEXT("GetActiveNodeSpeakerState - INVALID Active Node"));
236 return NAME_None;
237 }
238
239 return Node->GetSpeakerState();
240}
241
243{
244 const UDlgNode* Node = GetActiveNode();
245 if (!IsValid(Node))
246 {
247 LogErrorWithContext(TEXT("GetActiveNodeVoiceSoundWave - INVALID Active Node"));
248 return nullptr;
249 }
250
251 return Node->GetNodeVoiceSoundWave();
252}
253
255{
256 const UDlgNode* Node = GetActiveNode();
257 if (!IsValid(Node))
258 {
259 LogErrorWithContext(TEXT("GetActiveNodeVoiceSoundBase - INVALID Active Node"));
260 return nullptr;
261 }
262
263 return Node->GetNodeVoiceSoundBase();
264}
265
267{
268 const UDlgNode* Node = GetActiveNode();
269 if (!IsValid(Node))
270 {
271 LogErrorWithContext(TEXT("GetActiveNodeVoiceDialogueWave - INVALID Active Node"));
272 return nullptr;
273 }
274
275 return Node->GetNodeVoiceDialogueWave();
276}
277
279{
280 const UDlgNode* Node = GetActiveNode();
281 if (!IsValid(Node))
282 {
283 LogErrorWithContext(TEXT("GetActiveNodeGenericData - INVALID Active Node"));
284 return nullptr;
285 }
286
287 return Node->GetNodeGenericData();
288}
289
291{
292 const UDlgNode* Node = GetActiveNode();
293 if (!IsValid(Node))
294 {
295 LogErrorWithContext(TEXT("GetActiveNodeData - INVALID Active Node"));
296 return nullptr;
297 }
298
299 return Node->GetNodeData();
300}
301
303{
304 const UDlgNode* Node = GetActiveNode();
305 if (!IsValid(Node))
306 {
307 LogErrorWithContext(TEXT("GetActiveNodeParticipantIcon - INVALID Active Node"));
308 return nullptr;
309 }
310
311 const FName SpeakerName = Node->GetNodeParticipantName();
312 auto* ObjectPtr = Participants.Find(SpeakerName);
313 if (ObjectPtr == nullptr || !IsValid(*ObjectPtr))
314 {
315 LogErrorWithContext(FString::Printf(
316 TEXT("GetActiveNodeParticipantIcon - The ParticipantName = `%s` from the Active Node does NOT exist in the current Participants"),
317 *SpeakerName.ToString()
318 ));
319 return nullptr;
320 }
321
322 return IDlgDialogueParticipant::Execute_GetParticipantIcon(*ObjectPtr, SpeakerName, Node->GetSpeakerState());
323}
324
326{
327 const UDlgNode* Node = GetActiveNode();
328 if (!IsValid(Node))
329 {
330 LogErrorWithContext(TEXT("GetActiveNodeParticipant - INVALID Active Node"));
331 return nullptr;
332 }
333
334 const FName SpeakerName = Node->GetNodeParticipantName();
335 auto* ObjectPtr = Participants.Find(Node->GetNodeParticipantName());
336 if (ObjectPtr == nullptr || !IsValid(*ObjectPtr))
337 {
338 LogErrorWithContext(FString::Printf(
339 TEXT("GetActiveNodeParticipant - The ParticipantName = `%s` from the Active Node does NOT exist in the current Participants"),
340 *SpeakerName.ToString()
341 ));
342 return nullptr;
343 }
344
345 return *ObjectPtr;
346}
347
349{
350 const UDlgNode* Node = GetActiveNode();
351 if (!IsValid(Node))
352 {
353 LogErrorWithContext(TEXT("GetActiveNodeParticipantName - INVALID Active Node"));
354 return NAME_None;
355 }
356
357 return Node->GetNodeParticipantName();
358}
359
361{
362 const UDlgNode* Node = GetActiveNode();
363 if (!IsValid(Node))
364 {
365 LogErrorWithContext(TEXT("GetActiveNodeParticipantDisplayName - INVALID Active Node"));
366 return FText::GetEmpty();
367 }
368
369 const FName SpeakerName = Node->GetNodeParticipantName();
370 auto* ObjectPtr = Participants.Find(SpeakerName);
371 if (ObjectPtr == nullptr || !IsValid(*ObjectPtr))
372 {
373 LogErrorWithContext(FString::Printf(
374 TEXT("GetActiveNodeParticipantDisplayName - The ParticipantName = `%s` from the Active Node does NOT exist in the current Participants"),
375 *SpeakerName.ToString()
376 ));
377 return FText::GetEmpty();
378 }
379
380 return IDlgDialogueParticipant::Execute_GetParticipantDisplayName(*ObjectPtr, SpeakerName);
381}
382
383UObject* UDlgContext::GetMutableParticipant(FName ParticipantName) const
384{
385 auto* ParticipantPtr = Participants.Find(ParticipantName);
386 if (ParticipantPtr != nullptr && IsValid(*ParticipantPtr))
387 {
388 return *ParticipantPtr;
389 }
390
391 return nullptr;
392}
393
394const UObject* UDlgContext::GetParticipant(FName ParticipantName) const
395{
396 auto* ParticipantPtr = Participants.Find(ParticipantName);
397 if (ParticipantPtr != nullptr && IsValid(*ParticipantPtr))
398 {
399 return *ParticipantPtr;
400 }
401
402 return nullptr;
403}
404
405bool UDlgContext::IsValidNodeIndex(int32 NodeIndex) const
406{
407 return Dialogue ? Dialogue->IsValidNodeIndex(NodeIndex) : false;
408}
409
410bool UDlgContext::IsValidNodeGUID(const FGuid& NodeGUID) const
411{
412 return Dialogue ? Dialogue->IsValidNodeGUID(NodeGUID) : false;
413}
414
415FGuid UDlgContext::GetNodeGUIDForIndex(int32 NodeIndex) const
416{
417 return Dialogue ? Dialogue->GetNodeGUIDForIndex(NodeIndex) : FGuid{};
418}
419
420int32 UDlgContext::GetNodeIndexForGUID(const FGuid& NodeGUID) const
421{
422 return Dialogue ? Dialogue->GetNodeIndexForGUID(NodeGUID) : INDEX_NONE;
423}
424
425bool UDlgContext::IsOptionConnectedToVisitedNode(int32 Index, bool bLocalHistory, bool bIndexSkipsUnsatisfiedEdges) const
426{
427 int32 TargetIndex = INDEX_NONE;
428
429 if (bIndexSkipsUnsatisfiedEdges)
430 {
431 if (!AvailableChildren.IsValidIndex(Index))
432 {
433 LogErrorWithContext(FString::Printf(TEXT("IsOptionConnectedToVisitedNode - INVALID Index = %d for AvailableChildren"), Index));
434 return false;
435 }
436 TargetIndex = AvailableChildren[Index].TargetIndex;
437 }
438 else
439 {
440 if (!AllChildren.IsValidIndex(Index))
441 {
442 LogErrorWithContext(FString::Printf(TEXT("IsOptionConnectedToVisitedNode - INVALID Index = %d for AllChildren"), Index));
443 return false;
444 }
445 TargetIndex = AllChildren[Index].GetEdge().TargetIndex;
446 }
447
448 const FGuid TargetGUID = GetNodeGUIDForIndex(TargetIndex);
449 if (bLocalHistory)
450 {
451 return History.Contains(TargetIndex, TargetGUID);
452 }
453
454 if (Dialogue == nullptr)
455 {
456 LogErrorWithContext(TEXT("IsOptionConnectedToVisitedNode - This Context does not have a valid Dialogue"));
457 return false;
458 }
459
460 return FDlgMemory::Get().IsNodeVisited(Dialogue->GetGUID(), TargetIndex, TargetGUID);
461}
462
463bool UDlgContext::IsOptionConnectedToEndNode(int32 Index, bool bIndexSkipsUnsatisfiedEdges) const
464{
465 int32 TargetIndex = INDEX_NONE;
466
467 if (bIndexSkipsUnsatisfiedEdges)
468 {
469 if (!AvailableChildren.IsValidIndex(Index))
470 {
471 LogErrorWithContext(FString::Printf(TEXT("IsOptionConnectedToEndNode - INVALID Index = %d for AvailableChildren"), Index));
472 return false;
473 }
474 TargetIndex = AvailableChildren[Index].TargetIndex;
475 }
476 else
477 {
478 if (!AllChildren.IsValidIndex(Index))
479 {
480 LogErrorWithContext(FString::Printf(TEXT("IsOptionConnectedToEndNode - INVALID Index = %d for AllChildren"), Index));
481 return false;
482 }
483 TargetIndex = AllChildren[Index].GetEdge().TargetIndex;
484 }
485
486 if (Dialogue == nullptr)
487 {
488 LogErrorWithContext(TEXT("IsOptionConnectedToEndNode - This Context does not have a valid Dialogue"));
489 return false;
490 }
491
492 const TArray<UDlgNode*>& Nodes = Dialogue->GetNodes();
493 if (Nodes.IsValidIndex(TargetIndex))
494 {
495 return Nodes[TargetIndex]->IsA<UDlgNode_End>();
496 }
497
498 LogErrorWithContext(FString::Printf(TEXT("IsOptionConnectedToEndNode - The examined Edge/Option at Index = %d does not point to a valid node"), Index));
499 return false;
500}
501
502bool UDlgContext::EnterNode(int32 NodeIndex, TSet<const UDlgNode*> NodesEnteredWithThisStep)
503{
504 check(Dialogue);
505 UDlgNode* Node = GetMutableNodeFromIndex(NodeIndex);
506 if (!IsValid(Node))
507 {
508 LogErrorWithContext(FString::Printf(TEXT("EnterNode - FAILED because of INVALID NodeIndex = %d"), NodeIndex));
509 return false;
510 }
511
512 ActiveNodeIndex = NodeIndex;
513 SetNodeVisited(NodeIndex, Node->GetGUID());
514
515 OnNodeEntered.Broadcast();
516
517 return Node->HandleNodeEnter(*this, NodesEnteredWithThisStep);
518}
519
521{
522 UObject* FirstParticipant = nullptr;
523 for (const auto& KeyValue : Participants)
524 {
525 if (KeyValue.Value)
526 {
527 FirstParticipant = KeyValue.Value;
528 break;
529 }
530 }
531 if (!FirstParticipant)
532 {
533 return nullptr;
534 }
535
536 auto* Context = NewObject<UDlgContext>(FirstParticipant, StaticClass());
537 Context->Dialogue = Dialogue;
538 Context->SetParticipants(Participants);
539 Context->ActiveNodeIndex = ActiveNodeIndex;
540 Context->AvailableChildren = AvailableChildren;
541 Context->AllChildren = AllChildren;
542 Context->History = History;
543 Context->bDialogueEnded = bDialogueEnded;
544
545 return Context;
546}
547
548void UDlgContext::SetNodeVisited(int32 NodeIndex, const FGuid& NodeGUID)
549{
550 FDlgMemory::Get().SetNodeVisited(Dialogue->GetGUID(), NodeIndex, NodeGUID);
551 History.Add(NodeIndex, NodeGUID);
552}
553
558
560{
561 return Cast<UDlgNode_SpeechSequence>(GetNodeFromIndex(ActiveNodeIndex));
562}
563
565{
566 check(Dialogue);
567 if (!Dialogue->IsValidNodeIndex(NodeIndex))
568 {
569 return nullptr;
570 }
571
572 return Dialogue->GetMutableNodeFromIndex(NodeIndex);
573}
574
575const UDlgNode* UDlgContext::GetNodeFromIndex(int32 NodeIndex) const
576{
577 check(Dialogue);
578 if (!Dialogue->IsValidNodeIndex(NodeIndex))
579 {
580 return nullptr;
581 }
582
583 return Dialogue->GetMutableNodeFromIndex(NodeIndex);
584}
585
587{
588 check(Dialogue);
589 if (!Dialogue->IsValidNodeGUID(NodeGUID))
590 {
591 return nullptr;
592 }
593
594 return Dialogue->GetMutableNodeFromGUID(NodeGUID);
595}
596
597const UDlgNode* UDlgContext::GetNodeFromGUID(const FGuid& NodeGUID) const
598{
599 check(Dialogue);
600 if (!Dialogue->IsValidNodeGUID(NodeGUID))
601 {
602 return nullptr;
603 }
604
605 return Dialogue->GetMutableNodeFromGUID(NodeGUID);
606}
607
608bool UDlgContext::IsNodeEnterable(int32 NodeIndex, TSet<const UDlgNode*> AlreadyVisitedNodes) const
609{
610 check(Dialogue);
611 if (const UDlgNode* Node = GetNodeFromIndex(NodeIndex))
612 {
613 return Node->CheckNodeEnterConditions(*this, AlreadyVisitedNodes);
614 }
615
616 return false;
617}
618
619bool UDlgContext::CanBeStarted(UDlgDialogue* InDialogue, const TMap<FName, UObject*>& InParticipants)
620{
621 if (!ValidateParticipantsMapForDialogue(TEXT("CanBeStarted"), InDialogue, InParticipants, false))
622 {
623 return false;
624 }
625
626 // Get first participant
627 UObject* FirstParticipant = nullptr;
628 for (const auto& KeyValue : InParticipants)
629 {
630 if (KeyValue.Value)
631 {
632 FirstParticipant = KeyValue.Value;
633 break;
634 }
635 }
636 check(FirstParticipant != nullptr);
637
638 // Create temporary context that is Garbage Collected after this function returns (hopefully)
639 auto* Context = NewObject<UDlgContext>(FirstParticipant, StaticClass());
640 Context->Dialogue = InDialogue;
641 Context->SetParticipants(InParticipants);
642
643 // Evaluate edges/children of the start node
644 const UDlgNode& StartNode = InDialogue->GetStartNode();
645 for (const FDlgEdge& ChildLink : StartNode.GetNodeChildren())
646 {
647 if (ChildLink.Evaluate(*Context, {}))
648 {
649 // Simulate EnterNode
650 UDlgNode* Node = Context->GetMutableNodeFromIndex(ChildLink.TargetIndex);
651 if (Node && Node->HasAnySatisfiedChild(*Context, {}))
652 {
653 return true;
654 }
655 }
656 }
657
658 return false;
659}
660
661bool UDlgContext::StartWithContext(const FString& ContextString, UDlgDialogue* InDialogue, const TMap<FName, UObject*>& InParticipants)
662{
663 const FString ContextMessage = ContextString.IsEmpty()
664 ? TEXT("Start")
665 : FString::Printf(TEXT("%s - Start"), *ContextString);
666
667 Dialogue = InDialogue;
668 SetParticipants(InParticipants);
670 {
671 return false;
672 }
673
674 // Evaluate edges/children of the start node
675 const UDlgNode& StartNode = Dialogue->GetStartNode();
676 for (const FDlgEdge& ChildLink : StartNode.GetNodeChildren())
677 {
678 if (ChildLink.Evaluate(*this, {}))
679 {
680 if (EnterNode(ChildLink.TargetIndex, {}))
681 {
682 return true;
683 }
684 }
685 }
686
687 LogErrorWithContext(FString::Printf(
688 TEXT("%s - FAILED because all possible start node condition failed. Edge conditions and children enter conditions from the start node are not satisfied"),
689 *ContextMessage
690 ));
691 return false;
692}
693
695 const FString& ContextString,
696 UDlgDialogue* InDialogue,
697 const TMap<FName, UObject*>& InParticipants,
698 int32 StartNodeIndex,
699 const FGuid& StartNodeGUID,
700 const FDlgHistory& StartHistory,
701 bool bFireEnterEvents
702)
703{
704 const FString ContextMessage = ContextString.IsEmpty()
705 ? TEXT("StartFromNode")
706 : FString::Printf(TEXT("%s - StartFromNode"), *ContextString);
707
708 Dialogue = InDialogue;
709 SetParticipants(InParticipants);
710 History = StartHistory;
712 {
713 return false;
714 }
715
716 // Get the StartNodeIndex from the GUID
717 if (StartNodeGUID.IsValid())
718 {
719 StartNodeIndex = GetNodeIndexForGUID(StartNodeGUID);
720 }
721
722 UDlgNode* Node = GetMutableNodeFromIndex(StartNodeIndex);
723 if (!IsValid(Node))
724 {
725 LogErrorWithContext(FString::Printf(
726 TEXT("%s - FAILED because StartNodeIndex = %d is INVALID. For StartNodeGUID = %s"),
727 *ContextMessage, StartNodeIndex, *StartNodeGUID.ToString()
728 ));
729 return false;
730 }
731
732 if (bFireEnterEvents)
733 {
734 return EnterNode(StartNodeIndex, {});
735 }
736
737 ActiveNodeIndex = StartNodeIndex;
738 SetNodeVisited(StartNodeIndex, Node->GetGUID());
739
740 return Node->ReevaluateChildren(*this, {});
741}
742
744{
745 FString ContextParticipants;
746 TSet<FString> ParticipantsNames;
747 for (const auto& KeyValue : Participants)
748 {
749 ParticipantsNames.Add(KeyValue.Key.ToString());
750 }
751
752 return FString::Printf(
753 TEXT("Dialogue = `%s`, ActiveNodeIndex = %d, Participants Names = `%s`"),
754 Dialogue ? *Dialogue->GetPathName() : TEXT("INVALID"),
756 *FString::Join(ParticipantsNames, TEXT(", "))
757 );
758}
759
760void UDlgContext::LogErrorWithContext(const FString& ErrorMessage) const
761{
763}
764
765FString UDlgContext::GetErrorMessageWithContext(const FString& ErrorMessage) const
766{
767 return FString::Printf(TEXT("%s.\nContext:\n\t%s"), *ErrorMessage, *GetContextString());
768}
769
771{
772 if (!IsValid(Participant))
773 {
775 }
776 if (!IsValid(Dialogue))
777 {
779 }
780
781 // Does not implement interface
782 if (!Participant->GetClass()->ImplementsInterface(UDlgDialogueParticipant::StaticClass()))
783 {
784 if (Participant->IsA<UBlueprint>())
785 {
787 }
788
790 }
791
792 // We are more relaxed about this
793 // Even if the user supplies more participants that required we still allow them to start the dialogue if the number of participants is bigger
794
795 // Does the participant name exist in the Dialogue?
796 // const FName ParticipantName = IDlgDialogueParticipant::Execute_GetParticipantName(Participant);
797 // if (!Dialogue->HasParticipant(ParticipantName))
798 // {
799 // return EDlgValidateStatus::DialogueDoesNotContainParticipant;
800 // }
801
803}
804
806 const FString& ContextString,
807 const UDlgDialogue* Dialogue,
808 const UObject* Participant,
809 bool bLog
810)
811{
813
814 // Act as IsValidParticipantForDialogue
815 if (!bLog)
816 {
817 return Status == EDlgValidateStatus::Valid;
818 }
819
820 switch (Status)
821 {
823 return true;
824
827 TEXT("%s - Dialogue is INVALID (not set or null).\nContext:\n\tParticipant = `%s`"),
828 *ContextString, Participant ? *Participant->GetPathName() : TEXT("INVALID")
829 );
830 return false;
831
834 TEXT("%s - Participant is INVALID (not set or null).\nContext:\n\tDialogue = `%s`"),
835 *ContextString, Dialogue ? *Dialogue->GetPathName() : TEXT("INVALID")
836 );
837 return false;
838
841 TEXT("%s - Participant Path = `%s` does not implement the IDlgDialogueParticipant/UDlgDialogueParticipant interface.\nContext:\n\tDialogue = `%s`"),
842 *ContextString, *Participant->GetPathName(), *Dialogue->GetPathName()
843 );
844 return false;
845
848 TEXT("%s - Participant Path = `%s` is a Blueprint Class (from the content browser) and NOT a Blueprint Instance (from the level world).\nContext:\n\tDialogue = `%s`"),
849 *ContextString, *Participant->GetPathName(), *Dialogue->GetPathName()
850 );
851 return false;
852
853 // case EDlgValidateStatus::DialogueDoesNotContainParticipant:
854 // FDlgLogger::Get().Errorf(
855 // TEXT("%s - Participant Path = `%s` with ParticipantName = `%s` is NOT referenced (DOES) not exist inside the Dialogue.\nContext:\n\tDialogue = `%s`"),
856 // *ContextString, *Participant->GetPathName(), *IDlgDialogueParticipant::Execute_GetParticipantName(Participant).ToString(), *Dialogue->GetPathName()
857 // );
858 // return false;
859
860 default:
861 FDlgLogger::Get().Errorf(TEXT("%s - ValidateParticipantForDialogue - Error EDlgValidateStatus Unhandled = %d"), *ContextString, static_cast<int32>(Status));
862 return false;
863 }
864}
865
867 const FString& ContextString,
868 const UDlgDialogue* Dialogue,
869 const TMap<FName, UObject*>& ParticipantsMap,
870 bool bLog
871)
872{
873 const FString ContextMessage = ContextString.IsEmpty()
874 ? FString::Printf(TEXT("ValidateParticipantsMapForDialogue"))
875 : FString::Printf(TEXT("%s - ValidateParticipantsMapForDialogue"), *ContextString);
876
877 if (!IsValid(Dialogue))
878 {
879 if (bLog)
880 {
881 FDlgLogger::Get().Errorf(TEXT("%s - FAILED because the supplied Dialogue Asset is INVALID (nullptr)"), *ContextMessage);
882 }
883 return false;
884 }
885 if (Dialogue->GetParticipantsData().Num() == 0)
886 {
887 if (bLog)
888 {
889 FDlgLogger::Get().Errorf(TEXT("%s - Dialogue = `%s` does not have any participants"), *ContextMessage, *Dialogue->GetPathName());
890 }
891 return false;
892 }
893
894 // Check if at least these participants are required
895 const TMap<FName, FDlgParticipantData>& DialogueParticipants = Dialogue->GetParticipantsData();
896 TArray<FName> ParticipantsRequiredArray;
897 const int32 ParticipantsNum = DialogueParticipants.GetKeys(ParticipantsRequiredArray);
898 TSet<FName> ParticipantsRequiredSet{ParticipantsRequiredArray};
899
900 // Iterate over Map
901 for (const auto& KeyValue : ParticipantsMap)
902 {
903 const FName ParticipantName = KeyValue.Key;
904 const UObject* Participant = KeyValue.Value;
905
906 // We must check this otherwise we can't get the name
907 if (!ValidateParticipantForDialogue(ContextMessage, Dialogue, Participant, bLog))
908 {
909 return false;
910 }
911
912 // Check the Map Key matches the Participant Name
913 // This should only happen if you constructed the map incorrectly by mistake
914 // If you used ConvertArrayOfParticipantsToMap this should have NOT happened
915 {
916 const FName ObjectParticipantName = IDlgDialogueParticipant::Execute_GetParticipantName(Participant);
917 if (ParticipantName != ObjectParticipantName)
918 {
919 if (bLog)
920 {
922 TEXT("%s - The Map has a KEY Participant Name = `%s` DIFFERENT to the VALUE of the Participant Path = `%s` with the Name = `%s` (KEY Participant Name != VALUE Participant Name)"),
923 *ContextMessage, *ParticipantName.ToString(), *Participant->GetPathName(), *ObjectParticipantName.ToString()
924 );
925 }
926 return false;
927 }
928 }
929
930 // We found one participant from our set
931 if (ParticipantsRequiredSet.Contains(ParticipantName))
932 {
933 ParticipantsRequiredSet.Remove(ParticipantName);
934 }
935 else
936 {
937 // Participant does note exist, just warn about it, we are relaxed about this
938 if (bLog)
939 {
941 TEXT("%s - Participant Path = `%s` with Participant Name = `%s` is NOT referenced (DOES) not exist inside the Dialogue. It is going to be IGNORED.\nContext:\n\tDialogue = `%s`"),
942 *ContextMessage, *Participant->GetPathName(), *ParticipantName.ToString(), *Dialogue->GetPathName()
943 );
944 }
945 }
946 }
947
948 // Some participants are missing
949 if (ParticipantsRequiredSet.Num() > 0)
950 {
951 if (bLog)
952 {
953 TArray<FString> ParticipantsMissing;
954 for (const auto Name : ParticipantsRequiredSet)
955 {
956 ParticipantsMissing.Add(Name.ToString());
957 }
958
959 const FString NameList = FString::Join(ParticipantsMissing, TEXT(", "));
961 TEXT("%s - FAILED for Dialogue = `%s` because the following Participant Names are MISSING: `%s"),
962 *ContextMessage, *Dialogue->GetPathName(), *NameList
963 );
964 }
965 return false;
966 }
967
968 return true;
969}
970
972 const FString& ContextString,
973 const UDlgDialogue* Dialogue,
974 const TArray<UObject*>& ParticipantsArray,
975 TMap<FName, UObject*>& OutParticipantsMap,
976 bool bLog
977)
978{
979 const FString ContextMessage = ContextString.IsEmpty()
980 ? FString::Printf(TEXT("ConvertArrayOfParticipantsToMap"))
981 : FString::Printf(TEXT("%s - ConvertArrayOfParticipantsToMap"), *ContextString);
982
983 // We don't allow to convert empty arrays
984 OutParticipantsMap.Empty();
985 if (ParticipantsArray.Num() == 0)
986 {
987 if (bLog)
988 {
990 TEXT("%s - Participants Array is EMPTY, can't convert anything. Dialogue = `%s`"),
991 *ContextMessage, Dialogue ? *Dialogue->GetPathName() : TEXT("INVALID")
992 );
993 }
994 return false;
995 }
996
997 for (int32 Index = 0; Index < ParticipantsArray.Num(); Index++)
998 {
999 UObject* Participant = ParticipantsArray[Index];
1000 const FString ContextMessageWithIndex = FString::Printf(TEXT("%s - Participant at Index = %d"), *ContextMessage, Index);
1001
1002 // We must check this otherwise we can't get the name
1003 if (!ValidateParticipantForDialogue(ContextMessageWithIndex, Dialogue, Participant, bLog))
1004 {
1005 return false;
1006 }
1007
1008 // Is Duplicate?
1009 // Just warn the user about it, but still continue our conversion
1010 const FName ParticipantName = IDlgDialogueParticipant::Execute_GetParticipantName(Participant);
1011 if (OutParticipantsMap.Contains(ParticipantName))
1012 {
1013 if (bLog)
1014 {
1016 TEXT("%s - Participant Path = `%s`, Participant Name = `%s` already exists in the Array. Ignoring it!"),
1017 *ContextMessageWithIndex, *Participant->GetPathName(), *ParticipantName.ToString()
1018 );
1019 }
1020 continue;
1021 }
1022
1023 OutParticipantsMap.Add(ParticipantName, Participant);
1024 }
1025
1026 return true;
1027}
EDlgValidateStatus
UENUM()
Definition DlgContext.h:65
@ ParticipantIsABlueprintClassAndDoesNotImplementInterface
@ ParticipantDoesNotImplementInterface
static FDlgLogger & Get()
Definition DlgLogger.h:24
FORCEINLINE void Error(const FString &Message)
Definition INYLogger.h:325
void Warningf(const FmtType &Fmt, Types... Args)
Definition INYLogger.h:308
void Errorf(const FmtType &Fmt, Types... Args)
Definition INYLogger.h:305
UCLASS(BlueprintType)
Definition DlgContext.h:96
bool ReevaluateOptions()
UFUNCTION(BlueprintCallable, Category = "Dialogue|Control")
void SetParticipants(const TMap< FName, UObject * > &InParticipants)
Definition DlgContext.h:764
UDlgNodeData * GetActiveNodeData() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
UDlgNode_SpeechSequence * GetMutableActiveNodeAsSpeechSequence() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode", DisplayName = "Get Active Node As Speech S...
bool EnterNode(int32 NodeIndex, TSet< const UDlgNode * > NodesEnteredWithThisStep)
FString GetErrorMessageWithContext(const FString &ErrorMessage) const
bool bDialogueEnded
Definition DlgContext.h:810
void OnRep_SerializedParticipants()
UFUNCTION()
void SerializeParticipants()
void LogErrorWithContext(const FString &ErrorMessage) const
FName GetActiveNodeSpeakerState() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
UDlgDialogue * Dialogue
UPROPERTY(Replicated)
Definition DlgContext.h:776
UDlgNode * GetMutableNodeFromGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Node From GUID")
TMap< FName, UObject * > Participants
UPROPERTY()
Definition DlgContext.h:790
UDlgNode * GetMutableNodeFromIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Node From Index")
TArray< UObject * > SerializedParticipants
UPROPERTY(Replicated, ReplicatedUsing = OnRep_SerializedParticipants)
Definition DlgContext.h:783
USoundWave * GetActiveNodeVoiceSoundWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
const FText & GetOptionTextFromAll(int32 Index) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|All")
FText GetActiveNodeParticipantDisplayName() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
static EDlgValidateStatus IsValidParticipantForDialogue(const UDlgDialogue *Dialogue, const UObject *Participant)
FName GetOptionSpeakerStateFromAll(int32 Index) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|All")
FOnNodeEntered OnNodeEntered
UPROPERTY(BlueprintAssignable, Category = "Events")
Definition DlgContext.h:817
FName GetActiveNodeParticipantName() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
UDlgContext * CreateCopy() const
UDlgContext(const FObjectInitializer &ObjectInitializer)
const FDlgEdge & GetOption(int32 OptionIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|Satisfied")
FName GetOptionSpeakerState(int32 OptionIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|Satisfied")
bool ChooseSpeechSequenceOptionFromReplicated(int32 OptionIndex)
UFUNCTION(BlueprintCallable, Category = "Dialogue|Control")
const UDlgNode_SpeechSequence * GetActiveNodeAsSpeechSequence() const
TArray< FDlgEdgeData > AllChildren
Definition DlgContext.h:803
bool ChooseOptionBasedOnAllOptionIndex(int32 Index)
UFUNCTION(BlueprintCallable, Category = "Dialogue|Control|All")
bool IsValidNodeIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data")
bool IsNodeEnterable(int32 NodeIndex, TSet< const UDlgNode * > AlreadyVisitedNodes) const
const FText & GetOptionText(int32 OptionIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|Satisfied")
const FText & GetActiveNodeText() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
USoundBase * GetActiveNodeVoiceSoundBase() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
static bool CanBeStarted(UDlgDialogue *InDialogue, const TMap< FName, UObject * > &InParticipants)
const UObject * GetParticipant(FName ParticipantName) const
FString GetContextString() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Context")
const UDlgNode * GetNodeFromIndex(int32 NodeIndex) const
static bool ConvertArrayOfParticipantsToMap(const FString &ContextString, const UDlgDialogue *Dialogue, const TArray< UObject * > &ParticipantsArray, TMap< FName, UObject * > &OutParticipantsMap, bool bLog=true)
bool IsOptionConnectedToEndNode(int32 Index, bool bIndexSkipsUnsatisfiedEdges=true) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data")
bool ChooseOption(int32 OptionIndex)
UFUNCTION(BlueprintCallable, Category = "Dialogue|Control")
UObject * GetMutableParticipant(FName ParticipantName) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Participant")
const TArray< FDlgCondition > & GetOptionEnterConditions(int32 OptionIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|Satisfied")
static bool ValidateParticipantsMapForDialogue(const FString &ContextString, const UDlgDialogue *Dialogue, const TMap< FName, UObject * > &ParticipantsMap, bool bLog=true)
UObject * GetActiveNodeParticipant() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
bool StartWithContextFromNode(const FString &ContextString, UDlgDialogue *InDialogue, const TMap< FName, UObject * > &InParticipants, int32 StartNodeIndex, const FGuid &StartNodeGUID, const FDlgHistory &StartHistory, bool bFireEnterEvents)
int32 ActiveNodeIndex
Definition DlgContext.h:793
bool StartWithContext(const FString &ContextString, UDlgDialogue *InDialogue, const TMap< FName, UObject * > &InParticipants)
const UDlgNode * GetActiveNode() const
Definition DlgContext.h:449
bool IsOptionConnectedToVisitedNode(int32 Index, bool bLocalHistory=false, bool bIndexSkipsUnsatisfiedEdges=true) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data")
FDlgHistory History
Definition DlgContext.h:807
const UDlgNode * GetNodeFromGUID(const FGuid &NodeGUID) const
static bool ValidateParticipantForDialogue(const FString &ContextString, const UDlgDialogue *Dialogue, const UObject *Participant, bool bLog=true)
FGuid GetNodeGUIDForIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Node GUID For Index")
void GetLifetimeReplicatedProps(TArray< FLifetimeProperty > &OutLifetimeProps) const override
const FDlgEdgeData & GetOptionFromAll(int32 Index) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|All")
UDlgNode * GetMutableActiveNode() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode", DisplayName = "Get Active Node")
Definition DlgContext.h:448
bool IsValidNodeGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data")
UObject * GetActiveNodeGenericData() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
int32 GetNodeIndexForGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Node Index For GUID")
bool IsOptionSatisfied(int32 Index) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Options|All")
void SetNodeVisited(int32 NodeIndex, const FGuid &NodeGUID)
UDialogueWave * GetActiveNodeVoiceDialogueWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
UTexture2D * GetActiveNodeParticipantIcon() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
TArray< FDlgEdge > AvailableChildren
Definition DlgContext.h:796
UCLASS(BlueprintType, Meta = (DisplayThumbnail = "true"))
Definition DlgDialogue.h:85
const TMap< FName, FDlgParticipantData > & GetParticipantsData() const
UFUNCTION(BlueprintPure, Category = "Dialogue")
UDlgNode * GetMutableNodeFromGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Node From GUID")
bool IsValidNodeIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue")
const TArray< UDlgNode * > & GetNodes() const
UFUNCTION(BlueprintPure, Category = "Dialogue")
const UDlgNode & GetStartNode() const
FGuid GetNodeGUIDForIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue", DisplayName = "Get Node GUID For Index")
FGuid GetGUID() const
UFUNCTION(BlueprintPure, Category = "Dialogue|GUID")
bool IsValidNodeGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue")
int32 GetNodeIndexForGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue", DisplayName = "Get Node Index For GUID")
UDlgNode * GetMutableNodeFromIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue", DisplayName = "Get Node From Index")
UCLASS(BlueprintType, ClassGroup = "Dialogue")
Definition DlgNode_End.h:21
UCLASS(BlueprintType, ClassGroup = "Dialogue")
UCLASS(Blueprintable, BlueprintType, Abstract, EditInlineNew)
Definition DlgNodeData.h:18
UCLASS(BlueprintType, Abstract, EditInlineNew, ClassGroup = "Dialogue")
Definition DlgNode.h:40
virtual bool HandleNodeEnter(UDlgContext &Context, TSet< const UDlgNode * > NodesEnteredWithThisStep)
Definition DlgNode.cpp:129
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 USoundBase * GetNodeVoiceSoundBase() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:295
FGuid GetGUID() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:105
virtual const TArray< FDlgEdge > & GetNodeChildren() const
Gets this nodes children (edges) as a const/mutable array.
Definition DlgNode.h:184
virtual FName GetNodeParticipantName() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:127
virtual UDlgNodeData * GetNodeData() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:323
virtual FName GetSpeakerState() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:309
virtual UObject * GetNodeGenericData() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:317
virtual UDialogueWave * GetNodeVoiceDialogueWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
Definition DlgNode.h:302
bool HasAnySatisfiedChild(const UDlgContext &Context, TSet< const UDlgNode * > AlreadyVisitedNodes) const
Definition DlgNode.cpp:212
virtual bool ReevaluateChildren(UDlgContext &Context, TSet< const UDlgNode * > AlreadyEvaluated)
Definition DlgNode.cpp:157
UCLASS(Abstract, ClassGroup = "Dialogue", HideCategories = ("DoNotShow"), AutoExpandCategories = ("De...
Definition DlgObject.h:13
USTRUCT(BlueprintType)
Definition DlgContext.h:28
static const FDlgEdgeData & GetInvalidEdge()
Definition DlgContext.h:38
USTRUCT(BlueprintType)
Definition DlgEdge.h:25
static const FDlgEdge & GetInvalidEdge()
Definition DlgEdge.h:109
USTRUCT(BlueprintType)
Definition DlgMemory.h:13
void Add(int32 NodeIndex, const FGuid &NodeGUID)
Definition DlgMemory.h:18
bool Contains(int32 NodeIndex, const FGuid &NodeGUID) const
Definition DlgMemory.h:67
bool IsNodeVisited(const FGuid &DialogueGUID, int32 NodeIndex, const FGuid &NodeGUID) const
Definition DlgMemory.h:157
void SetNodeVisited(const FGuid &DialogueGUID, int32 NodeIndex, const FGuid &NodeGUID)
Definition DlgMemory.h:145
static FDlgMemory & Get()
Definition DlgMemory.h:117