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/
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.
14 See the COPYING file for more details.
19 * Templates related to animations.
23 const T animated<T>::void_value_ = T();
26 inline animated<T>::animated(int start_time)
27 : starting_frame_time_(start_time)
28 , does_not_change_(true)
30 , force_next_update_(false)
32 , max_animation_time_(0)
36 , last_update_tick_(0)
37 , current_frame_key_(0)
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)
46 , force_next_update_(false)
48 , max_animation_time_(0)
52 , last_update_tick_(0)
53 , current_frame_key_(0)
56 for(const auto& config_pair : cfg) {
57 add_frame(config_pair.first, config_pair.second, force_change);
62 inline void animated<T>::add_frame(int duration, const T& value, bool force_change)
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.
67 does_not_change_ = !force_change;
68 frames_.push_back(frame(duration, value, starting_frame_time_));
70 does_not_change_ = false;
71 frames_.push_back(frame(duration, value, frames_.back().start_time_ + frames_.back().duration_));
76 inline void animated<T>::start_animation(int start_time, bool cycles)
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);
83 current_frame_key_ = 0;
84 force_next_update_ = !frames_.empty();
88 inline void animated<T>::update_last_draw_time(double acceleration)
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_);
96 if(!started_ && start_tick_ != 0) {
97 // animation is paused
98 start_tick_ += get_current_animation_tick() - last_update_tick_;
101 last_update_tick_ = get_current_animation_tick();
102 if(force_next_update_) {
103 force_next_update_ = false;
107 if(does_not_change_) {
111 // Always update last_update_tick_, for the animation_time functions to work.
116 if(frames_.empty()) {
117 does_not_change_ = true;
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;
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_++;
136 inline bool animated<T>::need_update() const
138 if(force_next_update_) {
142 if(does_not_change_) {
146 if(frames_.empty()) {
150 if(!started_ && start_tick_ == 0) {
154 if(get_current_animation_tick() > static_cast<int>(get_current_frame_end_time() / acceleration_ + start_tick_)) {
162 inline bool animated<T>::animation_finished_potential() const
164 if(frames_.empty()) {
168 if(!started_ && start_tick_ == 0) {
176 if(tick_to_time(get_current_animation_tick()) > get_end_time()) {
184 inline bool animated<T>::animation_finished() const
186 if(frames_.empty()) {
190 if(!started_ && start_tick_ == 0) {
198 if(get_animation_time() > get_end_time()) {
206 inline int animated<T>::get_animation_time_potential() const
208 if(!started_ && start_tick_ == 0) {
209 return starting_frame_time_;
212 return tick_to_time(get_current_animation_tick());
216 inline int animated<T>::get_animation_time() const
218 if(!started_ && start_tick_ == 0) {
219 return starting_frame_time_;
222 int time = tick_to_time(last_update_tick_);
223 if(time > max_animation_time_ && max_animation_time_ > 0) {
224 return max_animation_time_;
230 inline void animated<T>::set_animation_time(int time)
232 start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - time) / acceleration_);
234 current_frame_key_ = 0;
235 force_next_update_ = true;
239 inline void animated<T>::set_max_animation_time(int time)
241 max_animation_time_ = time;
245 inline int animated<T>::get_animation_duration() const
247 return get_end_time() - get_begin_time();
251 inline const T& animated<T>::get_current_frame() const
253 if(frames_.empty()) {
257 return frames_[current_frame_key_].value_;
261 inline int animated<T>::get_current_frame_begin_time() const
263 if(frames_.empty()) {
264 return starting_frame_time_;
267 return frames_[current_frame_key_].start_time_;
271 inline int animated<T>::get_current_frame_end_time() const
273 if(frames_.empty()) {
274 return starting_frame_time_;
277 return get_current_frame_begin_time() + get_current_frame_duration();
281 inline int animated<T>::get_current_frame_duration() const
283 if(frames_.empty()) {
287 return frames_[current_frame_key_].duration_;
291 inline int animated<T>::get_current_frame_time() const
293 if(frames_.empty()) {
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());
302 inline const T& animated<T>::get_first_frame() const
304 if(frames_.empty()) {
308 return frames_[0].value_;
312 inline const T& animated<T>::get_frame(std::size_t n) const
314 if(n >= frames_.size()) {
318 return frames_[n].value_;
322 inline const T& animated<T>::get_last_frame() const
324 if(frames_.empty()) {
328 return frames_.back().value_;
332 inline std::size_t animated<T>::get_frames_count() const
334 return frames_.size();
338 inline int animated<T>::get_begin_time() const
340 return starting_frame_time_;
344 inline int animated<T>::time_to_tick(int animation_time) const
346 if(!started_ && start_tick_ == 0) {
350 return start_tick_ + static_cast<int>((animation_time - starting_frame_time_) / acceleration_);
354 inline int animated<T>::tick_to_time(int animation_tick) const
356 if(!started_ && start_tick_ == 0) {
360 return static_cast<int>((static_cast<double>(animation_tick - start_tick_) * acceleration_) + starting_frame_time_);
364 inline int animated<T>::get_end_time() const
366 if(frames_.empty()) {
367 return starting_frame_time_;
370 return frames_.back().start_time_ + frames_.back().duration_;
374 void animated<T>::remove_frames_until(int new_starting_time)
376 while(starting_frame_time_ < new_starting_time && !frames_.empty()) {
377 starting_frame_time_ += frames_[0].duration_;
378 frames_.erase(frames_.begin());
383 inline void animated<T>::set_end_time(int new_ending_time)
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_;
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;
399 inline void animated<T>::set_begin_time(int new_begin_time)
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;