2019-08-14 19:07:23 +02:00
using System ;
using System.Collections.Generic ;
using Microsoft.Xna.Framework ;
2020-03-21 00:49:43 +01:00
using MLEM.Misc ;
2019-08-14 19:07:23 +02:00
using MLEM.Textures ;
namespace MLEM.Animations {
2020-05-20 23:59:40 +02:00
/// <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 {
2019-08-14 19:07:23 +02:00
private readonly List < ConditionedAnimation > animations = new List < ConditionedAnimation > ( ) ;
private ConditionedAnimation currAnimation ;
2019-09-30 20:55:50 +02:00
private ConditionedAnimation CurrAnimation {
get {
if ( this . isDirty ) {
this . isDirty = false ;
2020-01-02 21:48:02 +01:00
this . animations . Sort ( ( a1 , a2 ) = > a2 . Priority . CompareTo ( a1 . Priority ) ) ;
2019-09-30 20:55:50 +02:00
this . FindAnimationToPlay ( ) ;
}
return this . currAnimation ;
}
set = > this . currAnimation = value ;
}
2020-05-20 23:59:40 +02:00
/// <summary>
/// The animation that is currently playing
/// </summary>
2019-09-30 20:55:50 +02:00
public SpriteAnimation CurrentAnimation = > this . CurrAnimation ? . Animation ;
2020-05-20 23:59:40 +02:00
/// <summary>
/// The frame that <see cref="CurrentAnimation"/> is displaying
/// </summary>
2019-08-21 17:04:20 +02:00
public AnimationFrame CurrentFrame = > this . CurrentAnimation ? . CurrentFrame ;
2020-05-20 23:59:40 +02:00
/// <summary>
/// The region that <see cref="CurrentFrame"/> has
/// </summary>
2019-08-21 17:04:20 +02:00
public TextureRegion CurrentRegion = > this . CurrentAnimation ? . CurrentRegion ;
2020-05-20 23:59:40 +02:00
/// <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 ;
2019-09-30 20:55:50 +02:00
private bool isDirty ;
2020-05-20 23:59:40 +02:00
/// <inheritdoc cref="SpriteAnimation.SpeedMultiplier"/>
2020-01-09 21:38:04 +01:00
public float SpeedMultiplier {
set {
foreach ( var anim in this . animations )
anim . Animation . SpeedMultiplier = value ;
}
}
2019-08-14 19:07:23 +02:00
2020-05-20 23:59:40 +02:00
/// <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>
2019-08-21 20:25:32 +02:00
public SpriteAnimationGroup Add ( SpriteAnimation anim , Func < bool > condition , int priority = 0 ) {
this . animations . Add ( new ConditionedAnimation ( anim , condition , priority ) ) ;
2019-09-30 20:55:50 +02:00
this . isDirty = true ;
2019-08-14 19:07:23 +02:00
return this ;
}
2020-08-13 19:46:49 +02:00
/// <inheritdoc cref="SpriteAnimation.Update(GameTime)"/>
2019-08-14 19:07:23 +02:00
public void Update ( GameTime time ) {
2020-08-13 19:46:49 +02:00
this . Update ( time . ElapsedGameTime ) ;
}
/// <inheritdoc cref="SpriteAnimation.Update(TimeSpan)"/>
public void Update ( TimeSpan elapsed ) {
2019-09-30 20:55:50 +02:00
this . FindAnimationToPlay ( ) ;
if ( this . CurrAnimation ! = null )
2020-08-13 19:46:49 +02:00
this . CurrAnimation . Animation . Update ( elapsed ) ;
2019-09-30 20:55:50 +02:00
}
2020-05-20 23:59:40 +02:00
/// <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>
2019-09-30 20:55:50 +02:00
public SpriteAnimation ByName ( string name ) {
return this . animations . Find ( anim = > anim . Animation . Name = = name ) ? . Animation ;
}
2020-05-20 23:59:40 +02:00
private void FindAnimationToPlay ( ) {
2019-08-21 20:25:32 +02:00
ConditionedAnimation animToPlay = null ;
2019-09-30 20:55:50 +02:00
if ( this . CurrAnimation ! = null & & this . CurrAnimation . ShouldPlay ( ) )
animToPlay = this . CurrAnimation ;
2019-08-21 20:25:32 +02:00
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 ;
2019-08-14 19:07:23 +02:00
}
2019-09-30 20:55:50 +02:00
if ( animToPlay ! = this . CurrAnimation ) {
this . OnAnimationChanged ? . Invoke ( this . CurrAnimation ? . Animation , animToPlay ? . Animation ) ;
this . CurrAnimation = animToPlay ;
2019-08-21 20:25:32 +02:00
if ( animToPlay ! = null )
animToPlay . Animation . Restart ( ) ;
}
}
2020-05-21 17:21:34 +02:00
/// <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>
2019-08-21 20:25:32 +02:00
public delegate void AnimationChanged ( SpriteAnimation oldAnim , SpriteAnimation newAnim ) ;
2019-08-14 19:07:23 +02:00
}
internal class ConditionedAnimation {
public readonly SpriteAnimation Animation ;
public readonly Func < bool > ShouldPlay ;
2019-08-21 20:25:32 +02:00
public readonly int Priority ;
2019-08-14 19:07:23 +02:00
2019-08-21 20:25:32 +02:00
public ConditionedAnimation ( SpriteAnimation animation , Func < bool > shouldPlay , int priority ) {
2019-08-14 19:07:23 +02:00
this . Animation = animation ;
this . ShouldPlay = shouldPlay ;
2019-08-21 20:25:32 +02:00
this . Priority = priority ;
2019-08-14 19:07:23 +02:00
}
}
}