1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-05-15 05:38:46 +02:00
MLEM/MLEM/Animations/SpriteAnimationGroup.cs
2022-06-17 18:23:47 +02:00

141 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using MLEM.Misc;
using MLEM.Textures;
namespace MLEM.Animations {
/// <summary>
/// Represents a list of <see cref="SpriteAnimation"/> 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.
/// </summary>
public class SpriteAnimationGroup : GenericDataHolder {
/// <summary>
/// Returns the animation that is currently playing.
/// </summary>
public SpriteAnimation CurrentAnimation {
get {
this.SortAnimationsIfDirty(true);
return this.currentAnimation?.Animation;
}
}
/// <summary>
/// Returns the frame that <see cref="CurrentAnimation"/> is displaying.
/// </summary>
public AnimationFrame CurrentFrame => this.CurrentAnimation?.CurrentFrame;
/// <summary>
/// Returns the <see cref="CurrentAnimation"/>'s <see cref="SpriteAnimation.CurrentRegion"/>.
/// </summary>
public TextureRegion CurrentRegion => this.CurrentAnimation?.CurrentRegion;
/// <summary>
/// Returns the <see cref="CurrentAnimation"/>'s <see cref="SpriteAnimation.CurrentRegions"/>.
/// </summary>
public IList<TextureRegion> CurrentRegions => this.CurrentAnimation?.CurrentRegions;
/// <inheritdoc cref="SpriteAnimation.SpeedMultiplier"/>
public float SpeedMultiplier {
set {
foreach (var anim in this.animations)
anim.Animation.SpeedMultiplier = value;
}
}
/// <summary>
/// A callback for when the currently displaying animation has changed due to a condition with a higher priority being met.
/// </summary>
public event AnimationChanged OnAnimationChanged;
private readonly List<ConditionedAnimation> animations = new List<ConditionedAnimation>();
private ConditionedAnimation currentAnimation;
private bool animationsDirty;
/// <summary>
/// Adds a <see cref="SpriteAnimation"/> to this group.
/// </summary>
/// <param name="anim">The animation to add</param>
/// <param name="condition">The condition that needs to be met for this animation to play</param>
/// <param name="priority">The priority of this animation. The higher the priority, the earlier it is picked for playing.</param>
/// <returns>This group, for chaining</returns>
public SpriteAnimationGroup Add(SpriteAnimation anim, Func<bool> condition, int priority = 0) {
this.animations.Add(new ConditionedAnimation(anim, condition, priority));
this.animationsDirty = true;
return this;
}
/// <inheritdoc cref="SpriteAnimation.Update(GameTime)"/>
public void Update(GameTime time) {
this.Update(time.ElapsedGameTime);
}
/// <inheritdoc cref="SpriteAnimation.Update(TimeSpan)"/>
public void Update(TimeSpan elapsed) {
this.FindAnimationToPlay();
if (this.CurrentAnimation != null)
this.CurrentAnimation.Update(elapsed);
}
/// <summary>
/// Find an animation in this group by name and returns it.
/// </summary>
/// <param name="name">The <see cref="SpriteAnimation.Name"/> of the animation</param>
/// <returns>The animation by that name, or <c>null</c> if there is none</returns>
public SpriteAnimation ByName(string name) {
return this.animations.Find(anim => anim.Animation.Name == name)?.Animation;
}
private void FindAnimationToPlay() {
this.SortAnimationsIfDirty(false);
ConditionedAnimation animToPlay = null;
if (this.currentAnimation != null && this.currentAnimation.ShouldPlay())
animToPlay = this.currentAnimation;
foreach (var anim in this.animations) {
// if we find an animation with a lower priority then it means we can break since the list is sorted by priority
if (animToPlay != null && anim.Priority <= animToPlay.Priority)
break;
if (anim.ShouldPlay())
animToPlay = anim;
}
if (animToPlay != this.currentAnimation) {
this.OnAnimationChanged?.Invoke(this.currentAnimation?.Animation, animToPlay?.Animation);
this.currentAnimation = animToPlay;
if (animToPlay != null)
animToPlay.Animation.Restart();
}
}
private void SortAnimationsIfDirty(bool findAnimationToPlay) {
if (this.animationsDirty) {
this.animationsDirty = false;
this.animations.Sort((a1, a2) => a2.Priority.CompareTo(a1.Priority));
if (findAnimationToPlay)
this.FindAnimationToPlay();
}
}
/// <summary>
/// A callback delegate for when a <see cref="SpriteAnimationGroup"/>'s current animation changed.
/// </summary>
/// <param name="oldAnim">The previous animation</param>
/// <param name="newAnim">The new animation</param>
public delegate void AnimationChanged(SpriteAnimation oldAnim, SpriteAnimation newAnim);
}
internal class ConditionedAnimation {
public readonly SpriteAnimation Animation;
public readonly Func<bool> ShouldPlay;
public readonly int Priority;
public ConditionedAnimation(SpriteAnimation animation, Func<bool> shouldPlay, int priority) {
this.Animation = animation;
this.ShouldPlay = shouldPlay;
this.Priority = priority;
}
}
}