The Battle for Wesnoth  1.15.12+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 "formula/callable_objects.hpp"
23 #include "game_config.hpp"
24 #include "game_errors.hpp" //thrown sometimes
25 //#include "gettext.hpp"
26 #include "log.hpp"
27 #include "units/abilities.hpp"
28 #include "units/animation.hpp"
29 #include "units/unit.hpp"
30 #include "utils/iterable_pair.hpp"
31 #include "utils/make_enum.hpp"
32 
35 
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  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 
454 const unit_type& unit_type::get_gender_unit_type(std::string gender) const
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 
475 const unit_type& unit_type::get_variation(const std::string& id) const
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()) {
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 
689 const std::string& unit_type::flag_rgb() const
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 
822  std::string str = std::string();
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 /**
874  * Insert a new value into a movetype, possibly calculating the value based on
875  * the existing values in the target movetype.
876  */
877 void patch_movetype(movetype& mt,
878  const std::string& type_to_patch,
879  const std::string& new_key,
880  const std::string& formula_str,
881  int default_val,
882  bool replace)
883 {
884  LOG_CONFIG << "Patching " << new_key << " into movetype." << type_to_patch << std::endl;
885  config mt_cfg;
886  mt.write(mt_cfg);
887 
888  if(!replace && mt_cfg.child_or_empty(type_to_patch).has_attribute(new_key)) {
889  // Don't replace if this type already exists in the config
890  return;
891  }
892 
893  // Make movement_costs.flat, defense.castle, etc available to the formula.
894  // The formula uses config_callables which take references to data in mt;
895  // the enclosing scope is to run all the destructors before calling mt's
896  // non-const merge() function. Probably unnecessary, but I'd rather write
897  // it this way than debug it afterwards.
898  config temp_cfg;
899  {
900  // Instances of wfl::config_callable take a reference to a config,
901  // which means that the "cumulative_values" variable below needs to be
902  // copied so that movement costs aren't overwritten by vision costs
903  // before the formula is evaluated.
904  std::list<config> config_copies;
905 
906  gui2::typed_formula<int> formula(formula_str, default_val);
907  wfl::map_formula_callable original;
908 
909  // These three need to follow movetype's fallback system, where values for
910  // movement costs are used for vision too.
911  const auto fallback_children = std::array<std::string, 3>{{"movement_costs", "vision_costs", "jamming_costs"}};
912  config cumulative_values;
913  for(const auto& x : fallback_children) {
914  if(mt_cfg.has_child(x)) {
915  cumulative_values.merge_with(mt_cfg.child(x));
916  }
917  config_copies.emplace_back(cumulative_values);
918  auto val = std::make_shared<wfl::config_callable>(config_copies.back());
919  original.add(x, val);
920 
921  // Allow "flat" to work as "vision_costs.flat" when patching vision_costs, etc
922  if(type_to_patch == x) {
923  original.set_fallback(val);
924  }
925  }
926 
927  // These don't need the fallback system
928  const auto child_names = std::array<std::string, 2>{{"defense", "resistance"}};
929  for(const auto& x : child_names) {
930  if(mt_cfg.has_child(x)) {
931  const auto& subtag = mt_cfg.child(x);
932  auto val = std::make_shared<wfl::config_callable>(subtag);
933  original.add(x, val);
934 
935  // Allow "arcane" to work as well as "resistance.arcane", etc
936  if(type_to_patch == x) {
937  original.set_fallback(val);
938  }
939  }
940  }
941 
942  LOG_CONFIG << " formula=" << formula_str << ", resolves to " << formula(original) << std::endl;
943  temp_cfg[new_key] = formula(original);
944  }
945  mt.merge(temp_cfg, type_to_patch, true);
946 }
947 } // unnamed namespace
948 
949 /**
950  * Modifies the provided config by merging all base units into it.
951  * The @a base_tree parameter is used for detecting and reporting
952  * cycles of base units and in particular to prevent infinite loops.
953  */
954 
955 void unit_type_data::apply_base_unit(unit_type& type, std::vector<std::string>& base_tree)
956 {
957  // Nothing to do.
958  if(type.base_unit_id_.empty()) {
959  return;
960  }
961 
962  // Detect recursion so the WML author is made aware of an error.
963  if(std::find(base_tree.begin(), base_tree.end(), type.base_unit_id_) != base_tree.end()) {
964  throw_base_unit_recursion_error(base_tree, type.base_unit_id_);
965  }
966 
967  // Find the base unit.
968  const unit_type_map::iterator itor = types_.find(type.base_unit_id_);
969  if(itor != types_.end()) {
970 
971  unit_type& base_type = itor->second;
972 
973  // Make sure the base unit has had its base units accounted for.
974  base_tree.push_back(type.base_unit_id_);
975 
976  apply_base_unit(base_type, base_tree);
977 
978  base_tree.pop_back();
979 
980  // Merge the base unit "under" our config.
981  type.writable_cfg().inherit_from(base_type.get_cfg());
982  }
983  else {
984  ERR_CF << "[base_unit]: unit type not found: " << type.base_unit_id_ << std::endl;
985  throw config::error("unit type not found: " + type.base_unit_id_);
986  }
987 }
988 
989 /**
990  * Handles inheritance for configs of [male], [female], and [variation].
991  * Also removes gendered children, as those serve no purpose.
992  * @a default_inherit is the default value for inherit=.
993  */
994 std::unique_ptr<unit_type> unit_type::create_sub_type(const config& var_cfg, bool default_inherit)
995 {
996  config var_copy = var_cfg;
997  if(var_cfg["inherit"].to_bool(default_inherit)) {
998  var_copy.inherit_from(get_cfg());
999  }
1000 
1001  var_copy.clear_children("male");
1002  var_copy.clear_children("female");
1003 
1004  return std::make_unique<unit_type>(std::move(var_copy), parent_id());
1005 }
1006 
1007 /**
1008  * Processes [variation] tags of @a ut_cfg, handling inheritance and
1009  * child clearing.
1010  */
1012 {
1013  // Most unit types do not have variations.
1014  if(!get_cfg().has_child("variation")) {
1015  return;
1016  }
1017 
1018  // Handle each variation's inheritance.
1019  for(const config& var_cfg : get_cfg().child_range("variation")) {
1020 
1021  std::unique_ptr<unit_type> var = create_sub_type(var_cfg, false);
1022 
1023  var->built_cfg_->remove_children("variation", [](const config&){return true;});
1024  var->variation_id_ = var_cfg["variation_id"].str();
1025  var->debug_id_ = debug_id_ + " [" + var->variation_id_ + "]";
1026 
1028  bool success;
1029  std::tie(ut, success) = variations_.emplace(var_cfg["variation_id"].str(), std::move(*var));
1030  if(!success) {
1031  ERR_CF << "Skipping duplicate unit variation ID: " << var_cfg["variation_id"] << "\n";
1032  }
1033  }
1034 
1035 
1036 }
1037 
1038 
1040 {
1041  // Complete the gender-specific children of the config.
1042  if(const config& male_cfg = get_cfg().child("male")) {
1043  gender_types_[0] = create_sub_type(male_cfg, true);
1044  gender_types_[0]->fill_variations();
1045  }
1046 
1047  if(const config& female_cfg = get_cfg().child("female")) {
1048  gender_types_[1] = create_sub_type(female_cfg, true);
1049  gender_types_[1]->fill_variations();
1050  }
1051 
1052  // Complete the variation-defining children of the config.
1053  fill_variations();
1054 
1056 }
1057 /**
1058  * Resets all data based on the provided config.
1059  * This includes some processing of the config, such as expanding base units.
1060  * A pointer to the config is stored, so the config must be persistent.
1061  */
1063 {
1064  LOG_UT << "unit_type_data::set_config, nunits: " << cfg.child_range("unit_type").size() << "\n";
1065 
1066  clear();
1067  units_cfg_ = cfg;
1068 
1069  for(const config& mt : cfg.child_range("movetype")) {
1070  movement_types_.emplace(mt["name"].str(), movetype(mt));
1071 
1073  }
1074 
1075  for(const config& r : cfg.child_range("race")) {
1076  const unit_race race(r);
1077  races_.emplace(race.id(), race);
1078 
1080  }
1081 
1082  // Movetype resistance patching
1083  DBG_CF << "Start of movetype patching" << std::endl;
1084  for(const config& r : cfg.child_range("resistance_defaults")) {
1085  const std::string& dmg_type = r["id"];
1086 
1087  for(const config::attribute& attr : r.attribute_range()) {
1088  const std::string& mt = attr.first;
1089 
1090  if(mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1091  continue;
1092  }
1093 
1094  DBG_CF << "Patching specific movetype " << mt << std::endl;
1095  patch_movetype(movement_types_[mt], "resistance", dmg_type, attr.second, 100, true);
1096  }
1097 
1098  if(r.has_attribute("default")) {
1099  for(movement_type_map::value_type& mt : movement_types_) {
1100  // Don't apply a default if a value is explicitly specified.
1101  if(r.has_attribute(mt.first)) {
1102  continue;
1103  }
1104  // The "none" movetype expects everything to have the default value (to be UNREACHABLE)
1105  if(mt.first == "none") {
1106  continue;
1107  }
1108 
1109  patch_movetype(mt.second, "resistance", dmg_type, r["default"], 100, false);
1110  }
1111  }
1112  }
1113  DBG_CF << "Split between resistance and cost patching" << std::endl;
1114 
1115  // Movetype move/defend patching
1116  for(const config& terrain : cfg.child_range("terrain_defaults")) {
1117  const std::string& ter_type = terrain["id"];
1118 
1119  struct ter_defs_to_movetype
1120  {
1121  /** The data to read from is in [terrain_defaults][subtag], and corresponds to [movetype][subtag] */
1122  std::string subtag;
1123  /** Deprecated names used in 1.14.0's [terrain_defaults]. For [defense] the name didn't change. */
1124  std::string alias;
1125  int default_val;
1126  };
1127  const std::array<ter_defs_to_movetype, 4> terrain_info_tags{
1128  ter_defs_to_movetype{{"movement_costs"}, {"movement"}, movetype::UNREACHABLE},
1129  ter_defs_to_movetype{{"vision_costs"}, {"vision"}, movetype::UNREACHABLE},
1130  ter_defs_to_movetype{{"jamming_costs"}, {"jamming"}, movetype::UNREACHABLE},
1131  ter_defs_to_movetype{{"defense"}, {"defense"}, 100}
1132  };
1133 
1134  for(const auto& cost_type : terrain_info_tags) {
1135  const std::string* src_tag = nullptr;
1136  if(terrain.has_child(cost_type.subtag)) {
1137  src_tag = &cost_type.subtag;
1138  }
1139  else if(terrain.has_child(cost_type.alias)) {
1140  // Check for the deprecated name, no deprecation warnings are printed.
1141  src_tag = &cost_type.alias;
1142  }
1143  if(!src_tag) {
1144  continue;
1145  }
1146 
1147  const config& info = terrain.child(*src_tag);
1148 
1149  for(const config::attribute& attr : info.attribute_range()) {
1150  const std::string& mt = attr.first;
1151 
1152  if(mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1153  continue;
1154  }
1155 
1156  patch_movetype(
1157  movement_types_[mt], cost_type.subtag, ter_type, attr.second, cost_type.default_val, true);
1158  }
1159 
1160  if(info.has_attribute("default")) {
1161  for(movement_type_map::value_type& mt : movement_types_) {
1162  // Don't apply a default if a value is explicitly specified.
1163  if(info.has_attribute(mt.first)) {
1164  continue;
1165  }
1166  // The "none" movetype expects everything to have the default value
1167  if(mt.first == "none") {
1168  continue;
1169  }
1170 
1171  patch_movetype(
1172  mt.second, cost_type.subtag, ter_type, info["default"], cost_type.default_val, false);
1173  }
1174  }
1175  }
1176  }
1177  DBG_CF << "End of movetype patching" << std::endl;
1178 
1179  for(const config& ut : cfg.child_range("unit_type")) {
1180  // Every type is required to have an id.
1181  std::string id = ut["id"].str();
1182  if(id.empty()) {
1183  ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
1184  continue;
1185  }
1186 
1187  if(types_.emplace(id, unit_type(ut)).second) {
1188  LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
1189  } else {
1190  ERR_CF << "Multiple [unit_type]s with id=" << id << " encountered." << std::endl;
1191  }
1192  }
1193 
1194  // Apply base units.
1195  for(auto& type : types_) {
1196  std::vector<std::string> base_tree(1, type.second.id());
1197  apply_base_unit(type.second, base_tree);
1198 
1200  }
1201 
1202  //handle [male], [female], [variation]
1203  for(auto& type : types_) {
1204  type.second.fill_variations_and_gender();
1205 
1207  }
1208 
1209  // Build all unit types. (This was not done within the loop for performance.)
1211 
1212  // Suppress some unit types (presumably used as base units) from the help.
1213  if(const config& hide_help = cfg.child("hide_help")) {
1214  hide_help_all_ = hide_help["all"].to_bool();
1216  }
1217  DBG_UT << "Finished creatign unti types\n";
1218 }
1219 
1221 {
1222  ut.build(status, movement_types_, races_, units_cfg().child_range("trait"));
1223 }
1224 
1225 /**
1226  * Finds a unit_type by its id() and makes sure it is built to the specified level.
1227  */
1228 const unit_type* unit_type_data::find(const std::string& key, unit_type::BUILD_STATUS status) const
1229 {
1230  if(key.empty() || key == "random") {
1231  return nullptr;
1232  }
1233 
1234  DBG_CF << "trying to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1235  const unit_type_map::iterator itor = types_.find(key);
1236 
1237  // This might happen if units of another era are requested (for example for savegames)
1238  if(itor == types_.end()) {
1239  DBG_CF << "unable to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1240  return nullptr;
1241  }
1242 
1243  // Make sure the unit_type is built to the requested level.
1244  build_unit_type(itor->second, status);
1245 
1246  return &itor->second;
1247 }
1248 
1249 void unit_type_data::check_types(const std::vector<std::string>& types) const
1250 {
1251  for(const std::string& type : types) {
1252  if(!find(type)) {
1253  throw game::game_error("unknown unit type: " + type);
1254  }
1255  }
1256 }
1257 
1259 {
1260  types_.clear();
1261  movement_types_.clear();
1262  races_.clear();
1264 
1265  hide_help_all_ = false;
1266  hide_help_race_.clear();
1267  hide_help_type_.clear();
1268 }
1269 
1271 {
1272  // Nothing to do if already built to the requested level.
1273  if(status <= build_status_) {
1274  return;
1275  }
1276 
1277  for(const auto& type : types_) {
1278  build_unit_type(type.second, status);
1279 
1281  }
1282 
1283  build_status_ = status;
1284 }
1285 
1287 {
1288  if(!cfg) {
1289  return;
1290  }
1291 
1292  hide_help_race_.emplace_back();
1293  hide_help_type_.emplace_back();
1294 
1295  std::vector<std::string> races = utils::split(cfg["race"]);
1296  hide_help_race_.back().insert(races.begin(), races.end());
1297 
1298  std::vector<std::string> types = utils::split(cfg["type"]);
1299  hide_help_type_.back().insert(types.begin(), types.end());
1300 
1301  std::vector<std::string> trees = utils::split(cfg["type_adv_tree"]);
1302  hide_help_type_.back().insert(trees.begin(), trees.end());
1303 
1304  for(const std::string& t_id : trees) {
1305  unit_type_map::iterator ut = types_.find(t_id);
1306 
1307  if(ut != types_.end()) {
1308  std::set<std::string> adv_tree = ut->second.advancement_tree();
1309  hide_help_type_.back().insert(adv_tree.begin(), adv_tree.end());
1310  }
1311  }
1312 
1313  // We recursively call all the imbricated [not] tags
1314  read_hide_help(cfg.child("not"));
1315 }
1316 
1317 bool unit_type_data::hide_help(const std::string& type, const std::string& race) const
1318 {
1319  bool res = hide_help_all_;
1320  int lvl = hide_help_all_ ? 1 : 0; // first level is covered by 'all=yes'
1321 
1322  // supposed to be equal but let's be cautious
1323  int lvl_nb = std::min(hide_help_race_.size(), hide_help_type_.size());
1324 
1325  for(; lvl < lvl_nb; ++lvl) {
1326  if(hide_help_race_[lvl].count(race) || hide_help_type_[lvl].count(type)) {
1327  res = !res; // each level is a [not]
1328  }
1329  }
1330 
1331  return res;
1332 }
1333 
1334 const unit_race* unit_type_data::find_race(const std::string& key) const
1335 {
1336  race_map::const_iterator i = races_.find(key);
1337  return i != races_.end() ? &i->second : nullptr;
1338 }
1339 
1341 {
1342  build_created();
1343  if(auto p_setxp = cfg.get("set_experience")) {
1344  experience_needed_ = p_setxp->to_int();
1345  }
1346  if(auto attr = cfg.get("set_advances_to")) {
1347  advances_to_ = utils::split(attr->str());
1348  }
1349  if(auto attr = cfg.get("set_cost")) {
1350  cost_ = attr->to_int(1);
1351  }
1352  if(auto attr = cfg.get("add_advancement")) {
1353  for(const auto& str : utils::split(attr->str())) {
1354  advances_to_.push_back(str);
1355  }
1356  }
1357  if(auto attr = cfg.get("remove_advancement")) {
1358  for(const auto& str : utils::split(attr->str())) {
1359  boost::remove_erase(advances_to_, str);
1360  }
1361  }
1362 
1363  // apply recursively to subtypes.
1364  for(int gender = 0; gender <= 1; ++gender) {
1365  if(!gender_types_[gender]) {
1366  continue;
1367  }
1368  gender_types_[gender]->apply_scenario_fix(cfg);
1369  }
1370 
1371  if(get_cfg().has_child("variation")) {
1372  // Make sure the variations are created.
1373  unit_types.build_unit_type(*this, VARIATIONS);
1374  for(auto& v : variations_) {
1375  v.second.apply_scenario_fix(cfg);
1376  }
1377  }
1378 }
1379 
1381 {
1382  unit_type_map::iterator itor = types_.find(cfg["type"].str());
1383  // This might happen if units of another era are requested (for example for savegames)
1384  if(itor != types_.end()) {
1385  itor->second.apply_scenario_fix(cfg);
1386  }
1387  else {
1388  // should we give an error message?
1389  }
1390 }
1391 
1393 {
1394  advances_to_.clear();
1395  const std::string& advances_to_val = get_cfg()["advances_to"];
1396  if(advances_to_val != "null" && !advances_to_val.empty()) {
1397  advances_to_ = utils::split(advances_to_val);
1398  }
1399  experience_needed_ = get_cfg()["experience"].to_int(500);
1400  cost_ = get_cfg()["cost"].to_int(1);
1401 
1402  // apply recursively to subtypes.
1403  for(int gender = 0; gender <= 1; ++gender) {
1404  if(!gender_types_[gender]) {
1405  continue;
1406  }
1407  gender_types_[gender]->remove_scenario_fixes();
1408  }
1409  for(auto& v : variations_) {
1410  v.second.remove_scenario_fixes();
1411  }
1412 }
1413 
1415 {
1416  for(auto& pair : types_) {
1417  pair.second.remove_scenario_fixes();
1418  }
1419 }
1420 
1421 void unit_type::check_id(std::string& id)
1422 {
1423  assert(!id.empty());
1424 
1425  // We don't allow leading whitepaces.
1426  if(id[0] == ' ') {
1427  throw error("Found unit type id with a leading whitespace \"" + id + "\"");
1428  }
1429 
1430  bool gave_warning = false;
1431 
1432  for(std::size_t pos = 0; pos < id.size(); ++pos) {
1433  const char c = id[pos];
1434  const bool valid = std::isalnum(c, std::locale::classic()) || c == '_' || c == ' ';
1435 
1436  if(!valid) {
1437  if(!gave_warning) {
1438  ERR_UT << "Found unit type id with invalid characters: \"" << id << "\"\n";
1439  gave_warning = true;
1440  }
1441 
1442  id[pos] = '_';
1443  }
1444  }
1445 }
1446 
1448 
1449 void adjust_profile(std::string& profile)
1450 {
1451  // Create a temp copy
1452  std::string temp = profile;
1453 
1454  static const std::string path_adjust = "/transparent";
1455  const std::string::size_type offset = profile.find_last_of('/', profile.find('~'));
1456 
1457  // If the path already refers to /transparent...
1458  if(profile.find(path_adjust) != std::string::npos && offset != std::string::npos) {
1459  if(!image::locator(profile).file_exists()) {
1460  profile.replace(profile.find(path_adjust), path_adjust.length(), "");
1461  }
1462 
1463  return;
1464  }
1465 
1466  // else, check for the file with /transparent appended...
1467  offset != std::string::npos ? temp.insert(offset, path_adjust) : temp = path_adjust + temp;
1468 
1469  // and use that path if it exists.
1470  if(image::locator(temp).file_exists()) {
1471  profile = temp;
1472  }
1473 }
int jamming_
Definition: types.hpp:333
std::string parent_id_
The id of the top ancestor of this unit_type.
Definition: types.hpp:321
int experience_needed_
Definition: types.hpp:362
const std::string & parent_id() const
The id of the original type from which this (variation) descended.
Definition: types.hpp:147
UNIT_ALIGNMENT ALIGNMENT
Definition: types.hpp:186
bool has_cfg_build_
Definition: types.hpp:314
#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:92
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:414
bool do_not_list_
Definition: types.hpp:359
unit_type::BUILD_STATUS build_status_
Definition: types.hpp:429
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:77
void read_hide_help(const config &cfg)
Parses the [hide_help] tag.
Definition: types.cpp:1286
#define DBG_UT
Definition: types.cpp:48
std::string default_variation_
Definition: types.hpp:350
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:953
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:1228
double xp_bar_scaling
Definition: game_config.cpp:78
std::string flag_rgb_
Definition: types.hpp:343
void clear_children(T... keys)
Definition: config.hpp:526
ALIGNMENT alignment_
Definition: types.hpp:365
t_string variation_name_
Definition: types.hpp:352
std::string profile_
Definition: types.hpp:342
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:101
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_
A suffix for id_, used when logging messages.
Definition: types.hpp:319
int hitpoints_
Definition: types.hpp:327
void check_types(const std::vector< std::string > &types) const
Definition: types.cpp:1249
const std::vector< std::string > & advances_to() const
A vector of unit_type ids that this unit_type can advance to.
Definition: types.hpp:117
void set_fallback(const_formula_callable_ptr fallback)
Definition: callable.hpp:258
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
int recall_cost_
Definition: types.hpp:330
static game_config_view wrap(const config &cfg)
bool has_attribute(config_key_type key) const
Definition: config.cpp:207
logger & info()
Definition: log.cpp:88
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:175
config_array_view child_range(config_key_type key) const
std::string variation_id_
Definition: types.hpp:351
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:406
const std::string & flag_rgb() const
Definition: types.cpp:689
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:263
~unit_type()
Definition: types.cpp:170
child_itors child_range(config_key_type key)
Definition: config.cpp:356
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:220
const config * cfg_
Definition: types.hpp:311
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:766
std::string unit_rgb
t_string description_
Definition: types.hpp:325
void fill_variations()
Processes [variation] tags of ut_cfg, handling inheritance and child clearing.
Definition: types.cpp:1011
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:1270
config possible_traits_
Definition: types.hpp:369
unit_type_data unit_types
Definition: types.cpp:1447
std::array< std::unique_ptr< unit_type >, 2 > gender_types_
Definition: types.hpp:347
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:276
STL namespace.
BUILD_STATUS
Records the status of the lazy building of unit types.
Definition: types.hpp:76
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:1380
void clear()
Definition: config.cpp:895
t_string type_name_
Definition: types.hpp:324
variations_map variations_
Definition: types.hpp:349
const race_map & races() const
Definition: types.hpp:390
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:866
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:43
void write(config &cfg) const
Writes the movement type data to the provided config.
Definition: movetype.cpp:913
static std::string _(const char *str)
Definition: gettext.hpp:92
const unit_type_map & types() const
Definition: types.hpp:389
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:833
int level_
Definition: types.hpp:329
A single unit type that the player may recruit.
Definition: types.hpp:44
const unit_race * race_
Never nullptr, but may point to the null race.
Definition: types.hpp:355
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:1552
config & writable_cfg()
Definition: types.hpp:299
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 merge_with(const config &c)
Merge config &#39;c&#39; into this config, overwriting this config&#39;s values.
Definition: config.cpp:1199
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:1220
std::string icon_
Definition: types.hpp:340
void inherit_from(const config &c)
Merge config &#39;c&#39; into this config, preserving this config&#39;s values.
Definition: config.cpp:1252
bool musthave_status(const std::string &status) const
Definition: types.cpp:641
void fill_variations_and_gender()
Definition: types.cpp:1039
movement_type_map movement_types_
Definition: types.hpp:418
bool zoc_
Definition: types.hpp:359
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
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:424
std::vector< std::set< std::string > > hide_help_race_
Definition: types.hpp:425
std::vector< unit_animation > animations_
Definition: types.hpp:374
bool blank() const
Tests for an attribute that was never set.
std::string small_profile_
Definition: types.hpp:341
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:313
std::string undead_variation_
Definition: types.hpp:337
void remove_scenario_fixes()
Definition: types.cpp:1392
int movement_
Definition: types.hpp:331
movetype movement_type_
Definition: types.hpp:367
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:143
const config::const_child_itors & additional_traits() const
Definition: race.cpp:120
std::map< std::string, movetype > movement_type_map
Definition: types.hpp:35
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:328
unsigned int num_traits() const
Definition: race.cpp:130
Generic locator abstracting the location of an image.
Definition: picture.hpp:59
int cost_
Definition: types.hpp:335
const std::string log_id() const
A variant on id() that is more descriptive, for use with message logging.
Definition: types.hpp:145
const game_config_view & units_cfg() const
Definition: types.hpp:427
const unit_race * find_race(const std::string &) const
Definition: types.cpp:1334
std::vector< t_string > special_notes_
Definition: types.hpp:326
unsigned int num_traits_
Definition: types.hpp:345
std::size_t i
Definition: function.cpp:940
game_config_view units_cfg_
Definition: types.hpp:428
std::vector< std::string > variations() const
Definition: types.cpp:714
unit_type_map types_
Definition: types.hpp:417
std::vector< std::string > get_ability_list() const
Definition: types.cpp:573
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1340
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:361
std::string usage_
Definition: types.hpp:336
#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:46
double hp_bar_scaling_
Definition: types.hpp:328
bool hide_help_all_
True if [hide_help] contains a &#39;all=yes&#39; at its root.
Definition: types.hpp:422
std::string base_unit_id_
from [base_unit]
Definition: types.hpp:323
static void fill_initial_animations(std::vector< unit_animation > &animations, const config &cfg)
Definition: animation.cpp:483
#define N_(String)
Definition: gettext.hpp:100
std::string id_
Definition: types.hpp:317
config & add_child(config_key_type key)
Definition: config.cpp:500
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:1317
#define LOG_UT
Definition: types.cpp:49
std::vector< ability_metadata > abilities_
Definition: types.hpp:357
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:332
bool empty() const
Definition: tstring.hpp:186
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:376
void set_config(const game_config_view &cfg)
Resets all data based on the provided config.
Definition: types.cpp:1062
void clear()
Definition: types.cpp:1258
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:334
#define LOG_CONFIG
Definition: types.cpp:44
bool hide_help_
Definition: types.hpp:359
std::vector< ability_metadata > adv_abilities_
Definition: types.hpp:357
void adjust_profile(std::string &profile)
Definition: types.cpp:1449
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:955
static std::string sgettext(const char *str)
Definition: gettext.hpp:61
void remove_scenario_fixes()
Definition: types.cpp:1414
static const map_location & null_location()
Definition: location.hpp:80
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:1421
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:994
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:477
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:59
attack_list attacks_cache_
Definition: types.hpp:315
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 locator points at exists.
Definition: picture.cpp:658
std::string image_
Definition: types.hpp:339
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
race_map races_
Definition: types.hpp:419
const config & child(config_key_type key) const
std::vector< unit_race::GENDER > genders_
Definition: types.hpp:371
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:70
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:376