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