using System.Text; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditorInternal; namespace HQFPSWeapons { public class ItemManagementWindow : EditorWindow { /// /// This is a hack for avoiding an issue with the ReorderableList's DrawHeader method. /// public static bool DrawingItemWindow { get; private set; } public enum Tab { ItemEditor, PropertyEditor } private const float DESCRIPTION_HEIGHT = 54f; private const float PROPERTY_HEIGHT = 40f; private Tab m_SelectedTab; private SerializedObject m_ItemDatabase; private ReorderableList m_CategoryList; private ReorderableList m_PropertyList; private Vector2 m_CategoriesScrollPos; private Vector2 m_TypesScrollPos; private Vector2 m_PropsScrollPos; private Vector2 m_ItemsScrollPos; private Vector2 m_ItemInspectorScrollPos; private ReorderableList m_ItemList; //private ReorderableList m_CurItemDescriptions; private ReorderableList m_CurItemProperties; private ReorderableList m_CurItemRequiredItems; private string[] m_ItemNamesFull; private string[] m_ItemNames; [MenuItem("Tools/HQ FPS Weapons Pack/Item Management...", false, 6)] public static void Init() { EditorWindow.GetWindow(true, "Item Management"); } public void OnGUI() { DrawingItemWindow = true; if(m_ItemDatabase == null) { EditorGUILayout.HelpBox("No ItemDatabase was found in the Resources folder!", MessageType.Error); if(GUILayout.Button("Refresh")) InitializeWindow(); if(m_ItemDatabase == null) return; } GUIStyle richTextStyle = new GUIStyle() { richText = true, alignment = TextAnchor.UpperRight }; // Display the database path EditorGUILayout.LabelField(string.Format("Database path: '{0}'", AssetDatabase.GetAssetPath(m_ItemDatabase.targetObject))); // Display the shortcuts EditorGUI.LabelField(new Rect(position.width - 262f, 0f, 256f, 16f), "Shift + D to duplicate", richTextStyle); EditorGUI.LabelField(new Rect(position.width - 262f, 16f, 256f, 16f), "Delete to delete", richTextStyle); Vector2 buttonSize = new Vector2(192f, 32f); float topPadding = 32f; // Draw the "Item Editor" button. Rect itemEditorButtonRect = new Rect(position.width * 0.38f - buttonSize.x / 2f, topPadding, buttonSize.x, buttonSize.y); if(m_SelectedTab == Tab.ItemEditor) UnityEngine.GUI.backgroundColor = Color.grey; else UnityEngine.GUI.backgroundColor = Color.white; if(UnityEngine.GUI.Button(itemEditorButtonRect, "Item Editor")) m_SelectedTab = Tab.ItemEditor; // Draw the "Property Editor" button. Rect propertyEditorButtonRect = new Rect(position.width * 0.62f - buttonSize.x / 2f, topPadding, buttonSize.x, buttonSize.y); if(m_SelectedTab == Tab.PropertyEditor) UnityEngine.GUI.backgroundColor = Color.grey; else UnityEngine.GUI.backgroundColor = Color.white; if(UnityEngine.GUI.Button(propertyEditorButtonRect, "Property Editor")) m_SelectedTab = Tab.PropertyEditor; // Reset the bg color. UnityEngine.GUI.backgroundColor = Color.white; // Horizontal line. UnityEngine.GUI.Box(new Rect(0f, topPadding + buttonSize.y * 1.25f, position.width, 1f), ""); // Draw the item / recipe editors. m_ItemDatabase.Update(); float innerWindowPadding = 8f; Rect innerWindowRect = new Rect(innerWindowPadding, topPadding + buttonSize.y * 1.25f + innerWindowPadding, position.width - innerWindowPadding * 2f, position.height - (topPadding + buttonSize.y * 1.25f + innerWindowPadding * 4.5f)); // Inner window box. UnityEngine.GUI.backgroundColor = Color.grey; UnityEngine.GUI.Box(innerWindowRect, ""); UnityEngine.GUI.backgroundColor = Color.white; if(m_SelectedTab == Tab.ItemEditor) DrawItemEditor(innerWindowRect); else if(m_SelectedTab == Tab.PropertyEditor) DrawPropertyEditor(innerWindowRect); m_ItemDatabase.ApplyModifiedProperties(); DrawingItemWindow = false; } private void OnEnable() { InitializeWindow(); Undo.undoRedoPerformed += Repaint; } private void InitializeWindow() { var database = Resources.LoadAll("")[0]; if(database) { m_ItemDatabase = new SerializedObject(database); m_CategoryList = new ReorderableList(m_ItemDatabase, m_ItemDatabase.FindProperty("m_Categories"), true, true ,true ,true); m_CategoryList.drawElementCallback += DrawCategory; m_CategoryList.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); }; m_CategoryList.onSelectCallback += On_SelectedCategory; m_CategoryList.onRemoveCallback = (ReorderableList list)=> { m_CategoryList.serializedProperty.DeleteArrayElementAtIndex(m_CategoryList.index); }; m_PropertyList = new ReorderableList(m_ItemDatabase, m_ItemDatabase.FindProperty("m_ItemProperties"), true, true, true, true); m_PropertyList.drawElementCallback += DrawItemPropertyDefinition; m_PropertyList.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); }; } } private void On_SelectedCategory(ReorderableList list) { m_ItemList = new ReorderableList(m_ItemDatabase, m_CategoryList.serializedProperty.GetArrayElementAtIndex(m_CategoryList.index).FindPropertyRelative("m_Items"), true, true, true, true); m_ItemList.drawElementCallback += DrawItem; m_ItemList.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); }; m_ItemList.onSelectCallback += On_SelectedItem; m_ItemList.onRemoveCallback = (ReorderableList l)=> { m_ItemList.serializedProperty.DeleteArrayElementAtIndex(m_ItemList.index); }; m_ItemList.onChangedCallback += On_SelectedItem; } private void On_SelectedItem(ReorderableList list) { if(m_ItemList == null || m_ItemList.count == 0 || m_ItemList.index == -1 || m_ItemList.index >= m_ItemList.count) return; m_ItemNames = ItemManagementUtility.GetItemNames(m_CategoryList.serializedProperty); m_ItemNamesFull = ItemManagementUtility.GetItemNamesFull(m_CategoryList.serializedProperty); m_CurItemProperties = new ReorderableList(m_ItemDatabase, m_ItemList.serializedProperty.GetArrayElementAtIndex(m_ItemList.index).FindPropertyRelative("m_PropertyValues"), true, true, true, true); m_CurItemProperties.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); }; m_CurItemProperties.drawElementCallback += DrawItemPropertyValue; m_CurItemProperties.elementHeight = PROPERTY_HEIGHT; } private void DrawItemPropertyValue(Rect rect, int index, bool isActive, bool isFocused) { var list = m_CurItemProperties; if(list.serializedProperty.arraySize == index) return; var element = list.serializedProperty.GetArrayElementAtIndex(index); rect.y += 2f; rect.height -= 2f; ItemManagementUtility.DrawItemProperty(rect, element, m_PropertyList); ItemManagementUtility.DoListElementBehaviours(list, index, isFocused, this); } private void DrawItemEditor(Rect totalRect) { // Inner window cross (partitioning in 4 smaller boxes) UnityEngine.GUI.Box(new Rect(totalRect.x, totalRect.y + totalRect.height * 0.5f, totalRect.width / 2f, 1f), ""); UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.5f, totalRect.y, 1f, totalRect.height), ""); Vector2 labelSize = new Vector2(192f, 20f); // Draw the item list. string itemListName = string.Format("Item List ({0})", (m_CategoryList.count == 0 || m_CategoryList.index == -1) ? "None" : m_CategoryList.serializedProperty.GetArrayElementAtIndex(m_CategoryList.index).FindPropertyRelative("m_Name").stringValue); UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.25f - labelSize.x * 0.5f, totalRect.y, labelSize.x, labelSize.y), itemListName); Rect itemListRect = new Rect(totalRect.x, totalRect.y + labelSize.y, totalRect.width * 0.5f - 2f, totalRect.height * 0.5f - labelSize.y - 1f); if(m_CategoryList.count != 0 && m_CategoryList.index != -1 && m_CategoryList.index < m_CategoryList.count) DrawList(m_ItemList, itemListRect, ref m_ItemsScrollPos); else { itemListRect.x -= 6f; //UnityEngine.GUI.Label(itemListRect, "Select a category...", new GUIStyle() { fontStyle = FontStyle.BoldAndItalic }); UnityEngine.GUI.Box(new Rect(itemListRect.x + itemListRect.width * 0.25f - labelSize.x * 0.5f, totalRect.y, labelSize.x / 1.2f, labelSize.y), "Select a category..."); } // Draw the categories. UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.25f - labelSize.x * 0.5f, totalRect.y + totalRect.height * 0.5f + 2f, labelSize.x, labelSize.y), "Category List"); DrawList(m_CategoryList, new Rect(totalRect.x, totalRect.y + totalRect.height * 0.5f + labelSize.y + 2f, totalRect.width * 0.5f - 2f, totalRect.height * 0.5f - labelSize.y - 3f), ref m_CategoriesScrollPos); // Inspector label. UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.75f - labelSize.x * 0.5f, totalRect.y, labelSize.x, labelSize.y), "Item Inspector"); // Draw the inspector. bool itemIsSelected = m_CategoryList.count != 0 && m_ItemList != null && m_ItemList.count != 0 && m_ItemList.index != -1 && m_ItemList.index < m_ItemList.count; Rect inspectorRect = new Rect(totalRect.x + totalRect.width * 0.5f + 4f, totalRect.y + labelSize.y, totalRect.width * 0.5f - 5f, totalRect.height - labelSize.y - 1f); if(itemIsSelected) DrawItemInspector(inspectorRect); else { inspectorRect.x += 4f; inspectorRect.y += 4f; UnityEngine.GUI.Box(inspectorRect, "Select an item to inspect..."); } } private void DrawList(ReorderableList list, Rect totalRect, ref Vector2 scrollPosition) { float scrollbarWidth = 16f; Rect onlySeenRect = new Rect(totalRect.x, totalRect.y, totalRect.width, totalRect.height); Rect allContentRect = new Rect(totalRect.x, totalRect.y, totalRect.width - scrollbarWidth, (list.count + 4) * list.elementHeight); scrollPosition = UnityEngine.GUI.BeginScrollView(onlySeenRect, scrollPosition, allContentRect, false, true); // Draw the clear button. Vector2 buttonSize = new Vector2(56f, 16f); if(list.count > 0 && UnityEngine.GUI.Button(new Rect(allContentRect.x + 2f, allContentRect.yMax - 60f, buttonSize.x, buttonSize.y), "Clear")) if(EditorUtility.DisplayDialog("Warning!", "Are you sure you want the list to be cleared? (All elements will be deleted)", "Yes", "Cancel")) list.serializedProperty.ClearArray(); list.DoList(allContentRect); UnityEngine.GUI.EndScrollView(); } private void DrawListElement(ReorderableList list, Rect rect, int index, bool isActive, bool isFocused) { if(list.serializedProperty.arraySize == index) return; var element = list.serializedProperty.GetArrayElementAtIndex(index); rect.y += 2; EditorGUI.PropertyField(new Rect(rect.x, rect.y, 256f, EditorGUIUtility.singleLineHeight), element, GUIContent.none); ItemManagementUtility.DoListElementBehaviours(list, index, isFocused, this); } private void DrawCategory(Rect rect, int index, bool isActive, bool isFocused) { ItemManagementUtility.DrawListElementByName(m_CategoryList, index, rect, "m_Name", isFocused, this); } private void DrawItem(Rect rect, int index, bool isActive, bool isFocused) { if(m_ItemList.serializedProperty.arraySize > index) { SerializedProperty item = m_ItemList.serializedProperty.GetArrayElementAtIndex(index); // SerializedProperty displayProp = item.FindPropertyRelative("m_DisplayName"); // string toUse = (displayProp.stringValue == string.Empty) ? "m_Name" : "m_DisplayName"; ItemManagementUtility.DrawListElementByName(m_ItemList, index, rect, "m_Name", isFocused, this); } } private void DrawItemPropertyDefinition(Rect rect, int index, bool isActive, bool isFocused) { DrawListElement(m_PropertyList, rect, index, isActive, isFocused); } private void DrawItemInspector(Rect viewportRect) { var item = m_ItemList.serializedProperty.GetArrayElementAtIndex(m_ItemList.index); GUI.Box(viewportRect, ""); float indentation = 4f; Rect rect = new Rect(viewportRect.x + indentation, viewportRect.y + indentation, viewportRect.width - indentation * 2, viewportRect.height - indentation * 2); m_ItemInspectorScrollPos = GUI.BeginScrollView(viewportRect, m_ItemInspectorScrollPos, new Rect(rect.x, rect.y, rect.width - 16f, 24f + EditorGUI.GetPropertyHeight(item, true))); // Draw item name rect.xMin += indentation; rect.xMax -= 16f; rect.yMin += indentation; GUI.Label(rect, item.FindPropertyRelative("m_Name").stringValue, new GUIStyle() { fontStyle = FontStyle.Bold, fontSize = 20}); // Draw all item fields rect.yMax -= 16f; rect.y += 24f; var properties = item.Copy().GetChildren(); rect.height = EditorGUIUtility.singleLineHeight; rect.y += EditorGUIUtility.standardVerticalSpacing; foreach(var prop in properties) { EditorGUI.PropertyField(rect, prop, true); rect.y += EditorGUI.GetPropertyHeight(prop, true) + EditorGUIUtility.standardVerticalSpacing; } GUI.EndScrollView(); } private void DrawPropertyEditor(Rect totalRect) { Vector2 labelSize = new Vector2(128f, 20f); // Properties label. UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.5f - labelSize.x * 0.5f, totalRect.y, labelSize.x, labelSize.y), "Property List"); // Draw the properties. totalRect.y += 24f; totalRect.height -= 25f; DrawList(m_PropertyList, totalRect, ref m_PropsScrollPos); } } public static class ItemManagementUtility { public static void DoListElementBehaviours(ReorderableList list, int index, bool isFocused, EditorWindow window = null) { var current = Event.current; if(current.type == EventType.KeyDown) { if(list.index == index && isFocused) { if(current.keyCode == KeyCode.Delete) { int newIndex = 0; if(list.count == 1) newIndex = -1; else if(index == list.count - 1) newIndex = index - 1; else if(index > 0) newIndex = index - 1; list.serializedProperty.DeleteArrayElementAtIndex(index); if(newIndex != -1) { list.index = newIndex; if(list.onSelectCallback != null) list.onSelectCallback(list); } Event.current.Use(); if(window) window.Repaint(); } else if(current.shift && current.keyCode == KeyCode.D) { list.serializedProperty.InsertArrayElementAtIndex(list.index); list.index ++; if(list.onSelectCallback != null) list.onSelectCallback(list); Event.current.Use(); if(window) window.Repaint(); } } } } public static string[] GetItemNamesFull(SerializedProperty categoryList) { List names = new List(); for(int i = 0;i < categoryList.arraySize;i ++) { var category = categoryList.GetArrayElementAtIndex(i); var itemList = category.FindPropertyRelative("m_Items"); for(int j = 0;j < itemList.arraySize;j ++) names.Add(category.FindPropertyRelative("m_Name").stringValue + "/" + itemList.GetArrayElementAtIndex(j).FindPropertyRelative("m_Name").stringValue); } return names.ToArray(); } public static string[] GetItemNames(SerializedProperty categoryList) { List names = new List(); for(int i = 0;i < categoryList.arraySize;i ++) { var category = categoryList.GetArrayElementAtIndex(i); var itemList = category.FindPropertyRelative("m_Items"); for(int j = 0;j < itemList.arraySize;j ++) names.Add(itemList.GetArrayElementAtIndex(j).FindPropertyRelative("m_Name").stringValue); } return names.ToArray(); } public static int GetItemIndex(SerializedProperty categoryList, string itemName) { int index = 0; for(int i = 0;i < categoryList.arraySize;i ++) { var category = categoryList.GetArrayElementAtIndex(i); var itemList = category.FindPropertyRelative("m_Items"); for(int j = 0;j < itemList.arraySize;j ++) { var name = itemList.GetArrayElementAtIndex(j).FindPropertyRelative("m_Name").stringValue; if(name == itemName) return index; index ++; } } return -1; } public static void DrawListElementByName(ReorderableList list, int index, Rect rect, string nameProperty, bool isFocused, EditorWindow window) { if(list.serializedProperty.arraySize == index) return; rect.y += 2; var element = list.serializedProperty.GetArrayElementAtIndex(index); var name = element.FindPropertyRelative(nameProperty); name.stringValue = EditorGUI.TextField(new Rect(rect.x, rect.y, 256f, 16f), name.stringValue); DoListElementBehaviours(list, index, isFocused, window); } public static void DrawItemProperty(Rect rect, SerializedProperty itemProperty, ReorderableList propertyList) { var name = itemProperty.FindPropertyRelative("m_Name"); float initialX = rect.x; // Source label. rect.width = 64f; rect.height = 16f; UnityEngine.GUI.Label(rect, "Property: "); // Source popup. var allProperties = GetStringNames(propertyList.serializedProperty, "m_Name"); if(allProperties.Length == 0) return; rect.x = rect.xMax; rect.width = 128f; int selectedIndex = GetStringIndex(name.stringValue, allProperties); selectedIndex = EditorGUI.Popup(rect, selectedIndex, allProperties); name.stringValue = allProperties[selectedIndex]; // Value label. rect.x = initialX; rect.width = 64f; rect.y = rect.yMax + 4f; UnityEngine.GUI.Label(rect, "Value: "); // Editing the value based on the type. rect.x = rect.xMax; DrawFloatProperty(rect, itemProperty.FindPropertyRelative("m_Float")); } public static string[] GetStringNames(SerializedProperty property, string subProperty = "") { List strings = new List(); for(int i = 0;i < property.arraySize;i ++) { if(subProperty == "") strings.Add(property.GetArrayElementAtIndex(i).stringValue); else strings.Add(property.GetArrayElementAtIndex(i).FindPropertyRelative(subProperty).stringValue); } return strings.ToArray(); } public static int GetStringIndex(string str, string[] strings) { for(int i = 0;i < strings.Length;i ++) if(strings[i] == str) return i; return 0; } private static void DrawFloatProperty(Rect position, SerializedProperty property) { var current = property.FindPropertyRelative("m_Current"); var defaultVal = property.FindPropertyRelative("m_Default"); current.floatValue = EditorGUI.FloatField(position, current.floatValue); defaultVal.floatValue = current.floatValue; } } }