The Battle for Wesnoth  1.15.1+dev
animated.tpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 by Philippe Plantier <ayin@anathas.org>
3  Copyright (C) 2005 - 2016 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License version 2
8  or at your option any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file animated.tpp
17  * Templates related to animations.
18  */
19 
20 template<typename T>
21 const T animated<T>::void_value_ = T();
22 
23 template<typename T>
24 inline animated<T>::animated(int start_time)
25  : starting_frame_time_(start_time)
26  , does_not_change_(true)
27  , started_(false)
28  , force_next_update_(false)
29  , frames_()
30  , max_animation_time_(0)
31  , start_tick_(0)
32  , cycles_(false)
33  , acceleration_(1)
34  , last_update_tick_(0)
35  , current_frame_key_(0)
36 {
37 }
38 
39 template<typename T>
40 inline animated<T>::animated(const std::vector<std::pair<int, T>>& cfg, int start_time, bool force_change)
41  : starting_frame_time_(start_time)
42  , does_not_change_(true)
43  , started_(false)
44  , force_next_update_(false)
45  , frames_()
46  , max_animation_time_(0)
47  , start_tick_(0)
48  , cycles_(false)
49  , acceleration_(1)
50  , last_update_tick_(0)
51  , current_frame_key_(0)
52 
53 {
54  for(const auto& config_pair : cfg) {
55  add_frame(config_pair.first, config_pair.second, force_change);
56  }
57 }
58 
59 template<typename T>
60 inline void animated<T>::add_frame(int duration, const T& value, bool force_change)
61 {
62  // NOTE: We cannot use emplace_back here, because the value may be a reference into the same vector,
63  // which case emplace_back could invalidate it before the new frame is constructed.
64  if(frames_.empty()) {
65  does_not_change_ = !force_change;
66  frames_.push_back(frame(duration, value, starting_frame_time_));
67  } else {
68  does_not_change_ = false;
69  frames_.push_back(frame(duration, value, frames_.back().start_time_ + frames_.back().duration_));
70  }
71 }
72 
73 template<typename T>
74 inline void animated<T>::start_animation(int start_time, bool cycles)
75 {
76  started_ = true;
77  last_update_tick_ = get_current_animation_tick();
78  acceleration_ = 1.0; // assume acceleration is 1, this will be fixed at first update_last_draw_time
79  start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - start_time) / acceleration_);
80 
81  cycles_ = cycles;
82  if(acceleration_ <= 0) {
83  acceleration_ = 1;
84  }
85  current_frame_key_ = 0;
86  force_next_update_ = !frames_.empty();
87 }
88 
89 template<typename T>
90 inline void animated<T>::update_last_draw_time(double acceleration)
91 {
92  if(acceleration > 0 && acceleration_ != acceleration) {
93  int tmp = tick_to_time(last_update_tick_);
94  acceleration_ = acceleration;
95  start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - tmp) / acceleration_);
96  }
97 
98  if(!started_ && start_tick_ != 0) {
99  // animation is paused
100  start_tick_ += get_current_animation_tick() - last_update_tick_;
101  }
102 
103  last_update_tick_ = get_current_animation_tick();
104  if(force_next_update_) {
105  force_next_update_ = false;
106  return;
107  }
108 
109  if(does_not_change_) {
110  return;
111  }
112 
113  // Always update last_update_tick_, for the animation_time functions to work.
114  if(!started_) {
115  return;
116  }
117 
118  if(frames_.empty()) {
119  does_not_change_ = true;
120  return;
121  }
122 
123  if(cycles_) {
124  while(get_animation_time() > get_end_time()) { // cut extra time
125  start_tick_ += std::max<int>(static_cast<int>(get_animation_duration() / acceleration_), 1);
126  current_frame_key_ = 0;
127  }
128  }
129 
130  if(get_current_frame_end_time() < get_animation_time() && // catch up
131  get_current_frame_end_time() < get_end_time()) { // don't go after the end
132  current_frame_key_++;
133  }
134 }
135 
136 template<typename T>
137 inline bool animated<T>::need_update() const
138 {
139  if(force_next_update_) {
140  return true;
141  }
142 
143  if(does_not_change_) {
144  return false;
145  }
146 
147  if(frames_.empty()) {
148  return false;
149  }
150 
151  if(!started_ && start_tick_ == 0) {
152  return false;
153  }
154 
155  if(get_current_animation_tick() > static_cast<int>(get_current_frame_end_time() / acceleration_ + start_tick_)) {
156  return true;
157  }
158 
159  return false;
160 }
161 
162 template<typename T>
163 inline bool animated<T>::animation_finished_potential() const
164 {
165  if(frames_.empty()) {
166  return true;
167  }
168 
169  if(!started_ && start_tick_ == 0) {
170  return true;
171  }
172 
173  if(cycles_) {
174  return true;
175  }
176 
177  if(tick_to_time(get_current_animation_tick()) > get_end_time()) {
178  return true;
179  }
180 
181  return false;
182 }
183 
184 template<typename T>
185 inline bool animated<T>::animation_finished() const
186 {
187  if(frames_.empty()) {
188  return true;
189  }
190 
191  if(!started_ && start_tick_ == 0) {
192  return true;
193  }
194 
195  if(cycles_) {
196  return true;
197  }
198 
199  if(get_animation_time() > get_end_time()) {
200  return true;
201  }
202 
203  return false;
204 }
205 
206 template<typename T>
207 inline int animated<T>::get_animation_time_potential() const
208 {
209  if(!started_ && start_tick_ == 0) {
210  return starting_frame_time_;
211  }
212 
213  return tick_to_time(get_current_animation_tick());
214 }
215 
216 template<typename T>
217 inline int animated<T>::get_animation_time() const
218 {
219  if(!started_ && start_tick_ == 0) {
220  return starting_frame_time_;
221  }
222 
223  int time = tick_to_time(last_update_tick_);
224  if (time > max_animation_time_ && max_animation_time_ > 0) {
225  return max_animation_time_;
226  }
227  return time;
228 }
229 
230 template<typename T>
231 inline void animated<T>::set_animation_time(int time)
232 {
233  start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - time) / acceleration_);
234 
235  current_frame_key_ = 0;
236  force_next_update_ = true;
237 }
238 
239 template<typename T>
240 inline void animated<T>::set_max_animation_time(int time)
241 {
242  max_animation_time_ = time;
243 }
244 
245 template<typename T>
246 inline int animated<T>::get_animation_duration() const
247 {
248  return get_end_time() - get_begin_time();
249 }
250 
251 template<typename T>
252 inline const T& animated<T>::get_current_frame() const
253 {
254  if(frames_.empty()) {
255  return void_value_;
256  }
257 
258  return frames_[current_frame_key_].value_;
259 }
260 
261 template<typename T>
262 inline int animated<T>::get_current_frame_begin_time() const
263 {
264  if(frames_.empty()) {
265  return starting_frame_time_;
266  }
267 
268  return frames_[current_frame_key_].start_time_;
269 }
270 
271 template<typename T>
272 inline int animated<T>::get_current_frame_end_time() const
273 {
274  if(frames_.empty()) {
275  return starting_frame_time_;
276  }
277 
278  return get_current_frame_begin_time() + get_current_frame_duration();
279 }
280 
281 template<typename T>
282 inline int animated<T>::get_current_frame_duration() const
283 {
284  if(frames_.empty()) {
285  return 0;
286  }
287 
288  return frames_[current_frame_key_].duration_;
289 }
290 
291 template<typename T>
292 inline int animated<T>::get_current_frame_time() const
293 {
294  if(frames_.empty()) {
295  return 0;
296  }
297 
298  // FIXME: get_animation_time() use acceleration but get_current_frame_begin_time() doesn't ?
299  return std::max<int>(0, get_animation_time() - get_current_frame_begin_time());
300 }
301 
302 template<typename T>
303 inline const T& animated<T>::get_first_frame() const
304 {
305  if(frames_.empty()) {
306  return void_value_;
307  }
308 
309  return frames_[0].value_;
310 }
311 
312 template<typename T>
313 inline const T& animated<T>::get_frame(std::size_t n) const
314 {
315  if(n >= frames_.size()) {
316  return void_value_;
317  }
318 
319  return frames_[n].value_;
320 }
321 
322 template<typename T>
323 inline const T& animated<T>::get_last_frame() const
324 {
325  if(frames_.empty()) {
326  return void_value_;
327  }
328 
329  return frames_.back().value_;
330 }
331 
332 template<typename T>
333 inline std::size_t animated<T>::get_frames_count() const
334 {
335  return frames_.size();
336 }
337 
338 template<typename T>
339 inline int animated<T>::get_begin_time() const
340 {
341  return starting_frame_time_;
342 }
343 
344 template<typename T>
345 inline int animated<T>::time_to_tick(int animation_time) const
346 {
347  if(!started_ && start_tick_ == 0) {
348  return 0;
349  }
350 
351  return start_tick_ + static_cast<int>((animation_time - starting_frame_time_) / acceleration_);
352 }
353 
354 template<typename T>
355 inline int animated<T>::tick_to_time(int animation_tick) const
356 {
357  if(!started_ && start_tick_ == 0) {
358  return 0;
359  }
360 
361  return static_cast<int>((static_cast<double>(animation_tick - start_tick_) * acceleration_) + starting_frame_time_);
362 }
363 
364 template<typename T>
365 inline int animated<T>::get_end_time() const
366 {
367  if(frames_.empty()) {
368  return starting_frame_time_;
369  }
370 
371  return frames_.back().start_time_ + frames_.back().duration_;
372 }
373 
374 template<typename T>
375 void animated<T>::remove_frames_until(int new_starting_time)
376 {
377  while(starting_frame_time_ < new_starting_time && !frames_.empty()) {
378  starting_frame_time_ += frames_[0].duration_;
379  frames_.erase(frames_.begin());
380  }
381 }
382 
383 template<typename T>
384 inline void animated<T>::set_end_time(int new_ending_time)
385 {
386  int last_start_time = starting_frame_time_;
387  typename std::vector<frame>::iterator current_frame = frames_.begin();
388  while(last_start_time < new_ending_time && current_frame != frames_.end()) {
389  last_start_time += current_frame->duration_;
390  ++current_frame;
391  }
392 
393  // at this point last_start_time is set to the beginning of the first frame past the end
394  // or set to frames_.end()
395  frames_.erase(current_frame, frames_.end());
396  frames_.back().duration_ += new_ending_time - last_start_time;
397 }
398 
399 template<typename T>
400 inline void animated<T>::set_begin_time(int new_begin_time)
401 {
402  const int variation = new_begin_time - starting_frame_time_;
403  starting_frame_time_ += variation;
404  for(auto& frame : frames_) {
405  frame.start_time_ += variation;
406  }
407 }