using System.Collections; using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.UI; namespace Cyber.Console { /// /// Controls an input and an output to implement a console interface to call /// arbitrary commands which can be set from anywhere in the program through /// . /// public class DebugConsole : MonoBehaviour { private static readonly Regex CommandPartRegex = new Regex("([^ \"]+ )|(\"[^\"]+\")"); /// /// The parent of the and . /// public GameObject Panel; /// /// The input for the console. /// public InputField InputField; /// /// The output for the console. /// public Text TextField; /// /// Controls the visibility of the console. For how this is controlled in-game, see . /// public bool Visible = false; /// /// The length of a row in the console in characters. /// public int RowLength = 80; /// /// The linecount threshold when the is cleaned /// up so it has lines left. /// public int MaxLinesUntilCleanup = 48; /// /// See . /// public int LineCountSavedFromCleanup = 24; private Dictionary Actions = new Dictionary(); private List Lines = new List(); private List Commands = new List(); private int LastCommandIndex = 0; private string LastUnexecutedCommand = ""; /// /// Creates a new , and sets the 's singleton. /// public DebugConsole() { Term.SetDebugConsole(this); } /// /// Tries to call the command in the . /// public void CallCommand() { if (InputField.text.Length == 0) { return; } // Log this command Println(InputField.text); Commands.Add(InputField.text); LastCommandIndex = Commands.Count; List Arguments = new List(); MatchCollection Matches = CommandPartRegex.Matches(InputField.text + " "); for (int i = 0; i < Matches.Count; i++) { Arguments.Add(Matches[i].Value.Replace('"', ' ').Trim()); } foreach (string Action in Actions.Keys) { string[] Parts = Action.Split(' '); if (Arguments.Count == Parts.Length && Arguments[0].Equals(Parts[0])) { Arguments.RemoveAt(0); Actions[Action].Call(Arguments); break; } } // Clear the input field InputField.text = ""; InputField.ActivateInputField(); } /// /// Adds a command to be used in the console. /// /// The command template that should be used. /// eg. "print (text)" or "add (number) (number)". /// Description. /// Action. public void AddCommand(string command, string description, DebugConsoleAction.Action action) { string PrettyDescription = command; foreach (string Line in description.Split('\n')) { PrettyDescription += "\n " + Line; } Actions[command] = new DebugConsoleAction(PrettyDescription, action); } /// /// Prints text into the DebugConsole and adds a newline. /// /// Text. public void Println(string text) { Print(text + "\n"); } /// /// Prints text into the Console. Wraps text at . /// /// /// If the linecount exceeds , the /// console is cleared to the point that it only has /// lines. /// /// Text. public void Print(string text) { // Wrap lines and log them to Lines int Index = 0; int EscapeIndex = 0; do { int NewLineIndex = text.IndexOf("\n", Index) + 1; if (NewLineIndex == 0 || NewLineIndex > Index + RowLength) { NewLineIndex = Index + Mathf.Min(text.Length - Index, RowLength); } string Line = text.Substring(Index, NewLineIndex - Index).Replace("\n", ""); Index = NewLineIndex; Lines.Add(Line); TextField.text += Line + "\n"; EscapeIndex++; } while (Index < text.Length && EscapeIndex < 10); if (Lines.Count > MaxLinesUntilCleanup) { // The print history is too long, clear up until there are only // a small amount left (so the user doesn't notice the cleanup) string NewLog = ""; Lines.RemoveRange(0, Lines.Count - LineCountSavedFromCleanup); foreach (string Line in Lines) { NewLog += Line; } TextField.text = NewLog; } } private void Start() { AddCommand("help", "Lists all commands.", (args) => { Println("Commands:"); foreach (string Action in Actions.Keys) { Println("- " + Action); } }); AddCommand("help (command)", "Describes the given command.", (args) => { // Check complete versions of the names (so you can do eg. help "help (command)") foreach (string Action in Actions.Keys) { if (Action.Equals(args[0])) { Println(Actions[Action].Description); return; } } // Check just names foreach (string Action in Actions.Keys) { string[] Parts = Action.Split(' '); if (Parts[0].Equals(args[0])) { Println(Actions[Action].Description); return; } } Println("That command doesn't exist."); }); AddCommand("print (text)", "Prints the given text.", (args) => { Println(args[0]); }); AddCommand("shutdown", "Shuts the game down.", (args) => { Application.Quit(); }); // Set an accurate row length (if the panel is set) if (Panel != null && Panel.GetComponent() != null) { CharacterInfo CharInfo; TextField.font.RequestCharactersInTexture("W", TextField.fontSize, TextField.fontStyle); TextField.font.GetCharacterInfo('W', out CharInfo, TextField.fontSize, TextField.fontStyle); float CharacterWidth = CharInfo.glyphWidth - 1; float PanelWidth = Panel.GetComponent().rect.width; RowLength = (int) (PanelWidth / CharacterWidth); } } private void Update() { // Inputs if (Input.GetButtonDown("Console Toggle")) { Visible = !Visible; } if (Input.GetButtonDown("Enter Command")) { CallCommand(); } if (Input.GetButtonDown("Previous Command")) { if (LastCommandIndex - 1 >= 0) { if (LastCommandIndex == Commands.Count) { // The last command is the last one that was executed // Save the currently written command so it can be returned to LastUnexecutedCommand = InputField.text; } InputField.text = Commands[--LastCommandIndex]; } } if (Input.GetButtonDown("Next Command")) { if (LastCommandIndex + 1 < Commands.Count) { InputField.text = Commands[++LastCommandIndex]; } else if (LastCommandIndex + 1 == Commands.Count) { LastCommandIndex++; InputField.text = LastUnexecutedCommand; } } // Slide up/down animation RectTransform Rect = Panel.GetComponent(); Vector2 OffsetMin = Rect.offsetMin; if (Visible) { if (OffsetMin.y > 1) { OffsetMin.y = Mathf.Lerp(OffsetMin.y, 0, 5f * Time.deltaTime); } if (!InputField.isFocused) { InputField.ActivateInputField(); } } else { if (1000 - OffsetMin.y > 1) { OffsetMin.y = Mathf.Lerp(OffsetMin.y, 1000, 1f * Time.deltaTime); } if (InputField.isFocused) { InputField.DeactivateInputField(); } } Rect.offsetMin = OffsetMin; } } }