| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- using DunGen.Editor.Validation;
- using DunGen.Graph;
- using System;
- using System.Collections.Generic;
- using UnityEditor;
- using UnityEditorInternal;
- using UnityEngine;
- namespace DunGen.Editor
- {
- [CustomEditor(typeof(DungeonFlow))]
- public sealed class DungeonFlowInspector : UnityEditor.Editor
- {
- #region Helpers
- private sealed class Properties
- {
- public SerializedProperty Length;
- public SerializedProperty BranchMode;
- public SerializedProperty BranchCount;
- public SerializedProperty KeyManager;
- public SerializedProperty DoorwayConnectionChance;
- public SerializedProperty TileInjectionRules;
- public SerializedProperty RestrictConnectionToSameSection;
- public SerializedProperty TileTagConnectionMode;
- public SerializedProperty TileConnectionTags;
- public SerializedProperty BranchTagPruneMode;
- public SerializedProperty BranchPruneTags;
- public SerializedProperty StraighteningSettings;
- public ReorderableList GlobalProps;
- public ReorderableList TileConnectionTagsList;
- public ReorderableList BranchPruneTagsList;
- public ReorderableList TileInjectionRulesList;
- }
- private static class Labels
- {
- public static readonly GUIContent Validate = new GUIContent("Validate Dungeon", "Runs a set of automated checks on the integrity of the dungeon, reporting any errors that are found");
- public static readonly GUIContent Length = new GUIContent("Length", "Min and max length of the main path. This will determine how long the dungeon is");
- public static readonly GUIContent BranchMode = new GUIContent("Branch Mode", "Determines how the number of branches is computed");
- public static readonly GUIContent BranchCount = new GUIContent("Branch Count", "The total number of branches to appear accross the entire dungeon. Only used when Branch Mode is set to Global");
- public static readonly GUIContent GlobalProps = new GUIContent("Global Props");
- public static readonly GUIContent KeyManager = new GUIContent("Key Manager", "Defines which keys are available to be placed throughout the dungeon. This can be left blank if you don't want to make use of the lock & key system");
- public static readonly GUIContent DoorwayConnectionHeader = new GUIContent("Doorway Connection");
- public static readonly GUIContent DoorwayConnectionChance = new GUIContent("Connection Chance", "The percentage chance that an unconnected but overlapping set of doorways will be connected. This can be overriden on a per-tile basis");
- public static readonly GUIContent RestrictConnectionToSameSection = new GUIContent("Restrict to Same Section", "If checked, doorways will only be connected if they lie on the same line segment in the dungeon flow graph");
- public static readonly GUIContent TileInjection = new GUIContent("Special Tile Injection", "Used to inject specific tiles into the dungeon layout based on a set of rules");
- public static readonly GUIContent OpenFlowEditor = new GUIContent("Open Flow Editor", "The node graph lets you design how the dungeon should be laid out");
- public static readonly GUIContent GlobalPropGroupID = new GUIContent("Group ID", "The prop ID. This should match the ID on the GlobalProp component placed inside Tiles");
- public static readonly GUIContent GlobalPropGroupCount = new GUIContent("Count", "The number of times this prop should appear across the entire dungeon");
- public static readonly GUIContent TileConnectionTagMode = new GUIContent("Mode", "How to apply the tag rules below. NOTE: This section is ignored if the tag pair list is empty.\n Accept: Tiles are only connected if their tags match one of the pairs in the list below.\n Reject: Tiles will always connect unless their tags match one of the pairs in the list below.");
- public static readonly GUIContent TileConnectionTags = new GUIContent("Tag Pairs");
- public static readonly GUIContent TileConnectionRules = new GUIContent("Tile Connection Rules", "Allows us to accept or reject a connection between two tiles based on the tags each of them have.");
- public static readonly GUIContent BranchPruneMode = new GUIContent("Branch Prune Mode", "The method by which tiles at the end of a branch are pruned based on the tags below.");
- public static readonly GUIContent BranchPruneTags = new GUIContent("Branch Prune Tags", "Tiles on the end of branches will be deleted depending on which tags they have. Based on the branch prune mode");
- public static readonly GUIContent PathStraighteningHeader = new GUIContent("Path Straightening", "Determines if and how the path should be straightened. These settings can be overridden in the Archetype asset and on Nodes in the flow graph");
- public static readonly GUIContent BranchingHeader = new GUIContent("Branching");
- public static readonly string LocalBranchMode = "In Local mode, the number of branches is calculated per-tile using the Archetype's 'Branch Count' property";
- public static readonly string GlobalBranchMode = "In Global mode, the number of branches is calculated across the entire dungeon. NOTE: The number of branches might be less than the specified minimum value, but will never be more than the maximum";
- public static readonly string SectionBranchMode = "In Section mode, the number of branches is calculated for each section using the 'Branch Count' property in that section's Archetype settings";
- }
- #endregion
- private Properties properties;
- private void OnEnable()
- {
- properties = new Properties()
- {
- Length = serializedObject.FindProperty(nameof(DungeonFlow.Length)),
- BranchMode = serializedObject.FindProperty(nameof(DungeonFlow.BranchMode)),
- BranchCount = serializedObject.FindProperty(nameof(DungeonFlow.BranchCount)),
- KeyManager = serializedObject.FindProperty(nameof(DungeonFlow.KeyManager)),
- DoorwayConnectionChance = serializedObject.FindProperty(nameof(DungeonFlow.DoorwayConnectionChance)),
- RestrictConnectionToSameSection = serializedObject.FindProperty(nameof(DungeonFlow.RestrictConnectionToSameSection)),
- TileInjectionRules = serializedObject.FindProperty(nameof(DungeonFlow.TileInjectionRules)),
- TileTagConnectionMode = serializedObject.FindProperty(nameof(DungeonFlow.TileTagConnectionMode)),
- TileConnectionTags = serializedObject.FindProperty(nameof(DungeonFlow.TileConnectionTags)),
- BranchTagPruneMode = serializedObject.FindProperty(nameof(DungeonFlow.BranchTagPruneMode)),
- BranchPruneTags = serializedObject.FindProperty(nameof(DungeonFlow.BranchPruneTags)),
- StraighteningSettings = serializedObject.FindProperty(nameof(DungeonFlow.GlobalStraighteningSettings)),
- GlobalProps = new ReorderableList(serializedObject, serializedObject.FindProperty("GlobalProps"), true, false, true, true)
- {
- drawElementCallback = (rect, index, isActive, isFocused) => DrawGlobalProp(rect, index),
- elementHeightCallback = GetGlobalPropHeight,
- },
- };
- properties.TileConnectionTagsList = new ReorderableList(serializedObject, properties.TileConnectionTags)
- {
- drawHeaderCallback = DrawTileConnectionTagsHeader,
- drawElementCallback = DrawTileConnectionTagsElement,
- };
- properties.BranchPruneTagsList = new ReorderableList(serializedObject, properties.BranchPruneTags)
- {
- drawHeaderCallback = (Rect rect) =>
- {
- EditorGUI.LabelField(rect, Labels.BranchPruneTags);
- },
- drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
- {
- EditorGUI.PropertyField(rect, properties.BranchPruneTags.GetArrayElementAtIndex(index), GUIContent.none);
- },
- };
- properties.TileInjectionRulesList = new ReorderableList(serializedObject, properties.TileInjectionRules, true, true, true, true)
- {
- drawHeaderCallback = rect =>
- {
- EditorGUI.LabelField(rect, Labels.TileInjection);
- },
- drawElementCallback = DrawTileInjectionRule,
- elementHeightCallback = index =>
- {
- var element = properties.TileInjectionRules.GetArrayElementAtIndex(index);
- // If collapsed, just one line
- if (!element.isExpanded)
- return EditorGUIUtility.singleLineHeight + 6;
- // If expanded, enough lines for all fields
- int lines = 8;
- return (lines + 1) * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) + 4;
- }
- };
- var flow = target as DungeonFlow;
- if (flow != null)
- {
- foreach (var line in flow.Lines)
- line.Graph = flow;
- foreach (var node in flow.Nodes)
- node.Graph = flow;
- }
- }
- private void DrawTileConnectionTagsHeader(Rect rect)
- {
- EditorGUI.LabelField(rect, Labels.TileConnectionTags);
- }
- private void DrawTileConnectionTagsElement(Rect rect, int index, bool isActive, bool isFocused)
- {
- EditorGUI.PropertyField(rect, properties.TileConnectionTags.GetArrayElementAtIndex(index), GUIContent.none);
- }
- private void DrawTileInjectionRule(Rect rect, int index, bool isActive, bool isFocused)
- {
- var element = properties.TileInjectionRules.GetArrayElementAtIndex(index);
- rect.y += 2;
- float lineHeight = EditorGUIUtility.singleLineHeight;
- float spacing = EditorGUIUtility.standardVerticalSpacing;
- var tileSetProp = element.FindPropertyRelative(nameof(TileInjectionRule.TileSet));
- string tileSetName = "None";
- if (tileSetProp != null && tileSetProp.objectReferenceValue != null)
- tileSetName = tileSetProp.objectReferenceValue.name;
- // Foldout
- element.isExpanded = EditorGUI.Foldout(
- new Rect(rect.x + 10, rect.y, rect.width, lineHeight),
- element.isExpanded,
- new GUIContent(tileSetName),
- true);
- if (!element.isExpanded)
- return;
- float y = rect.y + lineHeight + spacing;
- // TileSet
- EditorGUI.PropertyField(
- new Rect(rect.x, y, rect.width, lineHeight),
- tileSetProp, new GUIContent("Tile Set"));
- y += lineHeight + spacing;
- // IsRequired
- var isRequiredProp = element.FindPropertyRelative("IsRequired");
- EditorGUI.PropertyField(
- new Rect(rect.x, y, rect.width, lineHeight),
- isRequiredProp, new GUIContent("Is Required?"));
- y += lineHeight + spacing;
- // CanAppearOnMainPath
- var canAppearOnMainPathProp = element.FindPropertyRelative("CanAppearOnMainPath");
- EditorGUI.PropertyField(
- new Rect(rect.x, y, rect.width, lineHeight),
- canAppearOnMainPathProp, new GUIContent("Can appear on Main Path?"));
- y += lineHeight + spacing;
- // CanAppearOnBranchPath
- var canAppearOnBranchPathProp = element.FindPropertyRelative("CanAppearOnBranchPath");
- EditorGUI.PropertyField(
- new Rect(rect.x, y, rect.width, lineHeight),
- canAppearOnBranchPathProp, new GUIContent("Can appear on Branch Path?"));
- y += lineHeight + spacing;
- // IsLocked
- var isLockedProp = element.FindPropertyRelative("IsLocked");
- EditorGUI.PropertyField(
- new Rect(rect.x, y, rect.width, lineHeight),
- isLockedProp, new GUIContent("Locked"));
- y += lineHeight + spacing;
- // LockID
- EditorGUI.BeginDisabledGroup(isLockedProp == null || !isLockedProp.boolValue);
- {
- var lockIDProp = element.FindPropertyRelative("LockID");
- var dungeonFlow = target as DungeonFlow;
- int keyID = lockIDProp.intValue;
- EditorGUI.BeginChangeCheck();
- EditorUtil.DrawKey(
- new Rect(rect.x, y, rect.width, lineHeight),
- new GUIContent("Lock Type"), dungeonFlow.KeyManager, ref keyID);
- if (EditorGUI.EndChangeCheck())
- lockIDProp.intValue = keyID;
- }
- EditorGUI.EndDisabledGroup();
- y += lineHeight + spacing;
- // NormalizedPathDepth
- var pathDepthProp = element.FindPropertyRelative("NormalizedPathDepth");
- EditorGUI.PropertyField(
- new Rect(rect.x, y, rect.width, lineHeight),
- pathDepthProp, new GUIContent("Path Depth"));
- y += lineHeight + spacing;
- // NormalizedBranchDepth
- var branchDepthProp = element.FindPropertyRelative("NormalizedBranchDepth");
- EditorGUI.PropertyField(
- new Rect(rect.x, y, rect.width, lineHeight),
- branchDepthProp, new GUIContent("Branch Depth"));
- }
- private string GetCurrentBranchModeLabel()
- {
- var dungeonFlow = target as DungeonFlow;
- switch (dungeonFlow.BranchMode)
- {
- case BranchMode.Local:
- return Labels.LocalBranchMode;
- case BranchMode.Global:
- return Labels.GlobalBranchMode;
- case BranchMode.Section:
- return Labels.SectionBranchMode;
- default:
- throw new NotImplementedException(string.Format("{0}.{1} is not implemented", typeof(BranchMode).Name, dungeonFlow.BranchMode));
- }
- }
- public override void OnInspectorGUI()
- {
- var data = target as DungeonFlow;
- if (data == null)
- return;
- serializedObject.Update();
- if (GUILayout.Button(Labels.Validate))
- DungeonValidator.Instance.Validate(data);
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- EditorGUILayout.PropertyField(properties.KeyManager, Labels.KeyManager);
- EditorGUILayout.PropertyField(properties.Length, Labels.Length);
- // Doorway Connections
- using (new EditorGUILayout.VerticalScope("box"))
- {
- EditorGUILayout.LabelField(Labels.DoorwayConnectionHeader, EditorStyles.boldLabel);
- EditorGUILayout.PropertyField(properties.DoorwayConnectionChance, Labels.DoorwayConnectionChance);
- EditorGUILayout.PropertyField(properties.RestrictConnectionToSameSection, Labels.RestrictConnectionToSameSection);
- }
- // Straightening Section
- using (new EditorGUILayout.VerticalScope("box"))
- {
- EditorGUILayout.LabelField(Labels.PathStraighteningHeader, EditorStyles.boldLabel);
- EditorUtil.DrawStraightenSettings(properties.StraighteningSettings, true);
- }
- // Branches Section
- using (new EditorGUILayout.VerticalScope("box"))
- {
- EditorGUILayout.LabelField(Labels.BranchingHeader, EditorStyles.boldLabel);
- // Branch Mode
- EditorGUILayout.HelpBox(GetCurrentBranchModeLabel(), MessageType.Info);
- EditorGUILayout.PropertyField(properties.BranchMode, Labels.BranchMode);
- EditorGUI.BeginDisabledGroup(data.BranchMode != BranchMode.Global);
- EditorGUILayout.PropertyField(properties.BranchCount, Labels.BranchCount);
- EditorGUI.EndDisabledGroup();
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- // Branch Prune Tags
- EditorGUILayout.PropertyField(properties.BranchTagPruneMode, Labels.BranchPruneMode);
- EditorGUILayout.Space();
- properties.BranchPruneTagsList.DoLayoutList();
- }
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- // Open Flow Editor
- if (GUILayout.Button(Labels.OpenFlowEditor))
- DungeonFlowEditorWindow.Open(data);
- EditorGUILayout.Space();
- // Tile Injection Rules (ReorderableList)
- properties.TileInjectionRulesList.DoLayoutList();
- EditorGUILayout.Space();
- // Global Props
- var globalProps = properties.GlobalProps.serializedProperty;
- globalProps.isExpanded = EditorGUILayout.Foldout(globalProps.isExpanded, Labels.GlobalProps, true);
- if (globalProps.isExpanded)
- {
- EditorGUI.indentLevel++;
- properties.GlobalProps.DoLayoutList();
- EditorGUI.indentLevel--;
- }
- EditorGUILayout.Space();
- // Tile Connection Rules
- properties.TileConnectionTags.isExpanded = EditorGUILayout.Foldout(properties.TileConnectionTags.isExpanded, Labels.TileConnectionRules);
- if (properties.TileConnectionTags.isExpanded)
- {
- EditorGUI.indentLevel++;
- EditorGUILayout.Space();
- EditorGUILayout.PropertyField(properties.TileTagConnectionMode, Labels.TileConnectionTagMode);
- EditorGUILayout.Space();
- properties.TileConnectionTagsList.DoLayoutList();
- EditorGUI.indentLevel--;
- }
- if (GUI.changed)
- EditorUtility.SetDirty(data);
- serializedObject.ApplyModifiedProperties();
- }
- private float GetGlobalPropHeight(int index)
- {
- return EditorGUI.GetPropertyHeight(properties.GlobalProps.serializedProperty.GetArrayElementAtIndex(index));
- }
- private void DrawGlobalProp(Rect rect, int index)
- {
- var propProperty = properties.GlobalProps.serializedProperty.GetArrayElementAtIndex(index);
- EditorGUI.PropertyField(rect, propProperty);
- }
- }
- }
|