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