The Battle for Wesnoth  1.15.3+dev
types.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://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 "units/types.hpp"
21 
22 #include "game_config.hpp"
23 #include "game_errors.hpp" //thrown sometimes
24 //#include "gettext.hpp"
25 #include "log.hpp"
26 #include "units/abilities.hpp"
27 #include "units/animation.hpp"
28 #include "units/unit.hpp"
29 #include "utils/iterable_pair.hpp"
30 #include "utils/make_enum.hpp"
31 
34 
35 #include <boost/regex.hpp>
36 #include <boost/range/algorithm_ext/erase.hpp>
37 
38 #include <array>
39 #include <locale>
40 
41 static lg::log_domain log_config("config");
42 #define ERR_CF LOG_STREAM(err, log_config)
43 #define WRN_CF LOG_STREAM(warn, log_config)
44 #define LOG_CONFIG LOG_STREAM(info, log_config)
45 #define DBG_CF LOG_STREAM(debug, log_config)
46 
47 static lg::log_domain log_unit("unit");
48 #define DBG_UT LOG_STREAM(debug, log_unit)
49 #define LOG_UT LOG_STREAM(info, log_unit)
50 #define ERR_UT LOG_STREAM(err, log_unit)
51 
52 /* ** unit_type ** */
53 
55  : cfg_(o.cfg_)
56  , id_(o.id_)
57  , debug_id_(o.debug_id_)
58  , parent_id_(o.parent_id_)
59  , base_unit_id_(o.base_unit_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  , variation_name_(o.variation_name_)
83  , race_(o.race_)
84  , abilities_(o.abilities_)
85  , adv_abilities_(o.adv_abilities_)
86  , zoc_(o.zoc_)
87  , hide_help_(o.hide_help_)
88  , do_not_list_(o.do_not_list_)
89  , advances_to_(o.advances_to_)
90  , experience_needed_(o.experience_needed_)
91  , alignment_(o.alignment_)
92  , movement_type_(o.movement_type_)
93  , possible_traits_(o.possible_traits_)
94  , genders_(o.genders_)
95  , animations_(o.animations_)
96  , build_status_(o.build_status_)
97 {
98  gender_types_[0].reset(gender_types_[0] != nullptr ? new unit_type(*o.gender_types_[0]) : nullptr);
99  gender_types_[1].reset(gender_types_[1] != nullptr ? new unit_type(*o.gender_types_[1]) : nullptr);
100 }
101 
102 unit_type::unit_type(defaut_ctor_t, const config& cfg, const std::string & parent_id)
103  : cfg_(nullptr)
104  , built_cfg_()
105  , has_cfg_build_()
106  , id_(cfg.has_attribute("id") ? cfg["id"].str() : parent_id)
107  , debug_id_()
108  , parent_id_(!parent_id.empty() ? parent_id : id_)
109  , base_unit_id_()
110  , type_name_()
111  , description_()
112  , hitpoints_(0)
113  , hp_bar_scaling_(0.0)
114  , xp_bar_scaling_(0.0)
115  , level_(0)
116  , recall_cost_()
117  , movement_(0)
118  , vision_(-1)
119  , jamming_(0)
120  , max_attacks_(0)
121  , cost_(0)
122  , usage_()
124  , image_()
125  , icon_()
126  , small_profile_()
127  , profile_()
128  , flag_rgb_()
129  , num_traits_(0)
130  , gender_types_()
131  , variations_()
133  , variation_name_()
134  , race_(&unit_race::null_race)
135  , abilities_()
136  , adv_abilities_()
137  , zoc_(false)
138  , hide_help_(false)
139  , do_not_list_()
140  , advances_to_()
141  , experience_needed_(0)
142  , alignment_(unit_type::ALIGNMENT::NEUTRAL)
143  , movement_type_()
144  , possible_traits_()
145  , genders_()
146  , animations_()
148 {
149  if(const config& base_unit = cfg.child("base_unit")) {
150  base_unit_id_ = base_unit["id"].str();
151  LOG_UT << "type '" << id_ << "' has base unit '" << base_unit_id_ << "'\n";
152  }
153  check_id(id_);
155 }
156 unit_type::unit_type(const config& cfg, const std::string & parent_id)
157  : unit_type(defaut_ctor_t(), cfg, parent_id)
158 {
159  cfg_ = &cfg;
160 
161 }
162 
163 unit_type::unit_type(config&& cfg, const std::string & parent_id)
164  : unit_type(defaut_ctor_t(), cfg, parent_id)
165 {
166  built_cfg_ = std::make_unique<config>(std::move(cfg));
167 }
168 
169 
171 {
172 }
173 
175  : id(cfg["id"])
176  , name(cfg["name"].t_str())
177  , name_inactive(cfg["name_inactive"].t_str())
178  , female_name(cfg["female_name"].t_str())
179  , female_name_inactive(cfg["female_name_inactive"].t_str())
180  , description(cfg["description"].t_str())
181  , description_inactive(cfg["description_inactive"].t_str())
182  , affect_self(cfg["affect_self"].to_bool())
183  , affect_allies(cfg["affect_allies"].to_bool())
184  , affect_enemies(cfg["affect_enemies"].to_bool())
185  , cumulative(cfg["cumulative"].to_bool())
186 {
187 }
188 
189 /**
190  * Load data into an empty unit_type (build to FULL).
191  */
193  const movement_type_map& mv_types, const race_map& races, const config_array_view& traits)
194 {
195  // Don't build twice.
196  if(FULL <= build_status_) {
197  return;
198  }
199 
200  // Make sure we are built to the preceding build level.
201  build_help_index(mv_types, races, traits);
202 
203  for(int i = 0; i < 2; ++i) {
204  if(gender_types_[i]) {
205  gender_types_[i]->build_full(mv_types, races, traits);
206  }
207  }
208 
209  if(race_ != &unit_race::null_race) {
210  if(undead_variation_.empty()) {
212  }
213  }
214 
215  zoc_ = get_cfg()["zoc"].to_bool(level_ > 0);
216 
218 
219  hp_bar_scaling_ = get_cfg()["hp_bar_scaling"].to_double(game_config::hp_bar_scaling);
220  xp_bar_scaling_ = get_cfg()["xp_bar_scaling"].to_double(game_config::xp_bar_scaling);
221 
222  // Propagate the build to the variations.
223  for(variations_map::value_type& variation : variations_) {
224  variation.second.build_full(mv_types, races, traits);
225  }
226 
227  // Deprecation messages, only seen when unit is parsed for the first time.
228 
230 }
231 
232 /**
233  * Partially load data into an empty unit_type (build to HELP_INDEXED).
234  */
236  const movement_type_map& mv_types, const race_map& races, const config_array_view& traits)
237 {
238  // Don't build twice.
239  if(HELP_INDEXED <= build_status_) {
240  return;
241  }
242 
243  // Make sure we are built to the preceding build level.
244  build_created();
245 
246  const config& cfg = get_cfg();
247 
248  type_name_ = cfg["name"];
249  description_ = cfg["description"];
250  hitpoints_ = cfg["hitpoints"].to_int(1);
251  level_ = cfg["level"];
252  recall_cost_ = cfg["recall_cost"].to_int(-1);
253  movement_ = cfg["movement"].to_int(1);
254  vision_ = cfg["vision"].to_int(-1);
255  jamming_ = cfg["jamming"].to_int(0);
256  max_attacks_ = cfg["attacks"].to_int(1);
257  usage_ = cfg["usage"].str();
258  undead_variation_ = cfg["undead_variation"].str();
259  default_variation_ = cfg["variation"].str();
260  image_ = cfg["image"].str();
261  icon_ = cfg["image_icon"].str();
262  small_profile_ = cfg["small_profile"].str();
263  profile_ = cfg["profile"].str();
264  flag_rgb_ = cfg["flag_rgb"].str();
265  do_not_list_ = cfg["do_not_list"].to_bool(false);
266 
267  for(const config& sn : cfg.child_range("special_note")) {
268  special_notes_.push_back(sn["note"]);
269  }
270 
272 
273  alignment_ = unit_type::ALIGNMENT::NEUTRAL;
274  alignment_.parse(cfg["alignment"].str());
275 
276  for(int i = 0; i < 2; ++i) {
277  if(gender_types_[i]) {
278  gender_types_[i]->build_help_index(mv_types, races, traits);
279  }
280  }
281 
282  for(auto& pair : variations_) {
283  pair.second.build_help_index(mv_types, races, traits);
284  }
285 
286  const race_map::const_iterator race_it = races.find(cfg["race"]);
287  if(race_it != races.end()) {
288  race_ = &race_it->second;
289  } else {
291  }
292 
293  // if num_traits is not defined, we use the num_traits from race
294  num_traits_ = cfg["num_traits"].to_int(race_->num_traits());
295 
296  for(const std::string& g : utils::split(cfg["gender"])) {
297  genders_.push_back(string_gender(g));
298  }
299 
300  // For simplicity in other parts of the code, we must have at least one gender.
301  if(genders_.empty()) {
302  genders_.push_back(unit_race::MALE);
303  }
304 
305  if(const config& abil_cfg = cfg.child("abilities")) {
306  for(const config::any_child& ab : abil_cfg.all_children_range()) {
307  abilities_.emplace_back(ab.cfg);
308  }
309  }
310 
311  for(const config& adv : cfg.child_range("advancement")) {
312  for(const config& effect : adv.child_range("effect")) {
313  const config& abil_cfg = effect.child("abilities");
314 
315  if(!abil_cfg || effect["apply_to"] != "new_ability") {
316  continue;
317  }
318 
319  for(const config::any_child& ab : abil_cfg.all_children_range()) {
320  adv_abilities_.emplace_back(ab.cfg);
321  }
322  }
323  }
324 
325  // Set the movement type.
326  const std::string move_type = cfg["movement_type"];
327  const movement_type_map::const_iterator find_it = mv_types.find(move_type);
328 
329  if(find_it != mv_types.end()) {
330  DBG_UT << "inheriting from movement_type '" << move_type << "'\n";
331  movement_type_ = find_it->second;
332  } else if(!move_type.empty()) {
333  DBG_UT << "movement_type '" << move_type << "' not found\n";
334  }
335 
336  // Override parts of the movement type with what is in our config.
337  movement_type_.merge(cfg);
338 
339  for(const config& t : traits) {
340  possible_traits_.add_child("trait", t);
341  }
342 
343  if(race_ != &unit_race::null_race) {
344  if(!race_->uses_global_traits()) {
346  }
347 
348  if(cfg["ignore_race_traits"].to_bool()) {
350  } else {
351  for(const config& t : race_->additional_traits()) {
352  if(alignment_ != unit_type::ALIGNMENT::NEUTRAL || t["id"] != "fearless")
353  possible_traits_.add_child("trait", t);
354  }
355  }
356 
357  if(undead_variation_.empty()) {
359  }
360  }
361 
362  // Insert any traits that are just for this unit type
363  for(const config& trait : cfg.child_range("trait")) {
364  possible_traits_.add_child("trait", trait);
365  }
366 
367  hide_help_ = cfg["hide_help"].to_bool();
368 
370 }
371 
372 /**
373  * Load the most needed data into an empty unit_type (build to CREATE).
374  * This creates the gender-specific types (if needed) and also defines how much
375  * experience is needed to advance as well as what this advances to.
376  */
378 {
379  // Don't build twice.
380  if(CREATED <= build_status_) {
381  return;
382  }
383 
384 
385  for(unsigned i = 0; i < gender_types_.size(); ++i) {
386  if(gender_types_[i]) {
387  gender_types_[i]->build_created();
388  }
389  }
390 
391  for(auto& pair : variations_) {
392  pair.second.build_created();
393  }
394 
395 
396  const config& cfg = get_cfg();
397 
398  const std::string& advances_to_val = cfg["advances_to"];
399  if(advances_to_val != "null" && !advances_to_val.empty()) {
400  advances_to_ = utils::split(advances_to_val);
401  }
402 
403 
404  type_name_ = cfg["name"].t_str();
405  variation_name_ = cfg["variation_name"].t_str();
406 
407  DBG_UT << "unit_type '" << log_id() << "' advances to : " << advances_to_val << "\n";
408 
409  experience_needed_ = cfg["experience"].to_int(500);
410  cost_ = cfg["cost"].to_int(1);
411 
413 }
414 
415 /**
416  * Performs a build of this to the indicated stage.
417  */
419  const movement_type_map& movement_types,
420  const race_map& races,
421  const config_array_view& traits)
422 {
423  DBG_UT << "Building unit type " << log_id() << ", level " << status << '\n';
424 
425  switch(status) {
426  case NOT_BUILT:
427  // Already done in the constructor.
428  return;
429 
430  case CREATED:
431  // Build the basic data.
432  build_created();
433  return;
434 
435  case VARIATIONS: // Implemented as part of HELP_INDEXED
436  case HELP_INDEXED:
437  // Build the data needed to feed the help index.
438  build_help_index(movement_types, races, traits);
439  return;
440 
441  case FULL:
442  build_full(movement_types, races, traits);
443  return;
444 
445  default:
446  ERR_UT << "Build of unit_type to unrecognized status (" << status << ") requested." << std::endl;
447  // Build as much as possible.
448  build_full(movement_types, races, traits);
449  return;
450  }
451 }
452 
453 const unit_type& unit_type::get_gender_unit_type(std::string gender) const
454 {
455  if(gender == unit_race::s_female) {
457  } else if(gender == unit_race::s_male) {
459  }
460 
461  return *this;
462 }
463 
465 {
466  const std::size_t i = gender;
467  if(i < gender_types_.size() && gender_types_[i] != nullptr) {
468  return *gender_types_[i];
469  }
470 
471  return *this;
472 }
473 
474 const unit_type& unit_type::get_variation(const std::string& id) const
475 {
476  const variations_map::const_iterator i = variations_.find(id);
477  if(i != variations_.end()) {
478  return i->second;
479  }
480 
481  return *this;
482 }
483 
485 {
486  if(description_.empty()) {
487  return (_("No description available."));
488  } else {
489  return description_;
490  }
491 }
492 
494 {
495  return !special_notes_.empty();
496 }
497 
498 const std::vector<t_string>& unit_type::special_notes() const {
499  return special_notes_;
500 }
501 
502 const std::vector<unit_animation>& unit_type::animations() const
503 {
504  if(animations_.empty()) {
505  unit_animation::fill_initial_animations(animations_, get_cfg());
506  }
507 
508  return animations_;
509 }
510 
512 {
513  if(!attacks_cache_.empty()) {
515  }
516 
517  for(const config& att : get_cfg().child_range("attack")) {
518  attacks_cache_.emplace_back(new attack_type(att));
519  }
520 
522 }
523 
524 namespace
525 {
526 int experience_modifier = 100;
527 }
528 
530  : old_value_(experience_modifier)
531 {
532  experience_modifier = modifier;
533 }
534 
536 {
537  experience_modifier = old_value_;
538 }
539 
541 {
542  return experience_modifier;
543 }
544 
545 int unit_type::experience_needed(bool with_acceleration) const
546 {
547  if(with_acceleration) {
548  int exp = (experience_needed_ * experience_modifier + 50) / 100;
549  if(exp < 1) {
550  exp = 1;
551  }
552 
553  return exp;
554  }
555 
556  return experience_needed_;
557 }
558 
559 bool unit_type::has_ability_by_id(const std::string& ability) const
560 {
561  if(const config& abil = get_cfg().child("abilities")) {
562  for(const config::any_child& ab : abil.all_children_range()) {
563  if(ab.cfg["id"] == ability) {
564  return true;
565  }
566  }
567  }
568 
569  return false;
570 }
571 
572 std::vector<std::string> unit_type::get_ability_list() const
573 {
574  std::vector<std::string> res;
575 
576  const config& abilities = get_cfg().child("abilities");
577  if(!abilities) {
578  return res;
579  }
580 
581  for(const config::any_child& ab : abilities.all_children_range()) {
582  std::string id = ab.cfg["id"];
583 
584  if(!id.empty()) {
585  res.push_back(std::move(id));
586  }
587  }
588 
589  return res;
590 }
591 
593 {
594  return hide_help_ || unit_types.hide_help(id_, race_->id());
595 }
596 
597 
598 static void advancement_tree_internal(const std::string& id, std::set<std::string>& tree)
599 {
600  const unit_type* ut = unit_types.find(id);
601  if(!ut) {
602  return;
603  }
604 
605  for(const std::string& adv : ut->advances_to()) {
606  if(tree.insert(adv).second) {
607  // insertion succeed, expand the new type
608  advancement_tree_internal(adv, tree);
609  }
610  }
611 }
612 
613 std::set<std::string> unit_type::advancement_tree() const
614 {
615  std::set<std::string> tree;
616  advancement_tree_internal(id_, tree);
617  return tree;
618 }
619 
620 const std::vector<std::string> unit_type::advances_from() const
621 {
622  // Currently not needed (only help call us and already did it)/
624 
625  std::vector<std::string> adv_from;
626  for(const unit_type_data::unit_type_map::value_type& ut : unit_types.types()) {
627  for(const std::string& adv : ut.second.advances_to()) {
628  if(adv == id_) {
629  adv_from.push_back(ut.second.id());
630  }
631  }
632  }
633 
634  return adv_from;
635 }
636 
637 // This function is only meant to return the likely state a given status
638 // for a new recruit of this type. It should not be used to check if
639 // a particular unit has it, use get_state(status_name) for that.
640 bool unit_type::musthave_status(const std::string& status_name) const
641 {
642  // Statuses default to absent.
643  bool current_status = false;
644 
645  // Look at all of the "musthave" traits to see if the
646  // status gets changed. In the unlikely event it gets changed
647  // multiple times, we want to try to do it in the same order
648  // that unit::apply_modifications does things.
649  for(const config& mod : possible_traits()) {
650  if(mod["availability"] != "musthave") {
651  continue;
652  }
653 
654  for(const config& effect : mod.child_range("effect")) {
655  // See if the effect only applies to
656  // certain unit types But don't worry
657  // about gender checks, since we don't
658  // know what the gender of the
659  // hypothetical recruit is.
660  const std::string& ut = effect["unit_type"];
661 
662  if(!ut.empty()) {
663  const std::vector<std::string>& types = utils::split(ut);
664 
665  if(std::find(types.begin(), types.end(), id()) == types.end()) {
666  continue;
667  }
668  }
669 
670  // We're only interested in status changes.
671  if(effect["apply_to"] != "status") {
672  continue;
673  }
674 
675  if(effect["add"] == status_name) {
676  current_status = true;
677  }
678 
679  if(effect["remove"] == status_name) {
680  current_status = false;
681  }
682  }
683  }
684 
685  return current_status;
686 }
687 
688 const std::string& unit_type::flag_rgb() const
689 {
690  return flag_rgb_.empty() ? game_config::unit_rgb : flag_rgb_;
691 }
692 
694 {
695  if(num_traits() == 0) {
696  return false;
697  }
698 
699  for(const auto& cfg : possible_traits()) {
700  const config::attribute_value& availability = cfg["availability"];
701  if(availability.blank()) {
702  return true;
703  }
704 
705  if(availability.str() != "musthave") {
706  return true;
707  }
708  }
709 
710  return false;
711 }
712 
713 std::vector<std::string> unit_type::variations() const
714 {
715  std::vector<std::string> retval;
716  retval.reserve(variations_.size());
717 
718  for(const variations_map::value_type& val : variations_) {
719  retval.push_back(val.first);
720  }
721 
722  return retval;
723 }
724 
725 bool unit_type::has_variation(const std::string& variation_id) const
726 {
727  return variations_.find(variation_id) != variations_.end();
728 }
729 
731 {
732  for(const variations_map::value_type& val : variations_) {
733  if(!val.second.hide_help()) {
734  return true;
735  }
736  }
737 
738  return false;
739 }
740 
741 int unit_type::resistance_against(const std::string& damage_name, bool attacker) const
742 {
743  int resistance = movement_type_.resistance_against(damage_name);
744  unit_ability_list resistance_abilities;
745 
746  if(const config& abilities = get_cfg().child("abilities")) {
747  for(const config& cfg : abilities.child_range("resistance")) {
748  if(!cfg["affect_self"].to_bool(true)) {
749  continue;
750  }
751 
752  if(!resistance_filter_matches(cfg, attacker, damage_name, 100 - resistance)) {
753  continue;
754  }
755 
757  }
758  }
759 
760  if(!resistance_abilities.empty()) {
761  unit_abilities::effect resist_effect(resistance_abilities, 100 - resistance, false);
762 
763  resistance = 100 - std::min<int>(
764  resist_effect.get_composite_value(),
765  resistance_abilities.highest("max_value").first
766  );
767  }
768 
769  return resistance;
770 }
771 
773  const config& cfg, bool attacker, const std::string& damage_name, int res) const
774 {
775  if(!(cfg["active_on"].empty() ||
776  (attacker && cfg["active_on"] == "offense") ||
777  (!attacker && cfg["active_on"] == "defense"))
778  ) {
779  return false;
780  }
781 
782  const std::string& apply_to = cfg["apply_to"];
783 
784  if(!apply_to.empty()) {
785  if(damage_name != apply_to) {
786  if(apply_to.find(',') != std::string::npos && apply_to.find(damage_name) != std::string::npos) {
787  const std::vector<std::string>& vals = utils::split(apply_to);
788 
789  if(std::find(vals.begin(), vals.end(), damage_name) == vals.end()) {
790  return false;
791  }
792  } else {
793  return false;
794  }
795  }
796  }
797 
798  if(!unit_abilities::filter_base_matches(cfg, res)) {
799  return false;
800  }
801 
802  return true;
803 }
804 
805 /** Implementation detail of unit_type::alignment_description */
806 
807 MAKE_ENUM (ALIGNMENT_FEMALE_VARIATION,
808  (LAWFUL, N_("female^lawful"))
809  (FEMALE_NEUTRAL, N_("female^neutral"))
810  (CHAOTIC , N_("female^chaotic"))
811  (LIMINAL, N_("female^liminal"))
812 )
813 
814 std::string unit_type::alignment_description(ALIGNMENT align, unit_race::GENDER gender)
815 {
816  static_assert(ALIGNMENT_FEMALE_VARIATION::count == ALIGNMENT::count,
817  "ALIGNMENT_FEMALE_VARIATION and ALIGNMENT do not have the same number of values");
818 
819  assert(align.valid());
820 
821  std::string str = std::string();
822 
823  if(gender == unit_race::FEMALE) {
824  ALIGNMENT_FEMALE_VARIATION fem = align.cast<ALIGNMENT_FEMALE_VARIATION::type>();
825  str = fem.to_string();
826  } else {
827  str = align.to_string();
828  }
829 
830  return translation::sgettext(str.c_str());
831 }
832 
833 /* ** unit_type_data ** */
834 
836  : types_()
837  , movement_types_()
838  , races_()
839  , hide_help_all_(false)
840  , hide_help_type_()
841  , hide_help_race_()
842  , units_cfg_()
843  , build_status_(unit_type::NOT_BUILT)
844 {
845 }
846 
847 
848 // Helpers for set_config()
849 
850 namespace
851 {
852 /**
853  * Spits out an error message and throws a config::error.
854  * Called when apply_base_unit() detects a cycle.
855  * (This exists merely to take the error message out of that function.)
856  */
857 void throw_base_unit_recursion_error(const std::vector<std::string>& base_tree, const std::string& base_id)
858 {
859  std::stringstream ss;
860  ss << "[base_unit] recursion loop in [unit_type] ";
861 
862  for(const std::string& step : base_tree) {
863  ss << step << "->";
864  }
865 
866  ss << base_id;
867  ERR_CF << ss.str() << '\n';
868 
869  throw config::error(ss.str());
870 }
871 
872 const boost::regex fai_identifier("[a-zA-Z_]+");
873 
874 template<typename MoveT>
875 void patch_movetype(
876  MoveT& mt, const std::string& type, const std::string& new_key, const std::string& formula_str, int default_val, bool replace)
877 {
878  config temp_cfg, original_cfg;
879  mt.write(original_cfg);
880 
881  if(!replace && !original_cfg[new_key].blank()) {
882  // Don't replace if the key already exists in the config (even if empty).
883  return;
884  }
885 
886  gui2::typed_formula<int> formula(formula_str);
887  wfl::map_formula_callable original;
888 
889  boost::sregex_iterator m(formula_str.begin(), formula_str.end(), fai_identifier);
890  for(const boost::sregex_iterator::value_type& p : std::make_pair(m, boost::sregex_iterator())) {
891  const std::string var_name = p.str();
892 
893  wfl::variant val(original_cfg[var_name].to_int(default_val));
894  original.add(var_name, val);
895  }
896 
897  temp_cfg[new_key] = formula(original);
898  mt.merge(temp_cfg, type, true);
899 }
900 } // unnamed namespace
901 
902 
903 
904 /**
905  * Modifies the provided config by merging all base units into it.
906  * The @a base_tree parameter is used for detecting and reporting
907  * cycles of base units and in particular to prevent infinite loops.
908  */
909 
910 void unit_type_data::apply_base_unit(unit_type& type, std::vector<std::string>& base_tree)
911 {
912  // Nothing to do.
913  if(type.base_unit_id_.empty()) {
914  return;
915  }
916 
917  // Detect recursion so the WML author is made aware of an error.
918  if(std::find(base_tree.begin(), base_tree.end(), type.base_unit_id_) != base_tree.end()) {
919  throw_base_unit_recursion_error(base_tree, type.base_unit_id_);
920  }
921 
922  // Find the base unit.
923  const unit_type_map::iterator itor = types_.find(type.base_unit_id_);
924  if(itor != types_.end()) {
925 
926  unit_type& base_type = itor->second;
927 
928  // Make sure the base unit has had its base units accounted for.
929  base_tree.push_back(type.base_unit_id_);
930 
931  apply_base_unit(base_type, base_tree);
932 
933  base_tree.pop_back();
934 
935  // Merge the base unit "under" our config.
936  type.writable_cfg().inherit_from(base_type.get_cfg());
937  }
938  else {
939  ERR_CF << "[base_unit]: unit type not found: " << type.base_unit_id_ << std::endl;
940  throw config::error("unit type not found: " + type.base_unit_id_);
941  }
942 }
943 
944 /**
945  * Handles inheritance for configs of [male], [female], and [variation].
946  * Also removes gendered children, as those serve no purpose.
947  * @a default_inherit is the default value for inherit=.
948  */
949 std::unique_ptr<unit_type> unit_type::create_sub_type(const config& var_cfg, bool default_inherit)
950 {
951  config var_copy = var_cfg;
952  if(var_cfg["inherit"].to_bool(default_inherit)) {
953  var_copy.inherit_from(get_cfg());
954  }
955 
956  var_copy.clear_children("male");
957  var_copy.clear_children("female");
958 
959  return std::make_unique<unit_type>(std::move(var_copy), parent_id());
960 }
961 
962 /**
963  * Processes [variation] tags of @a ut_cfg, handling inheritance and
964  * child clearing.
965  */
967 {
968  // Most unit types do not have variations.
969  if(!get_cfg().has_child("variation")) {
970  return;
971  }
972 
973  // Handle each variation's inheritance.
974  for(const config& var_cfg : get_cfg().child_range("variation")) {
975 
976  std::unique_ptr<unit_type> var = create_sub_type(var_cfg, false);
977 
978  var->built_cfg_->remove_children("variation", [](const config&){return true;});
979  var->debug_id_ = debug_id_ + " [" + var->variation_id_ + "]";
980 
982  bool success;
983  std::tie(ut, success) = variations_.emplace(var_cfg["variation_id"].str(), std::move(*var));
984  if(!success) {
985  ERR_CF << "Skipping duplicate unit variation ID: " << var_cfg["variation_id"] << "\n";
986  }
987  }
988 
989 
990 }
991 
992 
994 {
995  // Complete the gender-specific children of the config.
996  if(const config& male_cfg = get_cfg().child("male")) {
997  gender_types_[0] = create_sub_type(male_cfg, true);
998  gender_types_[0]->fill_variations();
999  }
1000 
1001  if(const config& female_cfg = get_cfg().child("female")) {
1002  gender_types_[1] = create_sub_type(female_cfg, true);
1003  gender_types_[1]->fill_variations();
1004  }
1005 
1006  // Complete the variation-defining children of the config.
1007  fill_variations();
1008 
1010 }
1011 /**
1012  * Resets all data based on the provided config.
1013  * This includes some processing of the config, such as expanding base units.
1014  * A pointer to the config is stored, so the config must be persistent.
1015  */
1017 {
1018  LOG_UT << "unit_type_data::set_config, nunits: " << cfg.child_range("unit_type").size() << "\n";
1019 
1020  clear();
1021  units_cfg_ = cfg;
1022 
1023  for(const config& mt : cfg.child_range("movetype")) {
1024  movement_types_.emplace(mt["name"].str(), movetype(mt));
1025 
1027  }
1028 
1029  for(const config& r : cfg.child_range("race")) {
1030  const unit_race race(r);
1031  races_.emplace(race.id(), race);
1032 
1034  }
1035 
1036  // Movetype resistance patching
1037  for(const config& r : cfg.child_range("resistance_defaults")) {
1038  const std::string& dmg_type = r["id"];
1039  config temp_cfg;
1040 
1041  for(const config::attribute& attr : r.attribute_range()) {
1042  const std::string& mt = attr.first;
1043 
1044  if(mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1045  continue;
1046  }
1047 
1048  patch_movetype(movement_types_[mt], "resistances", dmg_type, attr.second, 100, true);
1049  }
1050 
1051  if(r.has_attribute("default")) {
1052  for(movement_type_map::value_type& mt : movement_types_) {
1053  // Don't apply a default if a value is explicitly specified.
1054  if(r.has_attribute(mt.first)) {
1055  continue;
1056  }
1057 
1058  patch_movetype(mt.second, "resistances", dmg_type, r["default"], 100, false);
1059  }
1060  }
1061  }
1062 
1063  // Movetype move/defend patching
1064  for(const config& terrain : cfg.child_range("terrain_defaults")) {
1065  const std::string& ter_type = terrain["id"];
1066  config temp_cfg;
1067 
1068  static const std::array<std::string, 4> terrain_info_tags {{"movement", "vision", "jamming", "defense"}};
1069 
1070  for(const std::string& tag : terrain_info_tags) {
1071  if(!terrain.has_child(tag)) {
1072  continue;
1073  }
1074 
1075  const config& info = terrain.child(tag);
1076 
1077  for(const config::attribute& attr : info.attribute_range()) {
1078  const std::string& mt = attr.first;
1079 
1080  if(mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1081  continue;
1082  }
1083 
1084  if(tag == "defense") {
1085  patch_movetype(movement_types_[mt], tag, ter_type, attr.second, 100, true);
1086  } else {
1087  patch_movetype(movement_types_[mt], tag, ter_type, attr.second, 99, true);
1088  }
1089  }
1090 
1091  if(info.has_attribute("default")) {
1092  for(movement_type_map::value_type& mt : movement_types_) {
1093  // Don't apply a default if a value is explicitly specified.
1094  if(info.has_attribute(mt.first)) {
1095  continue;
1096  }
1097 
1098  if(tag == "defense") {
1099  patch_movetype(mt.second, tag, ter_type, info["default"], 100, false);
1100  } else {
1101  patch_movetype(mt.second, tag, ter_type, info["default"], 99, false);
1102  }
1103  }
1104  }
1105  }
1106  }
1107 
1108  for(const config& ut : cfg.child_range("unit_type")) {
1109  // Every type is required to have an id.
1110  std::string id = ut["id"].str();
1111  if(id.empty()) {
1112  ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
1113  continue;
1114  }
1115 
1116  if(types_.emplace(id, unit_type(ut)).second) {
1117  LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
1118  } else {
1119  ERR_CF << "Multiple [unit_type]s with id=" << id << " encountered." << std::endl;
1120  }
1121  }
1122 
1123  // Apply base units.
1124  for(auto& type : types_) {
1125  std::vector<std::string> base_tree(1, type.second.id());
1126  apply_base_unit(type.second, base_tree);
1127 
1129  }
1130 
1131  //handle [male], [female], [variation]
1132  for(auto& type : types_) {
1133  type.second.fill_variations_and_gender();
1134 
1136  }
1137 
1138  // Build all unit types. (This was not done within the loop for performance.)
1140 
1141  // Suppress some unit types (presumably used as base units) from the help.
1142  if(const config& hide_help = cfg.child("hide_help")) {
1143  hide_help_all_ = hide_help["all"].to_bool();
1145  }
1146  DBG_UT << "Finished creatign unti types\n";
1147 }
1148 
1150 {
1151  ut.build(status, movement_types_, races_, units_cfg().child_range("trait"));
1152 }
1153 
1154 /**
1155  * Finds a unit_type by its id() and makes sure it is built to the specified level.
1156  */
1157 const unit_type* unit_type_data::find(const std::string& key, unit_type::BUILD_STATUS status) const
1158 {
1159  if(key.empty() || key == "random") {
1160  return nullptr;
1161  }
1162 
1163  DBG_CF << "trying to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1164  const unit_type_map::iterator itor = types_.find(key);
1165 
1166  // This might happen if units of another era are requested (for example for savegames)
1167  if(itor == types_.end()) {
1168  DBG_CF << "unable to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1169  return nullptr;
1170  }
1171 
1172  // Make sure the unit_type is built to the requested level.
1173  build_unit_type(itor->second, status);
1174 
1175  return &itor->second;
1176 }
1177 
1178 void unit_type_data::check_types(const std::vector<std::string>& types) const
1179 {
1180  for(const std::string& type : types) {
1181  if(!find(type)) {
1182  throw game::game_error("unknown unit type: " + type);
1183  }
1184  }
1185 }
1186 
1188 {
1189  types_.clear();
1190  movement_types_.clear();
1191  races_.clear();
1193 
1194  hide_help_all_ = false;
1195  hide_help_race_.clear();
1196  hide_help_type_.clear();
1197 }
1198 
1200 {
1201  // Nothing to do if already built to the requested level.
1202  if(status <= build_status_) {
1203  return;
1204  }
1205 
1206  for(const auto& type : types_) {
1207  build_unit_type(type.second, status);
1208 
1210  }
1211 
1212  build_status_ = status;
1213 }
1214 
1216 {
1217  if(!cfg) {
1218  return;
1219  }
1220 
1221  hide_help_race_.emplace_back();
1222  hide_help_type_.emplace_back();
1223 
1224  std::vector<std::string> races = utils::split(cfg["race"]);
1225  hide_help_race_.back().insert(races.begin(), races.end());
1226 
1227  std::vector<std::string> types = utils::split(cfg["type"]);
1228  hide_help_type_.back().insert(types.begin(), types.end());
1229 
1230  std::vector<std::string> trees = utils::split(cfg["type_adv_tree"]);
1231  hide_help_type_.back().insert(trees.begin(), trees.end());
1232 
1233  for(const std::string& t_id : trees) {
1234  unit_type_map::iterator ut = types_.find(t_id);
1235 
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 recursively call all the imbricated [not] tags
1243  read_hide_help(cfg.child("not"));
1244 }
1245 
1246 bool unit_type_data::hide_help(const std::string& type, const std::string& race) const
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  }
1259 
1260  return res;
1261 }
1262 
1263 const unit_race* unit_type_data::find_race(const std::string& key) const
1264 {
1265  race_map::const_iterator i = races_.find(key);
1266  return i != races_.end() ? &i->second : nullptr;
1267 }
1268 
1270 {
1271  build_created();
1272  if(auto p_setxp = cfg.get("set_experience")) {
1273  experience_needed_ = p_setxp->to_int();
1274  }
1275  if(auto attr = cfg.get("set_advances_to")) {
1276  advances_to_ = utils::split(attr->str());
1277  }
1278  if(auto attr = cfg.get("set_cost")) {
1279  cost_ = attr->to_int(1);
1280  }
1281  if(auto attr = cfg.get("add_advancement")) {
1282  for(const auto& str : utils::split(attr->str())) {
1283  advances_to_.push_back(str);
1284  }
1285  }
1286  if(auto attr = cfg.get("remove_advancement")) {
1287  for(const auto& str : utils::split(attr->str())) {
1288  boost::remove_erase(advances_to_, str);
1289  }
1290  }
1291 
1292  // apply recursively to subtypes.
1293  for(int gender = 0; gender <= 1; ++gender) {
1294  if(!gender_types_[gender]) {
1295  continue;
1296  }
1297  gender_types_[gender]->apply_scenario_fix(cfg);
1298  }
1299 
1300  if(get_cfg().has_child("variation")) {
1301  // Make sure the variations are created.
1302  unit_types.build_unit_type(*this, VARIATIONS);
1303  for(auto& v : variations_) {
1304  v.second.apply_scenario_fix(cfg);
1305  }
1306  }
1307 }
1308 
1310 {
1311  unit_type_map::iterator itor = types_.find(cfg["type"].str());
1312  // This might happen if units of another era are requested (for example for savegames)
1313  if(itor != types_.end()) {
1314  itor->second.apply_scenario_fix(cfg);
1315  }
1316  else {
1317  // should we give an error message?
1318  }
1319 }
1320 
1322 {
1323  advances_to_.clear();
1324  const std::string& advances_to_val = get_cfg()["advances_to"];
1325  if(advances_to_val != "null" && !advances_to_val.empty()) {
1326  advances_to_ = utils::split(advances_to_val);
1327  }
1328  experience_needed_ = get_cfg()["experience"].to_int(500);
1329  cost_ = get_cfg()["cost"].to_int(1);
1330 
1331  // apply recursively to subtypes.
1332  for(int gender = 0; gender <= 1; ++gender) {
1333  if(!gender_types_[gender]) {
1334  continue;
1335  }
1336  gender_types_[gender]->remove_scenario_fixes();
1337  }
1338  for(auto& v : variations_) {
1339  v.second.remove_scenario_fixes();
1340  }
1341 }
1342 
1344 {
1345  for(auto& pair : types_) {
1346  pair.second.remove_scenario_fixes();
1347  }
1348 }
1349 
1350 void unit_type::check_id(std::string& id)
1351 {
1352  assert(!id.empty());
1353 
1354  // We don't allow leading whitepaces.
1355  if(id[0] == ' ') {
1356  throw error("Found unit type id with a leading whitespace \"" + id + "\"");
1357  }
1358 
1359  bool gave_warning = false;
1360 
1361  for(std::size_t pos = 0; pos < id.size(); ++pos) {
1362  const char c = id[pos];
1363  const bool valid = std::isalnum(c, std::locale::classic()) || c == '_' || c == ' ';
1364 
1365  if(!valid) {
1366  if(!gave_warning) {
1367  ERR_UT << "Found unit type id with invalid characters: \"" << id << "\"\n";
1368  gave_warning = true;
1369  }
1370 
1371  id[pos] = '_';
1372  }
1373  }
1374 }
1375 
1377 
1378 void adjust_profile(std::string& profile)
1379 {
1380  // Create a temp copy
1381  std::string temp = profile;
1382 
1383  static const std::string path_adjust = "/transparent";
1384  const std::string::size_type offset = profile.find_last_of('/', profile.find('~'));
1385 
1386  // If the path already refers to /transparent...
1387  if(profile.find(path_adjust) != std::string::npos && offset != std::string::npos) {
1388  if(!image::locator(profile).file_exists()) {
1389  profile.replace(profile.find(path_adjust), path_adjust.length(), "");
1390  }
1391 
1392  return;
1393  }
1394 
1395  // else, check for the file with /transparent appended...
1396  offset != std::string::npos ? temp.insert(offset, path_adjust) : temp = path_adjust + temp;
1397 
1398  // and use that path if it exists.
1399  if(image::locator(temp).file_exists()) {
1400  profile = temp;
1401  }
1402 }
int jamming_
Definition: types.hpp:321
std::string parent_id_
A suffix for id_, used when logging messages.
Definition: types.hpp:309
int experience_needed_
Definition: types.hpp:349
const std::string & parent_id() const
The id of the original type from which this (variation) descended.
Definition: types.hpp:143
UNIT_ALIGNMENT ALIGNMENT
Definition: types.hpp:182
bool has_cfg_build_
Definition: types.hpp:304
#define DBG_CF
Definition: types.cpp:45
static void advancement_tree_internal(const std::string &id, std::set< std::string > &tree)
Definition: types.cpp:598
bool empty() const
Definition: unit.hpp:101
config & child(config_key_type 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:420
bool do_not_list_
Definition: types.hpp:346
unit_type::BUILD_STATUS build_status_
Definition: types.hpp:416
static const std::string s_male
Standard string id (not translatable) for MALE.
Definition: race.hpp:28
double hp_bar_scaling
Definition: game_config.cpp:78
void read_hide_help(const config &cfg)
Parses the [hide_help] tag.
Definition: types.cpp:1215
#define DBG_UT
Definition: types.cpp:48
std::string default_variation_
Definition: types.hpp:338
static int get_acceleration()
Definition: types.cpp:540
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:921
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:1157
double xp_bar_scaling
Definition: game_config.cpp:79
std::string flag_rgb_
Definition: types.hpp:331
void clear_children(T... keys)
Definition: config.hpp:479
ALIGNMENT alignment_
Definition: types.hpp:352
t_string variation_name_
Definition: types.hpp:340
std::string profile_
Definition: types.hpp:330
void build(BUILD_STATUS status, const movement_type_map &movement_types, const race_map &races, const config_array_view &traits)
Performs a build of this to the indicated stage.
Definition: types.cpp:418
void emplace_back(T &&... args)
Definition: unit.hpp:110
const std::string & id() const
Definition: race.hpp:34
Variant for storing WML attributes.
const std::vector< t_string > & special_notes() const
Definition: types.cpp:498
std::string debug_id_
Definition: types.hpp:308
int hitpoints_
Definition: types.hpp:315
void check_types(const std::vector< std::string > &types) const
Definition: types.cpp:1178
const std::vector< std::string > & advances_to() const
A vector of unit_type ids that this unit_type can advance to.
Definition: types.hpp:113
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
int recall_cost_
Definition: types.hpp:318
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
logger & info()
Definition: log.cpp:90
config_array_view child_range(config_key_type key) const
std::string variation_id_
Definition: types.hpp:339
const std::string & flag_rgb() const
Definition: types.cpp:688
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:267
~unit_type()
Definition: types.cpp:170
child_itors child_range(config_key_type key)
Definition: config.cpp:362
bool has_special_notes() const
Definition: types.cpp:493
static void progress(loading_stage stage=loading_stage::none)
attribute_map::value_type attribute
Definition: config.hpp:226
const config * cfg_
Definition: types.hpp:301
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:742
std::string unit_rgb
t_string description_
Definition: types.hpp:313
void fill_variations()
Processes [variation] tags of ut_cfg, handling inheritance and child clearing.
Definition: types.cpp:966
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:545
void build_all(unit_type::BUILD_STATUS status)
Makes sure the all unit_types are built to the specified level.
Definition: types.cpp:1199
config possible_traits_
Definition: types.hpp:356
unit_type_data unit_types
Definition: types.cpp:1376
std::array< std::unique_ptr< unit_type >, 2 > gender_types_
Definition: types.hpp:335
void build_help_index(const movement_type_map &movement_types, const race_map &races, const config_array_view &traits)
Partially load data into an empty unit_type (build to HELP_INDEXED).
Definition: types.cpp:235
int resistance_against(const std::string &damage_name, bool attacker) const
Gets resistance while considering custom WML abilities.
Definition: types.cpp:741
const config & get_cfg() const
Definition: types.hpp:268
STL namespace.
BUILD_STATUS
Records the status of the lazy building of unit types.
Definition: types.hpp:74
std::set< std::string > advancement_tree() const
Get the advancement tree.
Definition: types.cpp:613
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1309
void clear()
Definition: config.cpp:863
t_string type_name_
Definition: types.hpp:312
variations_map variations_
Definition: types.hpp:337
const race_map & races() const
Definition: types.hpp:377
attack_itors make_attack_itors(attack_list &atks)
void merge(const config &new_cfg, bool overwrite=true)
Merges the given config over the existing data, the config should have zero or more children named "m...
Definition: movetype.cpp:855
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:41
const unit_type_map & types() const
Definition: types.hpp:376
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
static lg::log_domain log_config("config")
static void add_color_info(const config &v, bool build_defaults)
const std::vector< std::string > advances_from() const
A vector of unit_type ids that can advance to this unit_type.
Definition: types.cpp:620
const_attr_itors attribute_range() const
Definition: config.cpp:809
int level_
Definition: types.hpp:317
A single unit type that the player may recruit.
Definition: types.hpp:44
const unit_race * race_
Definition: types.hpp:342
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:1342
config & writable_cfg()
Definition: types.hpp:289
static const std::string s_female
Standard string id (not translatable) for FEMALE.
Definition: race.hpp:27
#define ERR_UT
Definition: types.cpp:50
void build_full(const movement_type_map &movement_types, const race_map &races, const config_array_view &traits)
Load data into an empty unit_type (build to FULL).
Definition: types.cpp:192
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.cpp:1149
std::string icon_
Definition: types.hpp:328
void inherit_from(const config &c)
Merge config &#39;c&#39; into this config, preserving this config&#39;s values.
Definition: config.cpp:1220
bool musthave_status(const std::string &status) const
Definition: types.cpp:640
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
void fill_variations_and_gender()
Definition: types.cpp:993
movement_type_map movement_types_
Definition: types.hpp:405
bool zoc_
Definition: types.hpp:346
static const unit_race null_race
Dummy race used when a race is not yet known.
Definition: race.hpp:66
bool hide_help() const
Definition: types.cpp:592
const std::vector< unit_animation > & animations() const
Definition: types.cpp:502
static const ::game_config_view * terrain
The terrain used to create the cache.
Definition: minimap.cpp:130
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:252
std::vector< std::set< std::string > > hide_help_type_
Definition: types.hpp:411
std::vector< std::set< std::string > > hide_help_race_
Definition: types.hpp:412
std::vector< unit_animation > animations_
Definition: types.hpp:361
bool blank() const
Tests for an attribute that was never set.
std::string small_profile_
Definition: types.hpp:329
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:141
std::unique_ptr< config > built_cfg_
Definition: types.hpp:303
std::string undead_variation_
Definition: types.hpp:325
void remove_scenario_fixes()
Definition: types.cpp:1321
int movement_
Definition: types.hpp:319
movetype movement_type_
Definition: types.hpp:354
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:139
const config::const_child_itors & additional_traits() const
Definition: race.cpp:118
std::map< std::string, movetype > movement_type_map
Definition: types.hpp:35
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
static lg::log_domain log_unit("unit")
double xp_bar_scaling_
Definition: types.hpp:316
unsigned int num_traits() const
Definition: race.cpp:128
int cost_
Definition: types.hpp:323
const std::string log_id() const
A variant on id() that is more descriptive, for use with message logging.
Definition: types.hpp:141
const game_config_view & units_cfg() const
Definition: types.hpp:414
const unit_race * find_race(const std::string &) const
Definition: types.cpp:1263
std::vector< t_string > special_notes_
Definition: types.hpp:314
unsigned int num_traits_
Definition: types.hpp:333
std::size_t i
Definition: function.cpp:933
game_config_view units_cfg_
Definition: types.hpp:415
std::vector< std::string > variations() const
Definition: types.cpp:713
unit_type_map types_
Definition: types.hpp:404
std::vector< std::string > get_ability_list() const
Definition: types.cpp:572
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1269
mock_party p
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:730
double g
Definition: astarsearch.cpp:64
std::vector< std::string > advances_to_
Definition: types.hpp:348
std::string usage_
Definition: types.hpp:324
#define ERR_CF
Definition: types.cpp:42
bool uses_global_traits() const
Definition: race.cpp:113
bool has_ability_by_id(const std::string &ability) const
Definition: types.cpp:559
int get_composite_value() const
Definition: abilities.hpp:48
double hp_bar_scaling_
Definition: types.hpp:316
bool hide_help_all_
True if [hide_help] contains a &#39;all=yes&#39; at its root.
Definition: types.hpp:409
std::string base_unit_id_
The id of the top ancestor of this unit_type.
Definition: types.hpp:311
#define N_(String)
Definition: gettext.hpp:99
std::string id_
Definition: types.hpp:307
config & add_child(config_key_type key)
Definition: config.cpp:476
unit_experience_accelerator(int modifier)
Definition: types.cpp:529
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
#define LOG_UT
Definition: types.cpp:49
std::vector< ability_metadata > abilities_
Never nullptr, but may point to the null race.
Definition: types.hpp:344
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:772
int vision_
Definition: types.hpp:320
bool empty() const
Definition: tstring.hpp:182
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:807
BUILD_STATUS build_status_
Definition: types.hpp:363
void set_config(const game_config_view &cfg)
Resets all data based on the provided config.
Definition: types.cpp:1016
void clear()
Definition: types.cpp:1187
double t
Definition: astarsearch.cpp:64
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:474
std::vector< std::reference_wrapper< const config > > config_array_view
int max_attacks_
Definition: types.hpp:322
bool find(E event, F functor)
Tests whether an event handler is available.
#define LOG_CONFIG
Definition: types.cpp:44
static UNUSEDNOWARN std::string sgettext(const char *str)
Definition: gettext.hpp:68
bool hide_help_
Definition: types.hpp:346
std::vector< ability_metadata > adv_abilities_
Definition: types.hpp:344
void adjust_profile(std::string &profile)
Definition: types.cpp:1378
Standard logging facilities (interface).
void apply_base_unit(unit_type &type, std::vector< std::string > &base_tree)
Modifies the provided config by merging all base units into it.
Definition: types.cpp:910
void remove_scenario_fixes()
Definition: types.cpp:1343
static const map_location & null_location()
Definition: location.hpp:85
const unit_type & get_gender_unit_type(std::string gender) const
Returns a gendered variant of this unit_type.
Definition: types.cpp:453
static void check_id(std::string &id)
Validate the id argument.
Definition: types.cpp:1350
retval
Default window/dialog return values.
Definition: retval.hpp:28
std::unique_ptr< unit_type > create_sub_type(const config &var_cfg, bool default_inherit)
Handles inheritance for configs of [male], [female], and [variation].
Definition: types.cpp:949
const_attack_itors attacks() const
Definition: types.cpp:511
std::map< std::string, unit_race > race_map
Definition: race.hpp:91
boost::iterator_range< boost::indirect_iterator< attack_list::const_iterator > > const_attack_itors
t_string unit_description() const
Definition: types.cpp:484
ability_metadata(const config &cfg)
Definition: types.cpp:174
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
attack_list attacks_cache_
Definition: types.hpp:305
mock_char c
bool has_random_traits() const
Definition: types.cpp:693
const std::string & undead_variation() const
Definition: race.hpp:48
bool has_variation(const std::string &variation_id) const
Definition: types.cpp:725
Defines the MAKE_ENUM macro.
bool file_exists() const
Tests whether the file the locater points at exists.
Definition: picture.cpp:662
std::string image_
Definition: types.hpp:327
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
race_map races_
Definition: types.hpp:406
const config & child(config_key_type key) const
std::vector< unit_race::GENDER > genders_
Definition: types.hpp:358
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:79
std::string str(const std::string &fallback="") const
void build_created()
Load the most needed data into an empty unit_type (build to CREATE).
Definition: types.cpp:377