Is it possible to create a LinkedList styled dialogue system?

I want to create a dialogue system that doesn’t have to be built at every new instance. Therefore, in my mind the best way to do this is to create a LinkedList with multiple possible branches, and end-nodes will have a type of interaction linked to an enum. (ie. accept quest, decline, open shop, etc.)

A simple way to do this is the following code:

USTRUCT(BlueprintType)
struct FDialogueTree
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Properties")
	TArray<FDialogueTree> DialogueNodes;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Properties")
	FText DialogueText;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Properties")
	int32 InteractType;
};

However, this resulted in catastrophic failure:

Error: 'Struct recursion via arrays is unsupported for properties.

Is there no workaround to this? A linked list like this is pretty standard practice; the leaf nodes are generally null pointers. If there is no way to correctly do this, what is generally used for dialogue options in Unreal? I’ve seen a few tutorials in this for Blueprints, but even then they seemed overly complex - Hard coding each option in. Are there any better options out there that I need to be aware of?

Reference your structures with an ID rather than a direct reference. There’s an ID field on your structure and the children list refer to their ID

 USTRUCT(BlueprintType)
 struct FDialogue
 {
     GENERATED_BODY()

     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Properties")
     FName ID;

     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Properties")
     TArray<FName> Children;

     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Properties")
     FText DialogueText;
 
     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Properties")
     int32 InteractType;
 };

Save all these dialogs in a flat array somewhere

 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogues")
 TArray<FDialog> DialogList;
 
 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogues")
 FName RootDialogID;

You can create a class on top of this for easier traversal and retrieval during runtime

Helper class for traversing the tree

class FDialogTree {
public:
	FDialogTree(const TArray<FDialog>& InDialogs) {
		for (const FDialog& Dialog : InDialogs) {
			DialogMap.Add(Dialog.ID, Dialog);
		}
	}

	FDialog Get(const FName& ID) {
		return DialogMap[ID];
	}
	
	TArray<FDialog> GetChildren(const FDialog& Dialog) {
		TArray<FDialog> Children;
		for (const FName& ChildID : Dialog.Children) {
			Children.Add(DialogMap[ChildID]);
		}
		return Children;
	}
	
private:
	TMap<FName, FDialog> DialogMap;
	
};

Helper class for editing the tree

class FDialogTreeEdit {
	FDialogTree(const TArray<FDialog>& InDialogs) {
		// ..
	}
	
	FDialog* CreateNew(const FName& ParentID) {
		// ...
	}
	
	TArray<FDialog> Save() {
		// ..
	}
	
};

Alternatively, use UObjects to store your data. They support recursive references, since they are pointers

This is great! Thank you! This is going to take a little bit to set up, but I am going to pre-emptively say that my question has been answered.

Thanks! Huge fan of your work, by the way!