1use graphics::ImageSize;
2use graphics::math::Scalar;
3
4use ai_behavior::{
5 Status,
6 Success,
7 Running,
8};
9
10use interpolation::EaseFunction;
11use sprite::Sprite;
12
13#[derive(Clone, PartialEq)]
15pub enum Animation {
16 MoveTo(f64, Scalar, Scalar),
20 MoveBy(f64, Scalar, Scalar),
24 RotateTo(f64, Scalar),
28 RotateBy(f64, Scalar),
32 ScaleTo(f64, Scalar, Scalar),
36 ScaleBy(f64, Scalar, Scalar),
40 FlipX(bool),
42 FlipY(bool),
44 Show,
46 Hide,
48 ToggleVisibility,
50 Blink(f64, usize),
52 FadeIn(f64),
56 FadeOut(f64),
60 FadeTo(f64, f64),
64 Ease(EaseFunction, Box<Animation>),
68}
69
70impl Animation {
71 pub fn to_state<I: ImageSize>(&self, sprite: &Sprite<I>) -> AnimationState {
73 use Animation::*;
74 use AnimationState as S;
75
76 match *self {
77 MoveTo(dur, dx, dy) => {
78 let (bx, by) = sprite.get_position();
79 S::Move(0.0, bx, by, dx - bx, dy - by, dur)
80 },
81 MoveBy(dur, cx, cy) => {
82 let (bx, by) = sprite.get_position();
83 S::Move(0.0, bx, by, cx, cy, dur)
84 },
85 RotateTo(dur, d) => {
86 let b = sprite.get_rotation();
87 S::Rotate(0.0, b, d - b, dur)
88 },
89 RotateBy(dur, c) => {
90 let b = sprite.get_rotation();
91 S::Rotate(0.0, b, c, dur)
92 },
93 ScaleTo(dur, dx, dy) => {
94 let (bx, by) = sprite.get_scale();
95 S::Scale(0.0, bx, by, dx - bx, dy - by, dur)
96 },
97 ScaleBy(dur, cx, cy) => {
98 let (bx, by) = sprite.get_scale();
99 S::Scale(0.0, bx, by, cx, cy, dur)
100 },
101 FlipX(flip_x) => {
102 let flip_y = sprite.get_flip_y();
103 S::Flip(flip_x, flip_y)
104 },
105 FlipY(flip_y) => {
106 let flip_x = sprite.get_flip_x();
107 S::Flip(flip_x, flip_y)
108 },
109 Show => {
110 S::Visibility(true)
111 },
112 Hide => {
113 S::Visibility(false)
114 },
115 ToggleVisibility => {
116 let visible = sprite.get_visible();
117 S::Visibility(!visible)
118 },
119 Blink(dur, times) => {
120 S::Blink(0.0, dur, 0, 2 * times)
121 },
122 FadeIn(dur) => {
123 let b = sprite.get_opacity() as f64;
124 S::Fade(0.0, b, 1.0 - b, dur)
125 },
126 FadeOut(dur) => {
127 let b = sprite.get_opacity() as f64;
128 S::Fade(0.0, b, 0.0 - b, dur)
129 },
130 FadeTo(dur, d) => {
131 let b = sprite.get_opacity() as f64;
132 S::Fade(0.0, b, d - b, dur)
133 },
134 Ease(f, ref animation) => {
135 S::Ease(f, Box::new(animation.to_state(sprite)))
136 },
137 }
138 }
139}
140
141#[derive(Clone)]
143pub enum AnimationState {
144 Move(f64, Scalar, Scalar, Scalar, Scalar, f64),
146 Rotate(f64, Scalar, Scalar, f64),
148 Scale(f64, Scalar, Scalar, Scalar, Scalar, f64),
150 Flip(bool, bool),
152 Visibility(bool),
154 Blink(f64, f64, usize, usize),
156 Fade(f64, f64, f64, f64),
158 Ease(EaseFunction, Box<AnimationState>),
160}
161
162impl AnimationState {
163 pub fn update<I: ImageSize>(
165 &self,
166 sprite: &mut Sprite<I>,
167 dt: f64
168 ) -> (Option<AnimationState>, Status, f64) {
169 use AnimationState::*;
170
171 match *self {
172 Move(t, bx, by, cx, cy, d) => {
173 let factor = (t + dt) / d;
174 update_position(sprite, factor, t + dt, bx, by, cx, cy, d)
175 },
176 Rotate(t, b, c, d) => {
177 let factor = (t + dt) / d;
178 update_rotation(sprite, factor, t + dt, b, c, d)
179 },
180 Scale(t, bx, by, cx, cy, d) => {
181 let factor = (t + dt) / d;
182 update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
183 },
184 Flip(flip_x, flip_y) => {
185 sprite.set_flip_x(flip_x);
186 sprite.set_flip_y(flip_y);
187 (None, Success, dt)
188 },
189 Visibility(visible) => {
190 sprite.set_visible(visible);
191 (None, Success, dt)
192 },
193 Blink(past, dur, cur, total) => {
194 let period = dur / total as f64;
195 if past + dt >= (cur + 1) as f64 * period {
196 let visible = sprite.get_visible();
197 sprite.set_visible(!visible);
198 if past + dt >= dur {
199 (None, Success, past + dt - dur)
200 } else {
201 (Some(Blink(past + dt, dur, cur + 1, total)),
202 Running, 0.0)
203 }
204 } else {
205 (Some(Blink(past + dt, dur, cur, total)),
206 Running, 0.0)
207 }
208 },
209 Fade(t, b, c, d) => {
210 let factor = (t + dt) / d;
211 update_opacity(sprite, factor, t + dt, b, c, d)
212 },
213 Ease(f, ref state) => {
214 let mut support_ease = true;
215 let (state, status, remain) = match **state {
216 Move(t, bx, by, cx, cy, d) => {
217 let factor = ::interpolation::Ease::calc((t + dt) / d, f);
218 update_position(sprite, factor, t + dt,
219 bx, by, cx, cy, d)
220 },
221 Rotate(t, b, c, d) => {
222 let factor = ::interpolation::Ease::calc((t + dt) / d, f);
223 update_rotation(sprite, factor, t + dt, b, c, d)
224 },
225 Scale(t, bx, by, cx, cy, d) => {
226 let factor = ::interpolation::Ease::calc((t + dt) / d, f);
227 update_scale(sprite, factor, t + dt, bx, by, cx, cy, d)
228 },
229 Fade(t, b, c, d) => {
230 let factor = ::interpolation::Ease::calc((t + dt) / d, f);
231 update_opacity(sprite, factor, t + dt, b, c, d)
232 },
233 _ => {
234 support_ease = false;
235 state.update(sprite, dt)
236 }
237 };
238
239 if !support_ease {
240 return (state, status, remain);
241 }
242
243 if let Some(state) = state {
244 (Some(AnimationState::Ease(f, Box::new(state))),
245 status, remain)
246 } else {
247 (None, status, remain)
248 }
249 },
250 }
251 }
252}
253
254fn update_position<I: ImageSize>(
255 sprite: &mut Sprite<I>,
256 factor: f64,
257 t: f64,
258 bx: f64,
259 by: f64,
260 cx: f64,
261 cy: f64,
262 d: f64
263) -> (Option<AnimationState>, Status, f64) {
264 if t >= d {
265 sprite.set_position(bx + cx, by + cy);
266 (None, Success, t - d)
267 } else {
268 sprite.set_position(bx + cx * factor, by + cy * factor);
269 (Some(AnimationState::Move(t, bx, by, cx, cy, d)),
270 Running, 0.0)
271 }
272}
273
274fn update_rotation<I: ImageSize>(
275 sprite: &mut Sprite<I>,
276 factor: f64,
277 t: f64,
278 b: f64,
279 c: f64,
280 d: f64
281) -> (Option<AnimationState>, Status, f64) {
282 if t >= d {
283 sprite.set_rotation(b + c);
284 (None, Success, t - d)
285 } else {
286 sprite.set_rotation(b + c * factor);
287 (Some(AnimationState::Rotate(t, b, c, d)),
288 Running, 0.0)
289 }
290}
291
292fn update_scale<I: ImageSize>(
293 sprite: &mut Sprite<I>,
294 factor: f64,
295 t: f64,
296 bx: f64,
297 by: f64,
298 cx: f64,
299 cy: f64,
300 d: f64
301) -> (Option<AnimationState>, Status, f64) {
302 if t >= d {
303 sprite.set_scale(bx + cx, by + cy);
304 (None, Success, t - d)
305 } else {
306 sprite.set_scale(bx + cx * factor, by + cy * factor);
307 (Some(AnimationState::Scale(t, bx, by, cx, cy, d)),
308 Running, 0.0)
309 }
310}
311
312fn update_opacity<I: ImageSize>(
313 sprite: &mut Sprite<I>,
314 factor: f64,
315 t: f64,
316 b: f64,
317 c: f64,
318 d: f64
319) -> (Option<AnimationState>, Status, f64) {
320 if t >= d {
321 sprite.set_opacity((b + c) as f32);
322 (None, Success, t - d)
323 } else {
324 sprite.set_opacity((b + c * factor) as f32);
325 (Some(AnimationState::Fade(t, b, c, d)),
326 Running, 0.0)
327 }
328}