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