The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
types.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/types.hpp"
23 
24 #include "game_config.hpp"
25 #include "game_errors.hpp" //thrown sometimes
26 //#include "gettext.hpp"
27 #include "log.hpp"
28 #include "utils/make_enum.hpp"
29 #include "units/unit.hpp"
30 #include "units/abilities.hpp"
31 #include "units/animation.hpp"
32 #include "util.hpp"
33 
36 
37 #include <boost/regex.hpp>
38 
39 static lg::log_domain log_config("config");
40 #define ERR_CF LOG_STREAM(err, log_config)
41 #define WRN_CF LOG_STREAM(warn, log_config)
42 #define LOG_CONFIG LOG_STREAM(info, log_config)
43 #define DBG_CF LOG_STREAM(debug, log_config)
44 
45 static lg::log_domain log_unit("unit");
46 #define DBG_UT LOG_STREAM(debug, log_unit)
47 #define ERR_UT LOG_STREAM(err, log_unit)
48 
49 
50 /* ** unit_type ** */
51 
52 
54  cfg_(o.cfg_),
55  unit_cfg_(), // Not copied; will be re-created if needed.
56  built_unit_cfg_(false),
57  id_(o.id_),
58  debug_id_(o.debug_id_),
59  base_id_(o.base_id_),
60  type_name_(o.type_name_),
61  description_(o.description_),
62  hitpoints_(o.hitpoints_),
63  hp_bar_scaling_(o.hp_bar_scaling_),
64  xp_bar_scaling_(o.xp_bar_scaling_),
65  level_(o.level_),
66  recall_cost_(o.recall_cost_),
67  movement_(o.movement_),
68  vision_(o.vision_),
69  jamming_(o.jamming_),
70  max_attacks_(o.max_attacks_),
71  cost_(o.cost_),
72  usage_(o.usage_),
73  undead_variation_(o.undead_variation_),
74  image_(o.image_),
75  icon_(o.icon_),
76  small_profile_(o.small_profile_),
77  profile_(o.profile_),
78  flag_rgb_(o.flag_rgb_),
79  num_traits_(o.num_traits_),
80  variations_(o.variations_),
81  default_variation_(o.default_variation_),
82  race_(o.race_),
83  alpha_(o.alpha_),
84  abilities_(o.abilities_),
85  adv_abilities_(o.adv_abilities_),
86  ability_tooltips_(o.ability_tooltips_),
87  adv_ability_tooltips_(o.adv_ability_tooltips_),
88  zoc_(o.zoc_),
89  hide_help_(o.hide_help_),
90  do_not_list_(o.do_not_list_),
91  advances_to_(o.advances_to_),
92  experience_needed_(o.experience_needed_),
93  in_advancefrom_(o.in_advancefrom_),
94  alignment_(o.alignment_),
95  movement_type_(o.movement_type_),
96  possible_traits_(o.possible_traits_),
97  genders_(o.genders_),
98  animations_(o.animations_),
99  build_status_(o.build_status_)
100 {
101  gender_types_[0] = o.gender_types_[0] != nullptr ? new unit_type(*o.gender_types_[0]) : nullptr;
102  gender_types_[1] = o.gender_types_[1] != nullptr ? new unit_type(*o.gender_types_[1]) : nullptr;
103 
104  for(variations_map::const_iterator i = o.variations_.begin(); i != o.variations_.end(); ++i) {
105  variations_[i->first] = new unit_type(*i->second);
106  }
107 }
108 
109 
110 unit_type::unit_type(const config &cfg, const std::string & parent_id) :
111  cfg_(cfg),
112  unit_cfg_(),
113  built_unit_cfg_(false),
114  id_(cfg_.has_attribute("id") ? cfg_["id"].str() : parent_id),
115  debug_id_(),
116  base_id_(!parent_id.empty() ? parent_id : id_),
117  type_name_(cfg_["name"].t_str()),
118  description_(),
119  hitpoints_(0),
120  hp_bar_scaling_(0.0),
121  xp_bar_scaling_(0.0),
122  level_(0),
123  recall_cost_(),
124  movement_(0),
125  vision_(-1),
126  jamming_(0),
127  max_attacks_(0),
128  cost_(0),
129  usage_(),
130  undead_variation_(),
131  image_(cfg_["image"].str()),
132  icon_(),
133  small_profile_(),
134  profile_(),
135  flag_rgb_(cfg_["flag_rgb"].str()),
136  num_traits_(0),
137  gender_types_(),
138  variations_(),
139  default_variation_(cfg_["variation"]),
140  variation_name_(cfg_["variation_name"].t_str()),
141  race_(&unit_race::null_race),
142  alpha_(ftofxp(1.0)),
143  abilities_(),
144  adv_abilities_(),
145  ability_tooltips_(),
146  adv_ability_tooltips_(),
147  zoc_(false),
148  hide_help_(false),
149  do_not_list_(cfg_["do_not_list"].to_bool(false)),
150  advances_to_(),
151  experience_needed_(0),
152  in_advancefrom_(false),
153  alignment_(unit_type::ALIGNMENT::NEUTRAL),
154  movement_type_(),
155  possible_traits_(),
156  genders_(),
157  animations_(),
158  build_status_(NOT_BUILT)
159 {
160  check_id(id_);
162  gender_types_[0] = nullptr;
163  gender_types_[1] = nullptr;
164 }
165 
167 {
168  delete gender_types_[0];
169  delete gender_types_[1];
170 
171  for(variations_map::iterator i = variations_.begin(); i != variations_.end(); ++i) {
172  delete i->second;
173  }
174 }
175 
176 /**
177  * Load data into an empty unit_type (build to FULL).
178  */
180  const race_map &races, const config::const_child_itors &traits)
181 {
182  // Don't build twice.
183  if ( FULL <= build_status_ )
184  return;
185  // Make sure we are built to the preceding build level.
186  build_help_index(mv_types, races, traits);
187 
188  for (int i = 0; i < 2; ++i) {
189  if (gender_types_[i])
190  gender_types_[i]->build_full(mv_types, races, traits);
191  }
192 
193  if ( race_ != &unit_race::null_race )
194  {
195  if (!race_->uses_global_traits()) {
197  }
198  if ( cfg_["ignore_race_traits"].to_bool() ) {
200  } else {
201  for (const config &t : race_->additional_traits())
202  {
203  if (alignment_ != unit_type::ALIGNMENT::NEUTRAL || t["id"] != "fearless")
204  possible_traits_.add_child("trait", t);
205  }
206  }
207  if (undead_variation_.empty()) {
209  }
210  }
211 
212  // Insert any traits that are just for this unit type
213  for (const config &trait : cfg_.child_range("trait"))
214  {
215  possible_traits_.add_child("trait", trait);
216  }
217 
218  zoc_ = cfg_["zoc"].to_bool(level_ > 0);
219 
220  const config::attribute_value & alpha_blend = cfg_["alpha"];
221  if(alpha_blend.empty() == false) {
222  alpha_ = ftofxp(alpha_blend.to_double());
223  }
224 
226 
227  hp_bar_scaling_ = cfg_["hp_bar_scaling"].to_double(game_config::hp_bar_scaling);
228  xp_bar_scaling_ = cfg_["xp_bar_scaling"].to_double(game_config::xp_bar_scaling);
229 
230  // Propagate the build to the variations.
231  for (variations_map::value_type & variation : variations_) {
232  variation.second->build_full(mv_types, races, traits);
233  }
234 
235  // Deprecation messages, only seen when unit is parsed for the first time.
236 
238 }
239 
240 /**
241  * Partially load data into an empty unit_type (build to HELP_INDEXED).
242  */
244  const race_map &races, const config::const_child_itors &traits)
245 {
246  // Don't build twice.
247  if ( HELP_INDEXED <= build_status_ )
248  return;
249  // Make sure we are built to the preceding build level.
250  build_created(mv_types, races, traits);
251 
252  type_name_ = cfg_["name"];
253  description_ = cfg_["description"];
254  hitpoints_ = cfg_["hitpoints"].to_int(1);
255  level_ = cfg_["level"];
256  recall_cost_ = cfg_["recall_cost"].to_int(-1);
257  movement_ = cfg_["movement"].to_int(1);
258  vision_ = cfg_["vision"].to_int(-1);
259  jamming_ = cfg_["jamming"].to_int(0);
260  max_attacks_ = cfg_["attacks"].to_int(1);
261  cost_ = cfg_["cost"].to_int(1);
262  usage_ = cfg_["usage"].str();
263  undead_variation_ = cfg_["undead_variation"].str();
264  image_ = cfg_["image"].str();
265  icon_ = cfg_["image_icon"].str();
266  small_profile_ = cfg_["small_profile"].str();
267  profile_ = cfg_["profile"].str();
269 
270  alignment_ = unit_type::ALIGNMENT::NEUTRAL;
271  alignment_.parse(cfg_["alignment"].str());
272 
273  for (int i = 0; i < 2; ++i) {
274  if (gender_types_[i])
275  gender_types_[i]->build_help_index(mv_types, races, traits);
276  }
277 
278  const race_map::const_iterator race_it = races.find(cfg_["race"]);
279  if(race_it != races.end()) {
280  race_ = &race_it->second;
281  } else {
283  }
284 
285  // if num_traits is not defined, we use the num_traits from race
286  num_traits_ = cfg_["num_traits"].to_int(race_->num_traits());
287 
288  const std::vector<std::string> genders = utils::split(cfg_["gender"]);
289  for(std::vector<std::string>::const_iterator g = genders.begin(); g != genders.end(); ++g) {
290  genders_.push_back(string_gender(*g));
291  }
292  // For simplicity in other parts of the code, we must have at least one gender.
293  if(genders_.empty()) {
294  genders_.push_back(unit_race::MALE);
295  }
296 
297  if (const config &abil_cfg = cfg_.child("abilities"))
298  {
299  for (const config::any_child &ab : abil_cfg.all_children_range()) {
300  const config::attribute_value &name = ab.cfg["name"];
301  if (!name.empty()) {
302  abilities_.push_back(name.t_str());
303  ability_tooltips_.push_back( ab.cfg["description"].t_str() );
304  }
305  }
306  }
307 
308  for (const config &adv : cfg_.child_range("advancement"))
309  {
310  for (const config &effect : adv.child_range("effect"))
311  {
312  const config &abil_cfg = effect.child("abilities");
313  if (!abil_cfg || effect["apply_to"] != "new_ability") {
314  continue;
315  }
316  for (const config::any_child &ab : abil_cfg.all_children_range()) {
317  const config::attribute_value &name = ab.cfg["name"];
318  if (!name.empty()) {
319  adv_abilities_.push_back(name.t_str());
320  adv_ability_tooltips_.push_back( ab.cfg["description"].t_str() );
321  }
322  }
323  }
324  }
325 
326  // Set the movement type.
327  const std::string move_type = cfg_["movement_type"];
328  const movement_type_map::const_iterator find_it = mv_types.find(move_type);
329  if ( find_it != mv_types.end() ) {
330  DBG_UT << "inheriting from movement_type '" << move_type << "'\n";
331  movement_type_ = find_it->second;
332  }
333  else if ( !move_type.empty() ) {
334  DBG_UT << "movement_type '" << move_type << "' not found\n";
335  }
336  // Override parts of the movement type with what is in our config.
338 
339  for (const config &t : traits)
340  {
341  possible_traits_.add_child("trait", t);
342  }
343  for (const config &var_cfg : cfg_.child_range("variation"))
344  {
345  const std::string& var_id = var_cfg["variation_id"].empty() ?
346  var_cfg["variation_name"] : var_cfg["variation_id"];
347 
348  unit_type *ut = new unit_type(var_cfg, id_);
349  ut->debug_id_ = debug_id_ + " [" + var_id + "]";
350  ut->base_id_ = base_id_; // In case this is not id_.
351  ut->build_help_index(mv_types, races, traits);
352  variations_.insert(std::make_pair(var_id, ut));
353  }
354 
355  hide_help_= cfg_["hide_help"].to_bool();
356 
358 }
359 
360 /**
361  * Load the most needed data into an empty unit_type (build to CREATE).
362  * This creates the gender-specific types (if needed) and also defines how much
363  * experience is needed to advance as well as what this advances to.
364  */
366  const race_map &races, const config::const_child_itors &traits)
367 {
368  // Don't build twice.
369  if ( CREATED <= build_status_ )
370  return;
371  // There is no preceding build level (other than being constructed).
372 
373  // These should still be nullptr from the constructor.
374  assert(gender_types_[0] == nullptr);
375  assert(gender_types_[1] == nullptr);
376 
377  if ( const config &male_cfg = cfg_.child("male") ) {
378  gender_types_[0] = new unit_type(male_cfg, id_);
379  gender_types_[0]->debug_id_ = debug_id_ + " (male)";
380  }
381 
382  if ( const config &female_cfg = cfg_.child("female") ) {
383  gender_types_[1] = new unit_type(female_cfg, id_);
384  gender_types_[1]->debug_id_ = debug_id_ + " (female)";
385  }
386 
387  for (int i = 0; i < 2; ++i) {
388  if (gender_types_[i])
389  gender_types_[i]->build_created(mv_types, races, traits);
390  }
391 
392  const std::string& advances_to_val = cfg_["advances_to"];
393  if(advances_to_val != "null" && advances_to_val != "")
394  advances_to_ = utils::split(advances_to_val);
395  DBG_UT << "unit_type '" << log_id() << "' advances to : " << advances_to_val << "\n";
396 
397  experience_needed_ = cfg_["experience"].to_int(500);
398 
400 }
401 
402 /**
403  * Performs a build of this to the indicated stage.
404  */
405 void unit_type::build(BUILD_STATUS status, const movement_type_map &movement_types,
406  const race_map &races, const config::const_child_itors &traits)
407 {
408  DBG_UT << "Building unit type " << log_id() << ", level " << status << '\n';
409 
410  switch (status) {
411  case NOT_BUILT:
412  // Already done in the constructor.
413  return;
414 
415  case CREATED:
416  // Build the basic data.
417  build_created(movement_types, races, traits);
418  return;
419 
420  case VARIATIONS: // Implemented as part of HELP_INDEXED
421  case HELP_INDEXED:
422  // Build the data needed to feed the help index.
423  build_help_index(movement_types, races, traits);
424  return;
425 
426  case FULL:
427  build_full(movement_types, races, traits);
428  return;
429 
430  default:
431  ERR_UT << "Build of unit_type to unrecognized status (" << status << ") requested." << std::endl;
432  // Build as much as possible.
433  build_full(movement_types, races, traits);
434  return;
435  }
436 }
437 
438 
440 {
442  else if (gender == unit_race::s_male) return get_gender_unit_type(unit_race::MALE);
443  else return *this;
444 }
445 
447 {
448  const size_t i = gender;
449  if(i < sizeof(gender_types_)/sizeof(*gender_types_)
450  && gender_types_[i] != nullptr) {
451  return *gender_types_[i];
452  }
453 
454  return *this;
455 }
456 
458 {
459  const variations_map::const_iterator i = variations_.find(id);
460  if(i != variations_.end()) {
461  return *i->second;
462  } else {
463  return *this;
464  }
465 }
466 
468 {
469  if(description_.empty()) {
470  return (_("No description available."));
471  } else {
472  return description_;
473  }
474 }
475 
476 const std::vector<unit_animation>& unit_type::animations() const {
477  if (animations_.empty()) {
479  }
480 
481  return animations_;
482 }
483 
484 std::vector<attack_type> unit_type::attacks() const
485 {
486  std::vector<attack_type> res;
487  for (const config &att : cfg_.child_range("attack")) {
488  res.push_back(attack_type(att));
489  }
490 
491  return res;
492 }
493 
494 
495 namespace {
496  int experience_modifier = 100;
497 }
498 
499 unit_experience_accelerator::unit_experience_accelerator(int modifier) : old_value_(experience_modifier)
500 {
501  experience_modifier = modifier;
502 }
503 
505 {
506  experience_modifier = old_value_;
507 }
508 
510 {
511  return experience_modifier;
512 }
513 
514 int unit_type::experience_needed(bool with_acceleration) const
515 {
516  if(with_acceleration) {
517  int exp = (experience_needed_ * experience_modifier + 50) /100;
518  if(exp < 1) exp = 1;
519  return exp;
520  }
521  return experience_needed_;
522 }
523 /*
524 const char* unit_type::alignment_description(unit_type::ALIGNMENT align, unit_race::GENDER gender)
525 {
526  static const char* aligns[] = { N_("lawful"), N_("neutral"), N_("chaotic"), N_("liminal") };
527  static const char* aligns_female[] = { N_("female^lawful"), N_("female^neutral"), N_("female^chaotic"), N_("female^liminal") };
528  const char** tlist = (gender == unit_race::MALE ? aligns : aligns_female);
529 
530  return (translation::sgettext(tlist[align]));
531 }*/
532 
533 bool unit_type::has_ability_by_id(const std::string& ability) const
534 {
535  if (const config &abil = cfg_.child("abilities"))
536  {
537  for (const config::any_child &ab : abil.all_children_range()) {
538  if (ab.cfg["id"] == ability)
539  return true;
540  }
541  }
542  return false;
543 }
544 
545 std::vector<std::string> unit_type::get_ability_list() const
546 {
547  std::vector<std::string> res;
548 
549  const config &abilities = cfg_.child("abilities");
550  if (!abilities) return res;
551 
552  for (const config::any_child &ab : abilities.all_children_range()) {
553  const std::string &id = ab.cfg["id"];
554  if (!id.empty())
555  res.push_back(id);
556  }
557 
558  return res;
559 }
560 
561 bool unit_type::hide_help() const {
562  return hide_help_ || unit_types.hide_help(id_, race_->id());
563 }
564 
565 void unit_type::add_advancement(const unit_type &to_unit,int xp)
566 {
567  const std::string &to_id = to_unit.id_;
568 
569  // Add extra advancement path to this unit type
570  LOG_CONFIG << "adding advancement from " << log_id() << " to " << to_unit.log_id() << "\n";
571  if(std::find(advances_to_.begin(), advances_to_.end(), to_id) == advances_to_.end()) {
572  advances_to_.push_back(to_id);
573  } else {
574  LOG_CONFIG << "advancement from " << log_id() << " to "
575  << to_unit.log_id() << " already known, ignoring.\n";
576  return;
577  }
578 
579  if(xp > 0) {
580  //xp is 0 in case experience= wasn't given.
581  if(!in_advancefrom_) {
582  //This function is called for and only for an [advancefrom] tag in a unit_type referencing this unit_type.
583  in_advancefrom_ = true;
584  experience_needed_ = xp;
585  DBG_UT << "Changing experience_needed from " << experience_needed_
586  << " to " << xp << " due to (first) [advancefrom] of "
587  << to_unit.log_id() << "\n";
588  }
589  else if(experience_needed_ > xp) {
590  experience_needed_ = xp;
591  DBG_UT << "Lowering experience_needed from " << experience_needed_
592  << " to " << xp << " due to (multiple, lower) [advancefrom] of "
593  << to_unit.log_id() << "\n";
594  }
595  else
596  DBG_UT << "Ignoring experience_needed change from " << experience_needed_
597  << " to " << xp << " due to (multiple, higher) [advancefrom] of "
598  << to_unit.log_id() << "\n";
599  }
600 
601  // Add advancements to gendered subtypes, if supported by to_unit
602  for(int gender=0; gender<=1; ++gender) {
603  if(gender_types_[gender] == nullptr) continue;
604  if(to_unit.gender_types_[gender] == nullptr) {
605  WRN_CF << to_unit.log_id() << " does not support gender " << gender << std::endl;
606  continue;
607  }
608  LOG_CONFIG << "gendered advancement " << gender << ": ";
609  gender_types_[gender]->add_advancement(*(to_unit.gender_types_[gender]),xp);
610  }
611 
612  if ( cfg_.has_child("variation") ) {
613  // Make sure the variations are created.
615 
616  // Add advancements to variation subtypes.
617  // Since these are still a rare and special-purpose feature,
618  // we assume that the unit designer knows what they're doing,
619  // and don't block advancements that would remove a variation.
621  v!=variations_.end(); ++v) {
622  LOG_CONFIG << "variation advancement: ";
623  v->second->add_advancement(to_unit,xp);
624  }
625  }
626 }
627 
628 static void advancement_tree_internal(const std::string& id, std::set<std::string>& tree)
629 {
630  const unit_type *ut = unit_types.find(id);
631  if (!ut)
632  return;
633 
634  for (const std::string& adv : ut->advances_to()) {
635  if (tree.insert(adv).second) {
636  // insertion succeed, expand the new type
637  advancement_tree_internal(adv, tree);
638  }
639  }
640 }
641 
642 std::set<std::string> unit_type::advancement_tree() const
643 {
644  std::set<std::string> tree;
646  return tree;
647 }
648 
649 const std::vector<std::string> unit_type::advances_from() const
650 {
651  // currently not needed (only help call us and already did it)
653 
654  std::vector<std::string> adv_from;
655  for (const unit_type_data::unit_type_map::value_type &ut : unit_types.types())
656  {
657  for (const std::string& adv : ut.second.advances_to()) {
658  if (adv == id_)
659  adv_from.push_back(ut.second.id());
660  }
661  }
662  return adv_from;
663 }
664 
665 
666 // This function is only meant to return the likely state a given status
667 // for a new recruit of this type. It should not be used to check if
668 // a particular unit has it, use get_state(status_name) for that.
669 bool unit_type::musthave_status(const std::string& status_name) const
670 {
671  // Statuses default to absent.
672  bool current_status = false;
673 
674  // Look at all of the "musthave" traits to see if the
675  // status gets changed. In the unlikely event it gets changed
676  // multiple times, we want to try to do it in the same order
677  // that unit::apply_modifications does things.
678  for (const config &mod : possible_traits())
679  {
680  if (mod["availability"] != "musthave")
681  continue;
682 
683  for (const config &effect : mod.child_range("effect"))
684  {
685  // See if the effect only applies to
686  // certain unit types But don't worry
687  // about gender checks, since we don't
688  // know what the gender of the
689  // hypothetical recruit is.
690  const std::string &ut = effect["unit_type"];
691  if (!ut.empty()) {
692  const std::vector<std::string> &types = utils::split(ut);
693  if(std::find(types.begin(), types.end(), id()) == types.end())
694  continue;
695  }
696 
697  // We're only interested in status changes.
698  if (effect["apply_to"] != "status") {
699  continue;
700  }
701  if (effect["add"] == status_name) {
702  current_status = true;
703  }
704  if (effect["remove"] == status_name) {
705  current_status = false;
706  }
707  }
708  }
709 
710  return current_status;
711 }
712 
714 {
715  if (num_traits() == 0) return false;
717  while(t.first != t.second) {
718  const config::attribute_value& availability = (*t.first)["availability"];
719  if(availability.blank()) return true;
720  if(strcmp(availability.str().c_str(), "musthave") != 0) return true;
721  ++t.first;
722  }
723  return false;
724 }
725 
726 std::vector<std::string> unit_type::variations() const
727 {
728  std::vector<std::string> retval;
729  retval.reserve(variations_.size());
730  for (const variations_map::value_type &val : variations_) {
731  retval.push_back(val.first);
732  }
733  return retval;
734 }
735 
736 bool unit_type::has_variation(const std::string& variation_id) const
737 {
738  return variations_.find(variation_id) != variations_.end();
739 }
740 
742 {
743  for (const variations_map::value_type &val : variations_) {
744  assert(val.second != nullptr);
745  if (!val.second->hide_help()) {
746  return true;
747  }
748  }
749 
750  return false;
751 }
752 
753 /**
754  * Generates (and returns) a trimmed config suitable for use with units.
755  */
757 {
758  // We start with all attributes.
759  assert(unit_cfg_.empty());
761 
762  // Remove "pure" unit_type attributes (attributes that do not get directly
763  // copied to units; some do get copied, but under different keys).
764  static char const *unit_type_attrs[] = { "attacks", "base_ids", "die_sound",
765  "experience", "flies", "healed_sound", "hide_help", "hitpoints",
766  "id", "ignore_race_traits", "inherit", "movement", "movement_type",
767  "name", "num_traits", "variation_id", "variation_name", "recall_cost",
768  "cost", "level", "gender", "flag_rgb", "alignment", "advances_to", "do_not_list"
769  };
770  for (const char *attr : unit_type_attrs) {
772  }
773  built_unit_cfg_ = true;
774  return unit_cfg_;
775 }
776 
777 int unit_type::resistance_against(const std::string& damage_name, bool attacker) const
778 {
779  int resistance = movement_type_.resistance_against(damage_name);
780  unit_ability_list resistance_abilities;
781  if (const config &abilities = cfg_.child("abilities")) {
782  for (const config& cfg : abilities.child_range("resistance")) {
783  if (!cfg["affect_self"].to_bool(true)) {
784  continue;
785  }
786  if (!resistance_filter_matches(cfg, attacker, damage_name, 100 - resistance)) {
787  continue;
788  }
789  resistance_abilities.push_back(unit_ability(&cfg, map_location::null_location()));
790  }
791  }
792  if (!resistance_abilities.empty()) {
793  unit_abilities::effect resist_effect(resistance_abilities, 100 - resistance, false);
794  resistance = 100 - std::min<int>(resist_effect.get_composite_value(),
795  resistance_abilities.highest("max_value").first);
796  }
797  return resistance;
798 }
799 
800 bool unit_type::resistance_filter_matches(const config& cfg, bool attacker, const std::string& damage_name, int res) const
801 {
802  if(!(cfg["active_on"].empty() || (attacker && cfg["active_on"]=="offense") || (!attacker && cfg["active_on"]=="defense"))) {
803  return false;
804  }
805  const std::string& apply_to = cfg["apply_to"];
806  if(!apply_to.empty()) {
807  if(damage_name != apply_to) {
808  if ( apply_to.find(',') != std::string::npos &&
809  apply_to.find(damage_name) != std::string::npos ) {
810  const std::vector<std::string>& vals = utils::split(apply_to);
811  if(std::find(vals.begin(),vals.end(),damage_name) == vals.end()) {
812  return false;
813  }
814  } else {
815  return false;
816  }
817  }
818  }
819  if (!unit_abilities::filter_base_matches(cfg, res)) return false;
820  return true;
821 }
822 
823 /** Implementation detail of unit_type::alignment_description */
824 
825 MAKE_ENUM (ALIGNMENT_FEMALE_VARIATION,
826  (LAWFUL, N_("female^lawful"))
827  (FEMALE_NEUTRAL, N_("female^neutral"))
828  (CHAOTIC, N_("female^chaotic"))
829  (LIMINAL, N_("female^liminal"))
830 )
831 
832 std::string unit_type::alignment_description(ALIGNMENT align, unit_race::GENDER gender)
833 {
834  static_assert(ALIGNMENT_FEMALE_VARIATION::count == ALIGNMENT::count, "ALIGNMENT_FEMALE_VARIATION and ALIGNMENT do not have the same number of values");
835  assert(align.valid());
836  std::string str = std::string();
837  if (gender == unit_race::FEMALE) {
838  ALIGNMENT_FEMALE_VARIATION fem = align.cast<ALIGNMENT_FEMALE_VARIATION::type>();
839  str = fem.to_string();
840  } else {
841  str = align.to_string();
842  }
843  return translation::sgettext(str.c_str());
844 }
845 
846 
847 /* ** unit_type_data ** */
848 
849 
851  types_(),
852  movement_types_(),
853  races_(),
854  hide_help_all_(false),
855  hide_help_type_(),
856  hide_help_race_(),
857  unit_cfg_(nullptr),
858  build_status_(unit_type::NOT_BUILT)
859 {
860 }
861 
862 
863 namespace { // Helpers for set_config()
864  /**
865  * Spits out an error message and throws a config::error.
866  * Called when apply_base_unit() detects a cycle.
867  * (This exists merely to take the error message out of that function.)
868  */
869  void throw_base_unit_recursion_error(
870  const std::vector<std::string> & base_tree, const std::string & base_id)
871  {
872  std::stringstream ss;
873  ss << "[base_unit] recursion loop in [unit_type] ";
874  for (const std::string &step : base_tree)
875  ss << step << "->";
876  ss << base_id;
877  ERR_CF << ss.str() << '\n';
878  throw config::error(ss.str());
879  }
880 
881  /**
882  * Locates the config for the unit type with id= @a key within @a all_types.
883  * Throws a config::error if the unit type cannot be found.
884  */
885  config & find_unit_type_config(const std::string & key, config & all_types)
886  {
887  config & cfg = all_types.find_child("unit_type", "id", key);
888  if ( cfg )
889  return cfg;
890 
891  // Bad WML!
892  ERR_CF << "unit type not found: " << key << std::endl;
893  ERR_CF << all_types << std::endl;
894  throw config::error("unit type not found: " + key);
895  }
896 
897  /**
898  * Modifies the provided config by merging all base units into it.
899  * The @a base_tree parameter is used solely for detecting and reporting
900  * cycles of base units; it is no longer needed to prevent infinite loops.
901  */
902  void apply_base_unit(config & ut_cfg, config & all_types,
903  std::vector<std::string> &base_tree)
904  {
905  // Get a list of base units to apply.
906  std::vector<std::string> base_ids;
907  for (config & base : ut_cfg.child_range("base_unit"))
908  base_ids.push_back(base["id"]);
909 
910  if ( base_ids.empty() )
911  // Nothing to do.
912  return;
913 
914  // Store the base ids for the help system.
915  ut_cfg["base_ids"] = utils::join(base_ids);
916 
917  // Clear the base units (otherwise they could interfere with the merge).
918  // This has the side-effect of breaking cycles, hence base_tree is
919  // merely for error detection, not error recovery.
920  ut_cfg.clear_children("base_unit");
921 
922  // Merge the base units, in order.
923  for (const std::string & base_id : base_ids) {
924  // Detect recursion so the WML author is made aware of an error.
925  if ( std::find(base_tree.begin(), base_tree.end(), base_id) != base_tree.end() )
926  throw_base_unit_recursion_error(base_tree, base_id);
927 
928  // Find the base unit.
929  config & base_cfg = find_unit_type_config(base_id, all_types);
930  // Make sure the base unit has had its base units accounted for.
931  base_tree.push_back(base_id);
932  apply_base_unit(base_cfg, all_types, base_tree);
933  base_tree.pop_back();
934 
935  // Merge the base unit "under" our config.
936  ut_cfg.inherit_from(base_cfg);
937  }
938  }
939 
940  /**
941  * Handles inheritance for configs of [male], [female], and [variation].
942  * Also removes gendered children, as those serve no purpose.
943  * @a default_inherit is the default value for inherit=.
944  */
945  void fill_unit_sub_type(config & var_cfg, const config & parent,
946  bool default_inherit)
947  {
948  if ( var_cfg["inherit"].to_bool(default_inherit) ) {
949  var_cfg.inherit_from(parent);
950  }
951  var_cfg.clear_children("male");
952  var_cfg.clear_children("female");
953  }
954 
955  /**
956  * Processes [variation] tags of @a ut_cfg, handling inheritance and
957  * child clearing.
958  */
959  void handle_variations(config & ut_cfg)
960  {
961  // Most unit types do not have variations.
962  if ( !ut_cfg.has_child("variation") )
963  return;
964 
965  // Pull the variations out of the base unit type.
966  config variations;
967  variations.splice_children(ut_cfg, "variation");
968 
969  // Handle each variation's inheritance.
970  for (config &var_cfg : variations.child_range("variation"))
971  fill_unit_sub_type(var_cfg, ut_cfg, false);
972 
973  // Restore the variations.
974  ut_cfg.splice_children(variations, "variation");
975  }
976 
977  const boost::regex fai_identifier("[a-zA-Z_]+");
978 
979  template<typename MoveT>
980  void patch_movetype(MoveT& mt, const std::string& new_key, const std::string& formula_str, int default_val, bool replace) {
981  config temp_cfg, original_cfg;
982  mt.write(original_cfg);
983  if (!replace && !original_cfg[new_key].blank()) {
984  // Don't replace if the key already exists in the config (even if empty).
985  return;
986  }
987  gui2::tformula<int> formula(formula_str);
989  boost::sregex_iterator m(formula_str.begin(), formula_str.end(), fai_identifier);
990  for (const boost::sregex_iterator::value_type& p : std::make_pair(m, boost::sregex_iterator())) {
991  const std::string var_name = p.str();
992  variant val(original_cfg[var_name].to_int(default_val));
993  original.add(var_name, val);
994  }
995  temp_cfg[new_key] = formula(original);
996  mt.merge(temp_cfg, true);
997  }
998 }// unnamed namespace
999 
1000 /**
1001  * Resets all data based on the provided config.
1002  * This includes some processing of the config, such as expanding base units.
1003  * A pointer to the config is stored, so the config must be persistent.
1004  */
1006 {
1007  DBG_UT << "unit_type_data::set_config, name: " << cfg["name"] << "\n";
1008 
1009  clear();
1010  unit_cfg_ = &cfg;
1011 
1012  for (const config &mt : cfg.child_range("movetype"))
1013  {
1014  movement_types_.insert(std::make_pair(mt["name"].str(), movetype(mt)));
1016  }
1017 
1018  for (const config &r : cfg.child_range("race"))
1019  {
1020  const unit_race race(r);
1021  races_.insert(std::pair<std::string,unit_race>(race.id(),race));
1023  }
1024 
1025  // Movetype resistance patching
1026  for (const config &r : cfg.child_range("resistance_defaults"))
1027  {
1028  const std::string& dmg_type = r["id"];
1029  config temp_cfg;
1030  for (const config::attribute &attr : r.attribute_range()) {
1031  const std::string &mt = attr.first;
1032  if (mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1033  continue;
1034  }
1035  patch_movetype(movement_types_[mt].get_resistances(), dmg_type, attr.second, 100, true);
1036  }
1037  if (r.has_attribute("default")) {
1038  for (movement_type_map::value_type &mt : movement_types_) {
1039  // Don't apply a default if a value is explicitly specified.
1040  if (r.has_attribute(mt.first)) {
1041  continue;
1042  }
1043  patch_movetype(mt.second.get_resistances(), dmg_type, r["default"], 100, false);
1044  }
1045  }
1046  }
1047 
1048  // Movetype move/defend patching
1049  for (const config &terrain : cfg.child_range("terrain_defaults"))
1050  {
1051  const std::string& ter_type = terrain["id"];
1052  config temp_cfg;
1053  static const std::string terrain_info_tags[] = {"movement", "vision", "jamming", "defense"};
1054  for (const std::string &tag : terrain_info_tags) {
1055  if (!terrain.has_child(tag)) {
1056  continue;
1057  }
1058  const config& info = terrain.child(tag);
1059  for (const config::attribute &attr : info.attribute_range()) {
1060  const std::string &mt = attr.first;
1061  if (mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1062  continue;
1063  }
1064  if (tag == "defense") {
1065  patch_movetype(movement_types_[mt].get_defense(), ter_type, attr.second, 100, true);
1066  } else if (tag == "vision") {
1067  patch_movetype(movement_types_[mt].get_vision(), ter_type, attr.second, 99, true);
1068  } else if (tag == "movement") {
1069  patch_movetype(movement_types_[mt].get_movement(), ter_type, attr.second, 99, true);
1070  } else if (tag == "jamming") {
1071  patch_movetype(movement_types_[mt].get_jamming(), ter_type, attr.second, 99, true);
1072  }
1073  }
1074  if (info.has_attribute("default")) {
1075  for (movement_type_map::value_type &mt : movement_types_) {
1076  // Don't apply a default if a value is explicitly specified.
1077  if (info.has_attribute(mt.first)) {
1078  continue;
1079  }
1080  if (tag == "defense") {
1081  patch_movetype(mt.second.get_defense(), ter_type, info["default"], 100, false);
1082  } else if (tag == "vision") {
1083  patch_movetype(mt.second.get_vision(), ter_type, info["default"], 99, false);
1084  } else if (tag == "movement") {
1085  patch_movetype(mt.second.get_movement(), ter_type, info["default"], 99, false);
1086  } else if (tag == "jamming") {
1087  patch_movetype(mt.second.get_jamming(), ter_type, info["default"], 99, false);
1088  }
1089  }
1090  }
1091  }
1092  }
1093 
1094  // Apply base units.
1095  for (config &ut : cfg.child_range("unit_type"))
1096  {
1097  if ( ut.has_child("base_unit") ) {
1098  // Derived units must specify a new id.
1099  // (An error message will be emitted later if id is empty.)
1100  const std::string id = ut["id"];
1101  if ( !id.empty() ) {
1102  std::vector<std::string> base_tree(1, id);
1103  apply_base_unit(ut, cfg, base_tree);
1105  }
1106  }
1107  }
1108 
1109  // Handle inheritance and recording of unit types.
1110  for (config &ut : cfg.child_range("unit_type"))
1111  {
1112  std::string id = ut["id"];
1113  // Every type is required to have an id.
1114  if ( id.empty() ) {
1115  ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
1116  continue;
1117  }
1118 
1119  // Complete the gender-specific children of the config.
1120  if ( config &male_cfg = ut.child("male") ) {
1121  fill_unit_sub_type(male_cfg, ut, true);
1122  handle_variations(male_cfg);
1123  }
1124  if ( config &female_cfg = ut.child("female") ) {
1125  fill_unit_sub_type(female_cfg, ut, true);
1126  handle_variations(female_cfg);
1127  }
1128 
1129  // Complete the variation-defining children of the config.
1130  handle_variations(ut);
1131 
1132  // Record this unit type.
1133  if ( insert(std::make_pair(id, unit_type(ut))).second ) {
1134  LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
1135  } else {
1136  ERR_CF << "Multiple [unit_type]s with id=" << id << " encountered." << std::endl;
1137  }
1138 
1140  }
1141 
1142  // Build all unit types. (This was not done within the loop for performance.)
1144 
1145  // Suppress some unit types (presumably used as base units) from the help.
1146  if (const config &hide_help = cfg.child("hide_help")) {
1147  hide_help_all_ = hide_help["all"].to_bool();
1149  }
1150 }
1151 
1152 /**
1153  * Finds a unit_type by its id() and makes sure it is built to the specified level.
1154  */
1156 {
1157  if (key.empty() || key == "random") return nullptr;
1158 
1159  DBG_CF << "trying to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1160  const unit_type_map::iterator itor = types_.find(key);
1161 
1162  //This might happen if units of another era are requested (for example for savegames)
1163  if (itor == types_.end()){
1164  /*
1165  for (unit_type_map::const_iterator ut = types_.begin(); ut != types_.end(); ut++)
1166  DBG_UT << "Known unit_types: key = '" << ut->first << "', id = '" << ut->second.log_id() << "'\n";
1167  */
1168  return nullptr;
1169  }
1170 
1171  // Make sure the unit_type is built to the requested level.
1172  build_unit_type(itor->second, status);
1173 
1174  return &itor->second;
1175 }
1176 
1177 void unit_type_data::check_types(const std::vector<std::string>& types) const
1178 {
1179  for (const std::string& type : types) {
1180  if(!find(type)) throw game::game_error("unknown unit type: " + type);
1181  }
1182 }
1183 
1185 {
1186  types_.clear();
1187  movement_types_.clear();
1188  races_.clear();
1190 
1191  hide_help_all_ = false;
1192  hide_help_race_.clear();
1193  hide_help_type_.clear();
1194 }
1195 
1197 {
1198  // Nothing to do if already built to the requested level.
1199  if ( status <= build_status_ )
1200  return;
1201  assert(unit_cfg_ != nullptr);
1202 
1203  for (unit_type_map::iterator u = types_.begin(), u_end = types_.end(); u != u_end; ++u) {
1204  build_unit_type(u->second, status);
1206  }
1207  // Handle [advancefrom] (once) after building to (at least) the CREATED level.
1208  // (Currently, this could be simply a test for build_status_ == NOT_BUILT,
1209  // but to guard against future changes, use a more thorough test.)
1211  for (unit_type_map::iterator u = types_.begin(), u_end = types_.end(); u != u_end; ++u) {
1212  add_advancement(u->second);
1213  }
1214 
1215  build_status_ = status;
1216 }
1217 
1219 {
1220  if (!cfg)
1221  return;
1222 
1223  hide_help_race_.push_back(std::set<std::string>());
1224  hide_help_type_.push_back(std::set<std::string>());
1225 
1226  std::vector<std::string> races = utils::split(cfg["race"]);
1227  hide_help_race_.back().insert(races.begin(), races.end());
1228 
1229  std::vector<std::string> types = utils::split(cfg["type"]);
1230  hide_help_type_.back().insert(types.begin(), types.end());
1231 
1232  std::vector<std::string> trees = utils::split(cfg["type_adv_tree"]);
1233  hide_help_type_.back().insert(trees.begin(), trees.end());
1234  for (const std::string& t_id : trees) {
1235  unit_type_map::iterator ut = types_.find(t_id);
1236  if (ut != types_.end()) {
1237  std::set<std::string> adv_tree = ut->second.advancement_tree();
1238  hide_help_type_.back().insert(adv_tree.begin(), adv_tree.end());
1239  }
1240  }
1241 
1242  // we call recursively all the imbricated [not] tags
1243  read_hide_help(cfg.child("not"));
1244 }
1245 
1247 {
1248  bool res = hide_help_all_;
1249  int lvl = hide_help_all_ ? 1 : 0; // first level is covered by 'all=yes'
1250 
1251  // supposed to be equal but let's be cautious
1252  int lvl_nb = std::min(hide_help_race_.size(), hide_help_type_.size());
1253 
1254  for (; lvl < lvl_nb; ++lvl) {
1255  if (hide_help_race_[lvl].count(race) || hide_help_type_[lvl].count(type))
1256  res = !res; // each level is a [not]
1257  }
1258  return res;
1259 }
1260 
1262 {
1263  const config& cfg = to_unit.get_cfg();
1264 
1265  for (const config &af : cfg.child_range("advancefrom"))
1266  {
1267  const std::string &from = af["unit"];
1268  int xp = af["experience"];
1269 
1270  unit_type_data::unit_type_map::iterator from_unit = types_.find(from);
1271 
1272  if (from_unit == types_.end()) {
1273  std::ostringstream msg;
1274  msg << "unit type '" << from << "' not found when resolving [advancefrom] tag for '"
1275  << to_unit.log_id() << "'";
1276  throw config::error(msg.str());
1277  }
1278 
1279  // Fix up advance_from references
1280  from_unit->second.add_advancement(to_unit, xp);
1281 
1282  DBG_UT << "Added advancement ([advancefrom]) from " << from << " to " << to_unit.log_id() << "\n";
1283  }
1284 }
1285 
1287 {
1288  race_map::const_iterator i = races_.find(key);
1289  return i != races_.end() ? &i->second : nullptr;
1290 }
1291 
1293 {
1294  assert(!id.empty());
1295  //we don't allow leading whitepaces
1296  if (id[0] == ' ') {
1297  throw error("Found unit type id with a leading whitespace \"" + id + "\"");
1298  }
1299  bool gave_wanrning = false;
1300  for (size_t pos = 0; pos < id.size(); ++pos) {
1301  const char c = id[pos];
1302  const bool valid = c == '_' || c == ' ' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
1303  if (!valid) {
1304  if (!gave_wanrning) {
1305  ERR_UT << "Found unit type id with invalid chracters: \"" << id << "\"\n";
1306  gave_wanrning = true;
1307  }
1308  id[pos] = '_';
1309  }
1310  }
1311 
1312 }
1313 
1315 
1316 
1318 {
1319  // Create a temp copy
1320  std::string temp = profile;
1321 
1322  static const std::string path_adjust = "/transparent";
1323  const std::string::size_type offset = profile.find_last_of('/', profile.find('~'));
1324 
1325  // If the path already refers to /transparent...
1326  if(profile.find(path_adjust) != std::string::npos && offset != std::string::npos) {
1327  if(!image::locator(profile).file_exists()) {
1328  profile.replace(profile.find(path_adjust), path_adjust.length(), "");
1329  }
1330 
1331  return;
1332  }
1333 
1334  // else, check for the file with /transparent appended...
1335  offset != std::string::npos ?
1336  temp.insert(offset, path_adjust) : temp = path_adjust + temp;
1337 
1338  // and use that path if it exists
1339  if(image::locator(temp).file_exists()) {
1340  profile = temp;
1341  }
1342 }
int jamming_
Definition: types.hpp:256
child_itors child_range(const std::string &key)
Definition: config.cpp:613
void set_config(config &cfg)
Resets all data based on the provided config.
Definition: types.cpp:1005
double to_double(double def=0.) const
Definition: config.cpp:333
int experience_needed_
Definition: types.hpp:287
void remove_attribute(const std::string &key)
Definition: config.cpp:534
std::string str() const
Definition: config.cpp:353
#define DBG_CF
Definition: types.cpp:43
static void advancement_tree_internal(const std::string &id, std::set< std::string > &tree)
Definition: types.cpp:628
unit_type::BUILD_STATUS build_status_
Definition: types.hpp:353
static const std::string s_male
Standard string id (not translatable) for MALE.
Definition: race.hpp:27
double hp_bar_scaling
void read_hide_help(const config &cfg)
Parses the [hide_help] tag.
Definition: types.cpp:1218
#define DBG_UT
Definition: types.cpp:46
bool uses_global_traits() const
Definition: race.cpp:133
static int get_acceleration()
Definition: types.cpp:509
void append_attributes(const config &cfg)
Adds attributes from cfg.
Definition: config.cpp:549
const std::string log_id() const
A variant on id() that is more descriptive, for use with message logging.
Definition: types.hpp:117
double xp_bar_scaling
bool has_variation(const std::string &variation_id) const
Definition: types.cpp:736
bool hide_help(const std::string &type_id, const std::string &race_id) const
Checks if the [hide_help] tag contains these IDs.
Definition: types.cpp:1246
const unit_type & get_gender_unit_type(std::string gender) const
Definition: types.cpp:439
const std::vector< t_string > & abilities() const
Definition: types.hpp:166
std::string id_
Definition: formula.cpp:636
const std::string race
const std::vector< std::string > advances_from() const
Definition: types.cpp:649
ALIGNMENT alignment_
Definition: types.hpp:291
std::set< std::string > advancement_tree() const
Get the advancement tree Build a set of unit type's id of this unit type's advancement tree...
Definition: types.cpp:642
std::string profile_
Definition: types.hpp:265
const GLfloat * c
Definition: glew.h:12741
int pos
Definition: formula.cpp:800
const std::string & undead_variation() const
Definition: race.hpp:46
std::vector< t_string > adv_abilities_
Definition: types.hpp:281
bool musthave_status(const std::string &status) const
Definition: types.cpp:669
std::string debug_id_
Definition: types.hpp:246
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
t_string t_str() const
Definition: config.cpp:358
int hitpoints_
Definition: types.hpp:250
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
int recall_cost_
Definition: types.hpp:253
std::string base_id_
A suffix for id_, used when logging messages.
Definition: types.hpp:247
logger & info()
Definition: log.cpp:91
bool has_ability_by_id(const std::string &ability) const
Definition: types.cpp:533
bool file_exists() const
Tests whether the file the locater points at exists.
Definition: image.cpp:628
~unit_type()
Definition: types.cpp:166
attribute_map::value_type attribute
Definition: config.hpp:393
std::vector< std::string > get_ability_list() const
Definition: types.cpp:545
surface image_
The image is cached in this surface.
Definition: canvas.cpp:961
const std::string & id() const
Definition: race.hpp:33
t_string description_
Definition: types.hpp:249
GLuint const GLfloat * val
Definition: glew.h:2614
void build_all(unit_type::BUILD_STATUS status)
Makes sure the all unit_types are built to the specified level.
Definition: types.cpp:1196
config possible_traits_
Definition: types.hpp:295
unit_type_data unit_types
Definition: types.cpp:1314
STL namespace.
BUILD_STATUS
Records the status of the lazy building of unit types.
Definition: types.hpp:64
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: abilities.cpp:426
GLboolean GLboolean g
Definition: glew.h:7319
void clear()
Definition: config.cpp:1055
const std::vector< unit_animation > & animations() const
Definition: types.cpp:476
t_string type_name_
The id of the top ancestor of this unit_type.
Definition: types.hpp:248
bool built_unit_cfg_
Generated as needed via get_cfg_for_units().
Definition: types.hpp:243
variations_map variations_
Definition: types.hpp:273
void merge(const config &new_cfg, bool overwrite=true)
Merges the given config over the existing data.
Definition: movetype.cpp:754
const config & cfg_
Definition: types.hpp:241
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:28
static config unit_type(const unit *u)
Definition: reports.cpp:161
void clear_children(const std::string &key)
Definition: config.cpp:820
bool empty() const
Definition: config.cpp:1105
bool has_random_traits() const
Definition: types.cpp:713
bool empty() const
Tests for an attribute that either was never set or was set to "".
Definition: config.cpp:375
GLdouble GLdouble t
Definition: glew.h:1366
void build_help_index(const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Partially load data into an empty unit_type (build to HELP_INDEXED).
Definition: types.cpp:243
static lg::log_domain log_config("config")
GLsizei GLenum GLenum * types
Definition: glew.h:3155
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Definition: config.hpp:214
Variant for storing WML attributes.
Definition: config.hpp:223
int level_
Definition: types.hpp:252
Template class can hold a value or a formula to calculate the value.
Definition: formula.hpp:48
const unit_race * race_
Definition: types.hpp:277
const config::const_child_itors & additional_traits() const
Definition: race.cpp:138
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:992
bool blank() const
Tests for an attribute that was never set.
Definition: config.cpp:367
static const std::string s_female
Standard string id (not translatable) for FEMALE.
Definition: race.hpp:26
#define ERR_UT
Definition: types.cpp:47
GLintptr offset
Definition: glew.h:1650
bool in_advancefrom_
Definition: types.hpp:288
std::string icon_
Definition: types.hpp:263
void inherit_from(const config &c)
Merge config 'c' into this config, preserving this config's values.
Definition: config.cpp:1401
bool has_child(const std::string &key) const
Determine whether a config has a child or not.
Definition: config.cpp:651
const config & build_unit_cfg() const
Generates (and returns) a trimmed config suitable for use with units.
Definition: types.cpp:756
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
const unit_race * find_race(const std::string &) const
Definition: types.cpp:1286
void splice_children(config &src, const std::string &key)
Moves all the children with tag key from src to this.
Definition: config.cpp:837
movement_type_map movement_types_
Definition: types.hpp:343
bool zoc_
Definition: types.hpp:284
static const unit_race null_race
Dummy race used when a race is not yet known.
Definition: race.hpp:49
const unit_type_map & types() const
Definition: types.hpp:313
std::vector< t_string > adv_ability_tooltips_
Definition: types.hpp:282
std::vector< std::set< std::string > > hide_help_type_
Definition: types.hpp:349
void add_color_info(const config &v)
const GLdouble * v
Definition: glew.h:1359
std::vector< std::set< std::string > > hide_help_race_
Definition: types.hpp:350
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
std::vector< unit_animation > animations_
Definition: types.hpp:300
std::string small_profile_
Definition: types.hpp:264
void add_advancement(unit_type &to_unit) const
Definition: types.cpp:1261
bool resistance_filter_matches(const config &cfg, bool attacker, const std::string &damage_name, int res) const
Identical to unit::resistance_filter_matches.
Definition: types.cpp:800
const std::vector< unit_race::GENDER > & genders() const
The returned vector will not be empty, provided this has been built to the HELP_INDEXED status...
Definition: types.hpp:198
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:741
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:161
std::string undead_variation_
Definition: types.hpp:260
void build_unit_type(const unit_type &ut, unit_type::BUILD_STATUS status) const
Makes sure the provided unit_type is built to the specified level.
Definition: types.hpp:326
void add_advancement(const unit_type &advance_to, int experience)
Adds an additional advancement path to a unit type.
Definition: types.cpp:565
config & add_child(const std::string &key)
Definition: config.cpp:743
int movement_
Definition: types.hpp:254
movetype movement_type_
Definition: types.hpp:293
std::map< std::string, movetype > movement_type_map
Definition: types.hpp:33
static const map_location & null_location()
Definition: location.hpp:195
GLfloat GLfloat p
Definition: glew.h:12766
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:135
Templates and utility-routines for strings and numbers.
#define WRN_CF
Definition: types.cpp:41
static lg::log_domain log_unit("unit")
void check_types(const std::vector< std::string > &types) const
Definition: types.cpp:1177
double xp_bar_scaling_
Definition: types.hpp:251
GLuint GLuint GLsizei count
Definition: glew.h:1221
unit_type * gender_types_[2]
Definition: types.hpp:270
std::string join(T const &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::pair< unit_type_map::iterator, bool > insert(const std::pair< std::string, unit_type > &utype)
Definition: types.hpp:336
int cost_
Definition: types.hpp:258
bool empty() const
Definition: unit.hpp:80
void build_created(const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Load the most needed data into an empty unit_type (build to CREATE).
Definition: types.cpp:365
void build(BUILD_STATUS status, const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Performs a build of this to the indicated stage.
Definition: types.cpp:405
const std::vector< std::string > & advances_to() const
Definition: types.hpp:97
GLuint res
Definition: glew.h:9258
static void progress(const char *stage_name=nullptr)
Definition: loadscreen.cpp:128
unsigned int num_traits_
Definition: types.hpp:268
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:213
void build_full(const movement_type_map &movement_types, const race_map &races, const config::const_child_itors &traits)
Load data into an empty unit_type (build to FULL).
Definition: types.cpp:179
unit_type_map types_
Definition: types.hpp:342
const config * unit_cfg_
Definition: types.hpp:352
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:457
std::pair< const config *, map_location > unit_ability
The things contained within a unit_ability_list.
Definition: unit.hpp:43
config::const_child_itors possible_traits() const
Definition: types.hpp:182
const_attr_itors attribute_range() const
Definition: config.cpp:984
std::vector< std::string > advances_to_
Definition: types.hpp:286
std::string usage_
Definition: types.hpp:259
#define ERR_CF
Definition: types.cpp:40
const race_map & races() const
Definition: types.hpp:314
size_t i
Definition: function.cpp:1057
std::vector< std::string > variations() const
Definition: types.cpp:726
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
double hp_bar_scaling_
Definition: types.hpp:251
bool hide_help_all_
True if [hide_help] contains a 'all=yes' at its root.
Definition: types.hpp:347
map_formula_callable & add(const std::string &key, const variant &value)
Definition: formula.cpp:41
static void fill_initial_animations(std::vector< unit_animation > &animations, const config &cfg)
Definition: animation.cpp:469
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
#define N_(String)
Definition: gettext.hpp:90
bool hide_help() const
Definition: types.cpp:561
const config & get_cfg() const
Definition: types.hpp:222
fixed_t alpha_
Never nullptr, but may point to the null race.
Definition: types.hpp:279
std::string id_
Definition: types.hpp:245
unit_experience_accelerator(int modifier)
Definition: types.cpp:499
std::string replace(std::string str, const std::string &src, const std::string &dst)
Replace all instances of src in str with dst.
GLuint const GLchar * name
Definition: glew.h:1782
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:514
std::vector< t_string > abilities_
Definition: types.hpp:281
int vision_
Definition: types.hpp:255
MAKE_ENUM(ALIGNMENT_FEMALE_VARIATION,(LAWFUL, N_("female^lawful"))(FEMALE_NEUTRAL, N_("female^neutral"))(CHAOTIC, N_("female^chaotic"))(LIMINAL, N_("female^liminal"))) std
Implementation detail of unit_type::alignment_description.
Definition: types.cpp:825
bool has_attribute(const std::string &key) const
Definition: config.cpp:514
BUILD_STATUS build_status_
Definition: types.hpp:302
const GLdouble * m
Definition: glew.h:6968
void clear()
Definition: types.cpp:1184
#define g
Definition: glew.h:12730
int max_attacks_
Definition: types.hpp:257
bool find(E event, F functor)
Tests whether an event handler is available.
unit_type(const config &cfg, const std::string &parent_id="")
Creates a unit type for the given config, but delays its build till later.
Definition: types.cpp:110
#define LOG_CONFIG
Definition: types.cpp:42
static UNUSEDNOWARN std::string sgettext(const char *str)
Definition: gettext.hpp:66
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
bool hide_help_
Definition: types.hpp:284
std::vector< attack_type > attacks() const
Definition: types.cpp:484
config & find_child(const std::string &key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:1010
void adjust_profile(std::string &profile)
Definition: types.cpp:1317
Standard logging facilities (interface).
static void check_id(std::string &id)
Definition: types.cpp:1292
unsigned int num_traits() const
Definition: types.hpp:109
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1155
config unit_cfg_
Definition: types.hpp:242
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.
void push_back(const unit_ability &ability)
Definition: unit.hpp:87
int get_composite_value() const
Definition: abilities.hpp:50
std::vector< t_string > ability_tooltips_
Definition: types.hpp:282
unsigned int num_traits() const
Definition: race.cpp:148
std::map< std::string, unit_race > race_map
Definition: race.hpp:73
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
Defines the MAKE_ENUM macro.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::string image_
Definition: types.hpp:262
race_map races_
Definition: types.hpp:344
bool empty() const
Definition: tstring.hpp:166
int resistance_against(const std::string &damage_name, bool attacker) const
Gets resistance while considering custom WML abilities.
Definition: types.cpp:777
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:115
std::vector< unit_race::GENDER > genders_
Definition: types.hpp:297
t_string unit_description() const
Definition: types.cpp:467
#define ftofxp(x)
IN: float or int - OUT: fixed_t.
Definition: util.hpp:503
const std::string valid
Little parts of regex templates used to parse Wml annoations.