diff --git a/.gitignore b/.gitignore
index 1a753be..d2f4e0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*.suo
*.user
*.sln.docstates
+.vs
# Build results
[Dd]ebug/
diff --git a/Debugger/App.config b/Debugger/App.config
index 75c4622..6a8ba55 100644
--- a/Debugger/App.config
+++ b/Debugger/App.config
@@ -1,5 +1,5 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/Debugger/AppearanceConfig.cs b/Debugger/AppearanceConfig.cs
new file mode 100644
index 0000000..f1e2725
--- /dev/null
+++ b/Debugger/AppearanceConfig.cs
@@ -0,0 +1,151 @@
+using System;
+using ModTools.UI;
+using UnityEngine;
+
+namespace ModTools
+{
+ internal sealed class AppearanceConfig : GUIWindow
+ {
+ private readonly string[] availableFonts;
+ private int selectedFont;
+
+ public AppearanceConfig()
+ : base("Appearance configuration", new Rect(16.0f, 16.0f, 600.0f, 490.0f), Skin, resizable: false)
+ {
+ availableFonts = Font.GetOSInstalledFontNames();
+ selectedFont = Array.IndexOf(availableFonts, MainWindow.Instance.Config.FontName);
+ }
+
+ protected override void DrawWindow()
+ {
+ var config = MainWindow.Instance.Config;
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Label("Font");
+ GUILayout.FlexibleSpace();
+
+ var newSelectedFont = GUIComboBox.Box(selectedFont, availableFonts, "AppearanceSettingsFonts");
+ if (newSelectedFont != selectedFont && newSelectedFont >= 0)
+ {
+ config.FontName = availableFonts[newSelectedFont];
+ selectedFont = newSelectedFont;
+ UpdateFont();
+ }
+
+ GUILayout.EndHorizontal();
+ GUILayout.BeginHorizontal();
+
+ GUILayout.Label("Font size");
+
+ var newFontSize = (int)GUILayout.HorizontalSlider(config.FontSize, 13.0f, 39.0f, GUILayout.Width(256));
+
+ if (newFontSize != config.FontSize)
+ {
+ config.FontSize = newFontSize;
+ UpdateFont();
+ }
+
+ GUILayout.EndHorizontal();
+
+ var newColor = DrawColorControl("Background", config.BackgroundColor);
+ if (newColor != config.BackgroundColor)
+ {
+ config.BackgroundColor = newColor;
+ BgTexture.SetPixel(0, 0, config.BackgroundColor);
+ BgTexture.Apply();
+ }
+
+ newColor = DrawColorControl("Title bar", config.TitleBarColor);
+ if (newColor != config.TitleBarColor)
+ {
+ config.TitleBarColor = newColor;
+ MoveNormalTexture.SetPixel(0, 0, config.TitleBarColor);
+ MoveNormalTexture.Apply();
+
+ MoveHoverTexture.SetPixel(0, 0, config.TitleBarColor * 1.2f);
+ MoveHoverTexture.Apply();
+ }
+
+ config.TitleBarTextColor = DrawColorControl("Title bar text", config.TitleBarTextColor);
+
+ config.GameObjectColor = DrawColorControl("GameObject", config.GameObjectColor);
+ config.EnabledComponentColor = DrawColorControl("Component (enabled)", config.EnabledComponentColor);
+ config.DisabledComponentColor = DrawColorControl("Component (disabled)", config.DisabledComponentColor);
+ config.SelectedComponentColor = DrawColorControl("Selected component", config.SelectedComponentColor);
+ config.KeywordColor = DrawColorControl("Keyword", config.KeywordColor);
+ config.NameColor = DrawColorControl("Member name", config.NameColor);
+ config.TypeColor = DrawColorControl("Member type", config.TypeColor);
+ config.ModifierColor = DrawColorControl("Member modifier", config.ModifierColor);
+ config.MemberTypeColor = DrawColorControl("Field type", config.MemberTypeColor);
+ config.ValueColor = DrawColorControl("Member value", config.ValueColor);
+
+ GUILayout.FlexibleSpace();
+
+ GUILayout.BeginHorizontal();
+
+ GUILayout.FlexibleSpace();
+
+ if (GUILayout.Button("OK", GUILayout.Width(100)))
+ {
+ MainWindow.Instance.SaveConfig();
+ Visible = false;
+ }
+
+ if (GUILayout.Button("Defaults", GUILayout.Width(100)))
+ {
+ ResetDoDefault(config);
+ selectedFont = Array.IndexOf(availableFonts, config.FontName);
+ }
+
+ GUILayout.EndHorizontal();
+ }
+
+ protected override void HandleException(Exception ex)
+ {
+ Logger.Error("Exception in AppearanceConfig - " + ex.Message);
+ Visible = false;
+ }
+
+ private static void ResetDoDefault(ModConfiguration config)
+ {
+ var template = new ModConfiguration();
+
+ config.BackgroundColor = template.BackgroundColor;
+ BgTexture.SetPixel(0, 0, config.BackgroundColor);
+ BgTexture.Apply();
+
+ config.TitleBarColor = template.TitleBarColor;
+ MoveNormalTexture.SetPixel(0, 0, config.TitleBarColor);
+ MoveNormalTexture.Apply();
+
+ MoveHoverTexture.SetPixel(0, 0, config.TitleBarColor * 1.2f);
+ MoveHoverTexture.Apply();
+
+ config.TitleBarTextColor = template.TitleBarTextColor;
+
+ config.GameObjectColor = template.GameObjectColor;
+ config.EnabledComponentColor = template.EnabledComponentColor;
+ config.DisabledComponentColor = template.DisabledComponentColor;
+ config.SelectedComponentColor = template.SelectedComponentColor;
+ config.NameColor = template.NameColor;
+ config.TypeColor = template.TypeColor;
+ config.ModifierColor = template.ModifierColor;
+ config.MemberTypeColor = template.MemberTypeColor;
+ config.ValueColor = template.ValueColor;
+ config.FontName = template.FontName;
+ config.FontSize = template.FontSize;
+
+ UpdateFont();
+ }
+
+ private Color DrawColorControl(string name, Color value)
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Label(name);
+ GUILayout.FlexibleSpace();
+ var newColor = GUIControls.CustomValueField(name, string.Empty, GUIControls.PresentColor, value);
+ GUILayout.EndHorizontal();
+ return newColor;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/Configuration.cs b/Debugger/Configuration.cs
deleted file mode 100644
index 600a4fd..0000000
--- a/Debugger/Configuration.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using System;
-using System.IO;
-using System.Xml.Serialization;
-using UnityEngine;
-
-namespace ModTools
-{
-
- public class Configuration
- {
-
- public Rect mainWindowRect = new Rect(128, 128, 356, 300);
- public bool mainWindowVisible = false;
-
- public Rect consoleRect = new Rect(16.0f, 16.0f, 512.0f, 256.0f);
- public bool consoleVisible = false;
-
- public int consoleMaxHistoryLength = 1024;
- public string consoleFormatString = "[{{type}}] {{caller}}: {{message}}";
- public bool showConsoleOnMessage = false;
- public bool showConsoleOnWarning = false;
- public bool showConsoleOnError = true;
- public bool consoleAutoScrollToBottom = true;
-
- public bool customPrefabsObject = true;
-
- public Rect sceneExplorerRect = new Rect(128, 440, 800, 500);
- public bool sceneExplorerVisible = false;
-
- public Rect watchesRect = new Rect(504, 128, 800, 300);
- public bool watchesVisible = false;
-
- public bool logExceptionsToConsole = true;
- public bool evaluatePropertiesAutomatically = true;
- public bool extendGamePanels = true;
- public bool useModToolsConsole = true;
-
- public Color backgroundColor = new Color(0.321f, 0.321f, 0.321f, 1.0f);
- public Color titlebarColor = new Color(0.247f, 0.282f, 0.364f, 1.0f);
- public Color titlebarTextColor = new Color(0.85f, 0.85f, 0.85f, 1.0f);
-
- public Color gameObjectColor = new Color(165.0f / 255.0f, 186.0f / 255.0f, 229.0f / 255.0f, 1.0f);
- public Color enabledComponentColor = Color.white;
- public Color disabledComponentColor = new Color(127.0f / 255.0f, 127.0f / 255.0f, 127.0f / 255.0f, 1.0f);
- public Color selectedComponentColor = new Color(233.0f / 255.0f, 138.0f / 255.0f, 23.0f / 255.0f, 1.0f);
-
- public Color nameColor = new Color(148.0f / 255.0f, 196.0f / 255.0f, 238.0f / 255.0f, 1.0f);
- public Color typeColor = new Color(58.0f / 255.0f, 179.0f / 255.0f, 58.0f / 255.0f, 1.0f);
- public Color keywordColor = new Color(233.0f / 255.0f, 102.0f / 255.0f, 47.0f / 255.0f, 1.0f);
- public Color modifierColor = new Color(84.0f / 255.0f, 109.0f / 255.0f, 57.0f / 255.0f, 1.0f);
- public Color memberTypeColor = new Color(86.0f / 255.0f, 127.0f / 255.0f, 68.0f / 255.0f, 1.0f);
- public Color valueColor = Color.white;
-
- public Color consoleMessageColor = Color.white;
- public Color consoleWarningColor = Color.yellow;
- public Color consoleErrorColor = new Color(0.7f, 0.1f, 0.1f, 1.0f);
- public Color consoleExceptionColor = new Color(1.0f, 0.0f, 0.0f, 1.0f);
-
- public bool sceneExplorerShowFields = true;
- public bool sceneExplorerShowProperties = true;
- public bool sceneExplorerShowMethods = false;
- public bool sceneExplorerShowModifiers = false;
- public bool sceneExplorerShowInheritedMembers = false;
- public bool sceneExplorerEvaluatePropertiesAutomatically = true;
- public bool sceneExplorerSortAlphabetically = true;
- public float sceneExplorerTreeIdentSpacing = 16.0f;
- public int sceneExplorerMaxHierarchyDepth = 32;
-
-
- public string scriptEditorWorkspacePath = "";
-
- public string fontName = "Courier New Bold";
- public int fontSize = 14;
-
- public int hiddenNotifications = 0;
- public int logLevel = 0;
-
- public void OnPreSerialize()
- {
-
- }
-
- public void OnPostDeserialize()
- {
-
- }
-
- public static void Serialize(string filename, Configuration config)
- {
- var serializer = new XmlSerializer(typeof(Configuration));
-
- using (var writer = new StreamWriter(filename))
- {
- config.OnPreSerialize();
- serializer.Serialize(writer, config);
- }
- }
-
- public static Configuration Deserialize(string filename)
- {
- var serializer = new XmlSerializer(typeof(Configuration));
-
- try
- {
- using (var reader = new StreamReader(filename))
- {
- var config = (Configuration) serializer.Deserialize(reader);
- config.OnPostDeserialize();
- return config;
- }
- }
- catch (Exception e)
- {
- UnityEngine.Debug.LogError("Error happened when deserializing config");
- UnityEngine.Debug.LogException(e);
- }
-
- return null;
- }
- }
-
-}
diff --git a/Debugger/Console/ConsoleMessage.cs b/Debugger/Console/ConsoleMessage.cs
new file mode 100644
index 0000000..2fd905a
--- /dev/null
+++ b/Debugger/Console/ConsoleMessage.cs
@@ -0,0 +1,27 @@
+using System.Diagnostics;
+using UnityEngine;
+
+namespace ModTools.Console
+{
+ internal sealed class ConsoleMessage
+ {
+ public ConsoleMessage(string caller, string message, LogType type, StackTrace trace)
+ {
+ Caller = caller;
+ Message = message;
+ Type = type;
+ Count = 1;
+ Trace = trace;
+ }
+
+ public string Caller { get; }
+
+ public string Message { get; }
+
+ public LogType Type { get; }
+
+ public int Count { get; set; }
+
+ public StackTrace Trace { get; }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/Console.cs b/Debugger/Console/CustomConsole.cs
similarity index 56%
rename from Debugger/Console.cs
rename to Debugger/Console/CustomConsole.cs
index e42f44d..a18726e 100644
--- a/Debugger/Console.cs
+++ b/Debugger/Console/CustomConsole.cs
@@ -4,63 +4,61 @@
using System.Linq;
using System.Reflection;
using ColossalFramework.UI;
+using ModTools.Scripting;
+using ModTools.UI;
using UnityEngine;
-namespace ModTools
+namespace ModTools.Console
{
-
- public class ConsoleMessage
- {
- public string caller;
- public string message;
- public LogType type;
- public int count;
- public StackTrace trace;
- }
-
- public class Console : GUIWindow
+ internal sealed class CustomConsole : GUIWindow, ILogger, IGameObject
{
+ public const string DefaultSource = @"
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using System.IO;
+using System.Linq;
+using ColossalFramework.UI;
+using UnityEngine;
- private static Configuration config => ModTools.Instance.config;
+namespace ModTools
+{{
+ class ModToolsCommandLineRunner : IModEntryPoint
+ {{
+ public void OnModLoaded()
+ {{
+ {0}
+ }}
- private GUIArea headerArea;
- private GUIArea consoleArea;
- private GUIArea commandLineArea;
- private bool focusCommandLineArea = false;
- private bool emptyCommandLineArea = true;
- private bool setCommandLinePosition = false;
- private int commandLinePosition;
+ public void OnModUnloaded()
+ {{
+ }}
+ }}
+}}";
- private float headerHeightCompact = 0.5f;
- private float headerHeightExpanded = 8.0f;
- private bool headerExpanded = false;
+ private const float HeaderHeightCompact = 0.5f;
+ private const float HeaderHeightExpanded = 8.0f;
+ private const float CommandLineAreaHeightCompact = 45.0f;
+ private const float CommandLineAreaHeightExpanded = 120.0f;
- private float commandLineAreaHeightCompact = 45.0f;
- private float commandLineAreaHeightExpanded = 120.0f;
+ private readonly Color orangeColor = new Color(1.0f, 0.647f, 0.0f, 1.0f);
- private bool commandLineAreaExpanded
- {
- get
- {
- var command = commandHistory[currentCommandHistoryIndex];
- if (command.Length == 0)
- {
- return false;
- }
+ private readonly GUIArea headerArea;
+ private readonly GUIArea consoleArea;
+ private readonly GUIArea commandLineArea;
- if (command.Contains('\n'))
- {
- return true;
- }
+ private readonly object historyLock = new object();
+ private readonly List history = new List();
+ private readonly List commandHistory = new List() { string.Empty };
- return command.Length >= 64;
- }
- }
+ private bool focusCommandLineArea;
+ private bool emptyCommandLineArea = true;
+ private bool setCommandLinePosition;
+ private int commandLinePosition;
+ private bool headerExpanded;
- private object historyLock = new object();
- private List history = new List();
- private List commandHistory = new List() { "" };
- private int currentCommandHistoryIndex = 0;
+ private int currentCommandHistoryIndex;
private Vector2 consoleScrollPosition = Vector2.zero;
private Vector2 commandLineScrollPosition = Vector2.zero;
@@ -68,79 +66,35 @@ private bool commandLineAreaExpanded
private DebugOutputPanel vanillaPanel;
private Transform oldVanillaPanelParent;
- private List> userNotifications;
+ private List> userNotifications;
- public Console() : base("Debug console", config.consoleRect, skin)
+ public CustomConsole()
+ : base("Debug console", Config.ConsoleRect, Skin)
{
- onDraw = DrawWindow;
- onException = HandleException;
- onUnityDestroy = HandleDestroy;
+ headerArea = new GUIArea(this)
+ .OffsetBy(vertical: 16f)
+ .ChangeSizeRelative(height: 0);
- headerArea = new GUIArea(this);
consoleArea = new GUIArea(this);
- commandLineArea = new GUIArea(this);
- RecalculateAreas();
+ commandLineArea = new GUIArea(this)
+ .ChangeSizeRelative(height: 0);
- onUnityGUI = () => KeyboardCallback();
+ RecalculateAreas();
}
- void KeyboardCallback()
+ private static ModConfiguration Config => MainWindow.Instance.Config;
+
+ private bool CommandLineAreaExpanded
{
- Event e = Event.current;
- if (e.type != EventType.KeyUp || GUI.GetNameOfFocusedControl() != "ModToolsConsoleCommandLine")
- {
- return;
- }
- if (setCommandLinePosition)
- {
- // return has been hit with control pressed in previous GUI event
- // reset the position to the remembered one
- setCommandLinePosition = false;
- TextEditor editor = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
-#if OLDVERSION
- editor.pos = editor.selectPos = commandLinePosition - 1;
-#else
- editor.cursorIndex = editor.selectIndex = commandLinePosition - 1;
-#endif
- }
- if (e.keyCode == KeyCode.Return)
+ get
{
- if (e.shift)
- {
- return;
- }
-
- // event.Use() does not consume the event, work around the enter being inserted into the textbox by deleting the line break
- TextEditor editor = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
-#if OLDVERSION
- int pos = editor.selectPos;
-#else
- int pos = editor.selectIndex;
-#endif
- String currentCommand = commandHistory[currentCommandHistoryIndex];
- String fixedCommand = currentCommand.Substring(0, pos - 1) + currentCommand.Substring(pos, currentCommand.Length - pos);
- commandHistory[currentCommandHistoryIndex] = fixedCommand;
-
- // if control is pressed when hitting return, do not empty the command line area
- // remember the currently selected position and reset it after the next redraw
- if (e.control)
- {
- emptyCommandLineArea = false;
- setCommandLinePosition = true;
- commandLinePosition = pos;
- }
-
- RunCommandLine();
+ var command = commandHistory[currentCommandHistoryIndex];
+ return command.Length == 0 ? false : command.Contains('\n') || command.Length >= 64;
}
}
- void HandleDestroy()
- {
- vanillaPanel.transform.parent = oldVanillaPanelParent;
- vanillaPanel = null;
- }
- void Update()
+ public void Update()
{
if (vanillaPanel == null)
{
@@ -149,117 +103,184 @@ void Update()
{
return;
}
+
vanillaPanel = panel;
oldVanillaPanelParent = vanillaPanel.transform.parent;
vanillaPanel.transform.parent = transform;
}
}
- public void AddMessage(string message, LogType type = LogType.Log, bool _internal = false)
+ public void Log(string message, LogType type)
{
lock (historyLock)
{
if (history.Count > 0)
{
- var last = history.Last();
- if (message == last.message && type == last.type)
+ var lastMessage = history[history.Count - 1];
+ if (message == lastMessage.Message && type == lastMessage.Type)
{
- last.count++;
+ lastMessage.Count++;
return;
}
}
}
- string caller = "";
+ var caller = string.Empty;
- StackTrace trace = new StackTrace(_internal ? 0 : 8);
+ var trace = new StackTrace(8);
- if (!_internal)
+ for (var i = 0; i < trace.FrameCount; i++)
{
- int i;
- for (i = 0; i < trace.FrameCount; i++)
- {
- MethodBase callingMethod = null;
+ MethodBase callingMethod = null;
- var frame = trace.GetFrame(i);
- if (frame != null)
- {
- callingMethod = frame.GetMethod();
- }
+ var frame = trace.GetFrame(i);
+ if (frame != null)
+ {
+ callingMethod = frame.GetMethod();
+ }
- if (callingMethod == null)
- {
- continue;
- }
- caller = callingMethod.DeclaringType != null ? $"{callingMethod.DeclaringType}.{callingMethod.Name}()" : $"{callingMethod}()";
- break;
+ if (callingMethod == null)
+ {
+ continue;
}
- }
- else
- {
- caller = "ModTools";
+
+ caller = callingMethod.DeclaringType != null ? $"{callingMethod.DeclaringType}.{callingMethod.Name}()" : $"{callingMethod}()";
+ break;
}
lock (historyLock)
{
- history.Add(new ConsoleMessage() { count = 1, caller = caller, message = message, type = type, trace = trace });
+ history.Add(new ConsoleMessage(caller, message, type, trace));
- if (history.Count >= config.consoleMaxHistoryLength)
+ if (history.Count >= Config.ConsoleMaxHistoryLength)
{
history.RemoveAt(0);
}
}
- if (type == LogType.Log && config.showConsoleOnMessage)
+ if (type == LogType.Log && Config.ShowConsoleOnMessage)
{
- visible = true;
+ Visible = true;
}
- else if (type == LogType.Warning && config.showConsoleOnWarning)
+ else if (type == LogType.Warning && Config.ShowConsoleOnWarning)
{
- visible = true;
+ Visible = true;
}
- else if ((type == LogType.Exception || type == LogType.Error) && config.showConsoleOnError)
+ else if ((type == LogType.Exception || type == LogType.Error) && Config.ShowConsoleOnError)
{
- visible = true;
+ Visible = true;
}
- if (config.consoleAutoScrollToBottom)
+ if (Config.ConsoleAutoScrollToBottom)
{
consoleScrollPosition.y = float.MaxValue;
}
}
- void RecalculateAreas()
+ public void DrawHeader()
{
- float headerHeight = headerExpanded ? headerHeightExpanded : headerHeightCompact;
- headerHeight *= config.fontSize;
- headerHeight += 32.0f;
+ headerArea.Begin();
+
+ if (headerExpanded)
+ {
+ DrawExpandedHeader();
+ }
+ else
+ {
+ DrawCompactHeader();
+ }
+
+ headerArea.End();
+ }
+
+ protected override void DrawWindow()
+ {
+ RecalculateAreas();
+ DrawHeader();
+ DrawConsole();
+ DrawCommandLineArea();
+ }
+
+ protected override void OnWindowDrawn()
+ {
+ var e = Event.current;
+ if (e.type != EventType.KeyUp || GUI.GetNameOfFocusedControl() != "ModToolsConsoleCommandLine")
+ {
+ return;
+ }
- headerArea.relativeSize.x = 1.0f;
- headerArea.absolutePosition.y = 16.0f;
- headerArea.absoluteSize.y = headerHeight;
+ if (setCommandLinePosition)
+ {
+ // return has been hit with control pressed in previous GUI event
+ // reset the position to the remembered one
+ setCommandLinePosition = false;
+ var editor = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
+ editor.selectIndex = commandLinePosition - 1;
+ editor.cursorIndex = editor.selectIndex;
+ }
- var commandLineAreaHeight = commandLineAreaExpanded
- ? commandLineAreaHeightExpanded
- : commandLineAreaHeightCompact;
+ if (e.keyCode == KeyCode.Return)
+ {
+ if (e.shift)
+ {
+ return;
+ }
+
+ // event.Use() does not consume the event, work around the enter being inserted into the textbox by
+ // deleting the line break
+ var editor = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl);
+ var pos = editor.selectIndex;
+ var currentCommand = commandHistory[currentCommandHistoryIndex];
+ commandHistory[currentCommandHistoryIndex]
+ = currentCommand.Substring(0, pos - 1) + currentCommand.Substring(pos, currentCommand.Length - pos);
+
+ // if control is pressed when hitting return, do not empty the command line area
+ // remember the currently selected position and reset it after the next redraw
+ if (e.control)
+ {
+ emptyCommandLineArea = false;
+ setCommandLinePosition = true;
+ commandLinePosition = pos;
+ }
- consoleArea.absolutePosition.y = 16.0f + headerHeight;
- consoleArea.relativeSize.x = 1.0f;
- consoleArea.relativeSize.y = 1.0f;
- consoleArea.absoluteSize.y = -(commandLineAreaHeight + headerHeight + 16.0f);
+ RunCommandLine();
+ }
+ }
- commandLineArea.relativePosition.y = 1.0f;
- commandLineArea.absolutePosition.y = -commandLineAreaHeight;
- commandLineArea.relativeSize.x = 1.0f;
- commandLineArea.absoluteSize.y = commandLineAreaHeight;
+ protected override void OnWindowDestroyed()
+ {
+ if (vanillaPanel != null)
+ {
+ vanillaPanel.transform.parent = oldVanillaPanelParent;
+ vanillaPanel = null;
+ }
}
- void HandleException(Exception ex)
+ protected override void HandleException(Exception ex) => Log("Exception in ModTools Console - " + ex.Message, LogType.Exception);
+
+ private void RecalculateAreas()
{
- AddMessage("Exception in ModTools Console - " + ex.Message, LogType.Exception);
+ var headerHeight = headerExpanded ? HeaderHeightExpanded : HeaderHeightCompact;
+ headerHeight *= Config.FontSize;
+ headerHeight += 32.0f;
+
+ headerArea.ChangeSizeBy(height: headerHeight);
+
+ var commandLineAreaHeight = CommandLineAreaExpanded
+ ? CommandLineAreaHeightExpanded
+ : CommandLineAreaHeightCompact;
+
+ consoleArea
+ .OffsetBy(vertical: 16f + headerHeight)
+ .ChangeSizeBy(height: -(commandLineAreaHeight + headerHeight + 16f));
+
+ commandLineArea
+ .OffsetRelative(vertical: 1f)
+ .OffsetBy(vertical: -commandLineAreaHeight)
+ .ChangeSizeBy(height: commandLineAreaHeight);
}
- void DrawCompactHeader()
+ private void DrawCompactHeader()
{
GUILayout.BeginHorizontal();
@@ -284,36 +305,36 @@ void DrawCompactHeader()
GUILayout.EndHorizontal();
}
- void DrawExpandedHeader()
+ private void DrawExpandedHeader()
{
GUILayout.BeginHorizontal();
GUILayout.Label("Log message format:", GUILayout.ExpandWidth(false));
- config.consoleFormatString = GUILayout.TextField(config.consoleFormatString, GUILayout.ExpandWidth(true));
+ Config.ConsoleFormatString = GUILayout.TextField(Config.ConsoleFormatString, GUILayout.ExpandWidth(true));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Max items in history:", GUILayout.ExpandWidth(false));
- GUIControls.IntField("ConsoleMaxItemsInHistory", "", ref config.consoleMaxHistoryLength, 0.0f, true, true);
+ Config.ConsoleMaxHistoryLength = GUIControls.PrimitiveValueField("ConsoleMaxItemsInHistory", string.Empty, Config.ConsoleMaxHistoryLength);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("Show console on:", GUILayout.ExpandWidth(false));
GUILayout.FlexibleSpace();
GUILayout.Label("Message", GUILayout.ExpandWidth(false));
- config.showConsoleOnMessage = GUILayout.Toggle(config.showConsoleOnMessage, "", GUILayout.ExpandWidth(false));
+ Config.ShowConsoleOnMessage = GUILayout.Toggle(Config.ShowConsoleOnMessage, string.Empty, GUILayout.ExpandWidth(false));
GUILayout.FlexibleSpace();
GUILayout.Label("Warning", GUILayout.ExpandWidth(false));
- config.showConsoleOnWarning = GUILayout.Toggle(config.showConsoleOnWarning, "", GUILayout.ExpandWidth(false));
+ Config.ShowConsoleOnWarning = GUILayout.Toggle(Config.ShowConsoleOnWarning, string.Empty, GUILayout.ExpandWidth(false));
GUILayout.FlexibleSpace();
GUILayout.Label("Error", GUILayout.ExpandWidth(false));
- config.showConsoleOnError = GUILayout.Toggle(config.showConsoleOnError, "", GUILayout.ExpandWidth(false));
-
+ Config.ShowConsoleOnError = GUILayout.Toggle(Config.ShowConsoleOnError, string.Empty, GUILayout.ExpandWidth(false));
+
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
-
+
GUILayout.BeginHorizontal();
GUILayout.Label("Auto-scroll on new messages:");
- config.consoleAutoScrollToBottom = GUILayout.Toggle(config.consoleAutoScrollToBottom, "");
+ Config.ConsoleAutoScrollToBottom = GUILayout.Toggle(Config.ConsoleAutoScrollToBottom, string.Empty);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
@@ -331,19 +352,19 @@ void DrawExpandedHeader()
if (GUILayout.Button("Save"))
{
- ModTools.Instance.SaveConfig();
+ MainWindow.Instance.SaveConfig();
}
if (GUILayout.Button("Reset"))
{
- var template = new Configuration();
- config.consoleMaxHistoryLength = template.consoleMaxHistoryLength;
- config.consoleFormatString = template.consoleFormatString;
- config.showConsoleOnMessage = template.showConsoleOnMessage;
- config.showConsoleOnWarning = template.showConsoleOnWarning;
- config.showConsoleOnError = template.showConsoleOnError;
+ var template = new ModConfiguration();
+ Config.ConsoleMaxHistoryLength = template.ConsoleMaxHistoryLength;
+ Config.ConsoleFormatString = template.ConsoleFormatString;
+ Config.ShowConsoleOnMessage = template.ShowConsoleOnMessage;
+ Config.ShowConsoleOnWarning = template.ShowConsoleOnWarning;
+ Config.ShowConsoleOnError = template.ShowConsoleOnError;
- ModTools.Instance.SaveConfig();
+ MainWindow.Instance.SaveConfig();
}
if (GUILayout.Button("Clear", GUILayout.ExpandWidth(false)))
@@ -357,25 +378,7 @@ void DrawExpandedHeader()
GUILayout.EndHorizontal();
}
- public void DrawHeader()
- {
- headerArea.Begin();
-
- if (headerExpanded)
- {
- DrawExpandedHeader();
- }
- else
- {
- DrawCompactHeader();
- }
-
- headerArea.End();
- }
-
- private Color orangeColor = new Color(1.0f, 0.647f, 0.0f, 1.0f);
-
- void DrawConsole()
+ private void DrawConsole()
{
userNotifications = UserNotifications.GetNotifications();
@@ -385,7 +388,7 @@ void DrawConsole()
foreach (var item in userNotifications)
{
- GUILayout.BeginHorizontal(skin.box);
+ GUILayout.BeginHorizontal(Skin.box);
GUI.contentColor = Color.cyan;
GUILayout.Label(item.Value);
@@ -406,28 +409,31 @@ void DrawConsole()
messages = history.ToArray();
}
- foreach (ConsoleMessage item in messages)
+ foreach (var item in messages)
{
- GUILayout.BeginHorizontal(skin.box);
+ GUILayout.BeginHorizontal(Skin.box);
- string msg = config.consoleFormatString.Replace("{{type}}", item.type.ToString())
- .Replace("{{caller}}", item.caller)
- .Replace("{{message}}", item.message);
+ var msg = Config.ConsoleFormatString.Replace("{{type}}", item.Type.ToString())
+ .Replace("{{caller}}", item.Caller)
+ .Replace("{{message}}", item.Message);
- switch (item.type)
+ switch (item.Type)
{
case LogType.Log:
- GUI.contentColor = config.consoleMessageColor;
+ GUI.contentColor = Config.ConsoleMessageColor;
break;
+
case LogType.Warning:
- GUI.contentColor = config.consoleWarningColor;
+ GUI.contentColor = Config.ConsoleWarningColor;
break;
+
case LogType.Error:
- GUI.contentColor = config.consoleErrorColor;
+ GUI.contentColor = Config.ConsoleErrorColor;
break;
+
case LogType.Assert:
case LogType.Exception:
- GUI.contentColor = config.consoleExceptionColor;
+ GUI.contentColor = Config.ConsoleExceptionColor;
break;
}
@@ -435,24 +441,24 @@ void DrawConsole()
GUILayout.FlexibleSpace();
- if (item.count > 1)
+ if (item.Count > 1)
{
GUI.contentColor = orangeColor;
- if (item.count > 1024)
+ if (item.Count > 1024)
{
GUI.contentColor = Color.red;
}
- GUILayout.Label(item.count.ToString(), skin.box);
+ GUILayout.Label(item.Count.ToString(), Skin.box);
}
else
{
- GUILayout.Label("");
+ GUILayout.Label(string.Empty);
}
GUI.contentColor = Color.white;
- var stackTrace = item.trace;
+ var stackTrace = item.Trace;
if (stackTrace != null)
{
GUIStackTrace.StackTraceButton(stackTrace);
@@ -466,7 +472,7 @@ void DrawConsole()
consoleArea.End();
}
- void DrawCommandLineArea()
+ private void DrawCommandLineArea()
{
commandLineArea.Begin();
@@ -483,6 +489,7 @@ void DrawCommandLineArea()
{
GUI.enabled = false;
}
+
GUILayout.EndScrollView();
GUILayout.EndVertical();
@@ -522,7 +529,6 @@ void DrawCommandLineArea()
GUILayout.EndVertical();
GUILayout.EndHorizontal();
-
commandLineArea.End();
// refocus the command line area after enter has been pressed
@@ -533,15 +539,15 @@ void DrawCommandLineArea()
}
}
- void RunCommandLine()
+ private void RunCommandLine()
{
var commandLine = commandHistory[currentCommandHistoryIndex];
if (emptyCommandLineArea)
{
- if (commandHistory.Last() != "")
+ if (commandHistory.Count > 0 && !string.IsNullOrEmpty(commandHistory[commandHistory.Count - 1]))
{
- commandHistory.Add("");
+ commandHistory.Add(string.Empty);
currentCommandHistoryIndex = commandHistory.Count - 1;
}
else
@@ -549,64 +555,26 @@ void RunCommandLine()
currentCommandHistoryIndex = commandHistory.Count - 1;
}
}
+
emptyCommandLineArea = true;
- var source = String.Format(defaultSource, commandLine);
- var file = new ScriptEditorFile() { path = "ModToolsCommandLineScript.cs", source = source };
- string errorMessage;
- IModEntryPoint instance;
- if (!ScriptCompiler.RunSource(new List() { file }, out errorMessage, out instance))
+ var source = string.Format(DefaultSource, commandLine);
+ var file = new ScriptEditorFile(source, "ModToolsCommandLineScript.cs");
+ if (!ScriptCompiler.RunSource(new List() { file }, out _, out var instance))
{
- Log.Error("Failed to compile command-line!");
+ Logger.Error("Failed to compile command-line!");
+ }
+ else if (instance != null)
+ {
+ Logger.Message("Executing command-line..");
+ instance.OnModLoaded();
}
else
{
- if (instance != null)
- {
- Log.Message("Executing command-line..");
- instance.OnModLoaded();
- }
- else
- {
- Log.Error("Error executing command-line..");
- }
+ Logger.Error("Error executing command-line..");
}
- commandLine = "";
- focusCommandLineArea = true;
- }
- void DrawWindow()
- {
- RecalculateAreas();
- DrawHeader();
- DrawConsole();
- DrawCommandLineArea();
+ focusCommandLineArea = true;
}
-
- private readonly string defaultSource = @"
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Reflection;
-using System.IO;
-using System.Linq;
-using ColossalFramework.UI;
-using UnityEngine;
-
-namespace ModTools
-{{
- class ModToolsCommandLineRunner : IModEntryPoint
- {{
- public void OnModLoaded()
- {{
- {0}
- }}
-
- public void OnModUnloaded()
- {{
- }}
- }}
-}}";
-
}
-}
+}
\ No newline at end of file
diff --git a/Debugger/StackTraceViewer.cs b/Debugger/Console/StackTraceViewer.cs
similarity index 60%
rename from Debugger/StackTraceViewer.cs
rename to Debugger/Console/StackTraceViewer.cs
index 86a6875..17bf287 100644
--- a/Debugger/StackTraceViewer.cs
+++ b/Debugger/Console/StackTraceViewer.cs
@@ -1,48 +1,37 @@
using System;
using System.Diagnostics;
+using ModTools.UI;
using UnityEngine;
-namespace ModTools
+namespace ModTools.Console
{
- public class StackTraceViewer : GUIWindow
+ internal sealed class StackTraceViewer : GUIWindow
{
+ private StackTrace trace;
+
+ private Vector2 scrollPos = Vector2.zero;
- private static Configuration config
+ private StackTraceViewer()
+ : base("Stack-trace viewer", new Rect(16.0f, 16.0f, 512.0f, 256.0f), Skin)
{
- get { return ModTools.Instance.config; }
}
- private StackTrace trace;
-
- private Vector2 scrollPos = Vector2.zero;
+ private static ModConfiguration Config => MainWindow.Instance.Config;
public static StackTraceViewer CreateStackTraceViewer(StackTrace trace)
{
var go = new GameObject("StackTraceViewer");
- go.transform.parent = ModTools.Instance.transform;
+ go.transform.parent = MainWindow.Instance.transform;
var viewer = go.AddComponent();
viewer.trace = trace;
return viewer;
}
- private StackTraceViewer() : base("Stack-trace viewer", new Rect(16.0f, 16.0f, 512.0f, 256.0f), skin)
- {
- onDraw = DrawWindow;
- onException = HandleException;
- onClose = HandleClosed;
- }
-
- void HandleClosed()
- {
- Destroy(this);
- }
+ protected override void OnWindowClosed() => Destroy(this);
- void HandleException(Exception ex)
- {
- Log.Error("Exception in StackTraceViewer - " + ex.Message);
- }
+ protected override void HandleException(Exception ex) => Logger.Error("Exception in StackTraceViewer - " + ex.Message);
- void DrawWindow()
+ protected override void DrawWindow()
{
if (trace == null)
{
@@ -57,19 +46,19 @@ void DrawWindow()
scrollPos = GUILayout.BeginScrollView(scrollPos);
- int count = 0;
+ var count = 0;
foreach (var frame in stackFrames)
{
- GUILayout.BeginHorizontal(skin.box);
+ GUILayout.BeginHorizontal(Skin.box);
var method = frame.GetMethod();
GUILayout.Label(count.ToString(), GUILayout.ExpandWidth(false));
-
- GUI.contentColor = config.nameColor;
+
+ GUI.contentColor = Config.NameColor;
GUILayout.Label(method.ToString(), GUILayout.ExpandWidth(false));
- GUI.contentColor = config.typeColor;
+ GUI.contentColor = Config.TypeColor;
if (method.DeclaringType != null)
{
@@ -84,7 +73,5 @@ void DrawWindow()
GUILayout.EndScrollView();
}
-
}
-
-}
+}
\ No newline at end of file
diff --git a/Debugger/CustomPrefabs.cs b/Debugger/CustomPrefabs.cs
index 913f6d7..8560dad 100644
--- a/Debugger/CustomPrefabs.cs
+++ b/Debugger/CustomPrefabs.cs
@@ -5,20 +5,26 @@
namespace ModTools
{
- public class CustomPrefabs : MonoBehaviour
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Code Quality", "IDE0052", Justification = "Intended for self-reflection in-game")]
+ internal sealed class CustomPrefabs : MonoBehaviour, IDestroyableObject, IAwakingObject
{
-
- private static bool bootstrapped = false;
private static GameObject thisGameObject;
- public VehicleInfo[] m_vehicles;
- public BuildingInfo[] m_buildings;
- public PropInfo[] m_props;
- public TreeInfo[] m_trees;
- public NetInfo[] m_nets;
- public EventInfo[] m_events;
- public TransportInfo[] m_transports;
- public CitizenInfo[] m_citizens;
+ private VehicleInfo[] vehicles;
+
+ private BuildingInfo[] buildings;
+
+ private PropInfo[] props;
+
+ private TreeInfo[] trees;
+
+ private NetInfo[] nets;
+
+ private EventInfo[] events;
+
+ private TransportInfo[] transports;
+
+ private CitizenInfo[] citizens;
public static void Bootstrap()
{
@@ -27,11 +33,6 @@ public static void Bootstrap()
thisGameObject = new GameObject("Custom Prefabs");
thisGameObject.AddComponent();
}
- if (bootstrapped)
- {
- return;
- }
- bootstrapped = true;
}
public static void Revert()
@@ -41,53 +42,50 @@ public static void Revert()
Destroy(thisGameObject);
thisGameObject = null;
}
-
- if (!bootstrapped)
- {
- return;
- }
- bootstrapped = false;
}
-
public void Awake()
{
- m_vehicles = GetCustomPrefabs();
- m_buildings = GetCustomPrefabs();
- m_props = GetCustomPrefabs();
- m_trees = GetCustomPrefabs();
- m_nets = GetCustomPrefabs();
- m_events = GetCustomPrefabs();
- m_transports = GetCustomPrefabs();
- m_citizens = GetCustomPrefabs();
+ vehicles = GetCustomPrefabs();
+ buildings = GetCustomPrefabs();
+ props = GetCustomPrefabs();
+ trees = GetCustomPrefabs();
+ nets = GetCustomPrefabs();
+ events = GetCustomPrefabs();
+ transports = GetCustomPrefabs();
+ citizens = GetCustomPrefabs();
}
public void OnDestroy()
{
- m_vehicles = null;
- m_buildings = null;
- m_props = null;
- m_trees = null;
- m_nets = null;
- m_events = null;
- m_transports = null;
- m_citizens = null;
+ vehicles = null;
+ buildings = null;
+ props = null;
+ trees = null;
+ nets = null;
+ events = null;
+ transports = null;
+ citizens = null;
}
- private static T[] GetCustomPrefabs() where T: PrefabInfo
+ private static T[] GetCustomPrefabs()
+ where T : PrefabInfo
{
- var result = new List();
var count = PrefabCollection.LoadedCount();
+ var result = new List(count);
for (uint i = 0; i < count; i++)
{
var prefab = PrefabCollection.GetPrefab(i);
- if(prefab == null || (!prefab.m_isCustomContent && prefab.name != null && !prefab.name.Contains('.')))
+ if (prefab == null || !prefab.m_isCustomContent && prefab.name?.Contains('.') == false)
{
continue;
}
+
result.Add(prefab);
}
- return result.OrderBy(p => p.name).ToArray();
- }
+
+ result.Sort((x, y) => string.CompareOrdinal(x?.name, y?.name));
+ return result.ToArray();
+ }
}
}
\ No newline at end of file
diff --git a/Debugger/DebugRenderer.cs b/Debugger/DebugRenderer.cs
index 7f82af3..4e77616 100644
--- a/Debugger/DebugRenderer.cs
+++ b/Debugger/DebugRenderer.cs
@@ -1,67 +1,68 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using ColossalFramework.UI;
+using ModTools.Explorer;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools
{
- public class DebugRenderer : MonoBehaviour
+ internal sealed class DebugRenderer : MonoBehaviour, IGameObject, IUIObject
{
-
- public bool drawDebugInfo = false;
+ private readonly List hoveredComponents = new List();
private GUIStyle normalRectStyle;
private GUIStyle hoveredRectStyle;
private GUIStyle infoWindowStyle;
private UIComponent hoveredComponent;
- private readonly List hoveredComponents = new List();
+
private long previousHash = 0;
- void Update()
+ public bool DrawDebugInfo { get; set; }
+
+ public void Update()
{
var hoveredLocal = hoveredComponent;
- if (drawDebugInfo && hoveredLocal != null)
+ if (!DrawDebugInfo || hoveredLocal == null)
{
- if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.F))
+ return;
+ }
+
+ if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.F))
+ {
+ var uiView = FindObjectOfType();
+ if (uiView == null)
{
- var uiView = FindObjectOfType();
- if (uiView == null)
- {
- return;
- }
-
- var current = hoveredLocal;
- var refChain = new ReferenceChain();
- refChain = refChain.Add(current);
- while (current != null)
- {
- refChain = refChain.Add(current.gameObject);
- current = current.parent;
- }
- ;
- refChain = refChain.Add(uiView.gameObject);
-
- var sceneExplorer = FindObjectOfType();
- sceneExplorer.ExpandFromRefChain(refChain.Reverse);
- sceneExplorer.visible = true;
+ return;
}
- if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.G))
+
+ var current = hoveredLocal;
+ var refChain = new ReferenceChain();
+ refChain = refChain.Add(current);
+ while (current != null)
{
- if (hoveredComponents.Count > 1 && hoveredComponent != null)
- {
- var index = hoveredComponents.IndexOf(hoveredComponent);
- var newIndex = (index + hoveredComponents.Count + 1) % hoveredComponents.Count;
- hoveredComponent = hoveredComponents[newIndex];
- }
+ refChain = refChain.Add(current.gameObject);
+ current = current.parent;
}
+
+ refChain = refChain.Add(uiView.gameObject);
+
+ var sceneExplorer = FindObjectOfType();
+ sceneExplorer.Show(refChain.GetReversedCopy());
+ }
+
+ if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.G) && hoveredComponents.Count > 1 && hoveredComponent != null)
+ {
+ var index = hoveredComponents.IndexOf(hoveredComponent);
+ var newIndex = (index + hoveredComponents.Count + 1) % hoveredComponents.Count;
+ hoveredComponent = hoveredComponents[newIndex];
}
}
- void OnGUI()
+ public void OnGUI()
{
- if (!drawDebugInfo)
+ if (!DrawDebugInfo)
{
return;
}
@@ -91,7 +92,7 @@ void OnGUI()
normal = { background = null },
hover = { background = null },
active = { background = null },
- focused = { background = null }
+ focused = { background = null },
};
}
@@ -102,7 +103,7 @@ void OnGUI()
return;
}
- UIComponent[] components = GetComponentsInChildren();
+ var components = GetComponentsInChildren();
Array.Sort(components, RenderSortFunc);
var mouse = Input.mousePosition;
@@ -110,7 +111,7 @@ void OnGUI()
hoveredComponents.Clear();
long hash = 0;
- for (int i = components.Length - 1; i > 0; i--)
+ for (var i = components.Length - 1; i > 0; i--)
{
var component = components[i];
@@ -138,17 +139,18 @@ void OnGUI()
hoveredComponents.Add(component);
}
}
+
if (hoveredComponent != null && hash != previousHash)
{
hoveredComponent = null;
previousHash = hash;
}
+
if (hoveredComponent == null && hoveredComponents.Count > 0)
{
- hoveredComponent = hoveredComponents.First();
+ hoveredComponent = hoveredComponents[0];
}
-
foreach (var component in components)
{
if (!component.isVisible)
@@ -160,7 +162,7 @@ void OnGUI()
var size = component.size;
var rect = CalculateRealComponentRect(position, size);
- GUI.Box(rect, "", hoveredComponent == component ? hoveredRectStyle : normalRectStyle);
+ GUI.Box(rect, string.Empty, hoveredComponent == component ? hoveredRectStyle : normalRectStyle);
}
if (hoveredComponent != null)
@@ -179,11 +181,11 @@ void OnGUI()
coords.y = Screen.height - size.y;
}
- GUI.Window(81871, new Rect(coords.x, coords.y, size.x, size.y), DoInfoWindow, "", infoWindowStyle);
+ GUI.Window(81871, new Rect(coords.x, coords.y, size.x, size.y), DoInfoWindow, string.Empty, infoWindowStyle);
}
}
- Rect CalculateRealComponentRect(Vector3 absolutePosition, Vector2 size)
+ private static Rect CalculateRealComponentRect(Vector3 absolutePosition, Vector2 size)
{
var dx = Screen.width / 1920.0f;
var dy = Screen.height / 1080.0f;
@@ -194,12 +196,16 @@ Rect CalculateRealComponentRect(Vector3 absolutePosition, Vector2 size)
return new Rect(absolutePosition.x, absolutePosition.y, size.x, size.y);
}
- void DoInfoWindow(int i)
+ private static long CalculateHash(UIComponent c)
+ => HashUtil.HashRect(new Rect(c.relativePosition.x, c.relativePosition.y, c.size.x, c.size.y));
+
+ private void DoInfoWindow(int i)
{
if (hoveredComponent == null)
{
return;
}
+
GUI.color = Color.red;
GUILayout.Label("[Press Ctrl+F to open it in SceneExplorer]");
GUI.color = Color.blue;
@@ -222,32 +228,25 @@ void DoInfoWindow(int i)
{
GUILayout.Label($"atlas.name: {interactiveComponent.atlas.name}");
}
+
var sprite = hoveredComponent as UISprite;
if (sprite != null)
{
GUILayout.Label($"atlas.name: {sprite.atlas?.name}");
GUILayout.Label($"spriteName: {sprite.spriteName}");
}
+
var textureSprite = hoveredComponent as UITextureSprite;
if (textureSprite != null)
{
GUILayout.Label($"texture.name: {textureSprite.texture?.name}");
}
+
GUILayout.Label($"zOrder: {hoveredComponent.zOrder}");
var hash = CalculateHash(hoveredComponent);
GUILayout.Label($"hash: {HashUtil.HashToString(hash)}");
}
- private long CalculateHash(UIComponent c)
- {
- return HashUtil.HashRect(new Rect(c.relativePosition.x, c.relativePosition.y,
- c.size.x, c.size.y));
- }
-
- private int RenderSortFunc(UIComponent lhs, UIComponent rhs)
- {
- return lhs.renderOrder.CompareTo(rhs.renderOrder);
- }
-
+ private int RenderSortFunc(UIComponent lhs, UIComponent rhs) => lhs.renderOrder.CompareTo(rhs.renderOrder);
}
}
\ No newline at end of file
diff --git a/Debugger/Explorer/GUIButtons.cs b/Debugger/Explorer/GUIButtons.cs
new file mode 100644
index 0000000..4def68a
--- /dev/null
+++ b/Debugger/Explorer/GUIButtons.cs
@@ -0,0 +1,205 @@
+using System;
+using ModTools.Utils;
+using UnityEngine;
+
+namespace ModTools.Explorer
+{
+ internal static class GUIButtons
+ {
+ private static object buffer;
+
+ public static void SetupButtons(ReferenceChain refChain, Type type, object value, int valueIndex)
+ {
+ switch (value)
+ {
+ case null:
+ return;
+
+ case PrefabInfo prefabInfo:
+ SetupButtonsForPrefab(prefabInfo);
+ goto default;
+
+ case MilestoneInfo milestoneInfo:
+ if (GUILayout.Button("Unlock"))
+ {
+ var wrapper = new MilestonesWrapper(UnlockManager.instance);
+ wrapper.UnlockMilestone(milestoneInfo.name);
+ }
+
+ return;
+
+ case NetInfo.Segment segmentInfo:
+ SetupMeshPreviewButton(name: null, segmentInfo.m_mesh, segmentInfo.m_material, segmentInfo.m_lodMesh, segmentInfo.m_lodMaterial);
+ goto default;
+
+ case NetInfo.Node nodeInfo:
+ SetupMeshPreviewButton(name: null, nodeInfo.m_mesh, nodeInfo.m_material, nodeInfo.m_lodMesh, nodeInfo.m_lodMaterial);
+ goto default;
+
+ case CitizenInstance instance
+ when valueIndex > 0 && (instance.m_flags & (CitizenInstance.Flags.Created | CitizenInstance.Flags.Deleted)) == CitizenInstance.Flags.Created:
+
+ InstanceID citizenInstanceInst = default;
+ citizenInstanceInst.CitizenInstance = (ushort)valueIndex;
+ SetupGotoButton(citizenInstanceInst, instance.GetLastFramePosition());
+ goto default;
+
+ case Citizen citizen when citizen.m_instance > 0:
+ ref var citizenInstance = ref CitizenManager.instance.m_instances.m_buffer[citizen.m_instance];
+ if ((citizenInstance.m_flags & (CitizenInstance.Flags.Created | CitizenInstance.Flags.Deleted)) == CitizenInstance.Flags.Created)
+ {
+ InstanceID citizenInstanceInst2 = default;
+ citizenInstanceInst2.CitizenInstance = citizen.m_instance;
+ SetupGotoButton(citizenInstanceInst2, citizenInstance.GetLastFramePosition());
+ }
+
+ goto default;
+
+ case Vehicle vehicle when valueIndex > 0 && (vehicle.m_flags & (Vehicle.Flags.Created | Vehicle.Flags.Deleted)) == Vehicle.Flags.Created:
+ InstanceID vehicleInst = default;
+ vehicleInst.Vehicle = (ushort)valueIndex;
+ SetupGotoButton(vehicleInst, vehicle.GetLastFramePosition());
+ break;
+
+ case VehicleParked parkedVehicle when valueIndex > 0 && (parkedVehicle.m_flags & 3) == 1:
+ InstanceID parkedVehicleInst = default;
+ parkedVehicleInst.ParkedVehicle = (ushort)valueIndex;
+ SetupGotoButton(parkedVehicleInst, parkedVehicle.m_position);
+ goto default;
+
+ case Building building when valueIndex > 0 && (building.m_flags & (Building.Flags.Created | Building.Flags.Deleted)) == Building.Flags.Created:
+ InstanceID buildingInst = default;
+ buildingInst.Building = (ushort)valueIndex;
+ SetupGotoButton(buildingInst, building.m_position);
+ goto default;
+
+ default:
+ if (GUILayout.Button("Copy"))
+ {
+ buffer = value;
+ }
+
+ return;
+ }
+
+ SetupTextureOrMeshButtons(type, value, refChain);
+
+ if (GUILayout.Button("Copy"))
+ {
+ buffer = value;
+ }
+ }
+
+ public static bool SetupPasteButon(Type type, out object paste)
+ {
+ paste = null;
+ if (buffer != null && type.IsInstanceOfType(buffer) && GUILayout.Button("Paste"))
+ {
+ paste = buffer;
+ return true;
+ }
+
+ return GUILayout.Button("Unset");
+ }
+
+ private static void SetupButtonsForPrefab(PrefabInfo prefabInfo)
+ {
+ switch (prefabInfo)
+ {
+ case VehicleInfo vehicleInfo:
+ SetupMeshPreviewButton(vehicleInfo.name, vehicleInfo.m_mesh, vehicleInfo.m_material, vehicleInfo.m_lodMesh, vehicleInfo.m_lodMaterial);
+ break;
+
+ case NetInfo _:
+ SetupPlopButton(prefabInfo);
+ break;
+
+ case BuildingInfo buildingInfo:
+ SetupPlopButton(prefabInfo);
+ SetupMeshPreviewButton(buildingInfo.name, buildingInfo.m_mesh, buildingInfo.m_material, buildingInfo.m_lodMesh, buildingInfo.m_lodMaterial);
+ break;
+
+ case PropInfo propInfo:
+ SetupPlopButton(prefabInfo);
+ SetupMeshPreviewButton(propInfo.name, propInfo.m_mesh, propInfo.m_material, propInfo.m_lodMesh, propInfo.m_lodMaterial);
+ break;
+
+ case TreeInfo treeInfo:
+ SetupPlopButton(prefabInfo);
+ SetupMeshPreviewButton(treeInfo.name, treeInfo.m_mesh, treeInfo.m_material, lodMesh: null, lodMaterial: null);
+ break;
+
+ case CitizenInfo citizenInfo:
+ SetupMeshPreviewButton(
+ citizenInfo.name,
+ citizenInfo.m_skinRenderer?.sharedMesh,
+ citizenInfo.m_skinRenderer?.material,
+ citizenInfo.m_lodMesh,
+ citizenInfo.m_lodMaterial);
+ break;
+ }
+ }
+
+ private static void SetupMeshPreviewButton(string name, Mesh mesh, Material material, Mesh lodMesh, Material lodMaterial)
+ {
+ if (mesh != null && GUILayout.Button("Preview"))
+ {
+ MeshViewer.CreateMeshViewer(name, mesh, material);
+ }
+
+ if (lodMesh != null && GUILayout.Button("Preview LOD"))
+ {
+ MeshViewer.CreateMeshViewer(name + "_LOD", lodMesh, lodMaterial);
+ }
+ }
+
+ private static void SetupTextureOrMeshButtons(Type type, object value, ReferenceChain refChain)
+ {
+ if (TypeUtil.IsTextureType(type))
+ {
+ var texture = (Texture)value;
+ if (GUILayout.Button("Preview"))
+ {
+ TextureViewer.CreateTextureViewer(texture);
+ }
+
+ if (GUILayout.Button("Dump .png"))
+ {
+ TextureUtil.DumpTextureToPNG(texture);
+ }
+ }
+ else if (TypeUtil.IsMeshType(type))
+ {
+ var mesh = (Mesh)value;
+
+ if (GUILayout.Button("Preview"))
+ {
+ MeshViewer.CreateMeshViewer(null, mesh, null);
+ }
+
+ if (mesh.isReadable && GUILayout.Button("Dump .obj"))
+ {
+ var outPath = refChain.ToString().Replace(' ', '_');
+ DumpUtil.DumpMeshAndTextures(outPath, mesh);
+ }
+ }
+ }
+
+ private static void SetupPlopButton(object @object)
+ {
+ var info = @object as PrefabInfo;
+ if (info != null && GUILayout.Button("Plop"))
+ {
+ Plopper.StartPlopping(info);
+ }
+ }
+
+ private static void SetupGotoButton(InstanceID instance, Vector3 position)
+ {
+ if (GUILayout.Button("Go to"))
+ {
+ ToolsModifierControl.cameraController.SetTarget(instance, position, true);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/GUIExplorer/GUICollection.cs b/Debugger/Explorer/GUICollection.cs
similarity index 52%
rename from Debugger/GUIExplorer/GUICollection.cs
rename to Debugger/Explorer/GUICollection.cs
index 33734f7..70ae2e4 100644
--- a/Debugger/GUIExplorer/GUICollection.cs
+++ b/Debugger/Explorer/GUICollection.cs
@@ -1,18 +1,20 @@
using System;
using System.Collections;
-using System.Collections.Generic;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools.Explorer
{
- public static class GUICollection
+ internal static class GUICollection
{
- public static void OnSceneTreeReflectICollection(SceneExplorerState state, ReferenceChain refChain, System.Object myProperty)
+ public static void OnSceneTreeReflectICollection(SceneExplorerState state, ReferenceChain refChain, object myProperty)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
- var collection = myProperty as ICollection;
- if (collection == null)
+ if (!(myProperty is ICollection collection))
{
return;
}
@@ -30,11 +32,12 @@ public static void OnSceneTreeReflectICollection(SceneExplorerState state, Refer
return;
}
+ var collectionItemType = collection.GetType().GetElementType();
+ var flagsField = collectionItemType?.GetField("m_flags");
+ var flagIsEnum = flagsField?.FieldType.IsEnum == true && Type.GetTypeCode(flagsField.FieldType) == TypeCode.Int32;
- int arrayStart;
- int arrayEnd;
- GUICollectionNavigation.SetUpCollectionNavigation("Collection", state, refChain, oldRefChain, collectionSize, out arrayStart, out arrayEnd);
- int count = 0;
+ GUICollectionNavigation.SetUpCollectionNavigation("Collection", state, refChain, oldRefChain, collectionSize, out var arrayStart, out var arrayEnd);
+ var count = 0;
foreach (var value in collection)
{
if (count < arrayStart)
@@ -44,18 +47,26 @@ public static void OnSceneTreeReflectICollection(SceneExplorerState state, Refer
}
refChain = oldRefChain.Add(count);
- var type = value.GetType();
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
- GUIExpander.ExpanderControls(state, refChain, type);
+ var isNullOrEmpty = value == null || flagIsEnum && Convert.ToInt32(flagsField.GetValue(value)) == 0;
+
+ var type = value?.GetType() ?? collectionItemType;
+ if (type != null)
+ {
+ if (!isNullOrEmpty)
+ {
+ GUIExpander.ExpanderControls(state, refChain, type);
+ }
- GUI.contentColor = ModTools.Instance.config.typeColor;
+ GUI.contentColor = MainWindow.Instance.Config.TypeColor;
- GUILayout.Label(type.ToString() + " ");
+ GUILayout.Label(type.ToString() + " ");
+ }
- GUI.contentColor = ModTools.Instance.config.nameColor;
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
GUILayout.Label($"{oldRefChain.LastItemName}.[{count}]");
@@ -63,28 +74,32 @@ public static void OnSceneTreeReflectICollection(SceneExplorerState state, Refer
GUILayout.Label(" = ");
- GUI.contentColor = ModTools.Instance.config.valueColor;
- GUILayout.Label(value == null ? "null" : value.ToString());
+ GUI.contentColor = MainWindow.Instance.Config.ValueColor;
+ GUILayout.Label(value == null ? "null" : isNullOrEmpty ? "empty" : value.ToString());
GUI.contentColor = Color.white;
GUILayout.FlexibleSpace();
- GUIButtons.SetupButtons(type, value, refChain);
+
+ if (!isNullOrEmpty)
+ {
+ GUIButtons.SetupButtons(refChain, type, value, count);
+ }
+
GUILayout.EndHorizontal();
- if (!TypeUtil.IsSpecialType(type) && state.expandedObjects.ContainsKey(refChain))
+ if (!isNullOrEmpty && !TypeUtil.IsSpecialType(type) && state.ExpandedObjects.Contains(refChain.UniqueId))
{
- if (value is GameObject)
+ if (value is GameObject go)
{
- var go = value as GameObject;
foreach (var component in go.GetComponents())
{
GUIComponent.OnSceneTreeComponent(state, refChain, component);
}
}
- else if (value is Transform)
+ else if (value is Transform transforms)
{
- GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, (Transform)value);
+ GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, transforms);
}
else
{
diff --git a/Debugger/GUIExplorer/GUICollectionNavigation.cs b/Debugger/Explorer/GUICollectionNavigation.cs
similarity index 52%
rename from Debugger/GUIExplorer/GUICollectionNavigation.cs
rename to Debugger/Explorer/GUICollectionNavigation.cs
index 86211fe..74a8159 100644
--- a/Debugger/GUIExplorer/GUICollectionNavigation.cs
+++ b/Debugger/Explorer/GUICollectionNavigation.cs
@@ -1,31 +1,37 @@
-using System.Collections.Generic;
+using ModTools.UI;
using UnityEngine;
namespace ModTools.Explorer
{
- public static class GUICollectionNavigation
+ internal static class GUICollectionNavigation
{
- public static void SetUpCollectionNavigation(string collectionLabel, SceneExplorerState state, ReferenceChain refChain, ReferenceChain oldRefChain, int collectionSize, out int arrayStart,
- out int arrayEnd)
+ public static void SetUpCollectionNavigation(
+ string collectionLabel,
+ SceneExplorerState state,
+ ReferenceChain refChain,
+ ReferenceChain oldRefChain,
+ int collectionSize,
+ out int arrayStart,
+ out int arrayEnd)
{
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
+
GUILayout.Label($"{collectionLabel} size: {collectionSize}");
- if (!state.selectedArrayStartIndices.ContainsKey(refChain))
+ if (!state.SelectedArrayStartIndices.TryGetValue(refChain.UniqueId, out arrayStart))
{
- state.selectedArrayStartIndices.Add(refChain, 0);
+ state.SelectedArrayStartIndices.Add(refChain.UniqueId, 0);
}
- if (!state.selectedArrayEndIndices.ContainsKey(refChain))
+ if (!state.SelectedArrayEndIndices.TryGetValue(refChain.UniqueId, out arrayEnd))
{
- state.selectedArrayEndIndices.Add(refChain, 32);
+ state.SelectedArrayEndIndices.Add(refChain.UniqueId, 32);
+ arrayEnd = 32;
}
- arrayStart = state.selectedArrayStartIndices[refChain];
- arrayEnd = state.selectedArrayEndIndices[refChain];
- GUIControls.IntField($"{oldRefChain}.arrayStart", "Start index", ref arrayStart, 0.0f, true, true);
- GUIControls.IntField($"{oldRefChain}.arrayEnd", "End index", ref arrayEnd, 0.0f, true, true);
+ arrayStart = GUIControls.PrimitiveValueField($"{oldRefChain}.arrayStart", "Start index", arrayStart);
+ arrayEnd = GUIControls.PrimitiveValueField($"{oldRefChain}.arrayEnd", "End index", arrayEnd);
GUILayout.Label("(32 items max)");
var pageSize = Mathf.Clamp(arrayEnd - arrayStart + 1, 1, Mathf.Min(32, collectionSize - arrayStart, arrayEnd + 1));
if (GUILayout.Button("◄", GUILayout.ExpandWidth(false)))
@@ -33,11 +39,13 @@ public static void SetUpCollectionNavigation(string collectionLabel, SceneExplor
arrayStart -= pageSize;
arrayEnd -= pageSize;
}
+
if (GUILayout.Button("►", GUILayout.ExpandWidth(false)))
{
arrayStart += pageSize;
arrayEnd += pageSize;
}
+
arrayStart = Mathf.Clamp(arrayStart, 0, collectionSize - pageSize);
arrayEnd = Mathf.Clamp(arrayEnd, pageSize - 1, collectionSize - 1);
if (arrayStart > arrayEnd)
@@ -50,8 +58,9 @@ public static void SetUpCollectionNavigation(string collectionLabel, SceneExplor
arrayEnd = arrayStart + 32;
arrayEnd = Mathf.Clamp(arrayEnd, 32, collectionSize - 1);
}
- state.selectedArrayStartIndices[refChain] = arrayStart;
- state.selectedArrayEndIndices[refChain] = arrayEnd;
+
+ state.SelectedArrayStartIndices[refChain.UniqueId] = arrayStart;
+ state.SelectedArrayEndIndices[refChain.UniqueId] = arrayEnd;
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
diff --git a/Debugger/GUIExplorer/GUIComponent.cs b/Debugger/Explorer/GUIComponent.cs
similarity index 55%
rename from Debugger/GUIExplorer/GUIComponent.cs
rename to Debugger/Explorer/GUIComponent.cs
index 43acf20..f609fda 100644
--- a/Debugger/GUIExplorer/GUIComponent.cs
+++ b/Debugger/Explorer/GUIComponent.cs
@@ -2,11 +2,14 @@
namespace ModTools.Explorer
{
- public static class GUIComponent
+ internal static class GUIComponent
{
public static void OnSceneTreeComponent(SceneExplorerState state, ReferenceChain refChain, Component component)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
if (component == null)
{
@@ -15,31 +18,26 @@ public static void OnSceneTreeComponent(SceneExplorerState state, ReferenceChain
}
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
- if (Util.ComponentIsEnabled(component))
- {
- GUI.contentColor = ModTools.Instance.config.enabledComponentColor;
- }
- else
- {
- GUI.contentColor = ModTools.Instance.config.disabledComponentColor;
- }
+ GUI.contentColor = GameObjectUtil.ComponentIsEnabled(component)
+ ? MainWindow.Instance.Config.EnabledComponentColor
+ : MainWindow.Instance.Config.DisabledComponentColor;
- if (state.currentRefChain == null || !state.currentRefChain.Equals(refChain.Add(component)))
+ if (state.CurrentRefChain?.IsSameChain(refChain.Add(component)) != true)
{
if (GUILayout.Button(">", GUILayout.ExpandWidth(false)))
{
- state.currentRefChain = refChain.Add(component);
- state.currentRefChain.IdentOffset = -(refChain.Length + 1);
+ state.CurrentRefChain = refChain.Add(component);
+ state.CurrentRefChain.IdentOffset = -(refChain.Length + 1);
}
}
else
{
- GUI.contentColor = ModTools.Instance.config.selectedComponentColor;
+ GUI.contentColor = MainWindow.Instance.Config.SelectedComponentColor;
if (GUILayout.Button("<", GUILayout.ExpandWidth(false)))
{
- state.currentRefChain = null;
+ state.CurrentRefChain = null;
}
}
diff --git a/Debugger/GUIExplorer/GUIEnumerable.cs b/Debugger/Explorer/GUIEnumerable.cs
similarity index 59%
rename from Debugger/GUIExplorer/GUIEnumerable.cs
rename to Debugger/Explorer/GUIEnumerable.cs
index b0f701d..5de3198 100644
--- a/Debugger/GUIExplorer/GUIEnumerable.cs
+++ b/Debugger/Explorer/GUIEnumerable.cs
@@ -1,41 +1,44 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
+using System.Collections;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools.Explorer
{
- public static class GUIEnumerable
+ internal static class GUIEnumerable
{
public static void OnSceneTreeReflectIEnumerable(SceneExplorerState state, ReferenceChain refChain, object myProperty)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
- var enumerable = myProperty as IEnumerable;
- if (enumerable == null)
+ if (!(myProperty is IEnumerable enumerable))
{
return;
}
- int count = 0;
+ var count = 0;
var oldRefChain = refChain;
foreach (var value in enumerable)
{
refChain = oldRefChain.Add(count);
- var type = value.GetType();
-
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
- GUIExpander.ExpanderControls(state, refChain, type);
+ var type = value?.GetType();
+ if (type != null)
+ {
+ GUIExpander.ExpanderControls(state, refChain, type);
- GUI.contentColor = ModTools.Instance.config.typeColor;
+ GUI.contentColor = MainWindow.Instance.Config.TypeColor;
- GUILayout.Label(type.ToString() + " ");
+ GUILayout.Label(type.ToString() + " ");
+ }
- GUI.contentColor = ModTools.Instance.config.nameColor;
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
GUILayout.Label($"{oldRefChain.LastItemName}.[{count}]");
@@ -43,7 +46,7 @@ public static void OnSceneTreeReflectIEnumerable(SceneExplorerState state, Refer
GUILayout.Label(" = ");
- GUI.contentColor = ModTools.Instance.config.valueColor;
+ GUI.contentColor = MainWindow.Instance.Config.ValueColor;
GUILayout.Label(value == null ? "null" : value.ToString());
@@ -52,19 +55,18 @@ public static void OnSceneTreeReflectIEnumerable(SceneExplorerState state, Refer
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
- if (!TypeUtil.IsSpecialType(type) && state.expandedObjects.ContainsKey(refChain))
+ if (type != null && !TypeUtil.IsSpecialType(type) && state.ExpandedObjects.Contains(refChain.UniqueId))
{
- if (value is GameObject)
+ if (value is GameObject go)
{
- var go = value as GameObject;
foreach (var component in go.GetComponents())
{
GUIComponent.OnSceneTreeComponent(state, refChain, component);
}
}
- else if (value is Transform)
+ else if (value is Transform transform)
{
- GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, (Transform)value);
+ GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, transform);
}
else
{
@@ -73,9 +75,9 @@ public static void OnSceneTreeReflectIEnumerable(SceneExplorerState state, Refer
}
count++;
- if (count >= 1024)
+ if (count >= 128)
{
- SceneExplorerCommon.OnSceneTreeMessage(refChain, "Array too large to display");
+ SceneExplorerCommon.OnSceneTreeMessage(refChain, "Enumerable too large to display");
break;
}
}
diff --git a/Debugger/GUIExplorer/GUIExpander.cs b/Debugger/Explorer/GUIExpander.cs
similarity index 50%
rename from Debugger/GUIExplorer/GUIExpander.cs
rename to Debugger/Explorer/GUIExpander.cs
index 6888ad4..70f77d7 100644
--- a/Debugger/GUIExplorer/GUIExpander.cs
+++ b/Debugger/Explorer/GUIExpander.cs
@@ -1,31 +1,29 @@
using System;
-using System.Collections.Generic;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools.Explorer
{
- public static class GUIExpander
+ internal static class GUIExpander
{
public static void ExpanderControls(SceneExplorerState state, ReferenceChain refChain, Type type, object o = null)
{
GUI.contentColor = Color.white;
- if (TypeUtil.IsSpecialType(type) || (o!=null && TypeUtil.IsEnumerable(o)))
+ if (TypeUtil.IsSpecialType(type) || o != null && TypeUtil.IsEnumerable(o))
{
return;
}
- if (state.expandedObjects.ContainsKey(refChain))
+
+ if (state.ExpandedObjects.Contains(refChain.UniqueId))
{
if (GUILayout.Button("-", GUILayout.ExpandWidth(false)))
{
- state.expandedObjects.Remove(refChain);
+ state.ExpandedObjects.Remove(refChain.UniqueId);
}
}
- else
+ else if (GUILayout.Button("+", GUILayout.ExpandWidth(false)))
{
- if (GUILayout.Button("+", GUILayout.ExpandWidth(false)))
- {
- state.expandedObjects.Add(refChain, true);
- }
+ state.ExpandedObjects.Add(refChain.UniqueId);
}
}
}
diff --git a/Debugger/GUIExplorer/GUIField.cs b/Debugger/Explorer/GUIField.cs
similarity index 71%
rename from Debugger/GUIExplorer/GUIField.cs
rename to Debugger/Explorer/GUIField.cs
index 22f3c00..802cb23 100644
--- a/Debugger/GUIExplorer/GUIField.cs
+++ b/Debugger/Explorer/GUIField.cs
@@ -1,17 +1,19 @@
using System;
-using System.Collections.Generic;
using System.Reflection;
+using ModTools.UI;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools.Explorer
{
- public static class GUIField
+ internal static class GUIField
{
public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceChain refChain, object obj, FieldInfo field)
{
- var hash = refChain.GetHashCode().ToString();
-
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
if (obj == null || field == null)
{
@@ -20,7 +22,7 @@ public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceCh
}
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
GUI.contentColor = Color.white;
@@ -32,7 +34,7 @@ public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceCh
}
catch (Exception e)
{
- UnityEngine.Debug.LogException(e);
+ Debug.LogException(e);
}
if (value != null)
@@ -45,9 +47,9 @@ public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceCh
GUI.enabled = false;
}
- if (ModTools.Instance.config.sceneExplorerShowModifiers)
+ if (MainWindow.Instance.Config.ShowModifiers)
{
- GUI.contentColor = ModTools.Instance.config.modifierColor;
+ GUI.contentColor = MainWindow.Instance.Config.ModifierColor;
if (field.IsPublic)
{
@@ -58,34 +60,33 @@ public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceCh
GUILayout.Label("private ");
}
- GUI.contentColor = ModTools.Instance.config.memberTypeColor;
+ GUI.contentColor = MainWindow.Instance.Config.MemberTypeColor;
GUILayout.Label("field ");
if (field.IsStatic)
{
- GUI.contentColor = ModTools.Instance.config.keywordColor;
+ GUI.contentColor = MainWindow.Instance.Config.KeywordColor;
GUILayout.Label("static ");
}
if (field.IsInitOnly)
{
- GUI.contentColor = ModTools.Instance.config.keywordColor;
+ GUI.contentColor = MainWindow.Instance.Config.KeywordColor;
GUILayout.Label("const ");
}
}
- GUI.contentColor = ModTools.Instance.config.typeColor;
+ GUI.contentColor = MainWindow.Instance.Config.TypeColor;
GUILayout.Label(field.FieldType + " ");
- GUI.contentColor = ModTools.Instance.config.nameColor;
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
GUILayout.Label(field.Name);
- GUI.contentColor = Color.white;
GUI.contentColor = Color.white;
GUILayout.Label(" = ");
- GUI.contentColor = ModTools.Instance.config.valueColor;
+ GUI.contentColor = MainWindow.Instance.Config.ValueColor;
if (value == null || !TypeUtil.IsSpecialType(field.FieldType))
{
@@ -95,8 +96,8 @@ public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceCh
{
try
{
- var newValue = GUIControls.EditorValueField(refChain, hash, field.FieldType, value);
- if (newValue != value)
+ var newValue = GUIControls.EditorValueField(refChain.UniqueId, field.FieldType, value);
+ if (!newValue.Equals(value))
{
field.SetValue(obj, newValue);
}
@@ -113,35 +114,37 @@ public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceCh
GUILayout.FlexibleSpace();
if (GUILayout.Button("Watch"))
{
- ModTools.Instance.watches.AddWatch(refChain);
+ MainWindow.Instance.Watches.AddWatch(refChain);
}
- GUIButtons.SetupButtons(field.FieldType, value, refChain);
+
+ GUIButtons.SetupButtons(refChain, field.FieldType, value, valueIndex: -1);
object paste = null;
var doPaste = !field.IsLiteral && !field.IsInitOnly;
if (doPaste)
{
doPaste = GUIButtons.SetupPasteButon(field.FieldType, out paste);
}
+
GUILayout.EndHorizontal();
- if (value != null && !TypeUtil.IsSpecialType(field.FieldType) && state.expandedObjects.ContainsKey(refChain))
+ if (value != null && !TypeUtil.IsSpecialType(field.FieldType) && state.ExpandedObjects.Contains(refChain.UniqueId))
{
- if (value is GameObject)
+ if (value is GameObject go)
{
- var go = value as GameObject;
foreach (var component in go.GetComponents())
{
GUIComponent.OnSceneTreeComponent(state, refChain, component);
}
}
- else if (value is Transform)
+ else if (value is Transform transform)
{
- GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, (Transform)value);
+ GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, transform);
}
else
{
GUIReflect.OnSceneTreeReflect(state, refChain, value);
}
}
+
if (doPaste)
{
try
@@ -150,10 +153,9 @@ public static void OnSceneTreeReflectField(SceneExplorerState state, ReferenceCh
}
catch (Exception e)
{
- Log.Warning(e.Message);
+ Logger.Warning(e.Message);
}
}
-
}
}
}
\ No newline at end of file
diff --git a/Debugger/Explorer/GUIList.cs b/Debugger/Explorer/GUIList.cs
new file mode 100644
index 0000000..dc7a9f9
--- /dev/null
+++ b/Debugger/Explorer/GUIList.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections;
+using ModTools.Utils;
+using UnityEngine;
+
+namespace ModTools.Explorer
+{
+ internal static class GUIList
+ {
+ public static void OnSceneTreeReflectIList(SceneExplorerState state, ReferenceChain refChain, object myProperty)
+ {
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
+
+ if (!(myProperty is IList list))
+ {
+ return;
+ }
+
+ var oldRefChain = refChain;
+ var collectionSize = list.Count;
+ if (collectionSize == 0)
+ {
+ GUILayout.BeginHorizontal();
+ GUI.contentColor = Color.yellow;
+ GUILayout.Label("List is empty!");
+ GUI.contentColor = Color.white;
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+ return;
+ }
+
+ var listItemType = list.GetType().GetElementType();
+ var flagsField = listItemType?.GetField("m_flags");
+ var flagIsEnum = flagsField?.FieldType.IsEnum == true && Type.GetTypeCode(flagsField.FieldType) == TypeCode.Int32;
+
+ GUICollectionNavigation.SetUpCollectionNavigation("List", state, refChain, oldRefChain, collectionSize, out var arrayStart, out var arrayEnd);
+ for (var i = arrayStart; i <= arrayEnd; i++)
+ {
+ refChain = oldRefChain.Add(i);
+
+ GUILayout.BeginHorizontal();
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
+
+ GUI.contentColor = Color.white;
+
+ var value = list[i];
+ var type = value?.GetType() ?? listItemType;
+ var isNullOrEmpty = value == null || flagIsEnum && Convert.ToInt32(flagsField.GetValue(value)) == 0;
+ if (type != null)
+ {
+ if (!isNullOrEmpty)
+ {
+ GUIExpander.ExpanderControls(state, refChain, type);
+ }
+
+ GUI.contentColor = MainWindow.Instance.Config.TypeColor;
+
+ GUILayout.Label($"{type} ");
+ }
+
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
+
+ GUILayout.Label($"{oldRefChain.LastItemName}.[{i}]");
+
+ GUI.contentColor = Color.white;
+
+ GUILayout.Label(" = ");
+
+ GUI.contentColor = MainWindow.Instance.Config.ValueColor;
+
+ GUILayout.Label(value == null ? "null" : isNullOrEmpty ? "empty" : value.ToString());
+
+ GUI.contentColor = Color.white;
+
+ GUILayout.FlexibleSpace();
+
+ if (!isNullOrEmpty)
+ {
+ GUIButtons.SetupButtons(refChain, type, value, i);
+ }
+
+ GUILayout.EndHorizontal();
+
+ if (!isNullOrEmpty && !TypeUtil.IsSpecialType(type) && state.ExpandedObjects.Contains(refChain.UniqueId))
+ {
+ if (value is GameObject go)
+ {
+ foreach (var component in go.GetComponents())
+ {
+ GUIComponent.OnSceneTreeComponent(state, refChain, component);
+ }
+ }
+ else if (value is Transform transform)
+ {
+ GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, transform);
+ }
+ else
+ {
+ GUIReflect.OnSceneTreeReflect(state, refChain, value);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/GUIExplorer/GUIMaterial.cs b/Debugger/Explorer/GUIMaterial.cs
similarity index 52%
rename from Debugger/GUIExplorer/GUIMaterial.cs
rename to Debugger/Explorer/GUIMaterial.cs
index fabc80f..430b4d1 100644
--- a/Debugger/GUIExplorer/GUIMaterial.cs
+++ b/Debugger/Explorer/GUIMaterial.cs
@@ -1,13 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using ModTools.UI;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools.Explorer
{
- public static class GUIMaterial
+ internal static class GUIMaterial
{
- private static readonly string[] textureProps = {
+ private static readonly string[] TextureProps =
+ {
"_BackTex",
"_BumpMap",
"_BumpSpecMap",
@@ -43,38 +43,24 @@ public static class GUIMaterial
"luminTex",
"searchTex",
"_SrcTex",
- "_Blurred"
+ "_Blurred",
};
- private static readonly string[] colorProps = {
+ private static readonly string[] ColorProps =
+ {
"_Color",
"_ColorV0",
"_ColorV1",
"_ColorV2",
- "_ColorV3"
- };
-
- private static readonly string[] vectorProps = new string[]
- {
- "_FloorParams",
- "_UvAnimation",
- "_WindAnimation",
- "_WindAnimationB",
- "_TyreLocation0",
- "_TyreLocation1",
- "_TyreLocation2",
- "_TyreLocation3",
- "_TyreLocation4",
- "_TyreLocation5",
- "_TyreLocation6",
- "_TyreLocation7",
- "_TyreParams"
+ "_ColorV3",
};
-
public static void OnSceneReflectUnityEngineMaterial(SceneExplorerState state, ReferenceChain refChain, Material material)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
if (material == null)
{
@@ -82,9 +68,9 @@ public static void OnSceneReflectUnityEngineMaterial(SceneExplorerState state, R
return;
}
- ReferenceChain oldRefChain = refChain;
+ var oldRefChain = refChain;
- foreach (var prop in textureProps)
+ foreach (var prop in TextureProps)
{
if (!material.HasProperty(prop))
{
@@ -102,15 +88,15 @@ public static void OnSceneReflectUnityEngineMaterial(SceneExplorerState state, R
var type = value.GetType();
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * (refChain.Ident + 1));
+ SceneExplorerCommon.InsertIndent(refChain.Ident + 1);
GUIExpander.ExpanderControls(state, refChain, type);
- GUI.contentColor = ModTools.Instance.config.typeColor;
+ GUI.contentColor = MainWindow.Instance.Config.TypeColor;
GUILayout.Label(type.ToString() + " ");
- GUI.contentColor = ModTools.Instance.config.nameColor;
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
GUILayout.Label(prop);
@@ -118,17 +104,16 @@ public static void OnSceneReflectUnityEngineMaterial(SceneExplorerState state, R
GUILayout.Label(" = ");
- GUI.contentColor = ModTools.Instance.config.valueColor;
+ GUI.contentColor = MainWindow.Instance.Config.ValueColor;
GUILayout.Label(value.ToString());
GUI.contentColor = Color.white;
GUILayout.FlexibleSpace();
- GUIButtons.SetupButtons(type, value, refChain);
- object paste;
- var doPaste = GUIButtons.SetupPasteButon(type, out paste);
+ GUIButtons.SetupButtons(refChain, type, value, valueIndex: -1);
+ var doPaste = GUIButtons.SetupPasteButon(type, out var paste);
GUILayout.EndHorizontal();
- if (!TypeUtil.IsSpecialType(type) && state.expandedObjects.ContainsKey(refChain))
+ if (!TypeUtil.IsSpecialType(type) && state.ExpandedObjects.Contains(refChain.UniqueId))
{
GUIReflect.OnSceneTreeReflect(state, refChain, value);
}
@@ -139,86 +124,61 @@ public static void OnSceneReflectUnityEngineMaterial(SceneExplorerState state, R
}
}
- foreach (string prop in colorProps)
+ foreach (var prop in ColorProps)
{
if (!material.HasProperty(prop))
{
continue;
}
- Color value = material.GetColor(prop);
+ var value = material.GetColor(prop);
refChain = oldRefChain.Add(prop);
var type = value.GetType();
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * (refChain.Ident + 1));
+ SceneExplorerCommon.InsertIndent(refChain.Ident + 1);
GUIExpander.ExpanderControls(state, refChain, type);
- GUI.contentColor = ModTools.Instance.config.typeColor;
+ GUI.contentColor = MainWindow.Instance.Config.TypeColor;
GUILayout.Label(type.ToString() + " ");
- GUI.contentColor = ModTools.Instance.config.nameColor;
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
GUILayout.Label(prop);
GUI.contentColor = Color.white;
GUILayout.Label(" = ");
- var f = value;
- GUI.contentColor = ModTools.Instance.config.valueColor;
+ GUI.contentColor = MainWindow.Instance.Config.ValueColor;
- var propertyCopy = prop;
- GUIControls.ColorField(refChain.ToString(), "", ref f, 0.0f, null, true, true, color => { material.SetColor(propertyCopy, color); });
- if (f != value)
+ var newColor = GUIControls.CustomValueField(refChain.UniqueId, string.Empty, GUIControls.PresentColor, value);
+ if (newColor != value)
{
- material.SetColor(prop, f);
+ material.SetColor(prop, newColor);
}
GUI.contentColor = Color.white;
GUILayout.FlexibleSpace();
- GUIButtons.SetupButtons(type, value, refChain);
- object paste;
- var doPaste = GUIButtons.SetupPasteButon(type, out paste);
+ GUIButtons.SetupButtons(refChain, type, value, valueIndex: -1);
+ var doPaste = GUIButtons.SetupPasteButon(type, out var paste);
GUILayout.EndHorizontal();
- if (!TypeUtil.IsSpecialType(type) && state.expandedObjects.ContainsKey(refChain))
+ if (!TypeUtil.IsSpecialType(type) && state.ExpandedObjects.Contains(refChain.UniqueId))
{
GUIReflect.OnSceneTreeReflect(state, refChain, value);
}
+
if (doPaste)
{
material.SetColor(prop, (Color)paste);
}
}
-// GUILayout.BeginHorizontal();
-// GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * (refChain.Ident + 1));
-// GUI.contentColor = ModTools.Instance.config.typeColor;
-//
-// GUILayout.Label("Shader:");
-//
-// GUI.contentColor = ModTools.Instance.config.nameColor;
-//
-// var shaders = Resources.FindObjectsOfTypeAll();
-// Array.Sort(shaders, (a, b) => string.Compare(a.name, b.name, StringComparison.Ordinal));
-// var availableShaders = shaders.Select(s => s.name).ToArray();
-// var currentShader = material.shader;
-// var selectedShader = Array.IndexOf(shaders, currentShader);
-//
-// var newSelectedShader = GUIComboBox.Box(selectedShader, availableShaders, "SceneExplorerShadersComboBox");
-// if (newSelectedShader != selectedShader)
-// {
-// material.shader = shaders[newSelectedShader];
-// }
-// GUILayout.FlexibleSpace();
-// GUILayout.EndHorizontal();
GUIReflect.OnSceneTreeReflect(state, refChain, material, true);
}
-
-
}
}
\ No newline at end of file
diff --git a/Debugger/GUIExplorer/GUIMethod.cs b/Debugger/Explorer/GUIMethod.cs
similarity index 70%
rename from Debugger/GUIExplorer/GUIMethod.cs
rename to Debugger/Explorer/GUIMethod.cs
index a3096ed..4a9dfdf 100644
--- a/Debugger/GUIExplorer/GUIMethod.cs
+++ b/Debugger/Explorer/GUIMethod.cs
@@ -1,14 +1,16 @@
-using System.Linq;
-using System.Reflection;
+using System.Reflection;
using UnityEngine;
namespace ModTools.Explorer
{
- public class GUIMethod
+ internal static class GUIMethod
{
- public static void OnSceneTreeReflectMethod(ReferenceChain refChain, System.Object obj, MethodInfo method)
+ public static void OnSceneTreeReflectMethod(ReferenceChain refChain, object obj, MethodInfo method)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
if (obj == null || method == null)
{
@@ -17,17 +19,16 @@ public static void OnSceneTreeReflectMethod(ReferenceChain refChain, System.Obje
}
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
- GUI.contentColor = ModTools.Instance.config.memberTypeColor;
+ GUI.contentColor = MainWindow.Instance.Config.MemberTypeColor;
GUILayout.Label("method ");
GUI.contentColor = Color.white;
GUILayout.Label(method.ReturnType.ToString() + " " + method.Name + "(");
- GUI.contentColor = ModTools.Instance.config.nameColor;
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
var first = true;
- var parameters = method.GetParameters();
- foreach (var param in parameters)
+ foreach (var param in method.GetParameters())
{
if (!first)
{
@@ -40,29 +41,30 @@ public static void OnSceneTreeReflectMethod(ReferenceChain refChain, System.Obje
GUILayout.Label(param.ParameterType.ToString() + " " + param.Name);
}
+
GUI.contentColor = Color.white;
GUILayout.Label(")");
GUILayout.FlexibleSpace();
if (!method.IsGenericMethod)
{
- if (!method.GetParameters().Any())
+ if (method.GetParameters().Length == 0)
{
if (GUILayout.Button("Invoke", GUILayout.ExpandWidth(false)))
{
method.Invoke(method.IsStatic ? null : obj, new object[] { });
}
}
- else if (method.GetParameters().Length == 1 &&
- method.GetParameters()[0].ParameterType.IsInstanceOfType(obj))
+ else if (method.GetParameters().Length == 1
+ && method.GetParameters()[0].ParameterType.IsInstanceOfType(obj))
{
if (GUILayout.Button("Invoke", GUILayout.ExpandWidth(false)))
{
method.Invoke(method.IsStatic ? null : obj, new[] { obj });
}
}
-
}
+
GUILayout.EndHorizontal();
}
}
diff --git a/Debugger/GUIExplorer/GUIProperty.cs b/Debugger/Explorer/GUIProperty.cs
similarity index 76%
rename from Debugger/GUIExplorer/GUIProperty.cs
rename to Debugger/Explorer/GUIProperty.cs
index 6375d30..8a2f0d6 100644
--- a/Debugger/GUIExplorer/GUIProperty.cs
+++ b/Debugger/Explorer/GUIProperty.cs
@@ -1,16 +1,20 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
+using ModTools.UI;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools.Explorer
{
- public class GUIProperty
+ internal static class GUIProperty
{
- public static void OnSceneTreeReflectProperty(SceneExplorerState state, ReferenceChain refChain, System.Object obj, PropertyInfo property)
+ public static void OnSceneTreeReflectProperty(SceneExplorerState state, ReferenceChain refChain, object obj, PropertyInfo property)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
if (obj == null || property == null)
{
@@ -18,17 +22,15 @@ public static void OnSceneTreeReflectProperty(SceneExplorerState state, Referenc
return;
}
- var hash = refChain.GetHashCode().ToString();
-
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
- bool propertyWasEvaluated = false;
+ var propertyWasEvaluated = false;
object value = null;
Exception exceptionOnGetting = null;
- if (property.CanRead && ModTools.Instance.config.sceneExplorerEvaluatePropertiesAutomatically || state.evaluatedProperties.ContainsKey(refChain))
+ if (property.CanRead && MainWindow.Instance.Config.EvaluateProperties || state.EvaluatedProperties.Contains(refChain.UniqueId))
{
try
{
@@ -53,37 +55,37 @@ public static void OnSceneTreeReflectProperty(SceneExplorerState state, Referenc
GUI.enabled = false;
}
- if (ModTools.Instance.config.sceneExplorerShowModifiers)
+ if (MainWindow.Instance.Config.ShowModifiers)
{
- GUI.contentColor = ModTools.Instance.config.memberTypeColor;
+ GUI.contentColor = MainWindow.Instance.Config.MemberTypeColor;
GUILayout.Label("property ");
if (!property.CanWrite)
{
- GUI.contentColor = ModTools.Instance.config.keywordColor;
+ GUI.contentColor = MainWindow.Instance.Config.KeywordColor;
GUILayout.Label("const ");
}
}
- GUI.contentColor = ModTools.Instance.config.typeColor;
+ GUI.contentColor = MainWindow.Instance.Config.TypeColor;
GUILayout.Label(property.PropertyType.ToString() + " ");
- GUI.contentColor = ModTools.Instance.config.nameColor;
+ GUI.contentColor = MainWindow.Instance.Config.NameColor;
GUILayout.Label(property.Name);
GUI.contentColor = Color.white;
GUILayout.Label(" = ");
- GUI.contentColor = ModTools.Instance.config.valueColor;
+ GUI.contentColor = MainWindow.Instance.Config.ValueColor;
- if (!ModTools.Instance.config.sceneExplorerEvaluatePropertiesAutomatically && !state.evaluatedProperties.ContainsKey(refChain))
+ if (!MainWindow.Instance.Config.EvaluateProperties && !state.EvaluatedProperties.Contains(refChain.UniqueId))
{
GUI.enabled = true;
if (GUILayout.Button("Evaluate"))
{
- state.evaluatedProperties.Add(refChain, true);
+ state.EvaluatedProperties.Add(refChain.UniqueId);
}
}
else
@@ -100,6 +102,7 @@ public static void OnSceneTreeReflectProperty(SceneExplorerState state, Referenc
exceptionOnGetting = e;
}
}
+
if (exceptionOnGetting != null)
{
GUI.contentColor = Color.red;
@@ -113,7 +116,6 @@ public static void OnSceneTreeReflectProperty(SceneExplorerState state, Referenc
return;
}
-
if (value == null || !TypeUtil.IsSpecialType(property.PropertyType))
{
if (property.CanRead)
@@ -131,7 +133,7 @@ public static void OnSceneTreeReflectProperty(SceneExplorerState state, Referenc
{
try
{
- var newValue = GUIControls.EditorValueField(refChain, hash, property.PropertyType, value);
+ var newValue = GUIControls.EditorValueField(refChain.UniqueId, property.PropertyType, value);
if (newValue != value)
{
property.SetValue(obj, newValue, null);
@@ -160,36 +162,38 @@ public static void OnSceneTreeReflectProperty(SceneExplorerState state, Referenc
if (GUILayout.Button("Watch"))
{
- ModTools.Instance.watches.AddWatch(refChain);
+ MainWindow.Instance.Watches.AddWatch(refChain);
}
- GUIButtons.SetupButtons(property.PropertyType, value, refChain);
+
+ GUIButtons.SetupButtons(refChain, property.PropertyType, value, valueIndex: -1);
object paste = null;
var doPaste = property.CanWrite;
if (doPaste)
{
doPaste = GUIButtons.SetupPasteButon(property.PropertyType, out paste);
}
+
GUILayout.EndHorizontal();
- if (value != null && state.expandedObjects.ContainsKey(refChain))
+ if (value != null && state.ExpandedObjects.Contains(refChain.UniqueId))
{
- if (value is GameObject)
+ if (value is GameObject go)
{
- var go = value as GameObject;
foreach (var component in go.GetComponents())
{
GUIComponent.OnSceneTreeComponent(state, refChain, component);
}
}
- else if (value is Transform)
+ else if (value is Transform transform)
{
- GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, (Transform)value);
+ GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, transform);
}
else
{
GUIReflect.OnSceneTreeReflect(state, refChain, value);
}
}
+
if (doPaste)
{
try
@@ -198,10 +202,9 @@ public static void OnSceneTreeReflectProperty(SceneExplorerState state, Referenc
}
catch (Exception e)
{
- Log.Warning(e.Message);
+ Logger.Warning(e.Message);
}
}
}
-
}
}
\ No newline at end of file
diff --git a/Debugger/GUIExplorer/GUIRecursiveTree.cs b/Debugger/Explorer/GUIRecursiveTree.cs
similarity index 63%
rename from Debugger/GUIExplorer/GUIRecursiveTree.cs
rename to Debugger/Explorer/GUIRecursiveTree.cs
index 4f1c963..9ad9cfb 100644
--- a/Debugger/GUIExplorer/GUIRecursiveTree.cs
+++ b/Debugger/Explorer/GUIRecursiveTree.cs
@@ -3,41 +3,41 @@
namespace ModTools.Explorer
{
- public class GUIRecursiveTree
+ internal static class GUIRecursiveTree
{
public static void OnSceneTreeRecursive(GameObject modToolsGo, SceneExplorerState state, ReferenceChain refChain, GameObject obj)
{
- if (obj == modToolsGo && !ModTools.DEBUG_MODTOOLS)
+#if !DEBUG
+ if (obj == modToolsGo)
{
return;
}
+#endif
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
-
- if (obj == null)
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
{
- SceneExplorerCommon.OnSceneTreeMessage(refChain, "null");
return;
}
- if (obj.name == "_ModToolsInternal" && !ModTools.DEBUG_MODTOOLS)
+ if (obj == null)
{
+ SceneExplorerCommon.OnSceneTreeMessage(refChain, "null");
return;
}
- if (state.expandedGameObjects.ContainsKey(refChain))
+ if (state.ExpandedGameObjects.Contains(refChain.UniqueId))
{
try
{
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
if (GUILayout.Button("-", GUILayout.ExpandWidth(false)))
{
- state.expandedGameObjects.Remove(refChain);
+ state.ExpandedGameObjects.Remove(refChain.UniqueId);
}
- GUI.contentColor = ModTools.Instance.config.gameObjectColor;
+ GUI.contentColor = MainWindow.Instance.Config.GameObjectColor;
GUILayout.Label(obj.name);
GUI.contentColor = Color.white;
@@ -45,9 +45,9 @@ public static void OnSceneTreeRecursive(GameObject modToolsGo, SceneExplorerStat
var components = obj.GetComponents(typeof(Component));
- if (ModTools.Instance.config.sceneExplorerSortAlphabetically)
+ if (MainWindow.Instance.Config.SortItemsAlphabetically)
{
- Array.Sort(components, (component, component1) => component.GetType().ToString().CompareTo(component1.GetType().ToString()));
+ Array.Sort(components, (x, y) => string.CompareOrdinal(x.GetType().ToString(), y.GetType().ToString()));
}
foreach (var component in components)
@@ -55,28 +55,28 @@ public static void OnSceneTreeRecursive(GameObject modToolsGo, SceneExplorerStat
GUIComponent.OnSceneTreeComponent(state, refChain.Add(component), component);
}
- for (int i = 0; i < obj.transform.childCount; i++)
+ for (var i = 0; i < obj.transform.childCount; i++)
{
OnSceneTreeRecursive(modToolsGo, state, refChain.Add(obj.transform.GetChild(i)), obj.transform.GetChild(i).gameObject);
}
}
catch (Exception)
{
- state.expandedGameObjects.Remove(refChain);
+ state.ExpandedGameObjects.Remove(refChain.UniqueId);
throw;
}
}
else
{
GUILayout.BeginHorizontal();
- GUILayout.Space(ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident);
+ SceneExplorerCommon.InsertIndent(refChain.Ident);
if (GUILayout.Button("+", GUILayout.ExpandWidth(false)))
{
- state.expandedGameObjects.Add(refChain, true);
+ state.ExpandedGameObjects.Add(refChain.UniqueId);
}
- GUI.contentColor = ModTools.Instance.config.gameObjectColor;
+ GUI.contentColor = MainWindow.Instance.Config.GameObjectColor;
GUILayout.Label(obj.name);
GUI.contentColor = Color.white;
GUILayout.EndHorizontal();
diff --git a/Debugger/GUIExplorer/GUIReflect.cs b/Debugger/Explorer/GUIReflect.cs
similarity index 66%
rename from Debugger/GUIExplorer/GUIReflect.cs
rename to Debugger/Explorer/GUIReflect.cs
index 430ecc0..caf6e66 100644
--- a/Debugger/GUIExplorer/GUIReflect.cs
+++ b/Debugger/Explorer/GUIReflect.cs
@@ -1,14 +1,18 @@
using System;
using System.Reflection;
+using ModTools.Utils;
using UnityEngine;
namespace ModTools.Explorer
{
- public static class GUIReflect
+ internal static class GUIReflect
{
- public static void OnSceneTreeReflect(SceneExplorerState state, ReferenceChain refChain, System.Object obj, bool rawReflection = false)
+ public static void OnSceneTreeReflect(SceneExplorerState state, ReferenceChain refChain, object obj, bool rawReflection = false)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
if (obj == null)
{
@@ -16,32 +20,30 @@ public static void OnSceneTreeReflect(SceneExplorerState state, ReferenceChain r
return;
}
- Type type = obj.GetType();
+ var type = obj.GetType();
if (!rawReflection)
{
- if (!type.IsValueType)
+ if (!type.IsValueType && state.PreventCircularReferences.Contains(obj))
{
- if (state.preventCircularReferences.Contains(obj))
+ try
{
- try
- {
- GUI.contentColor = Color.yellow;
- SceneExplorerCommon.OnSceneTreeMessage(refChain, "Circular reference detected");
- }
- finally
- {
- GUI.contentColor = Color.white;
- }
- return;
+ GUI.contentColor = Color.yellow;
+ SceneExplorerCommon.OnSceneTreeMessage(refChain, "Circular reference detected");
}
+ finally
+ {
+ GUI.contentColor = Color.white;
+ }
+
+ return;
}
- state.preventCircularReferences.Add(obj);
+ state.PreventCircularReferences.Add(obj);
- if (type == typeof(UnityEngine.Transform))
+ if (type == typeof(Transform))
{
- GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, (UnityEngine.Transform)obj);
+ GUITransform.OnSceneTreeReflectUnityEngineTransform(refChain, (Transform)obj);
return;
}
@@ -65,33 +67,35 @@ public static void OnSceneTreeReflect(SceneExplorerState state, ReferenceChain r
if (type == typeof(Material))
{
- GUIMaterial.OnSceneReflectUnityEngineMaterial(state, refChain, (UnityEngine.Material)obj);
+ GUIMaterial.OnSceneReflectUnityEngineMaterial(state, refChain, (Material)obj);
return;
}
- if (type == typeof(Mesh))
+
+ if (type == typeof(Mesh) && !((Mesh)obj).isReadable)
{
- if (!((Mesh)obj).isReadable)
- {
- SceneExplorerCommon.OnSceneTreeMessage(refChain, "Mesh is not readable");
- return;
- }
+ SceneExplorerCommon.OnSceneTreeMessage(refChain, "Mesh is not readable");
+ return;
}
-
}
- var members = TypeUtil.GetAllMembers(type, ModTools.Instance.config.sceneExplorerShowInheritedMembers);
+ var members = TypeUtil.GetAllMembers(type, MainWindow.Instance.Config.ShowInheritedMembers);
- if (ModTools.Instance.config.sceneExplorerSortAlphabetically)
+ if (MainWindow.Instance.Config.SortItemsAlphabetically)
{
- Array.Sort(members, (info, info1) => string.Compare(info.Name, info1.Name, StringComparison.Ordinal));
+ Array.Sort(members, (x, y) => string.CompareOrdinal(x.Name, y.Name));
}
- foreach (MemberInfo member in members)
+ foreach (var member in members)
{
- if (member.MemberType == MemberTypes.Field && ModTools.Instance.config.sceneExplorerShowFields)
+ if (member.MemberType == MemberTypes.Field && MainWindow.Instance.Config.ShowFields)
{
var field = (FieldInfo)member;
+ if (field.IsLiteral && !field.IsInitOnly && !MainWindow.Instance.Config.ShowConsts)
+ {
+ continue;
+ }
+
try
{
GUIField.OnSceneTreeReflectField(state, refChain.Add(field), obj, field);
@@ -101,7 +105,7 @@ public static void OnSceneTreeReflect(SceneExplorerState state, ReferenceChain r
SceneExplorerCommon.OnSceneTreeMessage(refChain, $"Exception when fetching field \"{field.Name}\" - {ex.Message}\n{ex.StackTrace}");
}
}
- else if (member.MemberType == MemberTypes.Property && ModTools.Instance.config.sceneExplorerShowProperties)
+ else if (member.MemberType == MemberTypes.Property && MainWindow.Instance.Config.ShowProperties)
{
var property = (PropertyInfo)member;
@@ -114,7 +118,7 @@ public static void OnSceneTreeReflect(SceneExplorerState state, ReferenceChain r
SceneExplorerCommon.OnSceneTreeMessage(refChain, $"Exception when fetching property \"{property.Name}\" - {ex.Message}\n{ex.StackTrace}");
}
}
- else if (member.MemberType == MemberTypes.Method && ModTools.Instance.config.sceneExplorerShowMethods)
+ else if (member.MemberType == MemberTypes.Method && MainWindow.Instance.Config.ShowMethods)
{
var method = (MethodInfo)member;
diff --git a/Debugger/GUIExplorer/GUITransform.cs b/Debugger/Explorer/GUITransform.cs
similarity index 52%
rename from Debugger/GUIExplorer/GUITransform.cs
rename to Debugger/Explorer/GUITransform.cs
index 5b6ea75..dc7df03 100644
--- a/Debugger/GUIExplorer/GUITransform.cs
+++ b/Debugger/Explorer/GUITransform.cs
@@ -1,77 +1,76 @@
-using System;
+using ModTools.UI;
using UnityEngine;
namespace ModTools.Explorer
{
- public class GUITransform
+ internal static class GUITransform
{
public static void OnSceneTreeReflectUnityEngineTransform(ReferenceChain refChain, Transform transform)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
+ {
+ return;
+ }
if (transform == null)
{
- SceneExplorerCommon.OnSceneTreeMessage(refChain, (string) "null");
+ SceneExplorerCommon.OnSceneTreeMessage(refChain, "null");
return;
}
var position = transform.position;
- OnSceneTreeReflectUnityEngineVector3(refChain.Add("position"), transform, "position", ref position);
+ OnSceneTreeReflectUnityEngineVector3(refChain.Add("position"), "position", ref position);
transform.position = position;
var localPosition = transform.localPosition;
- OnSceneTreeReflectUnityEngineVector3(refChain.Add("localPosition"), transform, "localPosition", ref localPosition);
+ OnSceneTreeReflectUnityEngineVector3(refChain.Add("localPosition"), "localPosition", ref localPosition);
transform.localPosition = localPosition;
var localEulerAngles = transform.localEulerAngles;
- OnSceneTreeReflectUnityEngineVector3(refChain.Add("localEulerAngles"), transform, "localEulerAngles", ref localEulerAngles);
+ OnSceneTreeReflectUnityEngineVector3(refChain.Add("localEulerAngles"), "localEulerAngles", ref localEulerAngles);
transform.localEulerAngles = localEulerAngles;
var rotation = transform.rotation;
- OnSceneTreeReflectUnityEngineQuaternion(refChain.Add("rotation"), transform, "rotation", ref rotation);
+ OnSceneTreeReflectUnityEngineQuaternion(refChain.Add("rotation"), "rotation", ref rotation);
transform.rotation = rotation;
var localRotation = transform.localRotation;
- OnSceneTreeReflectUnityEngineQuaternion(refChain.Add("localRotation"), transform, "localRotation", ref localRotation);
+ OnSceneTreeReflectUnityEngineQuaternion(refChain.Add("localRotation"), "localRotation", ref localRotation);
transform.localRotation = localRotation;
var localScale = transform.localScale;
- OnSceneTreeReflectUnityEngineVector3(refChain.Add("localScale"), transform, "localScale", ref localScale);
+ OnSceneTreeReflectUnityEngineVector3(refChain.Add("localScale"), "localScale", ref localScale);
transform.localScale = localScale;
}
- private static void OnSceneTreeReflectUnityEngineQuaternion(ReferenceChain refChain, T obj, string name, ref Quaternion vec)
+ private static void OnSceneTreeReflectUnityEngineQuaternion(ReferenceChain refChain, string name, ref Quaternion vec)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
-
- GUIControls.QuaternionField(refChain.ToString(), name, ref vec, ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident, () =>
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
{
- try
- {
- ModTools.Instance.watches.AddWatch(refChain);
- }
- catch (Exception ex)
- {
- Log.Error("Exception in ModTools:OnSceneTreeReflectUnityEngineQuaternion - " + ex.Message);
- }
- });
+ return;
+ }
+
+ vec = GUIControls.CustomValueField(
+ refChain.UniqueId,
+ name,
+ GUIControls.PresentQuaternion,
+ vec,
+ MainWindow.Instance.Config.TreeIdentSpacing * refChain.Ident);
}
- private static void OnSceneTreeReflectUnityEngineVector3(ReferenceChain refChain, T obj, string name, ref UnityEngine.Vector3 vec)
+ private static void OnSceneTreeReflectUnityEngineVector3(ReferenceChain refChain, string name, ref Vector3 vec)
{
- if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain)) return;
-
- GUIControls.Vector3Field(refChain.ToString(), name, ref vec, ModTools.Instance.config.sceneExplorerTreeIdentSpacing * refChain.Ident, () =>
+ if (!SceneExplorerCommon.SceneTreeCheckDepth(refChain))
{
- try
- {
- ModTools.Instance.watches.AddWatch(refChain);
- }
- catch (Exception ex)
- {
- Log.Error("Exception in ModTools:OnSceneTreeReflectUnityEngineVector3 - " + ex.Message);
- }
- });
+ return;
+ }
+
+ vec = GUIControls.CustomValueField(
+ refChain.UniqueId,
+ name,
+ GUIControls.PresentVector3,
+ vec,
+ MainWindow.Instance.Config.TreeIdentSpacing * refChain.Ident);
}
}
}
\ No newline at end of file
diff --git a/Debugger/Utils/GameObjectUtil.cs b/Debugger/Explorer/GameObjectUtil.cs
similarity index 56%
rename from Debugger/Utils/GameObjectUtil.cs
rename to Debugger/Explorer/GameObjectUtil.cs
index 99d8d20..fe63bfb 100644
--- a/Debugger/Utils/GameObjectUtil.cs
+++ b/Debugger/Explorer/GameObjectUtil.cs
@@ -1,17 +1,15 @@
using System.Collections.Generic;
using UnityEngine;
-namespace ModTools
+namespace ModTools.Explorer
{
- public static class GameObjectUtil
+ internal static class GameObjectUtil
{
-
public static Dictionary FindSceneRoots()
{
- Dictionary roots = new Dictionary();
+ var roots = new Dictionary();
- GameObject[] objects = GameObject.FindObjectsOfType();
- foreach (var obj in objects)
+ foreach (var obj in Object.FindObjectsOfType())
{
if (!roots.ContainsKey(obj.transform.root.gameObject))
{
@@ -30,6 +28,7 @@ public static List> FindComponentsOfType(str
{
FindComponentsOfType(typeName, root, list);
}
+
return list;
}
@@ -41,34 +40,16 @@ public static void FindComponentsOfType(string typeName, GameObject gameObject,
list.Add(new KeyValuePair(gameObject, component));
}
- for (int i = 0; i < gameObject.transform.childCount; i++)
+ for (var i = 0; i < gameObject.transform.childCount; i++)
{
FindComponentsOfType(typeName, gameObject.transform.GetChild(i).gameObject, list);
}
}
- public static string WhereIs(GameObject gameObject, bool logToConsole = true)
- {
- string outResult = gameObject.name;
- WhereIsInternal(gameObject, ref outResult);
-
- if (logToConsole)
- {
- Debug.LogWarning(outResult);
- }
-
- return outResult;
- }
-
- private static void WhereIsInternal(GameObject gameObject, ref string outResult)
+ public static bool ComponentIsEnabled(Component component)
{
- outResult = gameObject.name + "." + outResult;
- if (gameObject.transform.parent != null)
- {
- WhereIsInternal(gameObject.transform.parent.gameObject, ref outResult);
- }
+ var prop = component.GetType().GetProperty("enabled");
+ return prop == null || (bool)prop.GetValue(component, null);
}
-
}
-
-}
+}
\ No newline at end of file
diff --git a/Debugger/Explorer/MeshViewer.cs b/Debugger/Explorer/MeshViewer.cs
new file mode 100644
index 0000000..c79eb13
--- /dev/null
+++ b/Debugger/Explorer/MeshViewer.cs
@@ -0,0 +1,265 @@
+using System;
+using ColossalFramework.UI;
+using ModTools.UI;
+using ModTools.Utils;
+using UnityEngine;
+
+namespace ModTools.Explorer
+{
+ internal sealed class MeshViewer : GUIWindow, IGameObject
+ {
+ private readonly RenderTexture targetRT;
+ private readonly Camera meshViewerCamera;
+ private readonly Light light;
+
+ private Mesh previewMesh;
+ private Material previewMaterial;
+ private string assetName;
+
+ private float distance = 4f;
+ private Vector2 previewDir = new Vector2(120f, -20f);
+ private Bounds bounds;
+ private Vector2 lastLeftMousePos = Vector2.zero;
+ private Vector2 lastRightMousePos = Vector2.zero;
+
+ private Material material;
+
+ private bool useOriginalShader;
+
+ public MeshViewer()
+ : base("Mesh Viewer", new Rect(512, 128, 512, 512), Skin)
+ {
+ try
+ {
+ light = GameObject.Find("Directional Light").GetComponent();
+ }
+ catch (Exception)
+ {
+ light = null;
+ }
+
+ meshViewerCamera = gameObject.AddComponent();
+ meshViewerCamera.transform.position = new Vector3(-10000.0f, -10000.0f, -10000.0f);
+ meshViewerCamera.fieldOfView = 30f;
+ meshViewerCamera.backgroundColor = Color.grey;
+ meshViewerCamera.nearClipPlane = 1.0f;
+ meshViewerCamera.farClipPlane = 1000.0f;
+ meshViewerCamera.enabled = false;
+ meshViewerCamera.allowHDR = true;
+
+ targetRT = new RenderTexture(512, 512, 24, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
+ meshViewerCamera.targetTexture = targetRT;
+ }
+
+ public static MeshViewer CreateMeshViewer(string assetName, Mesh mesh, Material material, bool calculateBounds = true)
+ {
+ var go = new GameObject("MeshViewer");
+ go.transform.parent = MainWindow.Instance.transform;
+ var meshViewer = go.AddComponent();
+ meshViewer.assetName = assetName;
+ meshViewer.previewMesh = mesh;
+ meshViewer.material = material;
+
+ meshViewer.previewMaterial = new Material(Shader.Find("Diffuse"));
+ if (material != null)
+ {
+ meshViewer.previewMaterial.mainTexture = material.mainTexture;
+ }
+
+ meshViewer.Setup(calculateBounds);
+ meshViewer.Visible = true;
+ meshViewer.SetCitizenInfoObjects(false);
+ return meshViewer;
+ }
+
+ public void Update()
+ {
+ if (previewMesh == null)
+ {
+ return;
+ }
+
+ var intensity = 0.0f;
+ var color = Color.black;
+ var enabled = false;
+
+ if (light != null)
+ {
+ intensity = light.intensity;
+ color = light.color;
+ enabled = light.enabled;
+ light.intensity = 2.0f;
+ light.color = Color.white;
+ light.enabled = true;
+ }
+
+ var magnitude = bounds.extents.magnitude;
+ var num1 = magnitude + 16f;
+ var num2 = magnitude * distance;
+ meshViewerCamera.transform.position = -Vector3.forward * num2;
+ meshViewerCamera.transform.rotation = Quaternion.identity;
+ meshViewerCamera.nearClipPlane = Mathf.Max(num2 - num1 * 1.5f, 0.01f);
+ meshViewerCamera.farClipPlane = num2 + num1 * 1.5f;
+ var q = Quaternion.Euler(previewDir.y, 0.0f, 0.0f)
+ * Quaternion.Euler(0.0f, previewDir.x, 0.0f);
+ var trs = Matrix4x4.TRS(q * -bounds.center, q, Vector3.one);
+
+ var material1 = (useOriginalShader && material != null) ? material : previewMaterial;
+ Graphics.DrawMesh(previewMesh, trs, material1, 0, meshViewerCamera, 0, null, false, false);
+ meshViewerCamera.RenderWithShader(material1.shader, string.Empty);
+
+ if (light != null)
+ {
+ light.intensity = intensity;
+ light.color = color;
+ light.enabled = enabled;
+ }
+ }
+
+ public void Setup(bool calculateBounds)
+ {
+ if (previewMesh == null)
+ {
+ return;
+ }
+
+ if (calculateBounds && previewMesh.isReadable)
+ {
+ bounds = new Bounds(Vector3.zero, Vector3.zero);
+ foreach (var vertex in previewMesh.vertices)
+ {
+ bounds.Encapsulate(vertex);
+ }
+ }
+ else
+ {
+ bounds = new Bounds(new Vector3(0, 0, 0), new Vector3(3, 3, 3));
+ }
+
+ distance = 4f;
+ }
+
+ public void SetCitizenInfoObjects(bool enabled)
+ {
+ foreach (var i in Resources.FindObjectsOfTypeAll())
+ {
+ i.gameObject.SetActive(enabled);
+ }
+ }
+
+ protected override void OnWindowClosed()
+ {
+ SetCitizenInfoObjects(true);
+ Destroy(this);
+ }
+
+ protected override void OnWindowDestroyed()
+ {
+ Destroy(meshViewerCamera);
+ Destroy(targetRT);
+ }
+
+ protected override void DrawWindow()
+ {
+ if (previewMesh == null)
+ {
+ Title = "Mesh Viewer";
+ GUILayout.Label("Use the Scene Explorer to select a Mesh for preview");
+ return;
+ }
+
+ Title = $"Previewing \"{assetName ?? previewMesh.name}\"";
+
+ GUILayout.BeginHorizontal();
+
+ if (material != null)
+ {
+ useOriginalShader = GUILayout.Toggle(useOriginalShader, "Original Shader");
+ if (previewMesh.isReadable)
+ {
+ if (GUILayout.Button("Dump mesh+textures", GUILayout.Width(160)))
+ {
+ DumpUtil.DumpMeshAndTextures(assetName ?? previewMesh.name, previewMesh, material);
+ }
+ }
+ else if (GUILayout.Button("Dump textures", GUILayout.Width(160)))
+ {
+ DumpUtil.DumpTextures(assetName, material);
+ }
+ }
+ else
+ {
+ useOriginalShader = false;
+ if (previewMesh.isReadable && GUILayout.Button("Dump mesh", GUILayout.Width(160)))
+ {
+ DumpUtil.DumpMeshAndTextures($"{previewMesh.name}", previewMesh);
+ }
+ }
+
+ if (previewMesh.isReadable)
+ {
+ GUILayout.Label($"Triangles: {previewMesh.triangles.Length / 3}");
+ }
+ else
+ {
+ var oldColor = GUI.color;
+ GUI.color = Color.yellow;
+ GUILayout.Label("Mesh isn't readable!");
+ GUI.color = oldColor;
+ }
+
+ if (material?.mainTexture != null)
+ {
+ GUILayout.Label($"Texture size: {material.mainTexture.width}x{material.mainTexture.height}");
+ }
+
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+
+ if (Event.current.type == EventType.MouseDown)
+ {
+ if (Event.current.button == 0 || Event.current.button == 2)
+ {
+ lastLeftMousePos = Event.current.mousePosition;
+ }
+ else
+ {
+ lastRightMousePos = Event.current.mousePosition;
+ }
+ }
+ else if (Event.current.type == EventType.MouseDrag)
+ {
+ var pos = Event.current.mousePosition;
+ if (Event.current.button == 0 || Event.current.button == 2)
+ {
+ if (lastLeftMousePos != Vector2.zero)
+ {
+ var moveDelta = (pos - lastLeftMousePos) * 2.0f;
+ previewDir -= moveDelta / Mathf.Min(targetRT.width, targetRT.height)
+ * UIView.GetAView().ratio * 140f;
+ previewDir.y = Mathf.Clamp(previewDir.y, -90f, 90f);
+ }
+
+ lastLeftMousePos = pos;
+ }
+ else
+ {
+ if (lastRightMousePos != Vector2.zero)
+ {
+ var moveDelta1 = pos - lastRightMousePos;
+ distance += (float)(moveDelta1.y / (double)targetRT.height
+ * UIView.GetAView().ratio * 40.0);
+ const float num1 = 6f;
+ var magnitude = bounds.extents.magnitude;
+ var num2 = magnitude + 16f;
+ distance = Mathf.Min(distance, 4f, num1 * (num2 / magnitude));
+ }
+
+ lastRightMousePos = pos;
+ }
+ }
+
+ GUI.DrawTexture(new Rect(0.0f, 64.0f, WindowRect.width, WindowRect.height - 64.0f), targetRT, ScaleMode.StretchToFill, false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/Explorer/Plopper.cs b/Debugger/Explorer/Plopper.cs
new file mode 100644
index 0000000..f39efa0
--- /dev/null
+++ b/Debugger/Explorer/Plopper.cs
@@ -0,0 +1,125 @@
+using System.Reflection;
+using ColossalFramework;
+using UnityEngine;
+
+namespace ModTools.Explorer
+{
+ internal static class Plopper
+ {
+ private static PrefabInfo ploppedPrefab;
+
+ public static void Reset() => ploppedPrefab = null;
+
+ public static void Update()
+ {
+ if (ploppedPrefab == null)
+ {
+ return;
+ }
+
+ var toolManager = Singleton.instance;
+ if (toolManager?.m_properties == null)
+ {
+ return;
+ }
+
+ if (Input.GetKeyDown(KeyCode.Escape))
+ {
+ Singleton.instance.m_properties.CurrentTool = ToolsModifierControl.GetTool();
+ ploppedPrefab = null;
+ return;
+ }
+
+ var currentTool = toolManager.m_properties.CurrentTool;
+ if (currentTool == null)
+ {
+ return;
+ }
+
+ if (currentTool is BuildingTool || currentTool is NetTool || currentTool is TreeTool
+ || currentTool is PropTool)
+ {
+ var prefabField = currentTool.GetType()
+ .GetField("m_prefab", BindingFlags.Instance | BindingFlags.Public);
+ if (prefabField != null)
+ {
+ var prefab = prefabField.GetValue(currentTool);
+ if ((PrefabInfo)prefab != ploppedPrefab)
+ {
+ ploppedPrefab = null;
+ }
+ }
+ else
+ {
+ ploppedPrefab = null;
+ }
+ }
+ else
+ {
+ ploppedPrefab = null;
+ }
+ }
+
+ public static void StartPlopping(PrefabInfo prefabInfo)
+ {
+ var currentTool = Singleton.instance.m_properties.CurrentTool;
+ if (currentTool == null)
+ {
+ return;
+ }
+
+ var defaultToolActive = currentTool is DefaultTool;
+
+ switch (prefabInfo)
+ {
+ case BuildingInfo buildingInfo when defaultToolActive || currentTool is BuildingTool:
+ var buildingTool = ToolsModifierControl.GetTool();
+ if (buildingTool != null)
+ {
+ buildingTool.m_prefab = buildingInfo;
+ buildingTool.m_relocate = 0;
+
+ SetCurrentTool(buildingTool);
+ }
+
+ break;
+
+ case NetInfo netInfo when defaultToolActive || currentTool is NetTool:
+ var netTool = ToolsModifierControl.GetTool();
+ if (netTool != null)
+ {
+ netTool.m_prefab = netInfo;
+ SetCurrentTool(netTool);
+ }
+
+ break;
+
+ case PropInfo propInfo when defaultToolActive || currentTool is PropTool:
+ var propTool = ToolsModifierControl.GetTool();
+ if (propTool != null)
+ {
+ propTool.m_prefab = propInfo;
+ SetCurrentTool(propTool);
+ }
+
+ break;
+
+ case TreeInfo treeInfo when defaultToolActive || currentTool is TreeTool:
+ var treeTool = ToolsModifierControl.GetTool();
+ if (treeTool != null)
+ {
+ ploppedPrefab = treeInfo;
+ SetCurrentTool(treeTool);
+ }
+
+ break;
+ }
+
+ void SetCurrentTool(ToolBase tool)
+ {
+ Singleton.instance.m_properties.CurrentTool = tool;
+ ploppedPrefab = prefabInfo;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/Explorer/ReferenceChain.cs b/Debugger/Explorer/ReferenceChain.cs
new file mode 100644
index 0000000..1e15a8b
--- /dev/null
+++ b/Debugger/Explorer/ReferenceChain.cs
@@ -0,0 +1,307 @@
+using System;
+using System.Collections;
+using System.Reflection;
+using System.Text;
+using UnityEngine;
+
+namespace ModTools.Explorer
+{
+ internal sealed class ReferenceChain : ICloneable
+ {
+ private readonly object[] chainObjects = new object[MainWindow.Instance.Config.MaxHierarchyDepth];
+ private readonly ReferenceType[] chainTypes = new ReferenceType[MainWindow.Instance.Config.MaxHierarchyDepth];
+ private string uniqueId;
+
+ public enum ReferenceType
+ {
+ None,
+ GameObject,
+ Component,
+ Field,
+ Property,
+ Method,
+ EnumerableItem,
+ SpecialNamedProperty,
+ }
+
+ public int IdentOffset { get; set; }
+
+ public int Ident => Length + IdentOffset - 1;
+
+ public int Length { get; private set; }
+
+ public object LastItem => chainObjects[Length - 1];
+
+ public string LastItemName => ItemToString(Length - 1);
+
+ public ReferenceType FirstItemType => chainTypes[0];
+
+ public string UniqueId
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(uniqueId))
+ {
+ return uniqueId;
+ }
+
+ var stringBuilder = new StringBuilder(Length * 32);
+ for (var i = 0; i < Length; ++i)
+ {
+ stringBuilder
+ .Append(chainTypes[i]).Append(':')
+ .Append(ItemToString(i)).Append('.');
+ }
+
+ uniqueId = stringBuilder.ToString();
+ return uniqueId;
+ }
+ }
+
+ object ICloneable.Clone() => Clone();
+
+ public ReferenceChain Clone()
+ {
+ var clone = new ReferenceChain { Length = Length };
+ for (var i = 0; i < Length; i++)
+ {
+ clone.chainObjects[i] = chainObjects[i];
+ clone.chainTypes[i] = chainTypes[i];
+ }
+
+ clone.IdentOffset = IdentOffset;
+
+ return clone;
+ }
+
+ public bool CheckDepth() => Length >= MainWindow.Instance.Config.MaxHierarchyDepth;
+
+ public ReferenceChain Add(GameObject go)
+ {
+ var copy = Clone();
+ copy.chainObjects[Length] = go;
+ copy.chainTypes[Length] = ReferenceType.GameObject;
+ copy.Length++;
+ return copy;
+ }
+
+ public ReferenceChain Add(Component component)
+ {
+ var copy = Clone();
+ copy.chainObjects[Length] = component;
+ copy.chainTypes[Length] = ReferenceType.Component;
+ copy.Length++;
+ return copy;
+ }
+
+ public ReferenceChain Add(FieldInfo fieldInfo)
+ {
+ var copy = Clone();
+ copy.chainObjects[Length] = fieldInfo;
+ copy.chainTypes[Length] = ReferenceType.Field;
+ copy.Length++;
+ return copy;
+ }
+
+ public ReferenceChain Add(PropertyInfo propertyInfo)
+ {
+ var copy = Clone();
+ copy.chainObjects[Length] = propertyInfo;
+ copy.chainTypes[Length] = ReferenceType.Property;
+ copy.Length++;
+ return copy;
+ }
+
+ public ReferenceChain Add(MethodInfo methodInfo)
+ {
+ var copy = Clone();
+ copy.chainObjects[Length] = methodInfo;
+ copy.chainTypes[Length] = ReferenceType.Method;
+ copy.Length++;
+ return copy;
+ }
+
+ public ReferenceChain Add(int index)
+ {
+ var copy = Clone();
+ copy.chainObjects[Length] = index;
+ copy.chainTypes[Length] = ReferenceType.EnumerableItem;
+ copy.Length++;
+ return copy;
+ }
+
+ public ReferenceChain Add(string namedProperty)
+ {
+ var copy = Clone();
+ copy.chainObjects[Length] = namedProperty;
+ copy.chainTypes[Length] = ReferenceType.SpecialNamedProperty;
+ copy.Length++;
+ return copy;
+ }
+
+ public ReferenceChain Trim(int num)
+ {
+ var copy = Clone();
+ copy.Length = Mathf.Min(num, Length);
+ return copy;
+ }
+
+ public object GetChainItem(int index) => index >= 0 && index < Length ? chainObjects[index] : null;
+
+ public ReferenceType GetChainItemType(int index)
+ => index >= 0 && index < Length ? chainTypes[index] : ReferenceType.None;
+
+ public override string ToString()
+ {
+ switch (Length)
+ {
+ case 0:
+ return string.Empty;
+
+ case 1:
+ return ItemToString(0);
+ }
+
+ var result = new StringBuilder();
+ result.Append(ItemToString(0));
+
+ for (var i = 1; i < Length; i++)
+ {
+ if (chainTypes[i] != ReferenceType.EnumerableItem)
+ {
+ result.Append(".");
+ }
+
+ result.Append(ItemToString(i));
+ }
+
+ return result.ToString();
+ }
+
+ public ReferenceChain GetReversedCopy()
+ {
+ var copy = new ReferenceChain
+ {
+ Length = Length,
+ IdentOffset = IdentOffset,
+ };
+
+ for (var i = 0; i < Length; i++)
+ {
+ copy.chainObjects[Length - i - 1] = chainObjects[i];
+ copy.chainTypes[Length - i - 1] = chainTypes[i];
+ }
+
+ return copy;
+ }
+
+ public object Evaluate()
+ {
+ object current = null;
+ for (var i = 0; i < Length; i++)
+ {
+ switch (chainTypes[i])
+ {
+ case ReferenceType.GameObject:
+ case ReferenceType.Component:
+ current = chainObjects[i];
+ break;
+
+ case ReferenceType.Field:
+ current = ((FieldInfo)chainObjects[i]).GetValue(current);
+ break;
+
+ case ReferenceType.Property:
+ current = ((PropertyInfo)chainObjects[i]).GetValue(current, null);
+ break;
+
+ case ReferenceType.Method:
+ break;
+
+ case ReferenceType.EnumerableItem:
+ var collection = current as IEnumerable;
+ var itemCount = 0;
+ foreach (var item in collection)
+ {
+ if (itemCount == (int)chainObjects[i])
+ {
+ current = item;
+ break;
+ }
+
+ itemCount++;
+ }
+
+ break;
+
+ case ReferenceType.SpecialNamedProperty:
+ break;
+ }
+ }
+
+ return current;
+ }
+
+ public bool IsSameChain(ReferenceChain other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
+
+ if (Length != other.Length)
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, other))
+ {
+ return true;
+ }
+
+ for (var i = 0; i < Length; ++i)
+ {
+ if (chainTypes[i] != other.chainTypes[i])
+ {
+ return false;
+ }
+
+ if (chainObjects[i] != other.chainObjects[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private string ItemToString(int i)
+ {
+ switch (chainTypes[i])
+ {
+ case ReferenceType.GameObject:
+ return ((GameObject)chainObjects[i]).name;
+
+ case ReferenceType.Component:
+ return ((Component)chainObjects[i]).name;
+
+ case ReferenceType.Field:
+ return ((FieldInfo)chainObjects[i]).Name;
+
+ case ReferenceType.Property:
+ return ((PropertyInfo)chainObjects[i]).Name;
+
+ case ReferenceType.Method:
+ return ((MethodInfo)chainObjects[i]).Name;
+
+ case ReferenceType.EnumerableItem:
+ return "[" + chainObjects[i] + "]";
+
+ case ReferenceType.SpecialNamedProperty:
+ return (string)chainObjects[i];
+ }
+
+ return string.Empty;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/Explorer/SceneExplorer.cs b/Debugger/Explorer/SceneExplorer.cs
new file mode 100644
index 0000000..7715eae
--- /dev/null
+++ b/Debugger/Explorer/SceneExplorer.cs
@@ -0,0 +1,488 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using ModTools.UI;
+using ModTools.Utils;
+using UnityEngine;
+
+namespace ModTools.Explorer
+{
+ internal sealed class SceneExplorer : GUIWindow, IGameObject, IAwakingObject
+ {
+ private const float WindowTopMargin = 16.0f;
+ private const float WindowBottomMargin = 8.0f;
+
+ private const float HeaderHeightCompact = 1.65f;
+ private const float HeaderHeightExpanded = 13.75f;
+
+ private const float SceneTreeWidth = 320.0f;
+
+ private const string ExpandDownButtonText = " ▼ ▼ ▼ ";
+ private const string CollapseUpButtonText = " ▲ ▲ ▲ ";
+ private const string ExpandRightButtonText = " ▶▶▶ ";
+ private const string CollapseLeftButtonText = " ◀◀◀ ";
+
+ private readonly GUIArea headerArea;
+ private readonly GUIArea sceneTreeArea;
+ private readonly GUIArea componentArea;
+
+ private Dictionary sceneRoots = new Dictionary();
+
+ private string findGameObjectFilter = string.Empty;
+ private string findObjectTypeFilter = string.Empty;
+ private string searchDisplayString = string.Empty;
+
+ private Vector2 sceneTreeScrollPosition = Vector2.zero;
+ private Vector2 componentScrollPosition = Vector2.zero;
+ private SceneExplorerState state;
+
+ private bool headerExpanded;
+ private bool treeExpanded = true;
+
+ public SceneExplorer()
+ : base("Scene Explorer", new Rect(128, 440, 800, 500), Skin)
+ {
+ headerArea = new GUIArea(this)
+ .ChangeSizeRelative(height: 0)
+ .OffsetBy(vertical: WindowTopMargin);
+
+ sceneTreeArea = new GUIArea(this)
+ .ChangeSizeRelative(width: 0)
+ .ChangeSizeBy(width: SceneTreeWidth);
+
+ componentArea = new GUIArea(this)
+ .OffsetBy(horizontal: SceneTreeWidth)
+ .ChangeSizeBy(width: -SceneTreeWidth);
+
+ state = new SceneExplorerState();
+
+ RecalculateAreas();
+ }
+
+ public void Awake() => Plopper.Reset();
+
+ public void Update() => Plopper.Update();
+
+ public void RecalculateAreas()
+ {
+ if (!treeExpanded || WindowRect.width < Screen.width / 4)
+ {
+ sceneTreeArea.ChangeSizeBy(width: 0);
+
+ componentArea
+ .OffsetBy(horizontal: 0)
+ .ChangeSizeBy(width: 0f);
+ }
+ else
+ {
+ sceneTreeArea.ChangeSizeBy(width: SceneTreeWidth);
+
+ componentArea
+ .OffsetBy(horizontal: SceneTreeWidth)
+ .ChangeSizeBy(width: -SceneTreeWidth);
+ }
+
+ var headerHeight = headerExpanded ? HeaderHeightExpanded : HeaderHeightCompact;
+ headerHeight *= MainWindow.Instance.Config.FontSize;
+ headerHeight += 32.0f;
+
+ var verticalOffset = headerHeight - WindowTopMargin;
+ var verticalSizeOffset = -(verticalOffset + WindowBottomMargin);
+
+ headerArea.ChangeSizeBy(height: verticalOffset);
+
+ sceneTreeArea
+ .OffsetBy(vertical: verticalOffset)
+ .ChangeSizeBy(height: verticalSizeOffset);
+
+ componentArea
+ .OffsetBy(vertical: verticalOffset)
+ .ChangeSizeBy(height: verticalSizeOffset);
+ }
+
+ public void Refresh()
+ {
+ sceneRoots = GameObjectUtil.FindSceneRoots();
+ TypeUtil.ClearTypeCache();
+ }
+
+ public void Show(ReferenceChain refChain)
+ {
+ if (refChain == null)
+ {
+ Logger.Error("SceneExplorer: ExpandFromRefChain(): Null refChain");
+ return;
+ }
+
+ if (refChain.Length == 0)
+ {
+ Logger.Error("SceneExplorer: ExpandFromRefChain(): Invalid refChain, expected Length >= 0");
+ return;
+ }
+
+ if (refChain.FirstItemType != ReferenceChain.ReferenceType.GameObject)
+ {
+ Logger.Error($"SceneExplorer: ExpandFromRefChain(): invalid chain type for element [0] - expected {ReferenceChain.ReferenceType.GameObject}, got {refChain.FirstItemType}");
+ return;
+ }
+
+ sceneRoots.Clear();
+ ClearExpanded();
+ searchDisplayString = $"Showing results for \"{refChain}\"";
+
+ var rootGameObject = (GameObject)refChain.GetChainItem(0);
+ sceneRoots.Add(rootGameObject, true);
+
+ var expandedRefChain = new ReferenceChain().Add(rootGameObject);
+ state.ExpandedGameObjects.Add(expandedRefChain.UniqueId);
+
+ for (var i = 1; i < refChain.Length; i++)
+ {
+ switch (refChain.GetChainItemType(i))
+ {
+ case ReferenceChain.ReferenceType.GameObject:
+ var go = (GameObject)refChain.GetChainItem(i);
+ expandedRefChain = expandedRefChain.Add(go);
+ state.ExpandedGameObjects.Add(expandedRefChain.UniqueId);
+ break;
+
+ case ReferenceChain.ReferenceType.Component:
+ var component = (Component)refChain.GetChainItem(i);
+ expandedRefChain = expandedRefChain.Add(component);
+ state.ExpandedComponents.Add(expandedRefChain.UniqueId);
+ break;
+
+ case ReferenceChain.ReferenceType.Field:
+ var field = (FieldInfo)refChain.GetChainItem(i);
+ expandedRefChain = expandedRefChain.Add(field);
+ state.ExpandedObjects.Add(expandedRefChain.UniqueId);
+ break;
+
+ case ReferenceChain.ReferenceType.Property:
+ var property = (PropertyInfo)refChain.GetChainItem(i);
+ expandedRefChain = expandedRefChain.Add(property);
+ state.ExpandedObjects.Add(expandedRefChain.UniqueId);
+ break;
+
+ case ReferenceChain.ReferenceType.EnumerableItem:
+ var index = (int)refChain.GetChainItem(i);
+ state.SelectedArrayStartIndices[expandedRefChain.UniqueId] = index;
+ state.SelectedArrayEndIndices[expandedRefChain.UniqueId] = index;
+ expandedRefChain = expandedRefChain.Add(index);
+ state.ExpandedObjects.Add(expandedRefChain.UniqueId);
+ break;
+ }
+ }
+
+ state.CurrentRefChain = refChain.Clone();
+ state.CurrentRefChain.IdentOffset = -state.CurrentRefChain.Length;
+ Visible = true;
+ }
+
+ public void DrawHeader()
+ {
+ headerArea.Begin();
+
+ if (headerExpanded)
+ {
+ DrawExpandedHeader();
+ }
+
+ GUILayout.BeginHorizontal();
+
+ if (GUILayout.Button(treeExpanded ? CollapseLeftButtonText : ExpandRightButtonText, GUILayout.ExpandWidth(false)))
+ {
+ treeExpanded = !treeExpanded;
+ RecalculateAreas();
+ }
+
+ GUILayout.Space(MainWindow.Instance.Config.TreeIdentSpacing);
+
+ if (GUILayout.Button(headerExpanded ? CollapseUpButtonText : ExpandDownButtonText))
+ {
+ headerExpanded = !headerExpanded;
+ RecalculateAreas();
+ }
+
+ GUILayout.EndHorizontal();
+
+ headerArea.End();
+ }
+
+ public void DrawExpandedHeader()
+ {
+ GUILayout.BeginHorizontal();
+
+ GUI.contentColor = Color.green;
+ GUILayout.Label("Show:", GUILayout.ExpandWidth(false));
+
+ var configChanged = false;
+
+ var showFields = GUILayout.Toggle(MainWindow.Instance.Config.ShowFields, " Fields");
+ if (MainWindow.Instance.Config.ShowFields != showFields)
+ {
+ MainWindow.Instance.Config.ShowFields = showFields;
+ configChanged = true;
+ }
+
+ GUILayout.Space(MainWindow.Instance.Config.TreeIdentSpacing);
+ var showConsts = GUILayout.Toggle(MainWindow.Instance.Config.ShowConsts, " Constants");
+ if (MainWindow.Instance.Config.ShowConsts != showConsts)
+ {
+ MainWindow.Instance.Config.ShowConsts = showConsts;
+ configChanged = true;
+ }
+
+ GUILayout.Space(MainWindow.Instance.Config.TreeIdentSpacing);
+ var showProperties = GUILayout.Toggle(MainWindow.Instance.Config.ShowProperties, " Properties");
+ if (MainWindow.Instance.Config.ShowProperties != showProperties)
+ {
+ MainWindow.Instance.Config.ShowProperties = showProperties;
+ configChanged = true;
+ }
+
+ GUILayout.Space(MainWindow.Instance.Config.TreeIdentSpacing);
+ var showMethods = GUILayout.Toggle(MainWindow.Instance.Config.ShowMethods, " Methods");
+ if (MainWindow.Instance.Config.ShowMethods != showMethods)
+ {
+ MainWindow.Instance.Config.ShowMethods = showMethods;
+ configChanged = true;
+ }
+
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+
+ var showModifiers = GUILayout.Toggle(MainWindow.Instance.Config.ShowModifiers, " Show field / property modifiers");
+ if (showModifiers != MainWindow.Instance.Config.ShowModifiers)
+ {
+ MainWindow.Instance.Config.ShowModifiers = showModifiers;
+ configChanged = true;
+ }
+
+ var showInheritedMembers = GUILayout.Toggle(MainWindow.Instance.Config.ShowInheritedMembers, " Show inherited members");
+ if (showInheritedMembers != MainWindow.Instance.Config.ShowInheritedMembers)
+ {
+ MainWindow.Instance.Config.ShowInheritedMembers = showInheritedMembers;
+ configChanged = true;
+ TypeUtil.ClearTypeCache();
+ }
+
+ var evaluatePropertiesAutomatically = GUILayout.Toggle(MainWindow.Instance.Config.EvaluateProperties, " Evaluate properties automatically");
+ if (evaluatePropertiesAutomatically != MainWindow.Instance.Config.EvaluateProperties)
+ {
+ MainWindow.Instance.Config.EvaluateProperties = evaluatePropertiesAutomatically;
+ configChanged = true;
+ }
+
+ var sortAlphabetically = GUILayout.Toggle(MainWindow.Instance.Config.SortItemsAlphabetically, " Sort alphabetically");
+ if (sortAlphabetically != MainWindow.Instance.Config.SortItemsAlphabetically)
+ {
+ MainWindow.Instance.Config.SortItemsAlphabetically = sortAlphabetically;
+ configChanged = true;
+ }
+
+ if (configChanged)
+ {
+ MainWindow.Instance.SaveConfig();
+ }
+
+ GUI.contentColor = Color.white;
+ DrawFindGameObjectPanel();
+ }
+
+ public void DrawSceneTree()
+ {
+ var gameObjects = sceneRoots.Keys.ToArray();
+
+ if (MainWindow.Instance.Config.SortItemsAlphabetically)
+ {
+ Array.Sort(gameObjects, (x, y) => string.CompareOrdinal(x?.name, y?.name));
+ }
+
+ if (!sceneTreeArea.Begin())
+ {
+ return;
+ }
+
+ GUILayout.BeginHorizontal();
+
+ if (GUILayout.Button("Refresh", GUILayout.ExpandWidth(false)))
+ {
+ Refresh();
+ }
+
+ if (GUILayout.Button("Fold all / Clear", GUILayout.ExpandWidth(false)))
+ {
+ ClearExpanded();
+ Refresh();
+ }
+
+ GUILayout.EndHorizontal();
+
+ if (!string.IsNullOrEmpty(searchDisplayString))
+ {
+ GUI.contentColor = Color.green;
+ GUILayout.Label(searchDisplayString);
+ GUI.contentColor = Color.white;
+ }
+
+ sceneTreeScrollPosition = GUILayout.BeginScrollView(sceneTreeScrollPosition);
+
+ foreach (var obj in gameObjects)
+ {
+ GUIRecursiveTree.OnSceneTreeRecursive(gameObject, state, new ReferenceChain().Add(obj), obj);
+ }
+
+ GUILayout.EndScrollView();
+
+ sceneTreeArea.End();
+ }
+
+ public void DrawComponent()
+ {
+ componentArea.Begin();
+
+ componentScrollPosition = GUILayout.BeginScrollView(componentScrollPosition);
+
+ if (state.CurrentRefChain != null)
+ {
+ try
+ {
+ GUIReflect.OnSceneTreeReflect(state, state.CurrentRefChain, state.CurrentRefChain.Evaluate());
+ }
+ catch (Exception e)
+ {
+ Debug.LogException(e);
+ state.CurrentRefChain = null;
+ throw;
+ }
+ }
+
+ GUILayout.EndScrollView();
+
+ componentArea.End();
+ }
+
+ protected override void HandleException(Exception ex)
+ {
+ Debug.LogException(ex);
+ state = new SceneExplorerState();
+ sceneRoots = GameObjectUtil.FindSceneRoots();
+ TypeUtil.ClearTypeCache();
+ }
+
+ protected override void DrawWindow()
+ {
+ RecalculateAreas();
+
+ var enterPressed = Event.current.type == EventType.KeyDown && (Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter);
+
+ if (enterPressed)
+ {
+ GUI.FocusControl(null);
+ }
+
+ state.PreventCircularReferences.Clear();
+
+ DrawHeader();
+ DrawSceneTree();
+ DrawComponent();
+ }
+
+ private void ClearExpanded()
+ {
+ state.ExpandedGameObjects.Clear();
+ state.ExpandedComponents.Clear();
+ state.ExpandedObjects.Clear();
+ state.EvaluatedProperties.Clear();
+ state.SelectedArrayStartIndices.Clear();
+ state.SelectedArrayEndIndices.Clear();
+ searchDisplayString = string.Empty;
+ sceneTreeScrollPosition = Vector2.zero;
+ state.CurrentRefChain = null;
+ TypeUtil.ClearTypeCache();
+ }
+
+ private void DrawFindGameObjectPanel()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Label("GameObject.Find");
+ findGameObjectFilter = GUILayout.TextField(findGameObjectFilter, GUILayout.Width(256));
+
+ if (findGameObjectFilter.Trim().Length == 0)
+ {
+ GUI.enabled = false;
+ }
+
+ if (GUILayout.Button("Find"))
+ {
+ ClearExpanded();
+ var go = GameObject.Find(findGameObjectFilter.Trim());
+ if (go != null)
+ {
+ sceneRoots.Clear();
+ state.ExpandedGameObjects.Add(new ReferenceChain().Add(go).UniqueId);
+ sceneRoots.Add(go, true);
+ sceneTreeScrollPosition = Vector2.zero;
+ searchDisplayString = $"Showing results for GameObject.Find(\"{findGameObjectFilter}\")";
+ }
+ }
+
+ if (GUILayout.Button("Reset"))
+ {
+ ClearExpanded();
+ sceneRoots = GameObjectUtil.FindSceneRoots();
+ sceneTreeScrollPosition = Vector2.zero;
+ searchDisplayString = string.Empty;
+ }
+
+ GUI.enabled = true;
+
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Label("GameObject.FindObjectsOfType");
+ findObjectTypeFilter = GUILayout.TextField(findObjectTypeFilter, GUILayout.Width(256));
+
+ if (findObjectTypeFilter.Trim().Length == 0)
+ {
+ GUI.enabled = false;
+ }
+
+ if (GUILayout.Button("Find"))
+ {
+ var gameObjects = GameObjectUtil.FindComponentsOfType(findObjectTypeFilter.Trim());
+
+ sceneRoots.Clear();
+ foreach (var item in gameObjects)
+ {
+ ClearExpanded();
+ state.ExpandedGameObjects.Add(new ReferenceChain().Add(item.Key).UniqueId);
+ if (gameObjects.Count == 1)
+ {
+ state.ExpandedComponents.Add(new ReferenceChain().Add(item.Key).Add(item.Value).UniqueId);
+ }
+
+ sceneRoots.Add(item.Key, true);
+ sceneTreeScrollPosition = Vector2.zero;
+ searchDisplayString = $"Showing results for GameObject.FindObjectsOfType({findObjectTypeFilter})";
+ }
+ }
+
+ if (GUILayout.Button("Reset"))
+ {
+ ClearExpanded();
+ sceneRoots = GameObjectUtil.FindSceneRoots();
+ sceneTreeScrollPosition = Vector2.zero;
+ searchDisplayString = string.Empty;
+ }
+
+ GUI.enabled = true;
+
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/Explorer/SceneExplorerCommon.cs b/Debugger/Explorer/SceneExplorerCommon.cs
new file mode 100644
index 0000000..b3575c5
--- /dev/null
+++ b/Debugger/Explorer/SceneExplorerCommon.cs
@@ -0,0 +1,46 @@
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+
+namespace ModTools.Explorer
+{
+ internal static class SceneExplorerCommon
+ {
+ private static readonly Dictionary IndentStrings = new Dictionary();
+
+ internal static void OnSceneTreeMessage(ReferenceChain refChain, string message)
+ {
+ GUILayout.BeginHorizontal();
+ InsertIndent(refChain.Ident);
+ GUILayout.Label(message);
+ GUILayout.EndHorizontal();
+ }
+
+ internal static bool SceneTreeCheckDepth(ReferenceChain refChain)
+ {
+ if (refChain.CheckDepth())
+ {
+ OnSceneTreeMessage(refChain, "Hierarchy too deep, sorry :(");
+ return false;
+ }
+
+ return true;
+ }
+
+ internal static void InsertIndent(int indent)
+ {
+ if (indent <= 0)
+ {
+ return;
+ }
+
+ if (!IndentStrings.TryGetValue(indent, out var indentString))
+ {
+ indentString = new StringBuilder().Insert(0, "· ", indent).ToString();
+ IndentStrings.Add(indent, indentString);
+ }
+
+ GUILayout.Label(indentString, GUILayout.Width(MainWindow.Instance.Config.TreeIdentSpacing * indent));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Debugger/Explorer/SceneExplorerState.cs b/Debugger/Explorer/SceneExplorerState.cs
new file mode 100644
index 0000000..da06850
--- /dev/null
+++ b/Debugger/Explorer/SceneExplorerState.cs
@@ -0,0 +1,23 @@
+using System.Collections.Generic;
+
+namespace ModTools.Explorer
+{
+ internal sealed class SceneExplorerState
+ {
+ public HashSet ExpandedGameObjects { get; } = new HashSet();
+
+ public HashSet ExpandedComponents { get; } = new HashSet();
+
+ public HashSet ExpandedObjects { get; } = new HashSet();
+
+ public HashSet EvaluatedProperties { get; } = new HashSet();
+
+ public Dictionary SelectedArrayStartIndices { get; } = new Dictionary();
+
+ public Dictionary SelectedArrayEndIndices { get; } = new Dictionary();
+
+ public HashSet