4#include "UObject/DevObjectVersion.h"
5#include "HAL/FileManager.h"
9#include "EdGraph/EdGraph.h"
10#include "EdGraph/EdGraphSchema.h"
24#define LOCTEXT_NAMESPACE "DlgDialogue"
48 Dialogue->GetDialogueEditorAccess()->UpdateDialogueToVersion_UseOnlyOneOutputAndInputPin(
Dialogue);
57 Super::PreSave(TargetPlatform);
95 TEXT(
"Dialogue = `%s` with Version MergeVirtualParentAndSelectorTypes will not be converted. See https://gitlab.com/snippets/1691704 for manual conversion"),
112 TEXT(
"Creating new GUID = `%s` for Dialogue = `%s` because of of invalid GUID."),
113 *
GUID.ToString(), *GetPathName()
118 const bool bHasDialogueEditorModule = GetDialogueEditorAccess().IsValid();
121 if (bHasDialogueEditorModule && !GetDialogueEditorAccess()->AreDialogueNodesInSyncWithGraphNodes(
this))
128 const int32 NodesNum =
Nodes.Num();
129 for (int32 NodeIndex = 0; NodeIndex < NodesNum; NodeIndex++)
133 if (bHasDialogueEditorModule)
135 checkf(Node->GetGraphNode(), TEXT(
"Expected DialogueVersion = %d to have a valid GraphNode for Node index = %d :("), DialogueVersion, NodeIndex);
140 const int32 EdgesNum = NodeEdges.Num();
141 for (int32 EdgeIndex = 0; EdgeIndex < EdgesNum; EdgeIndex++)
143 const FDlgEdge& Edge = NodeEdges[EdgeIndex];
154 TEXT(
"Node with index = %d does not have a valid Edge index = %d with TargetIndex = %d"),
166 Super::PostInitProperties();
169 if (HasAnyFlags(RF_ClassDefaultObject | RF_NeedLoad))
178 if (GetDialogueEditorAccess().IsValid())
193 TEXT(
"Creating new GUID = `%s` for Dialogue = `%s` because of new created Dialogue."),
194 *
GUID.ToString(), *GetPathName()
201 Super::PostRename(OldOuter, OldName);
207 Super::PostDuplicate(bDuplicateForPIE);
213 TEXT(
"Creating new GUID = `%s` for Dialogue = `%s` because Dialogue was copied."),
214 *
GUID.ToString(), *GetPathName()
220 Super::PostEditImport();
226 TEXT(
"Creating new GUID = `%s` for Dialogue = `%s` because Dialogue was copied."),
227 *
GUID.ToString(), *GetPathName()
232TSharedPtr<IDlgDialogueEditorAccess> UDlgDialogue::DialogueEditorAccess =
nullptr;
234bool UDlgDialogue::Modify(
bool bAlwaysMarkDirty)
241 const bool bWasSaved = Super::Modify(bAlwaysMarkDirty);
255void UDlgDialogue::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
257 Super::PostEditChangeProperty(PropertyChangedEvent);
260 check(OnDialoguePropertyChanged.IsBound());
261 OnDialoguePropertyChanged.Broadcast(PropertyChangedEvent);
264void UDlgDialogue::PostEditChangeChainProperty(
struct FPropertyChangedChainEvent& PropertyChangedEvent)
268 const auto* ActiveMemberNode = PropertyChangedEvent.PropertyChain.GetActiveMemberNode();
269 const auto* ActivePropertyNode = PropertyChangedEvent.PropertyChain.GetActiveNode();
270 const FName MemberPropertyName = ActiveMemberNode && ActiveMemberNode->GetValue() ? ActiveMemberNode->GetValue()->GetFName() : NAME_None;
271 const FName PropertyName = ActivePropertyNode && ActivePropertyNode->GetValue() ? ActivePropertyNode->GetValue()->GetFName() : NAME_None;
286 if (!
Participant.ParticipantClass->ImplementsInterface(UDlgDialogueParticipant::StaticClass()))
294 Super::PostEditChangeChainProperty(PropertyChangedEvent);
297void UDlgDialogue::AddReferencedObjects(
UObject* InThis, FReferenceCollector& Collector)
301 Collector.AddReferencedObject(This->DlgGraph, This);
302 Super::AddReferencedObjects(InThis, Collector);
309void UDlgDialogue::CreateGraph()
313 if (DlgGraph !=
nullptr)
320 StartNode = ConstructDialogueNode<UDlgNode_Speech>();
324 DlgGraph = GetDialogueEditorAccess()->CreateNewDialogueGraph(
this);
327 DlgGraph->GetSchema()->CreateDefaultNodesForGraph(*DlgGraph);
331void UDlgDialogue::ClearGraph()
333 if (!IsValid(DlgGraph))
339 GetDialogueEditorAccess()->RemoveAllGraphNodes(
this);
342 DlgGraph->GetSchema()->CreateDefaultNodesForGraph(*DlgGraph);
346void UDlgDialogue::CompileDialogueNodesFromGraphNodes()
348 if (!bCompileDialogue)
353 FDlgLogger::Get().
Infof(TEXT(
"Compiling Dialogue = `%s` (Graph data -> Dialogue data)`"), *GetPathName());
354 GetDialogueEditorAccess()->CompileDialogueNodesFromGraphNodes(
this);
377 IFileManager& FileManager = IFileManager::Get();
386 TextFormatIndex < TextFormatsNum; TextFormatIndex++)
390 if (FileManager.FileExists(*CurrentTextFileName))
400 if (!FileManager.FileExists(*TextFileName))
402 FDlgLogger::Get().
Errorf(TEXT(
"Reloading data for Dialogue = `%s` FROM file = `%s` FAILED, because the file does not exist"), *GetPathName(), *TextFileName);
411 FDlgLogger::Get().
Infof(TEXT(
"Reloading data for Dialogue = `%s` FROM file = `%s`"), *GetPathName(), *TextFileName);
439 StartNode = ConstructDialogueNode<UDlgNode_Speech>();
445 if (DuplicateDialogues.Num() > 0)
447 if (DuplicateDialogues.Contains(
this))
452 TEXT(
"Creating new GUID = `%s` for Dialogue = `%s` because the input file contained a duplicate GUID."),
453 *
GUID.ToString(), *GetPathName()
460 TEXT(
"Found Duplicate Dialogue that does not belong to this Dialogue = `%s`, DuplicateDialogues.Num = %d"),
461 *GetPathName(), DuplicateDialogues.Num()
475 CompileDialogueNodesFromGraphNodes();
502 FDlgLogger::Get().
Infof(TEXT(
"Exporting data for Dialogue = `%s` TO file = `%s`"), *GetPathName(), *TextFileName);
510 JsonWriter.
Write(GetClass(),
this);
517 DlgWriter.
Write(GetClass(),
this);
527 TextFormatIndex < TextFormatsNum; TextFormatIndex++)
536 check(!bHasExtension);
547 const FName& ValidParticipantName = ParticipantName == NAME_None ? FallbackParticipantName : ParticipantName;
550 if (bCheckNone && ValidParticipantName == NAME_None)
553 TEXT(
"Ignoring ParticipantName = None, Context = `%s`. Either your node participant name is None or your participant name is None."),
556 return BlackHoleParticipant;
564 const FString NodeContext = FString::Printf(TEXT(
"Node %s"), NodeIndex > INDEX_NONE ? *FString::FromInt(NodeIndex) : TEXT(
"Start") );
569 const int32 TargetIndex = Edge.TargetIndex;
575 const FString ContextMessage = FString::Printf(TEXT(
"Adding Edge primary condition data from %s to Node %d"), *NodeContext, TargetIndex);
579 if (
Condition.IsSecondParticipantInvolved())
581 const FString ContextMessage = FString::Printf(TEXT(
"Adding Edge secondary condition data from %s to Node %d"), *NodeContext, TargetIndex);
591 static constexpr bool bEdges =
true;
592 static constexpr bool bUpdateGraphNode =
false;
598 if (bUpdateTextsNamespacesAndKeys)
623 const int32 NodesNum =
Nodes.Num();
624 for (int32 NodeIndex = 0; NodeIndex < NodesNum; NodeIndex++)
626 const FString NodeContext = FString::Printf(TEXT(
"Node %d"), NodeIndex);
634 TArray<FName> Participants;
652 const FString ContextMessage = FString::Printf(TEXT(
"Adding primary condition data for %s"), *NodeContext);
656 if (
Condition.IsSecondParticipantInvolved())
658 const FString ContextMessage = FString::Printf(TEXT(
"Adding secondary condition data for %s"), *NodeContext);
671 for (int32 EdgeIndex = 0; EdgeIndex < NumNodeChildren; EdgeIndex++)
682 const FString ContextMessage = FString::Printf(TEXT(
"Adding Edge text arguments data from %s, to Node %d"), *NodeContext, TargetIndex);
691 const FString ContextMessage = FString::Printf(TEXT(
"Adding events data for %s"), *NodeContext);
699 const FString ContextMessage = FString::Printf(TEXT(
"Adding text arguments data for %s"), *NodeContext);
711 TSet<FName> Participants;
718 if (!Participants.Contains(ExaminedName) || ExaminedName.IsNone())
723 Participants.Remove(ExaminedName);
735 FDlgLogger::Get().
Warning(TEXT(
"Trying to fill ParticipantsClasses, got a Participant name = None. Ignoring!"));
742 TArray<UClass*> NativeClasses;
743 TArray<UClass*> BlueprintClasses;
752 if (Struct.ParticipantName == NAME_None || Struct.ParticipantClass !=
nullptr)
758 if (BlueprintClassesMap.Contains(Struct.ParticipantName))
760 const TArray<FDlgClassAndObject>&
Array = BlueprintClassesMap.FindChecked(Struct.ParticipantName);
761 if (
Array.Num() == 1)
763 Struct.ParticipantClass =
Array[0].Class;
768 if (Struct.ParticipantClass ==
nullptr && NativeClassesMap.Contains(Struct.ParticipantName))
770 const TArray<FDlgClassAndObject>&
Array = NativeClassesMap.FindChecked(Struct.ParticipantName);
771 if (
Array.Num() == 1)
773 Struct.ParticipantClass =
Array[0].Class;
784 return Nodes[NodeIndex]->GetGUID();
795 return *NodeIndexPtr;
815 for (int32 NodeIndex = 0; NodeIndex <
Nodes.Num(); NodeIndex++)
828 Nodes[NodeIndex] = InNode;
844 if (!
Nodes.IsValidIndex(NodeIndex))
863 bool bHasEndNode =
false;
868 Node->SetFlags(RF_Transactional);
876 if (!bHasEndNode &&
Nodes.Num() > 0)
878 auto* EndNode = ConstructDialogueNode<UDlgNode_End>();
879 EndNode->SetNodeParticipantName(
Nodes[0]->GetNodeParticipantName());
885 for (int32 i = 0; i <
Nodes.Num() - 1; ++i)
890 if (!Node->IsA<
UDlgNode_End>() && NodeChildren.Num() == 0)
902 return GetTextFilePathName(GetDefault<UDlgSystemSettings>()->DialogueTextFormat, bAddExtension);
930 if (TextFilePathName.IsEmpty())
934 TEXT(
"Can't delete text file for Dialogue = `%s` because the file path name is empty :O"),
940 const FString FullPathName = TextFilePathName + FileExtension;
947 for (
const FString& FileExtension : GetDefault<UDlgSystemSettings>()->GetAllTextFileExtensions())
961 static const TCHAR* Separator = TEXT(
"/");
964 FString PathName = FPaths::GetBaseFilename(AssetPathName,
false);
968 FString ContentDir = FPaths::ProjectContentDir();
969 if (!PathName.RemoveFromStart(TEXT(
"/Game/")))
972 TArray<FString> PathParts;
973 PathName.ParseIntoArray(PathParts, Separator);
974 if (PathParts.Num() > 0)
976 const FString PluginName = PathParts[0];
977 const FString PluginDir = FPaths::ProjectPluginsDir() / PluginName;
980 if (FPaths::DirectoryExists(PluginDir))
982 ContentDir = PluginDir / TEXT(
"Content/");
986 PathParts.RemoveAt(0);
987 PathName = FString::Join(PathParts, Separator);
991 return ContentDir + PathName;
998#undef LOCTEXT_NAMESPACE
FDevVersionRegistration GRegisterDlgDialogueObjectVersion(FDlgDialogueObjectVersion::GUID, FDlgDialogueObjectVersion::LatestVersion, TEXT("Dev-DlgDialogue"))
void UpdateDialogueToVersion_ConvertedNodesToUObject(UDlgDialogue *Dialogue)
void UpdateDialogueToVersion_UseOnlyOneOutputAndInputPin(UDlgDialogue *Dialogue)
EDlgDialogueTextFormat
UENUM()
void InitializeParser(const FString &FilePath) override
void ReadAllProperty(const UStruct *ReferenceClass, void *TargetObject, UObject *DefaultObjectOuter=nullptr) override
bool ExportToFile(const FString &FileName) override
void Write(const UStruct *StructDefinition, const void *Object) override
static FORCEINLINE bool IsPathInProjectDirectory(const FString &Path)
static bool DeleteFile(const FString &PathName, bool bVerbose=true)
static bool GetAllClassesImplementingInterface(const UClass *InterfaceClass, TArray< UClass * > &OutNativeClasses, TArray< UClass * > &OutBlueprintClasses)
static TMap< FName, TArray< FDlgClassAndObject > > ConvertDialogueParticipantsClassesIntoMap(const TArray< UClass * > &Classes)
The DlgJsonParser class mostly adapted for Dialogues, copied from FJsonObjectConverter See IDlgParser...
void ReadAllProperty(const UStruct *ReferenceClass, void *TargetObject, UObject *DefaultObjectOuter=nullptr) override
void InitializeParser(const FString &FilePath) override
The DlgJsonWriter class mostly adapted for Dialogues, copied from FJsonObjectConverter See IDlgWriter...
bool ExportToFile(const FString &FileName) override
void Write(const UStruct *StructDefinition, const void *ContainerPtr) override
static FDlgLogger & Get()
FORCEINLINE void Warning(const FString &Message)
void Warningf(const FmtType &Fmt, Types... Args)
void Infof(const FmtType &Fmt, Types... Args)
void Errorf(const FmtType &Fmt, Types... Args)
void Debugf(const FmtType &Fmt, Types... Args)
UCLASS(BlueprintType, Meta = (DisplayThumbnail = "true"))
static FString GetTextFilePathNameFromAssetPathName(const FString &AssetPathName)
void ImportFromFileFormat(EDlgDialogueTextFormat TextFormat)
TSet< FName > AllSpeakerStates
UPROPERTY(VisibleAnywhere, AdvancedDisplay, Category = "Dialogue", Meta = (DlgNoExport))
TMap< FName, FDlgParticipantData > ParticipantsData
UPROPERTY(VisibleAnywhere, AdvancedDisplay, Category = "Dialogue", Meta = (DlgNoExport))
FDlgParticipantData & GetParticipantDataEntry(FName ParticipantName, FName FallbackParticipantName, bool bCheckNone, const FString &ContextMessage)
bool IsValidNodeIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue")
void GetAllParticipantNames(TSet< FName > &OutSet) const
UFUNCTION(BlueprintPure, Category = "Dialogue")
UDlgNode * StartNode
UPROPERTY(Instanced)
void SetNodes(const TArray< UDlgNode * > &InNodes)
void SetNode(int32 NodeIndex, UDlgNode *InNode)
FGuid GUID
UPROPERTY(VisibleAnywhere, Category = "Dialogue")
void ExportToFile() const
bool HasGUID() const
UFUNCTION(BlueprintPure, Category = "Dialogue|GUID")
FGuid GetNodeGUIDForIndex(int32 NodeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue", DisplayName = "Get Node GUID For Index")
bool IsEndNode(int32 NodeIndex) const
void AddConditionsDataFromNodeEdges(const UDlgNode *Node, int32 NodeIndex)
bool DeleteTextFileForExtension(const FString &FileExtension) const
void SetStartNode(UDlgNode *InStartNode)
void Serialize(FArchive &Ar) override
bool DeleteTextFileForTextFormat(EDlgDialogueTextFormat TextFormat) const
void PostDuplicate(bool bDuplicateForPIE) override
TMap< FGuid, int32 > NodesGUIDToIndexMap
UPROPERTY(VisibleAnywhere, AdvancedDisplay, Category = "Dialogue", DisplayName = "Nodes GUID To Index...
void UpdateGUIDToIndexMap(const UDlgNode *Node, int32 NodeIndex)
int32 GetNodeIndexForGUID(const FGuid &NodeGUID) const
UFUNCTION(BlueprintPure, Category = "Dialogue", DisplayName = "Get Node Index For GUID")
void PostRename(UObject *OldOuter, FName OldName) override
void ExportToFileFormat(EDlgDialogueTextFormat TextFormat) const
FName Name
UPROPERTY(VisibleAnywhere, Category = "Dialogue")
void PreSave(const class ITargetPlatform *TargetPlatform) override
bool IsInProjectDirectory() const
TArray< FDlgParticipantClass > ParticipantsClasses
UPROPERTY(EditAnywhere, EditFixedSize, Category = "Dialogue")
void RebuildAndUpdateNode(UDlgNode *Node, const UDlgSystemSettings &Settings, bool bUpdateTextsNamespacesAndKeys)
bool DeleteAllTextFiles() const
void PostInitProperties() override
FString GetTextFilePathName(bool bAddExtension=true) const
void UpdateAndRefreshData(bool bUpdateTextsNamespacesAndKeys=false)
FName GetDialogueFName() const
UFUNCTION(BlueprintPure, Category = "Dialogue")
void PostEditImport() override
TArray< UDlgNode * > Nodes
UPROPERTY(AdvancedDisplay, EditFixedSize, Instanced, Meta = (DlgWriteIndex))
static TArray< UDlgDialogue * > GetDialoguesWithDuplicateGUIDs()
UCLASS(BlueprintType, ClassGroup = "Dialogue")
UCLASS(BlueprintType, Abstract, EditInlineNew, ClassGroup = "Dialogue")
virtual void GetAssociatedParticipants(TArray< FName > &OutArray) const
virtual const TArray< FDlgTextArgument > & GetTextArguments() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
FGuid GetGUID() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual void RebuildTextArguments(bool bEdges, bool bUpdateGraphNode=true)
virtual void AddAllSpeakerStatesIntoSet(TSet< FName > &OutStates) const
virtual const TArray< FDlgEdge > & GetNodeChildren() const
Gets this nodes children (edges) as a const/mutable array.
virtual int32 GetNumNodeChildren() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual FName GetNodeParticipantName() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual const TArray< FDlgEvent > & GetNodeEnterEvents() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual void UpdateTextsNamespacesAndKeys(const UDlgSystemSettings &Settings, bool bEdges, bool bUpdateGraphNode=true)
virtual const FDlgEdge & GetNodeChildAt(int32 EdgeIndex) const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
bool HasGUID() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
virtual void AddNodeChild(const FDlgEdge &InChild)
virtual void UpdateTextsValuesFromDefaultsAndRemappings(const UDlgSystemSettings &Settings, bool bEdges, bool bUpdateGraphNode=true)
virtual const TArray< FDlgCondition > & GetNodeEnterConditions() const
UFUNCTION(BlueprintPure, Category = "Dialogue|Node")
UCLASS(Config = Engine, DefaultConfig, meta = (DisplayName = "Dialogue System Settings"))
static FString GetTextFileExtension(EDlgDialogueTextFormat TextFormat)
static bool HasTextFileExtension(EDlgDialogueTextFormat TextFormat)
bool bAutoSetDefaultParticipantClasses
UPROPERTY(Category = "Dialogue", Config, EditAnywhere)
@ UseOnlyOneOutputAndInputPin
@ AddCustomObjectsToParticipantsData
@ ConvertedNodesToUObject
@ MergeVirtualParentAndSelectorTypes
int32 TargetIndex
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Dialogue|Edge", Meta = (ClampMin = -1))
FName SpeakerState
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue|Edge")
const TArray< FDlgTextArgument > & GetTextArguments() const
void AddConditionPrimaryData(const FDlgCondition &Condition)
void AddTextArgumentData(const FDlgTextArgument &TextArgument)
void AddEventData(const FDlgEvent &Event)
void AddConditionSecondaryData(const FDlgCondition &Condition)