daily pastebin goal
32%
SHARE
TWEET

CmdLine.cs

mvaganov May 1st, 2017 (edited) 103 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using UnityEngine;
  2. using UnityEngine.SceneManagement;
  3. using UnityEngine.EventSystems;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using UnityEngine.UI;
  9. // make sure you have "TextMesh Pro" downloaded and imported from the Unity Asset Store. "Examples" unnecessary
  10. using TMPro;
  11.  
  12. /// <summary>A Command Line emulation for Unity3D v5.6+. Just use 'CmdLine.Log()'
  13. /// Latest version at: https://pastebin.com/nphdEi1z added colored log text when intercepting Debug</summary>
  14. /// <description>MIT License - TL;DR - This code is free, don't bother me about it!</description>
  15. /// <author email="mvaganov@hotmail.com">Michael Vaganov</author>
  16. public class CmdLine : MonoBehaviour {
  17.     #region commands
  18.     /// <summary>watching for commands *about to execute*.</summary>
  19.     public event CommandHandler onCommand;
  20.     /// <summary>known commands</summary>
  21.     private Dictionary<string, Command> commands = new Dictionary<string, Command> ();
  22.     /// <summary>queue of instructions that this command line needs to execute.</summary>
  23.     private List<string> instructionList = new List<string> ();
  24.  
  25.     /// <summary>example of how to populate the command-line with commands</summary>
  26.     public void PopulateWithBasicCommands () {
  27.         //When adding commands, you must add a call below to registerCommand() with its name, implementation method, and help text.
  28.         addCommand ("help", (args) => {
  29.             log (" - - - -\n" + CommandHelpString () + "\n - - - -");
  30.         }, "prints this help.");
  31.         addCommand ("load", (args) => {
  32.             if (args.Length > 1) {
  33.                 if(args[1] == ".") { args[1] = SceneManager.GetActiveScene ().name; }
  34.                 SceneManager.LoadScene (args[1]);
  35.             } else { log("to reload current scene, type <#"+colorSet.special+">load "+
  36.                 SceneManager.GetActiveScene ().name+"</color>"); }
  37.         }, "loads given scene. use: load <scene name>");
  38.         addCommand ("pref", (args) => {
  39.             for (int i = 1; i < args.Length; ++i) {
  40.                 string output = null;
  41.                 try{ output = "<#"+colorSet.special+">"+PlayerPrefs.GetString (args [i])+"</color>"; }
  42.                 catch(System.Exception e){ output = "<#"+colorSet.error+">"+e.ToString()+"</color>"; }
  43.                 if(output == null) { output =  "<#"+colorSet.error+">null</color>"; }
  44.                 log (args [i] + ":" + output);
  45.             }
  46.         }, "shows player prefs value. use: pref [variableName, ...]");
  47.         addCommand ("prefsave", (args) => {
  48.             if (args.Length > 1) {
  49.                 PlayerPrefs.SetString (args [1], (args.Length > 2) ? args [2] : null);
  50.                 PlayerPrefs.Save ();
  51.             } else {
  52.                 log ("missing arguments");
  53.             }
  54.         }, "save player prefs value. use: pref variableName variableValue]");
  55.         addCommand ("prefreset", (args) => {
  56.             PlayerPrefs.DeleteAll ();
  57.             PlayerPrefs.Save ();
  58.         }, "clears player prefs.");
  59.     }
  60.     /// <param name="command">name of the command to add (case insensitive)</param>
  61.     /// <param name="handler">code to execute with this command, think standard main</param>
  62.     /// <param name="help">reference information, think concise man-pages. Make help <c>"\b"</c> for hidden commands</param>
  63.     public void addCommand (string command, CommandHandler handler, string help) {
  64.         commands.Add (command.ToLower (), new Command (command, handler, help));
  65.     }
  66.     /// <param name="command">name of the command to add (case insensitive)</param>
  67.     /// <param name="handler">code to execute with this command, think standard main</param>
  68.     /// <param name="help">reference information, think concise man-pages. Make help <c>"\b"</c> for hidden commands</param>
  69.     public static void AddCommand (string command, CommandHandler handler, string help) {
  70.         Instance.addCommand (command, handler, help);
  71.     }
  72.     /// <param name="commands">dictionary of commands to begin using, replacing old commands</param>
  73.     public void SetCommands (Dictionary<string, Command> commands) { this.commands = commands; }
  74.     /// <summary>replace current commands with no commands</summary>
  75.     public void ClearCommands () { commands.Clear (); }
  76.     /// <summary>command-line handler. think "standard main" from Java or C/C++.
  77.     /// args[0] is the command, args[1] and beyond are the arguments.</summary>
  78.     public delegate void CommandHandler (string[] args);
  79.     public class Command {
  80.         public string command { get; private set; }
  81.         public CommandHandler handler { get; private set; }
  82.         public string help { get; private set; }
  83.         public Command (string command, CommandHandler handler, string helpReferenceText) {
  84.             this.command = command; this.handler = handler; this.help = helpReferenceText;
  85.         }
  86.     }
  87.     /// <returns>a list of usable commands</returns>
  88.     public string CommandHelpString () {
  89.         StringBuilder sb = new StringBuilder ();
  90.         foreach (Command cmd in commands.Values) {
  91.             if(cmd.help != "\b") // commands with a single backspace as help text are hidden commands
  92.                 sb.Append (((sb.Length > 0) ? "\n" : "") + cmd.command + ": " + cmd.help);
  93.         }
  94.         return sb.ToString ();
  95.     }
  96.     /// <summary>Enqueues a command to run, which will be run during the next Update</summary>
  97.     /// <param name="commandWithArguments">Command string, with arguments.</param>
  98.     public void EnqueueRun(string commandWithArguments) {
  99.         instructionList.Add(commandWithArguments);
  100.     }
  101.     public void Run (string commandWithArguments) {
  102.         if (waitingToReadLine != null) {
  103.             waitingToReadLine (commandWithArguments);
  104.             waitingToReadLine = null;
  105.         } else if (onInput != null) {
  106.             onInput(commandWithArguments);
  107.         } else {
  108.             if (commandWithArguments == null || commandWithArguments.Length == 0) { return; }
  109.             string s = commandWithArguments.Trim (whitespace); // cut leading & trailing whitespace
  110.             string[] args = ParseArguments (s).ToArray();
  111.             if (args.Length < 1) { return; }
  112.             if (onCommand != null) { onCommand (args); }
  113.             Run (args [0].ToLower (), args);
  114.         }
  115.     }
  116.     /// <param name="command">Command.</param>
  117.     /// <param name="args">Arguments. [0] is the name of the command, with [1] and beyond being the arguments</param>
  118.     public void Run (string command, string[] args) {
  119.         Command cmd = null;
  120.         // try to find the given command. or the default command. if we can't find either...
  121.         if (!commands.TryGetValue (command, out cmd) && !commands.TryGetValue ("", out cmd)) {
  122.             // error!
  123.             string error = "Unknown command '" + NoparseFilterAroundTag(command) + "'";
  124.             if (args.Length > 1) { error += " with arguments "; }
  125.             for (int i = 1; i < args.Length; ++i) {
  126.                 if (i > 1) { error += ", "; }
  127.                 error += "'" + NoparseFilterAroundTag(args [i]) + "'";
  128.             }
  129.             log (error);
  130.         }
  131.         // if we have a command
  132.         if (cmd != null) {
  133.             // execute it if it has valid code
  134.             if (cmd.handler != null) {
  135.                 cmd.handler (args);
  136.             } else {
  137.                 log ("Null command '" + command + "'");
  138.             }
  139.         }
  140.     }
  141.     #endregion // commands
  142.     #region user interface
  143.     public string promptArtifact = "$ ";
  144.     [Tooltip ("the main viewable UI component")]
  145.     private Canvas mainview;
  146.     public enum Interactivity { disabled, screenOverlayOnly, worldSpaceOnly, activeScreenInactiveWorld };
  147.     public Interactivity interactivity = Interactivity.activeScreenInactiveWorld;
  148.     [Tooltip ("Which key shows the terminal")]
  149.     public KeyCode keyToActivate = KeyCode.BackQuote;
  150.     [Tooltip ("Which key hides the terminal")]
  151.     public KeyCode keyToDeactivate = KeyCode.Escape;
  152.     [Tooltip ("used to size the console Rect Transform on creation as a UI overlay")]
  153.     public RectTransformSettings screenOverlaySettings;
  154.     [Tooltip ("fill this out to set the console in the world someplace")]
  155.     public PutItInWorldSpace worldSpaceSettings = new PutItInWorldSpace(0.005f, Vector2.zero);
  156.     [Tooltip ("used to color the console on creation")]
  157.     public InitialColorSettings colorSet = new InitialColorSettings();
  158.     private TMPro.TMP_InputField tmpInputField;
  159.     /// <summary>used to prevent multiple simultaneous toggles of visibility</summary>
  160.     private bool togglingVisiblityWithMultitouch = false;
  161.     [Tooltip ("If true, will show up and take user input immediately")]
  162.     public bool activeOnStart = true;
  163.     private bool needToShowUserPrompt = true;
  164.     [Tooltip ("The TextMeshPro font used. If null, built-in-font should be used.")]
  165.     public TMP_FontAsset textMeshProFont;
  166.     /// <summary>which command line is currently active, and disabling user controls</summary>
  167.     private static CmdLine currentlyActiveCmdLine, disabledUserControls;
  168.     /// <summary>used to check which command line is the best one for the user controlling the main camera</summary>
  169.     private float viewscore;
  170.  
  171.     [System.Serializable]
  172.     public class InitialColorSettings {
  173.         public Color background = new Color (0, 0, 0, 0.5f);
  174.         public Color text = new Color (1, 1, 1);
  175.         public Color errorText = new Color (1, 0, 0);
  176.         public Color specialText = new Color (1, .75f, 0);
  177.         public Color scrollbar = new Color (1, 1, 1, 0.5f);
  178.         public Color userInput = new Color(.5f,1,.75f);
  179.         public Color userSelection = new Color(1,.75f,.75f,.75f);
  180.         public string input { get{ return CmdLine.ColorToHexCode (userInput); } }
  181.         public string error { get{ return CmdLine.ColorToHexCode (errorText); } }
  182.         public string special { get{ return CmdLine.ColorToHexCode (specialText); } }
  183.     }
  184.     [System.Serializable]
  185.     public class RectTransformSettings {
  186.         public Vector2 anchorMin = Vector2.zero, anchorMax = Vector2.one,
  187.         offsetMin = Vector2.zero, offsetMax = Vector2.zero;
  188.     }
  189.     [System.Serializable]
  190.     public class PutItInWorldSpace {
  191.         [Tooltip("If zero, will automatically set to current Screen's pixel size")]
  192.         public Vector2 screenSize = new Vector2(0,0);
  193.         public float textScale = 0.005f;
  194.         public PutItInWorldSpace(float scale, Vector2 size) {
  195.             this.textScale = scale;
  196.             this.screenSize = size;
  197.         }
  198.         public void ApplySettingsTo(Canvas c) {
  199.             if (screenSize == Vector2.zero) { screenSize = new Vector2 (Screen.width, Screen.height); }
  200.             c.renderMode = RenderMode.WorldSpace;
  201.             RectTransform r = c.GetComponent<RectTransform> ();
  202.             r.sizeDelta = screenSize;
  203.             c.transform.localPosition = Vector3.zero;
  204.             c.transform.localRotation = Quaternion.identity;
  205.             r.anchoredPosition = Vector2.zero;
  206.             r.localScale = new Vector3 (textScale, textScale, textScale);
  207.         }
  208.     }
  209.     public bool IsInOverlayMode() {
  210.         return mainview.renderMode == RenderMode.ScreenSpaceOverlay;
  211.     }
  212.     public void PositionInWorld(Vector3 center, Vector2 size = default(Vector2), float scale = 0.005f) {
  213.         if (size == Vector2.zero) size = new Vector2 (Screen.width, Screen.height);
  214.         PutItInWorldSpace ws = new PutItInWorldSpace(scale, size);
  215.         if (mainview == null) {
  216.             worldSpaceSettings = ws;
  217.         } else {
  218.             ws.ApplySettingsTo (mainview);
  219.         }
  220.     }
  221.     public void SetOverlayModeInsteadOfWorld(bool useOverlay) {
  222.         if (useOverlay && mainview.renderMode != RenderMode.ScreenSpaceOverlay) {
  223.             mainview.renderMode = RenderMode.ScreenSpaceOverlay;
  224.         } else if(!useOverlay) {
  225.             worldSpaceSettings.ApplySettingsTo (mainview);
  226.         }
  227.     }
  228.     private Canvas CreateUI () {
  229.         mainview = FindComponentUpHierarchy<Canvas> (transform);
  230.         if (!mainview) {
  231.             mainview = (new GameObject ("canvas")).AddComponent<Canvas> (); // so that the UI can be drawn at all
  232.             mainview.renderMode = RenderMode.ScreenSpaceOverlay;
  233.             if (!mainview.GetComponent<CanvasScaler> ()) {
  234.                 mainview.gameObject.AddComponent<CanvasScaler> (); // so that text is pretty when zoomed
  235.             }
  236.             if (!mainview.GetComponent<GraphicRaycaster> ()) {
  237.                 mainview.gameObject.AddComponent<GraphicRaycaster> (); // so that mouse can select input area
  238.             }
  239.             mainview.transform.SetParent (transform);
  240.         }
  241.         GameObject tmpGo = new GameObject ("TextMeshPro - InputField");
  242.         tmpGo.transform.SetParent (mainview.transform);
  243.         Image img = tmpGo.AddComponent<Image> ();
  244.         img.color = colorSet.background;
  245.         if (screenOverlaySettings == null) {
  246.             MaximizeRectTransform (tmpGo.transform);
  247.         } else {
  248.             RectTransform r = tmpGo.GetComponent<RectTransform> ();
  249.             r.anchorMin = screenOverlaySettings.anchorMin;
  250.             r.anchorMax = screenOverlaySettings.anchorMax;
  251.             r.offsetMin = screenOverlaySettings.offsetMin;
  252.             r.offsetMax = screenOverlaySettings.offsetMax;
  253.         }
  254.         tmpInputField = tmpGo.AddComponent<TMP_InputField> ();
  255.         tmpInputField.lineType = TMP_InputField.LineType.MultiLineNewline;
  256.         tmpInputField.textViewport = tmpInputField.GetComponent<RectTransform> ();
  257.         TextMeshProUGUI tmpText = (new GameObject ("Text")).AddComponent<TextMeshProUGUI> ();
  258.         if (textMeshProFont != null) {
  259.             tmpText.font = textMeshProFont;
  260.         }
  261.         tmpText.fontSize = 14;
  262.         tmpText.transform.SetParent (tmpGo.transform);
  263.         tmpInputField.textComponent = tmpText;
  264.         tmpInputField.fontAsset = tmpText.font;
  265.         tmpInputField.pointSize = tmpText.fontSize;
  266.         MaximizeRectTransform (tmpText.transform);
  267.         tmpGo.AddComponent<RectMask2D> ();
  268.         tmpInputField.onFocusSelectAll = false;
  269.         tmpText.color = colorSet.text;
  270.         tmpInputField.selectionColor = colorSet.userSelection;
  271.         tmpInputField.customCaretColor = true;
  272.         tmpInputField.caretColor = colorSet.userInput;
  273.         tmpInputField.caretWidth = 5;
  274.         tmpInputField.ActivateInputField ();
  275.         tmpInputField.onValueChanged.AddListener (listener_OnValueChanged);
  276.         tmpInputField.characterValidation = TMP_InputField.CharacterValidation.CustomValidator;
  277.         tmpInputField.inputValidator = GetInputValidator();
  278.         if (tmpInputField.verticalScrollbar == null) {
  279.             GameObject scrollbar = new GameObject ("scrollbar vertical");
  280.             scrollbar.transform.SetParent (tmpInputField.transform);
  281.             scrollbar.AddComponent<RectTransform> ();
  282.             tmpInputField.verticalScrollbar = scrollbar.AddComponent<Scrollbar> ();
  283.             tmpInputField.verticalScrollbar.direction = Scrollbar.Direction.TopToBottom;
  284.             RectTransform r = scrollbar.GetComponent<RectTransform> ();
  285.             r.anchorMin = new Vector2 (1, 0);
  286.             r.anchorMax = Vector2.one;
  287.             r.offsetMax = Vector3.zero;
  288.             r.offsetMin = new Vector2 (-16, 0);
  289.         }
  290.         if (tmpInputField.verticalScrollbar.handleRect == null) {
  291.             GameObject slideArea = new GameObject ("sliding area");
  292.             slideArea.transform.SetParent (tmpInputField.verticalScrollbar.transform);
  293.             RectTransform r = slideArea.AddComponent<RectTransform> ();
  294.             MaximizeRectTransform (slideArea.transform);
  295.             r.offsetMin = new Vector2 (10, 10);
  296.             r.offsetMax = new Vector2 (-10, -10);
  297.             GameObject handle = new GameObject ("handle");
  298.             Image bimg = handle.AddComponent<Image> ();
  299.             bimg.color = colorSet.scrollbar;
  300.             handle.transform.SetParent (slideArea.transform);
  301.             r = handle.GetComponent<RectTransform> ();
  302.             r.anchorMin = r.anchorMax = Vector2.zero;
  303.             r.offsetMax = new Vector2 (5, 5);
  304.             r.offsetMin = new Vector2 (-5, -5);
  305.             r.pivot = Vector2.one;
  306.             tmpInputField.verticalScrollbar.handleRect = r;
  307.             tmpInputField.verticalScrollbar.targetGraphic = img;
  308.         }
  309.         // an event system is required... if there isn't one, make one
  310.         StandaloneInputModule input = FindObjectOfType (typeof(StandaloneInputModule)) as StandaloneInputModule;
  311.         if (input == null) {
  312.             input = (new GameObject ("<EventSystem>")).AddComponent<StandaloneInputModule> ();
  313.         }
  314.         // put all UI in the UI layer
  315.         SetLayerRecursive (mainview.gameObject, LayerMask.NameToLayer ("UI"));
  316.         // turn it off and then back on again... that fixes some things.
  317.         tmpGo.SetActive (false); tmpGo.SetActive (true);
  318.         // put it in the world (if needed)
  319.         if (interactivity == Interactivity.worldSpaceOnly
  320.             || interactivity == Interactivity.activeScreenInactiveWorld) {
  321.             worldSpaceSettings.ApplySettingsTo (mainview);
  322.         }
  323.         return mainview;
  324.     }
  325.     private static RectTransform MaximizeRectTransform (Transform t) {
  326.         return MaximizeRectTransform (t.GetComponent<RectTransform> ());
  327.     }
  328.     private static RectTransform MaximizeRectTransform (RectTransform r) {
  329.         r.anchorMax = Vector2.one;
  330.         r.anchorMin = r.offsetMin = r.offsetMax = Vector2.zero;
  331.         return r;
  332.     }
  333.     public bool IsVisible () {
  334.         return mainview != null && mainview.gameObject.activeInHierarchy;
  335.     }
  336.     /// <summary>shows (true) or hides (false).</summary>
  337.     public void SetVisibility (bool visible) {
  338.         if (mainview == null) {
  339.             activeOnStart = visible;
  340.         } else {
  341.             mainview.gameObject.SetActive (visible);
  342.         }
  343.     }
  344.     /// <param name="enabled">If <c>true</c>, reads from keybaord. if <c>false</c>, stops reading from keyboard</param>
  345.     public void SetInputActive(bool enabled) {
  346.         if (enabled) {
  347.             tmpInputField.ActivateInputField ();
  348.         } else {
  349.             tmpInputField.DeactivateInputField ();
  350.         }
  351.     }
  352.     /// <param name="enableInteractive"><c>true</c> to turn this on (and turn the previous CmdLine off)</param>
  353.     public void SetInteractive(bool enableInteractive) {
  354.         bool activityWhenStarted = tmpInputField.interactable;
  355.         if (enableInteractive && currentlyActiveCmdLine != null) {
  356.             currentlyActiveCmdLine.SetInteractive (false);
  357.         }
  358.         tmpInputField.interactable = enableInteractive; // makes focus possible
  359.         switch (interactivity) {
  360.         case Interactivity.screenOverlayOnly:
  361.             if (!IsInOverlayMode ()) {
  362.                 SetOverlayModeInsteadOfWorld (true);
  363.             }
  364.             SetVisibility (enableInteractive);
  365.             break;
  366.         case Interactivity.worldSpaceOnly:
  367.             if (!IsVisible ()) {
  368.                 SetVisibility (true);
  369.             }
  370.             if (enableInteractive)
  371.                 SetOverlayModeInsteadOfWorld (false);
  372.             break;
  373.         case Interactivity.activeScreenInactiveWorld:
  374.             if (!IsVisible ()) {
  375.                 SetVisibility (true);
  376.             }
  377.             SetOverlayModeInsteadOfWorld (enableInteractive);
  378.             break;
  379.         }
  380.         tmpInputField.verticalScrollbar.value = 1; // scroll to the bottom
  381.         MoveCaretToEnd (); // move caret focus to end
  382.         SetInputActive (tmpInputField.interactable); // request/deny focus
  383.         if (enableInteractive) {
  384.             currentlyActiveCmdLine = this;
  385.         } else if (currentlyActiveCmdLine == this) {
  386.             // if this command line has disabled the user
  387.             if (disabledUserControls == currentlyActiveCmdLine) {
  388.                 // tell it to re-enable controls
  389.                 //if(disabledUserControls.onDeactivateInteraction != null) disabledUserControls.onDeactivateInteraction.Invoke ();
  390.                 if(!callbacks.ignoreCallbacks && callbacks.whenThisDeactivates != null) callbacks.whenThisDeactivates.Invoke();
  391.                 disabledUserControls = null;
  392.             }
  393.             currentlyActiveCmdLine = null;
  394.         }
  395.     }
  396.     public bool IsInteractive() { return tmpInputField != null && tmpInputField.interactable; }
  397.     /// <summary>Moves the caret to the end, clearing all selections in the process</summary>
  398.     public void MoveCaretToEnd () {
  399.         int lastPoint = GetRawText ().Length;
  400.         SetCaretPosition (lastPoint);
  401.     }
  402.     #endregion // user interface
  403.     #region input validation
  404.     /// <summary>console data that should not be modifiable as user input</summary>
  405.     private string nonUserInput = "";
  406.     private CmdLineValidator inputvalidator;
  407.     /// <summary>keeps track of user selection so that the text field can be fixed if selected text is removed</summary>
  408.     private int selectBegin, selectEnd;
  409.     /// <summary>what replaces an attempt to un-escape the TextMeshPro noparse boundary in the command line</summary>
  410.     public const string NOPARSE_REPLACEMENT = ">NOPARSE<";
  411.     /// <summary>flag to move text view to the bottom when content is added</summary>
  412.     private bool showBottomWhenTextIsAdded = false;
  413.     /// <summary>if text is being modified to refresh it after the user did something naughty</summary>
  414.     private bool addingOnChanged = false;
  415.     [Tooltip ("Maximum number of lines to retain.")]
  416.     public int maxLines = 99;
  417.     [Tooltip ("lines with more characters than this will count as more than one line.")]
  418.     public int maxColumnsPerLine = 120;
  419.  
  420.     private CmdLineValidator GetInputValidator() {
  421.         if (inputvalidator == null) {
  422.             inputvalidator = ScriptableObject.CreateInstance<CmdLineValidator> ();
  423.             inputvalidator.cmd = this;
  424.         }
  425.         return inputvalidator;
  426.     }
  427.     private void listener_OnTextSelectionChange (string str, int start, int end) {
  428.         selectBegin = Math.Min (start, end);
  429.         selectEnd = Math.Max (start, end);
  430.     }
  431.     /// <summary>the class that tries to keep the user from wrecking the command line terminal</summary>
  432.     private class CmdLineValidator : TMP_InputValidator {
  433.         public CmdLine cmd;
  434.         public bool isUserEnteringInput = false;
  435.         public void AddUserInput(string userInput) {
  436.             string s = cmd.tmpInputField.text;
  437.             int cursor = cmd.tmpInputField.caretPosition; // should this be caretPosition?
  438.             for(int i = 0; i < userInput.Length; ++i) {
  439.                 char c = userInput [i];
  440.                 Validate(ref s, ref cursor, c);
  441.             }
  442.             cmd.tmpInputField.text = s;
  443.             cmd.tmpInputField.caretPosition = cursor;
  444.         }
  445.         int AddUserInput(ref string text, char letter) {
  446.             int added = 0;
  447.             if(!isUserEnteringInput) {
  448.                 isUserEnteringInput = true;
  449.                 string headr = BEGIN_USER_INPUT();
  450.                 text += headr;
  451.                 added += headr.Length;
  452.                 cmd.nonUserInput = text;
  453.             }
  454.             text += letter; added += 1;
  455.             return added;
  456.         }
  457.         public int EndUserInput(bool forced) {
  458.             if (forced)
  459.                 isUserEnteringInput = true;
  460.             string s = cmd.tmpInputField.text;
  461.             int returned = EndUserInput (ref s);
  462.             cmd.tmpInputField.text = s;
  463.             return returned;
  464.         }
  465.         public bool HasProperInputTags(string text) {
  466.             List<string> tags = CmdLine.CalculateTextMeshProTags(text, false);
  467.             if (tags.Count == 0 || !tags.Contains ("noparse"))
  468.                 return false;
  469.             string colorTag = "#" + cmd.colorSet.input;
  470.             return tags.Contains(colorTag);
  471.         }
  472.         public bool CheckIfUserInputTagsArePresent(string text) {
  473.             // check if the new text has the input tags opened
  474.             return isUserEnteringInput = HasProperInputTags(text);
  475.         }
  476.         public string BEGIN_USER_INPUT() {
  477.             return "<#" + cmd.colorSet.input + "><noparse>";
  478.         }
  479.         public string END_USER_INPUT() { return "</noparse></color>"; }
  480.         private int EndUserInput(ref string text) {
  481.             int added = 0;
  482.             if (isUserEnteringInput) {
  483.                 isUserEnteringInput = false;
  484.                 string expectedheadr = BEGIN_USER_INPUT ();
  485.                 if(text.EndsWith(expectedheadr)) {
  486.                     text = text.Substring(0, text.Length-expectedheadr.Length);
  487.                     added -= expectedheadr.Length;
  488.                 } else {
  489.                     string footr = END_USER_INPUT();
  490.                     text += footr;
  491.                     added += footr.Length;
  492.                 }
  493.                 cmd.nonUserInput = text;
  494.             }
  495.             return added;
  496.         }
  497.         public override char Validate(ref string text, ref int i, char c) {
  498.             if (!cmd.IsInteractive ()) return '\0';
  499.             char letter = '\0';
  500.             if (i < text.Length) {
  501.                 letter = text [i];
  502.             }
  503.             if (i < cmd.nonUserInput.Length) {
  504.                 i = cmd.GetRawText ().Length;
  505.             }
  506.             i += AddUserInput (ref text, c);
  507.             // if the user is attempting to break out of noparse...
  508.             if (c == '>') {
  509.                 // check if a tag is being ended
  510.                 int startOfTag = text.LastIndexOf ('<');
  511.                 int endOfTag = text.LastIndexOf ('>', text.Length - 2);
  512.                 if (startOfTag >= 0 && startOfTag > endOfTag) {
  513.                     string possibleTag = text.Substring (startOfTag).ToLower ();
  514.                     // unescape, incase the user is being trixie with unescape sequences...
  515.                     possibleTag = CmdLine.Unescape (possibleTag);
  516.                     // and if they are, just don't let them.
  517.                     if (possibleTag.Contains ("noparse")) {
  518.                         text = text.Substring (0, startOfTag) + NOPARSE_REPLACEMENT;
  519.                     }
  520.                 }
  521.             }
  522.             // if the user wants to execute (because they pressed enter)
  523.             else if (c =='\n') {
  524.                 string inpt = cmd.GetUserInput ();
  525.                 int start = 0, end = -1;
  526.                 do {
  527.                     end = inpt.IndexOf("\n", start);
  528.                     if(end >= start && start < inpt.Length) {
  529.                         int len = end-start;
  530.                         if(len > 0) {
  531.                             cmd.EnqueueRun(inpt.Substring(start, len));
  532.                         }
  533.                         start = end+1;
  534.                     }
  535.                 } while(end > 0);
  536.                 if (start < inpt.Length) {
  537.                     cmd.EnqueueRun(inpt.Substring(start));
  538.                 }
  539.                 EndUserInput (ref text);
  540.             }
  541.             return '\0';
  542.         }
  543.     }
  544.     private void listener_OnValueChanged (string str) {
  545.         if (addingOnChanged) return;
  546.         addingOnChanged = true;
  547.         string newAddition = Input.inputString;
  548.         // don't allow output text to be modified.
  549.         if (GetCaretPosition () < nonUserInput.Length) {
  550.             int offset = selectBegin - selectEnd;
  551.             string alreadyTyped = GetUserInput (offset);
  552.             setText (nonUserInput+alreadyTyped);
  553.             MoveCaretToEnd ();
  554.         }
  555.         addingOnChanged = false;
  556.     }
  557.     private void EndUserInputIfNeeded() {
  558.         if (GetInputValidator().isUserEnteringInput) {
  559.             inputvalidator.isUserEnteringInput = false;
  560.             setText (GetAllText () + inputvalidator.END_USER_INPUT());
  561.         }
  562.     }
  563.     /// <summary>if the given text is a tag, returns the tag with noparse around it.</summary>
  564.     private string NoparseFilterAroundTag(string text) {
  565.         if (text.IndexOf ('<') < 0) return text;
  566.         return "<noparse>" + text + "</noparse>";
  567.     }
  568.     private int CutoffIndexToEnsureLineCount (String s, int maxLines) {
  569.         int lineCount = 0, columnCount = 0, index;
  570.         for (index = s.Length; index > 0; --index) {
  571.             if (s [index - 1] == '\n' || columnCount++ >= maxColumnsPerLine) {
  572.                 lineCount++;
  573.                 columnCount = 0;
  574.                 if (lineCount >= maxLines) { break; }
  575.             }
  576.         }
  577.         return index;
  578.     }
  579.     public int GetUserInputLength(int offset = 0) {
  580.         string s = GetRawText ();
  581.         return s.Length - (nonUserInput.Length+offset);
  582.     }
  583.     /// <returns>The user input, which is text that the user has entered (at the bottom)</returns>
  584.     private string GetUserInput (int offset = 0) {
  585.         string s = GetRawText ();
  586.         int len = s.Length - (nonUserInput.Length+offset);
  587.         return (len > 0)?s.Substring (nonUserInput.Length+offset, len):"";
  588.     }
  589.     /// <param name="text">What the the output text should be (turns current user input into text output)</param>
  590.     public void setText (string text) {
  591.         int cutIndex = CutoffIndexToEnsureLineCount (text, maxLines);
  592.         List<string> tags = null;
  593.         if (cutIndex != 0) {
  594.             tags = CalculateTextMeshProTags (text.Substring (0, cutIndex), false);
  595.             text = text.Substring (cutIndex);
  596.             if (tags != null && tags.Count > 0) {
  597.                 string openingTags = "";
  598.                 for (int i = 0; i < tags.Count; ++i) {
  599.                     openingTags += "<" + tags [i] + ">";
  600.                 }
  601.                 text = openingTags + text;
  602.             }
  603.         }
  604.         nonUserInput = text;
  605.         SetRawText (nonUserInput);
  606.         // if text is replaced during input, this refreshes tags around input
  607.         if (inputvalidator != null) {
  608.             inputvalidator.CheckIfUserInputTagsArePresent (text);
  609.         }
  610.     }
  611.     #endregion // input validation
  612.     #region singleton
  613.     /// <summary>the singleton instance. One will be created if none exist.</summary>
  614.     private static CmdLine instance;
  615.     public static CmdLine Instance {
  616.         get {
  617.             if (instance == null) {
  618.                 if ((instance = FindObjectOfType (typeof(CmdLine)) as CmdLine) == null) {
  619.                     GameObject g = new GameObject ();
  620.                     instance = g.AddComponent<CmdLine> ();
  621.                     g.name = "<" + instance.GetType ().Name + ">";
  622.                 }
  623.             }
  624.             return instance;
  625.         }
  626.     }
  627.     #endregion // singleton
  628.     #region static utility functions
  629.     /// <summary>Convenience method. Finds the component here, or in a parent object.</summary>
  630.     public static T FindComponentUpHierarchy<T> (Transform t) where T : Component {
  631.         T found = null;
  632.         while (t != null && found == null) {
  633.             found = t.GetComponent<T> ();
  634.             t = t.parent;
  635.         }
  636.         return found;
  637.     }
  638.     /// <param name="layer">what Unity layer to set the given object, and all child objects, recursive</param>
  639.     public static void SetLayerRecursive (GameObject go, int layer) {
  640.         go.layer = layer;
  641.         for (int i = 0; i < go.transform.childCount; ++i) {
  642.             Transform t = go.transform.GetChild (i);
  643.             if (t != null) {
  644.                 SetLayerRecursive (t.gameObject, layer);
  645.             }
  646.         }
  647.     }
  648.     /// <returns>A list of the open/close tags in the given strings</returns>
  649.     /// <param name="str">where to look for tags</param>
  650.     /// <param name="keepClosedTags">If <c>false</c>, remove correctly closed tags</param>
  651.     public static List<string> CalculateTextMeshProTags(string str, bool keepClosedTags = true) {
  652.         List<string> tags = new List<string> ();
  653.         bool noparse = false;
  654.         for (int i = 0; i < str.Length; ++i) {
  655.             char c = str [i];
  656.             if (c == '<') {
  657.                 int end = str.IndexOf ('>', i);
  658.                 string token = null;
  659.                 if (end > 0) {
  660.                     // just get the starting token, ignore properties after the first space
  661.                     int space = str.IndexOf (' ', i);
  662.                     if (space >= 0 && space < end) { end = space; }
  663.                     token = str.Substring (i+1, end-(i+1));
  664.                 }
  665.                 // if noparse is one of the tags, ignore all other tags till noparse is closed.
  666.                 if (noparse) {
  667.                     if (token != null && token.Trim () == "/noparse") {
  668.                         noparse = false;
  669.                     } else {
  670.                         token = null;
  671.                     }
  672.                 }
  673.                 if (!noparse && token.Trim () == "noparse") {
  674.                     noparse = true;
  675.                 }
  676.                 if (!keepClosedTags && token != null) {
  677.                     if (token.StartsWith ("/") && tags.Count > 0) {
  678.                         int whichTag = tags.IndexOf (token.Substring (1));
  679.                         if (token == "/color") {
  680.                             for(int e = tags.Count-1; e >= 0; --e) {
  681.                                 if (tags [e].StartsWith ("#")) {
  682.                                     whichTag = e; break;
  683.                                 }
  684.                             }
  685.                         }
  686.                         if (whichTag >= 0) {
  687.                             tags.RemoveAt (whichTag);
  688.                             token = null;
  689.                         }
  690.                     } else if (token.EndsWith ("/")) { token = null; }
  691.                 }
  692.                 if (token != null) { tags.Add (token); }
  693.             }
  694.         }
  695.         return tags;
  696.     }
  697.     public static string ColorToHexCode(Color c) {
  698.         int r = (int)(255 * c.r),  g = (int)(255 * c.g), b = (int)(255 * c.b), a = (int)(255 * c.a);
  699.         return r.ToString("X2")+g.ToString("X2")+b.ToString("X2")+((c.a!=1)?a.ToString("X2"):"");
  700.     }
  701.     private static readonly char[] quotes = new char[] { '\'', '\"' },
  702.     whitespace = new char[] { ' ', '\t', '\n', '\b', '\r' };
  703.     /// <returns>index of the end of the token that starts at the given index 'i'</returns>
  704.     public static int FindEndArgumentToken (string str, int i) {
  705.         bool isWhitespace;
  706.         do {
  707.             isWhitespace = System.Array.IndexOf (whitespace, str [i]) >= 0;
  708.             if (isWhitespace) { ++i; }
  709.         } while (isWhitespace && i < str.Length);
  710.         int index = System.Array.IndexOf (quotes, str [i]);
  711.         char startQuote = (index >= 0) ? quotes [index] : '\0';
  712.         if (startQuote != '\0') { ++i; }
  713.         while (i < str.Length) {
  714.             if (startQuote != '\0') {
  715.                 if (str [i] == '\\') {
  716.                     i++; // skip the next character for an escape sequence. just leave it there.
  717.                 } else {
  718.                     index = System.Array.IndexOf (quotes, str [i]);
  719.                     bool endsQuote = index >= 0 && quotes [index] == startQuote;
  720.                     if (endsQuote) { i++; break; }
  721.                 }
  722.             } else {
  723.                 isWhitespace = System.Array.IndexOf (whitespace, str [i]) >= 0;
  724.                 if (isWhitespace) { break; }
  725.             }
  726.             i++;
  727.         }
  728.         if (i >= str.Length) { i = str.Length; }
  729.         return i;
  730.     }
  731.     /// <returns>split command-line arguments</returns>
  732.     public static List<string> ParseArguments (string commandLineInput) {
  733.         int index = 0;
  734.         string token;
  735.         List<string> tokens = new List<string> ();
  736.         while (index < commandLineInput.Length) {
  737.             int end = FindEndArgumentToken (commandLineInput, index);
  738.             if (index != end) {
  739.                 token = commandLineInput.Substring (index, end - index).TrimStart (whitespace);
  740.                 token = Unescape (token);
  741.                 int qi = System.Array.IndexOf (quotes, token [0]);
  742.                 if (qi >= 0 && token [token.Length - 1] == quotes [qi]) {
  743.                     token = token.Substring (1, token.Length - 2);
  744.                 }
  745.                 tokens.Add (token);
  746.             }
  747.             index = end;
  748.         }
  749.         return tokens;
  750.     }
  751.     /* https://msdn.microsoft.com/en-us/library/aa691087(v=vs.71).aspx */
  752.     private readonly static SortedDictionary<char, char> EscapeMap = new SortedDictionary<char, char> {
  753.         { '0','\0' }, { 'a','\a' }, { 'b','\b' }, { 'f','\f' },
  754.         { 'n','\n' }, { 'r','\r' }, { 't','\t' }, { 'v','\v' },
  755.     };
  756.     /// <summary>convenience method to unescape standard escape sequence strings</summary>
  757.     /// <param name="escaped">Escaped.</param>
  758.     public static string Unescape (string escaped) {
  759.         if (escaped == null) { return escaped; }
  760.         StringBuilder sb = new StringBuilder ();
  761.         bool inEscape = false;
  762.         int startIndex = 0;
  763.         for (int i = 0; i < escaped.Length; i++) {
  764.             if (!inEscape) {
  765.                 inEscape = escaped [i] == '\\';
  766.             } else {
  767.                 char c;
  768.                 if (!EscapeMap.TryGetValue (escaped [i], out c)) {
  769.                     c = escaped [i]; // unknown escape sequences are literals
  770.                 }
  771.                 sb.Append (escaped.Substring (startIndex, i - startIndex - 1));
  772.                 sb.Append (c);
  773.                 startIndex = i + 1;
  774.                 inEscape = false;
  775.             }
  776.         }
  777.         sb.Append (escaped.Substring (startIndex));
  778.         return sb.ToString ();
  779.     }
  780.  
  781.     #endregion // static utility functions
  782.     #region public API
  783.     /// <summary>if delegates are here, calls this code instead of executing a known a command</summary>
  784.     private event DoAfterStringIsRead waitingToReadLine;
  785.     /// <summary>If this is set, ignore the native command line functionality, and just do this</summary>
  786.     public DoAfterStringIsRead onInput;
  787.  
  788.     /// <summary>what to do after a string is read.</summary>
  789.     public delegate void DoAfterStringIsRead (string readFromUser);
  790.     public delegate void DoAfterVisiblityChange ();
  791.     public static void SetText(string text) { Instance.setText (text); }
  792.     /// <returns>The all text, including user input</returns>
  793.     public string GetAllText () { return (tmpInputField) ? GetRawText () : nonUserInput; }
  794.     /// <param name="text">Text to add as output, also turning current user input into text output</param>
  795.     public void AddText (string text) {
  796.         EndUserInputIfNeeded ();
  797.         setText (GetAllText () + text);
  798.     }
  799.     /// <param name="text">line to add as output, also turning current user input into text output</param>
  800.     public void println (string line) { AddText (line + "\n"); }
  801.     public void readLineAsync (DoAfterStringIsRead stringCallback) {
  802.         if (!IsInteractive () && tmpInputField != null) { SetInteractive (true); }
  803.         waitingToReadLine += stringCallback;
  804.     }
  805.     public void getInputAsync(DoAfterStringIsRead stringCallback) { readLineAsync (stringCallback); }
  806.     public static void GetInputAsync(DoAfterStringIsRead stringCallback) { Instance.readLineAsync (stringCallback); }
  807.     public static void ReadLine (DoAfterStringIsRead stringCallback) { Instance.readLineAsync (stringCallback); }
  808.     /// <summary>Instance.println(line)</summary>
  809.     public static void Log (string line) { Instance.println (line); }
  810.     public void log (string line) { println (line); }
  811.     public void readLine (DoAfterStringIsRead stringCallback) { readLineAsync (stringCallback); }
  812.     public string GetRawText () { return tmpInputField.text; }
  813.     public void SetRawText (string s) { if(tmpInputField != null){ tmpInputField.text = s; } }
  814.     public int GetCaretPosition () { return tmpInputField.stringPosition; }
  815.     public void SetCaretPosition (int pos) { tmpInputField.stringPosition = pos; }
  816.     #endregion // pubilc API
  817.     #region Debug.Log intercept
  818.     [SerializeField, Tooltip ("If true, all Debug.Log messages will be intercepted and duplicated here.")]
  819.     private bool interceptDebugLog = true;
  820.     /// <summary>if this object was intercepting Debug.Logs, this will ensure that it un-intercepts as needed</summary>
  821.     private bool dbgIntercepted = false;
  822.  
  823.     public void EnableDebugLogIntercept () { SetDebugLogIntercept (interceptDebugLog); }
  824.     public void DisableDebugLogIntercept () { SetDebugLogIntercept (false); }
  825.     public void SetDebugLogIntercept (bool intercept) {
  826.         if (intercept && !dbgIntercepted) {
  827.             Application.logMessageReceived += HandleLog;
  828.             dbgIntercepted = true;
  829.         } else if (!intercept && dbgIntercepted) {
  830.             Application.logMessageReceived -= HandleLog;
  831.             dbgIntercepted = false;
  832.         }
  833.     }
  834.     private void HandleLog (string logString, string stackTrace, LogType type) {
  835.         switch (type) {
  836.         case LogType.Error:
  837.             AddText ("<#"+ColorToHexCode(Color.Lerp(colorSet.text, Color.red, 0.5f))+">"+logString+"</color>\n");
  838.             break;
  839.         case LogType.Exception:
  840.             AddText ("<#"+ColorToHexCode(Color.Lerp(colorSet.text, Color.magenta, 0.5f))+">"+logString+"</color>\n");
  841.             break;
  842.         case LogType.Warning:
  843.             AddText ("<#"+ColorToHexCode(Color.Lerp(colorSet.text, Color.yellow, 0.5f))+">"+logString+"</color>\n");
  844.             break;
  845.         default:
  846.             log (logString);
  847.             break;
  848.         }
  849.     }
  850.     #endregion // Debug.Log intercept
  851.     #region Unity Editor interaction
  852.     private static Mesh _editorMesh = null; // one variable to enable better UI in the editor
  853.  
  854.     void OnDrawGizmos() {
  855.         if (_editorMesh == null) {
  856.             _editorMesh = new Mesh ();
  857.             _editorMesh.vertices = new Vector3[]{ new Vector3(-.5f, .5f),new Vector3( .5f, .5f), new Vector3(-.5f,-.5f),new Vector3( .5f,-.5f)};
  858.             _editorMesh.triangles = new int[]{ 0,1,2,  3,2,1 };
  859.             _editorMesh.RecalculateNormals ();
  860.             _editorMesh.RecalculateBounds ();
  861.         }
  862.         Vector3 s = this.worldSpaceSettings.screenSize;
  863.         if (s == Vector3.zero) {
  864.             s = new Vector3 (
  865.                 Screen.width*transform.lossyScale.x*worldSpaceSettings.textScale,
  866.                 Screen.height*transform.lossyScale.y*worldSpaceSettings.textScale,
  867.                 1*transform.lossyScale.z*worldSpaceSettings.textScale);
  868.         }
  869.         Gizmos.DrawMesh(_editorMesh, transform.position, transform.rotation, s);
  870.         Transform t = transform;
  871.         // calculate extents
  872.         Vector3[] points = new Vector3[]{(t.up*s.y/2 + t.right*s.x/-2),(t.up*s.y/2 + t.right*s.x/2),
  873.             (t.up*s.y/-2 + t.right*s.x/2),(t.up*s.y/-2 + t.right*s.x/-2)};
  874.         for (int i = 0; i < points.Length; ++i) { points[i] += t.position; }
  875.         Gizmos.color = colorSet.background;
  876.         for (int i = 0; i < points.Length; ++i) {
  877.             Gizmos.DrawLine (points [i], points [(i + 1) % points.Length]);
  878.         }
  879.     }
  880.     #endregion // Unity Editor interaction
  881.     #region Enable/Disable
  882.     [System.Serializable]
  883.     public struct Callbacks {
  884.         [Tooltip("When the command line goes into active editing. This may be useful to refresh info for the command line, or disable a 3D FPS controller.")]
  885.         public UnityEngine.Events.UnityEvent whenThisActivates;
  886.         [Tooltip("When the command line leaves active editing. This may be useful to re-enable a 3D FPS controller.")]
  887.         public UnityEngine.Events.UnityEvent whenThisDeactivates;
  888.         public bool ignoreCallbacks;
  889.     }
  890.     [Tooltip("Recommended scripts to pair with the CmdLine: pastebin.com/FaT6i5yF\nwhenThisActivates:    StopPhysics.enablePhysics()\nwhenThisDeactivates: StopPhysics.disablePhysics()")]
  891.     public Callbacks callbacks = new Callbacks();
  892.     #endregion // Enable/Disable
  893.     #region MonoBehaviour
  894.     void Start () {
  895.         CreateUI ();
  896.         showBottomWhenTextIsAdded = true;
  897.         // test code
  898.         PopulateWithBasicCommands ();
  899.         if (nonUserInput.Length == 0) {
  900.             log (Application.productName + ", v" + Application.version);
  901.         } else {
  902.             setText (nonUserInput);
  903.         }
  904.         SetInteractive (activeOnStart);
  905.     }
  906.     void Update () {
  907.         // toggle visibility based on key presses
  908.         bool toggle = Input.GetKeyDown(IsInteractive () ? keyToDeactivate : keyToActivate );
  909.         // or toggle visibility when 5 fingers touch
  910.         if (Input.touches.Length == 5) {
  911.             if (!togglingVisiblityWithMultitouch) {
  912.                 toggle = true;
  913.                 togglingVisiblityWithMultitouch = true;
  914.             }
  915.         } else {
  916.             togglingVisiblityWithMultitouch = false;
  917.         }
  918.         if (toggle) {
  919.             if (!IsInteractive ()) {
  920.                 // check to see how clearly the user is looking at this CmdLine
  921.                 if (mainview.renderMode == RenderMode.ScreenSpaceOverlay) {
  922.                     this.viewscore = 1;
  923.                 } else {
  924.                     Vector3 lookPosition = Camera.main.transform.position;
  925.                     Vector3 gaze = Camera.main.transform.forward;
  926.                     Vector3 delta = transform.position - lookPosition;
  927.                     float distFromCam = delta.magnitude;
  928.                     float viewAlignment = Vector3.Dot (gaze, delta / distFromCam);
  929.                     if (viewAlignment < 0) {
  930.                         this.viewscore = -1;
  931.                     } else {
  932.                         this.viewscore = (1 - viewAlignment) * distFromCam;
  933.                     }
  934.                 }
  935.                 if (currentlyActiveCmdLine == null
  936.                     || (currentlyActiveCmdLine != null && (currentlyActiveCmdLine.viewscore < 0
  937.                         || (this.viewscore >= 0 && this.viewscore <= currentlyActiveCmdLine.viewscore)))) {
  938.                     SetInteractive (true);
  939.                 }
  940.             } else {
  941.                 SetInteractive (false);
  942.                 this.viewscore = -1;
  943.             }
  944.         }
  945.         // stop trying to show the bottom if the user wants to scroll
  946.         if (Input.GetAxis ("Mouse ScrollWheel") != 0) {
  947.             showBottomWhenTextIsAdded = tmpInputField.verticalScrollbar.value == 1;
  948.         }
  949.         if (showBottomWhenTextIsAdded) {
  950.             tmpInputField.verticalScrollbar.value = 1;
  951.         }
  952.         if (needToShowUserPrompt && onInput == null && (waitingToReadLine == null || waitingToReadLine.GetInvocationList ().Length == 0)) {
  953.             // in case of keyboard mashing...
  954.             if (GetUserInputLength () > 0) {
  955.                 string userInput = GetUserInput ();
  956.                 SetText (nonUserInput);  GetInputValidator().EndUserInput (true);
  957.                 AddText (promptArtifact); GetInputValidator ().AddUserInput (userInput);
  958.                 nonUserInput = tmpInputField.text.Substring (0, tmpInputField.text.Length - userInput.Length);
  959.             } else { AddText (promptArtifact); }
  960.             needToShowUserPrompt = false;
  961.         }
  962.         // run any queued-up commands
  963.         while (instructionList.Count > 0) {
  964.             Run (instructionList[0]);
  965.             instructionList.RemoveAt (0);
  966.             needToShowUserPrompt = true;
  967.         }
  968.         // if this is the active command line and it has not yet disabled user controls. done in update to stop many onStart and onStop calls from being invoked in series
  969.         if (currentlyActiveCmdLine == this && disabledUserControls != this) {
  970.             // if another command line disabled user controls
  971.             if (disabledUserControls != null) {
  972.                 // tell it to re-enable controls
  973.                 if(!callbacks.ignoreCallbacks && callbacks.whenThisDeactivates != null) callbacks.whenThisDeactivates.Invoke();
  974.             }
  975.             disabledUserControls = this;
  976.             if(!callbacks.ignoreCallbacks && callbacks.whenThisActivates != null) callbacks.whenThisActivates.Invoke();
  977.         }
  978.     }
  979.     #endregion // MonoBehaviour
  980. }
RAW Paste Data
Top