ItemManagementWindow.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. using System.Text;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using UnityEditorInternal;
  6. namespace HQFPSWeapons
  7. {
  8. public class ItemManagementWindow : EditorWindow
  9. {
  10. /// <summary>
  11. /// This is a hack for avoiding an issue with the ReorderableList's DrawHeader method.
  12. /// </summary>
  13. public static bool DrawingItemWindow { get; private set; }
  14. public enum Tab { ItemEditor, PropertyEditor }
  15. private const float DESCRIPTION_HEIGHT = 54f;
  16. private const float PROPERTY_HEIGHT = 40f;
  17. private Tab m_SelectedTab;
  18. private SerializedObject m_ItemDatabase;
  19. private ReorderableList m_CategoryList;
  20. private ReorderableList m_PropertyList;
  21. private Vector2 m_CategoriesScrollPos;
  22. private Vector2 m_TypesScrollPos;
  23. private Vector2 m_PropsScrollPos;
  24. private Vector2 m_ItemsScrollPos;
  25. private Vector2 m_ItemInspectorScrollPos;
  26. private ReorderableList m_ItemList;
  27. //private ReorderableList m_CurItemDescriptions;
  28. private ReorderableList m_CurItemProperties;
  29. private ReorderableList m_CurItemRequiredItems;
  30. private string[] m_ItemNamesFull;
  31. private string[] m_ItemNames;
  32. [MenuItem("Tools/HQ FPS Weapons Pack/Item Management...", false, 6)]
  33. public static void Init()
  34. {
  35. EditorWindow.GetWindow<ItemManagementWindow>(true, "Item Management");
  36. }
  37. public void OnGUI()
  38. {
  39. DrawingItemWindow = true;
  40. if(m_ItemDatabase == null)
  41. {
  42. EditorGUILayout.HelpBox("No ItemDatabase was found in the Resources folder!", MessageType.Error);
  43. if(GUILayout.Button("Refresh"))
  44. InitializeWindow();
  45. if(m_ItemDatabase == null)
  46. return;
  47. }
  48. GUIStyle richTextStyle = new GUIStyle() { richText = true, alignment = TextAnchor.UpperRight };
  49. // Display the database path
  50. EditorGUILayout.LabelField(string.Format("Database path: '{0}'", AssetDatabase.GetAssetPath(m_ItemDatabase.targetObject)));
  51. // Display the shortcuts
  52. EditorGUI.LabelField(new Rect(position.width - 262f, 0f, 256f, 16f), "<b>Shift + D</b> to duplicate", richTextStyle);
  53. EditorGUI.LabelField(new Rect(position.width - 262f, 16f, 256f, 16f), "<b>Delete</b> to delete", richTextStyle);
  54. Vector2 buttonSize = new Vector2(192f, 32f);
  55. float topPadding = 32f;
  56. // Draw the "Item Editor" button.
  57. Rect itemEditorButtonRect = new Rect(position.width * 0.38f - buttonSize.x / 2f, topPadding, buttonSize.x, buttonSize.y);
  58. if(m_SelectedTab == Tab.ItemEditor)
  59. UnityEngine.GUI.backgroundColor = Color.grey;
  60. else
  61. UnityEngine.GUI.backgroundColor = Color.white;
  62. if(UnityEngine.GUI.Button(itemEditorButtonRect, "Item Editor"))
  63. m_SelectedTab = Tab.ItemEditor;
  64. // Draw the "Property Editor" button.
  65. Rect propertyEditorButtonRect = new Rect(position.width * 0.62f - buttonSize.x / 2f, topPadding, buttonSize.x, buttonSize.y);
  66. if(m_SelectedTab == Tab.PropertyEditor)
  67. UnityEngine.GUI.backgroundColor = Color.grey;
  68. else
  69. UnityEngine.GUI.backgroundColor = Color.white;
  70. if(UnityEngine.GUI.Button(propertyEditorButtonRect, "Property Editor"))
  71. m_SelectedTab = Tab.PropertyEditor;
  72. // Reset the bg color.
  73. UnityEngine.GUI.backgroundColor = Color.white;
  74. // Horizontal line.
  75. UnityEngine.GUI.Box(new Rect(0f, topPadding + buttonSize.y * 1.25f, position.width, 1f), "");
  76. // Draw the item / recipe editors.
  77. m_ItemDatabase.Update();
  78. float innerWindowPadding = 8f;
  79. 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));
  80. // Inner window box.
  81. UnityEngine.GUI.backgroundColor = Color.grey;
  82. UnityEngine.GUI.Box(innerWindowRect, "");
  83. UnityEngine.GUI.backgroundColor = Color.white;
  84. if(m_SelectedTab == Tab.ItemEditor)
  85. DrawItemEditor(innerWindowRect);
  86. else if(m_SelectedTab == Tab.PropertyEditor)
  87. DrawPropertyEditor(innerWindowRect);
  88. m_ItemDatabase.ApplyModifiedProperties();
  89. DrawingItemWindow = false;
  90. }
  91. private void OnEnable()
  92. {
  93. InitializeWindow();
  94. Undo.undoRedoPerformed += Repaint;
  95. }
  96. private void InitializeWindow()
  97. {
  98. var database = Resources.LoadAll<ItemDatabase>("")[0];
  99. if(database)
  100. {
  101. m_ItemDatabase = new SerializedObject(database);
  102. m_CategoryList = new ReorderableList(m_ItemDatabase, m_ItemDatabase.FindProperty("m_Categories"), true, true ,true ,true);
  103. m_CategoryList.drawElementCallback += DrawCategory;
  104. m_CategoryList.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); };
  105. m_CategoryList.onSelectCallback += On_SelectedCategory;
  106. m_CategoryList.onRemoveCallback = (ReorderableList list)=> { m_CategoryList.serializedProperty.DeleteArrayElementAtIndex(m_CategoryList.index); };
  107. m_PropertyList = new ReorderableList(m_ItemDatabase, m_ItemDatabase.FindProperty("m_ItemProperties"), true, true, true, true);
  108. m_PropertyList.drawElementCallback += DrawItemPropertyDefinition;
  109. m_PropertyList.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); };
  110. }
  111. }
  112. private void On_SelectedCategory(ReorderableList list)
  113. {
  114. m_ItemList = new ReorderableList(m_ItemDatabase, m_CategoryList.serializedProperty.GetArrayElementAtIndex(m_CategoryList.index).FindPropertyRelative("m_Items"), true, true, true, true);
  115. m_ItemList.drawElementCallback += DrawItem;
  116. m_ItemList.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); };
  117. m_ItemList.onSelectCallback += On_SelectedItem;
  118. m_ItemList.onRemoveCallback = (ReorderableList l)=> { m_ItemList.serializedProperty.DeleteArrayElementAtIndex(m_ItemList.index); };
  119. m_ItemList.onChangedCallback += On_SelectedItem;
  120. }
  121. private void On_SelectedItem(ReorderableList list)
  122. {
  123. if(m_ItemList == null || m_ItemList.count == 0 || m_ItemList.index == -1 || m_ItemList.index >= m_ItemList.count)
  124. return;
  125. m_ItemNames = ItemManagementUtility.GetItemNames(m_CategoryList.serializedProperty);
  126. m_ItemNamesFull = ItemManagementUtility.GetItemNamesFull(m_CategoryList.serializedProperty);
  127. m_CurItemProperties = new ReorderableList(m_ItemDatabase, m_ItemList.serializedProperty.GetArrayElementAtIndex(m_ItemList.index).FindPropertyRelative("m_PropertyValues"), true, true, true, true);
  128. m_CurItemProperties.drawHeaderCallback = (Rect rect)=> { EditorGUI.LabelField(rect, ""); };
  129. m_CurItemProperties.drawElementCallback += DrawItemPropertyValue;
  130. m_CurItemProperties.elementHeight = PROPERTY_HEIGHT;
  131. }
  132. private void DrawItemPropertyValue(Rect rect, int index, bool isActive, bool isFocused)
  133. {
  134. var list = m_CurItemProperties;
  135. if(list.serializedProperty.arraySize == index)
  136. return;
  137. var element = list.serializedProperty.GetArrayElementAtIndex(index);
  138. rect.y += 2f;
  139. rect.height -= 2f;
  140. ItemManagementUtility.DrawItemProperty(rect, element, m_PropertyList);
  141. ItemManagementUtility.DoListElementBehaviours(list, index, isFocused, this);
  142. }
  143. private void DrawItemEditor(Rect totalRect)
  144. {
  145. // Inner window cross (partitioning in 4 smaller boxes)
  146. UnityEngine.GUI.Box(new Rect(totalRect.x, totalRect.y + totalRect.height * 0.5f, totalRect.width / 2f, 1f), "");
  147. UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.5f, totalRect.y, 1f, totalRect.height), "");
  148. Vector2 labelSize = new Vector2(192f, 20f);
  149. // Draw the item list.
  150. 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);
  151. UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.25f - labelSize.x * 0.5f, totalRect.y, labelSize.x, labelSize.y), itemListName);
  152. Rect itemListRect = new Rect(totalRect.x, totalRect.y + labelSize.y, totalRect.width * 0.5f - 2f, totalRect.height * 0.5f - labelSize.y - 1f);
  153. if(m_CategoryList.count != 0 && m_CategoryList.index != -1 && m_CategoryList.index < m_CategoryList.count)
  154. DrawList(m_ItemList, itemListRect, ref m_ItemsScrollPos);
  155. else
  156. {
  157. itemListRect.x -= 6f;
  158. //UnityEngine.GUI.Label(itemListRect, "Select a category...", new GUIStyle() { fontStyle = FontStyle.BoldAndItalic });
  159. 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...");
  160. }
  161. // Draw the categories.
  162. 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");
  163. 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);
  164. // Inspector label.
  165. UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.75f - labelSize.x * 0.5f, totalRect.y, labelSize.x, labelSize.y), "Item Inspector");
  166. // Draw the inspector.
  167. bool itemIsSelected = m_CategoryList.count != 0 && m_ItemList != null && m_ItemList.count != 0 && m_ItemList.index != -1 && m_ItemList.index < m_ItemList.count;
  168. 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);
  169. if(itemIsSelected)
  170. DrawItemInspector(inspectorRect);
  171. else
  172. {
  173. inspectorRect.x += 4f;
  174. inspectorRect.y += 4f;
  175. UnityEngine.GUI.Box(inspectorRect, "Select an item to inspect...");
  176. }
  177. }
  178. private void DrawList(ReorderableList list, Rect totalRect, ref Vector2 scrollPosition)
  179. {
  180. float scrollbarWidth = 16f;
  181. Rect onlySeenRect = new Rect(totalRect.x, totalRect.y, totalRect.width, totalRect.height);
  182. Rect allContentRect = new Rect(totalRect.x, totalRect.y, totalRect.width - scrollbarWidth, (list.count + 4) * list.elementHeight);
  183. scrollPosition = UnityEngine.GUI.BeginScrollView(onlySeenRect, scrollPosition, allContentRect, false, true);
  184. // Draw the clear button.
  185. Vector2 buttonSize = new Vector2(56f, 16f);
  186. if(list.count > 0 && UnityEngine.GUI.Button(new Rect(allContentRect.x + 2f, allContentRect.yMax - 60f, buttonSize.x, buttonSize.y), "Clear"))
  187. if(EditorUtility.DisplayDialog("Warning!", "Are you sure you want the list to be cleared? (All elements will be deleted)", "Yes", "Cancel"))
  188. list.serializedProperty.ClearArray();
  189. list.DoList(allContentRect);
  190. UnityEngine.GUI.EndScrollView();
  191. }
  192. private void DrawListElement(ReorderableList list, Rect rect, int index, bool isActive, bool isFocused)
  193. {
  194. if(list.serializedProperty.arraySize == index)
  195. return;
  196. var element = list.serializedProperty.GetArrayElementAtIndex(index);
  197. rect.y += 2;
  198. EditorGUI.PropertyField(new Rect(rect.x, rect.y, 256f, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
  199. ItemManagementUtility.DoListElementBehaviours(list, index, isFocused, this);
  200. }
  201. private void DrawCategory(Rect rect, int index, bool isActive, bool isFocused)
  202. {
  203. ItemManagementUtility.DrawListElementByName(m_CategoryList, index, rect, "m_Name", isFocused, this);
  204. }
  205. private void DrawItem(Rect rect, int index, bool isActive, bool isFocused)
  206. {
  207. if(m_ItemList.serializedProperty.arraySize > index)
  208. {
  209. SerializedProperty item = m_ItemList.serializedProperty.GetArrayElementAtIndex(index);
  210. // SerializedProperty displayProp = item.FindPropertyRelative("m_DisplayName");
  211. // string toUse = (displayProp.stringValue == string.Empty) ? "m_Name" : "m_DisplayName";
  212. ItemManagementUtility.DrawListElementByName(m_ItemList, index, rect, "m_Name", isFocused, this);
  213. }
  214. }
  215. private void DrawItemPropertyDefinition(Rect rect, int index, bool isActive, bool isFocused)
  216. {
  217. DrawListElement(m_PropertyList, rect, index, isActive, isFocused);
  218. }
  219. private void DrawItemInspector(Rect viewportRect)
  220. {
  221. var item = m_ItemList.serializedProperty.GetArrayElementAtIndex(m_ItemList.index);
  222. GUI.Box(viewportRect, "");
  223. float indentation = 4f;
  224. Rect rect = new Rect(viewportRect.x + indentation, viewportRect.y + indentation, viewportRect.width - indentation * 2, viewportRect.height - indentation * 2);
  225. m_ItemInspectorScrollPos = GUI.BeginScrollView(viewportRect, m_ItemInspectorScrollPos, new Rect(rect.x, rect.y, rect.width - 16f, 24f + EditorGUI.GetPropertyHeight(item, true)));
  226. // Draw item name
  227. rect.xMin += indentation;
  228. rect.xMax -= 16f;
  229. rect.yMin += indentation;
  230. GUI.Label(rect, item.FindPropertyRelative("m_Name").stringValue, new GUIStyle() { fontStyle = FontStyle.Bold, fontSize = 20});
  231. // Draw all item fields
  232. rect.yMax -= 16f;
  233. rect.y += 24f;
  234. var properties = item.Copy().GetChildren();
  235. rect.height = EditorGUIUtility.singleLineHeight;
  236. rect.y += EditorGUIUtility.standardVerticalSpacing;
  237. foreach(var prop in properties)
  238. {
  239. EditorGUI.PropertyField(rect, prop, true);
  240. rect.y += EditorGUI.GetPropertyHeight(prop, true) + EditorGUIUtility.standardVerticalSpacing;
  241. }
  242. GUI.EndScrollView();
  243. }
  244. private void DrawPropertyEditor(Rect totalRect)
  245. {
  246. Vector2 labelSize = new Vector2(128f, 20f);
  247. // Properties label.
  248. UnityEngine.GUI.Box(new Rect(totalRect.x + totalRect.width * 0.5f - labelSize.x * 0.5f, totalRect.y, labelSize.x, labelSize.y), "Property List");
  249. // Draw the properties.
  250. totalRect.y += 24f;
  251. totalRect.height -= 25f;
  252. DrawList(m_PropertyList, totalRect, ref m_PropsScrollPos);
  253. }
  254. }
  255. public static class ItemManagementUtility
  256. {
  257. public static void DoListElementBehaviours(ReorderableList list, int index, bool isFocused, EditorWindow window = null)
  258. {
  259. var current = Event.current;
  260. if(current.type == EventType.KeyDown)
  261. {
  262. if(list.index == index && isFocused)
  263. {
  264. if(current.keyCode == KeyCode.Delete)
  265. {
  266. int newIndex = 0;
  267. if(list.count == 1)
  268. newIndex = -1;
  269. else if(index == list.count - 1)
  270. newIndex = index - 1;
  271. else if(index > 0)
  272. newIndex = index - 1;
  273. list.serializedProperty.DeleteArrayElementAtIndex(index);
  274. if(newIndex != -1)
  275. {
  276. list.index = newIndex;
  277. if(list.onSelectCallback != null)
  278. list.onSelectCallback(list);
  279. }
  280. Event.current.Use();
  281. if(window)
  282. window.Repaint();
  283. }
  284. else if(current.shift && current.keyCode == KeyCode.D)
  285. {
  286. list.serializedProperty.InsertArrayElementAtIndex(list.index);
  287. list.index ++;
  288. if(list.onSelectCallback != null)
  289. list.onSelectCallback(list);
  290. Event.current.Use();
  291. if(window)
  292. window.Repaint();
  293. }
  294. }
  295. }
  296. }
  297. public static string[] GetItemNamesFull(SerializedProperty categoryList)
  298. {
  299. List<string> names = new List<string>();
  300. for(int i = 0;i < categoryList.arraySize;i ++)
  301. {
  302. var category = categoryList.GetArrayElementAtIndex(i);
  303. var itemList = category.FindPropertyRelative("m_Items");
  304. for(int j = 0;j < itemList.arraySize;j ++)
  305. names.Add(category.FindPropertyRelative("m_Name").stringValue + "/" + itemList.GetArrayElementAtIndex(j).FindPropertyRelative("m_Name").stringValue);
  306. }
  307. return names.ToArray();
  308. }
  309. public static string[] GetItemNames(SerializedProperty categoryList)
  310. {
  311. List<string> names = new List<string>();
  312. for(int i = 0;i < categoryList.arraySize;i ++)
  313. {
  314. var category = categoryList.GetArrayElementAtIndex(i);
  315. var itemList = category.FindPropertyRelative("m_Items");
  316. for(int j = 0;j < itemList.arraySize;j ++)
  317. names.Add(itemList.GetArrayElementAtIndex(j).FindPropertyRelative("m_Name").stringValue);
  318. }
  319. return names.ToArray();
  320. }
  321. public static int GetItemIndex(SerializedProperty categoryList, string itemName)
  322. {
  323. int index = 0;
  324. for(int i = 0;i < categoryList.arraySize;i ++)
  325. {
  326. var category = categoryList.GetArrayElementAtIndex(i);
  327. var itemList = category.FindPropertyRelative("m_Items");
  328. for(int j = 0;j < itemList.arraySize;j ++)
  329. {
  330. var name = itemList.GetArrayElementAtIndex(j).FindPropertyRelative("m_Name").stringValue;
  331. if(name == itemName)
  332. return index;
  333. index ++;
  334. }
  335. }
  336. return -1;
  337. }
  338. public static void DrawListElementByName(ReorderableList list, int index, Rect rect, string nameProperty, bool isFocused, EditorWindow window)
  339. {
  340. if(list.serializedProperty.arraySize == index)
  341. return;
  342. rect.y += 2;
  343. var element = list.serializedProperty.GetArrayElementAtIndex(index);
  344. var name = element.FindPropertyRelative(nameProperty);
  345. name.stringValue = EditorGUI.TextField(new Rect(rect.x, rect.y, 256f, 16f), name.stringValue);
  346. DoListElementBehaviours(list, index, isFocused, window);
  347. }
  348. public static void DrawItemProperty(Rect rect, SerializedProperty itemProperty, ReorderableList propertyList)
  349. {
  350. var name = itemProperty.FindPropertyRelative("m_Name");
  351. float initialX = rect.x;
  352. // Source label.
  353. rect.width = 64f;
  354. rect.height = 16f;
  355. UnityEngine.GUI.Label(rect, "Property: ");
  356. // Source popup.
  357. var allProperties = GetStringNames(propertyList.serializedProperty, "m_Name");
  358. if(allProperties.Length == 0)
  359. return;
  360. rect.x = rect.xMax;
  361. rect.width = 128f;
  362. int selectedIndex = GetStringIndex(name.stringValue, allProperties);
  363. selectedIndex = EditorGUI.Popup(rect, selectedIndex, allProperties);
  364. name.stringValue = allProperties[selectedIndex];
  365. // Value label.
  366. rect.x = initialX;
  367. rect.width = 64f;
  368. rect.y = rect.yMax + 4f;
  369. UnityEngine.GUI.Label(rect, "Value: ");
  370. // Editing the value based on the type.
  371. rect.x = rect.xMax;
  372. DrawFloatProperty(rect, itemProperty.FindPropertyRelative("m_Float"));
  373. }
  374. public static string[] GetStringNames(SerializedProperty property, string subProperty = "")
  375. {
  376. List<string> strings = new List<string>();
  377. for(int i = 0;i < property.arraySize;i ++)
  378. {
  379. if(subProperty == "")
  380. strings.Add(property.GetArrayElementAtIndex(i).stringValue);
  381. else
  382. strings.Add(property.GetArrayElementAtIndex(i).FindPropertyRelative(subProperty).stringValue);
  383. }
  384. return strings.ToArray();
  385. }
  386. public static int GetStringIndex(string str, string[] strings)
  387. {
  388. for(int i = 0;i < strings.Length;i ++)
  389. if(strings[i] == str)
  390. return i;
  391. return 0;
  392. }
  393. private static void DrawFloatProperty(Rect position, SerializedProperty property)
  394. {
  395. var current = property.FindPropertyRelative("m_Current");
  396. var defaultVal = property.FindPropertyRelative("m_Default");
  397. current.floatValue = EditorGUI.FloatField(position, current.floatValue);
  398. defaultVal.floatValue = current.floatValue;
  399. }
  400. }
  401. }