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 #pragma once 16 17 #include "gettext.hpp" 18 #include "utils/make_enum.hpp" 19 #include "map/location.hpp" 20 #include "movetype.hpp" 21 #include "units/race.hpp" 22 #include "units/attack_type.hpp" 23 #include "game_errors.hpp" 24 25 #include <array> 26 #include <map> 27 #include <set> 28 #include <string> 29 #include <vector> 30 31 class unit_ability_list; 32 class unit_animation; 33 34 35 typedef std::map<std::string, movetype> movement_type_map; 36 37 38 /** 39 * A single unit type that the player may recruit. 40 * Individual units are defined by the unit class. 41 */ 42 class unit_type 43 { 44 public: 45 class error : public game::game_error 46 { 47 public: error(const std::string & msg)48 error(const std::string& msg) 49 : game::game_error(msg) 50 { 51 } 52 }; 53 /** 54 * Creates a unit type for the given config, but delays its build 55 * till later. 56 * @note @a cfg is not copied, so it has to point to some permanent 57 * storage, that is, a child of unit_type_data::unit_cfg. 58 */ 59 explicit unit_type(const config &cfg, const std::string & parent_id=""); 60 unit_type(const unit_type& o); 61 62 ~unit_type(); 63 64 /// Records the status of the lazy building of unit types. 65 /// These are in order of increasing levels of being built. 66 /// HELP_INDEX is already defined in a windows header under some conditions. 67 enum BUILD_STATUS {NOT_BUILT, CREATED, VARIATIONS, HELP_INDEXED, FULL}; 68 69 /** 70 * Validate the id argument. 71 * Replaces invalid characters in the reference with underscores. 72 * @param id The proposed id for a unit_type. 73 * @throw error if id starts with a space. 74 */ 75 static void check_id(std::string& id); 76 77 private: // These will be called by build(). 78 /// Load data into an empty unit_type (build to FULL). 79 void build_full(const movement_type_map &movement_types, 80 const race_map &races, const config::const_child_itors &traits); 81 /// Partially load data into an empty unit_type (build to HELP_INDEXED). 82 void build_help_index(const movement_type_map &movement_types, 83 const race_map &races, const config::const_child_itors &traits); 84 /// Load the most needed data into an empty unit_type (build to CREATE). 85 void build_created(const movement_type_map &movement_types, 86 const race_map &races, const config::const_child_itors &traits); 87 88 typedef std::map<std::string,unit_type> variations_map; 89 public: 90 /// Performs a build of this to the indicated stage. 91 void build(BUILD_STATUS status, const movement_type_map &movement_types, 92 const race_map &races, const config::const_child_itors &traits); 93 /// Performs a build of this to the indicated stage. 94 /// (This does not logically change the unit type, so allow const access.) build(BUILD_STATUS status,const movement_type_map & movement_types,const race_map & races,const config::const_child_itors & traits) const95 void build(BUILD_STATUS status, const movement_type_map &movement_types, 96 const race_map &races, const config::const_child_itors &traits) const 97 { const_cast<unit_type *>(this)->build(status, movement_types, races, traits); } 98 99 100 /** 101 * Adds an additional advancement path to a unit type. 102 * This is used to implement the [advancefrom] tag. 103 */ 104 void add_advancement(const unit_type &advance_to,int experience); 105 106 /** Get the advancement tree 107 * @return A set of ids of all unit_type objects that this unit_type can 108 * directly or indirectly advance to. 109 */ 110 std::set<std::string> advancement_tree() const; 111 112 /// A vector of unit_type ids that this unit_type can advance to. advances_to() const113 const std::vector<std::string>& advances_to() const { return advances_to_; } 114 /// A vector of unit_type ids that can advance to this unit_type. 115 const std::vector<std::string> advances_from() const; 116 117 /// Returns two iterators pointing to a range of AMLA configs. modification_advancements() const118 config::const_child_itors modification_advancements() const 119 { return cfg_.child_range("advancement"); } 120 121 /** 122 * Returns a gendered variant of this unit_type. 123 * @param gender "male" or "female". 124 */ 125 const unit_type& get_gender_unit_type(std::string gender) const; 126 /// Returns a gendered variant of this unit_type based on the given parameter. 127 const unit_type& get_gender_unit_type(unit_race::GENDER gender) const; 128 129 const unit_type& get_variation(const std::string& id) const; 130 /** Info on the type of unit that the unit reanimates as. */ undead_variation() const131 const std::string& undead_variation() const { return undead_variation_; } 132 num_traits() const133 unsigned int num_traits() const { return num_traits_; } 134 135 /** The name of the unit in the current language setting. */ type_name() const136 const t_string& type_name() const { return type_name_; } 137 138 /// The id for this unit_type. id() const139 const std::string& id() const { return id_; } 140 /// A variant on id() that is more descriptive, for use with message logging. log_id() const141 const std::string log_id() const { return id_ + debug_id_; } 142 /// The id of the original type from which this (variation) descended. base_id() const143 const std::string& base_id() const { return base_id_; } 144 // NOTE: this used to be a const object reference, but it messed up with the 145 // translation engine upon changing the language in the same session. 146 t_string unit_description() const; hitpoints() const147 int hitpoints() const { return hitpoints_; } hp_bar_scaling() const148 double hp_bar_scaling() const { return hp_bar_scaling_; } xp_bar_scaling() const149 double xp_bar_scaling() const { return xp_bar_scaling_; } level() const150 int level() const { return level_; } recall_cost() const151 int recall_cost() const { return recall_cost_;} movement() const152 int movement() const { return movement_; } vision() const153 int vision() const { return vision_ < 0 ? movement() : vision_; } 154 /// If @a base_value is set to true, do not fall back to movement(). vision(bool base_value) const155 int vision(bool base_value) const { return base_value ? vision_ : vision(); } jamming() const156 int jamming() const {return jamming_; } max_attacks() const157 int max_attacks() const { return max_attacks_; } cost() const158 int cost() const { return cost_; } default_variation() const159 const std::string& default_variation() const { return default_variation_; } variation_name() const160 const std::string& variation_name() const { return variation_name_; } usage() const161 const std::string& usage() const { return usage_; } image() const162 const std::string& image() const { return image_; } icon() const163 const std::string& icon() const { return icon_; } small_profile() const164 const std::string &small_profile() const { return small_profile_; } big_profile() const165 const std::string &big_profile() const { return profile_; } halo() const166 std::string halo() const { return cfg_["halo"]; } ellipse() const167 std::string ellipse() const { return cfg_["ellipse"]; } generate_name() const168 bool generate_name() const { return cfg_["generate_name"].to_bool(true); } 169 const std::vector<unit_animation>& animations() const; 170 171 const std::string& flag_rgb() const; 172 173 const_attack_itors attacks() const; movement_type() const174 const movetype & movement_type() const { return movement_type_; } 175 176 int experience_needed(bool with_acceleration=true) const; 177 178 MAKE_ENUM (ALIGNMENT, 179 (LAWFUL, N_("lawful")) 180 (NEUTRAL, N_("neutral")) 181 (CHAOTIC, N_("chaotic")) 182 (LIMINAL, N_("liminal")) 183 ) 184 alignment() const185 ALIGNMENT alignment() const { return alignment_; } 186 static std::string alignment_description(ALIGNMENT align, unit_race::GENDER gender = unit_race::MALE); 187 188 struct ability_metadata 189 { 190 explicit ability_metadata(const config& cfg); 191 192 std::string id; 193 194 t_string name; 195 t_string name_inactive; 196 197 t_string female_name; 198 t_string female_name_inactive; 199 200 t_string description; 201 t_string description_inactive; 202 203 bool affect_self; 204 bool affect_allies; 205 bool affect_enemies; 206 bool cumulative; 207 }; 208 abilities_metadata() const209 const std::vector<ability_metadata>& abilities_metadata() const { return abilities_; } 210 211 /** Some extra abilities that may be gained through AMLA advancements. */ adv_abilities_metadata() const212 const std::vector<ability_metadata>& adv_abilities_metadata() const { return adv_abilities_; } 213 can_advance() const214 bool can_advance() const { return !advances_to_.empty(); } 215 216 bool musthave_status(const std::string& status) const; 217 has_zoc() const218 bool has_zoc() const { return zoc_; } 219 220 bool has_ability_by_id(const std::string& ability) const; 221 std::vector<std::string> get_ability_list() const; 222 possible_traits() const223 config::const_child_itors possible_traits() const 224 { return possible_traits_.child_range("trait"); } 225 abilities_cfg() const226 const config& abilities_cfg() const 227 { return cfg_.child_or_empty("abilities"); } 228 advancements() const229 config::const_child_itors advancements() const 230 { return cfg_.child_range("advancement"); } 231 events() const232 config::const_child_itors events() const 233 { return cfg_.child_range("event"); } 234 235 bool has_random_traits() const; 236 237 /// The returned vector will not be empty, provided this has been built 238 /// to the HELP_INDEXED status. genders() const239 const std::vector<unit_race::GENDER>& genders() const { return genders_; } has_gender_variation(const unit_race::GENDER gender) const240 bool has_gender_variation(const unit_race::GENDER gender) const 241 { 242 return std::find(genders_.begin(), genders_.end(), gender) != genders_.end(); 243 } 244 245 std::vector<std::string> variations() const; variation_types() const246 const variations_map& variation_types() const {return variations_; } 247 248 /** 249 * @param variation_id The id of the variation we search for. 250 * @return Whether one of the type's variations' (or the 251 * siblings' if the unit_type is a variation 252 * itself) id matches @a variation_id. 253 */ 254 bool has_variation(const std::string& variation_id) const; 255 256 /** 257 * Whether the unit type has at least one help-visible variation. 258 */ 259 bool show_variations_in_help() const; 260 261 /// Returns the ID of this type's race without the need to build the type. race_id() const262 std::string race_id() const { return cfg_["race"]; } //race_->id(); } 263 /// Never returns nullptr, but may point to the null race. 264 /// Requires building to the HELP_INDEXED status to get the correct race. race() const265 const unit_race* race() const { return race_; } 266 bool hide_help() const; do_not_list() const267 bool do_not_list() const { return do_not_list_; } 268 get_cfg() const269 const config &get_cfg() const { return cfg_; } 270 /// Returns a trimmed config suitable for use with units. get_cfg_for_units() const271 const config & get_cfg_for_units() const 272 { return built_unit_cfg_ ? unit_cfg_ : build_unit_cfg(); } 273 274 /// Gets resistance while considering custom WML abilities. 275 /// Attention: Filters in resistance-abilities will be ignored. 276 int resistance_against(const std::string& damage_name, bool attacker) const; 277 278 private: 279 /// Generates (and returns) a trimmed config suitable for use with units. 280 const config & build_unit_cfg() const; 281 282 /// Identical to unit::resistance_filter_matches. 283 bool resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name, int res) const; 284 285 private: 286 void operator=(const unit_type& o); 287 288 const config &cfg_; 289 mutable config unit_cfg_; /// Generated as needed via get_cfg_for_units(). 290 mutable bool built_unit_cfg_; 291 mutable attack_list attacks_cache_; 292 293 std::string id_; 294 std::string debug_id_; /// A suffix for id_, used when logging messages. 295 std::string base_id_; /// The id of the top ancestor of this unit_type. 296 t_string type_name_; 297 t_string description_; 298 int hitpoints_; 299 double hp_bar_scaling_, xp_bar_scaling_; 300 int level_; 301 int recall_cost_; 302 int movement_; 303 int vision_; 304 int jamming_; 305 int max_attacks_; 306 int cost_; 307 std::string usage_; 308 std::string undead_variation_; 309 310 std::string image_; 311 std::string icon_; 312 std::string small_profile_; 313 std::string profile_; 314 std::string flag_rgb_; 315 316 unsigned int num_traits_; 317 318 std::array<std::unique_ptr<unit_type>, 2> gender_types_; 319 320 variations_map variations_; 321 std::string default_variation_; 322 std::string variation_name_; 323 324 const unit_race* race_; /// Never nullptr, but may point to the null race. 325 326 std::vector<ability_metadata> abilities_, adv_abilities_; 327 328 bool zoc_, hide_help_, do_not_list_; 329 330 std::vector<std::string> advances_to_; 331 int experience_needed_; 332 bool in_advancefrom_; 333 334 335 ALIGNMENT alignment_; 336 337 movetype movement_type_; 338 339 config possible_traits_; 340 341 std::vector<unit_race::GENDER> genders_; 342 343 // animations are loaded only after the first animations() call 344 mutable std::vector<unit_animation> animations_; 345 346 BUILD_STATUS build_status_; 347 }; 348 349 class unit_type_data 350 { 351 public: 352 unit_type_data(const unit_type_data&) = delete; 353 unit_type_data& operator=(const unit_type_data&) = delete; 354 355 unit_type_data(); 356 357 typedef std::map<std::string,unit_type> unit_type_map; 358 types() const359 const unit_type_map &types() const { return types_; } races() const360 const race_map &races() const { return races_; } traits() const361 const config::const_child_itors traits() const { return unit_cfg_->child_range("trait"); } 362 void set_config(config &cfg); 363 364 /// Finds a unit_type by its id() and makes sure it is built to the specified level. 365 const unit_type *find(const std::string &key, unit_type::BUILD_STATUS status = unit_type::FULL) const; 366 void check_types(const std::vector<std::string>& types) const; 367 const unit_race *find_race(const std::string &) const; 368 369 /// Makes sure the all unit_types are built to the specified level. 370 void build_all(unit_type::BUILD_STATUS status); 371 /// Makes sure the provided unit_type is built to the specified level. build_unit_type(const unit_type & ut,unit_type::BUILD_STATUS status) const372 void build_unit_type(const unit_type & ut, unit_type::BUILD_STATUS status) const 373 { ut.build(status, movement_types_, races_, unit_cfg_->child_range("trait")); } 374 375 /** Checks if the [hide_help] tag contains these IDs. */ 376 bool hide_help(const std::string &type_id, const std::string &race_id) const; 377 378 private: 379 /** Parses the [hide_help] tag. */ 380 void read_hide_help(const config &cfg); 381 382 void clear(); 383 384 void add_advancefrom(const config& unit_cfg) const; 385 void add_advancement(unit_type& to_unit) const; 386 387 mutable unit_type_map types_; 388 movement_type_map movement_types_; 389 race_map races_; 390 391 /** True if [hide_help] contains a 'all=yes' at its root. */ 392 bool hide_help_all_; 393 // vectors containing the [hide_help] and its sub-tags [not] 394 std::vector< std::set<std::string>> hide_help_type_; 395 std::vector< std::set<std::string>> hide_help_race_; 396 397 const config *unit_cfg_; 398 unit_type::BUILD_STATUS build_status_; 399 }; 400 401 extern unit_type_data unit_types; 402 403 void adjust_profile(std::string& profile); 404 405 struct unit_experience_accelerator { 406 unit_experience_accelerator(int modifier); 407 ~unit_experience_accelerator(); 408 static int get_acceleration(); 409 private: 410 int old_value_; 411 }; 412