├── .gitignore ├── Assets ├── DialogueSystem.meta ├── DialogueSystem │ ├── Enumerations.meta │ ├── Enumerations │ │ ├── DSDialogueType.cs │ │ └── DSDialogueType.cs.meta │ ├── Scripts.meta │ ├── Scripts │ │ ├── DSDialogue.cs │ │ ├── DSDialogue.cs.meta │ │ ├── Data.meta │ │ ├── Data │ │ │ ├── DSDialogueChoiceData.cs │ │ │ └── DSDialogueChoiceData.cs.meta │ │ ├── ScriptableObjects.meta │ │ └── ScriptableObjects │ │ │ ├── DSDialogueContainerSO.cs │ │ │ ├── DSDialogueContainerSO.cs.meta │ │ │ ├── DSDialogueGroupSO.cs │ │ │ ├── DSDialogueGroupSO.cs.meta │ │ │ ├── DSDialogueSO.cs │ │ │ └── DSDialogueSO.cs.meta │ ├── Utilities.meta │ └── Utilities │ │ ├── CollectionUtility.cs │ │ ├── CollectionUtility.cs.meta │ │ ├── SerializableDictionary.cs │ │ ├── SerializableDictionary.cs.meta │ │ ├── TextUtility.cs │ │ └── TextUtility.cs.meta ├── Editor Default Resources.meta ├── Editor Default Resources │ ├── DialogueSystem.meta │ └── DialogueSystem │ │ ├── DSGraphViewStyles.uss │ │ ├── DSGraphViewStyles.uss.meta │ │ ├── DSNodeStyles.uss │ │ ├── DSNodeStyles.uss.meta │ │ ├── DSToolbarStyles.uss │ │ ├── DSToolbarStyles.uss.meta │ │ ├── DSVariables.uss │ │ └── DSVariables.uss.meta ├── Editor.meta └── Editor │ ├── DialogueSystem.meta │ └── DialogueSystem │ ├── Data.meta │ ├── Data │ ├── Error.meta │ ├── Error │ │ ├── DSErrorData.cs │ │ ├── DSErrorData.cs.meta │ │ ├── DSGroupErrorData.cs │ │ ├── DSGroupErrorData.cs.meta │ │ ├── DSNodeErrorData.cs │ │ └── DSNodeErrorData.cs.meta │ ├── Save.meta │ └── Save │ │ ├── DSChoiceSaveData.cs │ │ ├── DSChoiceSaveData.cs.meta │ │ ├── DSGraphSaveDataSO.cs │ │ ├── DSGraphSaveDataSO.cs.meta │ │ ├── DSGroupSaveData.cs │ │ ├── DSGroupSaveData.cs.meta │ │ ├── DSNodeSaveData.cs │ │ └── DSNodeSaveData.cs.meta │ ├── Elements.meta │ ├── Elements │ ├── DSGroup.cs │ ├── DSGroup.cs.meta │ ├── DSMultipleChoiceNode.cs │ ├── DSMultipleChoiceNode.cs.meta │ ├── DSNode.cs │ ├── DSNode.cs.meta │ ├── DSSingleChoiceNode.cs │ └── DSSingleChoiceNode.cs.meta │ ├── Inspectors.meta │ ├── Inspectors │ ├── DSInspector.cs │ └── DSInspector.cs.meta │ ├── Utilities.meta │ ├── Utilities │ ├── DSElementUtility.cs │ ├── DSElementUtility.cs.meta │ ├── DSIOUtility.cs │ ├── DSIOUtility.cs.meta │ ├── DSInspectorUtility.cs │ ├── DSInspectorUtility.cs.meta │ ├── DSStyleUtility.cs │ └── DSStyleUtility.cs.meta │ ├── Windows.meta │ └── Windows │ ├── DSEditorWindow.cs │ ├── DSEditorWindow.cs.meta │ ├── DSGraphView.cs │ ├── DSGraphView.cs.meta │ ├── DSSearchWindow.cs │ └── DSSearchWindow.cs.meta ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Uu]ser[Ss]ettings/ 12 | 13 | # MemoryCaptures can get excessive in size. 14 | # They also could contain extremely sensitive data 15 | /[Mm]emoryCaptures/ 16 | 17 | # Asset meta data should only be ignored when the corresponding asset is also ignored 18 | !/[Aa]ssets/**/*.meta 19 | 20 | # Uncomment this line if you wish to ignore the asset store tools plugin 21 | # /[Aa]ssets/AssetStoreTools* 22 | 23 | # Autogenerated Jetbrains Rider plugin 24 | /[Aa]ssets/Plugins/Editor/JetBrains* 25 | 26 | # Visual Studio cache directory 27 | .vs/ 28 | 29 | # Gradle cache directory 30 | .gradle/ 31 | 32 | # Autogenerated VS/MD/Consulo solution and project files 33 | ExportedObj/ 34 | .consulo/ 35 | *.csproj 36 | *.unityproj 37 | *.sln 38 | *.suo 39 | *.tmp 40 | *.user 41 | *.userprefs 42 | *.pidb 43 | *.booproj 44 | *.svd 45 | *.pdb 46 | *.mdb 47 | *.opendb 48 | *.VC.db 49 | 50 | # Unity3D generated meta files 51 | *.pidb.meta 52 | *.pdb.meta 53 | *.mdb.meta 54 | 55 | # Unity3D generated file on crash reports 56 | sysinfo.txt 57 | 58 | # Builds 59 | *.apk 60 | *.aab 61 | *.unitypackage 62 | 63 | # Crashlytics generated file 64 | crashlytics-build.properties 65 | 66 | # Packed Addressables 67 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 68 | 69 | # Temporary auto-generated Android Assets 70 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 71 | /[Aa]ssets/[Ss]treamingAssets/aa/* 72 | 73 | /[Pp]roject[Ss]ettings 74 | /[Aa]ssets/[Ss]cenes* 75 | 76 | *.vsconfig 77 | -------------------------------------------------------------------------------- /Assets/DialogueSystem.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 729251388f9017346b73d74bc6f53f38 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Enumerations.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 58c1a4eeecb1a024bb2cb9fcfe0d62df 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Enumerations/DSDialogueType.cs: -------------------------------------------------------------------------------- 1 | namespace DS.Enumerations 2 | { 3 | public enum DSDialogueType 4 | { 5 | SingleChoice, 6 | MultipleChoice 7 | } 8 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Enumerations/DSDialogueType.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2788553d3ff47084b87b80b86a00c8a7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 31bf3f0a1f8e2ea4aa570d8b3935b26a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/DSDialogue.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace DS 4 | { 5 | using ScriptableObjects; 6 | 7 | public class DSDialogue : MonoBehaviour 8 | { 9 | /* Dialogue Scriptable Objects */ 10 | [SerializeField] private DSDialogueContainerSO dialogueContainer; 11 | [SerializeField] private DSDialogueGroupSO dialogueGroup; 12 | [SerializeField] private DSDialogueSO dialogue; 13 | 14 | /* Filters */ 15 | [SerializeField] private bool groupedDialogues; 16 | [SerializeField] private bool startingDialoguesOnly; 17 | 18 | /* Indexes */ 19 | [SerializeField] private int selectedDialogueGroupIndex; 20 | [SerializeField] private int selectedDialogueIndex; 21 | } 22 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/DSDialogue.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2f70cf55b2086fe4e916735c1b64ee09 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/Data.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8c274ca9af9c64a428f5a68191c14d24 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/Data/DSDialogueChoiceData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace DS.Data 5 | { 6 | using ScriptableObjects; 7 | 8 | [Serializable] 9 | public class DSDialogueChoiceData 10 | { 11 | [field: SerializeField] public string Text { get; set; } 12 | [field: SerializeField] public DSDialogueSO NextDialogue { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/Data/DSDialogueChoiceData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e777183e0b086b54d83ac04d60552930 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/ScriptableObjects.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 935f75669972d0147807f334b5ea893c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/ScriptableObjects/DSDialogueContainerSO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace DS.ScriptableObjects 5 | { 6 | public class DSDialogueContainerSO : ScriptableObject 7 | { 8 | [field: SerializeField] public string FileName { get; set; } 9 | [field: SerializeField] public SerializableDictionary> DialogueGroups { get; set; } 10 | [field: SerializeField] public List UngroupedDialogues { get; set; } 11 | 12 | public void Initialize(string fileName) 13 | { 14 | FileName = fileName; 15 | 16 | DialogueGroups = new SerializableDictionary>(); 17 | UngroupedDialogues = new List(); 18 | } 19 | 20 | public List GetDialogueGroupNames() 21 | { 22 | List dialogueGroupNames = new List(); 23 | 24 | foreach (DSDialogueGroupSO dialogueGroup in DialogueGroups.Keys) 25 | { 26 | dialogueGroupNames.Add(dialogueGroup.GroupName); 27 | } 28 | 29 | return dialogueGroupNames; 30 | } 31 | 32 | public List GetGroupedDialogueNames(DSDialogueGroupSO dialogueGroup, bool startingDialoguesOnly) 33 | { 34 | List groupedDialogues = DialogueGroups[dialogueGroup]; 35 | 36 | List groupedDialogueNames = new List(); 37 | 38 | foreach (DSDialogueSO groupedDialogue in groupedDialogues) 39 | { 40 | if (startingDialoguesOnly && !groupedDialogue.IsStartingDialogue) 41 | { 42 | continue; 43 | } 44 | 45 | groupedDialogueNames.Add(groupedDialogue.DialogueName); 46 | } 47 | 48 | return groupedDialogueNames; 49 | } 50 | 51 | public List GetUngroupedDialogueNames(bool startingDialoguesOnly) 52 | { 53 | List ungroupedDialogueNames = new List(); 54 | 55 | foreach (DSDialogueSO ungroupedDialogue in UngroupedDialogues) 56 | { 57 | if (startingDialoguesOnly && !ungroupedDialogue.IsStartingDialogue) 58 | { 59 | continue; 60 | } 61 | 62 | ungroupedDialogueNames.Add(ungroupedDialogue.DialogueName); 63 | } 64 | 65 | return ungroupedDialogueNames; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/ScriptableObjects/DSDialogueContainerSO.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: caaa76378291a144d849f29230be0a5a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/ScriptableObjects/DSDialogueGroupSO.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace DS.ScriptableObjects 4 | { 5 | public class DSDialogueGroupSO : ScriptableObject 6 | { 7 | [field: SerializeField] public string GroupName { get; set; } 8 | 9 | public void Initialize(string groupName) 10 | { 11 | GroupName = groupName; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/ScriptableObjects/DSDialogueGroupSO.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4553f56b2363b9d4b924dda785b05977 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/ScriptableObjects/DSDialogueSO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace DS.ScriptableObjects 5 | { 6 | using Data; 7 | using Enumerations; 8 | 9 | public class DSDialogueSO : ScriptableObject 10 | { 11 | [field: SerializeField] public string DialogueName { get; set; } 12 | [field: SerializeField] [field: TextArea()] public string Text { get; set; } 13 | [field: SerializeField] public List Choices { get; set; } 14 | [field: SerializeField] public DSDialogueType DialogueType { get; set; } 15 | [field: SerializeField] public bool IsStartingDialogue { get; set; } 16 | 17 | public void Initialize(string dialogueName, string text, List choices, DSDialogueType dialogueType, bool isStartingDialogue) 18 | { 19 | DialogueName = dialogueName; 20 | Text = text; 21 | Choices = choices; 22 | DialogueType = dialogueType; 23 | IsStartingDialogue = isStartingDialogue; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Scripts/ScriptableObjects/DSDialogueSO.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f262818b55b0f4418296bad0176bdab 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Utilities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 78fd3cea49c08ff4486f1e8bcf7d82f8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Utilities/CollectionUtility.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DS.Utilities 4 | { 5 | public static class CollectionUtility 6 | { 7 | public static void AddItem(this SerializableDictionary> serializableDictionary, T key, K value) 8 | { 9 | if (serializableDictionary.ContainsKey(key)) 10 | { 11 | serializableDictionary[key].Add(value); 12 | 13 | return; 14 | } 15 | 16 | serializableDictionary.Add(key, new List() { value }); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Utilities/CollectionUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 055f8607301d8b74a84a4ba2f52654e6 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Utilities/SerializableDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using UnityEngine; 6 | using UnityEngine.Serialization; 7 | 8 | public class SerializableDictionary 9 | { 10 | } 11 | 12 | [Serializable] 13 | public class SerializableDictionary : SerializableDictionary, IDictionary, ISerializationCallbackReceiver 14 | { 15 | [SerializeField] 16 | private List list = new List(); 17 | 18 | [Serializable] 19 | public struct SerializableKeyValuePair 20 | { 21 | public TKey Key; 22 | public TValue Value; 23 | 24 | public SerializableKeyValuePair(TKey key, TValue value) 25 | { 26 | Key = key; 27 | Value = value; 28 | } 29 | 30 | public void SetValue(TValue value) 31 | { 32 | Value = value; 33 | } 34 | } 35 | 36 | private Dictionary KeyPositions => _keyPositions.Value; 37 | private Lazy> _keyPositions; 38 | 39 | public SerializableDictionary() 40 | { 41 | _keyPositions = new Lazy>(MakeKeyPositions); 42 | } 43 | 44 | public SerializableDictionary(IDictionary dictionary) 45 | { 46 | _keyPositions = new Lazy>(MakeKeyPositions); 47 | 48 | if (dictionary == null) 49 | { 50 | throw new ArgumentException("The passed dictionary is null."); 51 | } 52 | 53 | foreach (KeyValuePair pair in dictionary) 54 | { 55 | Add(pair.Key, pair.Value); 56 | } 57 | } 58 | 59 | private Dictionary MakeKeyPositions() 60 | { 61 | int numEntries = list.Count; 62 | 63 | Dictionary result = new Dictionary(numEntries); 64 | 65 | for (int i = 0; i < numEntries; ++i) 66 | { 67 | result[list[i].Key] = (uint) i; 68 | } 69 | 70 | return result; 71 | } 72 | 73 | public void OnBeforeSerialize() 74 | { 75 | } 76 | 77 | public void OnAfterDeserialize() 78 | { 79 | // After deserialization, the key positions might be changed 80 | _keyPositions = new Lazy>(MakeKeyPositions); 81 | } 82 | 83 | #region IDictionary 84 | public TValue this[TKey key] 85 | { 86 | get => list[(int) KeyPositions[key]].Value; 87 | set 88 | { 89 | if (KeyPositions.TryGetValue(key, out uint index)) 90 | { 91 | list[(int) index].SetValue(value); 92 | } 93 | else 94 | { 95 | KeyPositions[key] = (uint) list.Count; 96 | 97 | list.Add(new SerializableKeyValuePair(key, value)); 98 | } 99 | } 100 | } 101 | 102 | public ICollection Keys => list.Select(tuple => tuple.Key).ToArray(); 103 | public ICollection Values => list.Select(tuple => tuple.Value).ToArray(); 104 | 105 | public void Add(TKey key, TValue value) 106 | { 107 | if (KeyPositions.ContainsKey(key)) 108 | { 109 | throw new ArgumentException("An element with the same key already exists in the dictionary."); 110 | } 111 | else 112 | { 113 | KeyPositions[key] = (uint) list.Count; 114 | 115 | list.Add(new SerializableKeyValuePair(key, value)); 116 | } 117 | } 118 | 119 | public bool ContainsKey(TKey key) => KeyPositions.ContainsKey(key); 120 | 121 | public bool Remove(TKey key) 122 | { 123 | if (KeyPositions.TryGetValue(key, out uint index)) 124 | { 125 | Dictionary kp = KeyPositions; 126 | 127 | kp.Remove(key); 128 | 129 | list.RemoveAt((int) index); 130 | 131 | int numEntries = list.Count; 132 | 133 | for (uint i = index; i < numEntries; i++) 134 | { 135 | kp[list[(int) i].Key] = i; 136 | } 137 | 138 | return true; 139 | } 140 | 141 | return false; 142 | } 143 | 144 | public bool TryGetValue(TKey key, out TValue value) 145 | { 146 | if (KeyPositions.TryGetValue(key, out uint index)) 147 | { 148 | value = list[(int) index].Value; 149 | 150 | return true; 151 | } 152 | 153 | value = default; 154 | 155 | return false; 156 | } 157 | #endregion 158 | 159 | #region ICollection 160 | public int Count => list.Count; 161 | public bool IsReadOnly => false; 162 | 163 | public void Add(KeyValuePair kvp) => Add(kvp.Key, kvp.Value); 164 | 165 | public void Clear() 166 | { 167 | list.Clear(); 168 | KeyPositions.Clear(); 169 | } 170 | 171 | public bool Contains(KeyValuePair kvp) => KeyPositions.ContainsKey(kvp.Key); 172 | 173 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 174 | { 175 | int numKeys = list.Count; 176 | 177 | if (array.Length - arrayIndex < numKeys) 178 | { 179 | throw new ArgumentException("arrayIndex"); 180 | } 181 | 182 | for (int i = 0; i < numKeys; ++i, ++arrayIndex) 183 | { 184 | SerializableKeyValuePair entry = list[i]; 185 | 186 | array[arrayIndex] = new KeyValuePair(entry.Key, entry.Value); 187 | } 188 | } 189 | 190 | public bool Remove(KeyValuePair kvp) => Remove(kvp.Key); 191 | #endregion 192 | 193 | #region IEnumerable 194 | public IEnumerator> GetEnumerator() 195 | { 196 | return list.Select(ToKeyValuePair).GetEnumerator(); 197 | 198 | KeyValuePair ToKeyValuePair(SerializableKeyValuePair skvp) 199 | { 200 | return new KeyValuePair(skvp.Key, skvp.Value); 201 | } 202 | } 203 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 204 | #endregion 205 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Utilities/SerializableDictionary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d33b6b33f70b9247b2418f79c7dc514 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/DialogueSystem/Utilities/TextUtility.cs: -------------------------------------------------------------------------------- 1 | public static class TextUtility 2 | { 3 | public static bool IsWhitespace(this char character) 4 | { 5 | switch (character) 6 | { 7 | case '\u0020': 8 | case '\u00A0': 9 | case '\u1680': 10 | case '\u2000': 11 | case '\u2001': 12 | case '\u2002': 13 | case '\u2003': 14 | case '\u2004': 15 | case '\u2005': 16 | case '\u2006': 17 | case '\u2007': 18 | case '\u2008': 19 | case '\u2009': 20 | case '\u200A': 21 | case '\u202F': 22 | case '\u205F': 23 | case '\u3000': 24 | case '\u2028': 25 | case '\u2029': 26 | case '\u0009': 27 | case '\u000A': 28 | case '\u000B': 29 | case '\u000C': 30 | case '\u000D': 31 | case '\u0085': 32 | { 33 | return true; 34 | } 35 | 36 | default: 37 | { 38 | return false; 39 | } 40 | } 41 | } 42 | 43 | // While unnecessary for this project, I've used the method seen here: https://stackoverflow.com/a/37368176 44 | // Benchmarks: https://stackoverflow.com/a/37347881 45 | public static string RemoveWhitespaces(this string text) 46 | { 47 | int textLength = text.Length; 48 | 49 | char[] textCharacters = text.ToCharArray(); 50 | 51 | int currentWhitespacelessTextLength = 0; 52 | 53 | for (int currentCharacterIndex = 0; currentCharacterIndex < textLength; ++currentCharacterIndex) 54 | { 55 | char currentTextCharacter = textCharacters[currentCharacterIndex]; 56 | 57 | if (currentTextCharacter.IsWhitespace()) 58 | { 59 | continue; 60 | } 61 | 62 | textCharacters[currentWhitespacelessTextLength++] = currentTextCharacter; 63 | } 64 | 65 | return new string(textCharacters, 0, currentWhitespacelessTextLength); 66 | } 67 | 68 | // See here for alternatives: https://stackoverflow.com/questions/3210393/how-do-i-remove-all-non-alphanumeric-characters-from-a-string-except-dash 69 | public static string RemoveSpecialCharacters(this string text) 70 | { 71 | int textLength = text.Length; 72 | 73 | char[] textCharacters = text.ToCharArray(); 74 | 75 | int currentWhitespacelessTextLength = 0; 76 | 77 | for (int currentCharacterIndex = 0; currentCharacterIndex < textLength; ++currentCharacterIndex) 78 | { 79 | char currentTextCharacter = textCharacters[currentCharacterIndex]; 80 | 81 | if (!char.IsLetterOrDigit(currentTextCharacter) && !currentTextCharacter.IsWhitespace()) 82 | { 83 | continue; 84 | } 85 | 86 | textCharacters[currentWhitespacelessTextLength++] = currentTextCharacter; 87 | } 88 | 89 | return new string(textCharacters, 0, currentWhitespacelessTextLength); 90 | } 91 | } -------------------------------------------------------------------------------- /Assets/DialogueSystem/Utilities/TextUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8464c806e8d3fb34fac44c4f3f0b0cf0 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor Default Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 70ef7c4fda037e846a62bedcceb7bdf1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c24c0b5f364f7bf46919048e7f9c817c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSGraphViewStyles.uss: -------------------------------------------------------------------------------- 1 | GridBackground 2 | { 3 | --grid-background-color: var(--ds-colors-grid-light-grey-1); 4 | --line-color: var(--ds-colors-grid-light-grey-2); 5 | --thick-line-color: var(--ds-colors-grid-light-grey-3); 6 | --spacing: var(--ds-metrics-grid-25); 7 | } -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSGraphViewStyles.uss.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bff5a0bb5a91ae24dabcd5e1dad28e1f 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} 11 | disableValidation: 0 12 | -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSNodeStyles.uss: -------------------------------------------------------------------------------- 1 | /* CONTAINERS */ 2 | 3 | .ds-node__extension-container 4 | { 5 | background-color: var(--ds-colors-window-dark-grey-1); 6 | padding: var(--ds-metrics-window-pixels-8); 7 | } 8 | 9 | .ds-node__main-container 10 | { 11 | background-color: var(--ds-colors-window-dark-grey-1); 12 | } 13 | 14 | .ds-node__custom-data-container 15 | { 16 | background-color: var(--ds-colors-window-dark-grey-3); 17 | border-radius: var(--ds-metrics-window-pixels-4); 18 | padding: var(--ds-metrics-window-pixels-8); 19 | } 20 | 21 | /* INPUTS */ 22 | 23 | .ds-node__text-field > .unity-text-field__input 24 | { 25 | background-color: var(--ds-colors-window-dark-grey-1); 26 | border-color: var(--ds-colors-window-dark-grey-6); 27 | border-radius: var(--ds-metrics-window-pixels-4); 28 | max-width: var(--ds-metrics-window-pixels-240); 29 | min-width: var(--ds-metrics-window-pixels-160); 30 | padding: var(--ds-metrics-window-pixels-8); 31 | white-space: normal; 32 | } 33 | 34 | .ds-node__text-field__hidden > .unity-text-field__input 35 | { 36 | background-color: transparent; 37 | border-color: transparent; 38 | } 39 | 40 | .ds-node__text-field__hidden > .unity-text-field__input:focus 41 | { 42 | background-color: var(--ds-colors-window-dark-grey-1); 43 | border-color: var(--ds-colors-window-dark-grey-6); 44 | } 45 | 46 | .ds-node__filename-text-field > .unity-text-field__input 47 | { 48 | max-width: var(--ds-metrics-window-pixels-1000); 49 | margin: var(--ds-metrics-window-pixels-4); 50 | padding: var(--ds-metrics-window-pixels-4); 51 | } 52 | 53 | .ds-node__choice-text-field > .unity-text-field__input 54 | { 55 | font-size: var(--ds-metrics-window-pixels-11); 56 | height: var(--ds-metrics-window-pixels-35); 57 | max-width: var(--ds-metrics-window-pixels-1000); 58 | min-width: var(--ds-metrics-window-pixels-0); 59 | } 60 | 61 | .ds-node__choice-text-field > .unity-text-field__input:focus 62 | { 63 | height: var(--ds-metrics-window-pixels-25); 64 | margin: var(--ds-metrics-window-pixels-4); 65 | padding: var(--ds-metrics-window-pixels-4); 66 | } 67 | 68 | .ds-node__quote-text-field > .unity-text-field__input 69 | { 70 | background-color: var(--ds-colors-window-dark-grey-4); 71 | border-color: transparent; 72 | border-left-color: var(--ds-colors-window-dark-grey-6); 73 | border-left-width: var(--ds-metrics-window-pixels-4); 74 | padding: var(--ds-metrics-window-pixels-16); 75 | max-width: var(--ds-metrics-window-pixels-600); 76 | } 77 | 78 | .ds-node__quote-text-field > .unity-text-field__input:hover 79 | { 80 | background-color: var(--ds-colors-window-dark-grey-5); 81 | } 82 | 83 | .ds-node__quote-text-field > .unity-text-field__input:focus 84 | { 85 | background-color: var(--ds-colors-window-dark-grey-1); 86 | border-color: var(--ds-colors-window-dark-grey-6); 87 | } 88 | 89 | /* BUTTONS */ 90 | 91 | .ds-node__button 92 | { 93 | background-color: var(--ds-colors-window-dark-grey-5); 94 | border-color: var(--ds-colors-window-dark-grey-1); 95 | margin: var(--ds-metrics-window-pixels-4); 96 | max-height: var(--ds-metrics-window-pixels-25); 97 | } 98 | 99 | .ds-node__button:hover 100 | { 101 | background-color: var(--ds-colors-window-dark-grey-2); 102 | } 103 | 104 | .ds-node__button:active 105 | { 106 | background-color: var(--ds-colors-window-dark-grey-3); 107 | color: var(--ds-colors-window-yellow); 108 | } 109 | 110 | /* ELEMENTS */ 111 | 112 | TextField 113 | { 114 | margin-left: var(--ds-metrics-window-pixels-0); 115 | } 116 | 117 | .ds-node__custom-data-container > Foldout 118 | { 119 | margin-left: var(--ds-metrics-window-pixels-n4); 120 | margin-top: var(--ds-metrics-window-pixels-4); 121 | } 122 | 123 | .ds-node__custom-data-container > Foldout > TextField 124 | { 125 | margin-left: var(--ds-metrics-window-pixels-n12); 126 | margin-top: var(--ds-metrics-window-pixels-8); 127 | } 128 | 129 | /* BUILT IN */ 130 | 131 | .unity-toggle__text 132 | { 133 | margin-left: var(--ds-metrics-window-pixels-2); 134 | } -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSNodeStyles.uss.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfc25a3d68773dc48880eb330b8dfa1d 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} 11 | disableValidation: 0 12 | -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSToolbarStyles.uss: -------------------------------------------------------------------------------- 1 | /* VISUAL ELEMENTS */ 2 | 3 | Toolbar 4 | { 5 | background-color: var(--ds-colors-window-dark-grey-1); 6 | height: var(--ds-metrics-window-pixels-35); 7 | } 8 | 9 | TextField 10 | { 11 | padding: var(--ds-metrics-window-pixels-4); 12 | } 13 | 14 | 15 | Button 16 | { 17 | background-color: var(--ds-colors-window-dark-grey-5); 18 | border-color: var(--ds-colors-window-dark-grey-1); 19 | margin: var(--ds-metrics-window-pixels-4); 20 | max-height: var(--ds-metrics-window-pixels-25); 21 | } 22 | 23 | Button:hover 24 | { 25 | background-color: var(--ds-colors-window-dark-grey-2); 26 | } 27 | 28 | Button:active 29 | { 30 | background-color: var(--ds-colors-window-dark-grey-3); 31 | color: var(--ds-colors-window-yellow); 32 | } 33 | 34 | /* BUILT-IN */ 35 | 36 | .unity-text-field__label 37 | { 38 | padding: var(--ds-metrics-window-pixels-4); 39 | } 40 | 41 | TextField:focus > .unity-text-field__label 42 | { 43 | color: var(--ds-colors-window-yellow); 44 | } 45 | 46 | .unity-text-field__input 47 | { 48 | background-color: var(--ds-colors-window-dark-grey-1); 49 | border-color: var(--ds-colors-window-dark-grey-6); 50 | border-radius: var(--ds-metrics-window-pixels-4); 51 | max-height: var(--ds-metrics-window-pixels-25); 52 | min-width: var(--ds-metrics-window-pixels-120); 53 | padding-left: var(--ds-metrics-window-pixels-6); 54 | } 55 | 56 | .unity-text-field__input:focus 57 | { 58 | color: var(--ds-colors-window-yellow); 59 | } 60 | 61 | .ds-toolbar__button__selected 62 | { 63 | color: var(--ds-colors-window-yellow); 64 | } -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSToolbarStyles.uss.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 39a6bbf5a88f1b342b23319a464fbcd5 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} 11 | disableValidation: 0 12 | -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSVariables.uss: -------------------------------------------------------------------------------- 1 | :root 2 | { 3 | --ds-colors-grid-light-grey-1: #2b2b2b; 4 | --ds-colors-grid-light-grey-2: rgba(51, 51, 51, 0.4); 5 | --ds-colors-grid-light-grey-3: rgba(51, 51, 51, 1); 6 | --ds-colors-window-dark-grey-1: #1D1D1E; 7 | --ds-colors-window-dark-grey-2: #2B2D2F; 8 | --ds-colors-window-dark-grey-3: #212325; 9 | --ds-colors-window-dark-grey-4: #232527; 10 | --ds-colors-window-dark-grey-5: #252729; 11 | --ds-colors-window-dark-grey-6: #27292C; 12 | --ds-colors-window-yellow: #FDD057; 13 | 14 | --ds-metrics-grid-25: 25; 15 | --ds-metrics-window-pixels-n12: -12px; 16 | --ds-metrics-window-pixels-n4: -4px; 17 | --ds-metrics-window-pixels-0: 0px; 18 | --ds-metrics-window-pixels-2: 2px; 19 | --ds-metrics-window-pixels-4: 4px; 20 | --ds-metrics-window-pixels-6: 6px; 21 | --ds-metrics-window-pixels-8: 8px; 22 | --ds-metrics-window-pixels-11: 11px; 23 | --ds-metrics-window-pixels-16: 16px; 24 | --ds-metrics-window-pixels-25: 25px; 25 | --ds-metrics-window-pixels-35: 35px; 26 | --ds-metrics-window-pixels-120: 120px; 27 | --ds-metrics-window-pixels-160: 160px; 28 | --ds-metrics-window-pixels-240: 240px; 29 | --ds-metrics-window-pixels-600: 600px; 30 | --ds-metrics-window-pixels-1000: 1000px; 31 | } -------------------------------------------------------------------------------- /Assets/Editor Default Resources/DialogueSystem/DSVariables.uss.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5f6f7e29da1cbc848a9d81925e254249 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} 11 | disableValidation: 0 12 | -------------------------------------------------------------------------------- /Assets/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: efe9b8dfb75b045409c2fdfb3003bf5a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4fe3ac67d2817fd40a520225f5f2e278 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b3153d385d702c14aba1126a0331a946 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Error.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5bff0a274a4ee7a48a234c7d7e1f48a4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Error/DSErrorData.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace DS.Data.Error 4 | { 5 | public class DSErrorData 6 | { 7 | public Color Color { get; set; } 8 | 9 | public DSErrorData() 10 | { 11 | GenerateRandomColor(); 12 | } 13 | 14 | private void GenerateRandomColor() 15 | { 16 | Color = new Color32( 17 | (byte) Random.Range(65, 256), 18 | (byte) Random.Range(50, 176), 19 | (byte) Random.Range(50, 176), 20 | 255 21 | ); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Error/DSErrorData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6810ff6f708130741a13ab1144a0eb7b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Error/DSGroupErrorData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DS.Data.Error 4 | { 5 | using Elements; 6 | 7 | public class DSGroupErrorData 8 | { 9 | public DSErrorData ErrorData { get; set; } 10 | public List Groups { get; set; } 11 | 12 | public DSGroupErrorData() 13 | { 14 | ErrorData = new DSErrorData(); 15 | Groups = new List(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Error/DSGroupErrorData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1a02d7d895c18e543828cc58efb4e1ba 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Error/DSNodeErrorData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DS.Data.Error 4 | { 5 | using Elements; 6 | 7 | public class DSNodeErrorData 8 | { 9 | public DSErrorData ErrorData { get; set; } 10 | public List Nodes { get; set; } 11 | 12 | public DSNodeErrorData() 13 | { 14 | ErrorData = new DSErrorData(); 15 | Nodes = new List(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Error/DSNodeErrorData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ae412fd45b413694a83085042bd8935f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf611275e401b0447b187227441bea6e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSChoiceSaveData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace DS.Data.Save 5 | { 6 | [Serializable] 7 | public class DSChoiceSaveData 8 | { 9 | [field: SerializeField] public string Text { get; set; } 10 | [field: SerializeField] public string NodeID { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSChoiceSaveData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19ed1936b23e1e74f9a7ff6f247cfb1d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSGraphSaveDataSO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace DS.Data.Save 5 | { 6 | public class DSGraphSaveDataSO : ScriptableObject 7 | { 8 | [field: SerializeField] public string FileName { get; set; } 9 | [field: SerializeField] public List Groups { get; set; } 10 | [field: SerializeField] public List Nodes { get; set; } 11 | [field: SerializeField] public List OldGroupNames { get; set; } 12 | [field: SerializeField] public List OldUngroupedNodeNames { get; set; } 13 | [field: SerializeField] public SerializableDictionary> OldGroupedNodeNames { get; set; } 14 | 15 | public void Initialize(string fileName) 16 | { 17 | FileName = fileName; 18 | 19 | Groups = new List(); 20 | Nodes = new List(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSGraphSaveDataSO.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0202a3095b7477a4091d56344f0af6b3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSGroupSaveData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace DS.Data.Save 5 | { 6 | [Serializable] 7 | public class DSGroupSaveData 8 | { 9 | [field: SerializeField] public string ID { get; set; } 10 | [field: SerializeField] public string Name { get; set; } 11 | [field: SerializeField] public Vector2 Position { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSGroupSaveData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 08b039bf44eccd94d918dc61b99b9109 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSNodeSaveData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | 5 | namespace DS.Data.Save 6 | { 7 | using Enumerations; 8 | 9 | [Serializable] 10 | public class DSNodeSaveData 11 | { 12 | [field: SerializeField] public string ID { get; set; } 13 | [field: SerializeField] public string Name { get; set; } 14 | [field: SerializeField] public string Text { get; set; } 15 | [field: SerializeField] public List Choices { get; set; } 16 | [field: SerializeField] public string GroupID { get; set; } 17 | [field: SerializeField] public DSDialogueType DialogueType { get; set; } 18 | [field: SerializeField] public Vector2 Position { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Data/Save/DSNodeSaveData.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f30466f94e56b16439f6edc9b9c67bc9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 754970360e4ea9c47a3cd2b50367cb1a 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSGroup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor.Experimental.GraphView; 3 | using UnityEngine; 4 | 5 | namespace DS.Elements 6 | { 7 | public class DSGroup : Group 8 | { 9 | public string ID { get; set; } 10 | public string OldTitle { get; set; } 11 | 12 | private Color defaultBorderColor; 13 | private float defaultBorderWidth; 14 | 15 | public DSGroup(string groupTitle, Vector2 position) 16 | { 17 | ID = Guid.NewGuid().ToString(); 18 | 19 | title = groupTitle; 20 | OldTitle = groupTitle; 21 | 22 | SetPosition(new Rect(position, Vector2.zero)); 23 | 24 | defaultBorderColor = contentContainer.style.borderBottomColor.value; 25 | defaultBorderWidth = contentContainer.style.borderBottomWidth.value; 26 | } 27 | 28 | public void SetErrorStyle(Color color) 29 | { 30 | contentContainer.style.borderBottomColor = color; 31 | contentContainer.style.borderBottomWidth = 2f; 32 | } 33 | 34 | public void ResetStyle() 35 | { 36 | contentContainer.style.borderBottomColor = defaultBorderColor; 37 | contentContainer.style.borderBottomWidth = defaultBorderWidth; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0455a1311a558c34188d3901fec96156 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSMultipleChoiceNode.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor.Experimental.GraphView; 2 | using UnityEngine; 3 | using UnityEngine.UIElements; 4 | 5 | namespace DS.Elements 6 | { 7 | using Data.Save; 8 | using Enumerations; 9 | using Utilities; 10 | using Windows; 11 | 12 | public class DSMultipleChoiceNode : DSNode 13 | { 14 | public override void Initialize(string nodeName, DSGraphView dsGraphView, Vector2 position) 15 | { 16 | base.Initialize(nodeName, dsGraphView, position); 17 | 18 | DialogueType = DSDialogueType.MultipleChoice; 19 | 20 | DSChoiceSaveData choiceData = new DSChoiceSaveData() 21 | { 22 | Text = "New Choice" 23 | }; 24 | 25 | Choices.Add(choiceData); 26 | } 27 | 28 | public override void Draw() 29 | { 30 | base.Draw(); 31 | 32 | /* MAIN CONTAINER */ 33 | 34 | Button addChoiceButton = DSElementUtility.CreateButton("Add Choice", () => 35 | { 36 | DSChoiceSaveData choiceData = new DSChoiceSaveData() 37 | { 38 | Text = "New Choice" 39 | }; 40 | 41 | Choices.Add(choiceData); 42 | 43 | Port choicePort = CreateChoicePort(choiceData); 44 | 45 | outputContainer.Add(choicePort); 46 | }); 47 | 48 | addChoiceButton.AddToClassList("ds-node__button"); 49 | 50 | mainContainer.Insert(1, addChoiceButton); 51 | 52 | /* OUTPUT CONTAINER */ 53 | 54 | foreach (DSChoiceSaveData choice in Choices) 55 | { 56 | Port choicePort = CreateChoicePort(choice); 57 | 58 | outputContainer.Add(choicePort); 59 | } 60 | 61 | RefreshExpandedState(); 62 | } 63 | 64 | private Port CreateChoicePort(object userData) 65 | { 66 | Port choicePort = this.CreatePort(); 67 | 68 | choicePort.userData = userData; 69 | 70 | DSChoiceSaveData choiceData = (DSChoiceSaveData) userData; 71 | 72 | Button deleteChoiceButton = DSElementUtility.CreateButton("X", () => 73 | { 74 | if (Choices.Count == 1) 75 | { 76 | return; 77 | } 78 | 79 | if (choicePort.connected) 80 | { 81 | graphView.DeleteElements(choicePort.connections); 82 | } 83 | 84 | Choices.Remove(choiceData); 85 | 86 | graphView.RemoveElement(choicePort); 87 | }); 88 | 89 | deleteChoiceButton.AddToClassList("ds-node__button"); 90 | 91 | TextField choiceTextField = DSElementUtility.CreateTextField(choiceData.Text, null, callback => 92 | { 93 | choiceData.Text = callback.newValue; 94 | }); 95 | 96 | choiceTextField.AddClasses( 97 | "ds-node__text-field", 98 | "ds-node__text-field__hidden", 99 | "ds-node__choice-text-field" 100 | ); 101 | 102 | choicePort.Add(choiceTextField); 103 | choicePort.Add(deleteChoiceButton); 104 | 105 | return choicePort; 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSMultipleChoiceNode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1476f49f4fe60544c88da684351c65cd 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEditor.Experimental.GraphView; 5 | using UnityEngine; 6 | using UnityEngine.UIElements; 7 | 8 | namespace DS.Elements 9 | { 10 | using Data.Save; 11 | using Enumerations; 12 | using Utilities; 13 | using Windows; 14 | 15 | public class DSNode : Node 16 | { 17 | public string ID { get; set; } 18 | public string DialogueName { get; set; } 19 | public List Choices { get; set; } 20 | public string Text { get; set; } 21 | public DSDialogueType DialogueType { get; set; } 22 | public DSGroup Group { get; set; } 23 | 24 | protected DSGraphView graphView; 25 | private Color defaultBackgroundColor; 26 | 27 | public override void BuildContextualMenu(ContextualMenuPopulateEvent evt) 28 | { 29 | evt.menu.AppendAction("Disconnect Input Ports", actionEvent => DisconnectInputPorts()); 30 | evt.menu.AppendAction("Disconnect Output Ports", actionEvent => DisconnectOutputPorts()); 31 | 32 | base.BuildContextualMenu(evt); 33 | } 34 | 35 | public virtual void Initialize(string nodeName, DSGraphView dsGraphView, Vector2 position) 36 | { 37 | ID = Guid.NewGuid().ToString(); 38 | 39 | DialogueName = nodeName; 40 | Choices = new List(); 41 | Text = "Dialogue text."; 42 | 43 | SetPosition(new Rect(position, Vector2.zero)); 44 | 45 | graphView = dsGraphView; 46 | defaultBackgroundColor = new Color(29f / 255f, 29f / 255f, 30f / 255f); 47 | 48 | mainContainer.AddToClassList("ds-node__main-container"); 49 | extensionContainer.AddToClassList("ds-node__extension-container"); 50 | } 51 | 52 | public virtual void Draw() 53 | { 54 | /* TITLE CONTAINER */ 55 | 56 | TextField dialogueNameTextField = DSElementUtility.CreateTextField(DialogueName, null, callback => 57 | { 58 | TextField target = (TextField) callback.target; 59 | 60 | target.value = callback.newValue.RemoveWhitespaces().RemoveSpecialCharacters(); 61 | 62 | if (string.IsNullOrEmpty(target.value)) 63 | { 64 | if (!string.IsNullOrEmpty(DialogueName)) 65 | { 66 | ++graphView.NameErrorsAmount; 67 | } 68 | } 69 | else 70 | { 71 | if (string.IsNullOrEmpty(DialogueName)) 72 | { 73 | --graphView.NameErrorsAmount; 74 | } 75 | } 76 | 77 | if (Group == null) 78 | { 79 | graphView.RemoveUngroupedNode(this); 80 | 81 | DialogueName = target.value; 82 | 83 | graphView.AddUngroupedNode(this); 84 | 85 | return; 86 | } 87 | 88 | DSGroup currentGroup = Group; 89 | 90 | graphView.RemoveGroupedNode(this, Group); 91 | 92 | DialogueName = target.value; 93 | 94 | graphView.AddGroupedNode(this, currentGroup); 95 | }); 96 | 97 | dialogueNameTextField.AddClasses( 98 | "ds-node__text-field", 99 | "ds-node__text-field__hidden", 100 | "ds-node__filename-text-field" 101 | ); 102 | 103 | titleContainer.Insert(0, dialogueNameTextField); 104 | 105 | /* INPUT CONTAINER */ 106 | 107 | Port inputPort = this.CreatePort("Dialogue Connection", Orientation.Horizontal, Direction.Input, Port.Capacity.Multi); 108 | 109 | inputContainer.Add(inputPort); 110 | 111 | /* EXTENSION CONTAINER */ 112 | 113 | VisualElement customDataContainer = new VisualElement(); 114 | 115 | customDataContainer.AddToClassList("ds-node__custom-data-container"); 116 | 117 | Foldout textFoldout = DSElementUtility.CreateFoldout("Dialogue Text"); 118 | 119 | TextField textTextField = DSElementUtility.CreateTextArea(Text, null, callback => Text = callback.newValue); 120 | 121 | textTextField.AddClasses( 122 | "ds-node__text-field", 123 | "ds-node__quote-text-field" 124 | ); 125 | 126 | textFoldout.Add(textTextField); 127 | 128 | customDataContainer.Add(textFoldout); 129 | 130 | extensionContainer.Add(customDataContainer); 131 | } 132 | 133 | public void DisconnectAllPorts() 134 | { 135 | DisconnectInputPorts(); 136 | DisconnectOutputPorts(); 137 | } 138 | 139 | private void DisconnectInputPorts() 140 | { 141 | DisconnectPorts(inputContainer); 142 | } 143 | 144 | private void DisconnectOutputPorts() 145 | { 146 | DisconnectPorts(outputContainer); 147 | } 148 | 149 | private void DisconnectPorts(VisualElement container) 150 | { 151 | foreach (Port port in container.Children()) 152 | { 153 | if (!port.connected) 154 | { 155 | continue; 156 | } 157 | 158 | graphView.DeleteElements(port.connections); 159 | } 160 | } 161 | 162 | public bool IsStartingNode() 163 | { 164 | Port inputPort = (Port) inputContainer.Children().First(); 165 | 166 | return !inputPort.connected; 167 | } 168 | 169 | public void SetErrorStyle(Color color) 170 | { 171 | mainContainer.style.backgroundColor = color; 172 | } 173 | 174 | public void ResetStyle() 175 | { 176 | mainContainer.style.backgroundColor = defaultBackgroundColor; 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSNode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bcac8e45ce96665439106116f8c84497 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSSingleChoiceNode.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor.Experimental.GraphView; 2 | using UnityEngine; 3 | 4 | namespace DS.Elements 5 | { 6 | using Data.Save; 7 | using Enumerations; 8 | using Utilities; 9 | using Windows; 10 | 11 | public class DSSingleChoiceNode : DSNode 12 | { 13 | public override void Initialize(string nodeName, DSGraphView dsGraphView, Vector2 position) 14 | { 15 | base.Initialize(nodeName, dsGraphView, position); 16 | 17 | DialogueType = DSDialogueType.SingleChoice; 18 | 19 | DSChoiceSaveData choiceData = new DSChoiceSaveData() 20 | { 21 | Text = "Next Dialogue" 22 | }; 23 | 24 | Choices.Add(choiceData); 25 | } 26 | 27 | public override void Draw() 28 | { 29 | base.Draw(); 30 | 31 | /* OUTPUT CONTAINER */ 32 | 33 | foreach (DSChoiceSaveData choice in Choices) 34 | { 35 | Port choicePort = this.CreatePort(choice.Text); 36 | 37 | choicePort.userData = choice; 38 | 39 | outputContainer.Add(choicePort); 40 | } 41 | 42 | RefreshExpandedState(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Elements/DSSingleChoiceNode.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b00006d8072719d4ea14e0eae22acb8b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Inspectors.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 36c7fff8c0299e04c87a19e29672b8ae 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Inspectors/DSInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor; 3 | 4 | namespace DS.Inspectors 5 | { 6 | using Utilities; 7 | using ScriptableObjects; 8 | 9 | [CustomEditor(typeof(DSDialogue))] 10 | public class DSInspector : Editor 11 | { 12 | /* Dialogue Scriptable Objects */ 13 | private SerializedProperty dialogueContainerProperty; 14 | private SerializedProperty dialogueGroupProperty; 15 | private SerializedProperty dialogueProperty; 16 | 17 | /* Filters */ 18 | private SerializedProperty groupedDialoguesProperty; 19 | private SerializedProperty startingDialoguesOnlyProperty; 20 | 21 | /* Indexes */ 22 | private SerializedProperty selectedDialogueGroupIndexProperty; 23 | private SerializedProperty selectedDialogueIndexProperty; 24 | 25 | private void OnEnable() 26 | { 27 | dialogueContainerProperty = serializedObject.FindProperty("dialogueContainer"); 28 | dialogueGroupProperty = serializedObject.FindProperty("dialogueGroup"); 29 | dialogueProperty = serializedObject.FindProperty("dialogue"); 30 | 31 | groupedDialoguesProperty = serializedObject.FindProperty("groupedDialogues"); 32 | startingDialoguesOnlyProperty = serializedObject.FindProperty("startingDialoguesOnly"); 33 | 34 | selectedDialogueGroupIndexProperty = serializedObject.FindProperty("selectedDialogueGroupIndex"); 35 | selectedDialogueIndexProperty = serializedObject.FindProperty("selectedDialogueIndex"); 36 | } 37 | 38 | public override void OnInspectorGUI() 39 | { 40 | serializedObject.Update(); 41 | 42 | DrawDialogueContainerArea(); 43 | 44 | DSDialogueContainerSO currentDialogueContainer = (DSDialogueContainerSO) dialogueContainerProperty.objectReferenceValue; 45 | 46 | if (currentDialogueContainer == null) 47 | { 48 | StopDrawing("Select a Dialogue Container to see the rest of the Inspector."); 49 | 50 | return; 51 | } 52 | 53 | DrawFiltersArea(); 54 | 55 | bool currentGroupedDialoguesFilter = groupedDialoguesProperty.boolValue; 56 | bool currentStartingDialoguesOnlyFilter = startingDialoguesOnlyProperty.boolValue; 57 | 58 | List dialogueNames; 59 | 60 | string dialogueFolderPath = $"Assets/DialogueSystem/Dialogues/{currentDialogueContainer.FileName}"; 61 | 62 | string dialogueInfoMessage; 63 | 64 | if (currentGroupedDialoguesFilter) 65 | { 66 | List dialogueGroupNames = currentDialogueContainer.GetDialogueGroupNames(); 67 | 68 | if (dialogueGroupNames.Count == 0) 69 | { 70 | StopDrawing("There are no Dialogue Groups in this Dialogue Container."); 71 | 72 | return; 73 | } 74 | 75 | DrawDialogueGroupArea(currentDialogueContainer, dialogueGroupNames); 76 | 77 | DSDialogueGroupSO dialogueGroup = (DSDialogueGroupSO) dialogueGroupProperty.objectReferenceValue; 78 | 79 | dialogueNames = currentDialogueContainer.GetGroupedDialogueNames(dialogueGroup, currentStartingDialoguesOnlyFilter); 80 | 81 | dialogueFolderPath += $"/Groups/{dialogueGroup.GroupName}/Dialogues"; 82 | 83 | dialogueInfoMessage = "There are no" + (currentStartingDialoguesOnlyFilter ? " Starting" : "") + " Dialogues in this Dialogue Group."; 84 | } 85 | else 86 | { 87 | dialogueNames = currentDialogueContainer.GetUngroupedDialogueNames(currentStartingDialoguesOnlyFilter); 88 | 89 | dialogueFolderPath += "/Global/Dialogues"; 90 | 91 | dialogueInfoMessage = "There are no" + (currentStartingDialoguesOnlyFilter ? " Starting" : "") + " Ungrouped Dialogues in this Dialogue Container."; 92 | } 93 | 94 | if (dialogueNames.Count == 0) 95 | { 96 | StopDrawing(dialogueInfoMessage); 97 | 98 | return; 99 | } 100 | 101 | DrawDialogueArea(dialogueNames, dialogueFolderPath); 102 | 103 | serializedObject.ApplyModifiedProperties(); 104 | } 105 | 106 | private void DrawDialogueContainerArea() 107 | { 108 | DSInspectorUtility.DrawHeader("Dialogue Container"); 109 | 110 | dialogueContainerProperty.DrawPropertyField(); 111 | 112 | DSInspectorUtility.DrawSpace(); 113 | } 114 | 115 | private void DrawFiltersArea() 116 | { 117 | DSInspectorUtility.DrawHeader("Filters"); 118 | 119 | groupedDialoguesProperty.DrawPropertyField(); 120 | startingDialoguesOnlyProperty.DrawPropertyField(); 121 | 122 | DSInspectorUtility.DrawSpace(); 123 | } 124 | 125 | private void DrawDialogueGroupArea(DSDialogueContainerSO dialogueContainer, List dialogueGroupNames) 126 | { 127 | DSInspectorUtility.DrawHeader("Dialogue Group"); 128 | 129 | int oldSelectedDialogueGroupIndex = selectedDialogueGroupIndexProperty.intValue; 130 | 131 | DSDialogueGroupSO oldDialogueGroup = (DSDialogueGroupSO) dialogueGroupProperty.objectReferenceValue; 132 | 133 | bool isOldDialogueGroupNull = oldDialogueGroup == null; 134 | 135 | string oldDialogueGroupName = isOldDialogueGroupNull ? "" : oldDialogueGroup.GroupName; 136 | 137 | UpdateIndexOnNamesListUpdate(dialogueGroupNames, selectedDialogueGroupIndexProperty, oldSelectedDialogueGroupIndex, oldDialogueGroupName, isOldDialogueGroupNull); 138 | 139 | selectedDialogueGroupIndexProperty.intValue = DSInspectorUtility.DrawPopup("Dialogue Group", selectedDialogueGroupIndexProperty, dialogueGroupNames.ToArray()); 140 | 141 | string selectedDialogueGroupName = dialogueGroupNames[selectedDialogueGroupIndexProperty.intValue]; 142 | 143 | DSDialogueGroupSO selectedDialogueGroup = DSIOUtility.LoadAsset($"Assets/DialogueSystem/Dialogues/{dialogueContainer.FileName}/Groups/{selectedDialogueGroupName}", selectedDialogueGroupName); 144 | 145 | dialogueGroupProperty.objectReferenceValue = selectedDialogueGroup; 146 | 147 | DSInspectorUtility.DrawDisabledFields(() => dialogueGroupProperty.DrawPropertyField()); 148 | 149 | DSInspectorUtility.DrawSpace(); 150 | } 151 | 152 | private void DrawDialogueArea(List dialogueNames, string dialogueFolderPath) 153 | { 154 | DSInspectorUtility.DrawHeader("Dialogue"); 155 | 156 | int oldSelectedDialogueIndex = selectedDialogueIndexProperty.intValue; 157 | 158 | DSDialogueSO oldDialogue = (DSDialogueSO) dialogueProperty.objectReferenceValue; 159 | 160 | bool isOldDialogueNull = oldDialogue == null; 161 | 162 | string oldDialogueName = isOldDialogueNull ? "" : oldDialogue.DialogueName; 163 | 164 | UpdateIndexOnNamesListUpdate(dialogueNames, selectedDialogueIndexProperty, oldSelectedDialogueIndex, oldDialogueName, isOldDialogueNull); 165 | 166 | selectedDialogueIndexProperty.intValue = DSInspectorUtility.DrawPopup("Dialogue", selectedDialogueIndexProperty, dialogueNames.ToArray()); 167 | 168 | string selectedDialogueName = dialogueNames[selectedDialogueIndexProperty.intValue]; 169 | 170 | DSDialogueSO selectedDialogue = DSIOUtility.LoadAsset(dialogueFolderPath, selectedDialogueName); 171 | 172 | dialogueProperty.objectReferenceValue = selectedDialogue; 173 | 174 | DSInspectorUtility.DrawDisabledFields(() => dialogueProperty.DrawPropertyField()); 175 | } 176 | 177 | private void StopDrawing(string reason, MessageType messageType = MessageType.Info) 178 | { 179 | DSInspectorUtility.DrawHelpBox(reason, messageType); 180 | 181 | DSInspectorUtility.DrawSpace(); 182 | 183 | DSInspectorUtility.DrawHelpBox("You need to select a Dialogue for this component to work properly at Runtime!", MessageType.Warning); 184 | 185 | serializedObject.ApplyModifiedProperties(); 186 | } 187 | 188 | private void UpdateIndexOnNamesListUpdate(List optionNames, SerializedProperty indexProperty, int oldSelectedPropertyIndex, string oldPropertyName, bool isOldPropertyNull) 189 | { 190 | if (isOldPropertyNull) 191 | { 192 | indexProperty.intValue = 0; 193 | 194 | return; 195 | } 196 | 197 | bool oldIndexIsOutOfBoundsOfNamesListCount = oldSelectedPropertyIndex > optionNames.Count - 1; 198 | bool oldNameIsDifferentThanSelectedName = oldIndexIsOutOfBoundsOfNamesListCount || oldPropertyName != optionNames[oldSelectedPropertyIndex]; 199 | 200 | if (oldNameIsDifferentThanSelectedName) 201 | { 202 | if (optionNames.Contains(oldPropertyName)) 203 | { 204 | indexProperty.intValue = optionNames.IndexOf(oldPropertyName); 205 | 206 | return; 207 | } 208 | 209 | indexProperty.intValue = 0; 210 | } 211 | } 212 | } 213 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Inspectors/DSInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6edc83970270f804e950bd4cbc527a1b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f0b0d9395b9001740b7229601b651af3 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSElementUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor.Experimental.GraphView; 3 | using UnityEngine.UIElements; 4 | 5 | namespace DS.Utilities 6 | { 7 | using Elements; 8 | 9 | public static class DSElementUtility 10 | { 11 | public static Button CreateButton(string text, Action onClick = null) 12 | { 13 | Button button = new Button(onClick) 14 | { 15 | text = text 16 | }; 17 | 18 | return button; 19 | } 20 | 21 | public static Foldout CreateFoldout(string title, bool collapsed = false) 22 | { 23 | Foldout foldout = new Foldout() 24 | { 25 | text = title, 26 | value = !collapsed 27 | }; 28 | 29 | return foldout; 30 | } 31 | 32 | public static Port CreatePort(this DSNode node, string portName = "", Orientation orientation = Orientation.Horizontal, Direction direction = Direction.Output, Port.Capacity capacity = Port.Capacity.Single) 33 | { 34 | Port port = node.InstantiatePort(orientation, direction, capacity, typeof(bool)); 35 | 36 | port.portName = portName; 37 | 38 | return port; 39 | } 40 | 41 | public static TextField CreateTextField(string value = null, string label = null, EventCallback> onValueChanged = null) 42 | { 43 | TextField textField = new TextField() 44 | { 45 | value = value, 46 | label = label 47 | }; 48 | 49 | if (onValueChanged != null) 50 | { 51 | textField.RegisterValueChangedCallback(onValueChanged); 52 | } 53 | 54 | return textField; 55 | } 56 | 57 | public static TextField CreateTextArea(string value = null, string label = null, EventCallback> onValueChanged = null) 58 | { 59 | TextField textArea = CreateTextField(value, label, onValueChanged); 60 | 61 | textArea.multiline = true; 62 | 63 | return textArea; 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSElementUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 56dd46addb73fd149b4a5789f846bcee 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSIOUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEditor; 5 | using UnityEditor.Experimental.GraphView; 6 | using UnityEngine; 7 | 8 | namespace DS.Utilities 9 | { 10 | using Data; 11 | using Data.Save; 12 | using Elements; 13 | using ScriptableObjects; 14 | using Windows; 15 | 16 | public static class DSIOUtility 17 | { 18 | private static DSGraphView graphView; 19 | 20 | private static string graphFileName; 21 | private static string containerFolderPath; 22 | 23 | private static List nodes; 24 | private static List groups; 25 | 26 | private static Dictionary createdDialogueGroups; 27 | private static Dictionary createdDialogues; 28 | 29 | private static Dictionary loadedGroups; 30 | private static Dictionary loadedNodes; 31 | 32 | public static void Initialize(DSGraphView dsGraphView, string graphName) 33 | { 34 | graphView = dsGraphView; 35 | 36 | graphFileName = graphName; 37 | containerFolderPath = $"Assets/DialogueSystem/Dialogues/{graphName}"; 38 | 39 | nodes = new List(); 40 | groups = new List(); 41 | 42 | createdDialogueGroups = new Dictionary(); 43 | createdDialogues = new Dictionary(); 44 | 45 | loadedGroups = new Dictionary(); 46 | loadedNodes = new Dictionary(); 47 | } 48 | 49 | public static void Save() 50 | { 51 | CreateDefaultFolders(); 52 | 53 | GetElementsFromGraphView(); 54 | 55 | DSGraphSaveDataSO graphData = CreateAsset("Assets/Editor/DialogueSystem/Graphs", $"{graphFileName}Graph"); 56 | 57 | graphData.Initialize(graphFileName); 58 | 59 | DSDialogueContainerSO dialogueContainer = CreateAsset(containerFolderPath, graphFileName); 60 | 61 | dialogueContainer.Initialize(graphFileName); 62 | 63 | SaveGroups(graphData, dialogueContainer); 64 | SaveNodes(graphData, dialogueContainer); 65 | 66 | SaveAsset(graphData); 67 | SaveAsset(dialogueContainer); 68 | } 69 | 70 | private static void SaveGroups(DSGraphSaveDataSO graphData, DSDialogueContainerSO dialogueContainer) 71 | { 72 | List groupNames = new List(); 73 | 74 | foreach (DSGroup group in groups) 75 | { 76 | SaveGroupToGraph(group, graphData); 77 | SaveGroupToScriptableObject(group, dialogueContainer); 78 | 79 | groupNames.Add(group.title); 80 | } 81 | 82 | UpdateOldGroups(groupNames, graphData); 83 | } 84 | 85 | private static void SaveGroupToGraph(DSGroup group, DSGraphSaveDataSO graphData) 86 | { 87 | DSGroupSaveData groupData = new DSGroupSaveData() 88 | { 89 | ID = group.ID, 90 | Name = group.title, 91 | Position = group.GetPosition().position 92 | }; 93 | 94 | graphData.Groups.Add(groupData); 95 | } 96 | 97 | private static void SaveGroupToScriptableObject(DSGroup group, DSDialogueContainerSO dialogueContainer) 98 | { 99 | string groupName = group.title; 100 | 101 | CreateFolder($"{containerFolderPath}/Groups", groupName); 102 | CreateFolder($"{containerFolderPath}/Groups/{groupName}", "Dialogues"); 103 | 104 | DSDialogueGroupSO dialogueGroup = CreateAsset($"{containerFolderPath}/Groups/{groupName}", groupName); 105 | 106 | dialogueGroup.Initialize(groupName); 107 | 108 | createdDialogueGroups.Add(group.ID, dialogueGroup); 109 | 110 | dialogueContainer.DialogueGroups.Add(dialogueGroup, new List()); 111 | 112 | SaveAsset(dialogueGroup); 113 | } 114 | 115 | private static void UpdateOldGroups(List currentGroupNames, DSGraphSaveDataSO graphData) 116 | { 117 | if (graphData.OldGroupNames != null && graphData.OldGroupNames.Count != 0) 118 | { 119 | List groupsToRemove = graphData.OldGroupNames.Except(currentGroupNames).ToList(); 120 | 121 | foreach (string groupToRemove in groupsToRemove) 122 | { 123 | RemoveFolder($"{containerFolderPath}/Groups/{groupToRemove}"); 124 | } 125 | } 126 | 127 | graphData.OldGroupNames = new List(currentGroupNames); 128 | } 129 | 130 | private static void SaveNodes(DSGraphSaveDataSO graphData, DSDialogueContainerSO dialogueContainer) 131 | { 132 | SerializableDictionary> groupedNodeNames = new SerializableDictionary>(); 133 | List ungroupedNodeNames = new List(); 134 | 135 | foreach (DSNode node in nodes) 136 | { 137 | SaveNodeToGraph(node, graphData); 138 | SaveNodeToScriptableObject(node, dialogueContainer); 139 | 140 | if (node.Group != null) 141 | { 142 | groupedNodeNames.AddItem(node.Group.title, node.DialogueName); 143 | 144 | continue; 145 | } 146 | 147 | ungroupedNodeNames.Add(node.DialogueName); 148 | } 149 | 150 | UpdateDialoguesChoicesConnections(); 151 | 152 | UpdateOldGroupedNodes(groupedNodeNames, graphData); 153 | UpdateOldUngroupedNodes(ungroupedNodeNames, graphData); 154 | } 155 | 156 | private static void SaveNodeToGraph(DSNode node, DSGraphSaveDataSO graphData) 157 | { 158 | List choices = CloneNodeChoices(node.Choices); 159 | 160 | DSNodeSaveData nodeData = new DSNodeSaveData() 161 | { 162 | ID = node.ID, 163 | Name = node.DialogueName, 164 | Choices = choices, 165 | Text = node.Text, 166 | GroupID = node.Group?.ID, 167 | DialogueType = node.DialogueType, 168 | Position = node.GetPosition().position 169 | }; 170 | 171 | graphData.Nodes.Add(nodeData); 172 | } 173 | 174 | private static void SaveNodeToScriptableObject(DSNode node, DSDialogueContainerSO dialogueContainer) 175 | { 176 | DSDialogueSO dialogue; 177 | 178 | if (node.Group != null) 179 | { 180 | dialogue = CreateAsset($"{containerFolderPath}/Groups/{node.Group.title}/Dialogues", node.DialogueName); 181 | 182 | dialogueContainer.DialogueGroups.AddItem(createdDialogueGroups[node.Group.ID], dialogue); 183 | } 184 | else 185 | { 186 | dialogue = CreateAsset($"{containerFolderPath}/Global/Dialogues", node.DialogueName); 187 | 188 | dialogueContainer.UngroupedDialogues.Add(dialogue); 189 | } 190 | 191 | dialogue.Initialize( 192 | node.DialogueName, 193 | node.Text, 194 | ConvertNodeChoicesToDialogueChoices(node.Choices), 195 | node.DialogueType, 196 | node.IsStartingNode() 197 | ); 198 | 199 | createdDialogues.Add(node.ID, dialogue); 200 | 201 | SaveAsset(dialogue); 202 | } 203 | 204 | private static List ConvertNodeChoicesToDialogueChoices(List nodeChoices) 205 | { 206 | List dialogueChoices = new List(); 207 | 208 | foreach (DSChoiceSaveData nodeChoice in nodeChoices) 209 | { 210 | DSDialogueChoiceData choiceData = new DSDialogueChoiceData() 211 | { 212 | Text = nodeChoice.Text 213 | }; 214 | 215 | dialogueChoices.Add(choiceData); 216 | } 217 | 218 | return dialogueChoices; 219 | } 220 | 221 | private static void UpdateDialoguesChoicesConnections() 222 | { 223 | foreach (DSNode node in nodes) 224 | { 225 | DSDialogueSO dialogue = createdDialogues[node.ID]; 226 | 227 | for (int choiceIndex = 0; choiceIndex < node.Choices.Count; ++choiceIndex) 228 | { 229 | DSChoiceSaveData nodeChoice = node.Choices[choiceIndex]; 230 | 231 | if (string.IsNullOrEmpty(nodeChoice.NodeID)) 232 | { 233 | continue; 234 | } 235 | 236 | dialogue.Choices[choiceIndex].NextDialogue = createdDialogues[nodeChoice.NodeID]; 237 | 238 | SaveAsset(dialogue); 239 | } 240 | } 241 | } 242 | 243 | private static void UpdateOldGroupedNodes(SerializableDictionary> currentGroupedNodeNames, DSGraphSaveDataSO graphData) 244 | { 245 | if (graphData.OldGroupedNodeNames != null && graphData.OldGroupedNodeNames.Count != 0) 246 | { 247 | foreach (KeyValuePair> oldGroupedNode in graphData.OldGroupedNodeNames) 248 | { 249 | List nodesToRemove = new List(); 250 | 251 | if (currentGroupedNodeNames.ContainsKey(oldGroupedNode.Key)) 252 | { 253 | nodesToRemove = oldGroupedNode.Value.Except(currentGroupedNodeNames[oldGroupedNode.Key]).ToList(); 254 | } 255 | 256 | foreach (string nodeToRemove in nodesToRemove) 257 | { 258 | RemoveAsset($"{containerFolderPath}/Groups/{oldGroupedNode.Key}/Dialogues", nodeToRemove); 259 | } 260 | } 261 | } 262 | 263 | graphData.OldGroupedNodeNames = new SerializableDictionary>(currentGroupedNodeNames); 264 | } 265 | 266 | private static void UpdateOldUngroupedNodes(List currentUngroupedNodeNames, DSGraphSaveDataSO graphData) 267 | { 268 | if (graphData.OldUngroupedNodeNames != null && graphData.OldUngroupedNodeNames.Count != 0) 269 | { 270 | List nodesToRemove = graphData.OldUngroupedNodeNames.Except(currentUngroupedNodeNames).ToList(); 271 | 272 | foreach (string nodeToRemove in nodesToRemove) 273 | { 274 | RemoveAsset($"{containerFolderPath}/Global/Dialogues", nodeToRemove); 275 | } 276 | } 277 | 278 | graphData.OldUngroupedNodeNames = new List(currentUngroupedNodeNames); 279 | } 280 | 281 | public static void Load() 282 | { 283 | DSGraphSaveDataSO graphData = LoadAsset("Assets/Editor/DialogueSystem/Graphs", graphFileName); 284 | 285 | if (graphData == null) 286 | { 287 | EditorUtility.DisplayDialog( 288 | "Could not find the file!", 289 | "The file at the following path could not be found:\n\n" + 290 | $"\"Assets/Editor/DialogueSystem/Graphs/{graphFileName}\".\n\n" + 291 | "Make sure you chose the right file and it's placed at the folder path mentioned above.", 292 | "Thanks!" 293 | ); 294 | 295 | return; 296 | } 297 | 298 | DSEditorWindow.UpdateFileName(graphData.FileName); 299 | 300 | LoadGroups(graphData.Groups); 301 | LoadNodes(graphData.Nodes); 302 | LoadNodesConnections(); 303 | } 304 | 305 | private static void LoadGroups(List groups) 306 | { 307 | foreach (DSGroupSaveData groupData in groups) 308 | { 309 | DSGroup group = graphView.CreateGroup(groupData.Name, groupData.Position); 310 | 311 | group.ID = groupData.ID; 312 | 313 | loadedGroups.Add(group.ID, group); 314 | } 315 | } 316 | 317 | private static void LoadNodes(List nodes) 318 | { 319 | foreach (DSNodeSaveData nodeData in nodes) 320 | { 321 | List choices = CloneNodeChoices(nodeData.Choices); 322 | 323 | DSNode node = graphView.CreateNode(nodeData.Name, nodeData.DialogueType, nodeData.Position, false); 324 | 325 | node.ID = nodeData.ID; 326 | node.Choices = choices; 327 | node.Text = nodeData.Text; 328 | 329 | node.Draw(); 330 | 331 | graphView.AddElement(node); 332 | 333 | loadedNodes.Add(node.ID, node); 334 | 335 | if (string.IsNullOrEmpty(nodeData.GroupID)) 336 | { 337 | continue; 338 | } 339 | 340 | DSGroup group = loadedGroups[nodeData.GroupID]; 341 | 342 | node.Group = group; 343 | 344 | group.AddElement(node); 345 | } 346 | } 347 | 348 | private static void LoadNodesConnections() 349 | { 350 | foreach (KeyValuePair loadedNode in loadedNodes) 351 | { 352 | foreach (Port choicePort in loadedNode.Value.outputContainer.Children()) 353 | { 354 | DSChoiceSaveData choiceData = (DSChoiceSaveData) choicePort.userData; 355 | 356 | if (string.IsNullOrEmpty(choiceData.NodeID)) 357 | { 358 | continue; 359 | } 360 | 361 | DSNode nextNode = loadedNodes[choiceData.NodeID]; 362 | 363 | Port nextNodeInputPort = (Port) nextNode.inputContainer.Children().First(); 364 | 365 | Edge edge = choicePort.ConnectTo(nextNodeInputPort); 366 | 367 | graphView.AddElement(edge); 368 | 369 | loadedNode.Value.RefreshPorts(); 370 | } 371 | } 372 | } 373 | 374 | private static void CreateDefaultFolders() 375 | { 376 | CreateFolder("Assets/Editor/DialogueSystem", "Graphs"); 377 | 378 | CreateFolder("Assets", "DialogueSystem"); 379 | CreateFolder("Assets/DialogueSystem", "Dialogues"); 380 | 381 | CreateFolder("Assets/DialogueSystem/Dialogues", graphFileName); 382 | CreateFolder(containerFolderPath, "Global"); 383 | CreateFolder(containerFolderPath, "Groups"); 384 | CreateFolder($"{containerFolderPath}/Global", "Dialogues"); 385 | } 386 | 387 | private static void GetElementsFromGraphView() 388 | { 389 | Type groupType = typeof(DSGroup); 390 | 391 | graphView.graphElements.ForEach(graphElement => 392 | { 393 | if (graphElement is DSNode node) 394 | { 395 | nodes.Add(node); 396 | 397 | return; 398 | } 399 | 400 | if (graphElement.GetType() == groupType) 401 | { 402 | DSGroup group = (DSGroup) graphElement; 403 | 404 | groups.Add(group); 405 | 406 | return; 407 | } 408 | }); 409 | } 410 | 411 | public static void CreateFolder(string parentFolderPath, string newFolderName) 412 | { 413 | if (AssetDatabase.IsValidFolder($"{parentFolderPath}/{newFolderName}")) 414 | { 415 | return; 416 | } 417 | 418 | AssetDatabase.CreateFolder(parentFolderPath, newFolderName); 419 | } 420 | 421 | public static void RemoveFolder(string path) 422 | { 423 | FileUtil.DeleteFileOrDirectory($"{path}.meta"); 424 | FileUtil.DeleteFileOrDirectory($"{path}/"); 425 | } 426 | 427 | public static T CreateAsset(string path, string assetName) where T : ScriptableObject 428 | { 429 | string fullPath = $"{path}/{assetName}.asset"; 430 | 431 | T asset = LoadAsset(path, assetName); 432 | 433 | if (asset == null) 434 | { 435 | asset = ScriptableObject.CreateInstance(); 436 | 437 | AssetDatabase.CreateAsset(asset, fullPath); 438 | } 439 | 440 | return asset; 441 | } 442 | 443 | public static T LoadAsset(string path, string assetName) where T : ScriptableObject 444 | { 445 | string fullPath = $"{path}/{assetName}.asset"; 446 | 447 | return AssetDatabase.LoadAssetAtPath(fullPath); 448 | } 449 | 450 | public static void SaveAsset(UnityEngine.Object asset) 451 | { 452 | EditorUtility.SetDirty(asset); 453 | 454 | AssetDatabase.SaveAssets(); 455 | AssetDatabase.Refresh(); 456 | } 457 | 458 | public static void RemoveAsset(string path, string assetName) 459 | { 460 | AssetDatabase.DeleteAsset($"{path}/{assetName}.asset"); 461 | } 462 | 463 | private static List CloneNodeChoices(List nodeChoices) 464 | { 465 | List choices = new List(); 466 | 467 | foreach (DSChoiceSaveData choice in nodeChoices) 468 | { 469 | DSChoiceSaveData choiceData = new DSChoiceSaveData() 470 | { 471 | Text = choice.Text, 472 | NodeID = choice.NodeID 473 | }; 474 | 475 | choices.Add(choiceData); 476 | } 477 | 478 | return choices; 479 | } 480 | } 481 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSIOUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d80222d6da26f9447b8352526297e0df 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSInspectorUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor; 3 | 4 | namespace DS.Utilities 5 | { 6 | public static class DSInspectorUtility 7 | { 8 | public static void DrawDisabledFields(Action action) 9 | { 10 | EditorGUI.BeginDisabledGroup(true); 11 | 12 | action.Invoke(); 13 | 14 | EditorGUI.EndDisabledGroup(); 15 | } 16 | 17 | public static void DrawHeader(string label) 18 | { 19 | EditorGUILayout.LabelField(label, EditorStyles.boldLabel); 20 | } 21 | 22 | public static void DrawHelpBox(string message, MessageType messageType = MessageType.Info, bool wide = true) 23 | { 24 | EditorGUILayout.HelpBox(message, messageType, wide); 25 | } 26 | 27 | public static int DrawPopup(string label, SerializedProperty selectedIndexProperty, string[] options) 28 | { 29 | return EditorGUILayout.Popup(label, selectedIndexProperty.intValue, options); 30 | } 31 | 32 | public static int DrawPopup(string label, int selectedIndex, string[] options) 33 | { 34 | return EditorGUILayout.Popup(label, selectedIndex, options); 35 | } 36 | 37 | public static bool DrawPropertyField(this SerializedProperty serializedProperty) 38 | { 39 | return EditorGUILayout.PropertyField(serializedProperty); 40 | } 41 | 42 | public static void DrawSpace(int amount = 4) 43 | { 44 | EditorGUILayout.Space(amount); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSInspectorUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 575886af01a7b8f4b906eb29d9515117 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSStyleUtility.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine.UIElements; 3 | 4 | namespace DS.Utilities 5 | { 6 | public static class DSStyleUtility 7 | { 8 | public static VisualElement AddClasses(this VisualElement element, params string[] classNames) 9 | { 10 | foreach (string className in classNames) 11 | { 12 | element.AddToClassList(className); 13 | } 14 | 15 | return element; 16 | } 17 | 18 | public static VisualElement AddStyleSheets(this VisualElement element, params string[] styleSheetNames) 19 | { 20 | foreach (string styleSheetName in styleSheetNames) 21 | { 22 | StyleSheet styleSheet = (StyleSheet) EditorGUIUtility.Load(styleSheetName); 23 | 24 | element.styleSheets.Add(styleSheet); 25 | } 26 | 27 | return element; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Utilities/DSStyleUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2ff82198914b40a47b5b9c7fbc248390 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Windows.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bfee208aaea8aa04fbe8fe4810d68c6f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Windows/DSEditorWindow.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using UnityEditor; 3 | using UnityEditor.UIElements; 4 | using UnityEngine.UIElements; 5 | 6 | namespace DS.Windows 7 | { 8 | using System; 9 | using Utilities; 10 | 11 | public class DSEditorWindow : EditorWindow 12 | { 13 | private DSGraphView graphView; 14 | 15 | private readonly string defaultFileName = "DialoguesFileName"; 16 | 17 | private static TextField fileNameTextField; 18 | private Button saveButton; 19 | private Button miniMapButton; 20 | 21 | [MenuItem("Window/DS/Dialogue Graph")] 22 | public static void Open() 23 | { 24 | GetWindow("Dialogue Graph"); 25 | } 26 | 27 | private void OnEnable() 28 | { 29 | AddGraphView(); 30 | AddToolbar(); 31 | 32 | AddStyles(); 33 | } 34 | 35 | private void AddGraphView() 36 | { 37 | graphView = new DSGraphView(this); 38 | 39 | graphView.StretchToParentSize(); 40 | 41 | rootVisualElement.Add(graphView); 42 | } 43 | 44 | private void AddToolbar() 45 | { 46 | Toolbar toolbar = new Toolbar(); 47 | 48 | fileNameTextField = DSElementUtility.CreateTextField(defaultFileName, "File Name:", callback => 49 | { 50 | fileNameTextField.value = callback.newValue.RemoveWhitespaces().RemoveSpecialCharacters(); 51 | }); 52 | 53 | saveButton = DSElementUtility.CreateButton("Save", () => Save()); 54 | 55 | Button loadButton = DSElementUtility.CreateButton("Load", () => Load()); 56 | Button clearButton = DSElementUtility.CreateButton("Clear", () => Clear()); 57 | Button resetButton = DSElementUtility.CreateButton("Reset", () => ResetGraph()); 58 | 59 | miniMapButton = DSElementUtility.CreateButton("Minimap", () => ToggleMiniMap()); 60 | 61 | toolbar.Add(fileNameTextField); 62 | toolbar.Add(saveButton); 63 | toolbar.Add(loadButton); 64 | toolbar.Add(clearButton); 65 | toolbar.Add(resetButton); 66 | toolbar.Add(miniMapButton); 67 | 68 | toolbar.AddStyleSheets("DialogueSystem/DSToolbarStyles.uss"); 69 | 70 | rootVisualElement.Add(toolbar); 71 | } 72 | 73 | private void AddStyles() 74 | { 75 | rootVisualElement.AddStyleSheets("DialogueSystem/DSVariables.uss"); 76 | } 77 | 78 | private void Save() 79 | { 80 | if (string.IsNullOrEmpty(fileNameTextField.value)) 81 | { 82 | EditorUtility.DisplayDialog("Invalid file name.", "Please ensure the file name you've typed in is valid.", "Roger!"); 83 | 84 | return; 85 | } 86 | 87 | DSIOUtility.Initialize(graphView, fileNameTextField.value); 88 | DSIOUtility.Save(); 89 | } 90 | 91 | private void Load() 92 | { 93 | string filePath = EditorUtility.OpenFilePanel("Dialogue Graphs", "Assets/Editor/DialogueSystem/Graphs", "asset"); 94 | 95 | if (string.IsNullOrEmpty(filePath)) 96 | { 97 | return; 98 | } 99 | 100 | Clear(); 101 | 102 | DSIOUtility.Initialize(graphView, Path.GetFileNameWithoutExtension(filePath)); 103 | DSIOUtility.Load(); 104 | } 105 | 106 | private void Clear() 107 | { 108 | graphView.ClearGraph(); 109 | } 110 | 111 | private void ResetGraph() 112 | { 113 | Clear(); 114 | 115 | UpdateFileName(defaultFileName); 116 | } 117 | 118 | private void ToggleMiniMap() 119 | { 120 | graphView.ToggleMiniMap(); 121 | 122 | miniMapButton.ToggleInClassList("ds-toolbar__button__selected"); 123 | } 124 | 125 | public static void UpdateFileName(string newFileName) 126 | { 127 | fileNameTextField.value = newFileName; 128 | } 129 | 130 | public void EnableSaving() 131 | { 132 | saveButton.SetEnabled(true); 133 | } 134 | 135 | public void DisableSaving() 136 | { 137 | saveButton.SetEnabled(false); 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Windows/DSEditorWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 688be0e5b0f88cf49a719ddac24ad96f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Windows/DSGraphView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor.Experimental.GraphView; 4 | using UnityEngine; 5 | using UnityEngine.UIElements; 6 | 7 | namespace DS.Windows 8 | { 9 | using Data.Error; 10 | using Data.Save; 11 | using Elements; 12 | using Enumerations; 13 | using Utilities; 14 | 15 | public class DSGraphView : GraphView 16 | { 17 | private DSEditorWindow editorWindow; 18 | private DSSearchWindow searchWindow; 19 | 20 | private MiniMap miniMap; 21 | 22 | private SerializableDictionary ungroupedNodes; 23 | private SerializableDictionary groups; 24 | private SerializableDictionary> groupedNodes; 25 | 26 | private int nameErrorsAmount; 27 | 28 | public int NameErrorsAmount 29 | { 30 | get 31 | { 32 | return nameErrorsAmount; 33 | } 34 | 35 | set 36 | { 37 | nameErrorsAmount = value; 38 | 39 | if (nameErrorsAmount == 0) 40 | { 41 | editorWindow.EnableSaving(); 42 | } 43 | 44 | if (nameErrorsAmount == 1) 45 | { 46 | editorWindow.DisableSaving(); 47 | } 48 | } 49 | } 50 | 51 | public DSGraphView(DSEditorWindow dsEditorWindow) 52 | { 53 | editorWindow = dsEditorWindow; 54 | 55 | ungroupedNodes = new SerializableDictionary(); 56 | groups = new SerializableDictionary(); 57 | groupedNodes = new SerializableDictionary>(); 58 | 59 | AddManipulators(); 60 | AddGridBackground(); 61 | AddSearchWindow(); 62 | AddMiniMap(); 63 | 64 | OnElementsDeleted(); 65 | OnGroupElementsAdded(); 66 | OnGroupElementsRemoved(); 67 | OnGroupRenamed(); 68 | OnGraphViewChanged(); 69 | 70 | AddStyles(); 71 | AddMiniMapStyles(); 72 | } 73 | 74 | public override List GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter) 75 | { 76 | List compatiblePorts = new List(); 77 | 78 | ports.ForEach(port => 79 | { 80 | if (startPort == port) 81 | { 82 | return; 83 | } 84 | 85 | if (startPort.node == port.node) 86 | { 87 | return; 88 | } 89 | 90 | if (startPort.direction == port.direction) 91 | { 92 | return; 93 | } 94 | 95 | compatiblePorts.Add(port); 96 | }); 97 | 98 | return compatiblePorts; 99 | } 100 | 101 | private void AddManipulators() 102 | { 103 | SetupZoom(ContentZoomer.DefaultMinScale, ContentZoomer.DefaultMaxScale); 104 | 105 | this.AddManipulator(new ContentDragger()); 106 | this.AddManipulator(new SelectionDragger()); 107 | this.AddManipulator(new RectangleSelector()); 108 | 109 | this.AddManipulator(CreateNodeContextualMenu("Add Node (Single Choice)", DSDialogueType.SingleChoice)); 110 | this.AddManipulator(CreateNodeContextualMenu("Add Node (Multiple Choice)", DSDialogueType.MultipleChoice)); 111 | 112 | this.AddManipulator(CreateGroupContextualMenu()); 113 | } 114 | 115 | private IManipulator CreateNodeContextualMenu(string actionTitle, DSDialogueType dialogueType) 116 | { 117 | ContextualMenuManipulator contextualMenuManipulator = new ContextualMenuManipulator( 118 | menuEvent => menuEvent.menu.AppendAction(actionTitle, actionEvent => AddElement(CreateNode("DialogueName", dialogueType, GetLocalMousePosition(actionEvent.eventInfo.localMousePosition)))) 119 | ); 120 | 121 | return contextualMenuManipulator; 122 | } 123 | 124 | private IManipulator CreateGroupContextualMenu() 125 | { 126 | ContextualMenuManipulator contextualMenuManipulator = new ContextualMenuManipulator( 127 | menuEvent => menuEvent.menu.AppendAction("Add Group", actionEvent => CreateGroup("DialogueGroup", GetLocalMousePosition(actionEvent.eventInfo.localMousePosition))) 128 | ); 129 | 130 | return contextualMenuManipulator; 131 | } 132 | 133 | public DSGroup CreateGroup(string title, Vector2 position) 134 | { 135 | DSGroup group = new DSGroup(title, position); 136 | 137 | AddGroup(group); 138 | 139 | AddElement(group); 140 | 141 | foreach (GraphElement selectedElement in selection) 142 | { 143 | if (!(selectedElement is DSNode)) 144 | { 145 | continue; 146 | } 147 | 148 | DSNode node = (DSNode) selectedElement; 149 | 150 | group.AddElement(node); 151 | } 152 | 153 | return group; 154 | } 155 | 156 | public DSNode CreateNode(string nodeName, DSDialogueType dialogueType, Vector2 position, bool shouldDraw = true) 157 | { 158 | Type nodeType = Type.GetType($"DS.Elements.DS{dialogueType}Node"); 159 | 160 | DSNode node = (DSNode) Activator.CreateInstance(nodeType); 161 | 162 | node.Initialize(nodeName, this, position); 163 | 164 | if (shouldDraw) 165 | { 166 | node.Draw(); 167 | } 168 | 169 | AddUngroupedNode(node); 170 | 171 | return node; 172 | } 173 | 174 | private void OnElementsDeleted() 175 | { 176 | deleteSelection = (operationName, askUser) => 177 | { 178 | Type groupType = typeof(DSGroup); 179 | Type edgeType = typeof(Edge); 180 | 181 | List groupsToDelete = new List(); 182 | List nodesToDelete = new List(); 183 | List edgesToDelete = new List(); 184 | 185 | foreach (GraphElement selectedElement in selection) 186 | { 187 | if (selectedElement is DSNode node) 188 | { 189 | nodesToDelete.Add(node); 190 | 191 | continue; 192 | } 193 | 194 | if (selectedElement.GetType() == edgeType) 195 | { 196 | Edge edge = (Edge) selectedElement; 197 | 198 | edgesToDelete.Add(edge); 199 | 200 | continue; 201 | } 202 | 203 | if (selectedElement.GetType() != groupType) 204 | { 205 | continue; 206 | } 207 | 208 | DSGroup group = (DSGroup) selectedElement; 209 | 210 | groupsToDelete.Add(group); 211 | } 212 | 213 | foreach (DSGroup groupToDelete in groupsToDelete) 214 | { 215 | List groupNodes = new List(); 216 | 217 | foreach (GraphElement groupElement in groupToDelete.containedElements) 218 | { 219 | if (!(groupElement is DSNode)) 220 | { 221 | continue; 222 | } 223 | 224 | DSNode groupNode = (DSNode) groupElement; 225 | 226 | groupNodes.Add(groupNode); 227 | } 228 | 229 | groupToDelete.RemoveElements(groupNodes); 230 | 231 | RemoveGroup(groupToDelete); 232 | 233 | RemoveElement(groupToDelete); 234 | } 235 | 236 | DeleteElements(edgesToDelete); 237 | 238 | foreach (DSNode nodeToDelete in nodesToDelete) 239 | { 240 | if (nodeToDelete.Group != null) 241 | { 242 | nodeToDelete.Group.RemoveElement(nodeToDelete); 243 | } 244 | 245 | RemoveUngroupedNode(nodeToDelete); 246 | 247 | nodeToDelete.DisconnectAllPorts(); 248 | 249 | RemoveElement(nodeToDelete); 250 | } 251 | }; 252 | } 253 | 254 | private void OnGroupElementsAdded() 255 | { 256 | elementsAddedToGroup = (group, elements) => 257 | { 258 | foreach (GraphElement element in elements) 259 | { 260 | if (!(element is DSNode)) 261 | { 262 | continue; 263 | } 264 | 265 | DSGroup dsGroup = (DSGroup) group; 266 | DSNode node = (DSNode) element; 267 | 268 | RemoveUngroupedNode(node); 269 | AddGroupedNode(node, dsGroup); 270 | } 271 | }; 272 | } 273 | 274 | private void OnGroupElementsRemoved() 275 | { 276 | elementsRemovedFromGroup = (group, elements) => 277 | { 278 | foreach (GraphElement element in elements) 279 | { 280 | if (!(element is DSNode)) 281 | { 282 | continue; 283 | } 284 | 285 | DSGroup dsGroup = (DSGroup) group; 286 | DSNode node = (DSNode) element; 287 | 288 | RemoveGroupedNode(node, dsGroup); 289 | AddUngroupedNode(node); 290 | } 291 | }; 292 | } 293 | 294 | private void OnGroupRenamed() 295 | { 296 | groupTitleChanged = (group, newTitle) => 297 | { 298 | DSGroup dsGroup = (DSGroup) group; 299 | 300 | dsGroup.title = newTitle.RemoveWhitespaces().RemoveSpecialCharacters(); 301 | 302 | if (string.IsNullOrEmpty(dsGroup.title)) 303 | { 304 | if (!string.IsNullOrEmpty(dsGroup.OldTitle)) 305 | { 306 | ++NameErrorsAmount; 307 | } 308 | } 309 | else 310 | { 311 | if (string.IsNullOrEmpty(dsGroup.OldTitle)) 312 | { 313 | --NameErrorsAmount; 314 | } 315 | } 316 | 317 | RemoveGroup(dsGroup); 318 | 319 | dsGroup.OldTitle = dsGroup.title; 320 | 321 | AddGroup(dsGroup); 322 | }; 323 | } 324 | 325 | private void OnGraphViewChanged() 326 | { 327 | graphViewChanged = (changes) => 328 | { 329 | if (changes.edgesToCreate != null) 330 | { 331 | foreach (Edge edge in changes.edgesToCreate) 332 | { 333 | DSNode nextNode = (DSNode) edge.input.node; 334 | 335 | DSChoiceSaveData choiceData = (DSChoiceSaveData) edge.output.userData; 336 | 337 | choiceData.NodeID = nextNode.ID; 338 | } 339 | } 340 | 341 | if (changes.elementsToRemove != null) 342 | { 343 | Type edgeType = typeof(Edge); 344 | 345 | foreach (GraphElement element in changes.elementsToRemove) 346 | { 347 | if (element.GetType() != edgeType) 348 | { 349 | continue; 350 | } 351 | 352 | Edge edge = (Edge) element; 353 | 354 | DSChoiceSaveData choiceData = (DSChoiceSaveData) edge.output.userData; 355 | 356 | choiceData.NodeID = ""; 357 | } 358 | } 359 | 360 | return changes; 361 | }; 362 | } 363 | 364 | public void AddUngroupedNode(DSNode node) 365 | { 366 | string nodeName = node.DialogueName.ToLower(); 367 | 368 | if (!ungroupedNodes.ContainsKey(nodeName)) 369 | { 370 | DSNodeErrorData nodeErrorData = new DSNodeErrorData(); 371 | 372 | nodeErrorData.Nodes.Add(node); 373 | 374 | ungroupedNodes.Add(nodeName, nodeErrorData); 375 | 376 | return; 377 | } 378 | 379 | List ungroupedNodesList = ungroupedNodes[nodeName].Nodes; 380 | 381 | ungroupedNodesList.Add(node); 382 | 383 | Color errorColor = ungroupedNodes[nodeName].ErrorData.Color; 384 | 385 | node.SetErrorStyle(errorColor); 386 | 387 | if (ungroupedNodesList.Count == 2) 388 | { 389 | ++NameErrorsAmount; 390 | 391 | ungroupedNodesList[0].SetErrorStyle(errorColor); 392 | } 393 | } 394 | 395 | public void RemoveUngroupedNode(DSNode node) 396 | { 397 | string nodeName = node.DialogueName.ToLower(); 398 | 399 | List ungroupedNodesList = ungroupedNodes[nodeName].Nodes; 400 | 401 | ungroupedNodesList.Remove(node); 402 | 403 | node.ResetStyle(); 404 | 405 | if (ungroupedNodesList.Count == 1) 406 | { 407 | --NameErrorsAmount; 408 | 409 | ungroupedNodesList[0].ResetStyle(); 410 | 411 | return; 412 | } 413 | 414 | if (ungroupedNodesList.Count == 0) 415 | { 416 | ungroupedNodes.Remove(nodeName); 417 | } 418 | } 419 | 420 | private void AddGroup(DSGroup group) 421 | { 422 | string groupName = group.title.ToLower(); 423 | 424 | if (!groups.ContainsKey(groupName)) 425 | { 426 | DSGroupErrorData groupErrorData = new DSGroupErrorData(); 427 | 428 | groupErrorData.Groups.Add(group); 429 | 430 | groups.Add(groupName, groupErrorData); 431 | 432 | return; 433 | } 434 | 435 | List groupsList = groups[groupName].Groups; 436 | 437 | groupsList.Add(group); 438 | 439 | Color errorColor = groups[groupName].ErrorData.Color; 440 | 441 | group.SetErrorStyle(errorColor); 442 | 443 | if (groupsList.Count == 2) 444 | { 445 | ++NameErrorsAmount; 446 | 447 | groupsList[0].SetErrorStyle(errorColor); 448 | } 449 | } 450 | 451 | private void RemoveGroup(DSGroup group) 452 | { 453 | string oldGroupName = group.OldTitle.ToLower(); 454 | 455 | List groupsList = groups[oldGroupName].Groups; 456 | 457 | groupsList.Remove(group); 458 | 459 | group.ResetStyle(); 460 | 461 | if (groupsList.Count == 1) 462 | { 463 | --NameErrorsAmount; 464 | 465 | groupsList[0].ResetStyle(); 466 | 467 | return; 468 | } 469 | 470 | if (groupsList.Count == 0) 471 | { 472 | groups.Remove(oldGroupName); 473 | } 474 | } 475 | 476 | public void AddGroupedNode(DSNode node, DSGroup group) 477 | { 478 | string nodeName = node.DialogueName.ToLower(); 479 | 480 | node.Group = group; 481 | 482 | if (!groupedNodes.ContainsKey(group)) 483 | { 484 | groupedNodes.Add(group, new SerializableDictionary()); 485 | } 486 | 487 | if (!groupedNodes[group].ContainsKey(nodeName)) 488 | { 489 | DSNodeErrorData nodeErrorData = new DSNodeErrorData(); 490 | 491 | nodeErrorData.Nodes.Add(node); 492 | 493 | groupedNodes[group].Add(nodeName, nodeErrorData); 494 | 495 | return; 496 | } 497 | 498 | List groupedNodesList = groupedNodes[group][nodeName].Nodes; 499 | 500 | groupedNodesList.Add(node); 501 | 502 | Color errorColor = groupedNodes[group][nodeName].ErrorData.Color; 503 | 504 | node.SetErrorStyle(errorColor); 505 | 506 | if (groupedNodesList.Count == 2) 507 | { 508 | ++NameErrorsAmount; 509 | 510 | groupedNodesList[0].SetErrorStyle(errorColor); 511 | } 512 | } 513 | 514 | public void RemoveGroupedNode(DSNode node, DSGroup group) 515 | { 516 | string nodeName = node.DialogueName.ToLower(); 517 | 518 | node.Group = null; 519 | 520 | List groupedNodesList = groupedNodes[group][nodeName].Nodes; 521 | 522 | groupedNodesList.Remove(node); 523 | 524 | node.ResetStyle(); 525 | 526 | if (groupedNodesList.Count == 1) 527 | { 528 | --NameErrorsAmount; 529 | 530 | groupedNodesList[0].ResetStyle(); 531 | 532 | return; 533 | } 534 | 535 | if (groupedNodesList.Count == 0) 536 | { 537 | groupedNodes[group].Remove(nodeName); 538 | 539 | if (groupedNodes[group].Count == 0) 540 | { 541 | groupedNodes.Remove(group); 542 | } 543 | } 544 | } 545 | 546 | private void AddGridBackground() 547 | { 548 | GridBackground gridBackground = new GridBackground(); 549 | 550 | gridBackground.StretchToParentSize(); 551 | 552 | Insert(0, gridBackground); 553 | } 554 | 555 | private void AddSearchWindow() 556 | { 557 | if (searchWindow == null) 558 | { 559 | searchWindow = ScriptableObject.CreateInstance(); 560 | } 561 | 562 | searchWindow.Initialize(this); 563 | 564 | nodeCreationRequest = context => SearchWindow.Open(new SearchWindowContext(context.screenMousePosition), searchWindow); 565 | } 566 | 567 | private void AddMiniMap() 568 | { 569 | miniMap = new MiniMap() 570 | { 571 | anchored = true 572 | }; 573 | 574 | miniMap.SetPosition(new Rect(15, 50, 200, 180)); 575 | 576 | Add(miniMap); 577 | 578 | miniMap.visible = false; 579 | } 580 | 581 | private void AddStyles() 582 | { 583 | this.AddStyleSheets( 584 | "DialogueSystem/DSGraphViewStyles.uss", 585 | "DialogueSystem/DSNodeStyles.uss" 586 | ); 587 | } 588 | 589 | private void AddMiniMapStyles() 590 | { 591 | StyleColor backgroundColor = new StyleColor(new Color32(29, 29, 30, 255)); 592 | StyleColor borderColor = new StyleColor(new Color32(51, 51, 51, 255)); 593 | 594 | miniMap.style.backgroundColor = backgroundColor; 595 | miniMap.style.borderTopColor = borderColor; 596 | miniMap.style.borderRightColor = borderColor; 597 | miniMap.style.borderBottomColor = borderColor; 598 | miniMap.style.borderLeftColor = borderColor; 599 | } 600 | 601 | public Vector2 GetLocalMousePosition(Vector2 mousePosition, bool isSearchWindow = false) 602 | { 603 | Vector2 worldMousePosition = mousePosition; 604 | 605 | if (isSearchWindow) 606 | { 607 | worldMousePosition = editorWindow.rootVisualElement.ChangeCoordinatesTo(editorWindow.rootVisualElement.parent, mousePosition - editorWindow.position.position); 608 | } 609 | 610 | Vector2 localMousePosition = contentViewContainer.WorldToLocal(worldMousePosition); 611 | 612 | return localMousePosition; 613 | } 614 | 615 | public void ClearGraph() 616 | { 617 | graphElements.ForEach(graphElement => RemoveElement(graphElement)); 618 | 619 | groups.Clear(); 620 | groupedNodes.Clear(); 621 | ungroupedNodes.Clear(); 622 | 623 | NameErrorsAmount = 0; 624 | } 625 | 626 | public void ToggleMiniMap() 627 | { 628 | miniMap.visible = !miniMap.visible; 629 | } 630 | } 631 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Windows/DSGraphView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c694b81ce7081f048b1c9d970c45e4a5 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Windows/DSSearchWindow.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEditor.Experimental.GraphView; 3 | using UnityEngine; 4 | 5 | namespace DS.Windows 6 | { 7 | using Elements; 8 | using Enumerations; 9 | 10 | public class DSSearchWindow : ScriptableObject, ISearchWindowProvider 11 | { 12 | private DSGraphView graphView; 13 | private Texture2D indentationIcon; 14 | 15 | public void Initialize(DSGraphView dsGraphView) 16 | { 17 | graphView = dsGraphView; 18 | 19 | indentationIcon = new Texture2D(1, 1); 20 | indentationIcon.SetPixel(0, 0, Color.clear); 21 | indentationIcon.Apply(); 22 | } 23 | 24 | public List CreateSearchTree(SearchWindowContext context) 25 | { 26 | List searchTreeEntries = new List() 27 | { 28 | new SearchTreeGroupEntry(new GUIContent("Create Elements")), 29 | new SearchTreeGroupEntry(new GUIContent("Dialogue Nodes"), 1), 30 | new SearchTreeEntry(new GUIContent("Single Choice", indentationIcon)) 31 | { 32 | userData = DSDialogueType.SingleChoice, 33 | level = 2 34 | }, 35 | new SearchTreeEntry(new GUIContent("Multiple Choice", indentationIcon)) 36 | { 37 | userData = DSDialogueType.MultipleChoice, 38 | level = 2 39 | }, 40 | new SearchTreeGroupEntry(new GUIContent("Dialogue Groups"), 1), 41 | new SearchTreeEntry(new GUIContent("Single Group", indentationIcon)) 42 | { 43 | userData = new Group(), 44 | level = 2 45 | } 46 | }; 47 | 48 | return searchTreeEntries; 49 | } 50 | 51 | public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context) 52 | { 53 | Vector2 localMousePosition = graphView.GetLocalMousePosition(context.screenMousePosition, true); 54 | 55 | switch (SearchTreeEntry.userData) 56 | { 57 | case DSDialogueType.SingleChoice: 58 | { 59 | DSSingleChoiceNode singleChoiceNode = (DSSingleChoiceNode) graphView.CreateNode("DialogueName", DSDialogueType.SingleChoice, localMousePosition); 60 | 61 | graphView.AddElement(singleChoiceNode); 62 | 63 | return true; 64 | } 65 | 66 | case DSDialogueType.MultipleChoice: 67 | { 68 | DSMultipleChoiceNode multipleChoiceNode = (DSMultipleChoiceNode) graphView.CreateNode("DialogueName", DSDialogueType.MultipleChoice, localMousePosition); 69 | 70 | graphView.AddElement(multipleChoiceNode); 71 | 72 | return true; 73 | } 74 | 75 | case Group _: 76 | { 77 | graphView.CreateGroup("DialogueGroup", localMousePosition); 78 | 79 | return true; 80 | } 81 | 82 | default: 83 | { 84 | return false; 85 | } 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /Assets/Editor/DialogueSystem/Windows/DSSearchWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ae13c3fd501cac4a92c82e03dcd0e60 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Gustavo Vieira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Dialogue System 2 | 3 | A Node-Based Dialogue System made for Unity using the Experimental GraphView API. 4 | 5 | ![Graph Example](https://imgur.com/LlLZkmc.png) 6 | 7 | # Tutorial 8 | 9 | This System is the final result of a [Tutorial Series](https://www.youtube.com/watch?v=nvELzBYMK1U&list=PL0yxB6cCkoWK38XT4stSztcLueJ_kTx5f) made by me 10 | whose purpose is to teach you how to use Unity's GraphView API. 11 | 12 | # Features 13 | 14 | This System offers the user a few features: 15 | 16 | ### Creation of Single or Multiple Choice Nodes 17 | 18 | Able to be created both through a contextual menu or a search window. 19 | 20 | ![Creation of the Nodes](https://imgur.com/g7Oo4pQ.gif) 21 | 22 | ### Grouping of Nodes 23 | 24 | Nodes can be grouped to allow for a better organization of the Graph. 25 | 26 | These are also useful to filter the dialogues by Groups in the Custom Inspector. 27 | 28 | ![Grouped Nodes](https://imgur.com/B21Znkk.gif) 29 | 30 | ### Feedback on Nodes or Groups with the same name in the same scope 31 | 32 | Whenever a Node or a Group have elements of the same type with the same name in the same scope, 33 | their colors will be updated to allow the user to know they need to update their names. 34 | 35 | The Save button will also be disabled until the names are changed. 36 | 37 | This happens because the Graph Elements will end up becoming asset files in the Project folder. 38 | 39 | ![Same Name in the Same Scope Error Feedback](https://imgur.com/6rEqcR0.gif) 40 | 41 | ### Minimap 42 | 43 | A minimap that allows the user to have an overview of where the Nodes are placed 44 | and to navigate to them by clicking in the respective shapes. 45 | 46 | ![Minimap](https://imgur.com/2rGVj3b.gif) 47 | 48 | ### Save & Load System 49 | 50 | A Graph can be saved into a Scriptable Object that can be used to load it again later on. 51 | 52 | The Graph elements will also be transformed into Scriptable Objects that can be used at Runtime. 53 | 54 | Some, or most of those Scriptable Objects are used just for the Custom Inspector. 55 | 56 | ![Save & Load System](https://imgur.com/PUk2Jtq.gif) 57 | 58 | ### Clear & Reset Button 59 | 60 | Allows for clearing the Graph (remove any element on it) and resetting the file name. 61 | 62 | ![Clearing and Resetting the Graph](https://imgur.com/ucHMBgQ.gif) 63 | 64 | ### Custom Inspector 65 | 66 | Allows the ease of choosing a starting dialogue for a runtime dialogue system the user might want to develop. 67 | 68 | The Inspector allows the filtering of dialogues by grouped and non-grouped dialogues, as well as by dialogues that can be considered "starting" dialogues. 69 | 70 | ![Custom Inspector](https://imgur.com/7gqUHpR.gif) 71 | 72 | # License 73 | 74 | [MIT License](LICENSE.md) 75 | 76 | # Version 77 | 78 | Current version: 1.0.3 79 | 80 | # Contributing 81 | 82 | This Dialogue System won't be updated further than this as its main purpose was to fit my game needs and the tutorial series, which should now be over. 83 | 84 | If you want to support me, subscribing to my [Youtube Channel](https://www.youtube.com/c/IndieWafflus) or following me on [Twitter](https://twitter.com/IndieWafflus) would help me out a ton and be appreciated. 85 | 86 | # Notes 87 | 88 | The GraphView API is likely being deprecated towards the [GTF (GraphTools Foundation) Package](https://docs.unity3d.com/Packages/com.unity.graphtools.foundation@0.8/manual/index.html), but should still work in Unity 2019 and Unity 2020. 89 | 90 | This was made using Unity 2020. 91 | --------------------------------------------------------------------------------