The Battle for Wesnoth  1.17.4+dev
types.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2022
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Handle unit-type specific attributes, animations, advancement.
19  */
20 
21 #include "units/types.hpp"
22 
23 #include "formula/callable_objects.hpp"
24 #include "game_config.hpp"
25 #include "game_errors.hpp" //thrown sometimes
26 //#include "gettext.hpp"
27 #include "language.hpp" // for string_table
28 #include "log.hpp"
29 #include "units/abilities.hpp"
30 #include "units/animation.hpp"
31 #include "units/unit.hpp"
32 #include "utils/iterable_pair.hpp"
33 
36 
37 #include <boost/range/algorithm_ext/erase.hpp>
38 
39 #include <array>
40 #include <locale>
41 
42 static lg::log_domain log_config("config");
43 #define ERR_CF LOG_STREAM(err, log_config)
44 #define WRN_CF LOG_STREAM(warn, log_config)
45 #define LOG_CONFIG LOG_STREAM(info, log_config)
46 #define DBG_CF LOG_STREAM(debug, log_config)
47 
48 static lg::log_domain log_unit("unit");
49 #define DBG_UT LOG_STREAM(debug, log_unit)
50 #define LOG_UT LOG_STREAM(info, log_unit)
51 #define ERR_UT LOG_STREAM(err, log_unit)
52 
53 /* ** unit_type ** */
54 
56  : cfg_(o.cfg_)
57  , id_(o.id_)
58  , debug_id_(o.debug_id_)
59  , parent_id_(o.parent_id_)
60  , base_unit_id_(o.base_unit_id_)
61  , type_name_(o.type_name_)
62  , description_(o.description_)
63  , hitpoints_(o.hitpoints_)
64  , hp_bar_scaling_(o.hp_bar_scaling_)
65  , xp_bar_scaling_(o.xp_bar_scaling_)
66  , level_(o.level_)
67  , recall_cost_(o.recall_cost_)
68  , movement_(o.movement_)
69  , vision_(o.vision_)
70  , jamming_(o.jamming_)
71  , max_attacks_(o.max_attacks_)
72  , cost_(o.cost_)
73  , usage_(o.usage_)
74  , undead_variation_(o.undead_variation_)
75  , image_(o.image_)
76  , icon_(o.icon_)
77  , small_profile_(o.small_profile_)
78  , profile_(o.profile_)
79  , flag_rgb_(o.flag_rgb_)
80  , num_traits_(o.num_traits_)
81  , variations_(o.variations_)
82  , default_variation_(o.default_variation_)
83  , variation_name_(o.variation_name_)
84  , race_(o.race_)
85  , abilities_(o.abilities_)
86  , adv_abilities_(o.adv_abilities_)
87  , zoc_(o.zoc_)
88  , hide_help_(o.hide_help_)
89  , do_not_list_(o.do_not_list_)
90  , advances_to_(o.advances_to_)
91  , experience_needed_(o.experience_needed_)
92  , alignment_(o.alignment_)
93  , movement_type_(o.movement_type_)
94  , possible_traits_(o.possible_traits_)
95  , genders_(o.genders_)
96  , animations_(o.animations_)
97  , build_status_(o.build_status_)
98 {
99  gender_types_[0].reset(gender_types_[0] != nullptr ? new unit_type(*o.gender_types_[0]) : nullptr);
100  gender_types_[1].reset(gender_types_[1] != nullptr ? new unit_type(*o.gender_types_[1]) : nullptr);
101 }
102 
103 unit_type::unit_type(defaut_ctor_t, const config& cfg, const std::string & parent_id)
104  : cfg_(nullptr)
105  , built_cfg_()
106  , has_cfg_build_()
107  , id_(cfg.has_attribute("id") ? cfg["id"].str() : parent_id)
108  , debug_id_()
109  , parent_id_(!parent_id.empty() ? parent_id : id_)
110  , base_unit_id_()
111  , type_name_()
112  , description_()
113  , hitpoints_(0)
114  , hp_bar_scaling_(0.0)
115  , xp_bar_scaling_(0.0)
116  , level_(0)
117  , recall_cost_()
118  , movement_(0)
119  , vision_(-1)
120  , jamming_(0)
121  , max_attacks_(0)
122  , cost_(0)
123  , usage_()
125  , image_()
126  , icon_()
127  , small_profile_()
128  , profile_()
129  , flag_rgb_()
130  , num_traits_(0)
131  , gender_types_()
132  , variations_()
134  , variation_name_()
135  , race_(&unit_race::null_race)
136  , abilities_()
137  , adv_abilities_()
138  , zoc_(false)
139  , hide_help_(false)
140  , do_not_list_()
141  , advances_to_()
142  , experience_needed_(0)
143  , alignment_(unit_alignments::type::neutral)
144  , movement_type_()
145  , possible_traits_()
146  , genders_()
147  , animations_()
149 {
150  if(const config& base_unit = cfg.child("base_unit")) {
151  base_unit_id_ = base_unit["id"].str();
152  LOG_UT << "type '" << id_ << "' has base unit '" << base_unit_id_ << "'\n";
153  }
154  check_id(id_);
156 }
157 unit_type::unit_type(const config& cfg, const std::string & parent_id)
158  : unit_type(defaut_ctor_t(), cfg, parent_id)
159 {
160  cfg_ = &cfg;
161 
162 }
163 
164 unit_type::unit_type(config&& cfg, const std::string & parent_id)
165  : unit_type(defaut_ctor_t(), cfg, parent_id)
166 {
167  built_cfg_ = std::make_unique<config>(std::move(cfg));
168 }
169 
170 
172 {
173 }
174 
176  : id(cfg["id"])
177  , name(cfg["name"].t_str())
178  , name_inactive(cfg["name_inactive"].t_str())
179  , female_name(cfg["female_name"].t_str())
180  , female_name_inactive(cfg["female_name_inactive"].t_str())
181  , description(cfg["description"].t_str())
182  , description_inactive(cfg["description_inactive"].t_str())
183  , affect_self(cfg["affect_self"].to_bool())
184  , affect_allies(cfg["affect_allies"].to_bool())
185  , affect_enemies(cfg["affect_enemies"].to_bool())
186  , cumulative(cfg["cumulative"].to_bool())
187 {
188 }
189 
190 /**
191  * Load data into an empty unit_type (build to FULL).
192  */
194  const movement_type_map& mv_types, const race_map& races, const config_array_view& traits)
195 {
196  // Don't build twice.
197  if(FULL <= build_status_) {
198  return;
199  }
200 
201  // Make sure we are built to the preceding build level.
202  build_help_index(mv_types, races, traits);
203 
204  for(int i = 0; i < 2; ++i) {
205  if(gender_types_[i]) {
206  gender_types_[i]->build_full(mv_types, races, traits);
207  }
208  }
209 
210  if(race_ != &unit_race::null_race) {
211  if(undead_variation_.empty()) {
213  }
214  }
215 
216  zoc_ = get_cfg()["zoc"].to_bool(level_ > 0);
217 
219 
220  hp_bar_scaling_ = get_cfg()["hp_bar_scaling"].to_double(game_config::hp_bar_scaling);
221  xp_bar_scaling_ = get_cfg()["xp_bar_scaling"].to_double(game_config::xp_bar_scaling);
222 
223  // Propagate the build to the variations.
224  for(variations_map::value_type& variation : variations_) {
225  variation.second.build_full(mv_types, races, traits);
226  }
227 
228  // Deprecation messages, only seen when unit is parsed for the first time.
229 
231 }
232 
233 /**
234  * Partially load data into an empty unit_type (build to HELP_INDEXED).
235  */
237  const movement_type_map& mv_types, const race_map& races, const config_array_view& traits)
238 {
239  // Don't build twice.
240  if(HELP_INDEXED <= build_status_) {
241  return;
242  }
243 
244  // Make sure we are built to the preceding build level.
245  build_created();
246 
247  const config& cfg = get_cfg();
248 
249  type_name_ = cfg["name"];
250  description_ = cfg["description"];
251  hitpoints_ = cfg["hitpoints"].to_int(1);
252  level_ = cfg["level"];
253  recall_cost_ = cfg["recall_cost"].to_int(-1);
254  movement_ = cfg["movement"].to_int(1);
255  vision_ = cfg["vision"].to_int(-1);
256  jamming_ = cfg["jamming"].to_int(0);
257  max_attacks_ = cfg["attacks"].to_int(1);
258  usage_ = cfg["usage"].str();
259  undead_variation_ = cfg["undead_variation"].str();
260  default_variation_ = cfg["variation"].str();
261  icon_ = cfg["image_icon"].str();
262  small_profile_ = cfg["small_profile"].str();
263  profile_ = cfg["profile"].str();
264  flag_rgb_ = cfg["flag_rgb"].str();
265  do_not_list_ = cfg["do_not_list"].to_bool(false);
266 
267  for(const config& sn : cfg.child_range("special_note")) {
268  special_notes_.push_back(sn["note"]);
269  }
270 
272 
273  alignment_ = unit_alignments::get_enum(cfg["alignment"].str()).value_or(unit_alignments::type::neutral);
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_alignments::type::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 
494 std::vector<t_string> unit_type::special_notes() const {
496 }
497 
498 static void append_special_note(std::vector<t_string>& notes, const t_string& new_note) {
499  if(new_note.empty()) return;
500  std::string_view note_plain = new_note.c_str();
501  utils::trim(note_plain);
502  if(note_plain.empty()) return;
503  auto iter = std::find(notes.begin(), notes.end(), new_note);
504  if(iter != notes.end()) return;
505  notes.push_back(new_note);
506 }
507 
508 std::vector<t_string> combine_special_notes(const std::vector<t_string> direct, const config& abilities, const_attack_itors attacks, const movetype& mt)
509 {
510  std::vector<t_string> notes;
511  for(const auto& note : direct) {
512  append_special_note(notes, note);
513  }
514  for(const config::any_child ability : abilities.all_children_range()) {
515  if(ability.cfg.has_attribute("special_note")) {
516  append_special_note(notes, ability.cfg["special_note"].t_str());
517  }
518  }
519  for(const auto& attack : attacks) {
520  for(const config::any_child ability : attack.specials().all_children_range()) {
521  if(ability.cfg.has_attribute("special_note")) {
522  append_special_note(notes, ability.cfg["special_note"].t_str());
523  }
524  }
525  if(auto attack_type_note = string_table.find("special_note_damage_type_" + attack.type()); attack_type_note != string_table.end()) {
526  append_special_note(notes, attack_type_note->second);
527  }
528  }
529  for(const auto& move_note : mt.special_notes()) {
530  append_special_note(notes, move_note);
531  }
532  return notes;
533 }
534 
535 const std::vector<unit_animation>& unit_type::animations() const
536 {
537  if(animations_.empty()) {
539  }
540 
541  return animations_;
542 }
543 
545 {
546  if(!attacks_cache_.empty()) {
548  }
549 
550  for(const config& att : get_cfg().child_range("attack")) {
551  attacks_cache_.emplace_back(new attack_type(att));
552  }
553 
555 }
556 
557 namespace
558 {
559 int experience_modifier = 100;
560 }
561 
563  : old_value_(experience_modifier)
564 {
565  experience_modifier = modifier;
566 }
567 
569 {
570  experience_modifier = old_value_;
571 }
572 
574 {
575  return experience_modifier;
576 }
577 
578 int unit_type::experience_needed(bool with_acceleration) const
579 {
580  if(with_acceleration) {
581  int exp = (experience_needed_ * experience_modifier + 50) / 100;
582  if(exp < 1) {
583  exp = 1;
584  }
585 
586  return exp;
587  }
588 
589  return experience_needed_;
590 }
591 
592 bool unit_type::has_ability_by_id(const std::string& ability) const
593 {
594  if(const config& abil = get_cfg().child("abilities")) {
595  for(const config::any_child ab : abil.all_children_range()) {
596  if(ab.cfg["id"] == ability) {
597  return true;
598  }
599  }
600  }
601 
602  return false;
603 }
604 
605 std::vector<std::string> unit_type::get_ability_list() const
606 {
607  std::vector<std::string> res;
608 
609  const config& abilities = get_cfg().child("abilities");
610  if(!abilities) {
611  return res;
612  }
613 
614  for(const config::any_child ab : abilities.all_children_range()) {
615  std::string id = ab.cfg["id"];
616 
617  if(!id.empty()) {
618  res.push_back(std::move(id));
619  }
620  }
621 
622  return res;
623 }
624 
626 {
627  return hide_help_ || unit_types.hide_help(id_, race_->id());
628 }
629 
630 
631 static void advancement_tree_internal(const std::string& id, std::set<std::string>& tree)
632 {
633  const unit_type* ut = unit_types.find(id);
634  if(!ut) {
635  return;
636  }
637 
638  for(const std::string& adv : ut->advances_to()) {
639  if(tree.insert(adv).second) {
640  // insertion succeed, expand the new type
641  advancement_tree_internal(adv, tree);
642  }
643  }
644 }
645 
646 std::set<std::string> unit_type::advancement_tree() const
647 {
648  std::set<std::string> tree;
649  advancement_tree_internal(id_, tree);
650  return tree;
651 }
652 
653 const std::vector<std::string> unit_type::advances_from() const
654 {
655  // Currently not needed (only help call us and already did it)/
657 
658  std::vector<std::string> adv_from;
659  for(const unit_type_data::unit_type_map::value_type& ut : unit_types.types()) {
660  for(const std::string& adv : ut.second.advances_to()) {
661  if(adv == id_) {
662  adv_from.push_back(ut.second.id());
663  }
664  }
665  }
666 
667  return adv_from;
668 }
669 
670 // This function is only meant to return the likely state a given status
671 // for a new recruit of this type. It should not be used to check if
672 // a particular unit has it, use get_state(status_name) for that.
673 bool unit_type::musthave_status(const std::string& status_name) const
674 {
675  // Statuses default to absent.
676  bool current_status = false;
677 
678  // Look at all of the "musthave" traits to see if the
679  // status gets changed. In the unlikely event it gets changed
680  // multiple times, we want to try to do it in the same order
681  // that unit::apply_modifications does things.
682  for(const config& mod : possible_traits()) {
683  if(mod["availability"] != "musthave") {
684  continue;
685  }
686 
687  for(const config& effect : mod.child_range("effect")) {
688  // See if the effect only applies to
689  // certain unit types But don't worry
690  // about gender checks, since we don't
691  // know what the gender of the
692  // hypothetical recruit is.
693  const std::string& ut = effect["unit_type"];
694 
695  if(!ut.empty()) {
696  const std::vector<std::string>& types = utils::split(ut);
697 
698  if(std::find(types.begin(), types.end(), id()) == types.end()) {
699  continue;
700  }
701  }
702 
703  // We're only interested in status changes.
704  if(effect["apply_to"] != "status") {
705  continue;
706  }
707 
708  if(effect["add"] == status_name) {
709  current_status = true;
710  }
711 
712  if(effect["remove"] == status_name) {
713  current_status = false;
714  }
715  }
716  }
717 
718  return current_status;
719 }
720 
721 const std::string& unit_type::flag_rgb() const
722 {
723  return flag_rgb_.empty() ? game_config::unit_rgb : flag_rgb_;
724 }
725 
727 {
728  if(num_traits() == 0) {
729  return false;
730  }
731 
732  for(const auto& cfg : possible_traits()) {
733  const config::attribute_value& availability = cfg["availability"];
734  if(availability.blank()) {
735  return true;
736  }
737 
738  if(availability.str() != "musthave") {
739  return true;
740  }
741  }
742 
743  return false;
744 }
745 
746 std::vector<std::string> unit_type::variations() const
747 {
748  std::vector<std::string> retval;
749  retval.reserve(variations_.size());
750 
751  for(const variations_map::value_type& val : variations_) {
752  retval.push_back(val.first);
753  }
754 
755  return retval;
756 }
757 
758 bool unit_type::has_variation(const std::string& variation_id) const
759 {
760  return variations_.find(variation_id) != variations_.end();
761 }
762 
764 {
765  for(const variations_map::value_type& val : variations_) {
766  if(!val.second.hide_help()) {
767  return true;
768  }
769  }
770 
771  return false;
772 }
773 
774 int unit_type::resistance_against(const std::string& damage_name, bool attacker) const
775 {
776  int resistance = movement_type_.resistance_against(damage_name);
777  unit_ability_list resistance_abilities;
778 
779  if(const config& abilities = get_cfg().child("abilities")) {
780  for(const config& cfg : abilities.child_range("resistance")) {
781  if(!cfg["affect_self"].to_bool(true)) {
782  continue;
783  }
784 
785  if(!resistance_filter_matches(cfg, attacker, damage_name, 100 - resistance)) {
786  continue;
787  }
788 
790  }
791  }
792 
793  if(!resistance_abilities.empty()) {
794  unit_abilities::effect resist_effect(resistance_abilities, 100 - resistance, false);
795 
796  resistance = 100 - std::min<int>(
797  resist_effect.get_composite_value(),
798  resistance_abilities.highest("max_value").first
799  );
800  }
801 
802  return resistance;
803 }
804 
806  const config& cfg, bool attacker, const std::string& damage_name, int res) const
807 {
808  if(!(cfg["active_on"].empty() ||
809  (attacker && cfg["active_on"] == "offense") ||
810  (!attacker && cfg["active_on"] == "defense"))
811  ) {
812  return false;
813  }
814 
815  const std::string& apply_to = cfg["apply_to"];
816 
817  if(!apply_to.empty()) {
818  if(damage_name != apply_to) {
819  if(apply_to.find(',') != std::string::npos && apply_to.find(damage_name) != std::string::npos) {
820  const std::vector<std::string>& vals = utils::split(apply_to);
821 
822  if(std::find(vals.begin(), vals.end(), damage_name) == vals.end()) {
823  return false;
824  }
825  } else {
826  return false;
827  }
828  }
829  }
830 
831  if(!unit_abilities::filter_base_matches(cfg, res)) {
832  return false;
833  }
834 
835  return true;
836 }
837 
838 /** Implementation detail of unit_type::alignment_description */
839 
841 {
842  static const unit_alignments::sized_array<t_string> male_names {_("lawful"), _("neutral"), _("chaotic"), _("liminal")};
843  static const unit_alignments::sized_array<t_string> female_names {_("female^lawful"), _("female^neutral"), _("female^chaotic"), _("female^liminal")};
844 
845  if(gender == unit_race::FEMALE) {
846  return female_names[static_cast<int>(align)];
847  } else {
848  return male_names[static_cast<int>(align)];
849  }
850 }
851 
852 /* ** unit_type_data ** */
853 
855  : types_()
856  , movement_types_()
857  , races_()
858  , hide_help_all_(false)
859  , hide_help_type_()
860  , hide_help_race_()
861  , units_cfg_()
862  , build_status_(unit_type::NOT_BUILT)
863 {
864 }
865 
866 
867 // Helpers for set_config()
868 
869 namespace
870 {
871 /**
872  * Spits out an error message and throws a config::error.
873  * Called when apply_base_unit() detects a cycle.
874  * (This exists merely to take the error message out of that function.)
875  */
876 void throw_base_unit_recursion_error(const std::vector<std::string>& base_tree, const std::string& base_id)
877 {
878  std::stringstream ss;
879  ss << "[base_unit] recursion loop in [unit_type] ";
880 
881  for(const std::string& step : base_tree) {
882  ss << step << "->";
883  }
884 
885  ss << base_id;
886  ERR_CF << ss.str() << '\n';
887 
888  throw config::error(ss.str());
889 }
890 
891 /**
892  * Insert a new value into a movetype, possibly calculating the value based on
893  * the existing values in the target movetype.
894  */
895 void patch_movetype(movetype& mt,
896  const std::string& type_to_patch,
897  const std::string& new_key,
898  const std::string& formula_str,
899  int default_val,
900  bool replace)
901 {
902  LOG_CONFIG << "Patching " << new_key << " into movetype." << type_to_patch << std::endl;
903  config mt_cfg;
904  mt.write(mt_cfg);
905 
906  if(!replace && mt_cfg.child_or_empty(type_to_patch).has_attribute(new_key)) {
907  // Don't replace if this type already exists in the config
908  return;
909  }
910 
911  // Make movement_costs.flat, defense.castle, etc available to the formula.
912  // The formula uses config_callables which take references to data in mt;
913  // the enclosing scope is to run all the destructors before calling mt's
914  // non-const merge() function. Probably unnecessary, but I'd rather write
915  // it this way than debug it afterwards.
916  config temp_cfg;
917  {
918  // Instances of wfl::config_callable take a reference to a config,
919  // which means that the "cumulative_values" variable below needs to be
920  // copied so that movement costs aren't overwritten by vision costs
921  // before the formula is evaluated.
922  std::list<config> config_copies;
923 
924  gui2::typed_formula<int> formula(formula_str, default_val);
925  wfl::map_formula_callable original;
926 
927  // These three need to follow movetype's fallback system, where values for
928  // movement costs are used for vision too.
929  const std::array fallback_children {"movement_costs", "vision_costs", "jamming_costs"};
930  config cumulative_values;
931  for(const auto& x : fallback_children) {
932  if(mt_cfg.has_child(x)) {
933  cumulative_values.merge_with(mt_cfg.child(x));
934  }
935  config_copies.emplace_back(cumulative_values);
936  auto val = std::make_shared<wfl::config_callable>(config_copies.back());
937  original.add(x, val);
938 
939  // Allow "flat" to work as "vision_costs.flat" when patching vision_costs, etc
940  if(type_to_patch == x) {
941  original.set_fallback(val);
942  }
943  }
944 
945  // These don't need the fallback system
946  const std::array child_names {"defense", "resistance"};
947  for(const auto& x : child_names) {
948  if(mt_cfg.has_child(x)) {
949  const auto& subtag = mt_cfg.child(x);
950  auto val = std::make_shared<wfl::config_callable>(subtag);
951  original.add(x, val);
952 
953  // Allow "arcane" to work as well as "resistance.arcane", etc
954  if(type_to_patch == x) {
955  original.set_fallback(val);
956  }
957  }
958  }
959 
960  LOG_CONFIG << " formula=" << formula_str << ", resolves to " << formula(original) << std::endl;
961  temp_cfg[new_key] = formula(original);
962  }
963  mt.merge(temp_cfg, type_to_patch, true);
964 }
965 } // unnamed namespace
966 
967 /**
968  * Modifies the provided config by merging all base units into it.
969  * The @a base_tree parameter is used for detecting and reporting
970  * cycles of base units and in particular to prevent infinite loops.
971  */
972 
973 void unit_type_data::apply_base_unit(unit_type& type, std::vector<std::string>& base_tree)
974 {
975  // Nothing to do.
976  if(type.base_unit_id_.empty()) {
977  return;
978  }
979 
980  // Detect recursion so the WML author is made aware of an error.
981  if(std::find(base_tree.begin(), base_tree.end(), type.base_unit_id_) != base_tree.end()) {
982  throw_base_unit_recursion_error(base_tree, type.base_unit_id_);
983  }
984 
985  // Find the base unit.
986  const unit_type_map::iterator itor = types_.find(type.base_unit_id_);
987  if(itor != types_.end()) {
988 
989  unit_type& base_type = itor->second;
990 
991  // Make sure the base unit has had its base units accounted for.
992  base_tree.push_back(type.base_unit_id_);
993 
994  apply_base_unit(base_type, base_tree);
995 
996  base_tree.pop_back();
997 
998  // Merge the base unit "under" our config.
999  type.writable_cfg().inherit_from(base_type.get_cfg());
1000  }
1001  else {
1002  ERR_CF << "[base_unit]: unit type not found: " << type.base_unit_id_ << std::endl;
1003  throw config::error("unit type not found: " + type.base_unit_id_);
1004  }
1005 }
1006 
1007 /**
1008  * Handles inheritance for configs of [male], [female], and [variation].
1009  * Also removes gendered children, as those serve no purpose.
1010  * @a default_inherit is the default value for inherit=.
1011  */
1012 std::unique_ptr<unit_type> unit_type::create_sub_type(const config& var_cfg, bool default_inherit)
1013 {
1014  config var_copy = var_cfg;
1015  if(var_cfg["inherit"].to_bool(default_inherit)) {
1016  var_copy.inherit_from(get_cfg());
1017  }
1018 
1019  var_copy.clear_children("male");
1020  var_copy.clear_children("female");
1021 
1022  return std::make_unique<unit_type>(std::move(var_copy), parent_id());
1023 }
1024 
1025 /**
1026  * Processes [variation] tags of @a ut_cfg, handling inheritance and
1027  * child clearing.
1028  */
1030 {
1031  // Most unit types do not have variations.
1032  if(!get_cfg().has_child("variation")) {
1033  return;
1034  }
1035 
1036  // Handle each variation's inheritance.
1037  for(const config& var_cfg : get_cfg().child_range("variation")) {
1038 
1039  std::unique_ptr<unit_type> var = create_sub_type(var_cfg, false);
1040 
1041  var->built_cfg_->remove_children("variation", [](const config&){return true;});
1042  var->variation_id_ = var_cfg["variation_id"].str();
1043  var->debug_id_ = debug_id_ + " [" + var->variation_id_ + "]";
1044 
1046  bool success;
1047  std::tie(ut, success) = variations_.emplace(var_cfg["variation_id"].str(), std::move(*var));
1048  if(!success) {
1049  ERR_CF << "Skipping duplicate unit variation ID: '" << var_cfg["variation_id"]
1050  << "' of unit_type '" << get_cfg()["id"] << "'\n";
1051  }
1052  }
1053 
1054 
1055 }
1056 
1057 
1059 {
1060  // Complete the gender-specific children of the config.
1061  if(const config& male_cfg = get_cfg().child("male")) {
1062  gender_types_[0] = create_sub_type(male_cfg, true);
1063  gender_types_[0]->fill_variations();
1064  }
1065 
1066  if(const config& female_cfg = get_cfg().child("female")) {
1067  gender_types_[1] = create_sub_type(female_cfg, true);
1068  gender_types_[1]->fill_variations();
1069  }
1070 
1071  // Complete the variation-defining children of the config.
1072  fill_variations();
1073 
1075 }
1076 /**
1077  * Resets all data based on the provided config.
1078  * This includes some processing of the config, such as expanding base units.
1079  * A pointer to the config is stored, so the config must be persistent.
1080  */
1082 {
1083  LOG_UT << "unit_type_data::set_config, nunits: " << cfg.child_range("unit_type").size() << "\n";
1084 
1085  clear();
1086  units_cfg_ = cfg;
1087 
1088  for(const config& mt : cfg.child_range("movetype")) {
1089  movement_types_.emplace(mt["name"].str(), movetype(mt));
1090 
1092  }
1093 
1094  for(const config& r : cfg.child_range("race")) {
1095  const unit_race race(r);
1096  races_.emplace(race.id(), race);
1097 
1099  }
1100 
1101  // Movetype resistance patching
1102  DBG_CF << "Start of movetype patching" << std::endl;
1103  for(const config& r : cfg.child_range("resistance_defaults")) {
1104  const std::string& dmg_type = r["id"];
1105 
1106  for(const config::attribute& attr : r.attribute_range()) {
1107  const std::string& mt = attr.first;
1108 
1109  if(mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1110  continue;
1111  }
1112 
1113  DBG_CF << "Patching specific movetype " << mt << std::endl;
1114  patch_movetype(movement_types_[mt], "resistance", dmg_type, attr.second, 100, true);
1115  }
1116 
1117  if(r.has_attribute("default")) {
1118  for(movement_type_map::value_type& mt : movement_types_) {
1119  // Don't apply a default if a value is explicitly specified.
1120  if(r.has_attribute(mt.first)) {
1121  continue;
1122  }
1123  // The "none" movetype expects everything to have the default value (to be UNREACHABLE)
1124  if(mt.first == "none") {
1125  continue;
1126  }
1127 
1128  patch_movetype(mt.second, "resistance", dmg_type, r["default"], 100, false);
1129  }
1130  }
1131  }
1132  DBG_CF << "Split between resistance and cost patching" << std::endl;
1133 
1134  // Movetype move/defend patching
1135  for(const config& terrain : cfg.child_range("terrain_defaults")) {
1136  const std::string& ter_type = terrain["id"];
1137 
1138  struct ter_defs_to_movetype
1139  {
1140  /** The data to read from is in [terrain_defaults][subtag], and corresponds to [movetype][subtag] */
1141  std::string subtag;
1142  /** Deprecated names used in 1.14.0's [terrain_defaults]. For [defense] the name didn't change. */
1143  std::string alias;
1144  int default_val;
1145  };
1146  const std::array terrain_info_tags{
1147  ter_defs_to_movetype{{"movement_costs"}, {"movement"}, movetype::UNREACHABLE},
1148  ter_defs_to_movetype{{"vision_costs"}, {"vision"}, movetype::UNREACHABLE},
1149  ter_defs_to_movetype{{"jamming_costs"}, {"jamming"}, movetype::UNREACHABLE},
1150  ter_defs_to_movetype{{"defense"}, {"defense"}, 100}
1151  };
1152 
1153  for(const auto& cost_type : terrain_info_tags) {
1154  const std::string* src_tag = nullptr;
1155  if(terrain.has_child(cost_type.subtag)) {
1156  src_tag = &cost_type.subtag;
1157  }
1158  else if(terrain.has_child(cost_type.alias)) {
1159  // Check for the deprecated name, no deprecation warnings are printed.
1160  src_tag = &cost_type.alias;
1161  }
1162  if(!src_tag) {
1163  continue;
1164  }
1165 
1166  const config& info = terrain.child(*src_tag);
1167 
1168  for(const config::attribute& attr : info.attribute_range()) {
1169  const std::string& mt = attr.first;
1170 
1171  if(mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1172  continue;
1173  }
1174 
1175  patch_movetype(
1176  movement_types_[mt], cost_type.subtag, ter_type, attr.second, cost_type.default_val, true);
1177  }
1178 
1179  if(info.has_attribute("default")) {
1180  for(movement_type_map::value_type& mt : movement_types_) {
1181  // Don't apply a default if a value is explicitly specified.
1182  if(info.has_attribute(mt.first)) {
1183  continue;
1184  }
1185  // The "none" movetype expects everything to have the default value
1186  if(mt.first == "none") {
1187  continue;
1188  }
1189 
1190  patch_movetype(
1191  mt.second, cost_type.subtag, ter_type, info["default"], cost_type.default_val, false);
1192  }
1193  }
1194  }
1195  }
1196  DBG_CF << "End of movetype patching" << std::endl;
1197 
1198  for(const config& ut : cfg.child_range("unit_type")) {
1199  // Every type is required to have an id.
1200  std::string id = ut["id"].str();
1201  if(id.empty()) {
1202  ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
1203  continue;
1204  }
1205 
1206  if(types_.emplace(id, unit_type(ut)).second) {
1207  LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
1208  } else {
1209  ERR_CF << "Multiple [unit_type]s with id=" << id << " encountered." << std::endl;
1210  }
1211  }
1212 
1213  // Apply base units.
1214  for(auto& type : types_) {
1215  std::vector<std::string> base_tree(1, type.second.id());
1216  apply_base_unit(type.second, base_tree);
1217 
1219  }
1220 
1221  //handle [male], [female], [variation]
1222  for(auto& type : types_) {
1223  type.second.fill_variations_and_gender();
1224 
1226  }
1227 
1228  // Build all unit types. (This was not done within the loop for performance.)
1230 
1231  // Suppress some unit types (presumably used as base units) from the help.
1232  if(const config& hide_help = cfg.child("hide_help")) {
1233  hide_help_all_ = hide_help["all"].to_bool();
1235  }
1236  DBG_UT << "Finished creatign unti types\n";
1237 }
1238 
1240 {
1241  ut.build(status, movement_types_, races_, units_cfg().child_range("trait"));
1242 }
1243 
1244 /**
1245  * Finds a unit_type by its id() and makes sure it is built to the specified level.
1246  */
1247 const unit_type* unit_type_data::find(const std::string& key, unit_type::BUILD_STATUS status) const
1248 {
1249  if(key.empty() || key == "random") {
1250  return nullptr;
1251  }
1252 
1253  DBG_CF << "trying to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1254  const unit_type_map::iterator itor = types_.find(key);
1255 
1256  // This might happen if units of another era are requested (for example for savegames)
1257  if(itor == types_.end()) {
1258  DBG_CF << "unable to find " << key << " in unit_type list (unit_type_data.unit_types)\n";
1259  return nullptr;
1260  }
1261 
1262  // Make sure the unit_type is built to the requested level.
1263  build_unit_type(itor->second, status);
1264 
1265  return &itor->second;
1266 }
1267 
1268 void unit_type_data::check_types(const std::vector<std::string>& types) const
1269 {
1270  for(const std::string& type : types) {
1271  if(!find(type)) {
1272  throw game::game_error("unknown unit type: " + type);
1273  }
1274  }
1275 }
1276 
1278 {
1279  types_.clear();
1280  movement_types_.clear();
1281  races_.clear();
1283 
1284  hide_help_all_ = false;
1285  hide_help_race_.clear();
1286  hide_help_type_.clear();
1287 }
1288 
1290 {
1291  // Nothing to do if already built to the requested level.
1292  if(status <= build_status_) {
1293  return;
1294  }
1295 
1296  for(const auto& type : types_) {
1297  build_unit_type(type.second, status);
1298 
1300  }
1301 
1302  build_status_ = status;
1303 }
1304 
1306 {
1307  if(!cfg) {
1308  return;
1309  }
1310 
1311  hide_help_race_.emplace_back();
1312  hide_help_type_.emplace_back();
1313 
1314  std::vector<std::string> races = utils::split(cfg["race"]);
1315  hide_help_race_.back().insert(races.begin(), races.end());
1316 
1317  std::vector<std::string> types = utils::split(cfg["type"]);
1318  hide_help_type_.back().insert(types.begin(), types.end());
1319 
1320  std::vector<std::string> trees = utils::split(cfg["type_adv_tree"]);
1321  hide_help_type_.back().insert(trees.begin(), trees.end());
1322 
1323  for(const std::string& t_id : trees) {
1324  unit_type_map::iterator ut = types_.find(t_id);
1325 
1326  if(ut != types_.end()) {
1327  std::set<std::string> adv_tree = ut->second.advancement_tree();
1328  hide_help_type_.back().insert(adv_tree.begin(), adv_tree.end());
1329  }
1330  }
1331 
1332  // We recursively call all the imbricated [not] tags
1333  read_hide_help(cfg.child("not"));
1334 }
1335 
1336 bool unit_type_data::hide_help(const std::string& type, const std::string& race) const
1337 {
1338  bool res = hide_help_all_;
1339  int lvl = hide_help_all_ ? 1 : 0; // first level is covered by 'all=yes'
1340 
1341  // supposed to be equal but let's be cautious
1342  int lvl_nb = std::min(hide_help_race_.size(), hide_help_type_.size());
1343 
1344  for(; lvl < lvl_nb; ++lvl) {
1345  if(hide_help_race_[lvl].count(race) || hide_help_type_[lvl].count(type)) {
1346  res = !res; // each level is a [not]
1347  }
1348  }
1349 
1350  return res;
1351 }
1352 
1353 const unit_race* unit_type_data::find_race(const std::string& key) const
1354 {
1355  race_map::const_iterator i = races_.find(key);
1356  return i != races_.end() ? &i->second : nullptr;
1357 }
1358 
1360 {
1361  build_created();
1362  if(auto p_setxp = cfg.get("set_experience")) {
1363  experience_needed_ = p_setxp->to_int();
1364  }
1365  if(auto attr = cfg.get("set_advances_to")) {
1366  advances_to_ = utils::split(attr->str());
1367  }
1368  if(auto attr = cfg.get("set_cost")) {
1369  cost_ = attr->to_int(1);
1370  }
1371  if(auto attr = cfg.get("add_advancement")) {
1372  for(const auto& str : utils::split(attr->str())) {
1373  advances_to_.push_back(str);
1374  }
1375  }
1376  if(auto attr = cfg.get("remove_advancement")) {
1377  for(const auto& str : utils::split(attr->str())) {
1378  boost::remove_erase(advances_to_, str);
1379  }
1380  }
1381 
1382  // apply recursively to subtypes.
1383  for(int gender = 0; gender <= 1; ++gender) {
1384  if(!gender_types_[gender]) {
1385  continue;
1386  }
1387  gender_types_[gender]->apply_scenario_fix(cfg);
1388  }
1389 
1390  if(get_cfg().has_child("variation")) {
1391  // Make sure the variations are created.
1392  unit_types.build_unit_type(*this, VARIATIONS);
1393  for(auto& v : variations_) {
1394  v.second.apply_scenario_fix(cfg);
1395  }
1396  }
1397 }
1398 
1400 {
1401  unit_type_map::iterator itor = types_.find(cfg["type"].str());
1402  // This might happen if units of another era are requested (for example for savegames)
1403  if(itor != types_.end()) {
1404  itor->second.apply_scenario_fix(cfg);
1405  }
1406  else {
1407  // should we give an error message?
1408  }
1409 }
1410 
1412 {
1413  advances_to_.clear();
1414  const std::string& advances_to_val = get_cfg()["advances_to"];
1415  if(advances_to_val != "null" && !advances_to_val.empty()) {
1416  advances_to_ = utils::split(advances_to_val);
1417  }
1418  experience_needed_ = get_cfg()["experience"].to_int(500);
1419  cost_ = get_cfg()["cost"].to_int(1);
1420 
1421  // apply recursively to subtypes.
1422  for(int gender = 0; gender <= 1; ++gender) {
1423  if(!gender_types_[gender]) {
1424  continue;
1425  }
1426  gender_types_[gender]->remove_scenario_fixes();
1427  }
1428  for(auto& v : variations_) {
1429  v.second.remove_scenario_fixes();
1430  }
1431 }
1432 
1434 {
1435  for(auto& pair : types_) {
1436  pair.second.remove_scenario_fixes();
1437  }
1438 }
1439 
1440 void unit_type::check_id(std::string& id)
1441 {
1442  assert(!id.empty());
1443 
1444  // We don't allow leading whitepaces.
1445  if(id[0] == ' ') {
1446  throw error("Found unit type id with a leading whitespace \"" + id + "\"");
1447  }
1448 
1449  bool gave_warning = false;
1450 
1451  for(std::size_t pos = 0; pos < id.size(); ++pos) {
1452  const char c = id[pos];
1453  const bool valid = std::isalnum(c, std::locale::classic()) || c == '_' || c == ' ';
1454 
1455  if(!valid) {
1456  if(!gave_warning) {
1457  ERR_UT << "Found unit type id with invalid characters: \"" << id << "\"\n";
1458  gave_warning = true;
1459  }
1460 
1461  id[pos] = '_';
1462  }
1463  }
1464 }
1465 
1467 
1468 void adjust_profile(std::string& profile)
1469 {
1470  // Create a temp copy
1471  std::string temp = profile;
1472 
1473  static const std::string path_adjust = "/transparent";
1474  const std::string::size_type offset = profile.find_last_of('/', profile.find('~'));
1475 
1476  // If the path already refers to /transparent...
1477  if(profile.find(path_adjust) != std::string::npos && offset != std::string::npos) {
1478  if(!image::locator(profile).file_exists()) {
1479  profile.replace(profile.find(path_adjust), path_adjust.length(), "");
1480  }
1481 
1482  return;
1483  }
1484 
1485  // else, check for the file with /transparent appended...
1486  offset != std::string::npos ? temp.insert(offset, path_adjust) : temp = path_adjust + temp;
1487 
1488  // and use that path if it exists.
1489  if(image::locator(temp).file_exists()) {
1490  profile = temp;
1491  }
1492 }
int jamming_
Definition: types.hpp:340
std::string parent_id_
The id of the top ancestor of this unit_type.
Definition: types.hpp:328
int experience_needed_
Definition: types.hpp:369
const std::string & parent_id() const
The id of the original type from which this (variation) descended.
Definition: types.hpp:148
bool has_cfg_build_
Definition: types.hpp:321
#define DBG_CF
Definition: types.cpp:46
static void advancement_tree_internal(const std::string &id, std::set< std::string > &tree)
Definition: types.cpp:631
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:402
bool do_not_list_
Definition: types.hpp:366
unit_type::BUILD_STATUS build_status_
Definition: types.hpp:436
static const std::string s_male
Standard string id (not translatable) for MALE.
Definition: race.hpp:29
double hp_bar_scaling
Definition: game_config.cpp:79
void read_hide_help(const config &cfg)
Parses the [hide_help] tag.
Definition: types.cpp:1305
#define DBG_UT
Definition: types.cpp:49
std::string default_variation_
Definition: types.hpp:357
static int get_acceleration()
Definition: types.cpp:573
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:978
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:1247
double xp_bar_scaling
Definition: game_config.cpp:80
std::string flag_rgb_
Definition: types.hpp:350
void clear_children(T... keys)
Definition: config.hpp:556
t_string variation_name_
Definition: types.hpp:359
std::string profile_
Definition: types.hpp:349
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
std::vector< t_string > combine_special_notes(const std::vector< t_string > direct, const config &abilities, const_attack_itors attacks, const movetype &mt)
Common logic for unit_type::special_notes() and unit::special_notes().
Definition: types.cpp:508
void emplace_back(T &&... args)
Definition: unit.hpp:101
const std::string & id() const
Definition: race.hpp:35
Variant for storing WML attributes.
std::string debug_id_
A suffix for id_, used when logging messages.
Definition: types.hpp:326
int hitpoints_
Definition: types.hpp:334
void check_types(const std::vector< std::string > &types) const
Definition: types.cpp:1268
const std::vector< std::string > & advances_to() const
A vector of unit_type ids that this unit_type can advance to.
Definition: types.hpp:118
void set_fallback(const_formula_callable_ptr fallback)
Definition: callable.hpp:265
int recall_cost_
Definition: types.hpp:337
static game_config_view wrap(const config &cfg)
bool has_attribute(config_key_type key) const
Definition: config.cpp:211
logger & info()
Definition: log.cpp:89
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:176
config_array_view child_range(config_key_type key) const
std::string variation_id_
Definition: types.hpp:358
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:394
const std::string & flag_rgb() const
Definition: types.cpp:721
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:263
utils::string_map::const_iterator find(const std::string &key) const
Look up the string mappings given in [language] tags.
Definition: language.cpp:94
~unit_type()
Definition: types.cpp:171
child_itors child_range(config_key_type key)
Definition: config.cpp:344
static void progress(loading_stage stage=loading_stage::none)
attribute_map::value_type attribute
Definition: config.hpp:221
const config * cfg_
Definition: types.hpp:318
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:780
std::string unit_rgb
t_string description_
Definition: types.hpp:332
void fill_variations()
Processes [variation] tags of ut_cfg, handling inheritance and child clearing.
Definition: types.cpp:1029
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:578
void build_all(unit_type::BUILD_STATUS status)
Makes sure the all unit_types are built to the specified level.
Definition: types.cpp:1289
config possible_traits_
Definition: types.hpp:376
unit_type_data unit_types
Definition: types.cpp:1466
const char * c_str() const
Definition: tstring.hpp:192
std::array< std::unique_ptr< unit_type >, 2 > gender_types_
Definition: types.hpp:354
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:236
int resistance_against(const std::string &damage_name, bool attacker) const
Gets resistance while considering custom WML abilities.
Definition: types.cpp:774
const config & get_cfg() const
Definition: types.hpp:283
BUILD_STATUS
Records the status of the lazy building of unit types.
Definition: types.hpp:77
std::set< std::string > advancement_tree() const
Get the advancement tree.
Definition: types.cpp:646
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1399
void clear()
Definition: config.cpp:920
t_string type_name_
Definition: types.hpp:331
variations_map variations_
Definition: types.hpp:356
const std::vector< t_string > & special_notes() const
Contents of any [special_note] tags.
Definition: movetype.hpp:347
const race_map & races() const
Definition: types.hpp:397
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:875
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:44
void write(config &cfg) const
Writes the movement type data to the provided config.
Definition: movetype.cpp:922
static std::string _(const char *str)
Definition: gettext.hpp:93
const unit_type_map & types() const
Definition: types.hpp:396
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:653
const_attr_itors attribute_range() const
Definition: config.cpp:858
int level_
Definition: types.hpp:336
A single unit type that the player may recruit.
Definition: types.hpp:45
const unit_race * race_
Never nullptr, but may point to the null race.
Definition: types.hpp:362
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:1664
config & writable_cfg()
Definition: types.hpp:306
static const std::string s_female
Standard string id (not translatable) for FEMALE.
Definition: race.hpp:28
#define ERR_UT
Definition: types.cpp:51
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:193
void merge_with(const config &c)
Merge config &#39;c&#39; into this config, overwriting this config&#39;s values.
Definition: config.cpp:1224
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:1239
std::string icon_
Definition: types.hpp:347
void inherit_from(const config &c)
Merge config &#39;c&#39; into this config, preserving this config&#39;s values.
Definition: config.cpp:1277
bool musthave_status(const std::string &status) const
Definition: types.cpp:673
unit_alignments::type alignment_
Definition: types.hpp:372
void fill_variations_and_gender()
Definition: types.cpp:1058
movement_type_map movement_types_
Definition: types.hpp:425
bool zoc_
Definition: types.hpp:366
static const unit_race null_race
Dummy race used when a race is not yet known.
Definition: race.hpp:69
bool hide_help() const
Definition: types.cpp:625
const movetype & movement_type() const
Definition: types.hpp:191
The base template for associating string values with enum values.
Definition: enum_base.hpp:30
const std::vector< unit_animation > & animations() const
Definition: types.cpp:535
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
std::vector< std::set< std::string > > hide_help_type_
Definition: types.hpp:431
std::vector< std::set< std::string > > hide_help_race_
Definition: types.hpp:432
std::vector< unit_animation > animations_
Definition: types.hpp:381
bool blank() const
Tests for an attribute that was never set.
std::string small_profile_
Definition: types.hpp:348
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:152
std::unique_ptr< config > built_cfg_
Definition: types.hpp:320
std::string undead_variation_
Definition: types.hpp:344
void remove_scenario_fixes()
Definition: types.cpp:1411
int movement_
Definition: types.hpp:338
movetype movement_type_
Definition: types.hpp:374
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:144
const config::const_child_itors & additional_traits() const
Definition: race.cpp:129
std::map< std::string, movetype > movement_type_map
Definition: types.hpp:36
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:47
static lg::log_domain log_unit("unit")
double xp_bar_scaling_
Definition: types.hpp:335
unsigned int num_traits() const
Definition: race.cpp:139
Generic locator abstracting the location of an image.
Definition: picture.hpp:60
int cost_
Definition: types.hpp:342
const std::string log_id() const
A variant on id() that is more descriptive, for use with message logging.
Definition: types.hpp:146
const game_config_view & units_cfg() const
Definition: types.hpp:434
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
const unit_race * find_race(const std::string &) const
Definition: types.cpp:1353
std::vector< t_string > special_notes_
Definition: types.hpp:333
utils::string_map::const_iterator end() const
Definition: language.cpp:99
unsigned int num_traits_
Definition: types.hpp:352
std::size_t i
Definition: function.cpp:967
game_config_view units_cfg_
Definition: types.hpp:435
std::vector< std::string > variations() const
Definition: types.cpp:746
unit_type_map types_
Definition: types.hpp:424
std::vector< std::string > get_ability_list() const
Definition: types.cpp:605
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1359
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:763
const config & abilities_cfg() const
Definition: types.hpp:236
double g
Definition: astarsearch.cpp:65
std::vector< std::string > advances_to_
Definition: types.hpp:368
std::string usage_
Definition: types.hpp:343
#define ERR_CF
Definition: types.cpp:43
bool uses_global_traits() const
Definition: race.cpp:124
bool has_ability_by_id(const std::string &ability) const
Definition: types.cpp:592
int get_composite_value() const
Definition: abilities.hpp:47
double hp_bar_scaling_
Definition: types.hpp:335
bool hide_help_all_
True if [hide_help] contains a &#39;all=yes&#39; at its root.
Definition: types.hpp:429
std::string base_unit_id_
from [base_unit]
Definition: types.hpp:330
static void fill_initial_animations(std::vector< unit_animation > &animations, const config &cfg)
Definition: animation.cpp:485
std::vector< t_string > special_notes() const
Returns all notes that should be displayed in the help page for this type, including those found in a...
Definition: types.cpp:494
std::string id_
Definition: types.hpp:324
config & add_child(config_key_type key)
Definition: config.cpp:514
unit_experience_accelerator(int modifier)
Definition: types.cpp:562
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:1336
#define LOG_UT
Definition: types.cpp:50
static std::string alignment_description(unit_alignments::type align, unit_race::GENDER gender=unit_race::MALE)
Implementation detail of unit_type::alignment_description.
Definition: types.cpp:840
std::vector< ability_metadata > abilities_
Definition: types.hpp:364
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:805
int vision_
Definition: types.hpp:339
bool empty() const
Definition: tstring.hpp:187
BUILD_STATUS build_status_
Definition: types.hpp:383
void set_config(const game_config_view &cfg)
Resets all data based on the provided config.
Definition: types.cpp:1081
static std::optional< typename T::type > get_enum(const std::string value)
Convert a string into its enum equivalent.
Definition: enum_base.hpp:52
void clear()
Definition: types.cpp:1277
double t
Definition: astarsearch.cpp:65
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:341
#define LOG_CONFIG
Definition: types.cpp:45
symbol_table string_table
Definition: language.cpp:65
static void append_special_note(std::vector< t_string > &notes, const t_string &new_note)
Definition: types.cpp:498
bool hide_help_
Definition: types.hpp:366
std::vector< ability_metadata > adv_abilities_
Definition: types.hpp:364
void adjust_profile(std::string &profile)
Definition: types.cpp:1468
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:973
void remove_scenario_fixes()
Definition: types.cpp:1433
static const map_location & null_location()
Definition: location.hpp:81
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:1440
void trim(std::string_view &s)
retval
Default window/dialog return values.
Definition: retval.hpp:29
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:1012
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:465
const_attack_itors attacks() const
Definition: types.cpp:544
std::map< std::string, unit_race > race_map
Definition: race.hpp:95
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:175
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
attack_list attacks_cache_
Definition: types.hpp:322
mock_char c
bool has_random_traits() const
Definition: types.cpp:726
const std::string & undead_variation() const
Definition: race.hpp:49
bool has_variation(const std::string &variation_id) const
Definition: types.cpp:758
bool file_exists() const
Tests whether the file the locator points at exists.
Definition: picture.cpp:673
std::string image_
Definition: types.hpp:346
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
race_map races_
Definition: types.hpp:426
const config & child(config_key_type key) const
std::vector< unit_race::GENDER > genders_
Definition: types.hpp:378
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