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