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