Convert animation code to use chrono types

This commit is contained in:
Charles Dang 2024-11-11 16:40:22 -05:00
parent 690ff375b7
commit b9bcdd3b63
16 changed files with 340 additions and 310 deletions

View file

@ -20,20 +20,18 @@
#include "animated.hpp"
#include <SDL2/SDL_timer.h>
// Put these here to ensure that there's only
// one instance of the current_ticks variable
namespace {
int current_ticks = 0;
std::chrono::steady_clock::time_point current_ticks;
}
void new_animation_frame()
{
current_ticks = SDL_GetTicks();
current_ticks = std::chrono::steady_clock::now();
}
int get_current_animation_tick()
std::chrono::steady_clock::time_point get_current_animation_tick()
{
return current_ticks;
}

View file

@ -20,25 +20,28 @@
#pragma once
#include <chrono>
#include <vector>
void new_animation_frame();
int get_current_animation_tick();
std::chrono::steady_clock::time_point get_current_animation_tick();
template<typename T>
class animated
{
public:
typedef std::pair<int, T> frame_description;
typedef std::pair<std::chrono::milliseconds, T> frame_description;
typedef std::vector<frame_description> anim_description;
animated(int start_time = 0);
explicit animated(const std::vector<frame_description>& cfg, int start_time = 0, bool force_change = false);
animated(const std::chrono::milliseconds& start_time = std::chrono::milliseconds{0});
explicit animated(const anim_description& cfg, const std::chrono::milliseconds& start_time = std::chrono::milliseconds{0}, bool force_change = false);
virtual ~animated() = default;
/** Adds a frame to an animation. */
void add_frame(int duration, const T& value, bool force_change = false);
void add_frame(const std::chrono::milliseconds& duration, const T& value, bool force_change = false);
bool not_started() const;
/**
* Starts an animation cycle.
@ -46,7 +49,7 @@ public:
* The first frame of the animation to start may be set to any value by
* using a start_time different to 0.
*/
void start_animation(int start_time, bool cycles = false);
void start_animation(const std::chrono::milliseconds& start_time, bool cycles = false);
void pause_animation()
{
started_ = false;
@ -54,16 +57,17 @@ public:
void restart_animation()
{
if(start_tick_)
if(start_tick_ != std::chrono::steady_clock::time_point{}) {
started_ = true;
}
}
int get_begin_time() const;
int get_end_time() const;
void set_begin_time(int new_begin_time);
std::chrono::milliseconds get_begin_time() const;
std::chrono::milliseconds get_end_time() const;
void set_begin_time(const std::chrono::milliseconds& new_begin_time);
int time_to_tick(int animation_time) const;
int tick_to_time(int animation_tick) const;
std::chrono::steady_clock::time_point time_to_tick(const std::chrono::milliseconds& animation_time) const;
std::chrono::milliseconds tick_to_time(const std::chrono::steady_clock::time_point& animation_tick) const;
void update_last_draw_time(double acceleration = 0);
bool need_update() const;
@ -76,17 +80,17 @@ public:
/** Returns true if the current animation was finished. */
bool animation_finished() const;
bool animation_finished_potential() const;
int get_animation_time() const;
int get_animation_time_potential() const;
void set_animation_time(int time);
void set_max_animation_time(int time);
std::chrono::milliseconds get_animation_time() const;
std::chrono::milliseconds get_animation_time_potential() const;
void set_animation_time(const std::chrono::milliseconds& time);
void set_max_animation_time(const std::chrono::milliseconds& time);
int get_animation_duration() const;
std::chrono::milliseconds get_animation_duration() const;
const T& get_current_frame() const;
int get_current_frame_begin_time() const;
int get_current_frame_end_time() const;
int get_current_frame_duration() const;
int get_current_frame_time() const;
std::chrono::milliseconds get_current_frame_begin_time() const;
std::chrono::milliseconds get_current_frame_end_time() const;
std::chrono::milliseconds get_current_frame_duration() const;
std::chrono::milliseconds get_current_frame_time() const;
const T& get_first_frame() const;
const T& get_frame(std::size_t n) const;
const T& get_last_frame() const;
@ -107,25 +111,18 @@ public:
protected:
friend class unit_animation;
void remove_frames_until(int starting_time);
void set_end_time(int ending_time);
void remove_frames_until(const std::chrono::milliseconds& starting_time);
void set_end_time(const std::chrono::milliseconds& ending_time);
int starting_frame_time_;
std::chrono::milliseconds starting_frame_time_;
private:
struct frame
{
frame(int duration, const T& value, int start_time)
: duration_(duration)
, value_(value)
, start_time_(start_time)
{
}
// Represents the timestamp of the frame start
int duration_;
std::chrono::milliseconds duration_;
T value_;
int start_time_;
std::chrono::milliseconds start_time_;
};
bool does_not_change_; // Optimization for 1-frame permanent animations
@ -135,13 +132,13 @@ private:
// Can set a maximum animation time so that movement in particular does not exceed potential time
// Ignored if has a value of 0
int max_animation_time_;
std::chrono::milliseconds max_animation_time_;
// These are only valid when anim is started
int start_tick_; // time at which we started
std::chrono::steady_clock::time_point start_tick_; // time at which we started
bool cycles_;
double acceleration_;
int last_update_tick_;
std::chrono::steady_clock::time_point last_update_tick_;
int current_frame_key_;
};

View file

@ -23,57 +23,56 @@ template<typename T>
const T animated<T>::void_value_ = T();
template<typename T>
inline animated<T>::animated(int start_time)
inline animated<T>::animated(const std::chrono::milliseconds& start_time)
: starting_frame_time_(start_time)
, does_not_change_(true)
, started_(false)
, force_next_update_(false)
, frames_()
, max_animation_time_(0)
, start_tick_(0)
, start_tick_()
, cycles_(false)
, acceleration_(1)
, last_update_tick_(0)
, last_update_tick_()
, current_frame_key_(0)
{
}
template<typename T>
inline animated<T>::animated(const std::vector<std::pair<int, T>>& cfg, int start_time, bool force_change)
inline animated<T>::animated(const animated<T>::anim_description& cfg, const std::chrono::milliseconds& start_time, bool force_change)
: starting_frame_time_(start_time)
, does_not_change_(true)
, started_(false)
, force_next_update_(false)
, frames_()
, max_animation_time_(0)
, start_tick_(0)
, start_tick_()
, cycles_(false)
, acceleration_(1)
, last_update_tick_(0)
, last_update_tick_()
, current_frame_key_(0)
{
for(const auto& config_pair : cfg) {
add_frame(config_pair.first, config_pair.second, force_change);
for(const auto& [duration, value] : cfg) {
add_frame(duration, value, force_change);
}
}
template<typename T>
inline void animated<T>::add_frame(int duration, const T& value, bool force_change)
inline void animated<T>::add_frame(const std::chrono::milliseconds& duration, const T& value, bool force_change)
{
// NOTE: We cannot use emplace_back here, because the value may be a reference into the same vector,
// which case emplace_back could invalidate it before the new frame is constructed.
if(frames_.empty()) {
does_not_change_ = !force_change;
frames_.push_back(frame(duration, value, starting_frame_time_));
frames_.push_back(frame{duration, value, starting_frame_time_});
} else {
does_not_change_ = false;
frames_.push_back(frame(duration, value, frames_.back().start_time_ + frames_.back().duration_));
frames_.push_back(frame{duration, value, frames_.back().start_time_ + frames_.back().duration_});
}
}
template<typename T>
inline void animated<T>::start_animation(int start_time, bool cycles)
inline void animated<T>::start_animation(const std::chrono::milliseconds& start_time, bool cycles)
{
started_ = true;
last_update_tick_ = get_current_animation_tick();
@ -88,12 +87,12 @@ template<typename T>
inline void animated<T>::update_last_draw_time(double acceleration)
{
if(acceleration > 0 && acceleration_ != acceleration) {
int tmp = tick_to_time(last_update_tick_);
auto tmp = tick_to_time(last_update_tick_);
acceleration_ = acceleration;
start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - tmp) / acceleration_);
start_tick_ = last_update_tick_ + std::chrono::duration_cast<std::chrono::milliseconds>((starting_frame_time_ - tmp) / acceleration_);
}
if(!started_ && start_tick_ != 0) {
if(!started_ && start_tick_ != std::chrono::steady_clock::time_point{}) {
// animation is paused
start_tick_ += get_current_animation_tick() - last_update_tick_;
}
@ -120,18 +119,24 @@ inline void animated<T>::update_last_draw_time(double acceleration)
if(cycles_) {
while(get_animation_time() > get_end_time()) { // cut extra time
start_tick_ += std::max<int>(static_cast<int>(get_animation_duration() / acceleration_), 1);
start_tick_ += std::max(std::chrono::floor<std::chrono::milliseconds>(get_animation_duration() / acceleration_), std::chrono::milliseconds{1});
current_frame_key_ = 0;
}
}
const int current_frame_end_time = get_current_frame_end_time();
const auto current_frame_end_time = get_current_frame_end_time();
// catch up && don't go after the end
if(current_frame_end_time < get_animation_time() && current_frame_end_time < get_end_time()) {
current_frame_key_++;
}
}
template<typename T>
inline bool animated<T>::not_started() const
{
return !started_ && start_tick_ == std::chrono::steady_clock::time_point{};
}
template<typename T>
inline bool animated<T>::need_update() const
{
@ -147,11 +152,11 @@ inline bool animated<T>::need_update() const
return false;
}
if(!started_ && start_tick_ == 0) {
if(not_started()) {
return false;
}
if(get_current_animation_tick() > static_cast<int>(get_current_frame_end_time() / acceleration_ + start_tick_)) {
if(get_current_animation_tick() > std::chrono::floor<std::chrono::milliseconds>(get_current_frame_end_time() / acceleration_ + start_tick_)) {
return true;
}
@ -165,7 +170,7 @@ inline bool animated<T>::animation_finished_potential() const
return true;
}
if(!started_ && start_tick_ == 0) {
if(not_started()) {
return true;
}
@ -187,7 +192,7 @@ inline bool animated<T>::animation_finished() const
return true;
}
if(!started_ && start_tick_ == 0) {
if(not_started()) {
return true;
}
@ -203,9 +208,9 @@ inline bool animated<T>::animation_finished() const
}
template<typename T>
inline int animated<T>::get_animation_time_potential() const
inline std::chrono::milliseconds animated<T>::get_animation_time_potential() const
{
if(!started_ && start_tick_ == 0) {
if(not_started()) {
return starting_frame_time_;
}
@ -213,36 +218,36 @@ inline int animated<T>::get_animation_time_potential() const
}
template<typename T>
inline int animated<T>::get_animation_time() const
inline std::chrono::milliseconds animated<T>::get_animation_time() const
{
if(!started_ && start_tick_ == 0) {
if(not_started()) {
return starting_frame_time_;
}
int time = tick_to_time(last_update_tick_);
if(time > max_animation_time_ && max_animation_time_ > 0) {
auto time = tick_to_time(last_update_tick_);
if(time > max_animation_time_ && max_animation_time_ > std::chrono::milliseconds{0}) {
return max_animation_time_;
}
return time;
}
template<typename T>
inline void animated<T>::set_animation_time(int time)
inline void animated<T>::set_animation_time(const std::chrono::milliseconds& time)
{
start_tick_ = last_update_tick_ + static_cast<int>((starting_frame_time_ - time) / acceleration_);
start_tick_ = last_update_tick_ + std::chrono::floor<std::chrono::milliseconds>((starting_frame_time_ - time) / acceleration_);
current_frame_key_ = 0;
force_next_update_ = true;
}
template<typename T>
inline void animated<T>::set_max_animation_time(int time)
inline void animated<T>::set_max_animation_time(const std::chrono::milliseconds& time)
{
max_animation_time_ = time;
}
template<typename T>
inline int animated<T>::get_animation_duration() const
inline std::chrono::milliseconds animated<T>::get_animation_duration() const
{
return get_end_time() - get_begin_time();
}
@ -258,7 +263,7 @@ inline const T& animated<T>::get_current_frame() const
}
template<typename T>
inline int animated<T>::get_current_frame_begin_time() const
inline std::chrono::milliseconds animated<T>::get_current_frame_begin_time() const
{
if(frames_.empty()) {
return starting_frame_time_;
@ -268,7 +273,7 @@ inline int animated<T>::get_current_frame_begin_time() const
}
template<typename T>
inline int animated<T>::get_current_frame_end_time() const
inline std::chrono::milliseconds animated<T>::get_current_frame_end_time() const
{
if(frames_.empty()) {
return starting_frame_time_;
@ -278,24 +283,24 @@ inline int animated<T>::get_current_frame_end_time() const
}
template<typename T>
inline int animated<T>::get_current_frame_duration() const
inline std::chrono::milliseconds animated<T>::get_current_frame_duration() const
{
if(frames_.empty()) {
return 0;
return std::chrono::milliseconds{0};
}
return frames_[current_frame_key_].duration_;
}
template<typename T>
inline int animated<T>::get_current_frame_time() const
inline std::chrono::milliseconds animated<T>::get_current_frame_time() const
{
if(frames_.empty()) {
return 0;
return std::chrono::milliseconds{0};
}
// FIXME: get_animation_time() use acceleration but get_current_frame_begin_time() doesn't ?
return std::max<int>(0, get_animation_time() - get_current_frame_begin_time());
return std::max(std::chrono::milliseconds{0}, get_animation_time() - get_current_frame_begin_time());
}
template<typename T>
@ -335,33 +340,33 @@ inline std::size_t animated<T>::get_frames_count() const
}
template<typename T>
inline int animated<T>::get_begin_time() const
inline std::chrono::milliseconds animated<T>::get_begin_time() const
{
return starting_frame_time_;
}
template<typename T>
inline int animated<T>::time_to_tick(int animation_time) const
inline std::chrono::steady_clock::time_point animated<T>::time_to_tick(const std::chrono::milliseconds& animation_time) const
{
if(!started_ && start_tick_ == 0) {
return 0;
if(not_started()) {
return {};
}
return start_tick_ + static_cast<int>((animation_time - starting_frame_time_) / acceleration_);
return start_tick_ + std::chrono::floor<std::chrono::milliseconds>((animation_time - starting_frame_time_) / acceleration_);
}
template<typename T>
inline int animated<T>::tick_to_time(int animation_tick) const
inline std::chrono::milliseconds animated<T>::tick_to_time(const std::chrono::steady_clock::time_point& animation_tick) const
{
if(!started_ && start_tick_ == 0) {
return 0;
if(not_started()) {
return std::chrono::milliseconds{0};
}
return static_cast<int>((static_cast<double>(animation_tick - start_tick_) * acceleration_) + starting_frame_time_);
return starting_frame_time_ + std::chrono::floor<std::chrono::milliseconds>((animation_tick - start_tick_) * acceleration_);
}
template<typename T>
inline int animated<T>::get_end_time() const
inline std::chrono::milliseconds animated<T>::get_end_time() const
{
if(frames_.empty()) {
return starting_frame_time_;
@ -371,7 +376,7 @@ inline int animated<T>::get_end_time() const
}
template<typename T>
void animated<T>::remove_frames_until(int new_starting_time)
void animated<T>::remove_frames_until(const std::chrono::milliseconds& new_starting_time)
{
while(starting_frame_time_ < new_starting_time && !frames_.empty()) {
starting_frame_time_ += frames_[0].duration_;
@ -380,9 +385,9 @@ void animated<T>::remove_frames_until(int new_starting_time)
}
template<typename T>
inline void animated<T>::set_end_time(int new_ending_time)
inline void animated<T>::set_end_time(const std::chrono::milliseconds& new_ending_time)
{
int last_start_time = starting_frame_time_;
auto last_start_time = starting_frame_time_;
auto current_frame = frames_.cbegin();
while(last_start_time < new_ending_time && current_frame != frames_.cend()) {
last_start_time += current_frame->duration_;
@ -396,9 +401,9 @@ inline void animated<T>::set_end_time(int new_ending_time)
}
template<typename T>
inline void animated<T>::set_begin_time(int new_begin_time)
inline void animated<T>::set_begin_time(const std::chrono::milliseconds& new_begin_time)
{
const int variation = new_begin_time - starting_frame_time_;
const auto variation = new_begin_time - starting_frame_time_;
starting_frame_time_ += variation;
for(auto& frame : frames_) {
frame.start_time_ += variation;

View file

@ -287,12 +287,12 @@ void display::reinit_flags_for_team(const team& t)
for(const std::string& item : items) {
const std::vector<std::string>& sub_items = utils::split(item, ':');
std::string str = item;
int time = 100;
auto time = 100ms;
if(sub_items.size() > 1) {
str = sub_items.front();
try {
time = std::max<int>(1, std::stoi(sub_items.back()));
time = std::max(1ms, std::chrono::milliseconds{std::stoi(sub_items.back())});
} catch(const std::invalid_argument&) {
ERR_DP << "Invalid time value found when constructing flag for side " << t.side() << ": " << sub_items.back();
}
@ -307,8 +307,9 @@ void display::reinit_flags_for_team(const team& t)
animated<image::locator>& f = flags_[t.side() - 1];
f = temp_anim;
auto time = f.get_end_time();
if (time > 0) {
f.start_animation(randomness::rng::default_instance().get_random_int(0, time-1), true);
if (time > 0ms) {
int start_time = randomness::rng::default_instance().get_random_int(0, time.count() - 1);
f.start_animation(std::chrono::milliseconds{start_time}, true);
} else {
// this can happen if both flag and game_config::images::flag are empty.
ERR_DP << "missing flag for side " << t.side();

View file

@ -161,7 +161,7 @@ halo_impl::effect::effect(int xpos, int ypos,
set_location(xpos, ypos);
images_.start_animation(0, infinite);
images_.start_animation(std::chrono::milliseconds{0}, infinite);
update();
}

View file

@ -33,6 +33,8 @@ static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine)
#define WRN_NG LOG_STREAM(warn, log_engine)
using namespace std::chrono_literals;
/**
*
* These legacy map_location functions moved here from map_location.?pp.
@ -142,12 +144,13 @@ void terrain_builder::tile::rebuild_cache(const std::string& tod, logs* log)
img_list.push_back(anim);
assert(anim.get_animation_duration() != 0);
assert(anim.get_animation_duration() != 0ms);
if(variant.random_start < 0)
img_list.back().set_animation_time(ri.rand % img_list.back().get_animation_duration());
else if(variant.random_start > 0)
img_list.back().set_animation_time(ri.rand % variant.random_start);
if(variant.random_start < 0ms) {
img_list.back().set_animation_time(std::chrono::milliseconds{ri.rand} % img_list.back().get_animation_duration());
} else if(variant.random_start > 0ms) {
img_list.back().set_animation_time(std::chrono::milliseconds{ri.rand} % variant.random_start);
}
if(!animate) {
img_list.back().pause_animation();
@ -352,8 +355,8 @@ void terrain_builder::rebuild_terrain(const map_location& loc)
if(!filename.empty()) {
animated<image::locator> img_loc;
img_loc.add_frame(100, image::locator("terrain/" + filename + ".png"));
img_loc.start_animation(0, true);
img_loc.add_frame(100ms, image::locator("terrain/" + filename + ".png"));
img_loc.start_animation(0ms, true);
btile.images_background.push_back(img_loc);
}
@ -363,8 +366,8 @@ void terrain_builder::rebuild_terrain(const map_location& loc)
if(!filename_ovl.empty()) {
animated<image::locator> img_loc_ovl;
img_loc_ovl.add_frame(100, image::locator("terrain/" + filename_ovl + ".png"));
img_loc_ovl.start_animation(0, true);
img_loc_ovl.add_frame(100ms, image::locator("terrain/" + filename_ovl + ".png"));
img_loc_ovl.start_animation(0ms, true);
btile.images_background.push_back(img_loc_ovl);
}
}
@ -448,10 +451,10 @@ bool terrain_builder::load_images(building_rule& rule)
const std::string modif = (has_tilde ? str.substr(tilde + 1) : "");
int time = 100;
auto time = 100ms;
if(items.size() > 1) {
try {
time = std::stoi(items.back());
time = std::chrono::milliseconds{std::stoi(items.back())};
} catch(const std::invalid_argument&) {
ERR_NG << "Invalid 'time' value in terrain image builder: " << items.back();
}
@ -465,7 +468,7 @@ bool terrain_builder::load_images(building_rule& rule)
if(res.get_frames_count() == 0)
break; // no valid images, don't register it
res.start_animation(0, true);
res.start_animation(0ms, true);
variant.images.push_back(std::move(res));
}
if(variant.images.empty())
@ -644,7 +647,7 @@ void terrain_builder::rotate_rule(building_rule& ret, int angle, const std::vect
terrain_builder::rule_image_variant::rule_image_variant(const std::string& image_string,
const std::string& variations,
int random_start)
const std::chrono::milliseconds& random_start)
: image_string(image_string)
, variations(variations)
, images()
@ -658,7 +661,7 @@ terrain_builder::rule_image_variant::rule_image_variant(const std::string& image
const std::string& variations,
const std::string& tod,
const std::string& has_flag,
int random_start)
const std::chrono::milliseconds& random_start)
: image_string(image_string)
, variations(variations)
, images()
@ -720,7 +723,7 @@ void terrain_builder::add_images_from_config(rule_imagelist& images, const confi
// If an integer is given then assign that, but if a bool is given, then assign -1 if true and 0 if false
int random_start = variant["random_start"].to_bool(true) ? variant["random_start"].to_int(-1) : 0;
images.back().variants.emplace_back(name, variations, tod, has_flag, random_start);
images.back().variants.emplace_back(name, variations, tod, has_flag, std::chrono::milliseconds{random_start});
}
// Adds the main (default) variant of the image at the end,
@ -730,7 +733,7 @@ void terrain_builder::add_images_from_config(rule_imagelist& images, const confi
int random_start = img["random_start"].to_bool(true) ? img["random_start"].to_int(-1) : 0;
images.back().variants.emplace_back(name, variations, random_start);
images.back().variants.emplace_back(name, variations, std::chrono::milliseconds{random_start});
}
}

View file

@ -165,14 +165,16 @@ public:
struct rule_image_variant
{
/** Constructor for the normal default case */
rule_image_variant(const std::string& image_string, const std::string& variations, int random_start = -1);
rule_image_variant(const std::string& image_string,
const std::string& variations,
const std::chrono::milliseconds& random_start = std::chrono::milliseconds{-1});
/** Constructor for true [variant] cases */
rule_image_variant(const std::string& image_string,
const std::string& variations,
const std::string& tod,
const std::string& has_flag,
int random_start = -1);
const std::chrono::milliseconds& random_start = std::chrono::milliseconds{-1});
/** A string representing either the filename for an image, or
* a list of images, with an optional timing for each image.
@ -211,7 +213,7 @@ public:
/** Specify the allowed amount of random shift (in milliseconds) applied
* to the animation start time, -1 for shifting without limitation.*/
int random_start;
std::chrono::milliseconds random_start;
};
/**

View file

@ -22,12 +22,16 @@
#include "play_controller.hpp"
#include "random.hpp"
#include "resources.hpp"
#include "serialization/chrono.hpp"
#include "units/animation_component.hpp"
#include "units/filter.hpp"
#include "units/unit.hpp"
#include "variable.hpp"
#include <algorithm>
#include <thread>
using namespace std::chrono_literals;
static std::string get_heal_sound(const config& cfg)
{
@ -246,7 +250,7 @@ static animation_branches prepare_animation(const config& cfg, const std::string
return expanded_animations;
}
unit_animation::unit_animation(int start_time,
unit_animation::unit_animation(const std::chrono::milliseconds& start_time,
const unit_frame& frame, const std::string& event, const int variation, const frame_builder& builder)
: terrain_types_()
, unit_filter_()
@ -493,12 +497,12 @@ void unit_animation::fill_initial_animations(std::vector<unit_animation>& animat
const std::string default_image = cfg["image"];
if(animation_base.empty()) {
animation_base.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "", unit_animation::DEFAULT_ANIM));
animation_base.push_back(unit_animation(0ms, frame_builder().image(default_image).duration(1ms), "", unit_animation::DEFAULT_ANIM));
}
animations.push_back(unit_animation(0, frame_builder().image(default_image).duration(1), "_disabled_", 0));
animations.push_back(unit_animation(0,
frame_builder().image(default_image).duration(300).blend("0.0~0.3:100,0.3~0.0:200", {255,255,255}),
animations.push_back(unit_animation(0ms, frame_builder().image(default_image).duration(1ms), "_disabled_", 0));
animations.push_back(unit_animation(0ms,
frame_builder().image(default_image).duration(300ms).blend("0.0~0.3:100,0.3~0.0:200", {255,255,255}),
"_disabled_selected_", 0));
for(const auto& base : animation_base) {
@ -508,44 +512,44 @@ void unit_animation::fill_initial_animations(std::vector<unit_animation>& animat
animations.push_back(base);
animations.back().event_ = { "_ghosted_" };
animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),particle::UNSET,"0.9", "", {0,0,0}, "", "", "~GS()");
animations.back().unit_anim_.override(0ms, animations.back().unit_anim_.get_animation_duration(),particle::UNSET,"0.9", "", {0,0,0}, "", "", "~GS()");
animations.push_back(base);
animations.back().event_ = { "_disabled_ghosted_" };
animations.back().unit_anim_.override(0, 1, particle::UNSET, "0.4", "", {0,0,0}, "", "", "~GS()");
animations.back().unit_anim_.override(0ms, 1ms, particle::UNSET, "0.4", "", {0,0,0}, "", "", "~GS()");
animations.push_back(base);
animations.back().event_ = { "selected" };
animations.back().unit_anim_.override(0, 300, particle::UNSET, "", "0.0~0.3:100,0.3~0.0:200", {255,255,255});
animations.back().unit_anim_.override(0ms, 300ms, particle::UNSET, "", "0.0~0.3:100,0.3~0.0:200", {255,255,255});
animations.push_back(base);
animations.back().event_ = { "recruited" };
animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "0~1:600");
animations.back().unit_anim_.override(0ms, 600ms, particle::NO_CYCLE, "0~1:600");
animations.push_back(base);
animations.back().event_ = { "levelin" };
animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "1~0:600", {255,255,255});
animations.back().unit_anim_.override(0ms, 600ms, particle::NO_CYCLE, "", "1~0:600", {255,255,255});
animations.push_back(base);
animations.back().event_ = { "levelout" };
animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "", "0~1:600,1", {255,255,255});
animations.back().unit_anim_.override(0ms, 600ms, particle::NO_CYCLE, "", "0~1:600,1", {255,255,255});
animations.push_back(base);
animations.back().event_ = { "pre_movement" };
animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
animations.back().unit_anim_.override(0ms, 1ms, particle::NO_CYCLE);
animations.push_back(base);
animations.back().event_ = { "post_movement" };
animations.back().unit_anim_.override(0, 1, particle::NO_CYCLE);
animations.back().unit_anim_.override(0ms, 1ms, particle::NO_CYCLE);
animations.push_back(base);
animations.back().event_ = { "movement" };
animations.back().unit_anim_.override(0, 200,
animations.back().unit_anim_.override(0ms, 200ms,
particle::NO_CYCLE, "", "", {0,0,0}, "0~1:200", std::to_string(get_abs_frame_layer(drawing_layer::unit_move_default)));
animations.push_back(base);
animations.back().event_ = { "defend" };
animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(),
animations.back().unit_anim_.override(0ms, animations.back().unit_anim_.get_animation_duration(),
particle::NO_CYCLE, "", "0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0});
animations.back().hits_.push_back(strike_result::type::hit);
animations.back().hits_.push_back(strike_result::type::kill);
@ -555,30 +559,30 @@ void unit_animation::fill_initial_animations(std::vector<unit_animation>& animat
animations.push_back(base);
animations.back().event_ = { "attack" };
animations.back().unit_anim_.override(-150, 300, particle::NO_CYCLE, "", "", {0,0,0}, "0~0.6:150,0.6~0:150", std::to_string(get_abs_frame_layer(drawing_layer::unit_move_default)));
animations.back().unit_anim_.override(-150ms, 300ms, particle::NO_CYCLE, "", "", {0,0,0}, "0~0.6:150,0.6~0:150", std::to_string(get_abs_frame_layer(drawing_layer::unit_move_default)));
animations.back().primary_attack_filter_.emplace_back("range", "melee");
animations.push_back(base);
animations.back().event_ = { "attack" };
animations.back().unit_anim_.override(-150, 150, particle::NO_CYCLE);
animations.back().unit_anim_.override(-150ms, 150ms, particle::NO_CYCLE);
animations.back().primary_attack_filter_.emplace_back("range", "ranged");
animations.push_back(base);
animations.back().event_ = { "death" };
animations.back().unit_anim_.override(0, 600, particle::NO_CYCLE, "1~0:600");
animations.back().unit_anim_.override(0ms, 600ms, particle::NO_CYCLE, "1~0:600");
animations.back().sub_anims_["_death_sound"] = particle();
animations.back().sub_anims_["_death_sound"].add_frame(1, frame_builder().sound(cfg["die_sound"]), true);
animations.back().sub_anims_["_death_sound"].add_frame(1ms, frame_builder().sound(cfg["die_sound"]), true);
animations.push_back(base);
animations.back().event_ = { "victory" };
animations.back().unit_anim_.override(0, animations.back().unit_anim_.get_animation_duration(), particle::CYCLE);
animations.back().unit_anim_.override(0ms, animations.back().unit_anim_.get_animation_duration(), particle::CYCLE);
animations.push_back(base);
animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "1~0:150");
animations.back().unit_anim_.override(0ms, 150ms, particle::NO_CYCLE, "1~0:150");
animations.back().event_ = { "pre_teleport" };
animations.push_back(base);
animations.back().unit_anim_.override(0, 150, particle::NO_CYCLE, "0~1:150,1");
animations.back().unit_anim_.override(0ms, 150ms, particle::NO_CYCLE, "0~1:150,1");
animations.back().event_ = { "post_teleport" };
animations.push_back(base);
@ -586,17 +590,17 @@ void unit_animation::fill_initial_animations(std::vector<unit_animation>& animat
animations.push_back(base);
animations.back().event_ = { "healed" };
animations.back().unit_anim_.override(0, 300, particle::NO_CYCLE, "", "0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {255,255,255});
animations.back().unit_anim_.override(0ms, 300ms, particle::NO_CYCLE, "", "0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {255,255,255});
const std::string healed_sound = get_heal_sound(cfg);
animations.back().sub_anims_["_healed_sound"].add_frame(1, frame_builder().sound(healed_sound), true);
animations.back().sub_anims_["_healed_sound"].add_frame(1ms, frame_builder().sound(healed_sound), true);
animations.push_back(base);
animations.back().event_ = { "poisoned" };
animations.back().unit_anim_.override(0, 300, particle::NO_CYCLE, "", "0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {0,255,0});
animations.back().unit_anim_.override(0ms, 300ms, particle::NO_CYCLE, "", "0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30,0.5:30,0:30", {0,255,0});
animations.back().sub_anims_["_poison_sound"] = particle();
animations.back().sub_anims_["_poison_sound"].add_frame(1, frame_builder().sound(game_config::sounds::status::poisoned), true);
animations.back().sub_anims_["_poison_sound"].add_frame(1ms, frame_builder().sound(game_config::sounds::status::poisoned), true);
}
}
@ -715,7 +719,7 @@ void unit_animation::add_anims( std::vector<unit_animation> & animations, const
animations.back().sub_anims_["_healed_sound"] = particle();
const std::string healed_sound = get_heal_sound(cfg);
animations.back().sub_anims_["_healed_sound"].add_frame(1,frame_builder().sound(healed_sound),true);
animations.back().sub_anims_["_healed_sound"].add_frame(1ms,frame_builder().sound(healed_sound),true);
}
for(const animation_branch &ab : prepare_animation(cfg, "poison_anim")) {
@ -729,7 +733,7 @@ void unit_animation::add_anims( std::vector<unit_animation> & animations, const
animations.emplace_back(anim);
animations.back().sub_anims_["_poison_sound"] = particle();
animations.back().sub_anims_["_poison_sound"].add_frame(1,frame_builder().sound(game_config::sounds::status::poisoned),true);
animations.back().sub_anims_["_poison_sound"].add_frame(1ms,frame_builder().sound(game_config::sounds::status::poisoned),true);
}
add_simple_anim(animations, cfg, "pre_movement_anim", "pre_movement", drawing_layer::unit_move_default);
@ -773,9 +777,9 @@ void unit_animation::add_anims( std::vector<unit_animation> & animations, const
animations.back().base_score_--;
image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
animations.back().add_frame(225, frame_builder()
animations.back().add_frame(225ms, frame_builder()
.image(image_loc.get_filename()+image_loc.get_modifications())
.duration(225)
.duration(225ms)
.blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
} else {
for(const std::string& hit_type : utils::split(anim["hits"])) {
@ -786,9 +790,9 @@ void unit_animation::add_anims( std::vector<unit_animation> & animations, const
image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
if(hit_type == "yes" || hit_type == strike_result::hit || hit_type == strike_result::kill) {
animations.back().add_frame(225, frame_builder()
animations.back().add_frame(225ms, frame_builder()
.image(image_loc.get_filename() + image_loc.get_modifications())
.duration(225)
.duration(225ms)
.blend("0.0,0.5:75,0.0:75,0.5:75,0.0", {255,0,0}));
}
}
@ -841,14 +845,14 @@ void unit_animation::add_anims( std::vector<unit_animation> & animations, const
animations.emplace_back(anim);
image::locator image_loc = animations.back().get_last_frame().end_parameters().image;
animations.back().add_frame(600, frame_builder()
animations.back().add_frame(600ms, frame_builder()
.image(image_loc.get_filename()+image_loc.get_modifications())
.duration(600)
.duration(600ms)
.highlight("1~0:600"));
if(!cfg["die_sound"].empty()) {
animations.back().sub_anims_["_death_sound"] = particle();
animations.back().sub_anims_["_death_sound"].add_frame(1,frame_builder().sound(cfg["die_sound"]),true);
animations.back().sub_anims_["_death_sound"].add_frame(1ms,frame_builder().sound(cfg["die_sound"]),true);
}
}
@ -873,16 +877,16 @@ void unit_animation::add_anims( std::vector<unit_animation> & animations, const
anim["apply_to"] = "pre_teleport";
animations.emplace_back(anim);
animations.back().unit_anim_.set_end_time(0);
animations.back().unit_anim_.set_end_time(0ms);
anim["apply_to"] ="post_teleport";
animations.emplace_back(anim);
animations.back().unit_anim_.remove_frames_until(0);
animations.back().unit_anim_.remove_frames_until(0ms);
}
}
void unit_animation::particle::override(int start_time
, int duration
void unit_animation::particle::override(const std::chrono::milliseconds& start_time
, const std::chrono::milliseconds& duration
, const cycle_state cycles
, const std::string& highlight
, const std::string& blend_ratio
@ -928,15 +932,15 @@ unit_animation::particle::particle(const config& cfg, const std::string& frame_s
, last_frame_begin_time_(0)
, cycles_(false)
{
starting_frame_time_ = std::numeric_limits<int>::max();
starting_frame_time_ = std::chrono::milliseconds::max();
config::const_child_itors range = cfg.child_range(frame_string + "frame");
if(!range.empty() && cfg[frame_string + "start_time"].empty()) {
for(const config& frame : range) {
starting_frame_time_ = std::min(starting_frame_time_, frame["begin"].to_int());
starting_frame_time_ = std::min(starting_frame_time_, chrono::parse_duration<std::chrono::milliseconds>(frame["begin"]));
}
} else {
starting_frame_time_ = cfg[frame_string + "start_time"].to_int();
starting_frame_time_ = chrono::parse_duration<std::chrono::milliseconds>(cfg[frame_string + "start_time"]);
}
for(const config& frame : range) {
@ -1006,27 +1010,27 @@ void unit_animation::update_last_draw_time()
}
}
int unit_animation::get_end_time() const
std::chrono::milliseconds unit_animation::get_end_time() const
{
int result = unit_anim_.get_end_time();
auto result = unit_anim_.get_end_time();
for(const auto& anim : sub_anims_) {
result = std::max<int>(result, anim.second.get_end_time());
result = std::max(result, anim.second.get_end_time());
}
return result;
}
int unit_animation::get_begin_time() const
std::chrono::milliseconds unit_animation::get_begin_time() const
{
int result = unit_anim_.get_begin_time();
auto result = unit_anim_.get_begin_time();
for(const auto& anim : sub_anims_) {
result = std::min<int>(result, anim.second.get_begin_time());
result = std::min(result, anim.second.get_begin_time());
}
return result;
}
void unit_animation::start_animation(int start_time
void unit_animation::start_animation(const std::chrono::milliseconds& start_time
, const map_location& src
, const map_location& dst
, const std::string& text
@ -1041,8 +1045,8 @@ void unit_animation::start_animation(int start_time
if(!text.empty()) {
particle crude_build;
crude_build.add_frame(1, frame_builder());
crude_build.add_frame(1, frame_builder().text(text, text_color), true);
crude_build.add_frame(1ms, frame_builder());
crude_build.add_frame(1ms, frame_builder().text(text, text_color), true);
sub_anims_["_add_text"] = crude_build;
}
@ -1155,7 +1159,7 @@ std::ostream& operator<<(std::ostream& outstream, const unit_animation& u_animat
std::string events_string = utils::join(u_animation.event_);
outstream << "[" << events_string << "]\n";
outstream << "\tstart_time=" << u_animation.get_begin_time() << '\n';
outstream << "\tstart_time=" << u_animation.get_begin_time().count() << '\n';
if(u_animation.hits_.size() > 0) {
std::vector<std::string> hits;
@ -1225,7 +1229,7 @@ std::ostream& operator<<(std::ostream& outstream, const unit_animation& u_animat
std::size_t pos = sub_frame_name.find("_frame");
if(pos != std::string::npos) sub_frame_name = sub_frame_name.substr(0, pos);
outstream << "\t" << sub_frame_name << "_start_time=" << p.second.get_begin_time() << '\n';
outstream << "\t" << sub_frame_name << "_start_time=" << p.second.get_begin_time().count() << '\n';
outstream << "\t[" << p.first << "]\n";
for(const std::string& frame_string : p.second.get_frame(i).debug_strings()) {
@ -1243,7 +1247,7 @@ std::ostream& operator<<(std::ostream& outstream, const unit_animation& u_animat
void unit_animation::particle::redraw(const frame_parameters& value,const map_location& src, const map_location& dst, halo::manager& halo_man)
{
const unit_frame& current_frame = get_current_frame();
const int animation_time = get_animation_time();
const auto animation_time = get_animation_time();
const frame_parameters default_val = parameters_.parameters(animation_time - get_begin_time());
// Everything is relative to the first frame in an attack/defense/etc. block.
@ -1281,12 +1285,12 @@ unit_animation::particle::~particle()
halo_id_.reset();
}
void unit_animation::particle::start_animation(int start_time)
void unit_animation::particle::start_animation(const std::chrono::milliseconds& start_time)
{
halo_id_.reset();
parameters_.override(get_animation_duration());
animated<unit_frame>::start_animation(start_time,cycles_);
last_frame_begin_time_ = get_begin_time() -1;
last_frame_begin_time_ = get_begin_time() - 1ms;
}
void unit_animator::add_animation(unit_const_ptr animated_unit
@ -1308,7 +1312,7 @@ void unit_animator::add_animation(unit_const_ptr animated_unit
animated_unit->anim_comp().choose_animation(src, event, dst, value, hit_type, attack, second_attack, value2);
if(!anim) return;
start_time_ = std::max<int>(start_time_, anim->get_begin_time());
start_time_ = std::max(start_time_, anim->get_begin_time());
animated_units_.AGGREGATE_EMPLACE(std::move(animated_unit), anim, text, text_color, src, with_bars);
}
@ -1321,7 +1325,7 @@ void unit_animator::add_animation(unit_const_ptr animated_unit
{
if(!animated_unit || !anim) return;
start_time_ = std::max<int>(start_time_, anim->get_begin_time());
start_time_ = std::max(start_time_, anim->get_begin_time());
animated_units_.AGGREGATE_EMPLACE(std::move(animated_unit), anim, text, text_color, src, with_bars);
}
@ -1366,14 +1370,14 @@ void unit_animator::replace_anim_if_invalid(unit_const_ptr animated_unit
void unit_animator::start_animations()
{
int begin_time = std::numeric_limits<int>::max();
auto begin_time = std::chrono::milliseconds::max();
for(const auto& anim : animated_units_) {
if(anim.my_unit->anim_comp().get_animation()) {
if(anim.animation) {
begin_time = std::min<int>(begin_time, anim.animation->get_begin_time());
begin_time = std::min(begin_time, anim.animation->get_begin_time());
} else {
begin_time = std::min<int>(begin_time, anim.my_unit->anim_comp().get_animation()->get_begin_time());
begin_time = std::min(begin_time, anim.my_unit->anim_comp().get_animation()->get_begin_time());
}
}
}
@ -1398,7 +1402,7 @@ bool unit_animator::would_end() const
return finished;
}
void unit_animator::wait_until(int animation_time) const
void unit_animator::wait_until(const std::chrono::milliseconds& animation_time) const
{
if(animated_units_.empty()) {
return;
@ -1412,21 +1416,25 @@ void unit_animator::wait_until(int animation_time) const
resources::controller->play_slice(false);
int end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
while(SDL_GetTicks() < unsigned(end_tick - std::min(int(20 / speed), 20))) {
using std::chrono::steady_clock;
auto end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
while(steady_clock::now() < end_tick - std::min(std::chrono::floor<std::chrono::milliseconds>(20ms / speed), 20ms)) {
if(!game_config::no_delay) {
SDL_Delay(std::clamp(int((animation_time - get_animation_time()) * speed), 0, 10));
auto rest = std::chrono::floor<std::chrono::milliseconds>((animation_time - get_animation_time()) * speed);
std::this_thread::sleep_for(std::clamp(rest, 0ms, 10ms));
}
resources::controller->play_slice(false);
end_tick = animated_units_[0].my_unit->anim_comp().get_animation()->time_to_tick(animation_time);
}
if(!game_config::no_delay) {
SDL_Delay(std::max<int>(0, end_tick - SDL_GetTicks() + 5));
auto rest = std::max<steady_clock::duration>(0ms, end_tick - steady_clock::now() + 5ms);
std::this_thread::sleep_for(rest);
}
new_animation_frame();
animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(0);
animated_units_[0].my_unit->anim_comp().get_animation()->set_max_animation_time(0ms);
}
void unit_animator::wait_for_end() const
@ -1437,7 +1445,7 @@ void unit_animator::wait_for_end() const
while(!finished) {
resources::controller->play_slice(false);
SDL_Delay(10);
std::this_thread::sleep_for(10ms);
finished = true;
for(const auto& anim : animated_units_) {
@ -1446,28 +1454,28 @@ void unit_animator::wait_for_end() const
}
}
int unit_animator::get_animation_time() const
std::chrono::milliseconds unit_animator::get_animation_time() const
{
if(animated_units_.empty()) {
return 0;
return 0ms;
}
return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time() ;
}
int unit_animator::get_animation_time_potential() const
std::chrono::milliseconds unit_animator::get_animation_time_potential() const
{
if(animated_units_.empty()) {
return 0;
return 0ms;
}
return animated_units_[0].my_unit->anim_comp().get_animation()->get_animation_time_potential() ;
}
int unit_animator::get_end_time() const
std::chrono::milliseconds unit_animator::get_end_time() const
{
int end_time = std::numeric_limits<int>::min();
auto end_time = std::chrono::milliseconds::min();
for(const auto& anim : animated_units_) {
if(anim.my_unit->anim_comp().get_animation()) {
end_time = std::max<int>(end_time, anim.my_unit->anim_comp().get_animation()->get_end_time());
end_time = std::max(end_time, anim.my_unit->anim_comp().get_animation()->get_end_time());
}
}

View file

@ -23,7 +23,6 @@
#include "units/ptr.hpp"
#include "units/strike_result.hpp"
class unit_animation
{
public:
@ -44,7 +43,7 @@ public:
return unit_anim_.get_last_frame();
}
void add_frame(int duration, const unit_frame& value, bool force_change = false)
void add_frame(const std::chrono::milliseconds& duration, const unit_frame& value, bool force_change = false)
{
unit_anim_.add_frame(duration,value,force_change);
}
@ -59,30 +58,30 @@ public:
bool animation_finished() const;
bool animation_finished_potential() const;
void update_last_draw_time();
int get_begin_time() const;
int get_end_time() const;
std::chrono::milliseconds get_begin_time() const;
std::chrono::milliseconds get_end_time() const;
int time_to_tick(int animation_time) const
auto time_to_tick(const std::chrono::milliseconds& animation_time) const
{
return unit_anim_.time_to_tick(animation_time);
}
int get_animation_time() const
auto get_animation_time() const
{
return unit_anim_.get_animation_time();
}
void set_max_animation_time(int time)
void set_max_animation_time(const std::chrono::milliseconds& time)
{
unit_anim_.set_max_animation_time(time);
}
int get_animation_time_potential() const
auto get_animation_time_potential() const
{
return unit_anim_.get_animation_time_potential();
}
void start_animation(int start_time
void start_animation(const std::chrono::milliseconds& start_time
, const map_location& src = map_location::null_location()
, const map_location& dst = map_location::null_location()
, const std::string& text = ""
@ -92,10 +91,9 @@ public:
void update_parameters(const map_location& src, const map_location& dst);
void pause_animation();
void restart_animation();
int get_current_frame_begin_time() const
auto get_current_frame_begin_time() const
{
return unit_anim_.get_current_frame_begin_time();
}
void redraw(frame_parameters& value, halo::manager& halo_man);
void clear_haloes();
@ -114,7 +112,7 @@ protected:
}
private:
explicit unit_animation(int start_time
explicit unit_animation(const std::chrono::milliseconds& start_time
, const unit_frame &frame
, const std::string& event = ""
, const int variation=DEFAULT_ANIM
@ -123,7 +121,7 @@ private:
class particle : public animated<unit_frame>
{
public:
explicit particle(int start_time = 0, const frame_builder& builder = frame_builder())
explicit particle(const std::chrono::milliseconds& start_time = std::chrono::milliseconds{0}, const frame_builder& builder = frame_builder())
: animated<unit_frame>(start_time)
, accelerate(true)
, parameters_(builder)
@ -137,8 +135,8 @@ private:
bool need_update() const;
bool need_minimal_update() const;
enum cycle_state {UNSET, CYCLE, NO_CYCLE};
void override(int start_time
, int duration
void override(const std::chrono::milliseconds& start_time
, const std::chrono::milliseconds& duration
, const cycle_state cycles
, const std::string& highlight = ""
, const std::string& blend_ratio =""
@ -148,7 +146,7 @@ private:
, const std::string& modifiers = "");
void redraw(const frame_parameters& value, const map_location& src, const map_location& dst, halo::manager& halo_man);
std::set<map_location> get_overlaped_hex(const frame_parameters& value,const map_location& src, const map_location& dst);
void start_animation(int start_time);
void start_animation(const std::chrono::milliseconds& start_time);
frame_parameters parameters(const frame_parameters& default_val) const
{
return get_current_frame().merge_parameters(get_current_frame_time(), parameters_.parameters(get_animation_time() - get_begin_time()), default_val);
@ -160,7 +158,7 @@ private:
//animation params that can be locally overridden by frames
frame_parsed_parameters parameters_;
halo::handle halo_id_;
int last_frame_begin_time_;
std::chrono::milliseconds last_frame_begin_time_;
bool cycles_;
};
@ -250,18 +248,18 @@ public:
void clear()
{
start_time_ = std::numeric_limits<int>::min();
start_time_ = std::chrono::milliseconds::min();
animated_units_.clear();
}
void set_all_standing();
bool would_end() const;
int get_animation_time() const;
int get_animation_time_potential() const;
int get_end_time() const;
std::chrono::milliseconds get_animation_time() const;
std::chrono::milliseconds get_animation_time_potential() const;
std::chrono::milliseconds get_end_time() const;
void wait_for_end() const;
void wait_until( int animation_time) const;
void wait_until(const std::chrono::milliseconds& animation_time) const;
private:
struct anim_elem
@ -275,5 +273,5 @@ private:
};
std::vector<anim_elem> animated_units_;
int start_time_ = std::numeric_limits<int>::min();
std::chrono::milliseconds start_time_ = std::chrono::milliseconds::min();
};

View file

@ -25,17 +25,19 @@
#include <set>
using namespace std::chrono_literals;
namespace
{
int get_next_idle_time()
std::chrono::steady_clock::time_point get_next_idle_tick()
{
if(!prefs::get().idle_anim()) {
return std::numeric_limits<int>::max();
return std::chrono::steady_clock::time_point::max();
}
const double rate = std::pow(2.0, -prefs::get().idle_anim_rate() / 10.0);
return get_current_animation_tick()
+ static_cast<int>(randomness::rng::default_instance().get_random_int(20000, 39999) * rate);
const int duration = randomness::rng::default_instance().get_random_int(20000, 39999) * rate;
return get_current_animation_tick() + std::chrono::milliseconds{duration};
}
} // namespace
@ -66,45 +68,45 @@ const unit_animation* unit_animation_component::choose_animation(const map_locat
void unit_animation_component::set_standing(bool with_bars)
{
if (prefs::get().show_standing_animations()&& !u_.incapacitated()) {
start_animation(std::numeric_limits<int>::max(), choose_animation(u_.loc_, "standing"),
start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "standing"),
with_bars, "", {0,0,0}, STATE_STANDING);
} else {
start_animation(std::numeric_limits<int>::max(), choose_animation(u_.loc_, "_disabled_"),
start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_disabled_"),
with_bars, "", {0,0,0}, STATE_STANDING);
}
}
void unit_animation_component::set_ghosted(bool with_bars)
{
start_animation(std::numeric_limits<int>::max(), choose_animation(u_.loc_, "_ghosted_"),
start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_ghosted_"),
with_bars);
anim_->pause_animation();
}
void unit_animation_component::set_disabled_ghosted(bool with_bars)
{
start_animation(std::numeric_limits<int>::max(), choose_animation(u_.loc_, "_disabled_ghosted_"),
start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_disabled_ghosted_"),
with_bars);
}
void unit_animation_component::set_idling()
{
start_animation(std::numeric_limits<int>::max(), choose_animation(u_.loc_, "idling"),
start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "idling"),
true, "", {0,0,0}, STATE_FORGET);
}
void unit_animation_component::set_selecting()
{
if (prefs::get().show_standing_animations() && !u_.incapacitated()) {
start_animation(std::numeric_limits<int>::max(), choose_animation(u_.loc_, "selected"),
start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "selected"),
true, "", {0,0,0}, STATE_FORGET);
} else {
start_animation(std::numeric_limits<int>::max(), choose_animation(u_.loc_, "_disabled_selected_"),
start_animation(std::chrono::milliseconds::max(), choose_animation(u_.loc_, "_disabled_selected_"),
true, "", {0,0,0}, STATE_FORGET);
}
}
void unit_animation_component::start_animation (int start_time, const unit_animation *animation,
void unit_animation_component::start_animation(const std::chrono::milliseconds& start_time, const unit_animation *animation,
bool with_bars, const std::string &text, color_t text_color, STATE state)
{
if (!animation) {
@ -119,11 +121,11 @@ void unit_animation_component::start_animation (int start_time, const unit_anima
bool accelerate = (state != STATE_FORGET && state != STATE_STANDING);
draw_bars_ = with_bars;
anim_.reset(new unit_animation(*animation));
const int real_start_time = start_time == std::numeric_limits<int>::max() ? anim_->get_begin_time() : start_time;
const auto real_start_time = start_time == std::chrono::milliseconds::max() ? anim_->get_begin_time() : start_time;
anim_->start_animation(real_start_time, u_.loc_, u_.loc_.get_direction(u_.facing_),
text, text_color, accelerate);
frame_begin_time_ = anim_->get_begin_time() -1;
next_idling_ = get_next_idle_time();
frame_begin_time_ = anim_->get_begin_time() - 1ms;
next_idling_ = get_next_idle_tick();
}
void unit_animation_component::refresh()
@ -139,10 +141,10 @@ void unit_animation_component::refresh()
{
return;
}
if (get_current_animation_tick() > next_idling_ + 1000)
if (get_current_animation_tick() > next_idling_ + 1000ms)
{
// prevent all units animating at the same time
next_idling_ = get_next_idle_time();
next_idling_ = get_next_idle_tick();
} else {
set_idling();
}

View file

@ -39,7 +39,7 @@ public:
anim_(nullptr),
animations_(),
state_(STATE_STANDING),
next_idling_(0),
next_idling_(),
frame_begin_time_(0),
draw_bars_(false),
refreshing_(false),
@ -53,7 +53,7 @@ public:
anim_(nullptr),
animations_(o.animations_),
state_(o.state_),
next_idling_(0),
next_idling_(),
frame_begin_time_(o.frame_begin_time_),
draw_bars_(o.draw_bars_),
refreshing_(o.refreshing_),
@ -86,7 +86,7 @@ public:
void set_selecting();
/** Begin an animation. */
void start_animation (int start_time, const unit_animation *animation,
void start_animation(const std::chrono::milliseconds& start_time, const unit_animation *animation,
bool with_bars, const std::string &text = "",
color_t text_color = {}, STATE state = STATE_ANIM);
@ -126,9 +126,9 @@ private:
STATE state_;
/** time for next idle animation */
int next_idling_;
std::chrono::steady_clock::time_point next_idling_;
/** time for the frame to begin */
int frame_begin_time_;
std::chrono::milliseconds frame_begin_time_;
/** bool indicating whether to draw bars with the unit */
bool draw_bars_;

View file

@ -19,6 +19,7 @@
#include "draw.hpp"
#include "game_display.hpp"
#include "log.hpp"
#include "serialization/chrono.hpp"
#include "sound.hpp"
static lg::log_domain log_engine("engine");
@ -80,18 +81,20 @@ frame_builder::frame_builder(const config& cfg,const std::string& frame_string)
}
if(const config::attribute_value* v = cfg.get(frame_string + "duration")) {
duration(v->to_int());
duration(chrono::parse_duration<std::chrono::milliseconds>(*v));
} else if(!cfg.get(frame_string + "end")) {
int halo_duration = (progressive_string(halo_, 1)).duration();
int image_duration = (progressive_image(image_, 1)).duration();
int image_diagonal_duration = (progressive_image(image_diagonal_, 1)).duration();
auto halo_duration = progressive_string(halo_, 1ms).duration();
auto image_duration = progressive_image(image_, 1ms).duration();
auto image_diagonal_duration = progressive_image(image_diagonal_, 1ms).duration();
duration(std::max(std::max(image_duration, image_diagonal_duration), halo_duration));
} else {
duration(cfg[frame_string + "end"].to_int() - cfg[frame_string + "begin"].to_int());
auto t1 = chrono::parse_duration<std::chrono::milliseconds>(cfg[frame_string + "begin"]);
auto t2 = chrono::parse_duration<std::chrono::milliseconds>(cfg[frame_string + "end"]);
duration(t2 - t1);
}
duration_ = std::max(duration_, 1);
duration_ = std::max(duration_, 1ms);
const auto& blend_color_key = cfg[frame_string + "blend_color"];
if(!blend_color_key.empty()) {
@ -141,7 +144,7 @@ frame_builder& frame_builder::halo(const std::string& halo, const std::string& h
return *this;
}
frame_builder& frame_builder::duration(const int duration)
frame_builder& frame_builder::duration(const std::chrono::milliseconds& duration)
{
duration_ = duration;
return *this;
@ -220,8 +223,8 @@ frame_builder& frame_builder::drawing_layer(const std::string& drawing_layer)
return *this;
}
frame_parsed_parameters::frame_parsed_parameters(const frame_builder& builder, int duration)
: duration_(duration ? duration : builder.duration_)
frame_parsed_parameters::frame_parsed_parameters(const frame_builder& builder, const std::chrono::milliseconds& duration)
: duration_(duration > std::chrono::milliseconds{0} ? duration : builder.duration_)
, image_(builder.image_,duration_)
, image_diagonal_(builder.image_diagonal_,duration_)
, image_mod_(builder.image_mod_)
@ -271,7 +274,7 @@ bool frame_parsed_parameters::need_update() const
return !this->does_not_change();
}
frame_parameters frame_parsed_parameters::parameters(int current_time) const
frame_parameters frame_parsed_parameters::parameters(const std::chrono::milliseconds& current_time) const
{
#ifdef __cpp_designated_initializers
return {
@ -330,7 +333,7 @@ frame_parameters frame_parsed_parameters::parameters(int current_time) const
#endif
}
void frame_parsed_parameters::override(int duration,
void frame_parsed_parameters::override(const std::chrono::milliseconds& duration,
const std::string& highlight,
const std::string& blend_ratio,
color_t blend_color,
@ -386,8 +389,8 @@ std::vector<std::string> frame_parsed_parameters::debug_strings() const
{
std::vector<std::string> v;
if(duration_ > 0) {
v.emplace_back("duration=" + utils::half_signed_value(duration_));
if(duration_ > 0ms) {
v.emplace_back("duration=" + utils::half_signed_value(duration_.count()));
}
if(!image_.get_original().empty()) {
@ -624,7 +627,7 @@ void render_unit_image(
}
} // namespace
void unit_frame::redraw(const int frame_time, bool on_start_time, bool in_scope_of_frame,
void unit_frame::redraw(const std::chrono::milliseconds& frame_time, bool on_start_time, bool in_scope_of_frame,
const map_location& src, const map_location& dst,
halo::handle& halo_id, halo::manager& halo_man,
const frame_parameters& animation_val, const frame_parameters& engine_val) const
@ -790,7 +793,7 @@ void unit_frame::redraw(const int frame_time, bool on_start_time, bool in_scope_
}
}
std::set<map_location> unit_frame::get_overlaped_hex(const int frame_time, const map_location& src, const map_location& dst,
std::set<map_location> unit_frame::get_overlaped_hex(const std::chrono::milliseconds& frame_time, const map_location& src, const map_location& dst,
const frame_parameters& animation_val, const frame_parameters& engine_val) const
{
display* disp = display::get_singleton();
@ -911,7 +914,8 @@ std::set<map_location> unit_frame::get_overlaped_hex(const int frame_time, const
* There is no absolute rule for merging, so creativity is the rule. If a value is never provided by the engine, assert.
* This way if it becomes used, people will easily find the right place to look.
*/
frame_parameters unit_frame::merge_parameters(int current_time, const frame_parameters& animation_val,
frame_parameters unit_frame::merge_parameters(const std::chrono::milliseconds& current_time,
const frame_parameters& animation_val,
const frame_parameters& engine_val) const
{
frame_parameters result;
@ -971,7 +975,7 @@ frame_parameters unit_frame::merge_parameters(int current_time, const frame_para
result.halo_mod = current_val.halo_mod + animation_val.halo_mod;
result.halo_mod += engine_val.halo_mod;
assert(engine_val.duration == 0);
assert(engine_val.duration == 0ms);
result.duration = current_val.duration;
assert(engine_val.sound.empty());

View file

@ -30,6 +30,8 @@
#include <boost/logic/tribool.hpp>
#include <chrono>
class config;
constexpr int get_abs_frame_layer(drawing_layer layer)
@ -40,7 +42,7 @@ constexpr int get_abs_frame_layer(drawing_layer layer)
/** All parameters from a frame at a given instant */
struct frame_parameters
{
int duration = 0;
std::chrono::milliseconds duration{0};
image::locator image;
image::locator image_diagonal;
@ -85,7 +87,7 @@ public:
frame_builder(const config& cfg, const std::string& frame_string = "");
/** Allow easy chained modifications. Will raised assert if used after initialization */
frame_builder& duration(const int duration);
frame_builder& duration(const std::chrono::milliseconds& duration);
frame_builder& image(const std::string& image, const std::string& image_mod = "");
frame_builder& image_diagonal(const std::string& image_diagonal, const std::string& image_mod = "");
frame_builder& sound(const std::string& sound);
@ -107,7 +109,7 @@ public:
private:
friend class frame_parsed_parameters;
int duration_;
std::chrono::milliseconds duration_;
std::string image_;
std::string image_diagonal_;
@ -145,9 +147,10 @@ private:
class frame_parsed_parameters
{
public:
frame_parsed_parameters(const frame_builder& builder = frame_builder(), int override_duration = 0);
frame_parsed_parameters(const frame_builder& builder = frame_builder(),
const std::chrono::milliseconds& override_duration = std::chrono::milliseconds{0});
void override(int duration,
void override(const std::chrono::milliseconds& duration,
const std::string& highlight = "",
const std::string& blend_ratio = "",
color_t blend_color = {0,0,0},
@ -156,9 +159,9 @@ public:
const std::string& modifiers = "");
/** Getters for the different parameters */
frame_parameters parameters(int current_time) const;
frame_parameters parameters(const std::chrono::milliseconds& current_time) const;
int duration() const{ return duration_;}
const std::chrono::milliseconds& duration() const { return duration_; }
bool does_not_change() const;
bool need_update() const;
@ -166,7 +169,7 @@ public:
std::vector<std::string> debug_strings() const;
private:
int duration_;
std::chrono::milliseconds duration_;
progressive_image image_;
progressive_image image_diagonal_;
@ -207,13 +210,14 @@ public:
// Constructors
unit_frame(const frame_builder& builder = frame_builder()) : builder_(builder) {}
void redraw(const int frame_time, bool on_start_time, bool in_scope_of_frame, const map_location& src, const map_location& dst,
void redraw(const std::chrono::milliseconds& frame_time, bool on_start_time, bool in_scope_of_frame, const map_location& src, const map_location& dst,
halo::handle& halo_id, halo::manager& halo_man, const frame_parameters& animation_val, const frame_parameters& engine_val) const;
frame_parameters merge_parameters(int current_time, const frame_parameters& animation_val,
frame_parameters merge_parameters(const std::chrono::milliseconds& current_time,
const frame_parameters& animation_val,
const frame_parameters& engine_val = frame_parameters()) const;
frame_parameters parameters(int current_time) const
frame_parameters parameters(const std::chrono::milliseconds& current_time) const
{
return builder_.parameters(current_time);
}
@ -223,7 +227,7 @@ public:
return builder_.parameters(duration());
}
int duration() const
const std::chrono::milliseconds& duration() const
{
return builder_.duration();
}
@ -244,7 +248,7 @@ public:
return builder_.debug_strings();
}
std::set<map_location> get_overlaped_hex(const int frame_time, const map_location& src, const map_location& dst,
std::set<map_location> get_overlaped_hex(const std::chrono::milliseconds& frame_time, const map_location& src, const map_location& dst,
const frame_parameters& animation_val, const frame_parameters& engine_val) const;
private:

View file

@ -18,31 +18,33 @@
#include "lexical_cast.hpp"
#include "serialization/string_utils.hpp"
#include <chrono>
#include <vector>
namespace image { class locator; }
using namespace std::chrono_literals;
template<typename T, typename D>
class progressive_base
{
public:
using data_t = std::vector<std::pair<D, int>>;
using data_t = std::vector<std::pair<D, std::chrono::milliseconds>>;
progressive_base(const std::string& input)
: data_()
, input_(input)
{}
virtual const T get_current_element(int current_time, T default_val) const = 0;
virtual const T get_current_element(const std::chrono::milliseconds& current_time, T default_val) const = 0;
virtual bool does_not_change() const
{
return data_.size() <= 1;
}
int duration() const
std::chrono::milliseconds duration() const
{
int total = 0;
std::chrono::milliseconds total{0};
for(const auto& entry : data_) {
total += entry.second;
}
@ -76,7 +78,7 @@ template<typename T>
class progressive_pair : public progressive_base<T, std::pair<T, T>>
{
public:
progressive_pair(const std::string& input = "", int duration = 0)
progressive_pair(const std::string& input = "", const std::chrono::milliseconds& duration = std::chrono::milliseconds{0})
: progressive_base<T, std::pair<T, T>>(input)
{
auto& base_data = progressive_pair_base_type::data();
@ -84,14 +86,14 @@ public:
const int split_flag = utils::REMOVE_EMPTY; // useless to strip spaces
const std::vector<std::string> comma_split = utils::split(input, ',', split_flag);
const int time_chunk = std::max<int>(1, duration / std::max<int>(comma_split.size(), 1));
const auto time_chunk = std::max(1ms, duration / std::max<int>(comma_split.size(), 1));
for(const auto& entry : comma_split) {
std::vector<std::string> colon_split = utils::split(entry, ':', split_flag);
int time = 0;
auto time = 0ms;
try {
time = (colon_split.size() > 1) ? std::stoi(colon_split[1]) : time_chunk;
time = (colon_split.size() > 1) ? std::chrono::milliseconds{std::stoi(colon_split[1])} : time_chunk;
} catch(const std::invalid_argument&) {
//ERR_NG << "Invalid time in unit animation: " << colon_split[1];
}
@ -106,18 +108,18 @@ public:
}
}
virtual const T get_current_element(int current_time, T default_val = T()) const override
virtual const T get_current_element(const std::chrono::milliseconds& current_time, T default_val = T()) const override
{
const auto& base_data = progressive_pair_base_type::data();
const int& base_duration = progressive_pair_base_type::duration();
const std::chrono::milliseconds& base_duration = progressive_pair_base_type::duration();
if(base_data.empty()) {
return default_val;
}
int time = 0;
auto time = 0ms;
unsigned int i = 0;
const int searched_time = std::clamp(current_time, 0, base_duration);
const std::chrono::milliseconds searched_time = std::clamp(current_time, 0ms, base_duration);
while(time < searched_time && i < base_data.size()) {
time += base_data[i].second;
@ -129,12 +131,12 @@ public:
time -= base_data[i].second;
}
const T first = base_data[i].first.first;
const T second = base_data[i].first.second;
const auto [first, second] = base_data[i].first;
using fractional_milliseconds = std::chrono::duration<double, std::milli>;
return T((
static_cast<double>(searched_time - time) /
static_cast<double>(base_data[i].second)
fractional_milliseconds{searched_time - time} /
fractional_milliseconds{base_data[i].second}
) * (second - first) + first);
}
@ -152,47 +154,51 @@ template<typename T>
class progressive_single : public progressive_base<T, T>
{
public:
progressive_single(const std::string& input = "", int duration = 0)
progressive_single(const std::string& input = "", const std::chrono::milliseconds& duration = std::chrono::milliseconds{0})
: progressive_base<T, T>(input)
{
auto& base_data = progressive_single_base_type::data();
const std::vector<std::string> first_pass = utils::square_parenthetical_split(input);
int time_chunk = std::max<int>(duration, 1);
auto time_chunk = std::max(duration, 1ms);
if(duration > 1 && !first_pass.empty()) {
if(duration > 1ms && !first_pass.empty()) {
// If duration specified, divide evenly the time for items with unspecified times
int total_specified_time = 0;
auto total_specified_time = 0ms;
for(const std::string& fp_string : first_pass) {
std::vector<std::string> second_pass = utils::split(fp_string, ':');
if(second_pass.size() > 1) {
try {
total_specified_time += std::stoi(second_pass[1]);
total_specified_time += std::chrono::milliseconds{std::stoi(second_pass[1])};
} catch(const std::invalid_argument&) {
//ERR_NG << "Invalid time in unit animation: " << second_pass[1];
}
}
}
time_chunk = std::max<int>((duration - total_specified_time) / first_pass.size(), 1);
// Fun Fact: since size_t is unsigned, template argument deduction does not yield chrono::milliseconds,
// but rather duration<unsigned long long, std::milli>. That's why we explicitly cast to milliseconds.
std::chrono::milliseconds new_time_chunk{(duration - total_specified_time) / first_pass.size()};
time_chunk = std::max(new_time_chunk, 1ms);
}
for(const std::string& fp_string : first_pass) {
std::vector<std::string> second_pass = utils::split(fp_string, ':');
if(second_pass.size() > 1) {
try {
base_data.push_back({std::move(second_pass[0]), std::stoi(second_pass[1])});
std::chrono::milliseconds time{std::stoi(second_pass[1])};
base_data.emplace_back(std::move(second_pass[0]), time);
} catch(const std::invalid_argument&) {
//ERR_NG << "Invalid time in unit animation: " << second_pass[1];
}
} else {
base_data.push_back({std::move(second_pass[0]) ,time_chunk});
base_data.emplace_back(std::move(second_pass[0]), time_chunk);
}
}
}
virtual const T get_current_element(int current_time, T default_val = T()) const override
virtual const T get_current_element(const std::chrono::milliseconds& current_time, T default_val = T()) const override
{
const auto& base_data = progressive_single_base_type::data();
@ -200,7 +206,7 @@ public:
return default_val;
}
int time = 0;
auto time = 0ms;
unsigned int i = 0;
while(time < current_time && i < base_data.size()) {
@ -209,6 +215,8 @@ public:
}
// TODO: what is this for?
// ^ Seems that the value of i at this point is the first index whose time is *greater* than current_time,
// so the index needs to be adjusted to give the first whose time is less (or equal).
if(i) {
i--;
}

View file

@ -124,9 +124,9 @@ void teleport_unit_between(const map_location& a, const map_location& b, unit& t
* but will likely not be clear when we return.
* @param disp The game display. Assumed neither locked nor faked.
* @returns The animation potential until this animation will finish.
* INT_MIN indicates that no animation is pending.
* milliseconds::min indicates that no animation is pending.
*/
int move_unit_between(const map_location& a,
std::chrono::milliseconds move_unit_between(const map_location& a,
const map_location& b,
unit_ptr temp_unit,
unsigned int step_num,
@ -135,7 +135,7 @@ int move_unit_between(const map_location& a,
display& disp)
{
if ( disp.fogged(a) && disp.fogged(b) ) {
return std::numeric_limits<int>::min();
return std::chrono::milliseconds::min();
}
temp_unit->set_location(a);
@ -151,12 +151,12 @@ int move_unit_between(const map_location& a,
// useless now, previous short draw() just did one
// new_animation_frame();
int target_time = animator.get_animation_time_potential();
auto target_time = animator.get_animation_time_potential();
// target_time must be short to avoid jumpy move
// std::cout << "target time: " << target_time << "\n";
// we round it to the next multiple of 200 so that movement aligns to hex changes properly
target_time += 200;
target_time -= target_time%200;
target_time += 200ms;
target_time -= target_time % 200ms;
return target_time;
}
@ -177,7 +177,7 @@ unit_mover::unit_mover(const std::vector<map_location>& path, bool animate, bool
animate_(animate),
force_scroll_(force_scroll),
animator_(),
wait_until_(std::numeric_limits<int>::min()),
wait_until_(std::chrono::milliseconds::min()),
shown_unit_(),
path_(path),
current_(0),
@ -392,10 +392,10 @@ void unit_mover::proceed_to(unit_ptr u, std::size_t path_index, bool update, boo
*/
void unit_mover::wait_for_anims()
{
if ( wait_until_ == std::numeric_limits<int>::max() )
if ( wait_until_ == std::chrono::milliseconds::max() )
// Wait for end (not currently used, but still supported).
animator_.wait_for_end();
else if ( wait_until_ != std::numeric_limits<int>::min() ) {
else if ( wait_until_ != std::chrono::milliseconds::min() ) {
// Wait until the specified time (used for normal movement).
animator_.wait_until(wait_until_);
// debug code, see unit_frame::redraw()
@ -416,7 +416,7 @@ void unit_mover::wait_for_anims()
}
// Reset data.
wait_until_ = std::numeric_limits<int>::min();
wait_until_ = std::chrono::milliseconds::min();
animator_.clear();
update_shown_unit();
@ -740,7 +740,7 @@ void unit_attack(display * disp, game_board & board,
animator.start_animations();
animator.wait_until(0);
animator.wait_until(0ms);
int damage_left = damage;
bool extra_hit_sounds_played = false;
while(damage_left > 0 && !animator.would_end()) {
@ -751,13 +751,13 @@ void unit_attack(display * disp, game_board & board,
extra_hit_sounds_played = true;
}
int step_left = (animator.get_end_time() - animator.get_animation_time() )/50;
auto step_left = (animator.get_end_time() - animator.get_animation_time() ) / 50ms;
if(step_left < 1) step_left = 1;
int removed_hp = damage_left/step_left ;
if(removed_hp < 1) removed_hp = 1;
defender.take_hit(removed_hp);
damage_left -= removed_hp;
animator.wait_until(animator.get_animation_time_potential() +50);
animator.wait_until(animator.get_animation_time_potential() + 50ms);
}
animator.wait_for_end();
// pass the animation back to the real unit

View file

@ -65,8 +65,8 @@ private: // data
const bool animate_;
const bool force_scroll_;
unit_animator animator_;
/** The animation potential to wait until. INT_MIN for no wait; INT_MAX to wait for end. */
int wait_until_;
/** The animation potential to wait until. milliseconds::min for no wait; milliseconds::max to wait for end. */
std::chrono::milliseconds wait_until_;
/** The unit to be (re-)shown after an animation finishes. */
unit_ptr shown_unit_;
const std::vector<map_location>& path_;