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