diff --git a/MLEM/Formatting/Codes/AnimatedCode.cs b/MLEM/Formatting/Codes/AnimatedCode.cs new file mode 100644 index 0000000..9e5eba4 --- /dev/null +++ b/MLEM/Formatting/Codes/AnimatedCode.cs @@ -0,0 +1,14 @@ +using System.Text.RegularExpressions; + +namespace MLEM.Formatting.Codes { + public class AnimatedCode : Code { + + public AnimatedCode(Match match) : base(match) { + } + + public override bool EndsHere(Code other) { + return other is AnimatedCode; + } + + } +} \ No newline at end of file diff --git a/MLEM/Formatting/Codes/Code.cs b/MLEM/Formatting/Codes/Code.cs index e6fe7cc..549666a 100644 --- a/MLEM/Formatting/Codes/Code.cs +++ b/MLEM/Formatting/Codes/Code.cs @@ -2,9 +2,10 @@ using System.Text.RegularExpressions; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Font; +using MLEM.Misc; namespace MLEM.Formatting.Codes { - public class Code { + public class Code : GenericDataHolder { public readonly Match Match; public Token Token { get; internal set; } @@ -25,7 +26,10 @@ namespace MLEM.Formatting.Codes { return null; } - public virtual bool DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, Vector2 pos, GenericFont font, Color color, float scale, float depth) { + public virtual void Update(GameTime time) { + } + + public virtual bool DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) { return false; } diff --git a/MLEM/Formatting/Codes/FontCode.cs b/MLEM/Formatting/Codes/FontCode.cs index 69bce99..3228763 100644 --- a/MLEM/Formatting/Codes/FontCode.cs +++ b/MLEM/Formatting/Codes/FontCode.cs @@ -14,5 +14,9 @@ namespace MLEM.Formatting.Codes { return this.font; } + public override bool EndsHere(Code other) { + return other is FontCode; + } + } } \ No newline at end of file diff --git a/MLEM/Formatting/Codes/ShadowCode.cs b/MLEM/Formatting/Codes/ShadowCode.cs new file mode 100644 index 0000000..e8eee17 --- /dev/null +++ b/MLEM/Formatting/Codes/ShadowCode.cs @@ -0,0 +1,25 @@ +using System.Text.RegularExpressions; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; +using MLEM.Font; + +namespace MLEM.Formatting.Codes { + public class ShadowCode : FontCode { + + private readonly Color color; + private readonly Vector2 offset; + + public ShadowCode(Match match, Color color, Vector2 offset) : base(match, null) { + this.color = color; + this.offset = offset; + } + + public override bool DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) { + font.DrawString(batch, cString, pos + this.offset * scale, this.color.CopyAlpha(color), 0, Vector2.Zero, scale, SpriteEffects.None, depth); + // we return false since we still want regular drawing to occur + return false; + } + + } +} \ No newline at end of file diff --git a/MLEM/Formatting/Codes/WobblyCode.cs b/MLEM/Formatting/Codes/WobblyCode.cs new file mode 100644 index 0000000..c20c64d --- /dev/null +++ b/MLEM/Formatting/Codes/WobblyCode.cs @@ -0,0 +1,31 @@ +using System; +using System.Text.RegularExpressions; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MLEM.Font; + +namespace MLEM.Formatting.Codes { + public class WobblyCode : AnimatedCode { + + private readonly float modifier; + private readonly float heightModifier; + public TimeSpan TimeIntoAnimation; + + public WobblyCode(Match match, float modifier, float heightModifier) : base(match) { + this.modifier = modifier; + this.heightModifier = heightModifier; + } + + public override void Update(GameTime time) { + this.TimeIntoAnimation += time.ElapsedGameTime; + } + + public override bool DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, int indexInToken, ref Vector2 pos, GenericFont font, ref Color color, ref float scale, float depth) { + var offset = new Vector2(0, (float) Math.Sin(this.Token.Index + indexInToken + this.TimeIntoAnimation.TotalSeconds * this.modifier) * font.LineHeight * this.heightModifier * scale); + pos += offset; + // we return false since we still want regular drawing to occur, we just changed the position + return false; + } + + } +} \ No newline at end of file diff --git a/MLEM/Formatting/TextFormatter.cs b/MLEM/Formatting/TextFormatter.cs index 45a00c6..e466703 100644 --- a/MLEM/Formatting/TextFormatter.cs +++ b/MLEM/Formatting/TextFormatter.cs @@ -7,9 +7,10 @@ using Microsoft.Xna.Framework; using MLEM.Extensions; using MLEM.Font; using MLEM.Formatting.Codes; +using MLEM.Misc; namespace MLEM.Formatting { - public class TextFormatter { + public class TextFormatter : GenericDataHolder { public readonly Dictionary Codes = new Dictionary(); @@ -17,7 +18,8 @@ namespace MLEM.Formatting { // font codes this.Codes.Add(new Regex(""), (f, m) => new FontCode(m, boldFont?.Invoke())); this.Codes.Add(new Regex(""), (f, m) => new FontCode(m, italicFont?.Invoke())); - this.Codes.Add(new Regex(""), (f, m) => new FontCode(m, null)); + this.Codes.Add(new Regex(@""), (f, m) => new ShadowCode(m, m.Groups[1].Success ? ColorExtensions.FromHex(m.Groups[1].Value) : Color.Black, new Vector2(float.TryParse(m.Groups[2].Value, out var offset) ? offset : 2))); + this.Codes.Add(new Regex(""), (f, m) => new FontCode(m, null)); // color codes foreach (var c in typeof(Color).GetProperties()) { @@ -28,6 +30,10 @@ namespace MLEM.Formatting { } this.Codes.Add(new Regex(@""), (f, m) => new ColorCode(m, ColorExtensions.FromHex(m.Groups[1].Value))); this.Codes.Add(new Regex(""), (f, m) => new ColorCode(m, null)); + + // animation codes + this.Codes.Add(new Regex(@""), (f, m) => new WobblyCode(m, float.TryParse(m.Groups[1].Value, out var mod) ? mod : 5, float.TryParse(m.Groups[2].Value, out var heightMod) ? heightMod : 1 / 8F)); + this.Codes.Add(new Regex(""), (f, m) => new AnimatedCode(m)); } public TokenizedString Tokenize(string s) { diff --git a/MLEM/Formatting/Token.cs b/MLEM/Formatting/Token.cs index bf3f948..78d4a1b 100644 --- a/MLEM/Formatting/Token.cs +++ b/MLEM/Formatting/Token.cs @@ -4,9 +4,10 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Font; using MLEM.Formatting.Codes; +using MLEM.Misc; namespace MLEM.Formatting { - public class Token { + public class Token : GenericDataHolder { public readonly Code[] AppliedCodes; public readonly int Index; @@ -33,9 +34,9 @@ namespace MLEM.Formatting { return this.AppliedCodes.Select(c => c.GetFont()).FirstOrDefault(); } - public void DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, Vector2 pos, GenericFont font, Color color, float scale, float depth) { + public void DrawCharacter(GameTime time, SpriteBatch batch, char c, string cString, int indexInToken, Vector2 pos, GenericFont font, Color color, float scale, float depth) { foreach (var code in this.AppliedCodes) { - if (code.DrawCharacter(time, batch, c, cString, pos, font, color, scale, depth)) + if (code.DrawCharacter(time, batch, c, cString, indexInToken, ref pos, font, ref color, ref scale, depth)) return; } diff --git a/MLEM/Formatting/TokenizedString.cs b/MLEM/Formatting/TokenizedString.cs index df3e590..89a28f7 100644 --- a/MLEM/Formatting/TokenizedString.cs +++ b/MLEM/Formatting/TokenizedString.cs @@ -4,18 +4,23 @@ using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Font; +using MLEM.Formatting.Codes; +using MLEM.Misc; namespace MLEM.Formatting { - public struct TokenizedString { + public class TokenizedString : GenericDataHolder { public readonly string RawString; public readonly string String; public readonly Token[] Tokens; + public readonly Code[] AllCodes; public TokenizedString(string rawString, string strg, Token[] tokens) { this.RawString = rawString; this.String = strg; this.Tokens = tokens; + // since a code can be present in multiple tokens, we use Distinct here + this.AllCodes = tokens.SelectMany(t => t.AppliedCodes).Distinct().ToArray(); } public void Split(GenericFont font, float width, float scale) { @@ -42,12 +47,18 @@ namespace MLEM.Formatting { } } + public void Update(GameTime time) { + foreach (var code in this.AllCodes) + code.Update(time); + } + public void Draw(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) { var innerOffset = new Vector2(); foreach (var token in this.Tokens) { var drawFont = token.GetFont() ?? font; var drawColor = token.GetColor() ?? color; - foreach (var c in token.Substring) { + for (var i = 0; i < token.Substring.Length; i++) { + var c = token.Substring[i]; if (c == '\n') { innerOffset.X = 0; innerOffset.Y += font.LineHeight * scale; @@ -55,7 +66,7 @@ namespace MLEM.Formatting { } var cString = c.ToString(); - token.DrawCharacter(time, batch, c, cString, pos + innerOffset, drawFont, drawColor, scale, depth); + token.DrawCharacter(time, batch, c, cString, i, pos + innerOffset, drawFont, drawColor, scale, depth); innerOffset.X += font.MeasureString(cString).X * scale; } } diff --git a/Sandbox/GameImpl.cs b/Sandbox/GameImpl.cs index bc47c87..3274fc3 100644 --- a/Sandbox/GameImpl.cs +++ b/Sandbox/GameImpl.cs @@ -113,15 +113,16 @@ namespace Sandbox { };*/ var formatter = new TextFormatter(); - var strg = "This is a formatted string with two bits of formatting! It also includesaverylongwordthatisformattedaswell."; + var strg = "This is a formatted string with two bits of formatting! It also includesaverylongwordthatisformattedaswell. Additionally, it wobbles and has a shadow or a weird shadow"; this.tokenized = formatter.Tokenize(strg); this.tokenized.Split(font, 400, 1); this.OnDraw += (g, time) => { this.SpriteBatch.Begin(); - this.tokenized.Draw(time, this.SpriteBatch, new Vector2(100, 20), font, Color.White, 1, 0); + this.tokenized.Draw(time, this.SpriteBatch, new Vector2(400, 20), font, Color.White, 1, 0); this.SpriteBatch.End(); }; + this.OnUpdate += (g, time) => this.tokenized.Update(time); } protected override void DoUpdate(GameTime gameTime) {