Refactor handling of unit animation progressive_* helper classes
This majorly reduces code duplication. Thanks to @jyrkive for help.
This commit is contained in:
parent
42f087abb3
commit
7d5acc92ce
4 changed files with 232 additions and 298 deletions
|
@ -1096,6 +1096,7 @@
|
|||
<Unit filename="../../src/units/formula_manager.hpp" />
|
||||
<Unit filename="../../src/units/frame.cpp" />
|
||||
<Unit filename="../../src/units/frame.hpp" />
|
||||
<Unit filename="../../src/units/frame_private.hpp" />
|
||||
<Unit filename="../../src/units/helper.cpp" />
|
||||
<Unit filename="../../src/units/helper.hpp" />
|
||||
<Unit filename="../../src/units/id.cpp" />
|
||||
|
|
|
@ -22,233 +22,6 @@
|
|||
static lg::log_domain log_engine("engine");
|
||||
#define ERR_NG LOG_STREAM(err, log_engine)
|
||||
|
||||
template<typename T>
|
||||
progressive_base<T>::progressive_base(const std::string& data, int duration)
|
||||
: data_()
|
||||
, input_(data)
|
||||
{
|
||||
int split_flag = utils::REMOVE_EMPTY; // useless to strip spaces
|
||||
const std::vector<std::string> comma_split = utils::split(data,',',split_flag);
|
||||
const int time_chunk = std::max<int>(1, 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;
|
||||
|
||||
try {
|
||||
time = (colon_split.size() > 1) ? std::stoi(colon_split[1]) : time_chunk;
|
||||
} catch(std::invalid_argument) {
|
||||
ERR_NG << "Invalid time in unit animation: " << colon_split[1] << "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
std::vector<std::string> range = utils::split(colon_split[0],'~',split_flag);
|
||||
T range0 = lexical_cast<T>(range[0]);
|
||||
T range1 = (range.size() > 1) ? lexical_cast<T>(range[1]) : range0;
|
||||
|
||||
data_.push_back({{range0, range1}, time});
|
||||
} catch(bad_lexical_cast) {}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T progressive_base<T>::get_current_element(int current_time, T default_val) const
|
||||
{
|
||||
if(data_.empty()) {
|
||||
return default_val;
|
||||
}
|
||||
|
||||
int time = 0;
|
||||
unsigned int sub_halo = 0;
|
||||
int searched_time = current_time;
|
||||
|
||||
if(searched_time < 0) searched_time = 0;
|
||||
if(searched_time > duration()) searched_time = duration();
|
||||
|
||||
while(time < searched_time && sub_halo < data_.size()) {
|
||||
time += data_[sub_halo].second;
|
||||
++sub_halo;
|
||||
}
|
||||
|
||||
if(sub_halo != 0) {
|
||||
sub_halo--;
|
||||
time -= data_[sub_halo].second;
|
||||
}
|
||||
|
||||
const T first = data_[sub_halo].first.first;
|
||||
const T second = data_[sub_halo].first.second;
|
||||
|
||||
return T((
|
||||
static_cast<double>(searched_time - time) /
|
||||
static_cast<double>(data_[sub_halo].second)
|
||||
) * (second - first) + first);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int progressive_base<T>::duration() const
|
||||
{
|
||||
int total = 0;
|
||||
for(const auto& entry : data_) {
|
||||
total += entry.second;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool progressive_base<T>::does_not_change() const
|
||||
{
|
||||
return data_.empty() || (data_.size() == 1 && data_[0].first.first == data_[0].first.second);
|
||||
}
|
||||
|
||||
progressive_string::progressive_string(const std::string& data,int duration)
|
||||
: data_()
|
||||
, input_(data)
|
||||
{
|
||||
const std::vector<std::string> first_pass = utils::square_parenthetical_split(data);
|
||||
int time_chunk = std::max<int>(duration, 1);
|
||||
|
||||
if(duration > 1 && !first_pass.empty()) {
|
||||
// If duration specified, divide evenly the time for items with unspecified times
|
||||
int total_specified_time = 0;
|
||||
|
||||
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]);
|
||||
} catch(std::invalid_argument) {
|
||||
ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_chunk = std::max<int>((duration - total_specified_time) / first_pass.size(), 1);
|
||||
}
|
||||
|
||||
for(const std::string& fp_string : first_pass) {
|
||||
std::vector<std::string> second_pass = utils::split(fp_string, ':');
|
||||
if(second_pass.size() > 1) {
|
||||
try {
|
||||
data_.push_back({std::move(second_pass[0]), std::stoi(second_pass[1])});
|
||||
} catch(std::invalid_argument) {
|
||||
ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
|
||||
}
|
||||
} else {
|
||||
data_.push_back({std::move(second_pass[0]) ,time_chunk});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int progressive_string::duration() const
|
||||
{
|
||||
int total = 0;
|
||||
for(const auto& entry : data_) {
|
||||
total += entry.second;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
progressive_image::progressive_image(const std::string& data,int duration)
|
||||
: data_()
|
||||
, input_(data)
|
||||
{
|
||||
const std::vector<std::string> first_pass = utils::square_parenthetical_split(data);
|
||||
int time_chunk = std::max<int>(duration, 1);
|
||||
|
||||
if(duration > 1 && !first_pass.empty() ) {
|
||||
// If duration specified, divide evenly the time for images with unspecified times
|
||||
int total_specified_time = 0;
|
||||
|
||||
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]);
|
||||
} catch(std::invalid_argument) {
|
||||
ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_chunk = std::max<int>((duration - total_specified_time) / first_pass.size(), 1);
|
||||
}
|
||||
|
||||
for(const std::string& fp_string : first_pass) {
|
||||
std::vector<std::string> second_pass = utils::split(fp_string, ':');
|
||||
if(second_pass.size() > 1) {
|
||||
try {
|
||||
data_.push_back({std::move(second_pass[0]), std::stoi(second_pass[1])});
|
||||
} catch(std::invalid_argument) {
|
||||
ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
|
||||
}
|
||||
} else {
|
||||
data_.push_back({std::move(second_pass[0]), time_chunk});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int progressive_image::duration() const
|
||||
{
|
||||
int total = 0;
|
||||
for(const auto& entry : data_) {
|
||||
total += entry.second;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
const image::locator empty_image;
|
||||
|
||||
const image::locator& progressive_image::get_current_element(int current_time) const
|
||||
{
|
||||
if(data_.empty()) {
|
||||
return empty_image;
|
||||
}
|
||||
|
||||
int time = 0;
|
||||
unsigned int sub_image = 0;
|
||||
|
||||
while(time < current_time && sub_image < data_.size()) {
|
||||
time += data_[sub_image].second;
|
||||
++sub_image;
|
||||
}
|
||||
|
||||
if(sub_image) {
|
||||
sub_image--;
|
||||
}
|
||||
|
||||
return data_[sub_image].first;
|
||||
}
|
||||
|
||||
static const std::string empty_string;
|
||||
|
||||
const std::string& progressive_string::get_current_element(int current_time) const
|
||||
{
|
||||
if(data_.empty()) {
|
||||
return empty_string;
|
||||
}
|
||||
|
||||
int time = 0;
|
||||
unsigned int sub_halo = 0;
|
||||
|
||||
while(time < current_time && sub_halo < data_.size()) {
|
||||
time += data_[sub_halo].second;
|
||||
++sub_halo;
|
||||
}
|
||||
|
||||
if(sub_halo) {
|
||||
sub_halo--;
|
||||
}
|
||||
|
||||
return data_[sub_halo].first;
|
||||
}
|
||||
|
||||
// Force compilation of the following template instantiations
|
||||
template class progressive_base<int>;
|
||||
template class progressive_base<double>;
|
||||
|
||||
frame_parameters::frame_parameters()
|
||||
: duration(0)
|
||||
, image()
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#ifndef UNIT_FRAME_H_INCLUDED
|
||||
#define UNIT_FRAME_H_INCLUDED
|
||||
|
||||
#include "units/frame_private.hpp"
|
||||
|
||||
#include "halo.hpp"
|
||||
#include "image.hpp"
|
||||
|
||||
|
@ -28,77 +30,6 @@
|
|||
|
||||
class config;
|
||||
|
||||
/* FIXME: these 'progressive_' classes are all mostly equivalent except for the exact type of the
|
||||
* data_ vector and the interactions with it. Need to figure how to reduce the code duplication -
|
||||
* perhaps inherit from progressive_base? Though do note the string/image functions are only
|
||||
* unique since they don't store a pair of pairs.
|
||||
*/
|
||||
template<typename T>
|
||||
class progressive_base
|
||||
{
|
||||
public:
|
||||
progressive_base(const std::string& data = "", int duration = 0);
|
||||
int duration() const;
|
||||
const T get_current_element(int time, T default_val = 0) const;
|
||||
bool does_not_change() const;
|
||||
|
||||
std::string get_original() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::pair<T, T>, int>> data_;
|
||||
std::string input_;
|
||||
};
|
||||
|
||||
class progressive_string
|
||||
{
|
||||
public:
|
||||
progressive_string(const std::string& data = "", int duration = 0);
|
||||
int duration() const;
|
||||
const std::string& get_current_element(int time) const;
|
||||
|
||||
bool does_not_change() const
|
||||
{
|
||||
return data_.size() <= 1;
|
||||
}
|
||||
|
||||
std::string get_original() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string,int>> data_;
|
||||
std::string input_;
|
||||
};
|
||||
|
||||
class progressive_image
|
||||
{
|
||||
public:
|
||||
progressive_image(const std::string& data = "", int duration = 0);
|
||||
int duration() const;
|
||||
const image::locator& get_current_element(int time) const;
|
||||
|
||||
bool does_not_change() const
|
||||
{
|
||||
return data_.size() <= 1;
|
||||
}
|
||||
|
||||
std::string get_original() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<image::locator,int>> data_;
|
||||
std::string input_;
|
||||
};
|
||||
|
||||
typedef progressive_base<int> progressive_int;
|
||||
typedef progressive_base<double> progressive_double;
|
||||
|
||||
/** All parameters from a frame at a given instant */
|
||||
struct frame_parameters
|
||||
{
|
||||
|
|
229
src/units/frame_private.hpp
Normal file
229
src/units/frame_private.hpp
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
Copyright (C) 2006 - 2016 by Jeremy Rosen <jeremy.rosen@enst-bretagne.fr>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef UNIT_FRAME_PRIVATE_HPP_INCLUDED
|
||||
#define UNIT_FRAME_PRIVATE_HPP_INCLUDED
|
||||
|
||||
#include "lexical_cast.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "utils/general.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace image { class locator; }
|
||||
|
||||
template<typename T, typename D>
|
||||
class progressive_base
|
||||
{
|
||||
public:
|
||||
using data_t = std::vector<std::pair<D, int>>;
|
||||
|
||||
progressive_base(const std::string& input)
|
||||
: data_()
|
||||
, input_(input)
|
||||
{}
|
||||
|
||||
virtual const T get_current_element(int current_time, T default_val) const = 0;
|
||||
|
||||
virtual bool does_not_change() const
|
||||
{
|
||||
return data_.size() <= 1;
|
||||
}
|
||||
|
||||
int duration() const
|
||||
{
|
||||
int total = 0;
|
||||
for(const auto& entry : data_) {
|
||||
total += entry.second;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
std::string get_original() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
data_t& data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
const data_t& data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
data_t data_;
|
||||
std::string input_;
|
||||
};
|
||||
|
||||
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_base<T, std::pair<T, T>>(input)
|
||||
{
|
||||
auto& base_data = progressive_pair_base_type::data();
|
||||
|
||||
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));
|
||||
|
||||
for(const auto& entry : comma_split) {
|
||||
std::vector<std::string> colon_split = utils::split(entry, ':', split_flag);
|
||||
int time = 0;
|
||||
|
||||
try {
|
||||
time = (colon_split.size() > 1) ? std::stoi(colon_split[1]) : time_chunk;
|
||||
} catch(std::invalid_argument) {
|
||||
//ERR_NG << "Invalid time in unit animation: " << colon_split[1] << "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
std::vector<std::string> range = utils::split(colon_split[0],'~',split_flag);
|
||||
T range0 = lexical_cast<T>(range[0]);
|
||||
T range1 = (range.size() > 1) ? lexical_cast<T>(range[1]) : range0;
|
||||
|
||||
base_data.push_back({{range0, range1}, time});
|
||||
} catch(bad_lexical_cast) {}
|
||||
}
|
||||
}
|
||||
|
||||
virtual const T get_current_element(int 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();
|
||||
|
||||
if(base_data.empty()) {
|
||||
return default_val;
|
||||
}
|
||||
|
||||
int time = 0;
|
||||
unsigned int i = 0;
|
||||
const int searched_time = util::clamp(current_time, 0, base_duration);
|
||||
|
||||
while(time < searched_time && i < base_data.size()) {
|
||||
time += base_data[i].second;
|
||||
++i;
|
||||
}
|
||||
|
||||
if(i != 0) {
|
||||
i--;
|
||||
time -= base_data[i].second;
|
||||
}
|
||||
|
||||
const T first = base_data[i].first.first;
|
||||
const T second = base_data[i].first.second;
|
||||
|
||||
return T((
|
||||
static_cast<double>(searched_time - time) /
|
||||
static_cast<double>(base_data[i].second)
|
||||
) * (second - first) + first);
|
||||
}
|
||||
|
||||
bool does_not_change() const override
|
||||
{
|
||||
const auto& base_data = progressive_pair_base_type::data();
|
||||
return base_data.empty() || (base_data.size() == 1 && base_data[0].first.first == base_data[0].first.second);
|
||||
}
|
||||
|
||||
private:
|
||||
using progressive_pair_base_type = progressive_base<T, std::pair<T, T>>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class progressive_single : public progressive_base<T, T>
|
||||
{
|
||||
public:
|
||||
progressive_single(const std::string& input = "", int duration = 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);
|
||||
|
||||
if(duration > 1 && !first_pass.empty()) {
|
||||
// If duration specified, divide evenly the time for items with unspecified times
|
||||
int total_specified_time = 0;
|
||||
|
||||
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]);
|
||||
} catch(std::invalid_argument) {
|
||||
//ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_chunk = std::max<int>((duration - total_specified_time) / first_pass.size(), 1);
|
||||
}
|
||||
|
||||
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])});
|
||||
} catch(std::invalid_argument) {
|
||||
//ERR_NG << "Invalid time in unit animation: " << second_pass[1] << "\n";
|
||||
}
|
||||
} else {
|
||||
base_data.push_back({std::move(second_pass[0]) ,time_chunk});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual const T get_current_element(int current_time, T default_val = T()) const override
|
||||
{
|
||||
const auto& base_data = progressive_single_base_type::data();
|
||||
|
||||
if(base_data.empty()) {
|
||||
return default_val;
|
||||
}
|
||||
|
||||
int time = 0;
|
||||
unsigned int i = 0;
|
||||
|
||||
while(time < current_time && i < base_data.size()) {
|
||||
time += base_data[i].second;
|
||||
++i;
|
||||
}
|
||||
|
||||
// TODO: what is this for?
|
||||
if(i) {
|
||||
i--;
|
||||
}
|
||||
|
||||
return base_data[i].first;
|
||||
}
|
||||
|
||||
private:
|
||||
using progressive_single_base_type = progressive_base<T, T>;
|
||||
};
|
||||
|
||||
// Common types used by the unit frame code.
|
||||
using progressive_int = progressive_pair<int>;
|
||||
using progressive_double = progressive_pair<double>;
|
||||
|
||||
using progressive_string = progressive_single<std::string>;
|
||||
using progressive_image = progressive_single<image::locator>;
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue