10#include "Engine/Texture2D.h"
12#include "Net/UnrealNetwork.h"
23 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
54 if (Node->OptionSelected(OptionIndex, *
this))
69 if (Node->OptionSelectedFromReplicated(OptionIndex, *
this))
83 LogErrorWithContext(FString::Printf(TEXT(
"ChooseOptionBasedOnAllOptionIndex - INVALID given Index = %d"), Index));
90 LogErrorWithContext(FString::Printf(TEXT(
"ChooseOptionBasedOnAllOptionIndex - given Index = %d is an unsatisfied edge"), Index));
126 LogErrorWithContext(FString::Printf(TEXT(
"GetOptionText - INVALID given OptionIndex = %d"), OptionIndex));
127 return FText::GetEmpty();
138 LogErrorWithContext(FString::Printf(TEXT(
"GetOptionSpeakerState - INVALID given OptionIndex = %d"), OptionIndex));
150 LogErrorWithContext(FString::Printf(TEXT(
"GetOptionEnterConditions - INVALID given OptionIndex = %d"), OptionIndex));
151 static TArray<FDlgCondition> EmptyArray;
163 LogErrorWithContext(FString::Printf(TEXT(
"GetOption - INVALID given OptionIndex = %d"), OptionIndex));
175 LogErrorWithContext(FString::Printf(TEXT(
"GetOptionTextFromAll - INVALID given Index = %d"), Index));
176 return FText::GetEmpty();
187 LogErrorWithContext(FString::Printf(TEXT(
"IsOptionSatisfied - INVALID given Index = %d"), Index));
199 LogErrorWithContext(FString::Printf(TEXT(
"GetOptionSpeakerStateFromAll - INVALID given Index = %d"), Index));
211 LogErrorWithContext(FString::Printf(TEXT(
"GetOptionFromAll - INVALID given Index = %d"), Index));
224 return FText::GetEmpty();
313 if (ObjectPtr ==
nullptr || !IsValid(*ObjectPtr))
316 TEXT(
"GetActiveNodeParticipantIcon - The ParticipantName = `%s` from the Active Node does NOT exist in the current Participants"),
317 *SpeakerName.ToString()
322 return IDlgDialogueParticipant::Execute_GetParticipantIcon(*ObjectPtr, SpeakerName, Node->
GetSpeakerState());
336 if (ObjectPtr ==
nullptr || !IsValid(*ObjectPtr))
339 TEXT(
"GetActiveNodeParticipant - The ParticipantName = `%s` from the Active Node does NOT exist in the current Participants"),
340 *SpeakerName.ToString()
366 return FText::GetEmpty();
371 if (ObjectPtr ==
nullptr || !IsValid(*ObjectPtr))
374 TEXT(
"GetActiveNodeParticipantDisplayName - The ParticipantName = `%s` from the Active Node does NOT exist in the current Participants"),
375 *SpeakerName.ToString()
377 return FText::GetEmpty();
380 return IDlgDialogueParticipant::Execute_GetParticipantDisplayName(*ObjectPtr, SpeakerName);
385 auto* ParticipantPtr =
Participants.Find(ParticipantName);
386 if (ParticipantPtr !=
nullptr && IsValid(*ParticipantPtr))
388 return *ParticipantPtr;
396 auto* ParticipantPtr =
Participants.Find(ParticipantName);
397 if (ParticipantPtr !=
nullptr && IsValid(*ParticipantPtr))
399 return *ParticipantPtr;
427 int32 TargetIndex = INDEX_NONE;
429 if (bIndexSkipsUnsatisfiedEdges)
433 LogErrorWithContext(FString::Printf(TEXT(
"IsOptionConnectedToVisitedNode - INVALID Index = %d for AvailableChildren"), Index));
442 LogErrorWithContext(FString::Printf(TEXT(
"IsOptionConnectedToVisitedNode - INVALID Index = %d for AllChildren"), Index));
445 TargetIndex =
AllChildren[Index].GetEdge().TargetIndex;
456 LogErrorWithContext(TEXT(
"IsOptionConnectedToVisitedNode - This Context does not have a valid Dialogue"));
465 int32 TargetIndex = INDEX_NONE;
467 if (bIndexSkipsUnsatisfiedEdges)
471 LogErrorWithContext(FString::Printf(TEXT(
"IsOptionConnectedToEndNode - INVALID Index = %d for AvailableChildren"), Index));
480 LogErrorWithContext(FString::Printf(TEXT(
"IsOptionConnectedToEndNode - INVALID Index = %d for AllChildren"), Index));
483 TargetIndex =
AllChildren[Index].GetEdge().TargetIndex;
488 LogErrorWithContext(TEXT(
"IsOptionConnectedToEndNode - This Context does not have a valid Dialogue"));
493 if (Nodes.IsValidIndex(TargetIndex))
498 LogErrorWithContext(FString::Printf(TEXT(
"IsOptionConnectedToEndNode - The examined Edge/Option at Index = %d does not point to a valid node"), Index));
508 LogErrorWithContext(FString::Printf(TEXT(
"EnterNode - FAILED because of INVALID NodeIndex = %d"), NodeIndex));
522 UObject* FirstParticipant =
nullptr;
527 FirstParticipant = KeyValue.Value;
531 if (!FirstParticipant)
536 auto* Context = NewObject<UDlgContext>(FirstParticipant, StaticClass());
613 return Node->CheckNodeEnterConditions(*
this, AlreadyVisitedNodes);
627 UObject* FirstParticipant =
nullptr;
628 for (
const auto& KeyValue : InParticipants)
632 FirstParticipant = KeyValue.Value;
636 check(FirstParticipant !=
nullptr);
639 auto* Context = NewObject<UDlgContext>(FirstParticipant, StaticClass());
640 Context->Dialogue = InDialogue;
641 Context->SetParticipants(InParticipants);
647 if (ChildLink.Evaluate(*Context, {}))
650 UDlgNode* Node = Context->GetMutableNodeFromIndex(ChildLink.TargetIndex);
663 const FString ContextMessage = ContextString.IsEmpty()
665 : FString::Printf(TEXT(
"%s - Start"), *ContextString);
678 if (ChildLink.Evaluate(*
this, {}))
680 if (
EnterNode(ChildLink.TargetIndex, {}))
688 TEXT(
"%s - FAILED because all possible start node condition failed. Edge conditions and children enter conditions from the start node are not satisfied"),
695 const FString& ContextString,
697 const TMap<FName, UObject*>& InParticipants,
698 int32 StartNodeIndex,
699 const FGuid& StartNodeGUID,
701 bool bFireEnterEvents
704 const FString ContextMessage = ContextString.IsEmpty()
705 ? TEXT(
"StartFromNode")
706 : FString::Printf(TEXT(
"%s - StartFromNode"), *ContextString);
717 if (StartNodeGUID.IsValid())
726 TEXT(
"%s - FAILED because StartNodeIndex = %d is INVALID. For StartNodeGUID = %s"),
727 *ContextMessage, StartNodeIndex, *StartNodeGUID.ToString()
732 if (bFireEnterEvents)
745 FString ContextParticipants;
746 TSet<FString> ParticipantsNames;
749 ParticipantsNames.Add(KeyValue.Key.ToString());
752 return FString::Printf(
753 TEXT(
"Dialogue = `%s`, ActiveNodeIndex = %d, Participants Names = `%s`"),
756 *FString::Join(ParticipantsNames, TEXT(
", "))
767 return FString::Printf(TEXT(
"%s.\nContext:\n\t%s"), *ErrorMessage, *
GetContextString());
782 if (!
Participant->GetClass()->ImplementsInterface(UDlgDialogueParticipant::StaticClass()))
806 const FString& ContextString,
827 TEXT(
"%s - Dialogue is INVALID (not set or null).\nContext:\n\tParticipant = `%s`"),
834 TEXT(
"%s - Participant is INVALID (not set or null).\nContext:\n\tDialogue = `%s`"),
841 TEXT(
"%s - Participant Path = `%s` does not implement the IDlgDialogueParticipant/UDlgDialogueParticipant interface.\nContext:\n\tDialogue = `%s`"),
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`"),
861 FDlgLogger::Get().
Errorf(TEXT(
"%s - ValidateParticipantForDialogue - Error EDlgValidateStatus Unhandled = %d"), *ContextString,
static_cast<int32
>(Status));
867 const FString& ContextString,
869 const TMap<FName, UObject*>& ParticipantsMap,
873 const FString ContextMessage = ContextString.IsEmpty()
874 ? FString::Printf(TEXT(
"ValidateParticipantsMapForDialogue"))
875 : FString::Printf(TEXT(
"%s - ValidateParticipantsMapForDialogue"), *ContextString);
881 FDlgLogger::Get().
Errorf(TEXT(
"%s - FAILED because the supplied Dialogue Asset is INVALID (nullptr)"), *ContextMessage);
896 TArray<FName> ParticipantsRequiredArray;
897 const int32 ParticipantsNum = DialogueParticipants.GetKeys(ParticipantsRequiredArray);
898 TSet<FName> ParticipantsRequiredSet{ParticipantsRequiredArray};
901 for (
const auto& KeyValue : ParticipantsMap)
903 const FName ParticipantName = KeyValue.Key;
916 const FName ObjectParticipantName = IDlgDialogueParticipant::Execute_GetParticipantName(
Participant);
917 if (ParticipantName != ObjectParticipantName)
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()
931 if (ParticipantsRequiredSet.Contains(ParticipantName))
933 ParticipantsRequiredSet.Remove(ParticipantName);
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()
949 if (ParticipantsRequiredSet.Num() > 0)
953 TArray<FString> ParticipantsMissing;
954 for (
const auto Name : ParticipantsRequiredSet)
956 ParticipantsMissing.Add(
Name.ToString());
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
972 const FString& ContextString,
974 const TArray<UObject*>& ParticipantsArray,
975 TMap<FName, UObject*>& OutParticipantsMap,
979 const FString ContextMessage = ContextString.IsEmpty()
980 ? FString::Printf(TEXT(
"ConvertArrayOfParticipantsToMap"))
981 : FString::Printf(TEXT(
"%s - ConvertArrayOfParticipantsToMap"), *ContextString);
984 OutParticipantsMap.Empty();
985 if (ParticipantsArray.Num() == 0)
990 TEXT(
"%s - Participants Array is EMPTY, can't convert anything. Dialogue = `%s`"),
997 for (int32 Index = 0; Index < ParticipantsArray.Num(); Index++)
1000 const FString ContextMessageWithIndex = FString::Printf(TEXT(
"%s - Participant at Index = %d"), *ContextMessage, Index);
1010 const FName ParticipantName = IDlgDialogueParticipant::Execute_GetParticipantName(
Participant);
1011 if (OutParticipantsMap.Contains(ParticipantName))
1016 TEXT(
"%s - Participant Path = `%s`, Participant Name = `%s` already exists in the Array. Ignoring it!"),
1017 *ContextMessageWithIndex, *
Participant->GetPathName(), *ParticipantName.ToString()
1023 OutParticipantsMap.Add(ParticipantName,
Participant);
EDlgValidateStatus
UENUM()
@ ParticipantIsABlueprintClassAndDoesNotImplementInterface
@ ParticipantDoesNotImplementInterface
static FDlgLogger & Get()
FORCEINLINE void Error(const FString &Message)
void Warningf(const FmtType &Fmt, Types... Args)
void Errorf(const FmtType &Fmt, Types... Args)
bool ReevaluateOptions()
UFUNCTION(BlueprintCallable, Category = "Dialogue|Control")
void SetParticipants(const TMap< FName, UObject * > &InParticipants)
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
void OnRep_SerializedParticipants()
UFUNCTION()
void SerializeParticipants()
void LogErrorWithContext(const FString &ErrorMessage) const
FName GetActiveNodeSpeakerState() const
UFUNCTION(BlueprintPure, Category = "Dialogue|ActiveNode")
UDlgDialogue * Dialogue
UPROPERTY(Replicated)
UDlgNode * GetMutableNodeFromGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Node From GUID")
TMap< FName, UObject * > Participants
UPROPERTY()
UDlgNode * GetMutableNodeFromIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data", DisplayName = "Get Node From Index")
TArray< UObject * > SerializedParticipants
UPROPERTY(Replicated, ReplicatedUsing = OnRep_SerializedParticipants)
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")
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
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)
bool StartWithContext(const FString &ContextString, UDlgDialogue *InDialogue, const TMap< FName, UObject * > &InParticipants)
const UDlgNode * GetActiveNode() const
bool IsOptionConnectedToVisitedNode(int32 Index, bool bLocalHistory=false, bool bIndexSkipsUnsatisfiedEdges=true) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Data")
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")
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
UCLASS(BlueprintType, Meta = (DisplayThumbnail = "true"))
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")
UCLASS(BlueprintType, ClassGroup = "Dialogue")
UCLASS(Blueprintable, BlueprintType, Abstract, EditInlineNew)
UCLASS(BlueprintType, Abstract, EditInlineNew, ClassGroup = "Dialogue")
virtual bool HandleNodeEnter(UDlgContext &Context, TSet< const UDlgNode * > NodesEnteredWithThisStep)
virtual const FText & GetNodeText() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
USoundWave * GetNodeVoiceSoundWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual USoundBase * GetNodeVoiceSoundBase() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
FGuid GetGUID() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual const TArray< FDlgEdge > & GetNodeChildren() const
Gets this nodes children (edges) as a const/mutable array.
virtual FName GetNodeParticipantName() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual UDlgNodeData * GetNodeData() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual FName GetSpeakerState() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual UObject * GetNodeGenericData() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual UDialogueWave * GetNodeVoiceDialogueWave() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
bool HasAnySatisfiedChild(const UDlgContext &Context, TSet< const UDlgNode * > AlreadyVisitedNodes) const
virtual bool ReevaluateChildren(UDlgContext &Context, TSet< const UDlgNode * > AlreadyEvaluated)
UCLASS(Abstract, ClassGroup = "Dialogue", HideCategories = ("DoNotShow"), AutoExpandCategories = ("De...
static const FDlgEdgeData & GetInvalidEdge()
static const FDlgEdge & GetInvalidEdge()
void Add(int32 NodeIndex, const FGuid &NodeGUID)
bool Contains(int32 NodeIndex, const FGuid &NodeGUID) const
bool IsNodeVisited(const FGuid &DialogueGUID, int32 NodeIndex, const FGuid &NodeGUID) const
void SetNodeVisited(const FGuid &DialogueGUID, int32 NodeIndex, const FGuid &NodeGUID)
static FDlgMemory & Get()