The Battle for Wesnoth  1.19.1+dev
animated.tpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2005 - 2024
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_ + (starting_frame_time_ - start_time);
82  cycles_ = cycles;
83  current_frame_key_ = 0;
84  force_next_update_ = !frames_.empty();
85 }
86 
87 template<typename T>
88 inline void animated<T>::update_last_draw_time(double acceleration)
89 {
90  if(acceleration > 0 && acceleration_ != acceleration) {
91  int tmp = tick_to_time(last_update_tick_);
92  acceleration_ = acceleration;
93  start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - tmp) / acceleration_);
94  }
95 
96  if(!started_ && start_tick_ != 0) {
97  // animation is paused
98  start_tick_ += get_current_animation_tick() - last_update_tick_;
99  }
100 
101  last_update_tick_ = get_current_animation_tick();
102  if(force_next_update_) {
103  force_next_update_ = false;
104  return;
105  }
106 
107  if(does_not_change_) {
108  return;
109  }
110 
111  // Always update last_update_tick_, for the animation_time functions to work.
112  if(!started_) {
113  return;
114  }
115 
116  if(frames_.empty()) {
117  does_not_change_ = true;
118  return;
119  }
120 
121  if(cycles_) {
122  while(get_animation_time() > get_end_time()) { // cut extra time
123  start_tick_ += std::max<int>(static_cast<int>(get_animation_duration() / acceleration_), 1);
124  current_frame_key_ = 0;
125  }
126  }
127 
128  const int current_frame_end_time = get_current_frame_end_time();
129  // catch up && don't go after the end
130  if(current_frame_end_time < get_animation_time() && current_frame_end_time < get_end_time()) {
131  current_frame_key_++;
132  }
133 }
134 
135 template<typename T>
136 inline bool animated<T>::need_update() const
137 {
138  if(force_next_update_) {
139  return true;
140  }
141 
142  if(does_not_change_) {
143  return false;
144  }
145 
146  if(frames_.empty()) {
147  return false;
148  }
149 
150  if(!started_ && start_tick_ == 0) {
151  return false;
152  }
153 
154  if(get_current_animation_tick() > static_cast<int>(get_current_frame_end_time() / acceleration_ + start_tick_)) {
155  return true;
156  }
157 
158  return false;
159 }
160 
161 template<typename T>
162 inline bool animated<T>::animation_finished_potential() const
163 {
164  if(frames_.empty()) {
165  return true;
166  }
167 
168  if(!started_ && start_tick_ == 0) {
169  return true;
170  }
171 
172  if(cycles_) {
173  return true;
174  }
175 
176  if(tick_to_time(get_current_animation_tick()) > get_end_time()) {
177  return true;
178  }
179 
180  return false;
181 }
182 
183 template<typename T>
184 inline bool animated<T>::animation_finished() const
185 {
186  if(frames_.empty()) {
187  return true;
188  }
189 
190  if(!started_ && start_tick_ == 0) {
191  return true;
192  }
193 
194  if(cycles_) {
195  return true;
196  }
197 
198  if(get_animation_time() > get_end_time()) {
199  return true;
200  }
201 
202  return false;
203 }
204 
205 template<typename T>
206 inline int animated<T>::get_animation_time_potential() const
207 {
208  if(!started_ && start_tick_ == 0) {
209  return starting_frame_time_;
210  }
211 
212  return tick_to_time(get_current_animation_tick());
213 }
214 
215 template<typename T>
216 inline int animated<T>::get_animation_time() const
217 {
218  if(!started_ && start_tick_ == 0) {
219  return starting_frame_time_;
220  }
221 
222  int time = tick_to_time(last_update_tick_);
223  if(time > max_animation_time_ && max_animation_time_ > 0) {
224  return max_animation_time_;
225  }
226  return time;
227 }
228 
229 template<typename T>
230 inline void animated<T>::set_animation_time(int time)
231 {
232  start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - time) / acceleration_);
233 
234  current_frame_key_ = 0;
235  force_next_update_ = true;
236 }
237 
238 template<typename T>
239 inline void animated<T>::set_max_animation_time(int time)
240 {
241  max_animation_time_ = time;
242 }
243 
244 template<typename T>
245 inline int animated<T>::get_animation_duration() const
246 {
247  return get_end_time() - get_begin_time();
248 }
249 
250 template<typename T>
251 inline const T& animated<T>::get_current_frame() const
252 {
253  if(frames_.empty()) {
254  return void_value_;
255  }
256 
257  return frames_[current_frame_key_].value_;
258 }
259 
260 template<typename T>
261 inline int animated<T>::get_current_frame_begin_time() const
262 {
263  if(frames_.empty()) {
264  return starting_frame_time_;
265  }
266 
267  return frames_[current_frame_key_].start_time_;
268 }
269 
270 template<typename T>
271 inline int animated<T>::get_current_frame_end_time() const
272 {
273  if(frames_.empty()) {
274  return starting_frame_time_;
275  }
276 
277  return get_current_frame_begin_time() + get_current_frame_duration();
278 }
279 
280 template<typename T>
281 inline int animated<T>::get_current_frame_duration() const
282 {
283  if(frames_.empty()) {
284  return 0;
285  }
286 
287  return frames_[current_frame_key_].duration_;
288 }
289 
290 template<typename T>
291 inline int animated<T>::get_current_frame_time() const
292 {
293  if(frames_.empty()) {
294  return 0;
295  }
296 
297  // FIXME: get_animation_time() use acceleration but get_current_frame_begin_time() doesn't ?
298  return std::max<int>(0, get_animation_time() - get_current_frame_begin_time());
299 }
300 
301 template<typename T>
302 inline const T& animated<T>::get_first_frame() const
303 {
304  if(frames_.empty()) {
305  return void_value_;
306  }
307 
308  return frames_[0].value_;
309 }
310 
311 template<typename T>
312 inline const T& animated<T>::get_frame(std::size_t n) const
313 {
314  if(n >= frames_.size()) {
315  return void_value_;
316  }
317 
318  return frames_[n].value_;
319 }
320 
321 template<typename T>
322 inline const T& animated<T>::get_last_frame() const
323 {
324  if(frames_.empty()) {
325  return void_value_;
326  }
327 
328  return frames_.back().value_;
329 }
330 
331 template<typename T>
332 inline std::size_t animated<T>::get_frames_count() const
333 {
334  return frames_.size();
335 }
336 
337 template<typename T>
338 inline int animated<T>::get_begin_time() const
339 {
340  return starting_frame_time_;
341 }
342 
343 template<typename T>
344 inline int animated<T>::time_to_tick(int animation_time) const
345 {
346  if(!started_ && start_tick_ == 0) {
347  return 0;
348  }
349 
350  return start_tick_ + static_cast<int>((animation_time - starting_frame_time_) / acceleration_);
351 }
352 
353 template<typename T>
354 inline int animated<T>::tick_to_time(int animation_tick) const
355 {
356  if(!started_ && start_tick_ == 0) {
357  return 0;
358  }
359 
360  return static_cast<int>((static_cast<double>(animation_tick - start_tick_) * acceleration_) + starting_frame_time_);
361 }
362 
363 template<typename T>
364 inline int animated<T>::get_end_time() const
365 {
366  if(frames_.empty()) {
367  return starting_frame_time_;
368  }
369 
370  return frames_.back().start_time_ + frames_.back().duration_;
371 }
372 
373 template<typename T>
374 void animated<T>::remove_frames_until(int new_starting_time)
375 {
376  while(starting_frame_time_ < new_starting_time && !frames_.empty()) {
377  starting_frame_time_ += frames_[0].duration_;
378  frames_.erase(frames_.begin());
379  }
380 }
381 
382 template<typename T>
383 inline void animated<T>::set_end_time(int new_ending_time)
384 {
385  int last_start_time = starting_frame_time_;
386  auto current_frame = frames_.cbegin();
387  while(last_start_time < new_ending_time && current_frame != frames_.cend()) {
388  last_start_time += current_frame->duration_;
389  ++current_frame;
390  }
391 
392  // at this point last_start_time is set to the beginning of the first frame past the end
393  // or set to frames_.end()
394  frames_.erase(current_frame, frames_.end());
395  frames_.back().duration_ += new_ending_time - last_start_time;
396 }
397 
398 template<typename T>
399 inline void animated<T>::set_begin_time(int new_begin_time)
400 {
401  const int variation = new_begin_time - starting_frame_time_;
402  starting_frame_time_ += variation;
403  for(auto& frame : frames_) {
404  frame.start_time_ += variation;
405  }
406 }