1 module evael.graphics.gui.animations.AnimationSet;
2 
3 import std.typecons;
4 
5 import evael.graphics.gui.animations.Animation;
6 import evael.graphics.gui.controls.Control;
7 
8 import dnogc.DynamicArray;
9 
10 /**
11  * AnimationSet.
12  *
13  * Contains multiple animations to be played one by one or all together.
14  */
15 class AnimationSet
16 {
17 	public enum SequenceType : ubyte
18 	{
19 		AllTogether,
20 		OneByOne
21 	}
22 
23 	protected alias OnSequenceEnd = void delegate();
24 	protected OnSequenceEnd m_onSequenceEnd;
25 	
26 	/// Animations
27 	private DynamicArray!Animation m_animations;
28 
29 	/// Current animation
30 	private uint m_currentAnimation;
31 	
32 	/// Replay animations ?
33 	private bool m_loop;
34 
35 	/// AnimationSet sequence type (play all animations together or one by one)
36 	private SequenceType m_sequenceType;
37 
38 	/// Animations duration in ms
39 	protected float m_duration;
40 
41 	private Control m_control;
42 
43 	/**
44 	 * AnimationSet constructor.
45 	 * Params:
46 	 *      loop : loop animations
47 	 *      sequenceType : sequence type
48 	 */
49 	@nogc @safe
50 	public this(in Flag!"loop" loop = No.loop, in SequenceType sequenceType = SequenceType.OneByOne) pure nothrow
51 	{
52 		this.m_currentAnimation = 0;
53 		this.m_loop = loop;
54 		this.m_sequenceType = sequenceType;
55 		this.m_duration = 1000.0f;
56 	}
57 
58 	public void dispose()
59 	{
60 		this.m_animations.dispose();
61 	}
62 
63 	public void update(in float deltaTime)
64 	{
65 		if (this.m_sequenceType == SequenceType.OneByOne)
66 		{
67 			this.m_animations[this.m_currentAnimation].update(deltaTime);
68 		}
69 		else
70 		{
71 			foreach (animation; this.m_animations)
72 			{
73 				animation.update(deltaTime);
74 			}
75 		}
76 	}
77 
78 	/**
79 	 * Adds animation to the set.
80 	 * Params:
81 	 *      animation : 
82 	 */
83 	@nogc @safe
84 	public void add(Animation animation) pure nothrow
85 	{
86 		animation.id = this.m_animations.length;
87 		
88 		this.m_animations.insert(animation);
89 
90 		animation.onAnimationEndEvent = &this.onAnimationEndEvent;
91 	}
92 
93 	/**
94 	 * Event received when an animation ended.
95 	 */
96 	public void onAnimationEndEvent(Animation endedAnimation)
97 	{
98 		final switch (this.m_sequenceType) with (SequenceType)
99 		{
100 			case OneByOne: 
101 			{
102 				if (++this.m_currentAnimation != this.m_animations.length)
103 					return;
104 				
105 				if (this.m_loop)
106 				{
107 					this.m_currentAnimation = 0;
108 
109 					foreach (animation; this.m_animations)
110 					{
111 						animation.reset();
112 					}
113 				}
114 				else
115 				{
116 					if (this.m_onSequenceEnd !is null)
117 					{
118 						this.m_onSequenceEnd();
119 					}
120 				}
121 				break;
122 			}
123 			case AllTogether:
124 			{
125 				// We are playing all animations together
126 				if (this.m_loop)
127 				{
128 					endedAnimation.reset();
129 				}
130 				else
131 				{
132 					import std.algorithm : countUntil;
133 
134 					auto animationIndex = this.m_animations[].countUntil!(a => a == endedAnimation);
135 
136 					if (animationIndex >= 0)
137 					{
138 						this.m_animations.remove(animationIndex);
139 					}
140 
141 					if (!this.m_animations.length)
142 					{
143 						if (this.m_onSequenceEnd !is null)
144 						{
145 							this.m_onSequenceEnd();
146 						}
147 					}
148 				}
149 				break;
150 			}
151 		}
152 	}
153 	
154 	@nogc @safe
155 	@property pure nothrow
156 	{
157 		public void control(Control value)
158 		{
159 			this.m_control = value;
160 
161 			foreach (animation; this.m_animations)
162 			{
163 				animation.control = value;
164 			}
165 		}
166 
167 		public void duration(in float value)
168 		{
169 			this.m_duration = value;
170 
171 			foreach (animation; this.m_animations)
172 			{
173 				animation.duration = value;
174 			}
175 		}
176 
177 		public void onSequenceEndEvent(OnSequenceEnd value)
178 		{
179 			this.m_onSequenceEnd = value;
180 		}
181 	}
182 }