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