From d8daa3779afae6198355a9d8c2193abbe316931a Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Wed, 20 May 2020 23:59:40 +0200 Subject: [PATCH] start on xml documentation, yay --- MLEM/Animations/SpriteAnimation.cs | 85 +++++++++++++++++++++++- MLEM/Animations/SpriteAnimationGroup.cs | 36 +++++++++- MLEM/Cameras/Camera.cs | 74 ++++++++++++++++++++- MLEM/Content/RawContentManager.cs | 18 +++++ MLEM/Content/RawContentReader.cs | 33 +++++++++ MLEM/Extensions/ColorExtensions.cs | 23 +++++++ MLEM/Extensions/GraphicsExtensions.cs | 22 ++++++ MLEM/Extensions/NumberExtensions.cs | 51 +++++++++++++- MLEM/Extensions/RandomExtensions.cs | 18 +++++ MLEM/Extensions/SpriteBatchExtensions.cs | 16 +++++ MLEM/Font/GenericFont.cs | 53 +++++++++++++++ MLEM/Font/GenericSpriteFont.cs | 19 ++++++ 12 files changed, 439 insertions(+), 9 deletions(-) diff --git a/MLEM/Animations/SpriteAnimation.cs b/MLEM/Animations/SpriteAnimation.cs index 405ea80..7fd8b59 100644 --- a/MLEM/Animations/SpriteAnimation.cs +++ b/MLEM/Animations/SpriteAnimation.cs @@ -5,10 +5,21 @@ using MLEM.Misc; using MLEM.Textures; namespace MLEM.Animations { + /// + /// A sprite animation that allows for any number of frames that each last any number of seconds + /// public class SpriteAnimation : GenericDataHolder { private AnimationFrame[] frames; + /// + /// Returns the at the given index. + /// Index ordering is based on the order that animation frames were added in. + /// + /// The index in the list of animation frames public AnimationFrame this[int index] => this.frames[index]; + /// + /// The frame that the animation is currently on. + /// public AnimationFrame CurrentFrame { get { // we might have overshot the end time by a little bit, so just return the last frame @@ -24,35 +35,86 @@ namespace MLEM.Animations { return this.frames[0]; } } + /// + /// The texture region that the animation's has + /// public TextureRegion CurrentRegion => this.CurrentFrame.Region; + /// + /// The total amount of time that this animation has. + /// This is auatomatically calculated based on the frame time of each frame. + /// public readonly double TotalTime; + /// + /// The amount of seconds that the animation has been going on for. + /// If is reached, this value resets to 0. + /// public double TimeIntoAnimation { get; private set; } + /// + /// The finished state of this animation. + /// This is only true for longer than a frame if is false. + /// public bool IsFinished { get; private set; } + /// + /// The name of this animation. This is useful if used in combination with . + /// public string Name; + /// + /// The speed multiplier that this animation should run with. + /// Numbers higher than 1 will increase the speed. + /// public float SpeedMultiplier = 1; - + /// + /// Set to false to stop this animation from looping. + /// To check if the animation has finished playing, see . + /// public bool IsLooping = true; - public Completed OnCompleted; + /// + /// A callback that gets fired when the animation completes. + /// + public event Completed OnCompleted; + /// + /// Set this to true to pause the playback of the animation. + /// will not continue and the will not change. + /// public bool IsPaused; + /// + /// Creates a new sprite animation that contains the given frames. + /// + /// The frames this animation should have public SpriteAnimation(params AnimationFrame[] frames) { this.frames = frames; foreach (var frame in frames) this.TotalTime += frame.Seconds; } + /// + /// Creates a new sprite animation that contains the given texture regions as frames. + /// + /// The amount of time that each frame should last for + /// The texture regions that should make up this animation public SpriteAnimation(double timePerFrame, params TextureRegion[] regions) : this(Array.ConvertAll(regions, region => new AnimationFrame(region, timePerFrame))) { } + /// + /// Creates a new sprite animation based on the given texture regions in rectangle-based format. + /// + /// The amount of time that each frame should last for + /// The texture that the regions should come from + /// The texture regions that should make up this animation public SpriteAnimation(double timePerFrame, Texture2D texture, params Rectangle[] regions) : this(timePerFrame, Array.ConvertAll(regions, region => new TextureRegion(texture, region))) { } + /// + /// Updates this animation, causing to be increased and the to be updated. + /// + /// The game's time public void Update(GameTime time) { this.SetTime(this.TimeIntoAnimation + time.ElapsedGameTime.TotalSeconds * this.SpeedMultiplier); } - + internal void SetTime(double totalTime) { if (this.IsFinished || this.IsPaused) return; @@ -67,6 +129,9 @@ namespace MLEM.Animations { } } + /// + /// Restarts this animation from the first frame. + /// public void Restart() { this.TimeIntoAnimation = 0; this.IsFinished = false; @@ -77,11 +142,25 @@ namespace MLEM.Animations { } + /// + /// Represents a single frame of a + /// public class AnimationFrame { + /// + /// The texture region that this frame should render + /// public readonly TextureRegion Region; + /// + /// The total amount of seconds that this frame should last for + /// public readonly double Seconds; + /// + /// Creates a new animation frame based on a texture region and a time + /// + /// The texture region that this frame should render + /// The total amount of seconds that this frame should last for public AnimationFrame(TextureRegion region, double seconds) { this.Region = region; this.Seconds = seconds; diff --git a/MLEM/Animations/SpriteAnimationGroup.cs b/MLEM/Animations/SpriteAnimationGroup.cs index 53140c0..1dce5df 100644 --- a/MLEM/Animations/SpriteAnimationGroup.cs +++ b/MLEM/Animations/SpriteAnimationGroup.cs @@ -5,7 +5,11 @@ using MLEM.Misc; using MLEM.Textures; namespace MLEM.Animations { - public class SpriteAnimationGroup : GenericDataHolder{ + /// + /// Represents a list of objects with a condition and priority attached to them. + /// Sprite animation groups can be used if any single entity should have multiple animations (like up, down, left, right standing and running animations) that should be automatically managed. + /// + public class SpriteAnimationGroup : GenericDataHolder { private readonly List animations = new List(); private ConditionedAnimation currAnimation; @@ -20,11 +24,24 @@ namespace MLEM.Animations { } set => this.currAnimation = value; } + /// + /// The animation that is currently playing + /// public SpriteAnimation CurrentAnimation => this.CurrAnimation?.Animation; + /// + /// The frame that is displaying + /// public AnimationFrame CurrentFrame => this.CurrentAnimation?.CurrentFrame; + /// + /// The region that has + /// public TextureRegion CurrentRegion => this.CurrentAnimation?.CurrentRegion; - public AnimationChanged OnAnimationChanged; + /// + /// A callback for when the currently displaying animation has changed due to a condition with a higher priority being met. + /// + public event AnimationChanged OnAnimationChanged; private bool isDirty; + /// public float SpeedMultiplier { set { foreach (var anim in this.animations) @@ -32,23 +49,36 @@ namespace MLEM.Animations { } } + /// + /// Adds a to this group. + /// + /// The animation to add + /// The condition that needs to be met for this animation to play + /// The priority of this animation. The higher the priority, the earlier it is picked for playing. + /// This group, for chaining public SpriteAnimationGroup Add(SpriteAnimation anim, Func condition, int priority = 0) { this.animations.Add(new ConditionedAnimation(anim, condition, priority)); this.isDirty = true; return this; } + /// public void Update(GameTime time) { this.FindAnimationToPlay(); if (this.CurrAnimation != null) this.CurrAnimation.Animation.Update(time); } + /// + /// Find an animation in this group by name and returns it. + /// + /// The of the animation + /// The animation by that name, or null if there is none public SpriteAnimation ByName(string name) { return this.animations.Find(anim => anim.Animation.Name == name)?.Animation; } - public void FindAnimationToPlay() { + private void FindAnimationToPlay() { ConditionedAnimation animToPlay = null; if (this.CurrAnimation != null && this.CurrAnimation.ShouldPlay()) animToPlay = this.CurrAnimation; diff --git a/MLEM/Cameras/Camera.cs b/MLEM/Cameras/Camera.cs index 25a544e..35dfcd8 100644 --- a/MLEM/Cameras/Camera.cs +++ b/MLEM/Cameras/Camera.cs @@ -5,18 +5,45 @@ using MLEM.Extensions; using MLEM.Misc; namespace MLEM.Cameras { + /// + /// Represents a simple, orthographic 2-dimensional camera that can be moved, scaled and that supports automatic viewport sizing. + /// To draw with the camera's positioning and scaling applied, use . + /// public class Camera { + /// + /// The top-left corner of the camera's viewport. + /// + /// public Vector2 Position; + /// + /// The scale that this camera's should have. + /// public float Scale { get => this.scale; set => this.scale = MathHelper.Clamp(value, this.MinScale, this.MaxScale); } private float scale = 1; + /// + /// The minimum that the camera can have + /// public float MinScale = 0; + /// + /// The maximum that the camera can have + /// public float MaxScale = float.MaxValue; + /// + /// If this is true, the camera will automatically adapt to changed screen sizes. + /// You can use to determine the initial screen size that this camera should base its calculations on. + /// public bool AutoScaleWithScreen; + /// + /// + /// public Point AutoScaleReferenceSize; + /// + /// The scale that this camera currently has, based on and if is true. + /// public float ActualScale { get { if (!this.AutoScaleWithScreen) @@ -24,6 +51,10 @@ namespace MLEM.Cameras { return Math.Min(this.Viewport.Width / (float) this.AutoScaleReferenceSize.X, this.Viewport.Height / (float) this.AutoScaleReferenceSize.Y) * this.Scale; } } + /// + /// The matrix that this camera "sees", based on its position and scale. + /// Use this in your calls to render based on the camera's viewport. + /// public Matrix ViewMatrix { get { var sc = this.ActualScale; @@ -33,39 +64,73 @@ namespace MLEM.Cameras { return Matrix.CreateScale(sc, sc, 1) * Matrix.CreateTranslation(new Vector3(pos, 0)); } } + /// + /// The bottom-right corner of the camera's viewport + /// + /// public Vector2 Max { get => this.Position + this.ScaledViewport; set => this.Position = value - this.ScaledViewport; } + /// + /// The center of the camera's viewport, or the position that the camera is looking at. + /// public Vector2 LookingPosition { get => this.Position + this.ScaledViewport / 2; set => this.Position = value - this.ScaledViewport / 2; } - public Rectangle Viewport => this.graphicsDevice.Viewport.Bounds; - public Vector2 ScaledViewport => new Vector2(this.Viewport.Width, this.Viewport.Height) / this.ActualScale; + private Rectangle Viewport => this.graphicsDevice.Viewport.Bounds; + private Vector2 ScaledViewport => new Vector2(this.Viewport.Width, this.Viewport.Height) / this.ActualScale; private readonly bool roundPosition; private readonly GraphicsDevice graphicsDevice; + /// + /// Creates a new camera. + /// + /// The game's graphics device + /// If this is true, the camera's and related properties will be rounded to full integers. public Camera(GraphicsDevice graphicsDevice, bool roundPosition = true) { this.graphicsDevice = graphicsDevice; this.AutoScaleReferenceSize = this.Viewport.Size; this.roundPosition = roundPosition; } + /// + /// Converts a given position in screen space to world space + /// + /// The position in screen space + /// The position in world space public Vector2 ToWorldPos(Vector2 pos) { return Vector2.Transform(pos, Matrix.Invert(this.ViewMatrix)); } + /// + /// Converts a given position in world space to screen space + /// + /// The position in world space + /// The position in camera space public Vector2 ToCameraPos(Vector2 pos) { return Vector2.Transform(pos, this.ViewMatrix); } + /// + /// Returns the area that this camera can see, in world space. + /// This can be useful for culling of tile and other entity renderers. + /// + /// A rectangle that represents the camera's visible area in world space public RectangleF GetVisibleRectangle() { var start = this.ToWorldPos(Vector2.Zero); return new RectangleF(start, this.ToWorldPos(new Vector2(this.Viewport.Width, this.Viewport.Height)) - start); } + /// + /// Forces the camera's bounds into the given min and max positions in world space. + /// If the space represented by the given values is smaller than what the camera can see, its position will be forced into the center of the area. + /// + /// The top left bound, in world space + /// The bottom right bound, in world space + /// Whether or not the camera's position changed as a result of the constraint public bool ConstrainWorldBounds(Vector2 min, Vector2 max) { var lastPos = this.Position; var visible = this.GetVisibleRectangle(); @@ -89,6 +154,11 @@ namespace MLEM.Cameras { return !this.Position.Equals(lastPos, 0.001F); } + /// + /// Zoom in the camera's view by a given amount, optionally focusing on a given center point. + /// + /// The amount to zoom in or out by + /// The position that should be regarded as the zoom's center, in screen space. The default value is the center. public void Zoom(float delta, Vector2? zoomCenter = null) { var center = (zoomCenter ?? this.Viewport.Size.ToVector2() / 2) / this.ActualScale; var lastScale = this.Scale; diff --git a/MLEM/Content/RawContentManager.cs b/MLEM/Content/RawContentManager.cs index c661de9..783f934 100644 --- a/MLEM/Content/RawContentManager.cs +++ b/MLEM/Content/RawContentManager.cs @@ -8,6 +8,9 @@ using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; namespace MLEM.Content { + /// + /// Represents a version of that doesn't load content binary xnb files, but rather as their regular formats. + /// public class RawContentManager : ContentManager, IGameComponent { private static readonly RawContentReader[] Readers = AppDomain.CurrentDomain.GetAssemblies() @@ -18,14 +21,29 @@ namespace MLEM.Content { private readonly List disposableAssets = new List(); + /// + /// The graphics device that this content manager uses + /// public readonly GraphicsDevice GraphicsDevice; + /// + /// Creates a new content manager with an optionally specified root directory. + /// + /// The service provider of your game + /// The root directory. Defaults to "Content" public RawContentManager(IServiceProvider serviceProvider, string rootDirectory = "Content") : base(serviceProvider, rootDirectory) { if (serviceProvider.GetService(typeof(IGraphicsDeviceService)) is IGraphicsDeviceService s) this.GraphicsDevice = s.GraphicsDevice; } + /// + /// Loads a raw asset with the given name, based on the . + /// If the asset was previously loaded using this method, the cached asset is merely returned. + /// + /// The path and name of the asset to load, without extension. + /// The type of asset to load + /// The asset, either loaded from the cache, or from disk. public override T Load(string assetName) { if (this.LoadedAssets.TryGetValue(assetName, out var ret) && ret is T t) return t; diff --git a/MLEM/Content/RawContentReader.cs b/MLEM/Content/RawContentReader.cs index 1aa1d33..8bad0ed 100644 --- a/MLEM/Content/RawContentReader.cs +++ b/MLEM/Content/RawContentReader.cs @@ -2,26 +2,59 @@ using System; using System.IO; namespace MLEM.Content { + /// + /// Represents a way for any kind of raw content file to be read using a + /// public abstract class RawContentReader { + /// + /// Returns if the given type can be loaded by this content reader + /// + /// The type of asset + /// If can be loaded by this content reader public abstract bool CanRead(Type t); + /// + /// Reads the content file from disk and returns it. + /// + /// The that is loading the asset + /// The full path to the asset, starting from the + /// A stream that leads to this asset + /// The type of asset to load + /// If this asset is being reloaded, this value contains the previous version of the asset. + /// The loaded asset public abstract object Read(RawContentManager manager, string assetPath, Stream stream, Type t, object existing); + /// + /// Represents the list of file extensions that this reader can read from. + /// + /// The list of valid extensions public abstract string[] GetFileExtensions(); } + /// public abstract class RawContentReader : RawContentReader { + /// public override bool CanRead(Type t) { return typeof(T).IsAssignableFrom(t); } + /// public override object Read(RawContentManager manager, string assetPath, Stream stream, Type t, object existing) { return this.Read(manager, assetPath, stream, (T) existing); } + /// + /// Reads the content file that is represented by our generic type from disk. + /// + /// The that is loading the asset + /// The full path to the asset, starting from the + /// A stream that leads to this asset + /// The type of asset to load + /// If this asset is being reloaded, this value contains the previous version of the asset. + /// The loaded asset protected abstract T Read(RawContentManager manager, string assetPath, Stream stream, T existing); } diff --git a/MLEM/Extensions/ColorExtensions.cs b/MLEM/Extensions/ColorExtensions.cs index dc08131..135c7a1 100644 --- a/MLEM/Extensions/ColorExtensions.cs +++ b/MLEM/Extensions/ColorExtensions.cs @@ -5,20 +5,43 @@ using Microsoft.Xna.Framework; namespace MLEM.Extensions { public static class ColorExtensions { + /// + /// Returns an inverted version of the color. + /// + /// The color to invert + /// The inverted color public static Color Invert(this Color color) { return new Color(Math.Abs(255 - color.R), Math.Abs(255 - color.G), Math.Abs(255 - color.B), color.A); } + /// + /// Parses a hexadecimal number into a color. + /// The number should be in the format 0xaarrggbb. + /// + /// The number to parse + /// The resulting color public static Color FromHex(uint value) { return new Color((int) (value >> 16 & 0xFF), (int) (value >> 8 & 0xFF), (int) (value >> 0 & 0xFF), (int) (value >> 24 & 0xFF)); } + /// + /// Parses a hexadecimal string into a color. + /// The string can optionally start with a #. + /// + /// The string to parse + /// The resulting color public static Color FromHex(string value) { if (value.StartsWith("#")) value = value.Substring(1); return FromHex(uint.Parse(value, NumberStyles.HexNumber)); } + /// + /// Copies the alpha value from into this color. + /// + /// The color + /// The color to copy the alpha from + /// The with 's alpha value public static Color CopyAlpha(this Color color, Color other) { return color * (other.A / 255F); } diff --git a/MLEM/Extensions/GraphicsExtensions.cs b/MLEM/Extensions/GraphicsExtensions.cs index 3007efa..bd2240f 100644 --- a/MLEM/Extensions/GraphicsExtensions.cs +++ b/MLEM/Extensions/GraphicsExtensions.cs @@ -9,6 +9,12 @@ namespace MLEM.Extensions { private static int lastWidth; private static int lastHeight; + /// + /// Sets the graphics device manager to fullscreen, properly taking into account the preferred backbuffer width and height to avoid lower resolutions for higher resolution screens. + /// + /// The graphics device manager + /// True if fullscreen should be enabled, false if disabled + /// Thrown when changing out of fullscreen mode before changing into fullscreen mode using this method public static void SetFullscreen(this GraphicsDeviceManager manager, bool fullscreen) { manager.IsFullScreen = fullscreen; if (fullscreen) { @@ -29,6 +35,10 @@ namespace MLEM.Extensions { manager.ApplyChanges(); } + /// + /// Save version of that doesn't reset window size to defaults + /// + /// The graphics device manager public static void ApplyChangesSafely(this GraphicsDeviceManager manager) { // If we don't do this, then applying changes will cause the // graphics device manager to reset the window size to the @@ -39,6 +49,11 @@ namespace MLEM.Extensions { manager.ApplyChanges(); } + /// + /// Resets preferred width and height back to the window's default bound values. + /// + /// The graphics device manager + /// The window whose bounds to use public static void ResetWidthAndHeight(this GraphicsDeviceManager manager, GameWindow window) { var (_, _, width, height) = window.ClientBounds; manager.PreferredBackBufferWidth = Math.Max(height, width); @@ -46,6 +61,13 @@ namespace MLEM.Extensions { manager.ApplyChanges(); } + /// + /// Starts a new using the specified render target. + /// The returned context automatically disposes when used in a using statement, which causes any previously applied render targets to be reapplied automatically. + /// + /// The graphics device + /// The render target to apply + /// public static TargetContext WithRenderTarget(this GraphicsDevice device, RenderTarget2D target) { return new TargetContext(device, target); } diff --git a/MLEM/Extensions/NumberExtensions.cs b/MLEM/Extensions/NumberExtensions.cs index 447304c..1bddce4 100644 --- a/MLEM/Extensions/NumberExtensions.cs +++ b/MLEM/Extensions/NumberExtensions.cs @@ -5,68 +5,115 @@ using MLEM.Misc; namespace MLEM.Extensions { public static class NumberExtensions { + /// public static int Floor(this float f) { return (int) Math.Floor(f); } + /// public static int Ceil(this float f) { return (int) Math.Ceiling(f); } + /// + /// Checks for decimal equality with a given tolerance. + /// + /// The first number to equate + /// The second number to equate + /// The equality tolerance + /// Whether or not and are different by at most public static bool Equals(this float first, float second, float tolerance) { - return Math.Abs(first- second) <= tolerance; + return Math.Abs(first - second) <= tolerance; } + /// public static bool Equals(this Vector2 first, Vector2 second, float tolerance) { return Math.Abs(first.X - second.X) <= tolerance && Math.Abs(first.Y - second.Y) <= tolerance; } + /// public static bool Equals(this Vector3 first, Vector3 second, float tolerance) { return Math.Abs(first.X - second.X) <= tolerance && Math.Abs(first.Y - second.Y) <= tolerance && Math.Abs(first.Z - second.Z) <= tolerance; } + /// public static bool Equals(this Vector4 first, Vector4 second, float tolerance) { return Math.Abs(first.X - second.X) <= tolerance && Math.Abs(first.Y - second.Y) <= tolerance && Math.Abs(first.Z - second.Z) <= tolerance && Math.Abs(first.W - second.W) <= tolerance; } + /// public static Vector2 Floor(this Vector2 vec) { return new Vector2(vec.X.Floor(), vec.Y.Floor()); } + /// public static Vector3 Floor(this Vector3 vec) { return new Vector3(vec.X.Floor(), vec.Y.Floor(), vec.Z.Floor()); } + /// public static Vector4 Floor(this Vector4 vec) { return new Vector4(vec.X.Floor(), vec.Y.Floor(), vec.Z.Floor(), vec.W.Floor()); } + /// + /// Multiplies a point by a given scalar. + /// + /// The point + /// The scalar + /// The point, multiplied by the scalar memberwise public static Point Multiply(this Point point, float f) { return new Point((point.X * f).Floor(), (point.Y * f).Floor()); } + /// + /// Divides a point by a given scalar. + /// + /// The point + /// The scalar + /// The point, divided by the scalar memberwise public static Point Divide(this Point point, float f) { return new Point((point.X / f).Floor(), (point.Y / f).Floor()); } + /// + /// Transforms a point by a given matrix. + /// + /// The point + /// The matrix + /// The point, transformed by the matrix public static Point Transform(this Point position, Matrix matrix) { return new Point( (position.X * matrix.M11 + position.Y * matrix.M21 + matrix.M41).Floor(), (position.X * matrix.M12 + position.Y * matrix.M22 + matrix.M42).Floor()); } + /// + /// Returns a copy of the given rectangle, moved by the given point. + /// The rectangle's size remains unchanged. + /// + /// The rectangle to move + /// The amount to move by + /// The moved rectangle public static Rectangle OffsetCopy(this Rectangle rect, Point offset) { rect.X += offset.X; rect.Y += offset.Y; return rect; } + /// public static RectangleF OffsetCopy(this RectangleF rect, Vector2 offset) { rect.X += offset.X; rect.Y += offset.Y; return rect; } + /// + /// Shrinks the rectangle by the given padding, causing its size to decrease by twice the amount and its position to be moved inwards by the amount. + /// + /// The rectangle to shrink + /// The padding to shrink by + /// The shrunk rectangle public static Rectangle Shrink(this Rectangle rect, Point padding) { rect.X += padding.X; rect.Y += padding.Y; @@ -75,6 +122,7 @@ namespace MLEM.Extensions { return rect; } + /// public static RectangleF Shrink(this RectangleF rect, Vector2 padding) { rect.X += padding.X; rect.Y += padding.Y; @@ -83,6 +131,7 @@ namespace MLEM.Extensions { return rect; } + /// public static RectangleF Shrink(this RectangleF rect, Padding padding) { rect.X += padding.Left; rect.Y += padding.Top; diff --git a/MLEM/Extensions/RandomExtensions.cs b/MLEM/Extensions/RandomExtensions.cs index 80be53f..3fec383 100644 --- a/MLEM/Extensions/RandomExtensions.cs +++ b/MLEM/Extensions/RandomExtensions.cs @@ -5,14 +5,32 @@ using System.Linq; namespace MLEM.Extensions { public static class RandomExtensions { + /// + /// Gets a random entry from the given list with uniform chance. + /// + /// The random + /// The entries to choose from + /// The entries' type + /// A random entry public static T GetRandomEntry(this Random random, params T[] entries) { return entries[random.Next(entries.Length)]; } + /// public static T GetRandomEntry(this Random random, IList entries) { return entries[random.Next(entries.Count)]; } + /// + /// Returns a random entry from the given list based on the specified weight function. + /// A higher weight for an entry increases its likeliness of being picked. + /// + /// The random + /// The entries to choose from + /// A function that applies weight to each entry + /// The entries' type + /// A random entry, based on the entries' weight + /// If the weight function returns different weights for the same entry public static T GetRandomWeightedEntry(this Random random, IList entries, Func weightFunc) { var totalWeight = entries.Sum(weightFunc); var goalWeight = random.Next(totalWeight); diff --git a/MLEM/Extensions/SpriteBatchExtensions.cs b/MLEM/Extensions/SpriteBatchExtensions.cs index 9c79969..71ae5e1 100644 --- a/MLEM/Extensions/SpriteBatchExtensions.cs +++ b/MLEM/Extensions/SpriteBatchExtensions.cs @@ -8,6 +8,12 @@ namespace MLEM.Extensions { private static Texture2D blankTexture; + /// + /// Returns a 1x1 pixel white texture that can be used for drawing solid color shapes. + /// This texture is automatically disposed of when the batch is disposed. + /// + /// The sprite batch + /// A 1x1 pixel white texture public static Texture2D GetBlankTexture(this SpriteBatch batch) { if (blankTexture == null) { blankTexture = new Texture2D(batch.GraphicsDevice, 1, 1); @@ -22,6 +28,13 @@ namespace MLEM.Extensions { return blankTexture; } + /// + /// Generates a that has a texture with a given color and outline color + /// + /// The sprite batch + /// The fill color of the texture + /// The outline color of the texture + /// A containing a 3x3 texture with an outline public static NinePatch GenerateTexture(this SpriteBatch batch, Color color, Color? outlineColor = null) { var outli = outlineColor ?? Color.Black; var tex = new Texture2D(batch.GraphicsDevice, 3, 3); @@ -39,16 +52,19 @@ namespace MLEM.Extensions { return new NinePatch(tex, 1); } + /// public static void Draw(this SpriteBatch batch, Texture2D texture, RectangleF destinationRectangle, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, SpriteEffects effects, float layerDepth) { var source = sourceRectangle ?? new Rectangle(0, 0, texture.Width, texture.Height); var scale = new Vector2(1F / source.Width, 1F / source.Height) * destinationRectangle.Size; batch.Draw(texture, destinationRectangle.Location, sourceRectangle, color, rotation, origin, scale, effects, layerDepth); } + /// public static void Draw(this SpriteBatch batch, Texture2D texture, RectangleF destinationRectangle, Rectangle? sourceRectangle, Color color) { batch.Draw(texture, destinationRectangle, sourceRectangle, color, 0, Vector2.Zero, SpriteEffects.None, 0); } + /// public static void Draw(this SpriteBatch batch, Texture2D texture, RectangleF destinationRectangle, Color color) { batch.Draw(texture, destinationRectangle, null, color); } diff --git a/MLEM/Font/GenericFont.cs b/MLEM/Font/GenericFont.cs index 475b516..9b75b55 100644 --- a/MLEM/Font/GenericFont.cs +++ b/MLEM/Font/GenericFont.cs @@ -5,38 +5,67 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MLEM.Font { + /// + /// Represents a font with additional abilities. + /// + /// public abstract class GenericFont { + /// + /// The bold version of this font. + /// public abstract GenericFont Bold { get; } + /// + /// The italic version of this font. + /// public abstract GenericFont Italic { get; } + /// public abstract float LineHeight { get; } + /// public abstract Vector2 MeasureString(string text); + /// public abstract Vector2 MeasureString(StringBuilder text); + /// public abstract void DrawString(SpriteBatch batch, string text, Vector2 position, Color color); + /// public abstract void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth); + /// public abstract void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth); + /// public abstract void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color); + /// public abstract void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth); + /// public abstract void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth); + /// + /// Draws a string with the given text alignment. + /// + /// The sprite batch to use + /// The string to draw + /// The position of the top left corner of the string + /// The alignment to use + /// The color to use public void DrawString(SpriteBatch batch, string text, Vector2 position, TextAlign align, Color color) { this.DrawString(batch, text, position, align, color, 0, Vector2.Zero, Vector2.One, SpriteEffects.None, 0); } + /// public void DrawString(SpriteBatch batch, string text, Vector2 position, TextAlign align, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) { this.DrawString(batch, text, position, align, color, rotation, origin, new Vector2(scale), effects, layerDepth); } + /// public void DrawString(SpriteBatch batch, string text, Vector2 position, TextAlign align, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { switch (align) { case TextAlign.Center: @@ -53,6 +82,16 @@ namespace MLEM.Font { this.DrawString(batch, text, position, color, rotation, origin, scale, effects, layerDepth); } + /// + /// Truncates a string to a given width. If the string's displayed area is larger than the maximum width, the string is cut off. + /// Optionally, the string can be cut off a bit sooner, adding the at the end instead. + /// + /// The text to truncate + /// The maximum width, in display pixels based on the font and scale + /// The scale to use for width measurements + /// If the string should be truncated from the back rather than the front + /// The characters to add to the end of the string if it is too long + /// The truncated string, or the same string if it is shorter than the maximum width public string TruncateString(string text, float width, float scale, bool fromBack = false, string ellipsis = "") { var total = new StringBuilder(); var ellipsisWidth = this.MeasureString(ellipsis).X * scale; @@ -74,6 +113,14 @@ namespace MLEM.Font { return total.ToString(); } + /// + /// Splits a string to a given maximum width, adding newline characters between each line. + /// Also splits long words. + /// + /// The text to split into multiple lines + /// The maximum width that each line should have + /// The scale to use for width measurements + /// The split string, containing newline characters at each new line public string SplitString(string text, float width, float scale) { var total = new StringBuilder(); foreach (var line in text.Split('\n')) { @@ -109,6 +156,12 @@ namespace MLEM.Font { return total.ToString(0, total.Length - 2); } + /// + /// Returns a string made up of the given content characters that is the given length long when displayed. + /// + /// The width that the string should have if the scale is 1 + /// The content that the string should contain. Defaults to a space. + /// public string GetWidthString(float width, char content = ' ') { var strg = content.ToString(); while (this.MeasureString(strg).X < width) diff --git a/MLEM/Font/GenericSpriteFont.cs b/MLEM/Font/GenericSpriteFont.cs index a85c94e..68de774 100644 --- a/MLEM/Font/GenericSpriteFont.cs +++ b/MLEM/Font/GenericSpriteFont.cs @@ -5,47 +5,66 @@ using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; namespace MLEM.Font { + /// public class GenericSpriteFont : GenericFont { public readonly SpriteFont Font; + /// public override GenericFont Bold { get; } + /// public override GenericFont Italic { get; } + /// public override float LineHeight => this.Font.LineSpacing; + /// + /// Creates a new generic font using . + /// Optionally, a bold and italic version of the font can be supplied. + /// + /// The font to wrap + /// A bold version of the font + /// An italic version of the font public GenericSpriteFont(SpriteFont font, SpriteFont bold = null, SpriteFont italic = null) { this.Font = font; this.Bold = bold != null ? new GenericSpriteFont(bold) : this; this.Italic = italic != null ? new GenericSpriteFont(italic) : this; } + /// public override Vector2 MeasureString(string text) { return this.Font.MeasureString(text); } + /// public override Vector2 MeasureString(StringBuilder text) { return this.Font.MeasureString(text); } + /// public override void DrawString(SpriteBatch batch, string text, Vector2 position, Color color) { batch.DrawString(this.Font, text, position, color); } + /// public override void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) { batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth); } + /// public override void DrawString(SpriteBatch batch, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth); } + /// public override void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color) { batch.DrawString(this.Font, text, position, color); } + /// public override void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects effects, float layerDepth) { batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth); } + /// public override void DrawString(SpriteBatch batch, StringBuilder text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { batch.DrawString(this.Font, text, position, color, rotation, origin, scale, effects, layerDepth); }