Introduced dialogue graph magic, created assest and importers. Added events for broadcasting puzzle steps
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
"references": [
|
||||
"GUID:d91d3f46515a6954caa674697afbf416",
|
||||
"GUID:69448af7b92c7f342b298e06a37122aa",
|
||||
"GUID:9e24947de15b9834991c9d8411ea37cf"
|
||||
"GUID:9e24947de15b9834991c9d8411ea37cf",
|
||||
"GUID:70ef9a24f4cfc4aec911c1414e3f90ad",
|
||||
"GUID:d1e08c06f8f9473888c892637c83c913"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
||||
3
Assets/Editor/Dialogue.meta
Normal file
3
Assets/Editor/Dialogue.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ced9daeaad4427f9feb953a53b6d3df
|
||||
timeCreated: 1758869763
|
||||
20
Assets/Editor/Dialogue/DialogueGraph.cs
Normal file
20
Assets/Editor/Dialogue/DialogueGraph.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Unity.GraphToolkit.Editor;
|
||||
|
||||
namespace Editor.Dialogue
|
||||
{
|
||||
[Serializable]
|
||||
[Graph(AssetExtension)]
|
||||
public class DialogueGraph : Graph
|
||||
{
|
||||
public const string AssetExtension = "dialoguegraph";
|
||||
|
||||
[MenuItem("Assets/Create/Dialogue Graph", false)]
|
||||
private static void CreateAssetFile()
|
||||
{
|
||||
GraphDatabase.PromptInProjectBrowserToCreateNewAsset<DialogueGraph>();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Editor/Dialogue/DialogueGraph.cs.meta
Normal file
3
Assets/Editor/Dialogue/DialogueGraph.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eff5cdbb7a22496e93560cffa5754029
|
||||
timeCreated: 1758869773
|
||||
84
Assets/Editor/Dialogue/DialogueGraphImporter.cs
Normal file
84
Assets/Editor/Dialogue/DialogueGraphImporter.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor.AssetImporters;
|
||||
using Unity.GraphToolkit.Editor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dialogue;
|
||||
|
||||
namespace Editor.Dialogue
|
||||
{
|
||||
[ScriptedImporter(1, DialogueGraph.AssetExtension)]
|
||||
public class DialogueGraphImporter : ScriptedImporter
|
||||
{
|
||||
public override void OnImportAsset(AssetImportContext ctx)
|
||||
{
|
||||
DialogueGraph editorGraph = GraphDatabase.LoadGraphForImporter<DialogueGraph>(ctx.assetPath);
|
||||
RuntimeDialogueGraph runtimeGraph = ScriptableObject.CreateInstance<RuntimeDialogueGraph>();
|
||||
var nodeIDMap = new Dictionary<INode, string>();
|
||||
|
||||
foreach (var node in editorGraph.GetNodes())
|
||||
{
|
||||
nodeIDMap[node] = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
// TODO: This could be done in the above loop, but for clarity, I'm keeping it separate for now.
|
||||
var startNode = editorGraph.GetNodes().OfType<StartNode>().FirstOrDefault();
|
||||
if (startNode != null)
|
||||
{
|
||||
var entryPoint = startNode.GetOutputPorts().FirstOrDefault()?.firstConnectedPort;
|
||||
if (entryPoint != null)
|
||||
{
|
||||
runtimeGraph.entryNodeID = nodeIDMap[entryPoint.GetNode()];
|
||||
}
|
||||
|
||||
runtimeGraph.speakerName = GetPortValue<string>(startNode.GetInputPortByName("SpeakerName"));
|
||||
}
|
||||
|
||||
foreach (var iNode in editorGraph.GetNodes())
|
||||
{
|
||||
if (iNode is StartNode || iNode is EndNode)
|
||||
continue;
|
||||
|
||||
var runtimeNode = new RuntimeDialogueNode{ nodeID = nodeIDMap[iNode]};
|
||||
if (iNode is DialogueNode dialogueNode)
|
||||
{
|
||||
ProcessDialogueNode(dialogueNode, runtimeNode, nodeIDMap);
|
||||
}
|
||||
|
||||
runtimeGraph.allNodes.Add(runtimeNode);
|
||||
}
|
||||
|
||||
ctx.AddObjectToAsset("RuntimeData", runtimeGraph);
|
||||
ctx.SetMainObject(runtimeGraph);
|
||||
}
|
||||
|
||||
private void ProcessDialogueNode(DialogueNode node, RuntimeDialogueNode runtimeNode, Dictionary<INode, string> nodeIDMap)
|
||||
{
|
||||
runtimeNode.dialogueLine = GetPortValue<string>(node.GetInputPortByName("DialogueLine"));
|
||||
|
||||
var nextNodePort = node.GetOutputPortByName("out")?.firstConnectedPort;
|
||||
if (nextNodePort != null)
|
||||
{
|
||||
runtimeNode.nextNodeID = nodeIDMap[nextNodePort.GetNode()];
|
||||
}
|
||||
}
|
||||
|
||||
private T GetPortValue<T>(IPort port)
|
||||
{
|
||||
if (port == null) return default(T);
|
||||
|
||||
if (port.isConnected)
|
||||
{
|
||||
if (port.firstConnectedPort.GetNode() is IVariableNode variableNode)
|
||||
{
|
||||
variableNode.variable.TryGetDefaultValue(out T value);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
port.TryGetValue(out T fallbackValue);
|
||||
return fallbackValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Editor/Dialogue/DialogueGraphImporter.cs.meta
Normal file
3
Assets/Editor/Dialogue/DialogueGraphImporter.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2334d3ee5254a2bbcb316035c681b27
|
||||
timeCreated: 1758871584
|
||||
196
Assets/Editor/Dialogue/DialogueNodes.cs
Normal file
196
Assets/Editor/Dialogue/DialogueNodes.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using UnityEngine;
|
||||
using Unity.GraphToolkit.Editor;
|
||||
using System;
|
||||
|
||||
namespace Editor.Dialogue
|
||||
{
|
||||
[Serializable]
|
||||
public class StartNode : Node
|
||||
{
|
||||
protected override void OnDefinePorts(IPortDefinitionContext context)
|
||||
{
|
||||
context.AddOutputPort("out").Build();
|
||||
|
||||
context.AddInputPort<string>("SpeakerName").Build();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EndNode : Node
|
||||
{
|
||||
protected override void OnDefinePorts(IPortDefinitionContext context)
|
||||
{
|
||||
context.AddInputPort("in").Build();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public enum DialogueType
|
||||
{
|
||||
SayOneLine,
|
||||
SayMultipleLines
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DialogueNode : Node
|
||||
{
|
||||
const string LineTypeOptionName = "DialogueLineType";
|
||||
const string NoLinesOptionName = "NoLines";
|
||||
const string LoopThroughDefaultLinesOptionName = "LoopThroughDefaultLines";
|
||||
const string DefaultDialogueLineOptionName = "DefaultDialogueLine";
|
||||
|
||||
|
||||
protected override void OnDefineOptions(IOptionDefinitionContext context)
|
||||
{
|
||||
context.AddOption<DialogueType>(LineTypeOptionName)
|
||||
.WithDisplayName("Default Line Type")
|
||||
.WithDefaultValue(DialogueType.SayOneLine)
|
||||
.Delayed();
|
||||
|
||||
|
||||
context.AddOption<int>(NoLinesOptionName)
|
||||
.WithDisplayName("Number of Default Lines")
|
||||
.WithDefaultValue(1)
|
||||
.Delayed();
|
||||
}
|
||||
|
||||
protected override void OnDefinePorts(IPortDefinitionContext context)
|
||||
{
|
||||
context.AddInputPort("in").Build();
|
||||
context.AddOutputPort("out").Build();
|
||||
|
||||
var lineTypeOption = GetNodeOptionByName(LineTypeOptionName);
|
||||
lineTypeOption.TryGetValue<DialogueType>(out var lineType);
|
||||
var lineCountOption = GetNodeOptionByName(NoLinesOptionName);
|
||||
lineCountOption.TryGetValue<int>(out var lineCount);
|
||||
|
||||
if (lineType == DialogueType.SayMultipleLines)
|
||||
{
|
||||
for (var i = 0; i < lineCount; i++)
|
||||
{
|
||||
context.AddInputPort<string>($"{DefaultDialogueLineOptionName}{i + 1}").WithDisplayName($"Default Dialogue Line {i + 1}").Build();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AddInputPort<string>($"{DefaultDialogueLineOptionName}").WithDisplayName("Default Dialogue Line").Build();
|
||||
}
|
||||
|
||||
context.AddInputPort<bool>($"{LoopThroughDefaultLinesOptionName}").WithDisplayName("Loop Through Default Lines?").Build();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WaitOnPuzzleStep : DialogueNode
|
||||
{
|
||||
const string RequiredPuzzleStep = "RequiredPuzzleStep";
|
||||
|
||||
protected override void OnDefinePorts(IPortDefinitionContext context)
|
||||
{
|
||||
context.AddInputPort<PuzzleStepSO>(RequiredPuzzleStep).WithDisplayName("Required Puzzle Step").Build();
|
||||
|
||||
base.OnDefinePorts(context);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WaitOnPickup : DialogueNode
|
||||
{
|
||||
const string RequiredPickupsOptionName = "RequiredPickup";
|
||||
|
||||
protected override void OnDefinePorts(IPortDefinitionContext context)
|
||||
{
|
||||
context.AddInputPort<PickupItemData>(RequiredPickupsOptionName).WithDisplayName("Required Pickup").Build();
|
||||
|
||||
base.OnDefinePorts(context);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WaitOnSlot : DialogueNode
|
||||
{
|
||||
const string RequiredSlotOptionName = "RequiredSlot";
|
||||
// Incorrect item - i.e. not the correct one but also not forbidden
|
||||
const string IncorrectItemLineTypeOptionName = "IncorrectItemDialogueLineType";
|
||||
const string IncorrectItemNoLinesOptionName = "IncorrectItemNoLines";
|
||||
const string LoopThroughIncorrectItemLinesOptionName = "LoopThroughIncorrectItemLines";
|
||||
const string IncorrectIteDialogueLineOptionName = "IncorrectItemDialogueLine";
|
||||
// Explicitely forbidden item
|
||||
const string ForbiddenItemLineTypeOptionName = "ForbiddenItemDialogueLineType";
|
||||
const string ForbiddenItemNoLinesOptionName = "ForbiddenItemNoLines";
|
||||
const string LoopThroughForbiddenItemLinesOptionName = "LoopThroughForbiddenItemLines";
|
||||
const string ForbiddenIteDialogueLineOptionName = "ForbiddenItemDialogueLine";
|
||||
|
||||
protected override void OnDefineOptions(IOptionDefinitionContext context)
|
||||
{
|
||||
base.OnDefineOptions(context);
|
||||
|
||||
// Incorrect
|
||||
context.AddOption<DialogueType>(IncorrectItemLineTypeOptionName)
|
||||
.WithDisplayName("Incorrect Item Line Type")
|
||||
.WithDefaultValue(DialogueType.SayOneLine)
|
||||
.Delayed();
|
||||
context.AddOption<int>(IncorrectItemNoLinesOptionName)
|
||||
.WithDisplayName("Number of Incorrect Item Lines")
|
||||
.WithDefaultValue(1)
|
||||
.Delayed();
|
||||
|
||||
// Forbidden
|
||||
context.AddOption<DialogueType>(ForbiddenItemLineTypeOptionName)
|
||||
.WithDisplayName("Forbidden Item Line Type")
|
||||
.WithDefaultValue(DialogueType.SayOneLine)
|
||||
.Delayed();
|
||||
context.AddOption<int>(ForbiddenItemNoLinesOptionName)
|
||||
.WithDisplayName("Forbidden of Incorrect Item Lines")
|
||||
.WithDefaultValue(1)
|
||||
.Delayed();
|
||||
}
|
||||
|
||||
protected override void OnDefinePorts(IPortDefinitionContext context)
|
||||
{
|
||||
context.AddInputPort<PickupItemData>(RequiredSlotOptionName).WithDisplayName("Required Slot").Build();
|
||||
|
||||
base.OnDefinePorts(context);
|
||||
|
||||
// Incorrect
|
||||
var incorrectItemLineTypeOption = GetNodeOptionByName(IncorrectItemLineTypeOptionName);
|
||||
incorrectItemLineTypeOption.TryGetValue<DialogueType>(out var incorrectItemLineType);
|
||||
var incorrectItemLineCountOption = GetNodeOptionByName(IncorrectItemNoLinesOptionName);
|
||||
incorrectItemLineCountOption.TryGetValue<int>(out var incorrectItemLineCount);
|
||||
|
||||
if (incorrectItemLineType == DialogueType.SayMultipleLines)
|
||||
{
|
||||
for (var i = 0; i < incorrectItemLineCount; i++)
|
||||
{
|
||||
context.AddInputPort<string>($"{IncorrectIteDialogueLineOptionName}{i + 1}").WithDisplayName($"Incorrect Item Dialogue Line {i + 1}").Build();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AddInputPort<string>($"{IncorrectIteDialogueLineOptionName}").WithDisplayName("Incorrect Item Dialogue Line").Build();
|
||||
}
|
||||
|
||||
context.AddInputPort<bool>($"{LoopThroughIncorrectItemLinesOptionName}").WithDisplayName("Loop Through Incorrect Item Lines?").Build();
|
||||
|
||||
// Forbidden
|
||||
var forbiddenItemLineTypeOption = GetNodeOptionByName(ForbiddenItemLineTypeOptionName);
|
||||
forbiddenItemLineTypeOption.TryGetValue<DialogueType>(out var forbiddenItemLineType);
|
||||
var forbiddenItemLineCountOption = GetNodeOptionByName(ForbiddenItemNoLinesOptionName);
|
||||
forbiddenItemLineCountOption.TryGetValue<int>(out var forbiddenItemLineCount);
|
||||
|
||||
if (forbiddenItemLineType == DialogueType.SayMultipleLines)
|
||||
{
|
||||
for (var i = 0; i < forbiddenItemLineCount; i++)
|
||||
{
|
||||
context.AddInputPort<string>($"{ForbiddenIteDialogueLineOptionName}{i + 1}").WithDisplayName($"Forbidden Item Dialogue Line {i + 1}").Build();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AddInputPort<string>($"{ForbiddenIteDialogueLineOptionName}").WithDisplayName("Forbidden Item Dialogue Line").Build();
|
||||
}
|
||||
|
||||
context.AddInputPort<bool>($"{LoopThroughForbiddenItemLinesOptionName}").WithDisplayName("Loop Through Forbidden Item Lines?").Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Editor/Dialogue/DialogueNodes.cs.meta
Normal file
3
Assets/Editor/Dialogue/DialogueNodes.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8b3213c46b447a9adc5627a273f2b2d
|
||||
timeCreated: 1758870466
|
||||
Reference in New Issue
Block a user