The Battle for Wesnoth  1.19.0-dev
frame_private.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2024
3  by Jeremy Rosen <jeremy.rosen@enst-bretagne.fr>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #pragma once
17 
18 #include "lexical_cast.hpp"
20 
21 #include <vector>
22 
23 namespace image { class locator; }
24 
25 template<typename T, typename D>
27 {
28 public:
29  using data_t = std::vector<std::pair<D, int>>;
30 
31  progressive_base(const std::string& input)
32  : data_()
33  , input_(input)
34  {}
35 
36  virtual const T get_current_element(int current_time, T default_val) const = 0;
37 
38  virtual bool does_not_change() const
39  {
40  return data_.size() <= 1;
41  }
42 
43  int duration() const
44  {
45  int total = 0;
46  for(const auto& entry : data_) {
47  total += entry.second;
48  }
49 
50  return total;
51  }
52 
53  std::string get_original() const
54  {
55  return input_;
56  }
57 
59  {
60  return data_;
61  }
62 
63  const data_t& data() const
64  {
65  return data_;
66  }
67 
68  virtual ~progressive_base() {}
69 
70 private:
72  std::string input_;
73 };
74 
75 template<typename T>
76 class progressive_pair : public progressive_base<T, std::pair<T, T>>
77 {
78 public:
79  progressive_pair(const std::string& input = "", int duration = 0)
80  : progressive_base<T, std::pair<T, T>>(input)
81  {
82  auto& base_data = progressive_pair_base_type::data();
83 
84  const int split_flag = utils::REMOVE_EMPTY; // useless to strip spaces
85 
86  const std::vector<std::string> comma_split = utils::split(input, ',', split_flag);
87  const int time_chunk = std::max<int>(1, duration / std::max<int>(comma_split.size(), 1));
88 
89  for(const auto& entry : comma_split) {
90  std::vector<std::string> colon_split = utils::split(entry, ':', split_flag);
91  int time = 0;
92 
93  try {
94  time = (colon_split.size() > 1) ? std::stoi(colon_split[1]) : time_chunk;
95  } catch(const std::invalid_argument&) {
96  //ERR_NG << "Invalid time in unit animation: " << colon_split[1];
97  }
98 
99  try {
100  std::vector<std::string> range = utils::split(colon_split[0],'~',split_flag);
101  T range0 = lexical_cast<T>(range[0]);
102  T range1 = (range.size() > 1) ? lexical_cast<T>(range[1]) : range0;
103 
104  base_data.push_back({{range0, range1}, time});
105  } catch(const bad_lexical_cast&) {}
106  }
107  }
108 
109  virtual const T get_current_element(int current_time, T default_val = T()) const override
110  {
111  const auto& base_data = progressive_pair_base_type::data();
112  const int& base_duration = progressive_pair_base_type::duration();
113 
114  if(base_data.empty()) {
115  return default_val;
116  }
117 
118  int time = 0;
119  unsigned int i = 0;
120  const int searched_time = std::clamp(current_time, 0, base_duration);
121 
122  while(time < searched_time && i < base_data.size()) {
123  time += base_data[i].second;
124  ++i;
125  }
126 
127  if(i != 0) {
128  i--;
129  time -= base_data[i].second;
130  }
131 
132  const T first = base_data[i].first.first;
133  const T second = base_data[i].first.second;
134 
135  return T((
136  static_cast<double>(searched_time - time) /
137  static_cast<double>(base_data[i].second)
138  ) * (second - first) + first);
139  }
140 
141  bool does_not_change() const override
142  {
143  const auto& base_data = progressive_pair_base_type::data();
144  return base_data.empty() || (base_data.size() == 1 && base_data[0].first.first == base_data[0].first.second);
145  }
146 
147 private:
149 };
150 
151 template<typename T>
153 {
154 public:
155  progressive_single(const std::string& input = "", int duration = 0)
156  : progressive_base<T, T>(input)
157  {
158  auto& base_data = progressive_single_base_type::data();
159 
160  const std::vector<std::string> first_pass = utils::square_parenthetical_split(input);
161  int time_chunk = std::max<int>(duration, 1);
162 
163  if(duration > 1 && !first_pass.empty()) {
164  // If duration specified, divide evenly the time for items with unspecified times
165  int total_specified_time = 0;
166 
167  for(const std::string& fp_string : first_pass) {
168  std::vector<std::string> second_pass = utils::split(fp_string, ':');
169  if(second_pass.size() > 1) {
170  try {
171  total_specified_time += std::stoi(second_pass[1]);
172  } catch(const std::invalid_argument&) {
173  //ERR_NG << "Invalid time in unit animation: " << second_pass[1];
174  }
175  }
176  }
177 
178  time_chunk = std::max<int>((duration - total_specified_time) / first_pass.size(), 1);
179  }
180 
181  for(const std::string& fp_string : first_pass) {
182  std::vector<std::string> second_pass = utils::split(fp_string, ':');
183  if(second_pass.size() > 1) {
184  try {
185  base_data.push_back({std::move(second_pass[0]), std::stoi(second_pass[1])});
186  } catch(const std::invalid_argument&) {
187  //ERR_NG << "Invalid time in unit animation: " << second_pass[1];
188  }
189  } else {
190  base_data.push_back({std::move(second_pass[0]) ,time_chunk});
191  }
192  }
193  }
194 
195  virtual const T get_current_element(int current_time, T default_val = T()) const override
196  {
197  const auto& base_data = progressive_single_base_type::data();
198 
199  if(base_data.empty()) {
200  return default_val;
201  }
202 
203  int time = 0;
204  unsigned int i = 0;
205 
206  while(time < current_time && i < base_data.size()) {
207  time += base_data[i].second;
208  ++i;
209  }
210 
211  // TODO: what is this for?
212  if(i) {
213  i--;
214  }
215 
216  return base_data[i].first;
217  }
218 
219 private:
221 };
222 
223 // Common types used by the unit frame code.
226 
const data_t & data() const
virtual ~progressive_base()
std::string input_
std::vector< std::pair< D, int > > data_t
progressive_base(const std::string &input)
int duration() const
std::string get_original() const
virtual const T get_current_element(int current_time, T default_val) const =0
virtual bool does_not_change() const
virtual const T get_current_element(int current_time, T default_val=T()) const override
progressive_pair(const std::string &input="", int duration=0)
bool does_not_change() const override
virtual const T get_current_element(int current_time, T default_val=T()) const override
progressive_single(const std::string &input="", int duration=0)
std::size_t i
Definition: function.cpp:968
New lexcical_cast header.
Functions to load and save images from/to disk.
@ REMOVE_EMPTY
std::vector< std::string > square_parenthetical_split(const std::string &val, const char separator, const std::string &left, const std::string &right, const int flags)
Similar to parenthetical_split, but also expands embedded square brackets.
std::vector< std::string > split(const config_attribute_value &val)
Thrown when a lexical_cast fails.