using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using MLEM.Misc;
using MLEM.Textures;
namespace MLEM.Animations {
///
/// 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;
private ConditionedAnimation CurrAnimation {
get {
if (this.isDirty) {
this.isDirty = false;
this.animations.Sort((a1, a2) => a2.Priority.CompareTo(a1.Priority));
this.FindAnimationToPlay();
}
return this.currAnimation;
}
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;
///
/// 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)
anim.Animation.SpeedMultiplier = value;
}
}
///
/// 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.Update(time.ElapsedGameTime);
}
///
public void Update(TimeSpan elapsed) {
this.FindAnimationToPlay();
if (this.CurrAnimation != null)
this.CurrAnimation.Animation.Update(elapsed);
}
///
/// 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;
}
private void FindAnimationToPlay() {
ConditionedAnimation animToPlay = null;
if (this.CurrAnimation != null && this.CurrAnimation.ShouldPlay())
animToPlay = this.CurrAnimation;
foreach (var anim in this.animations) {
// if we find an animation with a lower priority then it means we can break
// because the list is sorted by priority
if (animToPlay != null && anim.Priority < animToPlay.Priority)
break;
if (anim.ShouldPlay())
animToPlay = anim;
}
if (animToPlay != this.CurrAnimation) {
this.OnAnimationChanged?.Invoke(this.CurrAnimation?.Animation, animToPlay?.Animation);
this.CurrAnimation = animToPlay;
if (animToPlay != null)
animToPlay.Animation.Restart();
}
}
///
/// A callback delegate for when a 's current animation changed.
///
/// The previous animation
/// The new animation
public delegate void AnimationChanged(SpriteAnimation oldAnim, SpriteAnimation newAnim);
}
internal class ConditionedAnimation {
public readonly SpriteAnimation Animation;
public readonly Func ShouldPlay;
public readonly int Priority;
public ConditionedAnimation(SpriteAnimation animation, Func shouldPlay, int priority) {
this.Animation = animation;
this.ShouldPlay = shouldPlay;
this.Priority = priority;
}
}
}