The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
attack_type.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Handle unit-type specific attributes, animations, advancement.
18  */
19 
20 #include "global.hpp"
21 
22 #include "units/attack_type.hpp"
23 #include "formula/callable_objects.hpp"
24 #include "formula/formula.hpp"
25 
26 #include "log.hpp"
28 #include "gettext.hpp"
29 
30 static lg::log_domain log_config("config");
31 #define ERR_CF LOG_STREAM(err, log_config)
32 #define WRN_CF LOG_STREAM(warn, log_config)
33 #define LOG_CONFIG LOG_STREAM(info, log_config)
34 #define DBG_CF LOG_STREAM(debug, log_config)
35 
36 static lg::log_domain log_unit("unit");
37 #define DBG_UT LOG_STREAM(debug, log_unit)
38 #define ERR_UT LOG_STREAM(err, log_unit)
39 
41  self_loc_(),
42  other_loc_(),
43  is_attacker_(false),
44  other_attack_(nullptr),
45  description_(cfg["description"].t_str()),
46  id_(cfg["name"]),
47  type_(cfg["type"]),
48  icon_(cfg["icon"]),
49  range_(cfg["range"]),
50  min_range_(cfg["min_range"].to_int(1)),
51  max_range_(cfg["max_range"].to_int(1)),
52  damage_(cfg["damage"]),
53  num_attacks_(cfg["number"]),
54  attack_weight_(cfg["attack_weight"].to_double(1.0)),
55  defense_weight_(cfg["defense_weight"].to_double(1.0)),
56  accuracy_(cfg["accuracy"]),
57  movement_used_(cfg["movement_used"].to_int(100000)),
58  parry_(cfg["parry"]),
59  specials_(cfg.child_or_empty("specials"))
60 {
61  if (description_.empty())
63 
64  if(icon_.empty()){
65  if (id_ != "")
66  icon_ = "attacks/" + id_ + ".png";
67  else
68  icon_ = "attacks/blank-attack.png";
69  }
70 }
71 
73 {
74 }
75 
77 {
78  if(accuracy_ == 0 && parry_ == 0) {
79  return "";
80  }
81 
82  std::ostringstream s;
84 
85  if(parry_ != 0) {
86  s << "/" << utils::signed_percent(parry_);
87  }
88 
89  return s.str();
90 }
91 
92 /**
93  * Returns whether or not *this matches the given @a filter, ignoring the
94  * complexities introduced by [and], [or], and [not].
95  */
96 static bool matches_simple_filter(const attack_type & attack, const config & filter)
97 {
98  const std::vector<std::string>& filter_range = utils::split(filter["range"]);
99  const std::string& filter_damage = filter["damage"];
100  const std::string& filter_attacks = filter["number"];
101  const std::string& filter_accuracy = filter["accuracy"];
102  const std::string& filter_parry = filter["parry"];
103  const std::string& filter_movement = filter["movement_used"];
104  const std::vector<std::string> filter_name = utils::split(filter["name"]);
105  const std::vector<std::string> filter_type = utils::split(filter["type"]);
106  const std::string filter_special = filter["special"];
107  const std::string filter_formula = filter["formula"];
108 
109  if ( !filter_range.empty() && std::find(filter_range.begin(), filter_range.end(), attack.range()) == filter_range.end() )
110  return false;
111 
112  if ( !filter_damage.empty() && !in_ranges(attack.damage(), utils::parse_ranges(filter_damage)) )
113  return false;
114 
115  if (!filter_attacks.empty() && !in_ranges(attack.num_attacks(), utils::parse_ranges(filter_attacks)))
116  return false;
117 
118  if (!filter_accuracy.empty() && !in_ranges(attack.accuracy(), utils::parse_ranges(filter_accuracy)))
119  return false;
120 
121  if (!filter_parry.empty() && !in_ranges(attack.parry(), utils::parse_ranges(filter_parry)))
122  return false;
123 
124  if (!filter_movement.empty() && !in_ranges(attack.movement_used(), utils::parse_ranges(filter_movement)))
125  return false;
126 
127  if ( !filter_name.empty() && std::find(filter_name.begin(), filter_name.end(), attack.id()) == filter_name.end() )
128  return false;
129 
130  if ( !filter_type.empty() && std::find(filter_type.begin(), filter_type.end(), attack.type()) == filter_type.end() )
131  return false;
132 
133  if ( !filter_special.empty() && !attack.get_special_bool(filter_special, true) )
134  return false;
135 
136  if (!filter_formula.empty()) {
137  try {
138  const attack_type_callable callable(attack);
139  const game_logic::formula form(filter_formula);
140  if(!form.evaluate(callable).as_bool()) {
141  return false;
142  }
143  return true;
144  } catch(game_logic::formula_error& e) {
145  lg::wml_error() << "Formula error in weapon filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
146  // Formulae with syntax errors match nothing
147  return false;
148  }
149  }
150 
151  // Passed all tests.
152  return true;
153 }
154 
155 /**
156  * Returns whether or not *this matches the given @a filter.
157  */
159 {
160  // Handle the basic filter.
161  bool matches = matches_simple_filter(*this, filter);
162 
163  // Handle [and], [or], and [not] with in-order precedence
164  for (const config::any_child &condition : filter.all_children_range() )
165  {
166  // Handle [and]
167  if ( condition.key == "and" )
168  matches = matches && matches_filter(condition.cfg);
169 
170  // Handle [or]
171  else if ( condition.key == "or" )
172  matches = matches || matches_filter(condition.cfg);
173 
174  // Handle [not]
175  else if ( condition.key == "not" )
176  matches = matches && !matches_filter(condition.cfg);
177  }
178 
179  return matches;
180 }
181 
182 namespace {
183  void add_and(std::stringstream &ss) {
184  if(ss.tellp() > 0)
185  ss << t_string(N_(" and "), "wesnoth");
186  }
187 }
188 
189 /**
190  * Modifies *this using the specifications in @a cfg, but only if *this matches
191  * @a cfg viewed as a filter.
192  *
193  * @returns whether or not @c this matched the @a cfg as a filter.
194  */
196 {
197  if( !matches_filter(cfg) )
198  return false;
199 
200  const std::string& set_name = cfg["set_name"];
201  const t_string& set_desc = cfg["set_description"];
202  const std::string& set_type = cfg["set_type"];
203  const std::string& set_icon = cfg["set_icon"];
204  const std::string& del_specials = cfg["remove_specials"];
205  const config &set_specials = cfg.child("set_specials");
206  const std::string& increase_damage = cfg["increase_damage"];
207  const std::string& set_damage = cfg["set_damage"];
208  const std::string& increase_attacks = cfg["increase_attacks"];
209  const std::string& set_attacks = cfg["set_attacks"];
210  const std::string& set_attack_weight = cfg["attack_weight"];
211  const std::string& set_defense_weight = cfg["defense_weight"];
212  const std::string& increase_accuracy = cfg["increase_accuracy"];
213  const std::string& set_accuracy = cfg["set_accuracy"];
214  const std::string& increase_parry = cfg["increase_parry"];
215  const std::string& set_parry = cfg["set_parry"];
216  const std::string& increase_movement = cfg["increase_movement_used"];
217  const std::string& set_movement = cfg["set_movement_used"];
218  // NB: If you add something here that requires a description,
219  // it needs to be added to describe_modification as well.
220 
221  if(set_name.empty() == false) {
222  id_ = set_name;
223  }
224 
225  if(set_desc.empty() == false) {
226  description_ = set_desc;
227  }
228 
229  if(set_type.empty() == false) {
230  type_ = set_type;
231  }
232 
233  if(set_icon.empty() == false) {
234  icon_ = set_icon;
235  }
236 
237  if(del_specials.empty() == false) {
238  const std::vector<std::string>& dsl = utils::split(del_specials);
239  config new_specials;
240  for (const config::any_child &vp : specials_.all_children_range()) {
241  std::vector<std::string>::const_iterator found_id =
242  std::find(dsl.begin(), dsl.end(), vp.cfg["id"].str());
243  if (found_id == dsl.end()) {
244  new_specials.add_child(vp.key, vp.cfg);
245  }
246  }
247  specials_ = new_specials;
248  }
249 
250  if (set_specials) {
251  const std::string &mode = set_specials["mode"];
252  if (mode != "append") {
253  specials_.clear();
254  }
255  for (const config::any_child &value : set_specials.all_children_range()) {
256  specials_.add_child(value.key, value.cfg);
257  }
258  }
259 
260  if(set_damage.empty() == false) {
262  if (damage_ < 0) {
263  damage_ = 0;
264  }
265  }
266 
267  if(increase_damage.empty() == false) {
268  damage_ = utils::apply_modifier(damage_, increase_damage, 0);
269  if (damage_ < 0) {
270  damage_ = 0;
271  }
272  }
273 
274  if(set_attacks.empty() == false) {
275  num_attacks_ = lexical_cast<int>(set_attacks);
276  if (num_attacks_ < 0) {
277  num_attacks_ = 0;
278  }
279 
280  }
281 
282  if(increase_attacks.empty() == false) {
283  num_attacks_ = utils::apply_modifier(num_attacks_, increase_attacks, 1);
284  }
285 
286  if(set_accuracy.empty() == false) {
288  }
289 
290  if(increase_accuracy.empty() == false) {
291  accuracy_ = utils::apply_modifier(accuracy_, increase_accuracy, 1);
292  }
293 
294  if(set_parry.empty() == false) {
295  parry_ = lexical_cast<int>(set_parry);
296  }
297 
298  if(increase_parry.empty() == false) {
299  parry_ = utils::apply_modifier(parry_, increase_parry, 1);
300  }
301 
302  if(set_movement.empty() == false) {
303  movement_used_ = lexical_cast<int>(set_movement);
304  }
305 
306  if(increase_movement.empty() == false) {
307  movement_used_ = utils::apply_modifier(movement_used_, increase_movement, 1);
308  }
309 
310  if(set_attack_weight.empty() == false) {
311  attack_weight_ = lexical_cast_default<double>(set_attack_weight,1.0);
312  }
313 
314  if(set_defense_weight.empty() == false) {
315  defense_weight_ = lexical_cast_default<double>(set_defense_weight,1.0);
316  }
317 
318  return true;
319 }
320 
321 /**
322  * Trimmed down version of apply_modification(), with no modifications actually
323  * made. This can be used to get a description of the modification(s) specified
324  * by @a cfg (if *this matches cfg as a filter).
325  *
326  * If *description is provided, it will be set to a (translated) description
327  * of the modification(s) applied (currently only changes to the number of
328  * strikes, damage, accuracy, and parry are included in this description).
329  *
330  * @returns whether or not @c this matched the @a cfg as a filter.
331  */
333 {
334  if( !matches_filter(cfg) )
335  return false;
336 
337  // Did the caller want the description?
338  if(description != nullptr) {
339  const std::string& increase_damage = cfg["increase_damage"];
340  const std::string& set_damage = cfg["set_damage"];
341  const std::string& increase_attacks = cfg["increase_attacks"];
342  const std::string& set_attacks = cfg["set_attacks"];
343  const std::string& increase_accuracy = cfg["increase_accuracy"];
344  const std::string& set_accuracy = cfg["set_accuracy"];
345  const std::string& increase_parry = cfg["increase_parry"];
346  const std::string& set_parry = cfg["set_parry"];
347  const std::string& increase_movement = cfg["increase_movement_used"];
348  const std::string& set_movement = cfg["set_movement_used"];
349 
350  std::stringstream desc;
351 
352  if(increase_damage.empty() == false) {
353  add_and(desc);
354  int inc_damage = lexical_cast<int>(increase_damage);
355  desc << utils::print_modifier(increase_damage) << " "
356  << _n("damage","damage", inc_damage);
357  }
358 
359  if(set_damage.empty() == false) {
360  add_and(desc);
361  int damage = lexical_cast<int>(increase_damage);
362  desc << set_damage << " " << _n("damage","damage", damage);
363  }
364 
365  if(increase_attacks.empty() == false) {
366  add_and(desc);
367  int inc_attacks = lexical_cast<int>(increase_attacks);
368  desc << utils::print_modifier(increase_attacks) << " "
369  << _n("strike", "strikes", inc_attacks);
370  }
371 
372  if(set_attacks.empty() == false) {
373  int num_attacks = lexical_cast<int>(set_attacks);
374  add_and(desc);
375  desc << set_attacks << " " << _n("strike", "strikes", num_attacks);
376  }
377 
378  if(set_accuracy.empty() == false) {
379  int accuracy = lexical_cast<int>(set_accuracy);
380 
381  add_and(desc);
382  // xgettext:no-c-format
383  desc << accuracy << " " << _("% accuracy");
384  }
385 
386  if(increase_accuracy.empty() == false) {
387  add_and(desc);
388  int inc_acc = lexical_cast<int>(increase_accuracy);
389  // Help xgettext with a directive to recognize the string as a non C printf-like string
390  // xgettext:no-c-format
391  desc << utils::signed_value(inc_acc) << _("% accuracy");
392  }
393 
394  if(set_parry.empty() == false) {
395  int parry = lexical_cast<int>(set_parry);
396 
397  add_and(desc);
398  desc << parry << _(" parry");
399  }
400 
401  if(increase_parry.empty() == false) {
402  add_and(desc);
403  int inc_parry = lexical_cast<int>(increase_parry);
404  // xgettext:no-c-format
405  desc << utils::signed_value(inc_parry) << _("% parry");
406  }
407 
408  if(set_movement.empty() == false) {
409  int movement_used = lexical_cast<int>(set_movement);
410 
411  add_and(desc);
412  desc << movement_used << " " << _n("movement point","movement points",movement_used);
413  }
414 
415  if(increase_movement.empty() == false) {
416  add_and(desc);
417  int inc_move = lexical_cast<int>(increase_movement);
418  desc << increase_movement << " " << _n("movement point","movement points",inc_move);
419  }
420 
421  *description = desc.str();
422  }
423 
424  return true;
425 }
426 
427 void attack_type::write(config& cfg) const
428 {
429  cfg["description"] = description_;
430  cfg["name"] = id_;
431  cfg["type"] = type_;
432  cfg["icon"] = icon_;
433  cfg["range"] = range_;
434  cfg["min_range"] = min_range_;
435  cfg["max_range"] = max_range_;
436  cfg["damage"] = damage_;
437  cfg["number"] = num_attacks_;
438  cfg["attack_weight"] = attack_weight_;
439  cfg["defense_weight"] = defense_weight_;
440  cfg["accuracy"] = accuracy_;
441  cfg["movement_used"] = movement_used_;
442  cfg["parry"] = parry_;
443  cfg.add_child("specials", specials_);
444 }
bool apply_modification(const config &cfg)
Modifies *this using the specifications in cfg, but only if *this matches cfg viewed as a filter...
std::string icon_
std::string type_
bool matches_filter(const config &filter) const
Returns whether or not *this matches the given filter.
std::string id_
Definition: formula.cpp:636
attack_type(const config &cfg)
Definition: attack_type.cpp:40
int movement_used() const
Definition: attack_type.hpp:90
static lg::log_domain log_unit("unit")
void clear()
Definition: config.cpp:1055
bool as_bool() const
Definition: variant.cpp:580
int parry() const
Definition: attack_type.hpp:44
To lexical_cast(From value)
Lexical cast converts one type to another.
GLenum mode
Definition: glew.h:2390
int accuracy() const
Definition: attack_type.hpp:43
int num_attacks() const
Definition: attack_type.hpp:46
double defense_weight_
bool get_special_bool(const std::string &special, bool simple_check=false) const
Returns whether or not *this has a special with a tag or id equal to special.
Definition: abilities.cpp:552
static bool matches_simple_filter(const attack_type &attack, const config &filter)
Returns whether or not *this matches the given filter, ignoring the complexities introduced by [and]...
Definition: attack_type.cpp:96
void set_name(const t_string &value)
Definition: attack_type.hpp:51
void write(config &cfg) const
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
GLsizei const GLfloat * value
Definition: glew.h:1817
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
double attack_weight_
std::string range_
static UNUSEDNOWARN std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:86
config & add_child(const std::string &key)
Definition: config.cpp:743
config specials_
const std::string & id() const
Definition: attack_type.hpp:36
std::string egettext(char const *msgid)
Definition: gettext.cpp:43
const std::string & type() const
Definition: attack_type.hpp:37
void set_defense_weight(double value)
Definition: attack_type.hpp:63
void set_specials(config value)
Definition: attack_type.hpp:64
t_string description_
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:31
void set_damage(int value)
Definition: attack_type.hpp:60
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:262
std::vector< std::pair< int, int > > parse_ranges(std::string const &str)
std::string signed_percent(int val)
Convert into a percentage (using the Unicode "−" and +0% convention.
const std::string & range() const
Definition: attack_type.hpp:39
~attack_type()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: attack_type.cpp:72
#define N_(String)
Definition: gettext.hpp:90
int damage() const
Definition: attack_type.hpp:45
std::string accuracy_parry_description() const
Definition: attack_type.cpp:76
bool describe_modification(const config &cfg, std::string *description)
Trimmed down version of apply_modification(), with no modifications actually made.
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:3448
void set_parry(int value)
Definition: attack_type.hpp:59
std::string id_
bool find(E event, F functor)
Tests whether an event handler is available.
int apply_modifier(const int number, const std::string &amount, const int minimum)
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
void set_icon(const std::string &value)
Definition: attack_type.hpp:54
Standard logging facilities (interface).
GLenum condition
Definition: glew.h:9969
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define e
std::string signed_value(int val)
Convert into a signed value (using the Unicode "−" and +0 convention.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
GLdouble s
Definition: glew.h:1358
std::string print_modifier(const std::string &mod)
void set_accuracy(int value)
Definition: attack_type.hpp:58
GLsizei const GLcharARB ** string
Definition: glew.h:4503
bool empty() const
Definition: tstring.hpp:166
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp > > &ranges)
Definition: util.hpp:195
void set_attack_weight(double value)
Definition: attack_type.hpp:62
static lg::log_domain log_config("config")
void set_type(const std::string &value)
Definition: attack_type.hpp:53