The Battle for Wesnoth  1.19.26+dev
race.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Generate race-specific unit-names.
19  */
20 
21 #include "units/race.hpp"
22 
23 #include "filesystem.hpp"
24 #include "log.hpp"
25 #include "utils/name_generator.hpp"
27 
28 static lg::log_domain log_wml("wml");
29 #define ERR_WML LOG_STREAM(err, log_wml)
30 
31 /** Dummy race used when a race is not yet known. */
33 /** Standard string id (not translatable) for FEMALE */
34 const std::string unit_race::s_female("female");
35 /** Standard string id (not translatable) for MALE */
36 const std::string unit_race::s_male("male");
37 
38 static const config& empty_traits() {
39  static config cfg;
40  return cfg;
41 }
42 
43 static const config& empty_topics() {
44  static config cfg;
45  return cfg;
46 }
47 
49  : cfg_()
50  , id_()
51  , name_()
52  , plural_name_()
53  , description_()
54  , ntraits_(0)
55  , traits_(empty_traits().child_range("trait"))
56  , topics_(empty_topics().child_range("topic"))
57  , global_traits_(true)
58  , undead_variation_()
59  , footprints_()
60  , help_taxonomy_()
61 {
62  for(auto& generator : name_generator_) {
63  generator.reset(new name_generator());
64  }
65 }
66 
68  : cfg_(cfg)
69  , id_(cfg["id"])
70  , icon_(cfg["editor_icon"])
71  , plural_name_(cfg["plural_name"].t_str())
72  , description_(cfg["description"].t_str())
73  , ntraits_(cfg["num_traits"].to_int())
74  , traits_(cfg.child_range("trait"))
75  , topics_(cfg.child_range("topic"))
76  , global_traits_(!cfg["ignore_global_traits"].to_bool())
77  , undead_variation_(cfg["undead_variation"])
78  , footprints_(cfg["footprints"])
79  , help_taxonomy_(cfg["help_taxonomy"])
80 {
81  if (plural_name_.empty()) {
82  lg::log_to_chat() << "[race] id='" << id_ << "' is missing a plural_name field.\n";
83  ERR_WML << "[race] id='" << id_ << "' is missing a plural_name field.";
84  plural_name_ = cfg["name"].t_str();
85  }
86 
87  // use "name" if "male_name" or "female_name" aren't available
88  name_[MALE] = cfg["male_name"].t_str();
89  if(name_[MALE].empty()) {
90  name_[MALE] = cfg["name"].t_str();
91  }
92  name_[FEMALE] = cfg["female_name"].t_str();
93  if(name_[FEMALE].empty()) {
94  name_[FEMALE] = cfg["name"].t_str();
95  }
96  if(std::any_of(name_.begin(), name_.end(), [](const auto& n) { return n.empty(); })) {
98  << "[race] id='" << id_
99  << "' is missing a singular name field (either 'name' or both 'male_name' and 'female_name').\n";
100  ERR_WML << "[race] id'" << id_
101  << "' is missing a singular name field (either 'name' or both 'male_name' and 'female_name').";
102  }
103 
104  name_generator_factory generator_factory = name_generator_factory(cfg, {"male", "female"});
105 
106  for(int i=MALE; i<NUM_GENDERS; i++) {
107  GENDER gender = static_cast<GENDER>(i);
108  name_generator_[i] = generator_factory.get_name_generator(gender_string(gender));
109  }
110 }
111 
113 {
114  return name_generator_[gender]->generate();
115 }
116 
118 {
119  return *name_generator_[gender];
120 }
121 
123 {
124  return global_traits_;
125 }
126 
128 {
129  return traits_;
130 }
131 
133 {
134  return topics_;
135 }
136 
137 unsigned int unit_race::num_traits() const { return ntraits_; }
138 
139 
140 const std::string& gender_string(unit_race::GENDER gender) {
141  switch(gender) {
142  case unit_race::FEMALE:
143  return unit_race::s_female;
144  default:
145  return unit_race::s_male;
146  }
147 }
148 
149 unit_race::GENDER string_gender(const std::string& str, unit_race::GENDER def) {
150  if ( str == unit_race::s_male ) {
151  return unit_race::MALE;
152  } else if ( str == unit_race::s_female ) {
153  return unit_race::FEMALE;
154  }
155  return def;
156 }
157 
159  const config& cfg, unit_race::GENDER gender, const std::string& male_key,
160  const std::string& female_key, const std::string& default_key)
161 {
162  return cfg.get_or(gender == unit_race::MALE ? male_key : female_key, default_key);
163 }
164 
165 std::string unit_race::get_icon_path_stem() const
166 {
167  if(!icon_.empty()) {
168  return icon_;
169  }
170 
171  std::string path = "icons/unit-groups/race_" + id_;
172 
173  // FIXME: hardcoded '30' is bad...
174  if(!filesystem::get_binary_file_location("images", path + "_30.png")) {
175  path = "icons/unit-groups/race_custom";
176  }
177 
178  return path;
179 }
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
const attribute_value & get_or(const std::string_view key, const std::string_view default_key) const
Chooses a value.
Definition: config.cpp:671
boost::iterator_range< const_child_iterator > const_child_itors
Definition: config.hpp:281
std::shared_ptr< name_generator > get_name_generator()
Gets the default name generator.
bool empty() const
Definition: tstring.hpp:200
bool global_traits_
Definition: race.hpp:88
bool uses_global_traits() const
Definition: race.cpp:122
std::string get_icon_path_stem() const
Gets this race's icon path without state/size suffix and extension.
Definition: race.cpp:165
std::string icon_
Definition: race.hpp:79
static const unit_race null_race
Dummy race used when a race is not yet known.
Definition: race.hpp:70
const config::const_child_itors & additional_topics() const
Definition: race.cpp:132
config::const_child_itors topics_
Definition: race.hpp:87
std::string generate_name(GENDER gender) const
Definition: race.cpp:112
unit_race()
Only used to construct null_race.
Definition: race.cpp:48
std::array< std::shared_ptr< name_generator >, NUM_GENDERS > name_generator_
Definition: race.hpp:84
unsigned int num_traits() const
Definition: race.cpp:137
unsigned int ntraits_
Definition: race.hpp:83
std::array< t_string, NUM_GENDERS > name_
Definition: race.hpp:80
std::string id_
Definition: race.hpp:78
config::const_child_itors traits_
Definition: race.hpp:86
const config::const_child_itors & additional_traits() const
Definition: race.cpp:127
static const std::string s_female
Standard string id (not translatable) for FEMALE.
Definition: race.hpp:29
static const std::string s_male
Standard string id (not translatable) for MALE.
Definition: race.hpp:30
t_string plural_name_
Definition: race.hpp:81
const name_generator & generator(GENDER gender) const
Definition: race.cpp:117
@ NUM_GENDERS
Definition: race.hpp:28
@ FEMALE
Definition: race.hpp:28
@ MALE
Definition: race.hpp:28
const config * cfg
Declarations for File-IO.
std::size_t i
Definition: function.cpp:1031
Standard logging facilities (interface).
utils::optional< std::string > get_binary_file_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual file of a given type, if it exists.
std::string path
Definition: filesystem.cpp:106
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:550
const std::string & gender_string(unit_race::GENDER gender)
Definition: race.cpp:140
static const config & empty_traits()
Definition: race.cpp:38
#define ERR_WML
Definition: race.cpp:29
static const config & empty_topics()
Definition: race.cpp:43
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:149
const config::attribute_value & gender_value(const config &cfg, unit_race::GENDER gender, const std::string &male_key, const std::string &female_key, const std::string &default_key)
Chooses a value from the given config based on gender.
Definition: race.cpp:158
static lg::log_domain log_wml("wml")
static map_location::direction n