Skip to main content

3.7.0 Code Changes

Narrative.uplugin

/Narrative/Narrative.uplugin

{
"FileVersion": 3,
"Version": 3,
"VersionName": "3.6.1",
"VersionName": "3.7.0",

...

"SupportURL": "mailto:contact@reubs.io",
"SupportURL": "mailto:contact@narrativetools.io",
"EngineVersion": "5.1.0",
"CanContainContent": true,
"Installed": false,
],
"Dependencies": [
"GameplayTags"
},
{
"Name": "NarrativePreEditor",
"Type": "Editor",
"LoadingPhase": "PreDefault"

...

NarrativeDialogueEditor.Build.cs

/Narrative/Source/NarrativeDialogueEditor/NarrativeDialogueEditor.Build.cs
 using UnrealBuildTool;

"BlueprintGraph",
"GraphEditor",
"GameplayTags",

...

"UMG"
}
"UMG",
"ToolMenus", "NarrativePreEditor"
}
);

DialogueBlueprint.cpp

/Narrative/Source/NarrativeDialogueEditor/Public/DialogueBlueprint.cpp

#include "DialogueBlueprint.h"

...

#include "DialogueSM.h"
#include "DialogueBlueprintGeneratedClass.h"

UDialogueBlueprint::UDialogueBlueprint()


IDialogueEditor.h

/Narrative/Source/NarrativeDialogueEditor/Public/IDialogueEditor.h
#pragma once

...

};// removed-end
// called when the dialogue icon is clicked on
virtual void JumpOrAddDialogueNode(FName NodeID) = 0;

};


DialogueBlueprint.h

/Narrative/Source/NarrativeDialogueEditor/Public/DialogueBlueprint.h
#pragma once

...

virtual bool CanAlwaysRecompileWhilePlayingInEditor() const override { return true; }
virtual bool CanAlwaysRecompileWhilePlayingInEditor() const override { return false; }

virtual void PostLoad() override;
virtual bool IsValidForBytecodeOnlyRecompile() const override { return false; }


DialogueEditorModes.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorModes.cpp
#include "DialogueEditorModes.h"
#include "DialogueEditorTabs.h"
#include "DialogueEditorToolbar.h"
#include "DialogueEditorDetails.h"
#include "DialogueEditorTabFactories.h"
#include "BlueprintEditorTabs.h"

...



DialogueGraphNode_NPC.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphNode_NPC.cpp
 #include "DialogueGraphNode_NPC.h"
#include "DialogueGraph.h"
#include "EdGraph/EdGraphSchema.h"

...



DialogueGraphEditor.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphEditor.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "Misc/NotifyHook.h"
#include "EditorUndoClient.h"
#include "Widgets/SWidget.h"
void SwitchToQuestAsset();
bool CanSwitchToQuestAsset();


...

/** Check to see if we can create a new state node */
bool CanCreateNewState() const;

/** Check to see if we can create a new action node */
bool CanCreateNewAction() const;

/** Create the menu used to make a new state node */
TSharedRef<SWidget> HandleCreateNewStateMenu() const;

/** Create the menu used to make a new action node */
TSharedRef<SWidget> HandleCreateNewActionMenu() const;

/** Handler for when a node class is picked */
void HandleNewNodeClassPicked(UClass* InClass) const;

virtual void OnCreateComment() override;

// adds new nodes to the graph from given assets
void AddNodesFromAssets(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* DestinationGraph, UEdGraphNode* SelectedNode) const;

void SetGraphEditable(const bool Editable);
bool IsGraphEditable() const;

/* IDialogueEditor */
virtual void JumpOrAddDialogueNode(FName NodeID) override;
/* IDialogueEditor */

protected:

void OnBeginPIE(bool bSimulating);
void OnEndPIE(bool bSimulating);


#if WITH_EDITORONLY_DATA
/// debugger instance for this graph
TSharedPtr<class FDialogueDebugger> Debugger;
#endif

...



DialogueEditorSettings.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorSettings.cpp
 
#include "DialogueEditorSettings.h"


DialogueConnectionDrawingPolicy.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueConnectionDrawingPolicy.cpp
 #include "DialogueConnectionDrawingPolicy.h"
#include "QuestSM.h"
#include "NarrativeDialogueSettings.h"
#include "DialogueGraphNode_NPC.h"
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
using FGraphLocationType = FVector2f;
#else
using FGraphLocationType = FVector2D;
#endif

...

FVector2D FDialogueGraphConnectionDrawingPolicy::ComputeSplineTangent(const FVector2D& Start, const FVector2D& End) const
FGraphLocationType FDialogueGraphConnectionDrawingPolicy::ComputeSplineTangent(const FGraphLocationType& Start, const FGraphLocationType& End) const
{

if (const UNarrativeDialogueSettings* DialogueSettings = GetDefault<UNarrativeDialogueSettings>())

...

const FVector2D DeltaPos = End - Start;
const FGraphLocationType DeltaPos = End - Start;
const bool bGoingForward = DialogueSettings->bEnableVerticalWiring ? DeltaPos.Y >= 0.f : DeltaPos.X >= 0.0f;

if (const UDialogueEditorSettings* DialogueEditorSettings = GetDefault<UDialogueEditorSettings>())

...

return Result.GetRotated(90.f);
return FGraphLocationType(Result.GetRotated(90.f));
}
else
{
return Result;
return FGraphLocationType(Result);

...

void FDialogueGraphConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params)
void FDialogueGraphConnectionDrawingPolicy::DrawConnection(int32 LayerId, const FGraphLocationType& Start, const FGraphLocationType& End, const FConnectionParams& Params)
{
if (const UNarrativeDialogueSettings* DialogueSettings = GetDefault<UNarrativeDialogueSettings>())
{

...

FVector2D Halfway = (Start + End) / 2;
FGraphLocationType Halfway = (Start + End) / 2;

if (DialogueSettings->bEnableVerticalWiring)
{

...

FVector2D ArrowPoint = Halfway - ArrowRadius * 2.f;
FGraphLocationType ArrowPoint = Halfway - ArrowRadius * 2.f;

const float Angle = DialogueSettings->bEnableVerticalWiring ? -HALF_PI : PI;


...

TOptional<FVector2D>(),
TOptional<FGraphLocationType>(),
FSlateDrawElement::RelativeToElement,
Params.WireColor
);

...

//Pivot for wires isnt correct so shift it, kinda hacky but works so meh
if (DialogueSettings->bEnableVerticalWiring)
{
FConnectionDrawingPolicy::DrawConnection(LayerId, Start - FVector2D(12.f * ZoomFactor, 0.f), End + FVector2D(16.f * ZoomFactor, 0.f), Params);
}
else
{
FConnectionDrawingPolicy::DrawConnection(LayerId, Start, End, Params);
}
FConnectionDrawingPolicy::DrawConnection(LayerId, Start, End, Params);
}
}



void FDialogueGraphConnectionDrawingPolicy::DrawPreviewConnector(const FGeometry& PinGeometry, const FGraphLocationType& StartPoint, const FGraphLocationType& EndPoint, UEdGraphPin* Pin)
{
bool bDrawVerticalWiring = false;

if (const UNarrativeDialogueSettings* DialogueSettings = GetDefault<UNarrativeDialogueSettings>())
{
bDrawVerticalWiring = DialogueSettings->bEnableVerticalWiring;
}

FGraphLocationType AdjustedStartPoint = StartPoint;

if (bDrawVerticalWiring)
{
AdjustedStartPoint.X -= PinGeometry.GetDrawSize().X / 2;
AdjustedStartPoint.Y -= PinGeometry.GetDrawSize().Y / 2;
}

FConnectionDrawingPolicy::DrawPreviewConnector(PinGeometry, AdjustedStartPoint, EndPoint, Pin);
}

void FDialogueGraphConnectionDrawingPolicy::DrawSplineWithArrow(const FGeometry& StartGeom, const FGeometry& EndGeom, const FConnectionParams& Params)
{
bool bDrawVerticalWiring = false;

if (const UNarrativeDialogueSettings* DialogueSettings = GetDefault<UNarrativeDialogueSettings>())
{
bDrawVerticalWiring = DialogueSettings->bEnableVerticalWiring;
}

if (bDrawVerticalWiring)
{

const FGraphLocationType StartPoint = FNarrativeGeometryHelper::HorizontalBottomOf(StartGeom);
const FGraphLocationType EndPoint = FNarrativeGeometryHelper::HorizontalTopOf(EndGeom);

FConnectionDrawingPolicy::DrawSplineWithArrow(StartPoint, EndPoint, Params);
}
else
{
//@TODO: These values should be pushed into the Slate style, they are compensating for a bit of
// empty space inside of the pin brush images.
const float StartFudgeX = 4.0f;
const float EndFudgeX = 4.0f;
const FGraphLocationType StartPoint = FGeometryHelper::VerticalMiddleRightOf(StartGeom) - FVector2D(StartFudgeX, 0.0f);
const FGraphLocationType EndPoint = FGeometryHelper::VerticalMiddleLeftOf(EndGeom) - FVector2D(ArrowRadius.X - EndFudgeX, 0);

FConnectionDrawingPolicy::DrawSplineWithArrow(StartPoint, EndPoint, Params);
}
}

...



NarrativeDialogueEditorModule.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/NarrativeDialogueEditorModule.cpp
 #include "NarrativeDialogueEditorModule.h"
#include "IDialogueEditor.h"

...

#include "DialogueEditorDetails.h"
#include "DialogueEditorSettings.h"
#include "NarrativeDialogueSettings.h"
#include "EdGraphUtilities.h"

...

#include "ISettingsSection.h"
#include "ISettingsContainer.h"
#include <ISettingsCategory.h>
#include "KismetCompiler.h"
#include "Engine/World.h"
#include "DialogueBlueprintCompiler.h"
#include "NodeSelector/DialogueNodeSelectorPropertyCustomization.h"
#include "NodeSelector/GraphPin_DialogueNodeSelector.h"

...



void FNarrativeDialogueEditorModule::StartupModule()
{
FDialogueEditorStyle::Initialize();

FEdGraphUtilities::RegisterVisualPinFactory(MakeShared<FPinFactoryDialogueNodeSelector>());

...

PropertyModule.RegisterCustomClassLayout("DialogueNode_NPC", FOnGetDetailCustomizationInstance::CreateStatic(&FDialogueEditorDetails::MakeInstance));
PropertyModule.RegisterCustomClassLayout("DialogueNode_Player", FOnGetDetailCustomizationInstance::CreateStatic(&FDialogueEditorDetails::MakeInstance));
//PropertyModule.RegisterCustomClassLayout("DialogueNode_NPC", FOnGetDetailCustomizationInstance::CreateStatic(&FDialogueEditorDetails::MakeInstance));
//PropertyModule.RegisterCustomClassLayout("DialogueNode_Player", FOnGetDetailCustomizationInstance::CreateStatic(&FDialogueEditorDetails::MakeInstance));
//PropertyModule.RegisterCustomPropertyTypeLayout("SpeakerSelector", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSpeakerSelectorCustomization::MakeInstance));

// Register custom property layout for FDialogueNodeSelector
PropertyModule.RegisterCustomPropertyTypeLayout(
"DialogueNodeSelector",
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FDialogueNodeSelectorPropertyTypeCustomization::MakeInstance)
);

PropertyModule.NotifyCustomizationModuleChanged();


PropertyModule.UnregisterCustomPropertyTypeLayout("DialogueNodeSelector");

FEdGraphUtilities::UnregisterVisualPinFactory(MakeShared<FPinFactoryDialogueNodeSelector>());

...

IMPLEMENT_MODULE(FNarrativeDialogueEditorModule, NarrativeDialogueEditor)// removed-end
IMPLEMENT_MODULE(FNarrativeDialogueEditorModule, NarrativeDialogueEditor)


DialogueEditorSettings.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorSettings.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "DialogueEditorSettings.generated.h"

/**


DialogueGraphNode.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphNode.cpp
 #include "DialogueGraphNode.h"
#include "DialogueGraph.h"


DialogueEditorToolbar.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorToolbar.cpp
 #include "DialogueEditorToolbar.h"
#include "Misc/Attribute.h"

...

#include "EditorStyleSet.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "DialogueGraphEditor.h"
#include "WorkflowOrientedApp/SModeWidget.h"
#include "DialogueEditorCommands.h"

...



DialogueBlueprintCompiler.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueBlueprintCompiler.cpp
 
#include "DialogueBlueprintCompiler.h"


DialogueGraphSchema.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphSchema.cpp
 #include "DialogueGraphSchema.h"
#include "DialogueGraphNode_Root.h"
#include "Sound/SoundBase.h"

...


void UDialogueGraphSchema::SetNodePosition(UEdGraphNode* Node, const FVector2D& Position) const
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
using FGraphLocationType = FVector2f;
#else
using FGraphLocationType = FVector2D;
#endif

void UDialogueGraphSchema::SetNodePosition(UEdGraphNode* Node, const FGraphLocationType& Position) const
{
Super::SetNodePosition(Node, Position);

#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
// Graph API gives FVector2f → convert to FVector2D for your data
DNode->DialogueNode->NodePos = FVector2D(Position);
#else
// Pre-5.6 NodePos already matches type
#endif
void UDialogueGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const
{
const FScopedTransaction Transaction(NSLOCTEXT("DialogueGraphSchema", "DialogueGraphSchema_BreakPinLinks", "Break Pin Links"));

Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation);
}

void UDialogueGraphSchema::DroppedAssetsOnGraph(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor? GEditor->GetEditorSubsystem<UAssetEditorSubsystem>() : nullptr;
if (!AssetEditorSubsystem)
{
return;
}

if (FDialogueGraphEditor* DialogueGraphEditor = static_cast<FDialogueGraphEditor*>(AssetEditorSubsystem->FindEditorForAsset(Graph->GetOuter(), true)))
{
DialogueGraphEditor->AddNodesFromAssets(Assets, GraphPosition, Graph, nullptr);
}
}

void UDialogueGraphSchema::DroppedAssetsOnNode(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphNode* Node) const
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor? GEditor->GetEditorSubsystem<UAssetEditorSubsystem>() : nullptr;
if (!AssetEditorSubsystem)
{
return;
}

UEdGraph* Graph = Node->GetGraph();
if (FDialogueGraphEditor* DialogueGraphEditor = static_cast<FDialogueGraphEditor*>(AssetEditorSubsystem->FindEditorForAsset(Graph->GetOuter(), true)))
{
DialogueGraphEditor->AddNodesFromAssets(Assets, GraphPosition, Graph, Node);
}
}

void UDialogueGraphSchema::DroppedAssetsOnPin(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphPin* Pin) const
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor? GEditor->GetEditorSubsystem<UAssetEditorSubsystem>() : nullptr;
if (!AssetEditorSubsystem)
{
return;
}

UEdGraph* Graph = Pin->GetOwningNode()->GetGraph();
if (FDialogueGraphEditor* DialogueGraphEditor = static_cast<FDialogueGraphEditor*>(AssetEditorSubsystem->FindEditorForAsset(Graph->GetOuter(), true)))
{
DialogueGraphEditor->AddNodesFromAssets(Assets, GraphPosition, Graph, Pin->GetOwningNode());
}
}

void UDialogueGraphSchema::GetAssetsGraphHoverMessage(const TArray<FAssetData>& Assets, const UEdGraph* HoverGraph, FString& OutTooltipText, bool& OutOkIcon) const
{
GetHoverMessage(Assets, OutTooltipText, OutOkIcon);
}

void UDialogueGraphSchema::GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const
{
GetHoverMessage(Assets, OutTooltipText, OutOkIcon);
}

void UDialogueGraphSchema::GetAssetsPinHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphPin* HoverPin, FString& OutTooltipText, bool& OutOkIcon) const
{
GetHoverMessage(Assets, OutTooltipText, OutOkIcon);
}

void UDialogueGraphSchema::GetHoverMessage(const TArray<struct FAssetData>& Assets, FString& OutTooltipText, bool& OutOkIcon) const
{
int32 NumSoundWaves = 0;
for (const FAssetData& Asset : Assets)
{
if (UObject* AssetObject = Asset.GetAsset(); AssetObject && AssetObject->IsA<USoundBase>())
{
NumSoundWaves++;
}
}

if (NumSoundWaves > 0)
{
OutOkIcon = true;
OutTooltipText = NumSoundWaves > 1? "Create nodes using sound waves." : "Create node using sound wave.";
}
}


...



DialogueAssetFactory.h

  • REMOVED

DialogueEditorTabFactories.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorTabFactories.cpp
 #include "DialogueEditorTabFactories.h"
#include "DialogueBlueprint.h"

...

#include "EditorStyleSet.h"
#include "Engine/Blueprint.h"
#include "DialogueEditorStyle.h"
#include "DialogueGraphEditor.h"


AssetTypeActions_DialogueBlueprint.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/AssetTypeActions_DialogueBlueprint.cpp
 
#include "AssetTypeActions_DialogueBlueprint.h"


DialogueGraph.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraph.cpp
 #include "DialogueGraph.h"
#include "Dialogue.h"
#include "NarrativeFunctionLibrary.h"
#include "NarrativeComponent.h"
#include "GraphEditAction.h"

...

#undef LOCTEXT_NAMESPACE // removed-end
#undef LOCTEXT_NAMESPACE


DialogueEditorDetails.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorDetails.h
 #pragma once

#include "IDetailChildrenBuilder.h"
#include "IDetailPropertyRow.h"
#include "PropertyHandle.h"

...



DialogueEditorTabs.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorTabs.cpp
 #include "DialogueEditorTabs.h"



DialogueEditorDetails.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorDetails.cpp
 #include "DialogueEditorDetails.h"
#include "DetailLayoutBuilder.h"

...

#include "DialogueBlueprint.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "Widgets/Input/SComboBox.h"
#include "IPropertyUtilities.h"
#include "PropertyHandle.h"

#define LOCTEXT_NAMESPACE "DialogueEditorDetails"

...



DialogueGraphEditor.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphEditor.cpp
 #include "DialogueGraphEditor.h"
#include "NarrativeDialogueEditorModule.h"

#include "DialogueDebugger.h"
#include "DialogueEditorStyle.h"
#include "SNodePanel.h"
#include "Sound/SoundBase.h"
FEditorDelegates::BeginPIE.RemoveAll(this);
FEditorDelegates::EndPIE.RemoveAll(this);

SetGraphEditable(true);

void FDialogueGraphEditor::SwitchToQuestAsset()
{
if (!DialogueBlueprint)
{
return;
}

UDialogue* Dialogue = Cast<UDialogue>(DialogueBlueprint->GeneratedClass.GetDefaultObject());

#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2
if (GEditor)
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (AssetEditorSubsystem && Dialogue)
{
AssetEditorSubsystem->OpenEditorForAsset(UBlueprint::GetBlueprintFromClass(Dialogue->EditorLinkedQuest));
}
}
#else
// UE 5.1 fallback (AssetEditorSubsystem does not exist)
if (GEditor)
{
GEditor->EditObject(Dialogue->EditorLinkedQuest);
}
#endif
}

bool FDialogueGraphEditor::CanSwitchToQuestAsset()
{
if (!DialogueBlueprint)
{
return false;
}

UDialogue* Dialogue = Cast<UDialogue>(DialogueBlueprint->GeneratedClass.GetDefaultObject());
return Dialogue? Dialogue->EditorLinkedQuest != nullptr : false;
}

// dialogue assets cannot be edited during PIE
FEditorDelegates::BeginPIE.AddRaw(this, &FDialogueGraphEditor::OnBeginPIE);
FEditorDelegates::EndPIE.AddRaw(this, &FDialogueGraphEditor::OnEndPIE);

// create a debugger instance before binding commands
Debugger = MakeShareable(new FDialogueDebugger);


...

//BindCommands();
RegisterMenus();


CreateInternalWidgets();

// setup debugger
Debugger->Setup(DialogueBlueprint, SharedThis(this));

if (UToolMenu* EditorToolbar = UToolMenus::Get()->FindMenu("AssetEditor.Dialogue Editor.Toolbar.DialogueEditor"))
{
Debugger->RegisterDebuggerToolbar(EditorToolbar);

FToolMenuSection& Section = EditorToolbar->AddSection("EditQuestAsset", FText::GetEmpty(), {"Debugging", EToolMenuInsertType::After});
Section.AddEntry(FToolMenuEntry::InitToolBarButton(
FDialogueEditorCommands::Get().OpenLinkedQuest,
LOCTEXT("DialogueGraphEditor_EditQuest", "Edit Quest"),
LOCTEXT("DialogueGraphEditor_EditQuest_ToolTip", "When A quest has this Dialogue Asset class is selected, this will switch to that quest."),
FSlateIcon(FDialogueEditorStyle::GetStyleSetName(), "DialogueEditor.QuestIcon"))
);
}


if (!FDialogueDebugger::IsPIENotSimulating())
{
SetGraphEditable(false);
}

ToolkitCommands->MapAction(
FDialogueEditorCommands::Get().OpenLinkedQuest,
FExecuteAction::CreateSP(this, &FDialogueGraphEditor::SwitchToQuestAsset),
FCanExecuteAction::CreateSP(this, &FDialogueGraphEditor::CanSwitchToQuestAsset));
// walk nodes to be pasted, and accumulate their position
FVector2D AvgNodePosition(0.0f, 0.0f);
for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
{
UEdGraphNode* Node = *It;
AvgNodePosition.X += Node->NodePosX;
AvgNodePosition.Y += Node->NodePosY;
}
// average the accumulated node positions
float InvNumNodes = 1.0f / static_cast<float>(PastedNodes.Num());
AvgNodePosition.X *= InvNumNodes;
AvgNodePosition.Y *= InvNumNodes;


...

PasteNode->NodePosX += 100.f;
PasteNode->NodePosY += 100.f;

PasteNode->SnapToGrid(16);
// set new paste node position, accounting for selected nodes average position
PasteNode->NodePosX = static_cast<int32>((PasteNode->NodePosX - AvgNodePosition.X) + GraphLocation.X);
PasteNode->NodePosY = static_cast<int32>((PasteNode->NodePosY - AvgNodePosition.Y) + GraphLocation.Y);

PasteNode->SnapToGrid(SNodePanel::GetSnapGridSize());

// Give new node a different Guid from the old one
PasteNode->CreateNewGuid();

FDialogueEditorCommands::Register();

if (Debugger)
{
Debugger->BindCommands(ToolkitCommands);
}
// read only
if (!IsGraphEditable())
{
return;
}

const FScopedTransaction Transaction(NSLOCTEXT("DialogueGraphEditor", "DialogueGraphEditor_EventFromNode", "Create event for node"));
DialogueBlueprint->Modify();

void FDialogueGraphEditor::AddNodesFromAssets(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* DestinationGraph, UEdGraphNode* SelectedNode) const
{
if (Assets.IsEmpty() || !DestinationGraph || !GEditor)
{
return;
}

TSharedPtr<SGraphEditor> FocusedGraph = FocusedGraphEdPtr.Pin();
if (!FocusedGraph.IsValid())
{
return;
}

// get dialogue
UDialogue* Dialogue = GetDialogueAsset()? Cast<UDialogue>(GetDialogueAsset()->GeneratedClass->GetDefaultObject()) : nullptr;

const FScopedTransaction Transaction(NSLOCTEXT("FDialogueGraphEditor", "FDialogueGraphEditor_NodeFromAssets", "Create nodes from assets"));
DestinationGraph->Modify();

// pin to connect to
UEdGraphPin* ConnectedPin = SelectedNode? SelectedNode->FindPin(NAME_None, EGPD_Output) : nullptr;

FocusedGraph->ClearSelectionSet();

FVector2D NewNodePos = GraphPosition;
for (const FAssetData& Asset : Assets)
{
// currently sound waves are only supported
if (USoundBase* SoundWave = Cast<USoundBase>(Asset.GetAsset()))
{
// check if there is a speaker in the asset name
const FString WavName = SoundWave->GetFName().ToString();
const FSpeakerInfo* Speaker = Dialogue->Speakers.FindByPredicate([&WavName](const FSpeakerInfo& SpeakerInfo)
{
return WavName.Contains(SpeakerInfo.SpeakerID.ToString());
});

// create new graph node
const UClass* NodeClass = (Speaker && Speaker->bIsPlayer) || WavName.Contains("Player")? UDialogueGraphNode_Player::StaticClass() : UDialogueGraphNode_NPC::StaticClass();
UDialogueGraphNode* NewGraphNode = NewObject<UDialogueGraphNode>(DestinationGraph, NodeClass);
if (!NewGraphNode)
{
continue;
}

FDialogueSchemaAction_NewNode AddNewNode;
AddNewNode.NodeTemplate = NewGraphNode;

// create node and assign sound wave
AddNewNode.PerformAction(DestinationGraph, ConnectedPin, NewNodePos);
NewGraphNode->DialogueNode->Line.DialogueSound = SoundWave;

// offset next node position
NewNodePos.X += 500.0f;

// when node is a npc node, set the speaker to the found speaker if any
if (UDialogueNode_NPC* NPCNode = Cast<UDialogueNode_NPC>(NewGraphNode->DialogueNode))
{
FName SpeakerID = Dialogue->Speakers.IsValidIndex(0)? Dialogue->Speakers[0].SpeakerID : NAME_None;

if (Speaker)
{
SpeakerID = Speaker->SpeakerID;
}

NPCNode->SetSpeakerID(SpeakerID);
}

// generate an id
NewGraphNode->DialogueNode->GenerateIDFromText();

DestinationGraph->NotifyGraphChanged();

// add new node to selection
FocusedGraph->SetNodeSelection(NewGraphNode, true);

GetDialogueAsset()->PostEditChange();
GetDialogueAsset()->MarkPackageDirty();

}
}
}

void FDialogueGraphEditor::SetGraphEditable(const bool Editable)
{
if (IsValid(DialogueBlueprint) && IsValid(DialogueBlueprint->DialogueGraph))
{
DialogueBlueprint->DialogueGraph->bEditable = Editable;
}
}

bool FDialogueGraphEditor::IsGraphEditable() const
{
return IsValid(DialogueBlueprint) && IsValid(DialogueBlueprint->DialogueGraph)? DialogueBlueprint->DialogueGraph->bEditable : false;
}

void FDialogueGraphEditor::JumpOrAddDialogueNode(FName NodeID)
{
// get dialogue
UDialogue* Dialogue = GetDialogueAsset()? Cast<UDialogue>(GetDialogueAsset()->GeneratedClass->GetDefaultObject()) : nullptr;
UEdGraph* DestinationGraph = Dialogue? GetDialogueAsset()->DialogueGraph : nullptr;
if (!DestinationGraph)
{
return;
}

// check if a node with the state ID already exists
TObjectPtr<UEdGraphNode>* ExistingNode = DialogueBlueprint->DialogueGraph->Nodes.FindByPredicate([this, &NodeID](const UEdGraphNode* Node)
{
const UDialogueGraphNode* DialogueNode = Cast<UDialogueGraphNode>(Node);
return DialogueNode? DialogueNode->DialogueNode->GetID() == NodeID : false;
});

// when no existing node was found, create a new one
if (!ExistingNode)
{
const FScopedTransaction Transaction(NSLOCTEXT("FDialogueGraphEditor", "FDialogueGraphEditor_NewNodeFromQuest", "Create node from Quest State"));
DialogueBlueprint->Modify();
DestinationGraph->Modify();

// create new graph node
UDialogueGraphNode* NewGraphNode = NewObject<UDialogueGraphNode>(DestinationGraph, UDialogueGraphNode_NPC::StaticClass());
if (!NewGraphNode)
{
return;
}

FDialogueSchemaAction_NewNode AddNewNode;
AddNewNode.NodeTemplate = NewGraphNode;

// create node
AddNewNode.PerformAction(DestinationGraph, nullptr, {});

// set NPC speaker
if (UDialogueNode_NPC* NPCNode = Cast<UDialogueNode_NPC>(NewGraphNode->DialogueNode))
{
const FName SpeakerID = Dialogue->Speakers.IsValidIndex(0)? Dialogue->Speakers[0].SpeakerID : NAME_None;
NPCNode->SetSpeakerID(SpeakerID);
}

// generate an id
NewGraphNode->DialogueNode->SetID(NodeID);

DestinationGraph->NotifyGraphChanged();

GetDialogueAsset()->PostEditChange();
GetDialogueAsset()->MarkPackageDirty();

JumpToNode(NewGraphNode);
}
else
{
JumpToNode(*ExistingNode);
}
}

void FDialogueGraphEditor::OnBeginPIE(bool bSimulating)
{
SetGraphEditable(false);
}

void FDialogueGraphEditor::OnEndPIE(bool bSimulating)
{
SetGraphEditable(true);
}


...


CreateDialogueCommandList();

SGraphEditor::FGraphEditorEvents InEvents;
InEvents.OnSelectionChanged = SGraphEditor::FOnSelectionChanged::CreateSP(this, &FDialogueGraphEditor::OnSelectedNodesChanged);

...


void FDialogueGraphEditor::ExtendToolbar()
{



SDialogueGraphNode.h

/Narrative/Source/NarrativeDialogueEditor/Private/SDialogueGraphNode.h
 #pragma once

EVisibility GetActiveStateImageVisibility() const;


...



DialogueGraphNode_Root.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphNode_Root.cpp
 #include "DialogueGraphNode_Root.h"
#include "DialogueEditorTypes.h"
#include "DialogueEditorSettings.h"

...



DialogueGraphSchema.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphSchema.h
#pragma once

...

virtual void SetNodePosition(UEdGraphNode* Node, const FVector2D& Position) const;
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
virtual void SetNodePosition(UEdGraphNode* Node, const FVector2f& GraphLocation) const;
#else
virtual void SetNodePosition(UEdGraphNode* Node, const FVector2D& GraphLocation) const;
#endif
virtual EGraphType GetGraphType(const UEdGraph* TestEdGraph) const;
virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const override;
virtual void DroppedAssetsOnGraph(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* Graph) const override;
virtual void DroppedAssetsOnNode(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphNode* Node) const override;
virtual void DroppedAssetsOnPin(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphPin* Pin) const override;
virtual void GetAssetsGraphHoverMessage(const TArray<FAssetData>& Assets, const UEdGraph* HoverGraph, FString& OutTooltipText, bool& OutOkIcon) const override;
virtual void GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const override;
virtual void GetAssetsPinHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphPin* HoverPin, FString& OutTooltipText, bool& OutOkIcon) const override;
//~ End EdGraphSchema Interface


void GetHoverMessage(const TArray<FAssetData>& Assets, FString& OutTooltipText, bool& OutOkIcon) const;

...



AssetTypeActions_DialogueAsset.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/AssetTypeActions_DialogueAsset.cpp
 
#include "AssetTypeActions_DialogueAsset.h"
#include "DialogueGraphEditor.h"

...



DialogueGraph.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraph.h
#pragma once

...

#include "GraphEditAction.h"
#include "DialogueGraph.generated.h"

class UDialogueGraphNode;


DialogueEditorCommands.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorCommands.h
 #pragma once

// jumps to the current active node
TSharedPtr<FUICommandInfo> JumpToActiveNode;

// When A quest has this Dialogue Asset class is selected, this will switch to that quest.
TSharedPtr<FUICommandInfo> OpenLinkedQuest;


...



DialogueGraphNode_Player.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphNode_Player.cpp
 #include "DialogueGraphNode_Player.h"
#include "DialogueGraph.h"
#include "EdGraph/EdGraphSchema.h"

...

#include "DialogueSM.h"
#include "Dialogue.h"
#include "DialogueGraphNode.h"
#include "DialogueEditorSettings.h"

#define LOCTEXT_NAMESPACE "DialogueGraphNode_Player"


DialogueConnectionDrawingPolicy.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueConnectionDrawingPolicy.h
 #pragma once


class FNarrativeGeometryHelper
{

public:
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
static FVector2f HorizontalBottomOf(const FGeometry& SomeGeometry)
{
const FVector2f GeometryDrawSize = SomeGeometry.GetDrawSize();
return FVector2f(
SomeGeometry.AbsolutePosition.X + (GeometryDrawSize.X / 2.f),
SomeGeometry.AbsolutePosition.Y + GeometryDrawSize.Y);
};

static FVector2f HorizontalTopOf(const FGeometry& SomeGeometry)
{
const FVector2f GeometryDrawSize = SomeGeometry.GetDrawSize();
return FVector2f(
SomeGeometry.AbsolutePosition.X + (GeometryDrawSize.X / 2.f),
SomeGeometry.AbsolutePosition.Y);
};
#else
static FVector2d HorizontalBottomOf(const FGeometry& SomeGeometry)
{
const FVector2d GeometryDrawSize = SomeGeometry.GetDrawSize();
return FVector2d(
SomeGeometry.AbsolutePosition.X + (GeometryDrawSize.X / 2.f),
SomeGeometry.AbsolutePosition.Y + GeometryDrawSize.Y);
};

static FVector2d HorizontalTopOf(const FGeometry& SomeGeometry)
{
const FVector2d GeometryDrawSize = SomeGeometry.GetDrawSize();
return FVector2d(
SomeGeometry.AbsolutePosition.X + (GeometryDrawSize.X / 2.f),
SomeGeometry.AbsolutePosition.Y);
};
#endif

};

...

virtual FVector2D ComputeSplineTangent(const FVector2D& Start, const FVector2D& End) const override;
void DetermineWiringStyle(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, /*inout*/ FConnectionParams& Params);
virtual void DrawConnection(int32 LayerId, const FVector2D& Start, const FVector2D& End, const FConnectionParams& Params) override;

#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
using FGraphLocationType = FVector2f;
#else
using FGraphLocationType = FVector2D;
#endif

virtual FGraphLocationType ComputeSplineTangent(const FGraphLocationType& Start, const FGraphLocationType& End) const override;
virtual void DrawConnection(int32 LayerId, const FGraphLocationType& Start, const FGraphLocationType& End, const FConnectionParams& Params) override;
virtual void DrawPreviewConnector(const FGeometry& PinGeometry, const FGraphLocationType& StartPoint, const FGraphLocationType& EndPoint, UEdGraphPin* Pin) override;

virtual void DrawSplineWithArrow(const FGeometry& StartGeom, const FGeometry& EndGeom, const FConnectionParams& Params);

const FSlateBrush* BacklinkImage;

...

};// removed-end
};


DialogueEditorStyle.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorStyle.cpp
 #include "DialogueEditorStyle.h"
#include "Styling/SlateStyle.h"
// edit quest asset icon
StyleSet->Set(FName(TEXT("DialogueEditor.QuestIcon")), new IMAGE_BRUSH("Quest", FVector2D(64.f, 64.f)));


...



DialogueNodeUserWidget.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueNodeUserWidget.cpp
 #include "DialogueNodeUserWidget.h"
#include <Components/VerticalBox.h>
#include <Components/Overlay.h>
#include <Components/VerticalBoxSlot.h>
#include <Blueprint/WidgetTree.h>

...



DialogueEditorCommands.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorCommands.cpp
 #include "DialogueEditorCommands.h"
#include "DialogueEditorStyle.h"
UI_COMMAND(JumpToActiveNode, "Jump To Active Node", "Jumps to the active node.", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(OpenLinkedQuest, "Edit Quest", "open connected quest.", EUserInterfaceActionType::Button, FInputChord());

...

#undef LOCTEXT_NAMESPACE // removed-end
#undef LOCTEXT_NAMESPACE


SDialogueGraphPin.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/SDialogueGraphPin.cpp
 

#include "SDialogueGraphPin.h"


DialogueAssetFactory.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueAssetFactory.cpp
 
#include "DialogueAssetFactory.h"

...

#include "BlueprintEditorSettings.h"
#include "DialogueGraph.h"
#include "DialogueGraphSchema.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "DialogueEditorSettings.h"

#define LOCTEXT_NAMESPACE "DialogueAssetFactory"


DialogueEditorModes.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorModes.h
#pragma once

...

#include "WorkflowOrientedApp/ApplicationMode.h"
#include "BlueprintEditorModes.h"

/** Application mode for main behavior tree editing mode */


DialogueGraphNode.h

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueGraphNode.h
#pragma once

...

class UK2Node_CustomEvent* OnPlayedCustomNode;
class UK2Node_CustomEvent* OnPlayedCustomNode;

UFUNCTION()
void OnStartedOrFinished(UDialogueNode* Node, bool bStarted);
void OnStartedOrFinished(UDialogueNode* Node, bool bStarted);

uint8 bActiveNode : 1;


...



DialogueEditorTypes.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/DialogueEditorTypes.cpp
 #include "DialogueEditorTypes.h"



SDialogueGraphNode.cpp

/Narrative/Source/NarrativeDialogueEditor/Private/SDialogueGraphNode.cpp
 #include "SDialogueGraphNode.h"
#include "DialogueGraphNode.h"

...

#include "DialogueGraph.h"
#include "SGraphPanel.h"
#include "ScopedTransaction.h"
#include "DialogueSM.h"

...

#include "DialogueEditorStyle.h"
#include "DialogueEditorSettings.h"
#include <Blueprint/UserWidget.h>
#include "Components/VerticalBox.h"
#include <Slate/SObjectWidget.h>
#include "DialogueDebugger.h"

// dialogue widget

...

[
DialogueNodeWidgetRef.ToSharedRef()
]
[
DialogueNodeWidgetRef.ToSharedRef()
]

// debugger overlay
+ SOverlay::Slot()
[
SNew(SImage)
.Image(FAppStyle::GetBrush(TEXT("Graph.Node.DiffHighlight")))
// yellow
.ColorAndOpacity(FLinearColor(0.0f, 1.0f, 0.0f, 0.75f))
.Visibility_Raw(this, &SDialogueGraphNode::GetActiveStateImageVisibility)
]
];
}
else // Old style yucky slate ones
EVisibility SDialogueGraphNode::GetActiveStateImageVisibility() const
{
const UDialogueGraphNode* DialogueGraphNode = Cast<UDialogueGraphNode>(GraphNode);
return DialogueGraphNode && DialogueGraphNode->bActiveNode? EVisibility::HitTestInvisible : EVisibility::Collapsed;
}


...

#undef LOCTEXT_NAMESPACE// removed-end
#undef LOCTEXT_NAMESPACE


NarrativeQuestEditor.Build.cs

/Narrative/Source/NarrativeQuestEditor/NarrativeQuestEditor.Build.cs
 using UnrealBuildTool;

"GameplayTags",

...

"UMG"
"UMG",
"ToolMenus",
"NarrativeDialogueEditor", "NarrativePreEditor" // NOTE: here for quick creating dialogue nodes from quest states
}
);



QuestBlueprint.h

/Narrative/Source/NarrativeQuestEditor/Public/QuestBlueprint.h
 #pragma once



QuestNodeUserWidget.h

/Narrative/Source/NarrativeQuestEditor/Public/QuestNodeUserWidget.h
 
UFUNCTION(BlueprintCallable, Category="Dialogue", meta=(DevelopmentOnly))
void AddOrJumpToMatchingDialogueNode();

UFUNCTION(BlueprintCallable, Category="Dialogue", meta=(DevelopmentOnly))
bool DoesMatchingDialogueNodeExist();


...



QuestGraphSchema.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphSchema.h
 #pragma once

virtual void BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const override;

...



QuestGraphNode.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode.cpp
 #include "QuestGraphNode.h"
#include "QuestGraph.h"


AssetTypeActions_NarrativeQuestTask.cpp

/Narrative/Source/NarrativeQuestEditor/Private/AssetTypeActions_NarrativeQuestTask.cpp
 #include "AssetTypeActions_NarrativeQuestTask.h"
#include "NarrativeDataTask.h"
#include "NarrativeCondition.h"

...

#include "QuestTask.h"
#include "NarrativeQuestTaskBlueprint.h"
#include <Factories/BlueprintFactory.h>



QuestGraphNode_Action.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode_Action.cpp
 #include "QuestGraphNode_Action.h"
#include "QuestEditorTypes.h"


AssetTypeActions_QuestAction.cpp

/Narrative/Source/NarrativeQuestEditor/Private/AssetTypeActions_QuestAction.cpp
 #include "AssetTypeActions_QuestAction.h"
#include "NarrativeDataTask.h"


K2Node_CompleteNarrativeTask.cpp

/Narrative/Source/NarrativeQuestEditor/Private/K2Node_CompleteNarrativeTask.cpp
 
#include "K2Node_CompleteNarrativeTask.h"
#include "EdGraphSchema_K2_Actions.h"

...

#include "BlueprintActionMenuBuilder.h"
#include "NarrativeFunctionLibrary.h"
#include "NarrativeComponent.h"
#include "NarrativeDataTask.h"
#include "K2Node_CallFunction.h"

...



QuestAssetFactory.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestAssetFactory.cpp
 #include "QuestAssetFactory.h"
#include "Editor/ClassViewer/Public/ClassViewerModule.h"

...

#include "Kismet2/SClassPickerDialog.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "QuestBlueprintGeneratedClass.h"
#include "Quest.h"
#include "BlueprintEditorSettings.h"

...

#include "QuestGraph.h"
#include "QuestGraphSchema.h"
#include "Kismet2/BlueprintEditorUtils.h"

#define LOCTEXT_NAMESPACE "QuestAssetFactory"



QuestGraph.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraph.h
#pragma once

...

#include "GraphEditAction.h"
#include "QuestGraph.generated.h"

class UQuestGraphNode;
virtual bool Modify(bool bAlwaysMarkDirty = true) override;

UQuestNode* DuplicateNode(const UQuestNode* OldNode);

...



QuestGraphNode_Root.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode_Root.cpp
 #include "QuestGraphNode_Root.h"
#include "QuestEditorTypes.h"


QuestConnectionDrawingPolicy.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestConnectionDrawingPolicy.cpp
 #include "QuestConnectionDrawingPolicy.h"
#include "QuestGraphNode_Action.h"


QuestEditorTabs.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorTabs.cpp
 #include "QuestEditorTabs.h"



QuestActionFactory.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestActionFactory.cpp
 #include "QuestActionFactory.h"
#include "NarrativeDataTask.h"


QuestBlueprintCompiler.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestBlueprintCompiler.cpp
 
#include "QuestBlueprintCompiler.h"
void FQuestBlueprintCompilerContext::CreateClassVariablesFromBlueprint()
{
Super::CreateClassVariablesFromBlueprint();


}


...

}
}

FixAbandonedQuestTemplate(QuestBP);


...

#undef LOCTEXT_NAMESPACE// removed-end
#undef LOCTEXT_NAMESPACE


QuestEditorSettings.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorSettings.cpp
 
#include "QuestEditorSettings.h"


QuestEditorCommands.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorCommands.h
 #pragma once

// jumps to the current active node
TSharedPtr<FUICommandInfo> JumpToActiveNode;

// When A Dialogue Asset class is selected for this quest, this will switch to that dialogue.
TSharedPtr<FUICommandInfo> OpenQuestDialogue;


...



SQuestGraphNode.h

/Narrative/Source/NarrativeQuestEditor/Private/SQuestGraphNode.h
 #pragma once

EVisibility GetActiveStateImageVisibility() const;

...



QuestEditorModes.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorModes.h
#pragma once

...

#include "WorkflowOrientedApp/ApplicationMode.h"
#include "BlueprintEditorModes.h"

/** Application mode for main behavior tree editing mode */


QuestEditorCommands.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorCommands.cpp
 #include "QuestEditorCommands.h"
#include "QuestEditorStyle.h"
UI_COMMAND(JumpToActiveNode, "Jump To Active Node", "Jumps to the active node.", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(OpenQuestDialogue, "Edit Quest", "Opens Quest Dialogue", EUserInterfaceActionType::Button, FInputChord());

...

#undef LOCTEXT_NAMESPACE // removed-end
#undef LOCTEXT_NAMESPACE


QuestGraphEditor.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphEditor.cpp
 #include "QuestGraphEditor.h"
#include "NarrativeQuestEditorModule.h"

...

#include "GraphEditorActions.h"
#include "Framework/Commands/GenericCommands.h"
#include "QuestGraphNode.h"
#include "Widgets/Layout/SBorder.h"

...

#include <SKismetInspector.h>
#include "Kismet2/DebuggerCommands.h"
#include "EdGraphSchema_K2_Actions.h"
#include "K2Node_CustomEvent.h"
#include "QuestDebugger.h"
#include "QuestEditorStyle.h"
#include "SNodePanel.h"

...

static const FString NarrativeHelpURL("http://www.google.com");
static const FString NarrativeHelpURL("https://docs.narrativetools.io/tales/");

FQuestGraphEditor::FQuestGraphEditor()
{
FEditorDelegates::BeginPIE.RemoveAll(this);
FEditorDelegates::EndPIE.RemoveAll(this);

// kept separate from debugger because regardless of a debugger, quest assets cannot be edited during PIE
FEditorDelegates::BeginPIE.AddRaw(this, &FQuestGraphEditor::OnBeginPIE);
FEditorDelegates::EndPIE.AddRaw(this, &FQuestGraphEditor::OnEndPIE);

// create a debugger instance before binding commands
Debugger = MakeShareable(new FQuestDebugger);


...




CreateDefaultCommands();
//BindCommands();
RegisterMenus();


...




// setup debugger
Debugger->Setup(QuestBlueprint, SharedThis(this));

if (UToolMenu* EditorToolbar = UToolMenus::Get()->FindMenu("AssetEditor.Quest Editor.Toolbar.QuestEditor"))
{
// TODO: update this to use ui commands so that it isn't global just like the edit dialogue asset
Debugger->RegisterDebuggerToolbar(EditorToolbar);

// add dialogue asset button
FToolMenuSection& Section = EditorToolbar->AddSection("EditDialogueAsset", FText::GetEmpty(), {"Debugging", EToolMenuInsertType::After});
Section.AddEntry(FToolMenuEntry::InitToolBarButton(
FQuestEditorCommands::Get().OpenQuestDialogue,
LOCTEXT("QuestGraphEditor_EditDialogue", "Edit Dialogue"),
LOCTEXT("QuestGraphEditor_EditDialogue_ToolTip", "When A Dialogue Asset class is selected for this quest, this will switch to that dialogue."),
FSlateIcon(FQuestEditorStyle::GetStyleSetName(), "QuestEditor.DialogueIcon"))
);
}

SetGraphEditable(FQuestDebugger::IsPIENotSimulating());

//Load all Quest Tasks into memory since we need them for editing quests
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FAssetData> AssetData;

...

CommentAction.PerformAction(Graph, NULL, GraphEditor->GetPasteLocation());
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
CommentAction.PerformAction(Graph, nullptr, GraphEditor->GetPasteLocation2f());
#else
CommentAction.PerformAction(Graph, nullptr, GraphEditor->GetPasteLocation());
#endif
}

}

...


// Do not allow root or PT nodes to be copied
// Do not allow root or PT nodes to be copied.
const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();
for (FGraphPanelSelectionSet::TConstIterator SelectedIter(SelectedNodes); SelectedIter; ++SelectedIter)
{
FNotificationInfo Info(NSLOCTEXT("QuestGraphEditor", "RootAndPersistantNodesCopyWarning", "Root and PersistentTask nodes can not be copied!"));
Info.ExpireDuration = 3.0f;
Info.bUseLargeFont = false;
Info.Image = FCoreStyle::Get().GetBrush(TEXT("MessageLog.Warning"));
TSharedPtr<SNotificationItem> Notification = FSlateNotificationManager::Get().AddNotification(Info);
if (Notification.IsValid())
{
Notification->SetCompletionState(SNotificationItem::CS_None);
}

...

{
if (UQuestGraph* DGraph = Cast<UQuestGraph>(DestinationGraph))
{
//Pasting quest nodes has caused more trouble than it has been worth - uncomment at your own risk!
//Quest_PasteNodesHere(Location);
{
if (UQuestGraph* QuestGraph = Cast<UQuestGraph>(DestinationGraph))
{
// Undo/Redo support
const FScopedTransaction Transaction(FGenericCommands::Get().Paste->GetDescription());
TSharedPtr<SGraphEditor> CurrentGraphEditor = FocusedGraphEdPtr.Pin();

if (!CurrentGraphEditor.IsValid())
{
return;
}

QuestGraph->Modify();

UQuestGraphNode* SelectedParent = NULL;
bool bHasMultipleNodesSelected = false;

const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

// Clear the selection set (newly pasted stuff will be selected)
CurrentGraphEditor->ClearSelectionSet();

// Grab the text to paste from the clipboard.
FString TextToImport;
FPlatformApplicationMisc::ClipboardPaste(TextToImport);

// Import the nodes
TSet<UEdGraphNode*> PastedNodes;
FEdGraphUtilities::ImportNodesFromText(QuestGraph, TextToImport, /*out*/ PastedNodes);

// walk nodes to be pasted, and accumulate their position
FVector2D AvgNodePosition(0.0f, 0.0f);
for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
{
UEdGraphNode* Node = *It;
AvgNodePosition.X += Node->NodePosX;
AvgNodePosition.Y += Node->NodePosY;
}
// average the accumulated node positions
float InvNumNodes = 1.0f / static_cast<float>(PastedNodes.Num());
AvgNodePosition.X *= InvNumNodes;
AvgNodePosition.Y *= InvNumNodes;

for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
{
UEdGraphNode* PasteNode = *It;

// node is null, skip and remove it
if (!PasteNode)
{
It.RemoveCurrent();
continue;
}

// set new paste node position, accounting for selected nodes average position
PasteNode->NodePosX = static_cast<int32>((PasteNode->NodePosX - AvgNodePosition.X) + GraphLocation.X);
PasteNode->NodePosY = static_cast<int32>((PasteNode->NodePosY - AvgNodePosition.Y) + GraphLocation.Y);

PasteNode->SnapToGrid(SNodePanel::GetSnapGridSize());

// Give new node a different Guid from the old one
PasteNode->CreateNewGuid();

// update if node is a quest node
if (UQuestGraphNode* PasteQuestNode = Cast<UQuestGraphNode>(PasteNode))
{
// New Quest graph node will point to old quest node, duplicate a new one for our new node
UQuestNode* DupNode = QuestGraph->DuplicateNode(PasteQuestNode->QuestNode);

// update the paste node with the newly created node
PasteQuestNode->QuestNode = DupNode;

// remove old on entered node
PasteQuestNode->OnEnteredCustomNode = nullptr;
}

// Select the newly pasted stuff
CurrentGraphEditor->SetNodeSelection(PasteNode, true);
}

//Now that everything has been pasted, iterate a second time to rebuild the new nodes connections
for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
{
UEdGraphNode* PasteNode = *It;

// although the node exists, we need to ensure that it is set up and actually added to the underlying quest
QuestGraph->NodeAdded(PasteNode);

// we only want to rewire nodes that can be, comment nodes cannot be for example
if (UQuestGraphNode* PasteQuestNode = Cast<UQuestGraphNode>(PasteNode))
{
// check that we aren't trying to wire a node output when none should exist.
UQuestGraphNode_State* FromState = Cast<UQuestGraphNode_State>(PasteNode);
if (UQuestState* NodeQuestState = FromState ? FromState->State : nullptr)
{
switch (NodeQuestState->StateNodeType)
{
case EStateNodeType::Failure: // fallthrough
case EStateNodeType::Success: continue;
default: break;
}
}

//Quest nodes connections will still be outdated, update these to the new connections
QuestGraph->PinRewired(PasteQuestNode, PasteQuestNode->GetOutputPin());
}
}

// Update UI
CurrentGraphEditor->NotifyGraphChanged();
if (UObject* GraphOwner = QuestGraph->GetOuter())
{
GraphOwner->PostEditChange();
GraphOwner->MarkPackageDirty();
}
}
else
{
#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
FVector2f Location = FVector2f(GraphLocation); // Handles FVector2f
FBlueprintEditor::PasteNodesHere(DestinationGraph, Location);
#else
FVector2D Location = FVector2D(GraphLocation); // Handles FVector2D
FBlueprintEditor::PasteNodesHere(DestinationGraph, Location);
#endif
}
}

void FQuestGraphEditor::Quest_SelectAllNodes()
FBlueprintEditor::PasteNodesHere(DestinationGraph, GraphLocation);
}
}

/*void FQuestGraphEditor::Quest_SelectAllNodes()

...

}

bool FQuestGraphEditor::Quest_CanSelectAllNodes() const
}*/

/*bool FQuestGraphEditor::Quest_CanSelectAllNodes() const
{
return true;
}

void FQuestGraphEditor::Quest_DeleteSelectedNodes()
}*/

/*void FQuestGraphEditor::Quest_DeleteSelectedNodes()
{

...

}

bool FQuestGraphEditor::Quest_CanDeleteNodes() const
}*/

/*bool FQuestGraphEditor::Quest_CanDeleteNodes() const
{
// If any of the nodes can be deleted then we should allow deleting
const FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

...

}

void FQuestGraphEditor::Quest_DeleteSelectedDuplicatableNodes()
}*/

/*void FQuestGraphEditor::Quest_DeleteSelectedDuplicatableNodes()
{
TSharedPtr<SGraphEditor> CurrentGraphEditor = UpdateGraphEdPtr.Pin();
if (!CurrentGraphEditor.IsValid())

...

}

void FQuestGraphEditor::Quest_CutSelectedNodes()
}*/

/*void FQuestGraphEditor::Quest_CutSelectedNodes()
{
CopySelectedNodes();
DeleteSelectedDuplicatableNodes();
}

bool FQuestGraphEditor::Quest_CanCutNodes() const
}*/

/*bool FQuestGraphEditor::Quest_CanCutNodes() const

...

}

void FQuestGraphEditor::Quest_CopySelectedNodes()
}*/

/*void FQuestGraphEditor::Quest_CopySelectedNodes()
{
// Export the selected nodes and place the text on the clipboard
FGraphPanelSelectionSet SelectedNodes = GetSelectedNodes();

...

}

bool FQuestGraphEditor::Quest_CanCopyNodes() const
}*/

/*bool FQuestGraphEditor::Quest_CanCopyNodes() const
{

//Copying nodes is disabled for now

...

}

void FQuestGraphEditor::Quest_PasteNodes()
}*/

/*void FQuestGraphEditor::Quest_PasteNodes()
{

if (TSharedPtr<SGraphEditor> CurrentGraphEditor = UpdateGraphEdPtr.Pin())

...

}

void FQuestGraphEditor::Quest_PasteNodesHere(const FVector2D& Location)
}*/

/*void FQuestGraphEditor::Quest_PasteNodesHere(const FVector2D& Location)
{
TSharedPtr<SGraphEditor> CurrentGraphEditor = FocusedGraphEdPtr.Pin();
if (!CurrentGraphEditor.IsValid())

...

FEdGraphUtilities::ImportNodesFromText(QuestGraph, TextToImport, /*out*/ PastedNodes);
FEdGraphUtilities::ImportNodesFromText(QuestGraph, TextToImport, out PastedNodes);

for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
{

...

}

bool FQuestGraphEditor::Quest_CanPasteNodes() const
}*/

/*bool FQuestGraphEditor::Quest_CanPasteNodes() const
{
//Pasting nodes is disabled for now
return false;

...

}

void FQuestGraphEditor::Quest_DuplicateNodes()
}*/

/*void FQuestGraphEditor::Quest_DuplicateNodes()
{
CopySelectedNodes();
PasteNodes();
}

bool FQuestGraphEditor::Quest_CanDuplicateNodes() const
}*/

/*bool FQuestGraphEditor::Quest_CanDuplicateNodes() const

...

}

void FQuestGraphEditor::Quest_CreateComment()
}*/

/*void FQuestGraphEditor::Quest_CreateComment()
{
TSharedPtr<SGraphEditor> GraphEditor = UpdateGraphEdPtr.Pin();
if (GraphEditor.IsValid())

...

}

bool FQuestGraphEditor::Quest_CanCreateComment() const
}*/

/*bool FQuestGraphEditor::Quest_CanCreateComment() const
{
return true;
}
}*/

}

void FQuestGraphEditor::SwitchToDialogueAsset()
{
if (!QuestBlueprint)
{
return;
}

UQuest* Quest = Cast<UQuest>(QuestBlueprint->GeneratedClass.GetDefaultObject());

#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 2
if (GEditor)
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (AssetEditorSubsystem && Quest)
{
AssetEditorSubsystem->OpenEditorForAsset(UBlueprint::GetBlueprintFromClass(Quest->GetQuestDialogueClass()));
}
}
#else
// UE 5.1 fallback (AssetEditorSubsystem does not exist)
if (GEditor)
{
GEditor->EditObject(Quest->GetQuestDialogueClass());
}
#endif
}

bool FQuestGraphEditor::CanSwitchToDialogueAsset()
{
if (!QuestBlueprint)
{
return false;
}

UQuest* Quest = Cast<UQuest>(QuestBlueprint->GeneratedClass.GetDefaultObject());
return Quest? Quest->GetQuestDialogueClass() != nullptr : false;

...



ToolkitCommands->MapAction(FQuestEditorCommands::Get().OpenQuestDialogue,
FExecuteAction::CreateSP(this, &FQuestGraphEditor::SwitchToDialogueAsset),
FCanExecuteAction::CreateSP(this, &FQuestGraphEditor::CanSwitchToDialogueAsset));

if (Debugger.IsValid())
{
Debugger->BindCommands(ToolkitCommands);
}

FBlueprintEditor::CreateDefaultCommands();
}

// read only
if (!IsGraphEditable())
{
return;
}

const FScopedTransaction Transaction(NSLOCTEXT("QuestGraphEditor", "QuestGraphEditor_EventFromNode", "Create event for node"));
QuestBlueprint->Modify();

void FQuestGraphEditor::SetGraphEditable(const bool Editable)
{
if (IsValid(QuestBlueprint) && IsValid(QuestBlueprint->QuestGraph))
{
QuestBlueprint->QuestGraph->bEditable = Editable;
}
}

bool FQuestGraphEditor::IsGraphEditable() const
{
const UEdGraph* EdGraph = IsValid(QuestBlueprint) ? QuestBlueprint->QuestGraph : nullptr;
return EdGraph? EdGraph->bEditable : false;
}


...


#undef LOCTEXT_NAMESPACE// removed-end
void FQuestGraphEditor::OnBeginPIE(bool bSimulating)
{
SetGraphEditable(false);
}

void FQuestGraphEditor::OnEndPIE(bool bSimulating)
{
SetGraphEditable(true);
}

#undef LOCTEXT_NAMESPACE


QuestEditorTabFactories.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorTabFactories.cpp
 #include "QuestEditorTabFactories.h"
#include "QuestBlueprint.h"

...

#include "EditorStyleSet.h"
#include "Engine/Blueprint.h"
#include "QuestEditorStyle.h"

#if ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 6
using FGraphLocationType = FVector2f;
#else
using FGraphLocationType = FVector2D;
#endif


...

FVector2D ViewLocation;
FGraphLocationType ViewLocation;
float ZoomAmount;
GraphEditor->GetViewLocation(ViewLocation, ZoomAmount);


...

#undef LOCTEXT_NAMESPACE// removed-end
#undef LOCTEXT_NAMESPACE


QuestGraph.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraph.cpp
 #include "QuestGraph.h"

#include "GraphEditAction.h"
#include "Quest.h"

...

#include "NarrativeFunctionLibrary.h"
#include "NarrativeDataTask.h"
#include "NarrativeComponent.h"
#include "QuestEditorSettings.h"


bool UQuestGraph::Modify(bool bAlwaysMarkDirty)
{
const bool bSavedToTransactionBuffer = Super::Modify(bAlwaysMarkDirty);

if (UQuestBlueprint* QuestAsset = Cast<UQuestBlueprint>(GetOuter()))
{
// we are changing the quest template too
if (QuestAsset->QuestTemplate)
{
QuestAsset->QuestTemplate->Modify(true);
}
}
return bSavedToTransactionBuffer;
}

...

UQuest* Quest = QuestAsset->QuestTemplate;

//If we rewired a pin, we need to update the quest nodes to match the graph nodes
if (Quest)
if (UQuest* Quest = QuestAsset? QuestAsset->QuestTemplate : nullptr)
{
if (Pin->Direction == EEdGraphPinDirection::EGPD_Output)

...


UQuestBlueprint* QuestAsset = CastChecked<UQuestBlueprint>(GetOuter());
UQuest* Quest = QuestAsset->QuestTemplate;


...

}
return;
}

// node quest is the correct state
if (StateNode->State != StateNode->QuestNode)
{
StateNode->State = Cast<UQuestState>(StateNode->QuestNode);
}
Quest->AddState(StateNode->State);
}
else if (UQuestGraphNode_Action* ActionNode = Cast<UQuestGraphNode_Action>(AddedNode))
{
if (!ActionNode->Branch)
// if the branch is null then a new one is needed
const bool bBranchIsNull = ActionNode->Branch == nullptr;
if (bBranchIsNull)

...

}
else
{
//Node already has a branch set, was probably created from context menu
if (ActionNode->Branch)
{
Quest->AddBranch(ActionNode->Branch);

//Use rename to fix a bug where old outer wasnt being set properly
ActionNode->Branch->Rename(nullptr, Quest);
}
}
}
return;
}

// node quest is the correct branch
if (ActionNode->Branch != ActionNode->QuestNode)
{
ActionNode->Branch = Cast<UQuestBranch>(ActionNode->QuestNode);
}

//Use rename to fix a bug where old outer wasn't being set properly
ActionNode->Branch->Rename(nullptr, Quest);
Quest->AddBranch(ActionNode->Branch);
}
}

UQuestNode* UQuestGraph::DuplicateNode(const UQuestNode* OldNode)
{
if (OldNode)
{
if (UQuestBlueprint* QuestAsset = Cast<UQuestBlueprint>(GetOuter()); QuestAsset && QuestAsset->QuestTemplate)
{
UQuestNode* DupNode = DuplicateObject<UQuestNode>(OldNode, OldNode->GetOuter());
DupNode->EnsureUniqueID();

// clear event name
DupNode->OnEnteredFuncName = NAME_None;

return DupNode;
}
}

return nullptr;
}

void UQuestGraph::PIEStarted(bool bIsSimulating)


QuestGraphNode_Failure.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode_Failure.cpp
 #include "QuestGraphNode_Failure.h"
#include "QuestEditorSettings.h"


K2Node_CompleteNarrativeTask.h

/Narrative/Source/NarrativeQuestEditor/Private/K2Node_CompleteNarrativeTask.h
#pragma once

...

#include "K2Node_CallFunction.h"
#include "K2Node_CompleteNarrativeTask.generated.h"

/**


QuestGraphNode_Success.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode_Success.cpp
 #include "QuestGraphNode_Success.h"
#include "QuestEditorSettings.h"


AssetTypeActions_QuestAsset.cpp

/Narrative/Source/NarrativeQuestEditor/Private/AssetTypeActions_QuestAsset.cpp
 #include "AssetTypeActions_QuestAsset.h"
#include "QuestBlueprint.h"

...

#include "QuestAssetFactory.h"
#include <Engine/BlueprintCore.h>
#include <Animation/AnimBlueprint.h>

#define LOCTEXT_NAMESPACE "AssetTypeActions"

...



QuestGraphEditor.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphEditor.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "Misc/NotifyHook.h"
#include "EditorUndoClient.h"
#include "Widgets/SWidget.h"


...


//Not all of these are used yet
void Quest_SelectAllNodes();
bool Quest_CanSelectAllNodes() const;
void Quest_DeleteSelectedNodes();
bool Quest_CanDeleteNodes() const;
void Quest_DeleteSelectedDuplicatableNodes();
void Quest_CutSelectedNodes();
bool Quest_CanCutNodes() const;
void Quest_CopySelectedNodes();
bool Quest_CanCopyNodes() const;
void Quest_PasteNodes();
void Quest_PasteNodesHere(const FVector2D& Location);
bool Quest_CanPasteNodes() const;
void Quest_DuplicateNodes();
bool Quest_CanDuplicateNodes() const;
void Quest_CreateComment();
bool Quest_CanCreateComment() const;

//none of these are used anymore
//void Quest_SelectAllNodes();
//bool Quest_CanSelectAllNodes() const;
//void Quest_DeleteSelectedNodes();
//bool Quest_CanDeleteNodes() const;
//void Quest_DeleteSelectedDuplicatableNodes();
//void Quest_CutSelectedNodes();
//bool Quest_CanCutNodes() const;
//void Quest_CopySelectedNodes();
//bool Quest_CanCopyNodes() const;
//void Quest_PasteNodes();
//void Quest_PasteNodesHere(const FVector2D& Location);
//bool Quest_CanPasteNodes() const;
//void Quest_DuplicateNodes();
//bool Quest_CanDuplicateNodes() const;
//void Quest_CreateComment();
//bool Quest_CanCreateComment() const;

void OnWorldChange(UWorld* World, EMapChangeType MapChangeType);


void SwitchToDialogueAsset();
bool CanSwitchToDialogueAsset();
void SetGraphEditable(const bool Editable);
bool IsGraphEditable() const;

void OnBeginPIE(bool bSimulating);
void OnEndPIE(bool bSimulating);

/// debugger instance for this graph
TSharedPtr<class FQuestDebugger> Debugger;


...



SQuestGraphNode.cpp

/Narrative/Source/NarrativeQuestEditor/Private/SQuestGraphNode.cpp
 #include "SQuestGraphNode.h"
#include "QuestGraphNode.h"

...

#include "QuestGraph.h"
#include "SGraphPanel.h"
#include "ScopedTransaction.h"
#include "QuestSM.h"
#include "QuestEditorStyle.h"

...

#include "QuestGraphNode_State.h"
#include "NarrativeEvent.h"
#include "QuestEditorSettings.h"
#include <Blueprint/UserWidget.h>

...

#include <Slate/SObjectWidget.h>
#include "QuestNodeUserWidget.h"
#include "QuestBlueprint.h"


...

+ SOverlay::Slot()
[
QuestNodeWidgetRef.ToSharedRef()
]

// quest node widget
+SOverlay::Slot()
[
QuestNodeWidgetRef.ToSharedRef()
]

// debugger overlay
+SOverlay::Slot()
[
SNew(SImage)
.Image(FAppStyle::GetBrush(TEXT("Graph.Node.DiffHighlight")))
// yellow
.ColorAndOpacity(FLinearColor(0.0f, 1.0f, 0.0f, 0.75f))
.Visibility_Raw(this, &SQuestGraphNode::GetActiveStateImageVisibility)
]
];
}
else // Old style yucky slate ones

...


if (UQuestGraphNode* QuestGraphNode = Cast<UQuestGraphNode>(GraphNode))
{
return QuestGraphNode->GetGraphNodeColor();
}

EVisibility SQuestGraphNode::GetActiveStateImageVisibility() const
{
const UQuestGraphNode* QuestGraphNode = Cast<UQuestGraphNode>(GraphNode);
return QuestGraphNode && QuestGraphNode->bActiveNode? EVisibility::HitTestInvisible : EVisibility::Collapsed;

...



QuestGraphNode_PersistentTasks.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode_PersistentTasks.cpp
 #include "QuestGraphNode_PersistentTasks.h"
#include "QuestEditorTypes.h"

...

#include "QuestSM.h"

void UQuestGraphNode_PersistentTasks::AllocateDefaultPins()
{


NarrativeQuestEditorModule.cpp

/Narrative/Source/NarrativeQuestEditor/Private/NarrativeQuestEditorModule.cpp
 #include "NarrativeQuestEditorModule.h"
#include "IQuestEditor.h"

...

#include "QuestTask.h"
#include "EdGraphUtilities.h"
#include "SQuestGraphNode.h"
#include "QuestGraphNode.h"

...

#include "ISettingsSection.h"
#include "ISettingsContainer.h"
#include <ISettingsCategory.h>
#include "KismetCompiler.h"
#include "QuestBlueprintCompiler.h"
#include <AssetRegistry/AssetRegistryModule.h>

#include "NodeSelector/GraphPin_QuestNodeSelector.h"
#include "NodeSelector/QuestNodeSelectorPropertyCustomization.h"
#include "UObject/CoreRedirects.h"
FEdGraphUtilities::RegisterVisualPinFactory(MakeShared<FPinFactoryQuestNodeSelector>());


// Register custom property layout for FQuestStateSelector
PropertyModule.RegisterCustomPropertyTypeLayout(
"QuestStateSelector",
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FQuestStateSelectorPropertyTypeCustomization::MakeInstance)
);

// Register custom property layout for FQuestBranchSelector
PropertyModule.RegisterCustomPropertyTypeLayout(
"QuestBranchSelector",
FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FQuestBranchSelectorPropertyTypeCustomization::MakeInstance)
);


PropertyModule.UnregisterCustomPropertyTypeLayout("DialogueNodeSelector");


FEdGraphUtilities::UnregisterVisualPinFactory(MakeShared<FPinFactoryQuestNodeSelector>());

...

IMPLEMENT_MODULE(FNarrativeQuestEditorModule, NarrativeQuestEditor)// removed-end
IMPLEMENT_MODULE(FNarrativeQuestEditorModule, NarrativeQuestEditor)


QuestEditorDetails.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorDetails.cpp
 #include "QuestEditorDetails.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"

...

#include "Quest.h"
#include "QuestBlueprint.h"
#include "DetailCategoryBuilder.h"
#include "NarrativeDataTask.h"
#include "PropertyHandle.h"

#define LOCTEXT_NAMESPACE "QuestEditorDetails"



QuestEditorModes.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorModes.cpp
 #include "QuestEditorModes.h"
#include "QuestEditorTabs.h"
#include "QuestEditorToolbar.h"
#include "QuestEditorDetails.h"

...

#include "BlueprintEditorTabs.h"

#define LOCTEXT_NAMESPACE "QuestGraphApplicationMode"



QuestEditorSettings.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorSettings.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "QuestEditorSettings.generated.h"

/**


QuestGraphNode_State.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode_State.cpp
 #include "QuestGraphNode_State.h"
#include "QuestEditorTypes.h"


QuestEditorStyle.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorStyle.cpp
 #include "QuestEditorStyle.h"
#include "Styling/SlateStyle.h"

...

StyleSet->Set(FName(TEXT("ClassThumbnail.NarrativePartyComponent")), new IMAGE_BRUSH("NarrativePartyLogo", FVector2D(64, 64)));
StyleSet->Set(FName(TEXT("ClassIcon.NarrativePartyComponent")), new IMAGE_BRUSH("NarrativePartyLogo", FVector2D(16, 16)));
StyleSet->Set(FName(TEXT("ClassThumbnail.NarrativeTalesPartyComponent")), new IMAGE_BRUSH("NarrativePartyLogo", FVector2D(64, 64)));
StyleSet->Set(FName(TEXT("ClassIcon.NarrativeTalesPartyComponent")), new IMAGE_BRUSH("NarrativePartyLogo", FVector2D(16, 16)));

//Toolbar buttons
StyleSet->Set(FName(TEXT("QuestEditor.Common.ShowQuestDetails")), new IMAGE_BRUSH("QuestEditor/Icons/ShowQuestDetails_40x", FVector2D(40.f, 40.f)));
StyleSet->Set(FName(TEXT("QuestEditor.DialogueIcon")), new IMAGE_BRUSH("Dialogue", FVector2D(64.f, 64.f)));

...



QuestGraphNode.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphNode.h
 #pragma once

class FQuestGraphEditor;

UQuestGraphNode()
: ParentNode(nullptr), QuestNode(nullptr), OnEnteredCustomNode(nullptr), bActiveNode(false)
{}

...

virtual FLinearColor GetGraphNodeColor() const { return FLinearColor(0.15f, 0.15f, 0.15f); };

virtual FLinearColor GetGraphNodeColor() const { return FLinearColor(0.15f, 0.15f, 0.15f); }
virtual FLinearColor GetGraphBorderNodeColor() const { return bActiveNode? FLinearColor::Yellow : FLinearColor::Transparent; }

uint8 bActiveNode : 1;

};


QuestEditorTypes.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorTypes.cpp
 #include "QuestEditorTypes.h"



QuestBlueprintCompiler.h

/Narrative/Source/NarrativeQuestEditor/Private/QuestBlueprintCompiler.h
 #pragma once

#include "QuestSM.h"
virtual void CreateClassVariablesFromBlueprint() override;
// Map of properties created for states; to aid in debug data generation
TMap<UQuestState*, FProperty*> StateToMemberVariableMap;


...



QuestEditorToolbar.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestEditorToolbar.cpp
 #include "QuestEditorToolbar.h"
#include "Misc/Attribute.h"

...

#include "EditorStyleSet.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "QuestGraphEditor.h"
#include "WorkflowOrientedApp/SModeWidget.h"


QuestNodeUserWidget.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestNodeUserWidget.cpp

// Copyright Narrative Tools 2022.


#include "QuestNodeUserWidget.h"
#include "DialogueBlueprint.h"
#include "IDialogueEditor.h"
#include "Dialogue.h"
#include "Quest.h"

void UQuestNodeUserWidget::AddOrJumpToMatchingDialogueNode()
{
UAssetEditorSubsystem* AssetEditorSubsystem = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
UBlueprint* DialogueBP = UBlueprint::GetBlueprintFromClass(Quest->GetQuestDialogueClass());
if (AssetEditorSubsystem && DialogueBP)
{
// find or open graph editor for bp
IDialogueEditor* DialogueEditor = static_cast<IDialogueEditor*>(AssetEditorSubsystem->FindEditorForAsset(DialogueBP, true));
if (!DialogueEditor)
{
AssetEditorSubsystem->OpenEditorForAsset(DialogueBP);
DialogueEditor = static_cast<IDialogueEditor*>(AssetEditorSubsystem->FindEditorForAsset(DialogueBP, true));
}

if (DialogueEditor)
{
DialogueEditor->JumpOrAddDialogueNode(Node->GetID());
}
}
}

bool UQuestNodeUserWidget::DoesMatchingDialogueNodeExist()
{
const FName NodeID = Node->GetID();
if (UDialogueBlueprint* DialogueBP = Quest? Cast<UDialogueBlueprint>(UBlueprint::GetBlueprintFromClass(Quest->GetQuestDialogueClass())) : nullptr)
{
return DialogueBP->DialogueTemplate->GetNodes().FindByPredicate([&NodeID](UDialogueNode* DialogueNode)
{
return DialogueNode->GetID() == NodeID;
}) != nullptr;
}

return false;
}

...



QuestGraphSchema.cpp

/Narrative/Source/NarrativeQuestEditor/Private/QuestGraphSchema.cpp
 #include "QuestGraphSchema.h"
#include "QuestGraphNode_Root.h"

...

#include "IQuestEditor.h"
#include "QuestBlueprint.h"
#include "Toolkits/ToolkitManager.h"
#include "QuestConnectionDrawingPolicy.h"
#include "QuestGraph.h"
#include "GraphEditorActions.h"
#include "Framework/Commands/GenericCommands.h"
#include "QuestEditorCommands.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "NarrativeDataTask.h"

...

FCategorizedGraphActionListBuilder ActionCategoryBuilder("Tasks: " + TaskCategory);

TSharedPtr<FQuestSchemaAction_NewNode> AddAction = UQuestGraphSchema::AddNewNodeAction(ActionCategoryBuilder, FText::GetEmpty(), FText::FromString(TaskDescription), FText::FromString(TaskDescription));

AddAction->UpdateSearchData(FText::FromString(TaskName), FText::FromString(TaskDescription), FText::GetEmpty(), FText::GetEmpty());
FString Category = "Tasks: " + TaskCategory;
FCategorizedGraphActionListBuilder ActionCategoryBuilder(Category);

TSharedPtr<FQuestSchemaAction_NewNode> AddAction = UQuestGraphSchema::AddNewNodeAction(ActionCategoryBuilder, FText::GetEmpty(), FText::FromString(TaskName), FText::FromString(TaskDescription));

//AddAction->UpdateSearchData(FText::FromString(TaskName), FText::FromString(TaskDescription), FText::GetEmpty(), FText::GetEmpty());
//AddAction->TaskClass = QuestTaskClass;

UQuestGraphNode_Action* Node = NewObject<UQuestGraphNode_Action>(ContextMenuBuilder.OwnerOfTemporaries, UQuestGraphNode_Action::StaticClass());
void UQuestGraphSchema::BreakPinLinks(UEdGraphPin& TargetPin, bool bSendsNodeNotifcation) const
{
const FScopedTransaction Transaction(NSLOCTEXT("QuestGraphSchema", "QuestGraphSchema_BreakPinLinks", "Break Pin Links"));

Super::BreakPinLinks(TargetPin, bSendsNodeNotifcation);
}


...

#undef LOCTEXT_NAMESPACE// removed-end
#undef LOCTEXT_NAMESPACE


Narrative.Build.cs

/Narrative/Source/Narrative/Narrative.Build.cs
 using UnrealBuildTool;

"GameplayTags"

...



Narrative.h

/Narrative/Source/Narrative/Public/Narrative.h
#pragma once

...


DECLARE_LOG_CATEGORY_EXTERN(LogNarrativeRuntime, All, All);

//Runtime module for narrative
class FNarrativeModule : public IModuleInterface


NarrativeQuestSettings.h

/Narrative/Source/Narrative/Public/NarrativeQuestSettings.h

// Fill out your copyright notice in the Description page of Project Settings.
// Copyright Narrative Tools 2025.

#pragma once


...

#include "UObject/NoExportTypes.h"
#include "NarrativeQuestSettings.generated.h"

/**


Quest.h

/Narrative/Source/Narrative/Public/Quest.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "QuestSM.h"
#include "QuestTask.h"
#include "Quest.generated.h"
class UDialogue;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnQuestPostLoad, const UQuest*, Quest);
class UQuest;

//Allows blueprints to define instanced quest requirements.
USTRUCT(BlueprintType)
struct NARRATIVE_API FInstancedQuestRequirement
{
GENERATED_BODY()


FInstancedQuestRequirement(){};

//The instanced goal
UPROPERTY(Instanced, EditAnywhere, BlueprintReadOnly, Category = "Requirement")
TObjectPtr<UQuestRequirement> Requirement;

};

/** Quest Requirements can be added/removed by quests at any time to define requirements the player needs to adhere to.
For example, you could add a 'Stay Near Actor' requirement that specifies we need to stay near a certain NPC or the quest fails.
Another example might be a 'Keep NPC Alive' requirement that fails the quest if a certain NPC dies. */
UCLASS(abstract, Blueprintable, BlueprintType, EditInlineNew)
class NARRATIVE_API UQuestRequirement : public UObject
{

GENERATED_BODY()

protected:

friend class UQuest;

UQuestRequirement();


// Allows the Object to get a valid UWorld from it's outer.
virtual UWorld* GetWorld() const override
{
if (HasAllFlags(RF_ClassDefaultObject))
{
// If we are a CDO, we must return nullptr instead of calling Outer->GetWorld() to fool UObject::ImplementsGetWorld.
return nullptr;
}

UObject* Outer = GetOuter();

while (Outer)
{
UWorld* World = Outer->GetWorld();
if (World)
{
return World;
}

Outer = Outer->GetOuter();
}

return nullptr;
};

//Called when this requirement is added to the quest.
UFUNCTION(BlueprintNativeEvent, Category = "Requirement")
void OnAdded(UQuest* Quest);
virtual void OnAdded_Implementation(UQuest* Quest);

//Called when this requirement is removed from the quest.
UFUNCTION(BlueprintNativeEvent, Category = "Requirement")
void OnRemoved(UQuest* Quest);
virtual void OnRemoved_Implementation(UQuest* Quest);

public:

//Get the quest that owns this requirement.
UFUNCTION(BlueprintPure, Category = "Requirement")
virtual UQuest* GetOwningQuest() const;

};


...


protected:

#if WITH_EDITOR
virtual void PostLoad() override;
virtual void PreEditChange(FProperty* PropertyAboutToChange) override;
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
void UpdateDialogueConnection();

#endif

//Called when the quest is loaded back in from disk
UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName="On Quest Post Load"))
void BPQuestPostLoad();

//Called when the quest is loaded back in from disk - allows you to set any quest state back up
virtual void QuestPostLoad();

//Called before tasks are ready - a good place to set up data tasks depend on

...

void BPPreQuestStarted(const UQuest* Quest);
void BPPreQuestStarted(const UQuest* Quest);

UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName="On Quest Started"))
void BPOnQuestStarted(const UQuest* Quest);

UFUNCTION()
void FailQuest(FText QuestFailedMessage);
void BPOnQuestStarted(const UQuest* Quest);

UFUNCTION(BlueprintCallable, Category = "Quests")
virtual void FailQuest(FText QuestFailedMessage);


...

void BPOnQuestFailed(const UQuest* Quest, const FText& QuestFailedMessage);
void BPOnQuestFailed(const UQuest* Quest, const FText& QuestFailedMessage);

/**Manually set the quest as succeeded. You'll need to provide some text for the UI as theres no node the quest, you're manually succeeding it.*/
UFUNCTION()
void SucceedQuest(FText QuestSucceededMessage);
UFUNCTION(BlueprintCallable, Category = "Quests")
virtual void SucceedQuest(FText QuestSucceededMessage);


...

void OnQuestTaskProgressChanged(const UNarrativeTask* Task, const class UQuestBranch* Step, int32 CurrentProgress, int32 RequiredProgress);
virtual void OnQuestTaskProgressChanged(const UNarrativeTask* Task, const class UQuestBranch* Step, int32 CurrentProgress, int32 RequiredProgress);

UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "On Quest Objective Progress Made"))
void BPOnQuestTaskProgressChanged(const UQuest* Quest, const UNarrativeTask* Task, const class UQuestBranch* Step, int32 CurrentProgress, int32 RequiredProgress);
void BPOnQuestTaskProgressChanged(const UQuest* Quest, const UNarrativeTask* Task, const class UQuestBranch* Step, int32 CurrentProgress, int32 RequiredProgress);


...

void OnQuestBranchCompleted(const class UQuestBranch* Branch);
virtual void OnQuestBranchCompleted(const class UQuestBranch* Branch);

UFUNCTION(BlueprintImplementableEvent, meta = (DisplayName = "On Branch Taken"))
void BPOnQuestBranchCompleted(const UQuest* Quest, const class UQuestBranch* Branch);

...



virtual void EnterState_Internal(UQuestState* NewState);
virtual void TakeBranch(UQuestBranch* Branch);


...




virtual void BeginQuest(const FName& OptionalStartFromID = NAME_None);

void HandleBeginDialogue();

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Quest Details")

// A dialogue asset holding all dialogue from the quest.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Dialogue")
TSubclassOf<UDialogue> QuestDialogue;

private:

#if WITH_EDITORONLY_DATA

// use to check if the QuestDialogue was cleared and to know what it was to remove refs
UPROPERTY()
TSubclassOf<UDialogue> LastQuestDialogue;

#endif

protected:

// if true, the quest dialogue will resume when you load back into the game
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Dialogue")
bool bResumeDialogueAfterLoad;
//Holds all the quest requirements
UPROPERTY()
TArray<TObjectPtr<UQuestRequirement>> QuestRequirements;


...

FOnQuestStarted QuestStarted;
FOnQuestStarted QuestStarted;

/**Called when the quest has been loaded back in.*/
UPROPERTY(BlueprintAssignable, Category = "Quests")
FOnQuestPostLoad OnQuestPostLoad;

/**Called when a quest is forgotten.*/
UPROPERTY(BlueprintAssignable, Category = "Quests")
FOnQuestForgotten QuestForgotten;
FOnQuestForgotten QuestForgotten;

...

UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Dialogue")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Quest")
AActor* SpawnQuestActor(TSubclassOf<class AActor> ActorClass, const FTransform& ActorTransform);
virtual AActor* SpawnQuestActor_Implementation(TSubclassOf<class AActor> ActorClass, const FTransform& ActorTransform);

/* Add a requirement to the quest. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Quest")
void AddQuestRequirement(UQuestRequirement* Requirement);
virtual void AddQuestRequirement_Implementation(UQuestRequirement* Requirement);

/* Remove a requirement from the quest. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Quest")
void RemoveQuestRequirement(UQuestRequirement* Requirement);
virtual void RemoveQuestRequirement_Implementation(UQuestRequirement* Requirement);


...

TArray<class APlayerController*> GetGroupMembers() const;

TArray<class APlayerController*> GetGroupMembers() const;

TSubclassOf<UDialogue> GetQuestDialogueClass() { return QuestDialogue; }
};

struct NARRATIVE_API FQuestDelegates final
{

DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnQuestStartedSignature, UNarrativeComponent*, UQuest*, const FName& OptionalStartFromID);
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnQuestEndSignature, UNarrativeComponent*, UQuest*);

// called when a quest starts
static FOnQuestStartedSignature OnQuestStarted;

// called when a quest ends
static FOnQuestEndSignature OnQuestEnd;
};


NarrativeEvent.h

/Narrative/Source/Narrative/Public/NarrativeEvent.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "NarrativeEvent.generated.h"


/**
This event will only fire if the following conditions are met
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category = "Events & Conditions")
TArray<class UNarrativeCondition*> Conditions;


...

void ExecuteEvent(APawn* Pawn, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);
virtual void ExecuteEvent_Implementation(APawn* Pawn, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);
void OnActivate(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);
virtual void OnActivate_Implementation(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);

UFUNCTION(BlueprintNativeEvent, Category = "Event")
void OnDeactivate(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);
virtual void OnDeactivate_Implementation(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);

/**Execute the event.
@param Target this is the target that the event was specified to run on.
*/
UFUNCTION(BlueprintNativeEvent, Category = "Event")
void ExecuteEvent(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);
virtual void ExecuteEvent_Implementation(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);

/**Define the text that will show up on a node if this event is added to it */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Event")

//Check if all the conditions are met on this quest/dialogue node
UFUNCTION(BlueprintCallable, Category = "Events & Conditions")
bool AreConditionsMet(APawn* Pawn, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent);

...



NarrativeDialogueSequence.h

/Narrative/Source/Narrative/Public/NarrativeDialogueSequence.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "LevelSequencePlayer.h"
#include "DialogueSM.h"
#include <MovieSceneSequencePlayer.h>
#include <CineCameraSettings.h>

...



NarrativeComponent.h

/Narrative/Source/Narrative/Public/NarrativeComponent.h
#pragma once

...

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnDialogueFinished, class UDialogue*, Dialogue, const bool, bStartingNewDialogue);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnDialogueFinished, class UDialogue*, Dialogue, const bool, bStartingNewDialogue, const EExitDialogueReason, Reason);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDialogueOptionSelected, class UDialogue*, Dialogue, class UDialogueNode_Player*, PlayerReply);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDialogueRepliesAvailable, class UDialogue*, Dialogue, const TArray<UDialogueNode_Player*>&, PlayerReplies);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FNPCDialogueLineStarted, class UDialogue*, Dialogue, class UDialogueNode_NPC*, Node, const FDialogueLine&, DialogueLine, const FSpeakerInfo&, Speaker);

...

UCLASS( ClassGroup=(Narrative), DisplayName = "Narrative Component", meta=(BlueprintSpawnableComponent) )
UCLASS( ClassGroup=(Narrative), DisplayName = "Narrative Tales Component", meta=(BlueprintSpawnableComponent) )
class NARRATIVE_API UNarrativeComponent : public UActorComponent
{
GENERATED_BODY()

/*A map of every narrative task the player has ever completed, where the key is the amount of times the action has been completed
a TMap means we can very efficiently track large numbers of actions, such as shooting where the player may shoot a gun thousands of times */
UPROPERTY(EditAnywhere, SaveGame, Category = "Quests")
TMap<FString, int32> MasterTaskList;

...

/*A map of every narrative task the player has ever completed, where the key is the amount of times the action has been completed
a TMap means we can very efficiently track large numbers of actions, such as shooting where the player may shoot a gun thousands of times

*/
UPROPERTY(EditAnywhere, Category = "Quests")
TMap<FString, int32> MasterTaskList;

//We set this flag to true during loading so we don't broadcast any quest update delegates as we load quests back in
bool bIsLoading;

virtual void DialogueRepliesAvailable(class UDialogue* Dialogue, const TArray<UDialogueNode_Player*>& PlayerReplies);

UFUNCTION()
virtual void DialogueLineStarted(class UDialogue* Dialogue, UDialogueNode* Node, const FDialogueLine& DialogueLine);

UFUNCTION()
virtual void DialogueLineFinished(class UDialogue* Dialogue, UDialogueNode* Node, const FDialogueLine& DialogueLine);

UFUNCTION()
virtual void DialogueBegan(class UDialogue* Dialogue);

UFUNCTION()
virtual void DialogueFinished(class UDialogue* Dialogue, const bool bStartingNewDialogue, const EExitDialogueReason Reason);

UFUNCTION()

...

UFUNCTION()
virtual void DialogueRepliesAvailable(class UDialogue* Dialogue, const TArray<UDialogueNode_Player*>& PlayerReplies);

UFUNCTION()
virtual void DialogueLineStarted(class UDialogue* Dialogue, UDialogueNode* Node, const FDialogueLine& DialogueLine);

UFUNCTION()
virtual void DialogueLineFinished(class UDialogue* Dialogue, UDialogueNode* Node, const FDialogueLine& DialogueLine);

UFUNCTION()
virtual void DialogueBegan(class UDialogue* Dialogue);

UFUNCTION()
virtual void DialogueFinished(class UDialogue* Dialogue, const bool bStartingNewDialogue);


public:

...

virtual void ClientExitDialogue();
virtual void ClientExitDialogue(const EExitDialogueReason Reason);

/**Used by the server to tell client to end dialogue*/
UFUNCTION(Client, Reliable, Category = "Dialogues")
virtual void ClientExitPartyDialogue();
virtual void ClientExitPartyDialogue(const EExitDialogueReason Reason);

...

virtual void TryExitDialogue();
virtual bool TryExitDialogue(const EExitDialogueReason Reason);

/**Exit the dialogue, will never fail*/
virtual void ExitDialogue();
virtual void ExitDialogue(const EExitDialogueReason Reason);


...

virtual void ServerTryExitDialogue();
virtual void ServerTryExitDialogue(const EExitDialogueReason Reason);

/**Return true if we're in a dialogue


...




};// removed-end
};


NarrativeCondition.h

/Narrative/Source/Narrative/Public/NarrativeCondition.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "NarrativeCondition.generated.h"

//How do we handle running this condition on a party dialogue?


QuestTask.h

/Narrative/Source/Narrative/Public/QuestTask.h

#pragma once

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"

...

* Tasks are blueprints that manage a task for the player to complete. This is mostly used by the quest editor.
* Tasks are blueprints that manage a task for the player to complete. This is mostly used by the quest system
* This is different from Data Tasks which are just data assets: a TaskName and Argument, ie "FindItem:Sword",
*
* A good example is the GoToLocation task that comes with narrative, which checks if the player has reached a goal location. This type


NarrativeDefaultCinecam.h

/Narrative/Source/Narrative/Public/NarrativeDefaultCinecam.h

// Fill out your copyright notice in the Description page of Project Settings.
// Copyright Narrative Tools 2025.

#pragma once



NarrativePartyComponent.h

/Narrative/Source/Narrative/Public/NarrativePartyComponent.h
#pragma once

...

virtual void ExitDialogue() override;
virtual void ExitDialogue(const EExitDialogueReason Reason);

/**Parties dont have owning controllers or pawns so we iterate our party members and find them that way*/
virtual APawn* GetOwningPawn() const override;


Dialogue.h

/Narrative/Source/Narrative/Public/Dialogue.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "LevelSequencePlayer.h"
#include "DialogueSM.h"
#include "MovieSceneSequencePlayer.h"
#include <Misc/Optional.h>
#include <GameplayTagContainer.h>
#include "Dialogue.generated.h"

class UQuest;
//The reason why a dialogue ended.
UENUM(BlueprintType)
enum class EExitDialogueReason : uint8
{
EDR_NoLines,
EDR_PlayerExited,
EDR_TooFarAway,
EDR_NewDialogueStarted,
EDR_StoppedByCinematic
};

OwnedTags.AddTag(FGameplayTag::RequestGameplayTag("Narrative.State.DialogueControlled", false));

//Tags to apply to this speaker for the duration of the dialogue
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Speaker Details")
FGameplayTagContainer OwnedTags;
bOverride_bFreeMovement = false;
bFreeMovement = true;
bOverride_bStopMovement = false;
bStopMovement = false;
bOverride_bUnskippable = false;
bUnskippable = false;

...

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play Params")
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Play Params")
FName StartFromID;

//The priority we want to play this dialogue at. -1 means use the dialogues default priority.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Play Params")
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Play Params")

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Overrides, meta = (PinHiddenByDefault, InlineEditConditionToggle))
uint32 bOverride_bFreeMovement : 1;

//Do we want the dialogue to be free movement?
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Play Params", meta = (editcondition = "bOverride_bFreeMovement"))
bool bFreeMovement;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Overrides, meta = (PinHiddenByDefault, InlineEditConditionToggle))
uint32 bOverride_bStopMovement : 1;

//Do we want the dialogue to stop the characters in it from moving when it begins?
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Play Params", meta = (editcondition = "bOverride_bStopMovement"))
bool bStopMovement;

UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Overrides, meta = (PinHiddenByDefault, InlineEditConditionToggle))
uint32 bOverride_bUnskippable : 1;

//Whether we want to allow the player to skip lines in this dialogue.
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Play Params", meta = (editcondition = "bOverride_bUnskippable"))
bool bUnskippable;

...

bool IsNameStableForNetworking() const override { return true; };
bool IsNameStableForNetworking() const override { return true; }
bool IsSupportedForNetworking() const override { return true; };

#if WITH_EDITOR
virtual void PostLoad() override;

UDialogueNode* GetCurrentNode() { return CurrentNode; }
// if > 0, Dialogue will auto-end if player goes greater than this distance from any speaker in the dialgue.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration")
float EndDialogueDist;

//If true, the player won't be able to skip lines in this dialogue.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Configuration")
bool bUnskippable;


...

UPROPERTY()
UPROPERTY(VisibleAnywhere, Category = "Debug")
class UDialogueNode_NPC* RootDialogue;

//Holds all of the npc replies in the dialogue
UPROPERTY()
UPROPERTY(VisibleAnywhere, Category = "Debug")

...

UPROPERTY()
UPROPERTY(VisibleAnywhere, Category = "Debug")
TArray<class UDialogueNode_Player*> PlayerReplies;

//Ends the current dialogue line

/*
* Return whether or not we can skip lines in this dialogue or not
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Dialogue")
bool CanSkipDialogue() const;
virtual bool CanSkipDialogue_Implementation() const;

...

AActor* LinkSpeakerAvatar(const FSpeakerInfo& Info);
virtual AActor* LinkSpeakerAvatar_Implementation(const FSpeakerInfo& Info);
AActor* LinkSpeakerAvatar(const FSpeakerInfo& Info, const int32 Idx);
virtual AActor* LinkSpeakerAvatar_Implementation(const FSpeakerInfo& Info, const int32 Idx);

/*
* Clean up a given actor from the world

...

virtual void ExitDialogue();
virtual void ExitDialogue(const EExitDialogueReason Reason);

UFUNCTION()
virtual void BlendingOutFinished();

#if WITH_EDITORONLY_DATA

// editor only ref to quest that directly uses this dialogue.
UPROPERTY()
TSubclassOf<UQuest> EditorLinkedQuest;

#endif

...

};// removed-end
};

struct NARRATIVE_API FDialogueDelegates final
{
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnDialogueStartedSignature, UNarrativeComponent*, UDialogue*, const FName& OptionalStartFromID);
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnDialogueEndSignature, UNarrativeComponent*, UDialogue*);

// called when a dialogue starts
static FOnDialogueStartedSignature OnDialogueStarted;

// called when a dialogue ends
static FOnDialogueEndSignature OnDialogueEnd;
};


NarrativeNodeBase.h

/Narrative/Source/Narrative/Public/NarrativeNodeBase.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "NarrativeEvent.h"
#include "NarrativeNodeBase.generated.h"



NarrativeDialogueSettings.h

/Narrative/Source/Narrative/Public/NarrativeDialogueSettings.h
#pragma once

...

#include "UObject/NoExportTypes.h"
#include "NarrativeDialogueSettings.generated.h"

/**


NarrativeFunctionLibrary.h

/Narrative/Source/Narrative/Public/NarrativeFunctionLibrary.h
 #pragma once

#include "NarrativeNodeSelector.h"
/* dialogue selector */
// makes a dialogue node selector, guaranteeing the use of the node selection list
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(Keywords="Dialogue Make Selector"))
static FDialogueNodeSelector MakeDialogueNodeSelector(FDialogueNodeSelector Selector);

// makes a dialogue node selector from a specific node ID
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(CompactNodeTitle="From ID", Keywords="Dialogue Make From ID Selector"))
static FDialogueNodeSelector MakeDialogueNodeSelectorFromID(FName NodeID);

// takes a dialogue selector, and get the node ID from it
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(CompactNodeTitle="Get ID", Keywords="Dialogue Get ID Selector"))
static void BreakDialogueNodeSelector(const FDialogueNodeSelector& Selector, FName& NodeID);

// Gets the node ID for a given dialogue node selector
UFUNCTION(BlueprintPure, DisplayName="Dialogue Node Selector To Name", Category = "Utilities", meta=(CompactNodeTitle = "->", BlueprintThreadSafe, BlueprintAutocast))
static FName Conv_DialogueNodeSelectorToName(const FDialogueNodeSelector& Selector);

// Gets a dialogue node selector from a given node ID
UFUNCTION(BlueprintPure, DisplayName="Name To Dialogue Node Selector", Category = "Utilities", meta=(CompactNodeTitle = "->", BlueprintThreadSafe, BlueprintAutocast))
static FDialogueNodeSelector Conv_NameToDialogueNodeSelector(const FName& NodeID);
/* dialogue selector */

/* quest state selector */
// makes a quest node selector, guaranteeing the use of the node selection list
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(Keywords="Quest Make Selector"))
static FQuestStateSelector MakeQuestStateSelector(FQuestStateSelector Selector);

// makes a quest state selector from a specific node ID
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(CompactNodeTitle="From ID", Keywords="Quest Make From ID Selector"))
static FQuestStateSelector MakeQuestStateSelectorFromID(FName NodeID);

// takes a quest state selector, and get the node ID from it
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(CompactNodeTitle="Get ID", Keywords="Quest Get ID Selector"))
static void BreakQuestStateSelector(const FQuestStateSelector& Selector, FName& NodeID);

// Gets the state ID for a given quest node selector
UFUNCTION(BlueprintPure, DisplayName="Quest State Selector To Name", Category = "Utilities", meta=(CompactNodeTitle = "->", BlueprintThreadSafe, BlueprintAutocast))
static FName Conv_QuestStateSelectorToName(const FQuestStateSelector& Selector);

// Gets a quest state selector from a given node ID
UFUNCTION(BlueprintPure, DisplayName="Name To Quest State Selector", Category = "Utilities", meta=(CompactNodeTitle = "->", BlueprintThreadSafe, BlueprintAutocast))
static FQuestStateSelector Conv_NameToQuestStateSelector(const FName& NodeID);
/* quest state selector */

/* quest branch selector */
// makes a quest branch selector, guaranteeing the use of the node selection list
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(Keywords="Quest Make Selector"))
static FQuestBranchSelector MakeQuestBranchSelector(FQuestBranchSelector Selector);

// makes a quest branch selector from a specific node ID
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(CompactNodeTitle="From ID", Keywords="Quest Make From ID Selector"))
static FQuestBranchSelector MakeQuestBranchSelectorFromID(FName NodeID);

// takes a quest branch selector, and get the node ID from it
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Narrative", meta=(CompactNodeTitle="Get ID", Keywords="Quest Get ID Selector"))
static void BreakQuestBranchSelector(const FQuestBranchSelector& Selector, FName& NodeID);

// Gets the branch ID for a given quest node selector
UFUNCTION(BlueprintPure, DisplayName="Quest Branch Selector To Name", Category = "Utilities", meta=(CompactNodeTitle = "->", BlueprintThreadSafe, BlueprintAutocast))
static FName Conv_QuestBranchSelectorToName(const FQuestBranchSelector& Selector);

// Gets a quest branch selector from a given node ID
UFUNCTION(BlueprintPure, DisplayName="Name To Quest Branch Selector", Category = "Utilities", meta=(CompactNodeTitle = "->", BlueprintThreadSafe, BlueprintAutocast))
static FQuestBranchSelector Conv_NameToQuestBranchSelector(const FName& NodeID);
/* quest branch selector */


...



DialogueSM.h

/Narrative/Source/Narrative/Public/DialogueSM.h
#pragma once

...

#include "MovieSceneSequencePlayer.h"
#include "DialogueSM.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDialogueNodeFinishedPlaying);

...

FacialAnimation = nullptr;
DialogueSound = nullptr;
DialogueMontage = nullptr;
FacialAnimation = nullptr;
Shot = nullptr;

...

class USoundBase* DialogueSound;
TObjectPtr<class USoundBase> DialogueSound;

/**
Narrative will play this montage on the first skeletalmeshcomponent found on your speaker with the tag "Body" added to it.

...

class UAnimMontage* DialogueMontage;
TObjectPtr<class UAnimMontage> DialogueMontage;

/**
Narrative will play this montage on the first skeletalmeshcomponent found on your speaker with the tag "Face" added to it.

...

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Line")
class UAnimMontage* FacialAnimation;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dialogue Line", meta = (DisplayName = "Facial Animation"))
TObjectPtr<class UAnimMontage> FacialAnimation;

/**
* Shot to play for this line. Overrides speaker shot if one is set

...

class UNarrativeDialogueSequence* Shot;
TObjectPtr<class UNarrativeDialogueSequence> Shot;

/**
* Optional conditions the line must pass for it to be selected
*/
UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite, Category = "Dialogue Line")
TArray<TObjectPtr<class UNarrativeCondition>> Conditions;
};

/**Base class for states and branches in the Dialogues state machine*/
//Returns a line with all conditions passing
public:


UFUNCTION(BlueprintCallable, Category="DialogueNode")
FText GetDialogueText() const;
FORCEINLINE bool IsAutoSelectIfOnlyReply() const { return bAutoSelectIfOnlyReply || IsRoutingNode(); };

...

};// removed-end

/**If true, this dialogue option will be automatically selected if it is the only reply available. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Details - Player Dialogue Node")
bool bAutoSelectIfOnlyReply = true;
};


QuestTask.cpp

/Narrative/Source/Narrative/Private/QuestTask.cpp

#include "Quest.h"
#include "NarrativeComponent.h"
#include <TimerManager.h>
#include <GameFramework/PlayerController.h>
#include "UObject/ConstructorHelpers.h"

...



DialogueBlueprintGeneratedClass.cpp

/Narrative/Source/Narrative/Private/DialogueBlueprintGeneratedClass.cpp
 #include "DialogueBlueprintGeneratedClass.h"
#include "Dialogue.h"
#include "DialogueSM.h"

...



NarrativeDialogueSequence.cpp

/Narrative/Source/Narrative/Private/NarrativeDialogueSequence.cpp
 
#include "NarrativeDialogueSequence.h"

...

if (SelectedSequence)
if (SelectedSequence && SequenceActor.IsValid())
{
SequenceActor->PlaybackSettings = PlaybackSettings;
SequenceActor->SetSequence(SelectedSequence);

...

if (SequenceActor->SequencePlayer)
{
SequenceActor->bOverrideInstanceData = AnchorOriginRule != EAnchorOriginRule::AOR_Disabled;

if (UDefaultLevelSequenceInstanceData* InstanceData = Cast<UDefaultLevelSequenceInstanceData>(SequenceActor->DefaultInstanceData))
{
InstanceData->TransformOrigin = AnchorOriginRule != EAnchorOriginRule::AOR_Disabled ? GetShotAnchorTransform() : FTransform();
}

SequenceActor->SequencePlayer->Play();
if (ULevelSequencePlayer* SP = SequenceActor->GetSequencePlayer())
{
SP->Stop();
SP->SetPlaybackPosition(FMovieSceneSequencePlaybackParams(0, EUpdatePositionMethod::Jump));
}

SequenceActor->bOverrideInstanceData = AnchorOriginRule != EAnchorOriginRule::AOR_Disabled;

if (UDefaultLevelSequenceInstanceData* InstanceData = Cast<UDefaultLevelSequenceInstanceData>(SequenceActor->DefaultInstanceData))
{
InstanceData->TransformOrigin = AnchorOriginRule != EAnchorOriginRule::AOR_Disabled ? GetShotAnchorTransform() : FTransform();
}

SequenceActor->PlaybackSettings = PlaybackSettings;
SequenceActor->SetSequence(SelectedSequence);

if (ULevelSequencePlayer* SP = SequenceActor->GetSequencePlayer())
{
SP->Play();

//Go in, and tell the cinecam to focus/track the speaker
for (auto& BoundObject : SequenceActor->SequencePlayer->GetBoundObjects(SequenceActor->FindNamedBinding(NAME_CinecamTag)))
for (auto& BoundObject : SP->GetBoundObjects(SequenceActor->FindNamedBinding(NAME_CinecamTag)))
{

...



NarrativeCondition.cpp

/Narrative/Source/Narrative/Private/NarrativeCondition.cpp
 
#include "NarrativeCondition.h"


NarrativeDataTask.cpp

/Narrative/Source/Narrative/Private/NarrativeDataTask.cpp
 #include "NarrativeDataTask.h"



DialogueSM.cpp

/Narrative/Source/Narrative/Private/DialogueSM.cpp
 #include "DialogueSM.h"
#include "Dialogue.h"

...

#include "NarrativePartyComponent.h"
#include "Animation/AnimInstance.h"
#include "Components/AudioComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameFramework/Character.h"
#include "TimerManager.h"
#include "Kismet/GameplayStatics.h"
#include "LevelSequencePlayer.h"
#include "LevelSequenceActor.h"
#include "Sound/SoundWave.h"


...

FDialogueLine NewLine = Line;

//Construct the line instead of adding it as a member as to not break dialogues made pre 2.2
if (AlternativeLines.Num())
{
TArray<FDialogueLine> AllLines = AlternativeLines;
AllLines.Add(Line);
NewLine = AllLines[FMath::RandRange(0, AllLines.Num() - 1)];
FDialogueLine NewLine;

TArray<FDialogueLine> AllLines = AlternativeLines;
AllLines.Add(Line);

TArray<FDialogueLine> AllPassingLines;

if (OwningComponent)
{
for (auto& LineToCheck : AllLines)
{
bool bAllConditionsPass = true;

//TODO consolidate the Character checking stuff into UNarrativeConditions as we're violating dont repeat yourself here - dialogue node base has something similar to this
for (auto& Cond : LineToCheck.Conditions)
{
if (Cond->CheckCondition(OwningComponent->GetOwningPawn(), OwningComponent->GetOwningController(), OwningComponent) == Cond->bNot)
{
bAllConditionsPass = false;
break;
}
}

if (bAllConditionsPass)
{
AllPassingLines.Add(LineToCheck);
}
}
}

//Randomly select a valid dialogue line
if (AllPassingLines.Num())
{
NewLine = AllPassingLines[FMath::RandRange(0, AllPassingLines.Num() - 1)];
}

if (NewLine.Duration == ELineDuration::LD_Default)
FText UDialogueNode::GetDialogueText() const
{
FText LineText = Line.Text.IsEmpty()? FText::GetEmpty() : Line.Text;

// if text is empty, check the sound wave if there are dialogue lines.
const USoundWave* LineSound = Cast<USoundWave>(Line.DialogueSound);
if (LineSound && LineSound->SupportsSubtitles() && LineText.IsEmpty())
{
// append each text item
FString Str;
for (const FSubtitleCue& SubtitleCue : LineSound->Subtitles)
{
Str.Append(SubtitleCue.Text.ToString() + ". ");
}

LineText = FText::FromString(Str);
}

return LineText;
}


...

if (PropertyChangedEvent.MemberProperty)
{
//If we changed the ID, make sure it doesn't conflict with any other IDs in the Dialogue
if (PropertyChangedEvent.MemberProperty->GetFName() == GET_MEMBER_NAME_CHECKED(FDialogueLine, Text))
{
FString DialogueAssetName = "";

if (UDialogue* Dialogue = Cast<UDialogue>(GetOuter()))
{
if (UObject* DialogueBP = Cast<UObject>(Dialogue->GetOuter()))
{
DialogueAssetName = DialogueBP->GetFName().ToString();
}
}

//Only autogenerate the ID if an ID hasn't been assigned yet
if (HasDefaultID())
{
GenerateIDFromText();
}
const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;

//If we changed the ID, make sure it doesn't conflict with any other IDs in the Dialogue
if (PropertyName == GET_MEMBER_NAME_CHECKED(FDialogueLine, Text))
{
FString DialogueAssetName = "";

if (UDialogue* Dialogue = Cast<UDialogue>(GetOuter()))
{
if (UObject* DialogueBP = Cast<UObject>(Dialogue->GetOuter()))
{
DialogueAssetName = DialogueBP->GetFName().ToString();
}
}

//Only autogenerate the ID if an ID hasn't been assigned yet
if (HasDefaultID())
{
GenerateIDFromText();
}
}
}

...

FString TextString = Line.Text.ToString();
FString TextString = GetDialogueText().ToString();

TArray<FString> ContentArray;
FString ContentString = "";

if (Dialogue->RootDialogue == this)
{
FinalString.Append("_Root");
}
const FString DefaultNodeID = DialogueAssetName + "_" + GetName();
const FString DefaultRootNodeID = DialogueAssetName + "_Root";


...

return (ID.ToString() == DialogueAssetName + "_" + GetName());
return ID.ToString() == DefaultNodeID || ID.ToString() == DefaultRootNodeID || ID.IsNone();
}

#endif

...

#undef LOCTEXT_NAMESPACE// removed-end
#undef LOCTEXT_NAMESPACE


NarrativeNodeBase.cpp

/Narrative/Source/Narrative/Private/NarrativeNodeBase.cpp
 
#include "NarrativeNodeBase.h"

...

Event->ExecuteEvent(Comp->GetOwningPawn(), Comp->GetOwningController(), Comp);
if (Event->AreConditionsMet(Pawn, Controller, NarrativeComponent))
{
Event->OnActivate(Comp->GetOwningPawn(), Comp->GetOwningController(), Comp); //execute event
Event->OnDeactivate(Comp->GetOwningPawn(), Comp->GetOwningController(), Comp); //deactivate event
}
}
}
}

...


if (!NarrativeComponent)
{
UE_LOG(LogNarrative, Warning, TEXT("Tried running conditions on node %s but Narrative Comp was null."), *GetNameSafe(this));


Narrative.cpp

/Narrative/Source/Narrative/Private/Narrative.cpp
 #include "Narrative.h"

DEFINE_LOG_CATEGORY(LogNarrativeRuntime);
#include "GameplayTagsManager.h"
#include "NarrativeGameplayTags.h"

...

UE_LOG(LogNarrativeRuntime, Log, TEXT("Narrative Runtime loaded."));
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module

FNarrativeGameplayTags::Get().InitializeNativeTags();
UGameplayTagsManager::Get().DoneAddingNativeTags();

}

void FNarrativeModule::ShutdownModule()

...

UE_LOG(LogNarrativeRuntime, Log, TEXT("Narrative Runtime unloaded."));

}

#undef LOCTEXT_NAMESPACE

...

IMPLEMENT_MODULE(FNarrativeModule, Narrative)// removed-end
IMPLEMENT_MODULE(FNarrativeModule, Narrative)


NarrativeComponent.cpp

/Narrative/Source/Narrative/Private/NarrativeComponent.cpp
 #include "NarrativeComponent.h"
#include "NarrativePartyComponent.h"

...

#include "NarrativeCondition.h"
#include "NarrativeEvent.h"
#include "NarrativeDialogueSettings.h"
#include "QuestTask.h"
#include "Engine/Engine.h"
#include "Kismet/GameplayStatics.h"

...

#include "Engine/ActorChannel.h"

DEFINE_LOG_CATEGORY(LogNarrative);



...

OnDialogueFinished.Broadcast(CurrentDialogue, true);
OnDialogueFinished.Broadcast(CurrentDialogue, true, EExitDialogueReason::EDR_NewDialogueStarted);

CurrentDialogue->Deinitialize();
CurrentDialogue = nullptr;
// notify external listeners
FDialogueDelegates::OnDialogueStarted.Broadcast(this, CurrentDialogue, PlayParams.StartFromID);


...

void UNarrativeComponent::ClientExitDialogue_Implementation()
void UNarrativeComponent::ClientExitDialogue_Implementation(const EExitDialogueReason Reason)
{
if (CurrentDialogue)
{
OnDialogueFinished.Broadcast(CurrentDialogue, false);
OnDialogueFinished.Broadcast(CurrentDialogue, false, Reason);

...

void UNarrativeComponent::ClientExitPartyDialogue_Implementation()
void UNarrativeComponent::ClientExitPartyDialogue_Implementation(const EExitDialogueReason Reason)
{
if (PartyComponent)
{
PartyComponent->ClientExitDialogue();
PartyComponent->ClientExitDialogue(Reason);

...

void UNarrativeComponent::TryExitDialogue()
bool UNarrativeComponent::TryExitDialogue(const EExitDialogueReason Reason)
{
if (CurrentDialogue && CurrentDialogue->bCanBeExited)
{

...

CurrentDialogue->OwningComp->ExitDialogue();
CurrentDialogue->OwningComp->ExitDialogue(Reason);
}
}
else

...

ServerTryExitDialogue();
}
}
}

void UNarrativeComponent::ExitDialogue()
ServerTryExitDialogue(Reason);
}

if (!CurrentDialogue)
{
return true;
}
}

return false;
}

void UNarrativeComponent::ExitDialogue(const EExitDialogueReason Reason)
{
if (HasAuthority())
{
ClientExitDialogue();
}
}

void UNarrativeComponent::ServerTryExitDialogue_Implementation()
{
TryExitDialogue();
ClientExitDialogue(Reason);
}
}

void UNarrativeComponent::ServerTryExitDialogue_Implementation(const EExitDialogueReason Reason)
{
TryExitDialogue(Reason);

...

void UNarrativeComponent::BeginSave(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Begun saving using save name: %s"), *SaveName);
}

void UNarrativeComponent::BeginLoad(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Begun loading using save name: %s"), *SaveName);
}

void UNarrativeComponent::SaveComplete(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Save complete for save name: %s"), *SaveName);
}

void UNarrativeComponent::LoadComplete(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Load complete for save name: %s"), *SaveName);
}

void UNarrativeComponent::DialogueRepliesAvailable(class UDialogue* Dialogue, const TArray<UDialogueNode_Player*>& PlayerReplies)
{


...

void UNarrativeComponent::DialogueFinished(UDialogue* Dialogue, const bool bStartingNewDialogue)
void UNarrativeComponent::DialogueFinished(UDialogue* Dialogue, const bool bStartingNewDialogue, const EExitDialogueReason Reason)
{

}
}

void UNarrativeComponent::BeginSave(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Begun saving using save name: %s"), *SaveName);
}

void UNarrativeComponent::BeginLoad(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Begun loading using save name: %s"), *SaveName);
}

void UNarrativeComponent::SaveComplete(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Save complete for save name: %s"), *SaveName);
}

void UNarrativeComponent::LoadComplete(FString SaveName)
{
UE_LOG(LogNarrative, Verbose, TEXT("Load complete for save name: %s"), *SaveName);

BegunQuest->QuestPostLoad();


...



NarrativeEvent.cpp

/Narrative/Source/Narrative/Private/NarrativeEvent.cpp
 
#include "NarrativeEvent.h"

#include "NarrativeComponent.h"
#include "NarrativeCondition.h"
#include "NarrativePartyComponent.h"
#include "Chaos/PBDSuspensionConstraintData.h"
}

void UNarrativeEvent::OnActivate_Implementation(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent)
{
ExecuteEvent(Target, Controller, NarrativeComponent);
}

void UNarrativeEvent::OnDeactivate_Implementation(APawn* Target, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent)
{


bool UNarrativeEvent::AreConditionsMet(APawn* Pawn, APlayerController* Controller, class UNarrativeComponent* NarrativeComponent)
{

if (!NarrativeComponent)
{
UE_LOG(LogNarrative, Warning, TEXT("Tried running conditions on node %s but Narrative Comp was null."), *GetNameSafe(this));
return false;
}

//Ensure all conditions are met
for (auto& Cond : Conditions)
{
if (Cond)
{
//We're running a condition on a party! Figure out who we need to run the condition on
if (UNarrativePartyComponent* PartyComp = Cast<UNarrativePartyComponent>(NarrativeComponent))
{
TArray<UNarrativeComponent*> ComponentsToCheck;

UE_LOG(LogNarrative, Warning, TEXT("Running on party..."));
//We need to check everyone in the party
if (Cond->PartyConditionPolicy == EPartyConditionPolicy::AllPlayersPass || Cond->PartyConditionPolicy == EPartyConditionPolicy::AnyPlayerPasses)
{
ComponentsToCheck.Append(PartyComp->GetPartyMembers());
}//We need to check the party leader
else if (Cond->PartyConditionPolicy == EPartyConditionPolicy::PartyLeaderPasses)
{
ComponentsToCheck.Add(PartyComp->GetPartyLeader());
}
else if (Cond->PartyConditionPolicy == EPartyConditionPolicy::PartyPasses)
{
ComponentsToCheck.Add(PartyComp);
}

bool bAnyonePassed = false;

//If any of our comps to check fail, return false
for (auto& ComponentToCheck : ComponentsToCheck)
{
const bool bConditionPassed = Cond && Cond->CheckCondition(ComponentToCheck->GetOwningPawn(), ComponentToCheck->GetOwningController(), ComponentToCheck) != Cond->bNot;
FString CondString = bConditionPassed ? "passed" : "failed";

if (bConditionPassed)
{
//We'll check the next condition since someone passed
if (Cond->PartyConditionPolicy == EPartyConditionPolicy::AnyPlayerPasses)
{
bAnyonePassed = true;
break;
}
}
else
{
if (Cond->PartyConditionPolicy != EPartyConditionPolicy::AnyPlayerPasses)
{
return false;
}
}

UE_LOG(LogNarrative, Warning, TEXT("Checking %s event condition, and they: %s"), *GetNameSafe(ComponentToCheck), *CondString);
}

//If we didn't break, no players passed
if (!bAnyonePassed && Cond->PartyConditionPolicy == EPartyConditionPolicy::AnyPlayerPasses)
{
return false;
}

}
else
{
if (Cond && Cond->CheckCondition(Pawn, Controller, NarrativeComponent) == Cond->bNot)
{
return false;
}
}

}
}

return true;
}

...



NarrativePartyComponent.cpp

/Narrative/Source/Narrative/Private/NarrativePartyComponent.cpp
 
#include "NarrativePartyComponent.h"

...

void UNarrativePartyComponent::ExitDialogue()
void UNarrativePartyComponent::ExitDialogue(const EExitDialogueReason Reason)
{
if (HasAuthority())
{

...

GroupMemberComp->ClientExitPartyDialogue();
GroupMemberComp->ClientExitPartyDialogue(Reason);
}
}

ClientExitDialogue();
ClientExitDialogue(Reason);

...

}// removed-end
}


NarrativeDialogueSettings.cpp

/Narrative/Source/Narrative/Private/NarrativeDialogueSettings.cpp
 #include "NarrativeDialogueSettings.h"



QuestBlueprintGeneratedClass.cpp

/Narrative/Source/Narrative/Private/QuestBlueprintGeneratedClass.cpp
 
#include "QuestBlueprintGeneratedClass.h"

...

#include "QuestSM.h"

void UQuestBlueprintGeneratedClass::InitializeQuest(class UQuest* Quest)
{


NarrativeSaveGame.cpp

/Narrative/Source/Narrative/Private/NarrativeSaveGame.cpp
 
#include "NarrativeSaveGame.h"


QuestSM.cpp

/Narrative/Source/Narrative/Private/QuestSM.cpp
 #include "QuestSM.h"
#include "Quest.h"

...

#include "NarrativeDataTask.h"
#include "NarrativeComponent.h"
#include "NarrativeQuestSettings.h"
#include "QuestTask.h"


...

if (OwningQuest)
if (OwningQuest && OwningQuest->GetQuestCompletion() == EQuestCompletion::QC_Started)
{
struct SOnEnteredStruct
{


NarrativeTaskManager.cpp

/Narrative/Source/Narrative/Private/NarrativeTaskManager.cpp
 
#include "NarrativeTaskManager.h"


NarrativeDefaultCinecam.cpp

/Narrative/Source/Narrative/Private/NarrativeDefaultCinecam.cpp

// Fill out your copyright notice in the Description page of Project Settings.
// Copyright Narrative Tools 2025.


#include "NarrativeDefaultCinecam.h"


NarrativeFunctionLibrary.cpp

/Narrative/Source/Narrative/Private/NarrativeFunctionLibrary.cpp
 
#include "NarrativeFunctionLibrary.h"

#include "NarrativeNodeSelector.h"

FDialogueNodeSelector UNarrativeFunctionLibrary::MakeDialogueNodeSelector(FDialogueNodeSelector Selector)
{
return Selector;
}

FDialogueNodeSelector UNarrativeFunctionLibrary::MakeDialogueNodeSelectorFromID(FName NodeID)
{
FDialogueNodeSelector Sel;
Sel.NodeID = NodeID;
Sel.Asset = nullptr;
return Sel;
}

void UNarrativeFunctionLibrary::BreakDialogueNodeSelector(const FDialogueNodeSelector& Selector, FName& NodeID)
{
NodeID = Selector.NodeID;
}

FName UNarrativeFunctionLibrary::Conv_DialogueNodeSelectorToName(const FDialogueNodeSelector& Selector)
{
return Selector.NodeID;
}

FDialogueNodeSelector UNarrativeFunctionLibrary::Conv_NameToDialogueNodeSelector(const FName& NodeID)
{
FDialogueNodeSelector Sel;
Sel.NodeID = NodeID;
return Sel;
}

FQuestStateSelector UNarrativeFunctionLibrary::MakeQuestStateSelector(FQuestStateSelector Selector)
{
return Selector;
}

FQuestStateSelector UNarrativeFunctionLibrary::MakeQuestStateSelectorFromID(FName NodeID)
{
FQuestStateSelector Sel;
Sel.NodeID = NodeID;
Sel.Asset = nullptr;
return Sel;
}

void UNarrativeFunctionLibrary::BreakQuestStateSelector(const FQuestStateSelector& Selector, FName& NodeID)
{
NodeID = Selector.NodeID;
}

FName UNarrativeFunctionLibrary::Conv_QuestStateSelectorToName(const FQuestStateSelector& Selector)
{
return Selector.NodeID;
}

FQuestStateSelector UNarrativeFunctionLibrary::Conv_NameToQuestStateSelector(const FName& NodeID)
{
FQuestStateSelector Sel;
Sel.NodeID = NodeID;
return Sel;
}

FQuestBranchSelector UNarrativeFunctionLibrary::MakeQuestBranchSelector(FQuestBranchSelector Selector)
{
return Selector;
}

FQuestBranchSelector UNarrativeFunctionLibrary::MakeQuestBranchSelectorFromID(FName NodeID)
{
FQuestBranchSelector Sel;
Sel.NodeID = NodeID;
Sel.Asset = nullptr;
return Sel;
}

void UNarrativeFunctionLibrary::BreakQuestBranchSelector(const FQuestBranchSelector& Selector, FName& NodeID)
{
NodeID = Selector.NodeID;
}

FName UNarrativeFunctionLibrary::Conv_QuestBranchSelectorToName(const FQuestBranchSelector& Selector)
{
return Selector.NodeID;
}

FQuestBranchSelector UNarrativeFunctionLibrary::Conv_NameToQuestBranchSelector(const FName& NodeID)
{
FQuestBranchSelector Sel;
Sel.NodeID = NodeID;
return Sel;
}

...



Quest.cpp

/Narrative/Source/Narrative/Private/Quest.cpp
 #include "Quest.h"
#include "NarrativeDataTask.h"
#include "QuestSM.h"

...

#include "NarrativeEvent.h"
#include "NarrativeComponent.h"
#include "NarrativeFunctionLibrary.h"
#include "NarrativePartyComponent.h"
#include "GameFramework/PlayerController.h"
#include "Net/UnrealNetwork.h"
#include <GameFramework/PlayerController.h>

FQuestDelegates::FOnQuestStartedSignature FQuestDelegates::OnQuestStarted;
FQuestDelegates::FOnQuestEndSignature FQuestDelegates::OnQuestEnd;

bResumeDialogueAfterLoad = true;

for (auto& Req : QuestRequirements)
{
if (Req)
{
Req->OnRemoved(this);
}
}

QuestRequirements.Empty();
// notify other listening objects
FQuestDelegates::OnQuestStarted.Broadcast(OwningComp, this, QuestStartID);

}
}

void UQuest::HandleBeginDialogue()
{
if (!QuestDialogue || !OwningComp)
{
return;
}

FDialoguePlayParams PlayParams;
PlayParams.bOverride_bFreeMovement = true;
PlayParams.bFreeMovement = true;
PlayParams.bOverride_bUnskippable = true;
PlayParams.bUnskippable = true;
PlayParams.StartFromID = CurrentState ? CurrentState->GetID() : NAME_None;
if (OwningComp->HasDialogueAvailable(QuestDialogue, PlayParams))
{
OwningComp->BeginDialogue(QuestDialogue, PlayParams);

if (!OwningComp->bIsLoading)
{
HandleBeginDialogue();
}

...

if (State->GetID() == ID)
if (State && State->GetID() == ID)
{
return State;
}

...

if (Branch->GetID() == ID)
if (Branch && Branch->GetID() == ID)
{
return Branch;
}
#if WITH_EDITOR
void UQuest::PostLoad()
{
// fix missing transaction flag on nodes
for (UQuestNode* Node : GetNodes())
{
Node->SetFlags(RF_Transactional);
}

// fix dialogue connection
UpdateDialogueConnection();

Super::PostLoad();
}

void UQuest::PreEditChange(FProperty* PropertyAboutToChange)
{
const FName PropertyName = (PropertyAboutToChange != nullptr) ? PropertyAboutToChange->GetFName() : NAME_None;

// update quest asset
if (PropertyName == GET_MEMBER_NAME_CHECKED(UQuest, QuestDialogue))
{
LastQuestDialogue = QuestDialogue;
}

Super::PreEditChange(PropertyAboutToChange);
}

void UQuest::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;

// update last dialogue connected quest
if (PropertyName == GET_MEMBER_NAME_CHECKED(UQuest, QuestDialogue))
{
UpdateDialogueConnection();
}

Super::PostEditChangeProperty(PropertyChangedEvent);
}

void UQuest::UpdateDialogueConnection()
{
if (UDialogue* LastDialogue = Cast<UDialogue>(LastQuestDialogue.GetDefaultObject()))
{
// remove connected quest ref
if (LastDialogue->EditorLinkedQuest == GetClass()->GetDefaultObject()->GetClass())
{
LastDialogue->Modify();
LastDialogue->EditorLinkedQuest = nullptr;
LastDialogue->MarkPackageDirty();
}
LastQuestDialogue = nullptr;
}

// update new dialogue connected quest
if (UDialogue* NewDialogue = Cast<UDialogue>(QuestDialogue.GetDefaultObject()))
{
// set connected quest ref
if (NewDialogue->EditorLinkedQuest == nullptr)
{
NewDialogue->Modify();
NewDialogue->EditorLinkedQuest = GetClass()->GetDefaultObject()->GetClass();
NewDialogue->MarkPackageDirty();
}
}
}
#endif

void UQuest::QuestPostLoad()
{
if (bResumeDialogueAfterLoad && QuestCompletion == EQuestCompletion::QC_Started)
{
HandleBeginDialogue();
}

BPQuestPostLoad();
OnQuestPostLoad.Broadcast(this);
}

// notify listeners this quest has ended
FQuestDelegates::OnQuestEnd.Broadcast(OwningComp, this);

// notify listeners this quest has ended
FQuestDelegates::OnQuestEnd.Broadcast(OwningComp, this);


QuestDialogue = nullptr;
}

void UQuest::AddQuestRequirement_Implementation(UQuestRequirement* Requirement)
{
if (!QuestRequirements.Contains(Requirement))
{
QuestRequirements.Add(Requirement);
Requirement->OnAdded(this);
}
}

void UQuest::RemoveQuestRequirement_Implementation(UQuestRequirement* Requirement)
{
if (QuestRequirements.Contains(Requirement))
{
QuestRequirements.Remove(Requirement);
Requirement->OnRemoved(this);
}

UQuestRequirement::UQuestRequirement()
{

}

void UQuestRequirement::OnAdded_Implementation(UQuest* Quest)
{

}

void UQuestRequirement::OnRemoved_Implementation(UQuest* Quest)
{

}

UQuest* UQuestRequirement::GetOwningQuest() const
{
return Cast<UQuest>(GetOuter());
}

...



NarrativeQuestSettings.cpp

/Narrative/Source/Narrative/Private/NarrativeQuestSettings.cpp

// Fill out your copyright notice in the Description page of Project Settings.
// Copyright Narrative Tools 2025.


#include "NarrativeQuestSettings.h"

...

}// removed-end
}


Dialogue.cpp

/Narrative/Source/Narrative/Private/Dialogue.cpp
 #include "Dialogue.h"
#include "NarrativeDialogueSettings.h"


FDialogueDelegates::FOnDialogueStartedSignature FDialogueDelegates::OnDialogueStarted;
FDialogueDelegates::FOnDialogueEndSignature FDialogueDelegates::OnDialogueEnd;
bUnskippable = false;
EndDialogueDist = -1.f;
if (PlayParams.Priority > -1)
{
Priority = PlayParams.Priority;
}

if (PlayParams.bOverride_bUnskippable)
{
bFreeMovement = PlayParams.bUnskippable;
}

if (PlayParams.bOverride_bFreeMovement)
{
bFreeMovement = PlayParams.bFreeMovement;
}

if (PlayParams.bOverride_bStopMovement)
{
bAutoStopMovement = PlayParams.bStopMovement;
}

StopDialogueSequence();


...


StopDialogueSequence();

if (DialogueAudio)
{
void UDialogue::PostLoad()
{
// fix missing transaction flag on nodes
for (UDialogueNode* Node : GetNodes())
{
Node->SetFlags(RF_Transactional);
}

Super::PostLoad();
}


...

void UDialogue::ExitDialogue()
void UDialogue::ExitDialogue(const EExitDialogueReason Reason)
{
if (OwningComp)
{
OwningComp->ExitDialogue();
OwningComp->ExitDialogue(Reason);

if (EndDialogueDist > 0.f && OwningPawn)
{
for (auto& Speaker : Speakers)
{
if (SpeakerAvatars.Contains(Speaker.GetSpeakerID()))
{
if (AActor* SpeakerAvatar = *SpeakerAvatars.Find(Speaker.GetSpeakerID()))
{
if (OwningPawn->GetDistanceTo(SpeakerAvatar) > EndDialogueDist)
{
OwningComp->ExitDialogue(EExitDialogueReason::EDR_TooFarAway);
}
}
}
}
}

}

bool UDialogue::CanSkipDialogue_Implementation() const
{
return !bUnskippable;

...


InitSpeakerAvatars();

if (bAdjustPlayerTransform)
{
AdjustPlayerTransform();
}

OldViewTarget = OwningController ? OwningController->GetViewTarget() : nullptr;


...

if (bAutoStopMovement)
//Free movement shouldnt autostop
if (bAutoStopMovement && !bFreeMovement)
{
if (const ACharacter* PlayerChar = Cast<ACharacter>(OwningPawn))
{
}

InitSpeakerAvatars();

if (bAdjustPlayerTransform)
{
AdjustPlayerTransform();
int32 Idx = 0;


...

if (AActor* SpeakerActor = LinkSpeakerAvatar(Speaker))
if (AActor* SpeakerActor = LinkSpeakerAvatar(Speaker, Idx))
{
if (IsValid(SpeakerActor))
{

++Idx;

...

if (AActor* SpeakerActor = LinkSpeakerAvatar(PlayerSpeakerInfo))
if (AActor* SpeakerActor = LinkSpeakerAvatar(PlayerSpeakerInfo, -1))
{
SpeakerAvatars.Add(PlayerSpeakerInfo.GetSpeakerID(), SpeakerActor);
PlayerSpeakerInfo.SpeakerAvatarTransform = SpeakerActor->GetActorTransform();

// notify listeners that this dialogue has ended
FDialogueDelegates::OnDialogueEnd.Broadcast(OwningComp, this);


...

bool bWantsAutoSelect = bFreeMovement;
bool bWantsAutoSelect = false; //bFreeMovement;

if (const UNarrativeDialogueSettings* DialogueSettings = GetDefault<UNarrativeDialogueSettings>())
{
if (DialogueSettings->bAutoSelectSingleResponse && AvailableResponses.Num() == 1)
if (DialogueSettings->bAutoSelectSingleResponse || (AvailableResponses.Num() == 1 && AvailableResponses.IsValidIndex(0) && AvailableResponses[0]->IsAutoSelectIfOnlyReply()))

...



//If a response is autoselect, select it and early out
for (auto& AvailableResponse : AvailableResponses)

...

ExitDialogue();
ExitDialogue(EExitDialogueReason::EDR_NoLines);
}
}


...


FVector EyesLoc;

//We want the head bone, but not where the head bone is as that moves around. We want the default offset the head bone is normally at. Even this is technically not perfect as this is the t-pose
FVector EyesLoc = FVector::ZeroVector;
FRotator EyesRot;

Actor->GetActorEyesViewPoint(EyesLoc, EyesRot);

const bool bIsChar = Actor->IsA<ACharacter>();

//Head is located at a different height per character, lets find it...

...

EyesLoc = SkelMesh->GetBoneLocation(DefaultHeadBoneName);
//EyesLoc.Z = SkelMesh->GetBoneLocation(DefaultHeadBoneName).Z;
break;
EyesLoc.Z = SkelMesh->GetBoneLocation(DefaultHeadBoneName).Z;
}
}
}

...

ExitDialogue();
ExitDialogue(EExitDialogueReason::EDR_NoLines);
return;
}


...

ExitDialogue();
ExitDialogue(EExitDialogueReason::EDR_NoLines);
}
}
}

...

AActor* UDialogue::LinkSpeakerAvatar_Implementation(const FSpeakerInfo& Info)
AActor* UDialogue::LinkSpeakerAvatar_Implementation(const FSpeakerInfo& Info, const int32 Idx)
{
//Default to using the OwningPawn, or DefaultNPCAvatar, unless something else can be found...
AActor* SpawnedActor = Info.GetSpeakerID() == PlayerSpeakerInfo.GetSpeakerID() ? OwningPawn : nullptr;


DialogueAsset.cpp

/Narrative/Source/Narrative/Private/DialogueAsset.cpp

#include "DialogueAsset.h"


W_NarrativeMenu_Dialogue.uasset

  • UPDATED (asset)

WBP_NarrativeButton_DialogueOption.uasset

  • UPDATED (asset)

BP_Narrative3Overlay.uasset

  • UPDATED (asset)

W_NarrativeMenu_QuestJournal.uasset

  • UPDATED (asset)

NE_BeginQuest.uasset

  • UPDATED (asset)

NE_BeginDialogue.uasset

  • REMOVED

BPT_FinishDialogue.uasset

  • UPDATED (asset)

BPT_PlayDialogueNode.uasset

  • UPDATED (asset)

BPT_GoToLocation.uasset

  • UPDATED (asset)