The Battle for Wesnoth  1.17.21+dev
types.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
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 "language.hpp" // for string_table
27 #include "log.hpp"
28 #include "units/abilities.hpp"
29 #include "units/animation.hpp"
30 #include "units/unit.hpp"
31 #include "utils/iterable_pair.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_()
123  , undead_variation_()
124  , image_()
125  , icon_()
126  , small_profile_()
127  , profile_()
128  , flag_rgb_()
129  , num_traits_(0)
130  , gender_types_()
131  , variations_()
132  , default_variation_()
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_alignments::type::neutral)
143  , movement_type_()
144  , possible_traits_()
145  , genders_()
146  , animations_()
147  , build_status_(NOT_BUILT)
148 {
149  if(auto base_unit = cfg.optional_child("base_unit")) {
150  base_unit_id_ = base_unit["id"].str();
151  LOG_UT << "type '" << id_ << "' has base unit '" << base_unit_id_ << "'";
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_alignments::get_enum(cfg["alignment"].str()).value_or(unit_alignments::type::neutral);
273 
274  for(int i = 0; i < 2; ++i) {
275  if(gender_types_[i]) {
276  gender_types_[i]->build_help_index(mv_types, races, traits);
277  }
278  }
279 
280  for(auto& pair : variations_) {
281  pair.second.build_help_index(mv_types, races, traits);
282  }
283 
284  const race_map::const_iterator race_it = races.find(cfg["race"]);
285  if(race_it != races.end()) {
286  race_ = &race_it->second;
287  } else {
289  }
290 
291  // if num_traits is not defined, we use the num_traits from race
292  num_traits_ = cfg["num_traits"].to_int(race_->num_traits());
293 
294  for(const std::string& g : utils::split(cfg["gender"])) {
295  genders_.push_back(string_gender(g));
296  }
297 
298  // For simplicity in other parts of the code, we must have at least one gender.
299  if(genders_.empty()) {
300  genders_.push_back(unit_race::MALE);
301  }
302 
303  if(auto abil_cfg = cfg.optional_child("abilities")) {
304  for(const config::any_child ab : abil_cfg->all_children_range()) {
305  abilities_.emplace_back(ab.cfg);
306  }
307  }
308 
309  for(const config& adv : cfg.child_range("advancement")) {
310  for(const config& effect : adv.child_range("effect")) {
311  auto abil_cfg = effect.optional_child("abilities");
312 
313  if(!abil_cfg || effect["apply_to"] != "new_ability") {
314  continue;
315  }
316 
317  for(const config::any_child ab : abil_cfg->all_children_range()) {
318  adv_abilities_.emplace_back(ab.cfg);
319  }
320  }
321  }
322 
323  // Set the movement type.
324  const std::string move_type = cfg["movement_type"];
325  const movement_type_map::const_iterator find_it = mv_types.find(move_type);
326 
327  if(find_it != mv_types.end()) {
328  DBG_UT << "inheriting from movement_type '" << move_type << "'";
329  movement_type_ = find_it->second;
330  } else if(!move_type.empty()) {
331  DBG_UT << "movement_type '" << move_type << "' not found";
332  }
333 
334  // Override parts of the movement type with what is in our config.
335  movement_type_.merge(cfg);
336 
337  for(const config& t : traits) {
338  possible_traits_.add_child("trait", t);
339  }
340 
341  if(race_ != &unit_race::null_race) {
342  if(!race_->uses_global_traits()) {
344  }
345 
346  if(cfg["ignore_race_traits"].to_bool()) {
348  } else {
349  for(const config& t : race_->additional_traits()) {
350  if(alignment_ != unit_alignments::type::neutral || t["id"] != "fearless")
351  possible_traits_.add_child("trait", t);
352  }
353  }
354 
355  if(undead_variation_.empty()) {
357  }
358  }
359 
360  // Insert any traits that are just for this unit type
361  for(const config& trait : cfg.child_range("trait")) {
362  possible_traits_.add_child("trait", trait);
363  }
364 
365  hide_help_ = cfg["hide_help"].to_bool();
366 
368 }
369 
370 /**
371  * Load the most needed data into an empty unit_type (build to CREATE).
372  * This creates the gender-specific types (if needed) and also defines how much
373  * experience is needed to advance as well as what this advances to.
374  */
376 {
377  // Don't build twice.
378  if(CREATED <= build_status_) {
379  return;
380  }
381 
382 
383  for(unsigned i = 0; i < gender_types_.size(); ++i) {
384  if(gender_types_[i]) {
385  gender_types_[i]->build_created();
386  }
387  }
388 
389  for(auto& pair : variations_) {
390  pair.second.build_created();
391  }
392 
393 
394  const config& cfg = get_cfg();
395 
396  const std::string& advances_to_val = cfg["advances_to"];
397  if(advances_to_val != "null" && !advances_to_val.empty()) {
398  advances_to_ = utils::split(advances_to_val);
399  }
400 
401 
402  type_name_ = cfg["name"].t_str();
403  variation_name_ = cfg["variation_name"].t_str();
404 
405  DBG_UT << "unit_type '" << log_id() << "' advances to : " << advances_to_val;
406 
407  experience_needed_ = cfg["experience"].to_int(500);
408  cost_ = cfg["cost"].to_int(1);
409 
410  //needed by the editor.
411  image_ = cfg["image"].str();
413 }
414 
415 /**
416  * Performs a build of this to the indicated stage.
417  */
419  const movement_type_map& movement_types,
420  const race_map& races,
421  const config_array_view& traits)
422 {
423  DBG_UT << "Building unit type " << log_id() << ", level " << status;
424 
425  switch(status) {
426  case NOT_BUILT:
427  // Already done in the constructor.
428  return;
429 
430  case CREATED:
431  // Build the basic data.
432  build_created();
433  return;
434 
435  case VARIATIONS: // Implemented as part of HELP_INDEXED
436  case HELP_INDEXED:
437  // Build the data needed to feed the help index.
438  build_help_index(movement_types, races, traits);
439  return;
440 
441  case FULL:
442  build_full(movement_types, races, traits);
443  return;
444 
445  default:
446  ERR_UT << "Build of unit_type to unrecognized status (" << status << ") requested.";
447  // Build as much as possible.
448  build_full(movement_types, races, traits);
449  return;
450  }
451 }
452 
453 const unit_type& unit_type::get_gender_unit_type(std::string gender) const
454 {
455  if(gender == unit_race::s_female) {
457  } else if(gender == unit_race::s_male) {
459  }
460 
461  return *this;
462 }
463 
465 {
466  const std::size_t i = gender;
467  if(i < gender_types_.size() && gender_types_[i] != nullptr) {
468  return *gender_types_[i];
469  }
470 
471  return *this;
472 }
473 
474 const unit_type& unit_type::get_variation(const std::string& id) const
475 {
476  const variations_map::const_iterator i = variations_.find(id);
477  if(i != variations_.end()) {
478  return i->second;
479  }
480 
481  return *this;
482 }
483 
485 {
486  if(description_.empty()) {
487  return (_("No description available."));
488  } else {
489  return description_;
490  }
491 }
492 
493 std::vector<t_string> unit_type::special_notes() const {
495 }
496 
497 static void append_special_note(std::vector<t_string>& notes, const t_string& new_note) {
498  if(new_note.empty()) return;
499  std::string_view note_plain = new_note.c_str();
500  utils::trim(note_plain);
501  if(note_plain.empty()) return;
502  auto iter = std::find(notes.begin(), notes.end(), new_note);
503  if(iter != notes.end()) return;
504  notes.push_back(new_note);
505 }
506 
507 std::vector<t_string> combine_special_notes(const std::vector<t_string> direct, const config& abilities, const_attack_itors attacks, const movetype& mt)
508 {
509  std::vector<t_string> notes;
510  for(const auto& note : direct) {
511  append_special_note(notes, note);
512  }
513  for(const config::any_child ability : abilities.all_children_range()) {
514  if(ability.cfg.has_attribute("special_note")) {
515  append_special_note(notes, ability.cfg["special_note"].t_str());
516  }
517  }
518  for(const auto& attack : attacks) {
519  for(const config::any_child ability : attack.specials().all_children_range()) {
520  if(ability.cfg.has_attribute("special_note")) {
521  append_special_note(notes, ability.cfg["special_note"].t_str());
522  }
523  }
524  if(auto attack_type_note = string_table.find("special_note_damage_type_" + attack.type()); attack_type_note != string_table.end()) {
525  append_special_note(notes, attack_type_note->second);
526  }
527  }
528  for(const auto& move_note : mt.special_notes()) {
529  append_special_note(notes, move_note);
530  }
531  return notes;
532 }
533 
534 const std::vector<unit_animation>& unit_type::animations() const
535 {
536  if(animations_.empty()) {
538  }
539 
540  return animations_;
541 }
542 
544 {
545  if(!attacks_cache_.empty()) {
547  }
548 
549  for(const config& att : get_cfg().child_range("attack")) {
550  attacks_cache_.emplace_back(new attack_type(att));
551  }
552 
554 }
555 
556 namespace
557 {
558 int experience_modifier = 100;
559 }
560 
562  : old_value_(experience_modifier)
563 {
564  experience_modifier = modifier;
565 }
566 
568 {
569  experience_modifier = old_value_;
570 }
571 
573 {
574  return experience_modifier;
575 }
576 
577 int unit_type::experience_needed(bool with_acceleration) const
578 {
579  if(with_acceleration) {
580  int exp = (experience_needed_ * experience_modifier + 50) / 100;
581  if(exp < 1) {
582  exp = 1;
583  }
584 
585  return exp;
586  }
587 
588  return experience_needed_;
589 }
590 
591 bool unit_type::has_ability_by_id(const std::string& ability) const
592 {
593  if(auto abil = get_cfg().optional_child("abilities")) {
594  for(const config::any_child ab : abil->all_children_range()) {
595  if(ab.cfg["id"] == ability) {
596  return true;
597  }
598  }
599  }
600 
601  return false;
602 }
603 
604 std::vector<std::string> unit_type::get_ability_list() const
605 {
606  std::vector<std::string> res;
607 
608  auto abilities = get_cfg().optional_child("abilities");
609  if(!abilities) {
610  return res;
611  }
612 
613  for(const config::any_child ab : abilities->all_children_range()) {
614  std::string id = ab.cfg["id"];
615 
616  if(!id.empty()) {
617  res.push_back(std::move(id));
618  }
619  }
620 
621  return res;
622 }
623 
625 {
626  return hide_help_ || unit_types.hide_help(id_, race_->id());
627 }
628 
629 
630 static void advancement_tree_internal(const std::string& id, std::set<std::string>& tree)
631 {
632  const unit_type* ut = unit_types.find(id);
633  if(!ut) {
634  return;
635  }
636 
637  for(const std::string& adv : ut->advances_to()) {
638  if(tree.insert(adv).second) {
639  // insertion succeed, expand the new type
640  advancement_tree_internal(adv, tree);
641  }
642  }
643 }
644 
645 std::set<std::string> unit_type::advancement_tree() const
646 {
647  std::set<std::string> tree;
649  return tree;
650 }
651 
652 const std::vector<std::string> unit_type::advances_from() const
653 {
654  // Currently not needed (only help call us and already did it)/
656 
657  std::vector<std::string> adv_from;
658  for(const unit_type_data::unit_type_map::value_type& ut : unit_types.types()) {
659  for(const std::string& adv : ut.second.advances_to()) {
660  if(adv == id_) {
661  adv_from.push_back(ut.second.id());
662  }
663  }
664  }
665 
666  return adv_from;
667 }
668 
669 // This function is only meant to return the likely state a given status
670 // for a new recruit of this type. It should not be used to check if
671 // a particular unit has it, use get_state(status_name) for that.
672 bool unit_type::musthave_status(const std::string& status_name) const
673 {
674  // Statuses default to absent.
675  bool current_status = false;
676 
677  // Look at all of the "musthave" traits to see if the
678  // status gets changed. In the unlikely event it gets changed
679  // multiple times, we want to try to do it in the same order
680  // that unit::apply_modifications does things.
681  for(const config& mod : possible_traits()) {
682  if(mod["availability"] != "musthave") {
683  continue;
684  }
685 
686  for(const config& effect : mod.child_range("effect")) {
687  // See if the effect only applies to
688  // certain unit types But don't worry
689  // about gender checks, since we don't
690  // know what the gender of the
691  // hypothetical recruit is.
692  const std::string& ut = effect["unit_type"];
693 
694  if(!ut.empty()) {
695  const std::vector<std::string>& types = utils::split(ut);
696 
697  if(std::find(types.begin(), types.end(), id()) == types.end()) {
698  continue;
699  }
700  }
701 
702  // We're only interested in status changes.
703  if(effect["apply_to"] != "status") {
704  continue;
705  }
706 
707  if(effect["add"] == status_name) {
708  current_status = true;
709  }
710 
711  if(effect["remove"] == status_name) {
712  current_status = false;
713  }
714  }
715  }
716 
717  return current_status;
718 }
719 
720 const std::string& unit_type::flag_rgb() const
721 {
722  return flag_rgb_.empty() ? game_config::unit_rgb : flag_rgb_;
723 }
724 
726 {
727  if(num_traits() == 0) {
728  return false;
729  }
730 
731  for(const auto& cfg : possible_traits()) {
732  const config::attribute_value& availability = cfg["availability"];
733  if(availability.blank()) {
734  return true;
735  }
736 
737  if(availability.str() != "musthave") {
738  return true;
739  }
740  }
741 
742  return false;
743 }
744 
745 std::vector<std::string> unit_type::variations() const
746 {
747  std::vector<std::string> retval;
748  retval.reserve(variations_.size());
749 
750  for(const variations_map::value_type& val : variations_) {
751  retval.push_back(val.first);
752  }
753 
754  return retval;
755 }
756 
757 bool unit_type::has_variation(const std::string& variation_id) const
758 {
759  return variations_.find(variation_id) != variations_.end();
760 }
761 
763 {
764  for(const variations_map::value_type& val : variations_) {
765  if(!val.second.hide_help()) {
766  return true;
767  }
768  }
769 
770  return false;
771 }
772 
773 int unit_type::resistance_against(const std::string& damage_name, bool attacker) const
774 {
775  int resistance = movement_type_.resistance_against(damage_name);
776  unit_ability_list resistance_abilities;
777 
778  if(auto abilities = get_cfg().optional_child("abilities")) {
779  for(const config& cfg : abilities->child_range("resistance")) {
780  if(!cfg["affect_self"].to_bool(true)) {
781  continue;
782  }
783 
784  if(!resistance_filter_matches(cfg, attacker, damage_name, 100 - resistance)) {
785  continue;
786  }
787 
789  }
790  }
791 
792  if(!resistance_abilities.empty()) {
793  unit_abilities::effect resist_effect(resistance_abilities, 100 - resistance);
794 
795  resistance = 100 - std::min<int>(
796  resist_effect.get_composite_value(),
797  resistance_abilities.highest("max_value").first
798  );
799  }
800 
801  return resistance;
802 }
803 
805  const config& cfg, bool attacker, const std::string& damage_name, int res) const
806 {
807  if(!(cfg["active_on"].empty() ||
808  (attacker && cfg["active_on"] == "offense") ||
809  (!attacker && cfg["active_on"] == "defense"))
810  ) {
811  return false;
812  }
813 
814  const std::string& apply_to = cfg["apply_to"];
815 
816  if(!apply_to.empty()) {
817  if(damage_name != apply_to) {
818  if(apply_to.find(',') != std::string::npos && apply_to.find(damage_name) != std::string::npos) {
819  const std::vector<std::string>& vals = utils::split(apply_to);
820 
821  if(std::find(vals.begin(), vals.end(), damage_name) == vals.end()) {
822  return false;
823  }
824  } else {
825  return false;
826  }
827  }
828  }
829 
830  if(!unit_abilities::filter_base_matches(cfg, res)) {
831  return false;
832  }
833 
834  return true;
835 }
836 
837 /** Implementation detail of unit_type::alignment_description */
838 
840 {
841  static const unit_alignments::sized_array<t_string> male_names {_("lawful"), _("neutral"), _("chaotic"), _("liminal")};
842  static const unit_alignments::sized_array<t_string> female_names {_("female^lawful"), _("female^neutral"), _("female^chaotic"), _("female^liminal")};
843 
844  if(gender == unit_race::FEMALE) {
845  return female_names[static_cast<int>(align)];
846  } else {
847  return male_names[static_cast<int>(align)];
848  }
849 }
850 
851 /* ** unit_type_data ** */
852 
854  : types_()
855  , movement_types_()
856  , races_()
857  , hide_help_all_(false)
858  , hide_help_type_()
859  , hide_help_race_()
860  , units_cfg_()
861  , build_status_(unit_type::NOT_BUILT)
862 {
863 }
864 
865 
866 // Helpers for set_config()
867 
868 namespace
869 {
870 /**
871  * Spits out an error message and throws a config::error.
872  * Called when apply_base_unit() detects a cycle.
873  * (This exists merely to take the error message out of that function.)
874  */
875 void throw_base_unit_recursion_error(const std::vector<std::string>& base_tree, const std::string& base_id)
876 {
877  std::stringstream ss;
878  ss << "[base_unit] recursion loop in [unit_type] ";
879 
880  for(const std::string& step : base_tree) {
881  ss << step << "->";
882  }
883 
884  ss << base_id;
885  ERR_CF << ss.str();
886 
887  throw config::error(ss.str());
888 }
889 
890 /**
891  * Insert a new value into a movetype, possibly calculating the value based on
892  * the existing values in the target movetype.
893  */
894 void patch_movetype(movetype& mt,
895  const std::string& type_to_patch,
896  const std::string& new_key,
897  const std::string& formula_str,
898  int default_val,
899  bool replace)
900 {
901  LOG_CONFIG << "Patching " << new_key << " into movetype." << type_to_patch;
902  config mt_cfg;
903  mt.write(mt_cfg, false);
904 
905  if(!replace && mt_cfg.child_or_empty(type_to_patch).has_attribute(new_key)) {
906  // Don't replace if this type already exists in the config
907  return;
908  }
909 
910  // Make movement_costs.flat, defense.castle, etc available to the formula.
911  // The formula uses config_callables which take references to data in mt;
912  // the enclosing scope is to run all the destructors before calling mt's
913  // non-const merge() function. Probably unnecessary, but I'd rather write
914  // it this way than debug it afterwards.
915  config temp_cfg;
916  {
917  // Instances of wfl::config_callable take a reference to a config,
918  // which means that the "cumulative_values" variable below needs to be
919  // copied so that movement costs aren't overwritten by vision costs
920  // before the formula is evaluated.
921  std::list<config> config_copies;
922 
923  gui2::typed_formula<int> formula(formula_str, default_val);
924  wfl::map_formula_callable original;
925 
926  // These three need to follow movetype's fallback system, where values for
927  // movement costs are used for vision too.
928  const std::array fallback_children {"movement_costs", "vision_costs", "jamming_costs"};
929  config cumulative_values;
930  for(const auto& x : fallback_children) {
931  if(mt_cfg.has_child(x)) {
932  cumulative_values.merge_with(mt_cfg.mandatory_child(x));
933  }
934  config_copies.emplace_back(cumulative_values);
935  auto val = std::make_shared<wfl::config_callable>(config_copies.back());
936  original.add(x, val);
937 
938  // Allow "flat" to work as "vision_costs.flat" when patching vision_costs, etc
939  if(type_to_patch == x) {
940  original.set_fallback(val);
941  }
942  }
943 
944  // These don't need the fallback system
945  const std::array child_names {"defense", "resistance"};
946  for(const auto& x : child_names) {
947  if(mt_cfg.has_child(x)) {
948  const auto& subtag = mt_cfg.mandatory_child(x);
949  auto val = std::make_shared<wfl::config_callable>(subtag);
950  original.add(x, val);
951 
952  // Allow "arcane" to work as well as "resistance.arcane", etc
953  if(type_to_patch == x) {
954  original.set_fallback(val);
955  }
956  }
957  }
958 
959  LOG_CONFIG << " formula=" << formula_str << ", resolves to " << formula(original);
960  temp_cfg[new_key] = formula(original);
961  }
962  mt.merge(temp_cfg, type_to_patch, true);
963 }
964 } // unnamed namespace
965 
966 /**
967  * Modifies the provided config by merging all base units into it.
968  * The @a base_tree parameter is used for detecting and reporting
969  * cycles of base units and in particular to prevent infinite loops.
970  */
971 
972 void unit_type_data::apply_base_unit(unit_type& type, std::vector<std::string>& base_tree)
973 {
974  // Nothing to do.
975  if(type.base_unit_id_.empty()) {
976  return;
977  }
978 
979  // Detect recursion so the WML author is made aware of an error.
980  if(std::find(base_tree.begin(), base_tree.end(), type.base_unit_id_) != base_tree.end()) {
981  throw_base_unit_recursion_error(base_tree, type.base_unit_id_);
982  }
983 
984  // Find the base unit.
985  const unit_type_map::iterator itor = types_.find(type.base_unit_id_);
986  if(itor != types_.end()) {
987 
988  unit_type& base_type = itor->second;
989 
990  // Make sure the base unit has had its base units accounted for.
991  base_tree.push_back(type.base_unit_id_);
992 
993  apply_base_unit(base_type, base_tree);
994 
995  base_tree.pop_back();
996 
997  // Merge the base unit "under" our config.
998  type.writable_cfg().inherit_from(base_type.get_cfg());
999  }
1000  else {
1001  ERR_CF << "[base_unit]: unit type not found: " << type.base_unit_id_;
1002  throw config::error("unit type not found: " + type.base_unit_id_);
1003  }
1004 }
1005 
1006 /**
1007  * Handles inheritance for configs of [male], [female], and [variation].
1008  * Also removes gendered children, as those serve no purpose.
1009  * @a default_inherit is the default value for inherit=.
1010  */
1011 std::unique_ptr<unit_type> unit_type::create_sub_type(const config& var_cfg, bool default_inherit)
1012 {
1013  config var_copy = var_cfg;
1014  if(var_cfg["inherit"].to_bool(default_inherit)) {
1015  var_copy.inherit_from(get_cfg());
1016  }
1017 
1018  var_copy.clear_children("male");
1019  var_copy.clear_children("female");
1020 
1021  return std::make_unique<unit_type>(std::move(var_copy), parent_id());
1022 }
1023 
1024 /**
1025  * Processes [variation] tags of @a ut_cfg, handling inheritance and
1026  * child clearing.
1027  */
1029 {
1030  // Most unit types do not have variations.
1031  if(!get_cfg().has_child("variation")) {
1032  return;
1033  }
1034 
1035  // Handle each variation's inheritance.
1036  for(const config& var_cfg : get_cfg().child_range("variation")) {
1037 
1038  std::unique_ptr<unit_type> var = create_sub_type(var_cfg, false);
1039 
1040  var->built_cfg_->remove_children("variation", [](const config&){return true;});
1041  var->variation_id_ = var_cfg["variation_id"].str();
1042  var->debug_id_ = debug_id_ + " [" + var->variation_id_ + "]";
1043 
1045  bool success;
1046  std::tie(ut, success) = variations_.emplace(var_cfg["variation_id"].str(), std::move(*var));
1047  if(!success) {
1048  ERR_CF << "Skipping duplicate unit variation ID: '" << var_cfg["variation_id"]
1049  << "' of unit_type '" << get_cfg()["id"] << "'";
1050  }
1051  }
1052 
1053 
1054 }
1055 
1056 
1058 {
1059  // Complete the gender-specific children of the config.
1060  if(auto male_cfg = get_cfg().optional_child("male")) {
1061  gender_types_[0] = create_sub_type(*male_cfg, true);
1062  gender_types_[0]->fill_variations();
1063  }
1064 
1065  if(auto female_cfg = get_cfg().optional_child("female")) {
1066  gender_types_[1] = create_sub_type(*female_cfg, true);
1067  gender_types_[1]->fill_variations();
1068  }
1069 
1070  // Complete the variation-defining children of the config.
1071  fill_variations();
1072 
1074 }
1075 /**
1076  * Resets all data based on the provided config.
1077  * This includes some processing of the config, such as expanding base units.
1078  * A pointer to the config is stored, so the config must be persistent.
1079  */
1081 {
1082  LOG_UT << "unit_type_data::set_config, nunits: " << cfg.child_range("unit_type").size();
1083 
1084  clear();
1085  units_cfg_ = cfg;
1086 
1087  for(const config& mt : cfg.child_range("movetype")) {
1088  movement_types_.emplace(mt["name"].str(), movetype(mt));
1089 
1091  }
1092 
1093  for(const config& r : cfg.child_range("race")) {
1094  const unit_race race(r);
1095  races_.emplace(race.id(), race);
1096 
1098  }
1099 
1100  // Movetype resistance patching
1101  DBG_CF << "Start of movetype patching";
1102  for(const config& r : cfg.child_range("resistance_defaults")) {
1103  const std::string& dmg_type = r["id"];
1104 
1105  for(const config::attribute& attr : r.attribute_range()) {
1106  const std::string& mt = attr.first;
1107 
1108  if(mt == "id" || mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1109  continue;
1110  }
1111 
1112  DBG_CF << "Patching specific movetype " << mt;
1113  patch_movetype(movement_types_[mt], "resistance", dmg_type, attr.second, 100, true);
1114  }
1115 
1116  if(r.has_attribute("default")) {
1117  for(movement_type_map::value_type& mt : movement_types_) {
1118  // Don't apply a default if a value is explicitly specified.
1119  if(r.has_attribute(mt.first)) {
1120  continue;
1121  }
1122  // The "none" movetype expects everything to have the default value (to be UNREACHABLE)
1123  if(mt.first == "none") {
1124  continue;
1125  }
1126 
1127  patch_movetype(mt.second, "resistance", dmg_type, r["default"], 100, false);
1128  }
1129  }
1130  }
1131  DBG_CF << "Split between resistance and cost patching";
1132 
1133  // Movetype move/defend patching
1134  for(const config& terrain : cfg.child_range("terrain_defaults")) {
1135  const std::string& ter_type = terrain["id"];
1136 
1137  struct ter_defs_to_movetype
1138  {
1139  /** The data to read from is in [terrain_defaults][subtag], and corresponds to [movetype][subtag] */
1140  std::string subtag;
1141  /** Deprecated names used in 1.14.0's [terrain_defaults]. For [defense] the name didn't change. */
1142  std::string alias;
1143  int default_val;
1144  };
1145  const std::array terrain_info_tags{
1146  ter_defs_to_movetype{{"movement_costs"}, {"movement"}, movetype::UNREACHABLE},
1147  ter_defs_to_movetype{{"vision_costs"}, {"vision"}, movetype::UNREACHABLE},
1148  ter_defs_to_movetype{{"jamming_costs"}, {"jamming"}, movetype::UNREACHABLE},
1149  ter_defs_to_movetype{{"defense"}, {"defense"}, 100}
1150  };
1151 
1152  for(const auto& cost_type : terrain_info_tags) {
1153  const std::string* src_tag = nullptr;
1154  if(terrain.has_child(cost_type.subtag)) {
1155  src_tag = &cost_type.subtag;
1156  }
1157  else if(terrain.has_child(cost_type.alias)) {
1158  // Check for the deprecated name, no deprecation warnings are printed.
1159  src_tag = &cost_type.alias;
1160  }
1161  if(!src_tag) {
1162  continue;
1163  }
1164 
1165  const config& info = terrain.mandatory_child(*src_tag);
1166 
1167  for(const config::attribute& attr : info.attribute_range()) {
1168  const std::string& mt = attr.first;
1169 
1170  if(mt == "default" || movement_types_.find(mt) == movement_types_.end()) {
1171  continue;
1172  }
1173 
1174  patch_movetype(
1175  movement_types_[mt], cost_type.subtag, ter_type, attr.second, cost_type.default_val, true);
1176  }
1177 
1178  if(info.has_attribute("default")) {
1179  for(movement_type_map::value_type& mt : movement_types_) {
1180  // Don't apply a default if a value is explicitly specified.
1181  if(info.has_attribute(mt.first)) {
1182  continue;
1183  }
1184  // The "none" movetype expects everything to have the default value
1185  if(mt.first == "none") {
1186  continue;
1187  }
1188 
1189  patch_movetype(
1190  mt.second, cost_type.subtag, ter_type, info["default"], cost_type.default_val, false);
1191  }
1192  }
1193  }
1194  }
1195  DBG_CF << "End of movetype patching";
1196 
1197  for(const config& ut : cfg.child_range("unit_type")) {
1198  // Every type is required to have an id.
1199  std::string id = ut["id"].str();
1200  if(id.empty()) {
1201  ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
1202  continue;
1203  }
1204 
1205  if(types_.emplace(id, unit_type(ut)).second) {
1206  LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)";
1207  } else {
1208  ERR_CF << "Multiple [unit_type]s with id=" << id << " encountered.";
1209  }
1210  }
1211 
1212  // Apply base units.
1213  for(auto& type : types_) {
1214  std::vector<std::string> base_tree(1, type.second.id());
1215  apply_base_unit(type.second, base_tree);
1216 
1218  }
1219 
1220  //handle [male], [female], [variation]
1221  for(auto& type : types_) {
1222  type.second.fill_variations_and_gender();
1223 
1225  }
1226 
1227  // Build all unit types. (This was not done within the loop for performance.)
1229 
1230  // Suppress some unit types (presumably used as base units) from the help.
1231  if(auto hide_help = cfg.optional_child("hide_help")) {
1232  hide_help_all_ = hide_help["all"].to_bool();
1234  }
1235  DBG_UT << "Finished creating unit types";
1236 }
1237 
1239 {
1240  ut.build(status, movement_types_, races_, units_cfg().child_range("trait"));
1241 }
1242 
1243 /**
1244  * Finds a unit_type by its id() and makes sure it is built to the specified level.
1245  */
1246 const unit_type* unit_type_data::find(const std::string& key, unit_type::BUILD_STATUS status) const
1247 {
1248  if(key.empty() || key == "random") {
1249  return nullptr;
1250  }
1251 
1252  DBG_CF << "trying to find " << key << " in unit_type list (unit_type_data.unit_types)";
1253  const unit_type_map::iterator itor = types_.find(key);
1254 
1255  // This might happen if units of another era are requested (for example for savegames)
1256  if(itor == types_.end()) {
1257  DBG_CF << "unable to find " << key << " in unit_type list (unit_type_data.unit_types)";
1258  return nullptr;
1259  }
1260 
1261  // Make sure the unit_type is built to the requested level.
1262  build_unit_type(itor->second, status);
1263 
1264  return &itor->second;
1265 }
1266 
1267 void unit_type_data::check_types(const std::vector<std::string>& types) const
1268 {
1269  for(const std::string& type : types) {
1270  if(!find(type)) {
1271  throw game::game_error("unknown unit type: " + type);
1272  }
1273  }
1274 }
1275 
1277 {
1278  types_.clear();
1279  movement_types_.clear();
1280  races_.clear();
1282 
1283  hide_help_all_ = false;
1284  hide_help_race_.clear();
1285  hide_help_type_.clear();
1286 }
1287 
1289 {
1290  // Nothing to do if already built to the requested level.
1291  if(status <= build_status_) {
1292  return;
1293  }
1294 
1295  for(const auto& type : types_) {
1296  build_unit_type(type.second, status);
1297 
1299  }
1300 
1301  build_status_ = status;
1302 }
1303 
1305 {
1306  hide_help_race_.emplace_back();
1307  hide_help_type_.emplace_back();
1308 
1309  std::vector<std::string> races = utils::split(cfg["race"]);
1310  hide_help_race_.back().insert(races.begin(), races.end());
1311 
1312  std::vector<std::string> types = utils::split(cfg["type"]);
1313  hide_help_type_.back().insert(types.begin(), types.end());
1314 
1315  std::vector<std::string> trees = utils::split(cfg["type_adv_tree"]);
1316  hide_help_type_.back().insert(trees.begin(), trees.end());
1317 
1318  for(const std::string& t_id : trees) {
1319  unit_type_map::iterator ut = types_.find(t_id);
1320 
1321  if(ut != types_.end()) {
1322  std::set<std::string> adv_tree = ut->second.advancement_tree();
1323  hide_help_type_.back().insert(adv_tree.begin(), adv_tree.end());
1324  }
1325  }
1326 
1327  // We recursively call all the imbricated [not] tags
1328  if(auto cfgnot = cfg.optional_child("not")) {
1329  read_hide_help(*cfgnot);
1330  }
1331 }
1332 
1333 bool unit_type_data::hide_help(const std::string& type, const std::string& race) const
1334 {
1335  bool res = hide_help_all_;
1336  int lvl = hide_help_all_ ? 1 : 0; // first level is covered by 'all=yes'
1337 
1338  // supposed to be equal but let's be cautious
1339  int lvl_nb = std::min(hide_help_race_.size(), hide_help_type_.size());
1340 
1341  for(; lvl < lvl_nb; ++lvl) {
1342  if(hide_help_race_[lvl].count(race) || hide_help_type_[lvl].count(type)) {
1343  res = !res; // each level is a [not]
1344  }
1345  }
1346 
1347  return res;
1348 }
1349 
1350 const unit_race* unit_type_data::find_race(const std::string& key) const
1351 {
1352  race_map::const_iterator i = races_.find(key);
1353  return i != races_.end() ? &i->second : nullptr;
1354 }
1355 
1357 {
1358  build_created();
1359  if(auto p_setxp = cfg.get("set_experience")) {
1360  experience_needed_ = p_setxp->to_int();
1361  }
1362  if(auto attr = cfg.get("set_advances_to")) {
1363  advances_to_ = utils::split(attr->str());
1364  }
1365  if(auto attr = cfg.get("set_cost")) {
1366  cost_ = attr->to_int(1);
1367  }
1368  if(auto attr = cfg.get("add_advancement")) {
1369  for(const auto& str : utils::split(attr->str())) {
1370  if(!utils::contains(advances_to_, str)) {
1371  advances_to_.push_back(str);
1372  }
1373  }
1374  }
1375  if(auto attr = cfg.get("remove_advancement")) {
1376  for(const auto& str : utils::split(attr->str())) {
1377  boost::remove_erase(advances_to_, str);
1378  }
1379  }
1380 
1381  // apply recursively to subtypes.
1382  for(int gender = 0; gender <= 1; ++gender) {
1383  if(!gender_types_[gender]) {
1384  continue;
1385  }
1386  gender_types_[gender]->apply_scenario_fix(cfg);
1387  }
1388 
1389  if(get_cfg().has_child("variation")) {
1390  // Make sure the variations are created.
1392  for(auto& v : variations_) {
1393  v.second.apply_scenario_fix(cfg);
1394  }
1395  }
1396 }
1397 
1399 {
1400  unit_type_map::iterator itor = types_.find(cfg["type"].str());
1401  // This might happen if units of another era are requested (for example for savegames)
1402  if(itor != types_.end()) {
1403  itor->second.apply_scenario_fix(cfg);
1404  }
1405  else {
1406  // should we give an error message?
1407  }
1408 }
1409 
1411 {
1412  advances_to_.clear();
1413  const std::string& advances_to_val = get_cfg()["advances_to"];
1414  if(advances_to_val != "null" && !advances_to_val.empty()) {
1415  advances_to_ = utils::split(advances_to_val);
1416  }
1417  experience_needed_ = get_cfg()["experience"].to_int(500);
1418  cost_ = get_cfg()["cost"].to_int(1);
1419 
1420  // apply recursively to subtypes.
1421  for(int gender = 0; gender <= 1; ++gender) {
1422  if(!gender_types_[gender]) {
1423  continue;
1424  }
1425  gender_types_[gender]->remove_scenario_fixes();
1426  }
1427  for(auto& v : variations_) {
1428  v.second.remove_scenario_fixes();
1429  }
1430 }
1431 
1433 {
1434  for(auto& pair : types_) {
1435  pair.second.remove_scenario_fixes();
1436  }
1437 }
1438 
1439 void unit_type::check_id(std::string& id)
1440 {
1441  assert(!id.empty());
1442 
1443  // We don't allow leading whitepaces.
1444  if(id[0] == ' ') {
1445  throw error("Found unit type id with a leading whitespace \"" + id + "\"");
1446  }
1447 
1448  bool gave_warning = false;
1449 
1450  for(std::size_t pos = 0; pos < id.size(); ++pos) {
1451  const char c = id[pos];
1452  const bool valid = std::isalnum(c, std::locale::classic()) || c == '_' || c == ' ';
1453 
1454  if(!valid) {
1455  if(!gave_warning) {
1456  ERR_UT << "Found unit type id with invalid characters: \"" << id << "\"";
1457  gave_warning = true;
1458  }
1459 
1460  id[pos] = '_';
1461  }
1462  }
1463 }
1464 
1466 
1467 void adjust_profile(std::string& profile)
1468 {
1469  // Create a temp copy
1470  std::string temp = profile;
1471 
1472  static const std::string path_adjust = "/transparent";
1473  const std::string::size_type offset = profile.find_last_of('/', profile.find('~'));
1474 
1475  // If the path already refers to /transparent...
1476  if(profile.find(path_adjust) != std::string::npos && offset != std::string::npos) {
1477  if(!image::locator(profile).file_exists()) {
1478  profile.replace(profile.find(path_adjust), path_adjust.length(), "");
1479  }
1480 
1481  return;
1482  }
1483 
1484  // else, check for the file with /transparent appended...
1485  offset != std::string::npos ? temp.insert(offset, path_adjust) : temp = path_adjust + temp;
1486 
1487  // and use that path if it exists.
1488  if(image::locator(temp).file_exists()) {
1489  profile = temp;
1490  }
1491 }
double t
Definition: astarsearch.cpp:65
double g
Definition: astarsearch.cpp:65
boost::iterator_range< boost::indirect_iterator< attack_list::const_iterator > > const_attack_itors
attack_itors make_attack_itors(attack_list &atks)
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
bool blank() const
Tests for an attribute that was never set.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
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:399
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:371
void clear_children(T... keys)
Definition: config.hpp:644
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:321
bool has_attribute(config_key_type key) const
Definition: config.cpp:159
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition: config.cpp:1130
void inherit_from(const config &c)
Merge config 'c' into this config, preserving this config's values.
Definition: config.cpp:1181
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:891
child_itors child_range(config_key_type key)
Definition: config.cpp:277
attribute_map::value_type attribute
Definition: config.hpp:301
void clear()
Definition: config.cpp:835
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:691
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:389
config & add_child(config_key_type key)
Definition: config.cpp:445
A class grating read only view to a vector of config objects, viewed as one config with all children ...
optional_const_config optional_child(config_key_type key) const
static game_config_view wrap(const config &cfg)
config_array_view child_range(config_key_type key) const
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
Generic locator abstracting the location of an image.
Definition: picture.hpp:64
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:45
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:176
void write(config &cfg, bool include_notes) const
Writes the movement type data to the provided config.
Definition: movetype.cpp:915
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:871
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:296
const std::vector< t_string > & special_notes() const
Contents of any [special_note] tags.
Definition: movetype.hpp:350
bool empty() const
Definition: tstring.hpp:187
const char * c_str() const
Definition: tstring.hpp:192
int get_composite_value() const
Definition: abilities.hpp:47
void emplace_back(T &&... args)
Definition: unit.hpp:102
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:70
bool empty() const
Definition: unit.hpp:92
static void fill_initial_animations(std::vector< unit_animation > &animations, const config &cfg)
Definition: animation.cpp:485
const std::string & id() const
Definition: race.hpp:35
bool uses_global_traits() const
Definition: race.cpp:124
static const unit_race null_race
Dummy race used when a race is not yet known.
Definition: race.hpp:69
const std::string & undead_variation() const
Definition: race.hpp:49
unsigned int num_traits() const
Definition: race.cpp:139
const config::const_child_itors & additional_traits() const
Definition: race.cpp:129
static const std::string s_female
Standard string id (not translatable) for FEMALE.
Definition: race.hpp:28
static const std::string s_male
Standard string id (not translatable) for MALE.
Definition: race.hpp:29
@ FEMALE
Definition: race.hpp:27
@ MALE
Definition: race.hpp:27
void set_config(const game_config_view &cfg)
Resets all data based on the provided config.
Definition: types.cpp:1080
unit_type::BUILD_STATUS build_status_
Definition: types.hpp:436
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:1246
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:1333
void clear()
Definition: types.cpp:1276
void remove_scenario_fixes()
Definition: types.cpp:1432
const race_map & races() const
Definition: types.hpp:397
bool hide_help_all_
True if [hide_help] contains a 'all=yes' at its root.
Definition: types.hpp:429
race_map races_
Definition: types.hpp:426
void build_all(unit_type::BUILD_STATUS status)
Makes sure the all unit_types are built to the specified level.
Definition: types.cpp:1288
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:972
unit_type_map types_
Definition: types.hpp:424
const unit_race * find_race(const std::string &) const
Definition: types.cpp:1350
void check_types(const std::vector< std::string > &types) const
Definition: types.cpp:1267
movement_type_map movement_types_
Definition: types.hpp:425
game_config_view units_cfg_
Definition: types.hpp:435
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:1238
std::vector< std::set< std::string > > hide_help_type_
Definition: types.hpp:431
const game_config_view & units_cfg() const
Definition: types.hpp:434
const unit_type_map & types() const
Definition: types.hpp:396
void read_hide_help(const config &cfg)
Parses the [hide_help] tag.
Definition: types.cpp:1304
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1398
std::vector< std::set< std::string > > hide_help_race_
Definition: types.hpp:432
A single unit type that the player may recruit.
Definition: types.hpp:46
std::string default_variation_
Definition: types.hpp:357
t_string description_
Definition: types.hpp:332
std::string image_
Definition: types.hpp:346
std::vector< unit_animation > animations_
Definition: types.hpp:381
const std::vector< std::string > advances_from() const
A vector of unit_type ids that can advance to this unit_type.
Definition: types.cpp:652
unit_alignments::type alignment_
Definition: types.hpp:372
int jamming_
Definition: types.hpp:340
int recall_cost_
Definition: types.hpp:337
const std::string & parent_id() const
The id of the original type from which this (variation) descended.
Definition: types.hpp:148
int cost_
Definition: types.hpp:342
void fill_variations()
Processes [variation] tags of ut_cfg, handling inheritance and child clearing.
Definition: types.cpp:1028
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:839
std::vector< t_string > special_notes_
Definition: types.hpp:333
variations_map variations_
Definition: types.hpp:356
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
int level_
Definition: types.hpp:336
const std::string & variation_id() const
The id of this variation; empty if it's a gender variation or a base unit.
Definition: types.hpp:150
const unit_race * race_
Never nullptr, but may point to the null race.
Definition: types.hpp:362
std::string parent_id_
The id of the top ancestor of this unit_type.
Definition: types.hpp:328
std::string flag_rgb_
Definition: types.hpp:350
std::string undead_variation_
Definition: types.hpp:344
bool has_random_traits() const
Definition: types.cpp:725
std::vector< ability_metadata > abilities_
Definition: types.hpp:364
void fill_variations_and_gender()
Definition: types.cpp:1057
const movetype & movement_type() const
Definition: types.hpp:191
bool show_variations_in_help() const
Whether the unit type has at least one help-visible variation.
Definition: types.cpp:762
int max_attacks_
Definition: types.hpp:341
std::string debug_id_
A suffix for id_, used when logging messages.
Definition: types.hpp:326
std::array< std::unique_ptr< unit_type >, 2 > gender_types_
Definition: types.hpp:354
bool has_ability_by_id(const std::string &ability) const
Definition: types.cpp:591
std::string small_profile_
Definition: types.hpp:348
t_string variation_name_
Definition: types.hpp:359
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:474
const_attack_itors attacks() const
Definition: types.cpp:543
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 apply_scenario_fix(const config &cfg)
Definition: types.cpp:1356
int vision_
Definition: types.hpp:339
int resistance_against(const std::string &damage_name, bool attacker) const
Gets resistance while considering custom WML abilities.
Definition: types.cpp:773
bool has_variation(const std::string &variation_id) const
Definition: types.cpp:757
bool hide_help_
Definition: types.hpp:366
t_string unit_description() const
Definition: types.cpp:484
static void check_id(std::string &id)
Validate the id argument.
Definition: types.cpp:1439
BUILD_STATUS
Records the status of the lazy building of unit types.
Definition: types.hpp:77
@ CREATED
Definition: types.hpp:77
@ NOT_BUILT
Definition: types.hpp:77
@ HELP_INDEXED
Definition: types.hpp:77
@ FULL
Definition: types.hpp:77
@ VARIATIONS
Definition: types.hpp:77
std::string id_
Definition: types.hpp:324
std::string usage_
Definition: types.hpp:343
const std::vector< unit_animation > & animations() const
Definition: types.cpp:534
std::vector< std::string > variations() const
Definition: types.cpp:745
bool hide_help() const
Definition: types.cpp:624
const std::string & flag_rgb() const
Definition: types.cpp:720
const config & abilities_cfg() const
Definition: types.hpp:236
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:493
attack_list attacks_cache_
Definition: types.hpp:322
unit_type_error error
Definition: types.hpp:52
double xp_bar_scaling_
Definition: types.hpp:335
std::vector< std::string > advances_to_
Definition: types.hpp:368
const config * cfg_
Definition: types.hpp:318
std::string base_unit_id_
from [base_unit]
Definition: types.hpp:330
double hp_bar_scaling_
Definition: types.hpp:335
config possible_traits_
Definition: types.hpp:376
const std::string log_id() const
A variant on id() that is more descriptive, for use with message logging.
Definition: types.hpp:146
const unit_type & get_gender_unit_type(std::string gender) const
Returns a gendered variant of this unit_type.
Definition: types.cpp:453
std::vector< unit_race::GENDER > genders_
Definition: types.hpp:378
bool zoc_
Definition: types.hpp:366
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:577
bool musthave_status(const std::string &status) const
Definition: types.cpp:672
int experience_needed_
Definition: types.hpp:369
movetype movement_type_
Definition: types.hpp:374
std::string icon_
Definition: types.hpp:347
int hitpoints_
Definition: types.hpp:334
bool do_not_list_
Definition: types.hpp:366
void build(BUILD_STATUS status, const movement_type_map &movement_types, const race_map &races, const config_array_view &traits)
Performs a build of this to the indicated stage.
Definition: types.cpp:418
std::vector< std::string > get_ability_list() const
Definition: types.cpp:604
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:804
void remove_scenario_fixes()
Definition: types.cpp:1410
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:1011
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
BUILD_STATUS build_status_
Definition: types.hpp:383
config::const_child_itors possible_traits() const
Definition: types.hpp:233
void build_created()
Load the most needed data into an empty unit_type (build to CREATE).
Definition: types.cpp:375
std::unique_ptr< config > built_cfg_
Definition: types.hpp:320
std::vector< ability_metadata > adv_abilities_
Definition: types.hpp:364
t_string type_name_
Definition: types.hpp:331
~unit_type()
Definition: types.cpp:170
const config & get_cfg() const
Definition: types.hpp:283
int movement_
Definition: types.hpp:338
unsigned int num_traits() const
Definition: types.hpp:138
unsigned int num_traits_
Definition: types.hpp:352
std::set< std::string > advancement_tree() const
Get the advancement tree.
Definition: types.cpp:645
std::string profile_
Definition: types.hpp:349
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
void set_fallback(const_formula_callable_ptr fallback)
Definition: callable.hpp:265
std::size_t i
Definition: function.cpp:968
std::vector< std::reference_wrapper< const config > > config_array_view
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
symbol_table string_table
Definition: language.cpp:65
Standard logging facilities (interface).
static bool file_exists(const bfs::path &fpath)
Definition: filesystem.cpp:321
double hp_bar_scaling
Definition: game_config.cpp:61
std::string unit_rgb
static void add_color_info(const game_config_view &v, bool build_defaults)
double xp_bar_scaling
Definition: game_config.cpp:62
retval
Default window/dialog return values.
Definition: retval.hpp:30
logger & info()
Definition: log.cpp:238
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:1742
void trim(std::string_view &s)
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:84
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:152
std::map< std::string, unit_race > race_map
Definition: race.hpp:103
Error used for any general game error, e.g.
Definition: game_errors.hpp:47
static const map_location & null_location()
Definition: location.hpp:81
The base template for associating string values with enum values.
Definition: enum_base.hpp:33
static constexpr std::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
std::array< T, size()> sized_array
Provide a alias template for an array of matching size.
Definition: enum_base.hpp:92
utils::string_map::const_iterator end() const
Definition: language.cpp:99
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_experience_accelerator(int modifier)
Definition: types.cpp:561
static int get_acceleration()
Definition: types.cpp:572
ability_metadata(const config &cfg)
Definition: types.cpp:174
mock_char c
#define LOG_CONFIG
Definition: types.cpp:44
static lg::log_domain log_unit("unit")
#define DBG_CF
Definition: types.cpp:45
unit_type_data unit_types
Definition: types.cpp:1465
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:507
#define LOG_UT
Definition: types.cpp:49
#define DBG_UT
Definition: types.cpp:48
static void advancement_tree_internal(const std::string &id, std::set< std::string > &tree)
Definition: types.cpp:630
#define ERR_UT
Definition: types.cpp:50
#define ERR_CF
Definition: types.cpp:42
void adjust_profile(std::string &profile)
Definition: types.cpp:1467
static void append_special_note(std::vector< t_string > &notes, const t_string &new_note)
Definition: types.cpp:497
static lg::log_domain log_config("config")
std::map< std::string, movetype > movement_type_map
Definition: types.hpp:36