The Battle for Wesnoth  1.15.5+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 
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 }
157  : unit_type(defaut_ctor_t(), cfg, parent_id)
158 {
159  cfg_ = &cfg;
160 
161 }
162 
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  icon_ = cfg["image_icon"].str();
261  small_profile_ = cfg["small_profile"].str();
262  profile_ = cfg["profile"].str();
263  flag_rgb_ = cfg["flag_rgb"].str();
264  do_not_list_ = cfg["do_not_list"].to_bool(false);
265 
266  for(const config& sn : cfg.child_range("special_note")) {
267  special_notes_.push_back(sn["note"]);
268  }
269 
271 
272  alignment_ = unit_type::ALIGNMENT::NEUTRAL;
273  alignment_.parse(cfg["alignment"].str());
274 
275  for(int i = 0; i < 2; ++i) {
276  if(gender_types_[i]) {
277  gender_types_[i]->build_help_index(mv_types, races, traits);
278  }
279  }
280 
281  for(auto& pair : variations_) {
282  pair.second.build_help_index(mv_types, races, traits);
283  }
284 
285  const race_map::const_iterator race_it = races.find(cfg["race"]);
286  if(race_it != races.end()) {
287  race_ = &race_it->second;
288  } else {
290  }
291 
292  // if num_traits is not defined, we use the num_traits from race
293  num_traits_ = cfg["num_traits"].to_int(race_->num_traits());
294 
295  for(const std::string& g : utils::split(cfg["gender"])) {
296  genders_.push_back(string_gender(g));
297  }
298 
299  // For simplicity in other parts of the code, we must have at least one gender.
300  if(genders_.empty()) {
301  genders_.push_back(unit_race::MALE);
302  }
303 
304  if(const config& abil_cfg = cfg.child("abilities")) {
305  for(const config::any_child& ab : abil_cfg.all_children_range()) {
306  abilities_.emplace_back(ab.cfg);
307  }
308  }
309 
310  for(const config& adv : cfg.child_range("advancement")) {
311  for(const config& effect : adv.child_range("effect")) {
312  const config& abil_cfg = effect.child("abilities");
313 
314  if(!abil_cfg || effect["apply_to"] != "new_ability") {
315  continue;
316  }
317 
318  for(const config::any_child& ab : abil_cfg.all_children_range()) {
319  adv_abilities_.emplace_back(ab.cfg);
320  }
321  }
322  }
323 
324  // Set the movement type.
325  const std::string move_type = cfg["movement_type"];
326  const movement_type_map::const_iterator find_it = mv_types.find(move_type);
327 
328  if(find_it != mv_types.end()) {
329  DBG_UT << "inheriting from movement_type '" << move_type << "'\n";
330  movement_type_ = find_it->second;
331  } else if(!move_type.empty()) {
332  DBG_UT << "movement_type '" << move_type << "' not found\n";
333  }
334 
335  // Override parts of the movement type with what is in our config.
336  movement_type_.merge(cfg);
337 
338  for(const config& t : traits) {
339  possible_traits_.add_child("trait", t);
340  }
341 
342  if(race_ != &unit_race::null_race) {
343  if(!race_->uses_global_traits()) {
345  }
346 
347  if(cfg["ignore_race_traits"].to_bool()) {
349  } else {
350  for(const config& t : race_->additional_traits()) {
351  if(alignment_ != unit_type::ALIGNMENT::NEUTRAL || t["id"] != "fearless")
352  possible_traits_.add_child("trait", t);
353  }
354  }
355 
356  if(undead_variation_.empty()) {
358  }
359  }
360 
361  // Insert any traits that are just for this unit type
362  for(const config& trait : cfg.child_range("trait")) {
363  possible_traits_.add_child("trait", trait);
364  }
365 
366  hide_help_ = cfg["hide_help"].to_bool();
367 
369 }
370 
371 /**
372  * Load the most needed data into an empty unit_type (build to CREATE).
373  * This creates the gender-specific types (if needed) and also defines how much
374  * experience is needed to advance as well as what this advances to.
375  */
377 {
378  // Don't build twice.
379  if(CREATED <= build_status_) {
380  return;
381  }
382 
383 
384  for(unsigned i = 0; i < gender_types_.size(); ++i) {
385  if(gender_types_[i]) {
386  gender_types_[i]->build_created();
387  }
388  }
389 
390  for(auto& pair : variations_) {
391  pair.second.build_created();
392  }
393 
394 
395  const config& cfg = get_cfg();
396 
397  const std::string& advances_to_val = cfg["advances_to"];
398  if(advances_to_val != "null" && !advances_to_val.empty()) {
399  advances_to_ = utils::split(advances_to_val);
400  }
401 
402 
403  type_name_ = cfg["name"].t_str();
404  variation_name_ = cfg["variation_name"].t_str();
405 
406  DBG_UT << "unit_type '" << log_id() << "' advances to : " << advances_to_val << "\n";
407 
408  experience_needed_ = cfg["experience"].to_int(500);
409  cost_ = cfg["cost"].to_int(1);
410 
411  //needed by the editor.
412  image_ = cfg["image"].str();
414 }
415 
416 /**
417  * Performs a build of this to the indicated stage.
418  */
420  const movement_type_map& movement_types,
421  const race_map& races,
422  const config_array_view& traits)
423 {
424  DBG_UT << "Building unit type " << log_id() << ", level " << status << '\n';
425 
426  switch(status) {
427  case NOT_BUILT:
428  // Already done in the constructor.
429  return;
430 
431  case CREATED:
432  // Build the basic data.
433  build_created();
434  return;
435 
436  case VARIATIONS: // Implemented as part of HELP_INDEXED
437  case HELP_INDEXED:
438  // Build the data needed to feed the help index.
439  build_help_index(movement_types, races, traits);
440  return;
441 
442  case FULL:
443  build_full(movement_types, races, traits);
444  return;
445 
446  default:
447  ERR_UT << "Build of unit_type to unrecognized status (" << status << ") requested." << std::endl;
448  // Build as much as possible.
449  build_full(movement_types, races, traits);
450  return;
451  }
452 }
453 
455 {
456  if(gender == unit_race::s_female) {
458  } else if(gender == unit_race::s_male) {
460  }
461 
462  return *this;
463 }
464 
466 {
467  const std::size_t i = gender;
468  if(i < gender_types_.size() && gender_types_[i] != nullptr) {
469  return *gender_types_[i];
470  }
471 
472  return *this;
473 }
474 
476 {
477  const variations_map::const_iterator i = variations_.find(id);
478  if(i != variations_.end()) {
479  return i->second;
480  }
481 
482  return *this;
483 }
484 
486 {
487  if(description_.empty()) {
488  return (_("No description available."));
489  } else {
490  return description_;
491  }
492 }
493 
495 {
496  return !special_notes_.empty();
497 }
498 
499 const std::vector<t_string>& unit_type::special_notes() const {
500  return special_notes_;
501 }
502 
503 const std::vector<unit_animation>& unit_type::animations() const
504 {
505  if(animations_.empty()) {
506  unit_animation::fill_initial_animations(animations_, get_cfg());
507  }
508 
509  return animations_;
510 }
511 
513 {
514  if(!attacks_cache_.empty()) {
516  }
517 
518  for(const config& att : get_cfg().child_range("attack")) {
519  attacks_cache_.emplace_back(new attack_type(att));
520  }
521 
523 }
524 
525 namespace
526 {
527 int experience_modifier = 100;
528 }
529 
531  : old_value_(experience_modifier)
532 {
533  experience_modifier = modifier;
534 }
535 
537 {
538  experience_modifier = old_value_;
539 }
540 
542 {
543  return experience_modifier;
544 }
545 
546 int unit_type::experience_needed(bool with_acceleration) const
547 {
548  if(with_acceleration) {
549  int exp = (experience_needed_ * experience_modifier + 50) / 100;
550  if(exp < 1) {
551  exp = 1;
552  }
553 
554  return exp;
555  }
556 
557  return experience_needed_;
558 }
559 
560 bool unit_type::has_ability_by_id(const std::string& ability) const
561 {
562  if(const config& abil = get_cfg().child("abilities")) {
563  for(const config::any_child& ab : abil.all_children_range()) {
564  if(ab.cfg["id"] == ability) {
565  return true;
566  }
567  }
568  }
569 
570  return false;
571 }
572 
573 std::vector<std::string> unit_type::get_ability_list() const
574 {
575  std::vector<std::string> res;
576 
577  const config& abilities = get_cfg().child("abilities");
578  if(!abilities) {
579  return res;
580  }
581 
582  for(const config::any_child& ab : abilities.all_children_range()) {
583  std::string id = ab.cfg["id"];
584 
585  if(!id.empty()) {
586  res.push_back(std::move(id));
587  }
588  }
589 
590  return res;
591 }
592 
594 {
595  return hide_help_ || unit_types.hide_help(id_, race_->id());
596 }
597 
598 
599 static void advancement_tree_internal(const std::string& id, std::set<std::string>& tree)
600 {
601  const unit_type* ut = unit_types.find(id);
602  if(!ut) {
603  return;
604  }
605 
606  for(const std::string& adv : ut->advances_to()) {
607  if(tree.insert(adv).second) {
608  // insertion succeed, expand the new type
609  advancement_tree_internal(adv, tree);
610  }
611  }
612 }
613 
614 std::set<std::string> unit_type::advancement_tree() const
615 {
616  std::set<std::string> tree;
617  advancement_tree_internal(id_, tree);
618  return tree;
619 }
620 
621 const std::vector<std::string> unit_type::advances_from() const
622 {
623  // Currently not needed (only help call us and already did it)/
625 
626  std::vector<std::string> adv_from;
627  for(const unit_type_data::unit_type_map::value_type& ut : unit_types.types()) {
628  for(const std::string& adv : ut.second.advances_to()) {
629  if(adv == id_) {
630  adv_from.push_back(ut.second.id());
631  }
632  }
633  }
634 
635  return adv_from;
636 }
637 
638 // This function is only meant to return the likely state a given status
639 // for a new recruit of this type. It should not be used to check if
640 // a particular unit has it, use get_state(status_name) for that.
641 bool unit_type::musthave_status(const std::string& status_name) const
642 {
643  // Statuses default to absent.
644  bool current_status = false;
645 
646  // Look at all of the "musthave" traits to see if the
647  // status gets changed. In the unlikely event it gets changed
648  // multiple times, we want to try to do it in the same order
649  // that unit::apply_modifications does things.
650  for(const config& mod : possible_traits()) {
651  if(mod["availability"] != "musthave") {
652  continue;
653  }
654 
655  for(const config& effect : mod.child_range("effect")) {
656  // See if the effect only applies to
657  // certain unit types But don't worry
658  // about gender checks, since we don't
659  // know what the gender of the
660  // hypothetical recruit is.
661  const std::string& ut = effect["unit_type"];
662 
663  if(!ut.empty()) {
664  const std::vector<std::string>& types = utils::split(ut);
665 
666  if(std::find(types.begin(), types.end(), id()) == types.end()) {
667  continue;
668  }
669  }
670 
671  // We're only interested in status changes.
672  if(effect["apply_to"] != "status") {
673  continue;
674  }
675 
676  if(effect["add"] == status_name) {
677  current_status = true;
678  }
679 
680  if(effect["remove"] == status_name) {
681  current_status = false;
682  }
683  }
684  }
685 
686  return current_status;
687 }
688 
690 {
691  return flag_rgb_.empty() ? game_config::unit_rgb : flag_rgb_;
692 }
693 
695 {
696  if(num_traits() == 0) {
697  return false;
698  }
699 
700  for(const auto& cfg : possible_traits()) {
701  const config::attribute_value& availability = cfg["availability"];
702  if(availability.blank()) {
703  return true;
704  }
705 
706  if(availability.str() != "musthave") {
707  return true;
708  }
709  }
710 
711  return false;
712 }
713 
714 std::vector<std::string> unit_type::variations() const
715 {
716  std::vector<std::string> retval;
717  retval.reserve(variations_.size());
718 
719  for(const variations_map::value_type& val : variations_) {
720  retval.push_back(val.first);
721  }
722 
723  return retval;
724 }
725 
726 bool unit_type::has_variation(const std::string& variation_id) const
727 {
728  return variations_.find(variation_id) != variations_.end();
729 }
730 
732 {
733  for(const variations_map::value_type& val : variations_) {
734  if(!val.second.hide_help()) {
735  return true;
736  }
737  }
738 
739  return false;
740 }
741 
742 int unit_type::resistance_against(const std::string& damage_name, bool attacker) const
743 {
744  int resistance = movement_type_.resistance_against(damage_name);
745  unit_ability_list resistance_abilities;
746 
747  if(const config& abilities = get_cfg().child("abilities")) {
748  for(const config& cfg : abilities.child_range("resistance")) {
749  if(!cfg["affect_self"].to_bool(true)) {
750  continue;
751  }
752 
753  if(!resistance_filter_matches(cfg, attacker, damage_name, 100 - resistance)) {
754  continue;
755  }
756 
758  }
759  }
760 
761  if(!resistance_abilities.empty()) {
762  unit_abilities::effect resist_effect(resistance_abilities, 100 - resistance, false);
763 
764  resistance = 100 - std::min<int>(
765  resist_effect.get_composite_value(),
766  resistance_abilities.highest("max_value").first
767  );
768  }
769 
770  return resistance;
771 }
772 
774  const config& cfg, bool attacker, const std::string& damage_name, int res) const
775 {
776  if(!(cfg["active_on"].empty() ||
777  (attacker && cfg["active_on"] == "offense") ||
778  (!attacker && cfg["active_on"] == "defense"))
779  ) {
780  return false;
781  }
782 
783  const std::string& apply_to = cfg["apply_to"];
784 
785  if(!apply_to.empty()) {
786  if(damage_name != apply_to) {
787  if(apply_to.find(',') != std::string::npos && apply_to.find(damage_name) != std::string::npos) {
788  const std::vector<std::string>& vals = utils::split(apply_to);
789 
790  if(std::find(vals.begin(), vals.end(), damage_name) == vals.end()) {
791  return false;
792  }
793  } else {
794  return false;
795  }
796  }
797  }
798 
799  if(!unit_abilities::filter_base_matches(cfg, res)) {
800  return false;
801  }
802 
803  return true;
804 }
805 
806 /** Implementation detail of unit_type::alignment_description */
807 
808 MAKE_ENUM (ALIGNMENT_FEMALE_VARIATION,
809  (LAWFUL, N_("female^lawful"))
810  (FEMALE_NEUTRAL, N_("female^neutral"))
811  (CHAOTIC , N_("female^chaotic"))
812  (LIMINAL, N_("female^liminal"))
813 )
814 
815 std::string unit_type::alignment_description(ALIGNMENT align, unit_race::GENDER gender)
816 {
817  static_assert(ALIGNMENT_FEMALE_VARIATION::count == ALIGNMENT::count,
818  "ALIGNMENT_FEMALE_VARIATION and ALIGNMENT do not have the same number of values");
819 
820  assert(align.valid());
821 
823 
824  if(gender == unit_race::FEMALE) {
825  ALIGNMENT_FEMALE_VARIATION fem = align.cast<ALIGNMENT_FEMALE_VARIATION::type>();
826  str = fem.to_string();
827  } else {
828  str = align.to_string();
829  }
830 
831  return translation::sgettext(str.c_str());
832 }
833 
834 /* ** unit_type_data ** */
835 
837  : types_()
838  , movement_types_()
839  , races_()
840  , hide_help_all_(false)
841  , hide_help_type_()
842  , hide_help_race_()
843  , units_cfg_()
844  , build_status_(unit_type::NOT_BUILT)
845 {
846 }
847 
848 
849 // Helpers for set_config()
850 
851 namespace
852 {
853 /**
854  * Spits out an error message and throws a config::error.
855  * Called when apply_base_unit() detects a cycle.
856  * (This exists merely to take the error message out of that function.)
857  */
858 void throw_base_unit_recursion_error(const std::vector<std::string>& base_tree, const std::string& base_id)
859 {
860  std::stringstream ss;
861  ss << "[base_unit] recursion loop in [unit_type] ";
862 
863  for(const std::string& step : base_tree) {
864  ss << step << "->";
865  }
866 
867  ss << base_id;
868  ERR_CF << ss.str() << '\n';
869 
870  throw config::error(ss.str());
871 }
872 
873 const boost::regex fai_identifier("[a-zA-Z_]+");
874 
875 template<typename MoveT>
876 void patch_movetype(
877  MoveT& mt, const std::string& type, const std::string& new_key, const std::string& formula_str, int default_val, bool replace)
878 {
879  config temp_cfg, original_cfg;
880  mt.write(original_cfg);
881 
882  if(!replace && !original_cfg[new_key].blank()) {
883  // Don't replace if the key already exists in the config (even if empty).
884  return;
885  }
886 
887  gui2::typed_formula<int> formula(formula_str);
888  wfl::map_formula_callable original;
889 
890  boost::sregex_iterator m(formula_str.begin(), formula_str.end(), fai_identifier);
891  for(const boost::sregex_iterator::value_type& p : std::make_pair(m, boost::sregex_iterator())) {
892  const std::string var_name = p.str();
893 
894  wfl::variant val(original_cfg[var_name].to_int(default_val));
895  original.add(var_name, val);
896  }
897 
898  temp_cfg[new_key] = formula(original);
899  mt.merge(temp_cfg, type, true);
900 }
901 } // unnamed namespace
902 
903 
904 
905 /**
906  * Modifies the provided config by merging all base units into it.
907  * The @a base_tree parameter is used for detecting and reporting
908  * cycles of base units and in particular to prevent infinite loops.
909  */
910 
911 void unit_type_data::apply_base_unit(unit_type& type, std::vector<std::string>& base_tree)
912 {
913  // Nothing to do.
914  if(type.base_unit_id_.empty()) {
915  return;
916  }
917 
918  // Detect recursion so the WML author is made aware of an error.
919  if(std::find(base_tree.begin(), base_tree.end(), type.base_unit_id_) != base_tree.end()) {
920  throw_base_unit_recursion_error(base_tree, type.base_unit_id_);
921  }
922 
923  // Find the base unit.
924  const unit_type_map::iterator itor = types_.find(type.base_unit_id_);
925  if(itor != types_.end()) {
926 
927  unit_type& base_type = itor->second;
928 
929  // Make sure the base unit has had its base units accounted for.
930  base_tree.push_back(type.base_unit_id_);
931 
932  apply_base_unit(base_type, base_tree);
933 
934  base_tree.pop_back();
935 
936  // Merge the base unit "under" our config.
937  type.writable_cfg().inherit_from(base_type.get_cfg());
938  }
939  else {
940  ERR_CF << "[base_unit]: unit type not found: " << type.base_unit_id_ << std::endl;
941  throw config::error("unit type not found: " + type.base_unit_id_);
942  }
943 }
944 
945 /**
946  * Handles inheritance for configs of [male], [female], and [variation].
947  * Also removes gendered children, as those serve no purpose.
948  * @a default_inherit is the default value for inherit=.
949  */
950 std::unique_ptr<unit_type> unit_type::create_sub_type(const config& var_cfg, bool default_inherit)
951 {
952  config var_copy = var_cfg;
953  if(var_cfg["inherit"].to_bool(default_inherit)) {
954  var_copy.inherit_from(get_cfg());
955  }
956 
957  var_copy.clear_children("male");
958  var_copy.clear_children("female");
959 
960  return std::make_unique<unit_type>(std::move(var_copy), parent_id());
961 }
962 
963 /**
964  * Processes [variation] tags of @a ut_cfg, handling inheritance and
965  * child clearing.
966  */
968 {
969  // Most unit types do not have variations.
970  if(!get_cfg().has_child("variation")) {
971  return;
972  }
973 
974  // Handle each variation's inheritance.
975  for(const config& var_cfg : get_cfg().child_range("variation")) {
976 
977  std::unique_ptr<unit_type> var = create_sub_type(var_cfg, false);
978 
979  var->built_cfg_->remove_children("variation", [](const config&){return true;});
980  var->variation_id_ = var_cfg["variation_id"].str();
981  var->debug_id_ = debug_id_ + " [" + var->variation_id_ + "]";
982 
984  bool success;
985  std::tie(ut, success) = variations_.emplace(var_cfg["variation_id"].str(), std::move(*var));
986  if(!success) {
987  ERR_CF << "Skipping duplicate unit variation ID: " << var_cfg["variation_id"] << "\n";
988  }
989  }
990 
991 
992 }
993 
994 
996 {
997  // Complete the gender-specific children of the config.
998  if(const config& male_cfg = get_cfg().child("male")) {
999  gender_types_[0] = create_sub_type(male_cfg, true);
1000  gender_types_[0]->fill_variations();
1001  }
1002 
1003  if(const config& female_cfg = get_cfg().child("female")) {
1004  gender_types_[1] = create_sub_type(female_cfg, true);
1005  gender_types_[1]->fill_variations();
1006  }
1007 
1008  // Complete the variation-defining children of the config.
1009  fill_variations();
1010 
1012 }
1013 /**
1014  * Resets all data based on the provided config.
1015  * This includes some processing of the config, such as expanding base units.
1016  * A pointer to the config is stored, so the config must be persistent.
1017  */
1019 {
1020  LOG_UT << "unit_type_data::set_config, nunits: " << cfg.child_range("unit_type").size() << "\n";
1021 
1022  clear();
1023  units_cfg_ = cfg;
1024 
1025  for(const config& mt : cfg.child_range("movetype")) {
1026  movement_types_.emplace(mt["name"].str(), movetype(mt));
1027 
1029  }
1030 
1031  for(const config& r : cfg.child_range("race")) {
1032  const unit_race race(r);
1033  races_.emplace(race.id(), race);
1034 
1036  }
1037 
1038  // Movetype resistance patching
1039  for(const config& r : cfg.child_range("resistance_defaults")) {
1040  const std::string& dmg_type = r["id"];
1041  config temp_cfg;
1042 
1043  for(const config::attribute& attr : r.attribute_range()) {
1044  const std::string& mt = attr.first;
1045 
1046  if(mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1047  continue;
1048  }
1049 
1050  patch_movetype(movement_types_[mt], "resistances", dmg_type, attr.second, 100, true);
1051  }
1052 
1053  if(r.has_attribute("default")) {
1054  for(movement_type_map::value_type& mt : movement_types_) {
1055  // Don't apply a default if a value is explicitly specified.
1056  if(r.has_attribute(mt.first)) {
1057  continue;
1058  }
1059 
1060  patch_movetype(mt.second, "resistances", dmg_type, r["default"], 100, false);
1061  }
1062  }
1063  }
1064 
1065  // Movetype move/defend patching
1066  for(const config& terrain : cfg.child_range("terrain_defaults")) {
1067  const std::string& ter_type = terrain["id"];
1068  config temp_cfg;
1069 
1070  static const std::array<std::string, 4> terrain_info_tags {{"movement", "vision", "jamming", "defense"}};
1071 
1072  for(const std::string& tag : terrain_info_tags) {
1073  if(!terrain.has_child(tag)) {
1074  continue;
1075  }
1076 
1077  const config& info = terrain.child(tag);
1078 
1079  for(const config::attribute& attr : info.attribute_range()) {
1080  const std::string& mt = attr.first;
1081 
1082  if(mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1083  continue;
1084  }
1085 
1086  if(tag == "defense") {
1087  patch_movetype(movement_types_[mt], tag, ter_type, attr.second, 100, true);
1088  } else {
1089  patch_movetype(movement_types_[mt], tag, ter_type, attr.second, 99, true);
1090  }
1091  }
1092 
1093  if(info.has_attribute("default")) {
1094  for(movement_type_map::value_type& mt : movement_types_) {
1095  // Don't apply a default if a value is explicitly specified.
1096  if(info.has_attribute(mt.first)) {
1097  continue;
1098  }
1099 
1100  if(tag == "defense") {
1101  patch_movetype(mt.second, tag, ter_type, info["default"], 100, false);
1102  } else {
1103  patch_movetype(mt.second, tag, ter_type, info["default"], 99, false);
1104  }
1105  }
1106  }
1107  }
1108  }
1109 
1110  for(const config& ut : cfg.child_range("unit_type")) {
1111  // Every type is required to have an id.
1112  std::string id = ut["id"].str();
1113  if(id.empty()) {
1114  ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
1115  continue;
1116  }
1117 
1118  if(types_.emplace(id, unit_type(ut)).second) {
1119  LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
1120  } else {
1121  ERR_CF << "Multiple [unit_type]s with id=" << id << " encountered." << std::endl;
1122  }
1123  }
1124 
1125  // Apply base units.
1126  for(auto& type : types_) {
1127  std::vector<std::string> base_tree(1, type.second.id());
1128  apply_base_unit(type.second, base_tree);
1129 
1131  }
1132 
1133  //handle [male], [female], [variation]
1134  for(auto& type : types_) {
1135  type.second.fill_variations_and_gender();
1136 
1138  }
1139 
1140  // Build all unit types. (This was not done within the loop for performance.)
1142 
1143  // Suppress some unit types (presumably used as base units) from the help.
1144  if(const config& hide_help = cfg.child("hide_help")) {
1145  hide_help_all_ = hide_help["all"].to_bool();
1147  }
1148  DBG_UT << "Finished creatign unti types\n";
1149 }
1150 
1152 {
1153  ut.build(status, movement_types_, races_, units_cfg().child_range("trait"));
1154 }
1155 
1156 /**
1157  * Finds a unit_type by its id() and makes sure it is built to the specified level.
1158  */
1160 {
1161  if(key.empty() || key == "random") {
1162  return nullptr;
1163  }
1164 
1165  DBG_CF << "trying to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1166  const unit_type_map::iterator itor = types_.find(key);
1167 
1168  // This might happen if units of another era are requested (for example for savegames)
1169  if(itor == types_.end()) {
1170  DBG_CF << "unable to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1171  return nullptr;
1172  }
1173 
1174  // Make sure the unit_type is built to the requested level.
1175  build_unit_type(itor->second, status);
1176 
1177  return &itor->second;
1178 }
1179 
1180 void unit_type_data::check_types(const std::vector<std::string>& types) const
1181 {
1182  for(const std::string& type : types) {
1183  if(!find(type)) {
1184  throw game::game_error("unknown unit type: " + type);
1185  }
1186  }
1187 }
1188 
1190 {
1191  types_.clear();
1192  movement_types_.clear();
1193  races_.clear();
1195 
1196  hide_help_all_ = false;
1197  hide_help_race_.clear();
1198  hide_help_type_.clear();
1199 }
1200 
1202 {
1203  // Nothing to do if already built to the requested level.
1204  if(status <= build_status_) {
1205  return;
1206  }
1207 
1208  for(const auto& type : types_) {
1209  build_unit_type(type.second, status);
1210 
1212  }
1213 
1214  build_status_ = status;
1215 }
1216 
1218 {
1219  if(!cfg) {
1220  return;
1221  }
1222 
1223  hide_help_race_.emplace_back();
1224  hide_help_type_.emplace_back();
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 
1235  for(const std::string& t_id : trees) {
1236  unit_type_map::iterator ut = types_.find(t_id);
1237 
1238  if(ut != types_.end()) {
1239  std::set<std::string> adv_tree = ut->second.advancement_tree();
1240  hide_help_type_.back().insert(adv_tree.begin(), adv_tree.end());
1241  }
1242  }
1243 
1244  // We recursively call all the imbricated [not] tags
1245  read_hide_help(cfg.child("not"));
1246 }
1247 
1249 {
1250  bool res = hide_help_all_;
1251  int lvl = hide_help_all_ ? 1 : 0; // first level is covered by 'all=yes'
1252 
1253  // supposed to be equal but let's be cautious
1254  int lvl_nb = std::min(hide_help_race_.size(), hide_help_type_.size());
1255 
1256  for(; lvl < lvl_nb; ++lvl) {
1257  if(hide_help_race_[lvl].count(race) || hide_help_type_[lvl].count(type)) {
1258  res = !res; // each level is a [not]
1259  }
1260  }
1261 
1262  return res;
1263 }
1264 
1266 {
1267  race_map::const_iterator i = races_.find(key);
1268  return i != races_.end() ? &i->second : nullptr;
1269 }
1270 
1272 {
1273  build_created();
1274  if(auto p_setxp = cfg.get("set_experience")) {
1275  experience_needed_ = p_setxp->to_int();
1276  }
1277  if(auto attr = cfg.get("set_advances_to")) {
1278  advances_to_ = utils::split(attr->str());
1279  }
1280  if(auto attr = cfg.get("set_cost")) {
1281  cost_ = attr->to_int(1);
1282  }
1283  if(auto attr = cfg.get("add_advancement")) {
1284  for(const auto& str : utils::split(attr->str())) {
1285  advances_to_.push_back(str);
1286  }
1287  }
1288  if(auto attr = cfg.get("remove_advancement")) {
1289  for(const auto& str : utils::split(attr->str())) {
1290  boost::remove_erase(advances_to_, str);
1291  }
1292  }
1293 
1294  // apply recursively to subtypes.
1295  for(int gender = 0; gender <= 1; ++gender) {
1296  if(!gender_types_[gender]) {
1297  continue;
1298  }
1299  gender_types_[gender]->apply_scenario_fix(cfg);
1300  }
1301 
1302  if(get_cfg().has_child("variation")) {
1303  // Make sure the variations are created.
1304  unit_types.build_unit_type(*this, VARIATIONS);
1305  for(auto& v : variations_) {
1306  v.second.apply_scenario_fix(cfg);
1307  }
1308  }
1309 }
1310 
1312 {
1313  unit_type_map::iterator itor = types_.find(cfg["type"].str());
1314  // This might happen if units of another era are requested (for example for savegames)
1315  if(itor != types_.end()) {
1316  itor->second.apply_scenario_fix(cfg);
1317  }
1318  else {
1319  // should we give an error message?
1320  }
1321 }
1322 
1324 {
1325  advances_to_.clear();
1326  const std::string& advances_to_val = get_cfg()["advances_to"];
1327  if(advances_to_val != "null" && !advances_to_val.empty()) {
1328  advances_to_ = utils::split(advances_to_val);
1329  }
1330  experience_needed_ = get_cfg()["experience"].to_int(500);
1331  cost_ = get_cfg()["cost"].to_int(1);
1332 
1333  // apply recursively to subtypes.
1334  for(int gender = 0; gender <= 1; ++gender) {
1335  if(!gender_types_[gender]) {
1336  continue;
1337  }
1338  gender_types_[gender]->remove_scenario_fixes();
1339  }
1340  for(auto& v : variations_) {
1341  v.second.remove_scenario_fixes();
1342  }
1343 }
1344 
1346 {
1347  for(auto& pair : types_) {
1348  pair.second.remove_scenario_fixes();
1349  }
1350 }
1351 
1353 {
1354  assert(!id.empty());
1355 
1356  // We don't allow leading whitepaces.
1357  if(id[0] == ' ') {
1358  throw error("Found unit type id with a leading whitespace \"" + id + "\"");
1359  }
1360 
1361  bool gave_warning = false;
1362 
1363  for(std::size_t pos = 0; pos < id.size(); ++pos) {
1364  const char c = id[pos];
1365  const bool valid = std::isalnum(c, std::locale::classic()) || c == '_' || c == ' ';
1366 
1367  if(!valid) {
1368  if(!gave_warning) {
1369  ERR_UT << "Found unit type id with invalid characters: \"" << id << "\"\n";
1370  gave_warning = true;
1371  }
1372 
1373  id[pos] = '_';
1374  }
1375  }
1376 }
1377 
1379 
1381 {
1382  // Create a temp copy
1383  std::string temp = profile;
1384 
1385  static const std::string path_adjust = "/transparent";
1386  const std::string::size_type offset = profile.find_last_of('/', profile.find('~'));
1387 
1388  // If the path already refers to /transparent...
1389  if(profile.find(path_adjust) != std::string::npos && offset != std::string::npos) {
1390  if(!image::locator(profile).file_exists()) {
1391  profile.replace(profile.find(path_adjust), path_adjust.length(), "");
1392  }
1393 
1394  return;
1395  }
1396 
1397  // else, check for the file with /transparent appended...
1398  offset != std::string::npos ? temp.insert(offset, path_adjust) : temp = path_adjust + temp;
1399 
1400  // and use that path if it exists.
1401  if(image::locator(temp).file_exists()) {
1402  profile = temp;
1403  }
1404 }
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:599
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
std::map< std::string, movetype > movement_type_map
Definition: types.hpp:35
void read_hide_help(const config &cfg)
Parses the [hide_help] tag.
Definition: types.cpp:1217
#define DBG_UT
Definition: types.cpp:48
std::string default_variation_
Definition: types.hpp:338
static int get_acceleration()
Definition: types.cpp:541
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:1159
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:419
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:499
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:1180
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
static game_config_view wrap(const config &cfg)
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:689
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:494
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:967
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:546
void build_all(unit_type::BUILD_STATUS status)
Makes sure the all unit_types are built to the specified level.
Definition: types.cpp:1201
config possible_traits_
Definition: types.hpp:356
unit_type_data unit_types
Definition: types.cpp:1378
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:742
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:614
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1311
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
std::string str
Definition: statement.cpp:110
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
static lg::log_domain log_config("config")
const std::vector< std::string > advances_from() const
A vector of unit_type ids that can advance to this unit_type.
Definition: types.cpp:621
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:1151
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:641
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:100
void fill_variations_and_gender()
Definition: types.cpp:995
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:68
bool hide_help() const
Definition: types.cpp:593
const std::vector< unit_animation > & animations() const
Definition: types.cpp:503
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:143
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:1323
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:120
static void add_color_info(const game_config_view &v, bool build_defaults)
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:130
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:1265
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:714
unit_type_map types_
Definition: types.hpp:404
std::vector< std::string > get_ability_list() const
Definition: types.cpp:573
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1271
mock_party p
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:731
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:115
bool has_ability_by_id(const std::string &ability) const
Definition: types.cpp:560
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:108
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:530
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:1248
#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:773
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:808
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:1018
void clear()
Definition: types.cpp:1189
double t
Definition: astarsearch.cpp:64
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:475
std::vector< std::reference_wrapper< const config > > config_array_view
std::vector< std::string > split(const config_attribute_value &val)
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:69
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:1380
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:911
void remove_scenario_fixes()
Definition: types.cpp:1345
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:454
static void check_id(std::string &id)
Validate the id argument.
Definition: types.cpp:1352
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:950
const_attack_itors attacks() const
Definition: types.cpp:512
std::map< std::string, unit_race > race_map
Definition: race.hpp:94
boost::iterator_range< boost::indirect_iterator< attack_list::const_iterator > > const_attack_itors
t_string unit_description() const
Definition: types.cpp:485
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:694
const std::string & undead_variation() const
Definition: race.hpp:48
bool has_variation(const std::string &variation_id) const
Definition: types.cpp:726
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
auto * ss
Definition: result_set.cpp:281
void build_created()
Load the most needed data into an empty unit_type (build to CREATE).
Definition: types.cpp:376