/* * Console.cs * Authors: August Zinsser * * Copyright Matthew Belmonte 2007 */ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; namespace tAC_Engine { /// /// A simple text console that can be drawn over the game. Allows the user to input lua commands at runtime. /// Only enabled if the game is in Debug mode. /// public static class Console { #if DEBUG /// /// Used by the history to tell if each item was a command entered into the console, or just text /// that was printed to the console /// private struct HistoryItem { public enum Code { Command, Output, Error }; public HistoryItem(string text, Code codeType) { Text = text; CodeType = codeType; } public string Text; public Code CodeType; } static SpriteBatch mSpriteBatch; // Internal drawing buffer static int mLeft = 5; // Graphical layout (in pixels) static int mBottom = 15; // '' static int mSpacing = 20; // '' static float mScreenPercentage = .333f; // Amount of the screen that the console takes up static bool mVisible = false; // Show or hide the entire console static bool mHoldingDownVisibleKey = false; // True when the user holds down the toggle visibility key static float mDeleteTimer = 0f; // Only register the delete funcitonality if the timer is 0 static float mDeleteHoldDelay = 1f; // Time that must pass before holding the delete key works static string mCursor = "> "; // Shows the user where he/she is typing static string mCommandString = ""; // The text being entered by the user static List mHistory; // The previous commands static int mHistoryCount = 25; // The size of the list to remember static int mHistoryIndex = 0; // When pressing the up arrow, which history is recalled static bool mWasInputLocked; // True if the input was locked before the console was brought up #endif /// /// Allocate variables /// static Console() { #if DEBUG mHistory = new List(); mSpriteBatch = new SpriteBatch(GenericBaseApplication.GameManager.GraphicsDevice); #endif } /// /// Prints the given text in to the console (Console must be visible to read it, so only has /// an effect in Debug mode). /// public static void Print(string text) { #if DEBUG mHistory.Insert(0, new HistoryItem(text, HistoryItem.Code.Output)); #endif } /// /// Does nothing if not in Debug mode /// public static void Update() { #if DEBUG // Remember original input lock state if (!mVisible) { mWasInputLocked = InputState.Locked; } // Unlock the inputstate for this update to allow typing InputState.Locked = false; // Check visibility settings if (InputState.IsKeyDown(Keys.OemTilde) && !mHoldingDownVisibleKey) { mVisible = !mVisible; mHoldingDownVisibleKey = true; } else if (InputState.IsKeyUp(Keys.OemTilde)) { mHoldingDownVisibleKey = false; } if (mVisible) { // Update the command line mCommandString += InputState.GetTextInput(); // Check for delete/backspace // Since XNA can't detect the Backspace key (hopefully it will in a future release), // use the left arrow or delete to delete characters if (InputState.IsKeyDown(Keys.Left) || InputState.IsKeyDown(Keys.Delete) || InputState.IsKeyDown(Keys.Back)) { if (mDeleteTimer < 0 || mDeleteTimer == mDeleteHoldDelay) { mCommandString = mCommandString.Substring(0, Math.Max(mCommandString.Length - 1, 0)); } mDeleteTimer -= GenericBaseApplication.GameManager.ElapsedSeconds; } else mDeleteTimer = mDeleteHoldDelay; // Check for an up or down arrow and iterate through the history accordingly if (mHistory.Count > 0) { if (InputState.IsKeyDown(Keys.Up) && InputState.WasKeyUp(Keys.Up)) { // Find the next command history item int oldIndex = mHistoryIndex; mHistoryIndex++; while (mHistoryIndex < mHistory.Count && mHistory[mHistoryIndex].CodeType != HistoryItem.Code.Command) { mHistoryIndex++; } if (mHistoryIndex >= mHistory.Count) mHistoryIndex = oldIndex; if (mHistory[mHistoryIndex].CodeType == HistoryItem.Code.Command) mCommandString = mHistory[mHistoryIndex].Text; } else if (InputState.IsKeyDown(Keys.Down) && InputState.WasKeyUp(Keys.Down)) { // Find the next command history item int oldIndex = mHistoryIndex; mHistoryIndex--; while (mHistoryIndex > 0 && mHistory[mHistoryIndex].CodeType != HistoryItem.Code.Command) { mHistoryIndex--; } if (mHistoryIndex < 0) mHistoryIndex = oldIndex; if (mHistoryIndex >= 0 && mHistory[mHistoryIndex].CodeType == HistoryItem.Code.Command) mCommandString = mHistory[mHistoryIndex].Text; } } // Check for a commit (Enter key) if (InputState.IsKeyDown(Keys.Enter) && mCommandString != "") { // Send the command the Lua Virtual Machine try { GenericBaseApplication.GameManager.LuaGameManagerVM.DoString(mCommandString); } catch (Exception e) { mHistory.Insert(0, new HistoryItem(e.Message, HistoryItem.Code.Error)); } finally { } // Update console commands mHistory.Insert(0, new HistoryItem(mCommandString, HistoryItem.Code.Command)); mCommandString = ""; mHistoryIndex = -1; } // Update history lines while (mHistory.Count > mHistoryCount) { mHistory.RemoveAt(mHistory.Count - 1); } // Lock inputstate so things under the console don't get keyboard input InputState.Locked = true; } else { InputState.Locked = mWasInputLocked; } #endif } /// /// Draws a semi-transparent box with a luac command line and history lines on top of everything else. /// Only draws things in Debug mode. /// public static void Draw() { #if DEBUG // Check visibility if (mVisible) { mSpriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None); // Draw the box int lineY = (int)(GenericBaseApplication.GameManager.ScreenHeight * mScreenPercentage); Texture2D lineTex = TextureManager.Load(@"Content\General\White"); mSpriteBatch.Draw( lineTex, new Rectangle(0, 0, GenericBaseApplication.GameManager.ScreenWidth, (int)(GenericBaseApplication.GameManager.ScreenHeight * mScreenPercentage)), null, new Color(75, 75, 155, 180), 0f, Vector2.Zero, SpriteEffects.None, 0f); mSpriteBatch.Draw( lineTex, new Rectangle(0, (int)(lineY - 2), GenericBaseApplication.GameManager.ScreenWidth, 4), null, new Color(200, 200, 200, 200), 0f, Vector2.Zero, SpriteEffects.None, 0f); mSpriteBatch.End(); // Draw the command line Rectangle cursorSize = GenericBaseApplication.GameManager.StandardFont.MeasureString(mCursor); GenericBaseApplication.GameManager.StandardFont.DrawString( new Vector2(mLeft, lineY - mBottom), mCursor, new Color(255, 150, 0, 255)); GenericBaseApplication.GameManager.StandardFont.DrawString( new Vector2(mLeft + (int)cursorSize.Width, lineY - mBottom), mCommandString, new Color(255, 255, 175, 255)); // Draw the history lines Color color; for (int i = 0; i < mHistory.Count; i++) { int y = lineY - (mSpacing) * (i + 2); if (mHistoryIndex == i) GenericBaseApplication.GameManager.StandardFont.DrawString( new Vector2(mLeft, y), mCursor, new Color(200, 150, 0, 200)); if (mHistory[i].CodeType == HistoryItem.Code.Command) color = new Color(200, 200, 150, 255); else if (mHistory[i].CodeType == HistoryItem.Code.Output) color = new Color(155, 255, 255, 255); else color = new Color(255, 0, 0, 255); GenericBaseApplication.GameManager.StandardFont.DrawString( new Vector2(mLeft + (int)cursorSize.Width, y), mHistory[i].Text, color); } } #endif } } }