diff --git a/TrueCraft.Client/Content/Fonts/Pixel_Bold.fnt b/TrueCraft.Client/Content/Fonts/Pixel_Bold.fnt new file mode 100644 index 0000000..22cd524 --- /dev/null +++ b/TrueCraft.Client/Content/Fonts/Pixel_Bold.fnt @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrueCraft.Client/Content/Fonts/Pixel_Bold_0.png b/TrueCraft.Client/Content/Fonts/Pixel_Bold_0.png new file mode 100644 index 0000000..28d52c9 Binary files /dev/null and b/TrueCraft.Client/Content/Fonts/Pixel_Bold_0.png differ diff --git a/TrueCraft.Client/Content/Fonts/Pixel_Italic.fnt b/TrueCraft.Client/Content/Fonts/Pixel_Italic.fnt new file mode 100644 index 0000000..f2cf770 --- /dev/null +++ b/TrueCraft.Client/Content/Fonts/Pixel_Italic.fnt @@ -0,0 +1,329 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrueCraft.Client/Content/Fonts/Pixel_Italic_0.png b/TrueCraft.Client/Content/Fonts/Pixel_Italic_0.png new file mode 100644 index 0000000..abe0c92 Binary files /dev/null and b/TrueCraft.Client/Content/Fonts/Pixel_Italic_0.png differ diff --git a/TrueCraft.Client/Content/Fonts/Pixel_Italic_1.png b/TrueCraft.Client/Content/Fonts/Pixel_Italic_1.png new file mode 100644 index 0000000..d4fde41 Binary files /dev/null and b/TrueCraft.Client/Content/Fonts/Pixel_Italic_1.png differ diff --git a/TrueCraft.Client/Content/Fonts/Pixel_Regular.fnt b/TrueCraft.Client/Content/Fonts/Pixel_Regular.fnt new file mode 100644 index 0000000..e011a9a --- /dev/null +++ b/TrueCraft.Client/Content/Fonts/Pixel_Regular.fnt @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrueCraft.Client/Content/Fonts/Pixel_Regular_0.png b/TrueCraft.Client/Content/Fonts/Pixel_Regular_0.png new file mode 100644 index 0000000..2ed79fa Binary files /dev/null and b/TrueCraft.Client/Content/Fonts/Pixel_Regular_0.png differ diff --git a/TrueCraft.Client/Content/Fonts/pixel-license.txt b/TrueCraft.Client/Content/Fonts/pixel-license.txt new file mode 100644 index 0000000..50911a7 --- /dev/null +++ b/TrueCraft.Client/Content/Fonts/pixel-license.txt @@ -0,0 +1,4 @@ +The FontStruction “Pixel UniCode” +(http://fontstruct.com/fontstructions/show/908795) by “ivancr72” is licensed +under a Creative Commons Attribution license +(http://creativecommons.org/licenses/by/3.0/). diff --git a/TrueCraft.Client/Interface/ChatInterface.cs b/TrueCraft.Client/Interface/ChatInterface.cs index 14bbc0f..d4460ef 100644 --- a/TrueCraft.Client/Interface/ChatInterface.cs +++ b/TrueCraft.Client/Interface/ChatInterface.cs @@ -1,15 +1,15 @@ using System; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using TrueCraft.Client.Events; +using System.Collections; using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using Microsoft.Xna.Framework.Graphics; using TrueCraft.Client.Rendering; using TrueCraft.Client.Input; -using Microsoft.Xna.Framework.Input; namespace TrueCraft.Client.Interface { - public class ChatInterface : IGameInterface + public class ChatInterface : Control { private static bool TryParseKey(Keys key, bool shift, out char value) { @@ -100,111 +100,37 @@ namespace TrueCraft.Client.Interface } } - public event EventHandler FocusChanged; + public bool HasFocus { get; set; } - private readonly object _syncLock = - new object(); - private bool _hasFocus; - - public bool HasFocus - { - get { return _hasFocus; } - private set - { - if (value != _hasFocus) - { - _hasFocus = value; - - var args = EventArgs.Empty; - if (FocusChanged != null) - FocusChanged(this, args); - } - } - } public MultiplayerClient Client { get; set; } public KeyboardComponent Keyboard { get; set; } public FontRenderer Font { get; set; } + private readonly object Lock = new object(); private string Input { get; set; } private List Messages { get; set; } + private Texture2D DummyTexture { get; set; } public ChatInterface(MultiplayerClient client, KeyboardComponent keyboard, FontRenderer font) { Client = client; Keyboard = keyboard; Font = font; + Input = string.Empty; - HasFocus = false; Messages = new List(); + DummyTexture = new Texture2D(keyboard.Game.GraphicsDevice, 1, 1); + DummyTexture.SetData(new Color[] { Color.White }); - keyboard.KeyUp += HandleKeyDown; - client.ChatMessage += HandleChatMessage; + Client.ChatMessage += OnChatMessage; + Keyboard.KeyDown += OnKeyDown; } - public void AddMessage(string message) + protected override void OnShow() { } + + protected override void OnUpdate(GameTime gameTime) { - lock (_syncLock) - { - Messages.Add(new ChatMessage(message)); - Console.WriteLine(message); - } - } - - private void HandleKeyDown(object sender, KeyboardKeyEventArgs e) - { - if (HasFocus) - { - if (e.Key == Keys.Enter) - { - HasFocus = false; - if (Input.Length != 0) - { - Client.SendMessage(Input); - Input = string.Empty; - } - return; - } - else if (e.Key == Keys.Back) - { - Input = Input.Substring(0, Input.Length - 1); - } - else if (e.Key == Keys.Escape) - { - HasFocus = false; - Input = string.Empty; - return; - } - - var shift = (Keyboard.State.IsKeyDown(Keys.LeftShift) || Keyboard.State.IsKeyDown(Keys.RightShift)); - var value = default(char); - - if (TryParseKey(e.Key, shift, out value)) - Input += new string(new char[] { value }); - } - else - { - switch (e.Key) - { - case Keys.T: - HasFocus = true; - break; - - case Keys.OemQuestion: - HasFocus = true; - Input += "/"; - break; - } - } - } - - void HandleChatMessage(object sender, ChatMessageEventArgs e) - { - AddMessage(e.Message); - } - - public void Update(GameTime gameTime) - { - lock (_syncLock) + lock (Lock) { for (int i = 0; i < Messages.Count; i++) { @@ -218,25 +144,78 @@ namespace TrueCraft.Client.Interface } } - public void DrawSprites(GameTime gameTime, SpriteBatch spriteBatch) + protected override void OnDrawSprites(GameTime gameTime, SpriteBatch spriteBatch) { - lock (_syncLock) + // UI scaling + var scale = GetScaleFactor(); + var xOrigin = (int)(10 * scale); + var yOffset = (int)(25 * scale); + var yOrigin = (int)(5 * scale) + (spriteBatch.GraphicsDevice.Viewport.Height - (yOffset * 7)); + + var color = Color.Lerp(Color.Transparent, Color.Black, 0.6f); + + lock (Lock) { + if ((Messages.Count == 0) && !HasFocus) return; + spriteBatch.Draw(DummyTexture, new Rectangle(xOrigin - 2, yOrigin - 2, (int)(600 * scale) + 4, (yOffset * 5) + 4), color); + + var total = 5; for (int i = (Messages.Count - 1); i >= 0; i--) { - if (Messages.Count == 0) - break; - - var invi = (Messages.Count - i); - var message = Messages[i]; - Font.DrawText(spriteBatch, 0, i * 30, message.Message); - if (invi >= 5) break; + total--; + + Font.DrawText(spriteBatch, xOrigin , yOrigin + (yOffset * total), message.Message, scale); + if (total == 0) break; + } + + if (HasFocus) + { + spriteBatch.Draw(DummyTexture, new Rectangle(xOrigin - 2, yOrigin + (yOffset * 5) + xOrigin - 2, (int)(600 * scale) + 4, yOffset + 4), color); + Font.DrawText(spriteBatch, xOrigin, yOrigin + (yOffset * 5) + xOrigin, "> " + Input, scale); } } + } + private void OnChatMessage(object sender, Events.ChatMessageEventArgs e) + { + AddMessage(e.Message); + } + + private void OnKeyDown(object sender, KeyboardKeyEventArgs e) + { if (HasFocus) - Font.DrawText(spriteBatch, 0, (6 * 30) + 15, "> " + Input); + { + if (e.Key == Keys.Back) + Input = Input.Substring(0, Input.Length - 1); + else + { + var shift = (Keyboard.State.IsKeyDown(Keys.LeftShift) || Keyboard.State.IsKeyDown(Keys.RightShift)); + var value = default(char); + + if (TryParseKey(e.Key, shift, out value)) + Input += new string(new char[] { value }); + } + } + else + { + if (Input != string.Empty) + { + Client.SendMessage(Input); + Input = string.Empty; + } + } + } + + protected override void OnHide() { } + + public void AddMessage(string message) + { + lock (Lock) + { + Messages.Add(new ChatMessage(message)); + Console.WriteLine(message); + } } } } \ No newline at end of file diff --git a/TrueCraft.Client/Interface/Control.cs b/TrueCraft.Client/Interface/Control.cs new file mode 100644 index 0000000..af3199b --- /dev/null +++ b/TrueCraft.Client/Interface/Control.cs @@ -0,0 +1,115 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace TrueCraft.Client.Interface +{ + /// + /// Abstract base class for uniformly-implemented game interfaces. + /// + public abstract class Control + : IGameInterface + { + private bool _isVisible; + + /// + /// Gets or sets whether the control is visible. + /// + public bool IsVisible + { + get { return _isVisible; } + set + { + if (_isVisible == value) + return; + + _isVisible = value; + if (_isVisible) OnShow(); + else OnHide(); + } + } + + /// + /// Gets or sets the scale for the control. + /// + public InterfaceScale Scale { get; set; } + + /// + /// Creates a new control. + /// + protected Control() { Scale = InterfaceScale.Medium; } + + /// + /// Shows the control. + /// + public void Show() { IsVisible = true; } + + /// + /// Hides the control. + /// + public void Hide() { IsVisible = false; } + + /// + /// Called when the control's visibility is set to true. + /// + protected abstract void OnShow(); + + /// + /// Called when the control is updated. + /// + /// + protected abstract void OnUpdate(GameTime gameTime); + + /// + /// Called when the control is drawn. + /// + /// + /// + protected abstract void OnDrawSprites(GameTime gameTime, SpriteBatch spriteBatch); + + /// + /// Called when the control's visibility is set to false. + /// + protected abstract void OnHide(); + + /// + /// Updates the control. + /// + /// + public void Update(GameTime gameTime) + { + OnUpdate(gameTime); + } + + /// + /// Draws the control. + /// + /// + /// + public virtual void DrawSprites(GameTime gameTime, SpriteBatch spriteBatch) + { + if (IsVisible) + OnDrawSprites(gameTime, spriteBatch); + } + + /// + /// Returns the preferred scale factor for the control. + /// + /// + protected float GetScaleFactor() + { + switch (Scale) + { + case InterfaceScale.Small: + return 0.75f; + + default: + case InterfaceScale.Medium: + return 1.0f; + + case InterfaceScale.Large: + return 1.25f; + } + } + } +} diff --git a/TrueCraft.Client/Interface/DebugInterface.cs b/TrueCraft.Client/Interface/DebugInterface.cs new file mode 100644 index 0000000..1255a2b --- /dev/null +++ b/TrueCraft.Client/Interface/DebugInterface.cs @@ -0,0 +1,56 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using TrueCraft.Client.Rendering; +using TrueCraft.Client.Input; + +namespace TrueCraft.Client.Interface +{ + public class DebugInterface : Control + { + public MultiplayerClient Client { get; set; } + public FontRenderer Font { get; set; } + + public int Vertices { private get; set; } + public int Chunks { private get; set; } + + public DebugInterface(MultiplayerClient client, FontRenderer font) + { + Client = client; + Font = font; + } + + protected override void OnShow() { } + + protected override void OnUpdate(GameTime gameTime) { } + + protected override void OnDrawSprites(GameTime gameTime, SpriteBatch spriteBatch) + { + // UI scaling + var scale = GetScaleFactor(); + var xOrigin = (int)(10 * scale); + var yOrigin = (int)(5 * scale); + var yOffset = (int)(25 * scale); + + var fps = (int)(1 / gameTime.ElapsedGameTime.TotalSeconds) + 1; + var position = Client.Position; + + Font.DrawText(spriteBatch, xOrigin, yOrigin, string.Format("§lRunning at {0} FPS", GetFPSColor(fps) + fps.ToString()), scale); + Font.DrawText(spriteBatch, xOrigin, yOrigin + (yOffset * 1), string.Format("§o{0} vertices", Vertices), scale); + Font.DrawText(spriteBatch, xOrigin, yOrigin + (yOffset * 2), string.Format("§o{0} chunks", Chunks), scale); + Font.DrawText(spriteBatch, xOrigin, yOrigin + (yOffset * 3), string.Format("§o<{0:N2}, {1:N2}, {2:N2}>", Client.Position.X, Client.Position.Y, Client.Position.Z), scale); + } + + protected override void OnHide() { } + + private string GetFPSColor(int fps) + { + if (fps <= 16) + return "§c"; + else if (fps <= 32) + return "§e"; + else + return "§a"; + } + } +} diff --git a/TrueCraft.Client/Interface/IGameInterface.cs b/TrueCraft.Client/Interface/IGameInterface.cs index 8f32498..27fb8a5 100644 --- a/TrueCraft.Client/Interface/IGameInterface.cs +++ b/TrueCraft.Client/Interface/IGameInterface.cs @@ -6,6 +6,8 @@ namespace TrueCraft.Client.Interface { public interface IGameInterface { + InterfaceScale Scale { get; set; } + void Update(GameTime gameTime); void DrawSprites(GameTime gameTime, SpriteBatch spriteBatch); } diff --git a/TrueCraft.Client/Interface/InterfaceScale.cs b/TrueCraft.Client/Interface/InterfaceScale.cs new file mode 100644 index 0000000..d33992c --- /dev/null +++ b/TrueCraft.Client/Interface/InterfaceScale.cs @@ -0,0 +1,13 @@ +using System; + +namespace TrueCraft.Client.Interface +{ + public enum InterfaceScale + { + Small = 0, + + Medium = 1, + + Large = 2 + } +} diff --git a/TrueCraft.Client/TrueCraft.Client.csproj b/TrueCraft.Client/TrueCraft.Client.csproj index 6cafb2f..aab8588 100644 --- a/TrueCraft.Client/TrueCraft.Client.csproj +++ b/TrueCraft.Client/TrueCraft.Client.csproj @@ -65,6 +65,9 @@ + + + @@ -142,6 +145,21 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest @@ -154,6 +172,15 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index 3d8b684..e16b68e 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -23,7 +23,7 @@ namespace TrueCraft.Client private MultiplayerClient Client { get; set; } private GraphicsDeviceManager Graphics { get; set; } private List Interfaces { get; set; } - private FontRenderer DejaVu { get; set; } + private FontRenderer Pixel { get; set; } private SpriteBatch SpriteBatch { get; set; } private IPEndPoint EndPoint { get; set; } private ChunkRenderer ChunkConverter { get; set; } @@ -32,6 +32,7 @@ namespace TrueCraft.Client public ConcurrentBag PendingMainThreadActions { get; set; } private ConcurrentBag IncomingChunks { get; set; } public ChatInterface ChatInterface { get; set; } + public DebugInterface DebugInterface { get; set; } private RenderTarget2D RenderTarget; private BoundingFrustum CameraView; private Camera Camera; @@ -129,14 +130,17 @@ namespace TrueCraft.Client if (UserSettings.Local.SelectedTexturePack != TexturePack.Default.Name) TextureMapper.AddTexturePack(TexturePack.FromArchive(Path.Combine(TexturePack.TexturePackPath, UserSettings.Local.SelectedTexturePack))); - DejaVu = new FontRenderer( - new Font(Content, "Fonts/DejaVu", FontStyle.Regular), - new Font(Content, "Fonts/DejaVu", FontStyle.Bold), + Pixel = new FontRenderer( + new Font(Content, "Fonts/Pixel", FontStyle.Regular), + new Font(Content, "Fonts/Pixel", FontStyle.Bold), null, // No support for underlined or strikethrough yet. The FontRenderer will revert to using the regular font style. null, // (I don't think BMFont has those options?) - new Font(Content, "Fonts/DejaVu", FontStyle.Italic)); - Interfaces.Add(ChatInterface = new ChatInterface(Client, KeyboardComponent, DejaVu)); - ChatInterface.FocusChanged += ChatInterface_FocusChanged; + new Font(Content, "Fonts/Pixel", FontStyle.Italic)); + Interfaces.Add(ChatInterface = new ChatInterface(Client, KeyboardComponent, Pixel)); + Interfaces.Add(DebugInterface = new DebugInterface(Client, Pixel)); + + ChatInterface.IsVisible = true; + DebugInterface.IsVisible = true; OpaqueEffect = new BasicEffect(GraphicsDevice); OpaqueEffect.EnableDefaultLighting(); @@ -160,23 +164,6 @@ namespace TrueCraft.Client base.LoadContent(); } - private bool MouseCapturedBackup; - - void ChatInterface_FocusChanged(object sender, EventArgs e) - { - //if (ChatInterface.HasFocus) - //{ - // MouseCapturedBackup == MouseCaptured; - // MouseCaptured = false; - //} - - //MouseCapturedBackup = MouseCaptured; - - //if (MouseCaptured && ChatInterface.HasFocus) - // MouseCaptured = false; - //else - } - protected override void OnExiting(object sender, EventArgs args) { ChunkConverter.Stop(); @@ -188,14 +175,32 @@ namespace TrueCraft.Client // TODO: Rebindable keys // TODO: Horizontal terrain collisions - if (ChatInterface.HasFocus) - return; - switch (e.Key) { - // Quit the game. + // Close game (or chat). case Keys.Escape: - Exit(); + if (ChatInterface.HasFocus) + ChatInterface.HasFocus = false; + else + Exit(); + break; + + // Open chat window. + case Keys.T: + if (!ChatInterface.HasFocus) + ChatInterface.HasFocus = true; + break; + + // Open chat window. + case Keys.OemQuestion: + if (!ChatInterface.HasFocus) + ChatInterface.HasFocus = true; + break; + + // Close chat window. + case Keys.Enter: + if (ChatInterface.HasFocus) + ChatInterface.HasFocus = false; break; // Take a screenshot. @@ -203,27 +208,45 @@ namespace TrueCraft.Client TakeScreenshot(); break; + // Toggle debug view. + case Keys.F3: + DebugInterface.IsVisible = !DebugInterface.IsVisible; + break; + + // Change interface scale. + case Keys.F4: + foreach (var item in Interfaces) + { + item.Scale = (InterfaceScale)(item.Scale + 1); + if ((int)item.Scale > 2) item.Scale = InterfaceScale.Small; + } + break; + // Move to the left. case Keys.A: case Keys.Left: + if (ChatInterface.HasFocus) break; Delta += Microsoft.Xna.Framework.Vector3.Left; break; // Move to the right. case Keys.D: case Keys.Right: + if (ChatInterface.HasFocus) break; Delta += Microsoft.Xna.Framework.Vector3.Right; break; // Move forwards. case Keys.W: case Keys.Up: + if (ChatInterface.HasFocus) break; Delta += Microsoft.Xna.Framework.Vector3.Forward; break; // Move backwards. case Keys.S: case Keys.Down: + if (ChatInterface.HasFocus) break; Delta += Microsoft.Xna.Framework.Vector3.Backward; break; @@ -237,32 +260,33 @@ namespace TrueCraft.Client private void OnKeyboardKeyUp(object sender, KeyboardKeyEventArgs e) { - if (ChatInterface.HasFocus) - return; - switch (e.Key) { // Stop moving to the left. case Keys.A: case Keys.Left: + if (ChatInterface.HasFocus) break; Delta -= Microsoft.Xna.Framework.Vector3.Left; break; // Stop moving to the right. case Keys.D: case Keys.Right: + if (ChatInterface.HasFocus) break; Delta -= Microsoft.Xna.Framework.Vector3.Right; break; // Stop moving forwards. case Keys.W: case Keys.Up: + if (ChatInterface.HasFocus) break; Delta -= Microsoft.Xna.Framework.Vector3.Forward; break; // Stop moving backwards. case Keys.S: case Keys.Down: + if (ChatInterface.HasFocus) break; Delta -= Microsoft.Xna.Framework.Vector3.Backward; break; } @@ -412,15 +436,12 @@ namespace TrueCraft.Client } } + DebugInterface.Vertices = verticies; + DebugInterface.Chunks = chunks; + SpriteBatch.Begin(); for (int i = 0; i < Interfaces.Count; i++) - { Interfaces[i].DrawSprites(gameTime, SpriteBatch); - } - - int fps = (int)(1 / gameTime.ElapsedGameTime.TotalSeconds); - DejaVu.DrawText(SpriteBatch, 0, GraphicsDevice.Viewport.Height - 30, - string.Format("{0} FPS, {1} verticies, {2} chunks, {3}", fps + 1, verticies, chunks, Client.Position)); SpriteBatch.End(); GraphicsDevice.SetRenderTarget(null);