The Battle for Wesnoth  1.17.0-dev
movetype.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2021
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 #pragma once
17 
18 #include "config.hpp"
20 
21 class attack_type;
22 namespace t_translation { struct terrain_code; }
23 
24 
25 /**
26  * The basic "size" of the unit - flying, small land, large land, etc.
27  * This encompasses terrain costs, defenses, and resistances.
28  *
29  * This class is used for both [movetype] and [unit] configs, which use the
30  * same data in their configs for [movement_costs], [defense], etc. However,
31  * the data for whether the unit flies is historically held in [movetype]'s
32  * "flies" vs [unit]'s "flying".
33  *
34  * Existing behavior of 1.14:
35  * * movetype::movetype(const config & cfg) will read only the "flies" key
36  * * movetype::merge(const config & cfg, bool overwrite) will read both keys,
37  * with "flying" taking priority if both are supplied
38  * * movetype::write() will write only the "flying" key
39  *
40  * @todo make this more logical. Ideas:
41  * * for 1.15, support both "flying" and "flies" in [movetype]
42  * * for 1.17 or later, drop the "flies"
43  */
44 class movetype
45 {
46 public:
47  /**
48  * A const-only interface for how many (movement, vision, or "jamming") points a
49  * unit needs for each hex. Functions to modify the costs are exposed by the
50  * movetype instance owning this terrain_costs, so that changes to movement will
51  * cascade to the vision, etc.
52  */
54  {
55  public:
56  virtual ~terrain_costs() = default;
57 
58  /**
59  * Returns the value associated with the given terrain.
60  *
61  * Calculated values are cached for later queries.
62  */
63  virtual int value(const t_translation::terrain_code & terrain) const = 0;
64 
65  /**
66  * Returns the cost associated with the given terrain.
67  * Costs are doubled when @a slowed is true.
68  */
69  int cost(const t_translation::terrain_code & terrain, bool slowed=false) const
70  {
71  int result = value(terrain);
72  return slowed && result != movetype::UNREACHABLE ? 2 * result : result;
73  }
74 
75  /**
76  * Does a sufficiently deep copy so that the returned object's lifespan
77  * is independent of other objects' lifespan. Never returns nullptr.
78  */
79  virtual std::unique_ptr<terrain_costs> make_standalone() const = 0;
80 
81  /** Writes our data to a config. */
82  virtual void write(config & cfg, const std::string & child_name="", bool merged=true) const = 0;
83  };
84 
85  /** Reverse of terrain_costs::write. Never returns nullptr. */
86  static std::unique_ptr<terrain_costs> read_terrain_costs(const config & cfg);
87 
88  // Forward declaration so that terrain_info can friend the
89  // swap(terrain_defense, terrain_defense) function
90  class terrain_defense;
91 
92 private:
93 
94  /**
95  * Stores a set of data based on terrain, in some cases with raw pointers to
96  * other instances of terrain_info (the fallback_).
97  *
98  * The data can either be a single instance (in which case it's
99  * writable and stored in unique_data_) or may have already been shared
100  * (via make_data_shareable()), in which case it's stored in shared_data_.
101  * There will always be exactly one of those two that's non-null,
102  * get_data() returns it from where-ever it is.
103  */
105  {
106  /** The terrain-based data. */
107  class data;
108  // The data class is not defined here to keep the header file cleaner.
109 
110  public:
111  /** The parameters used when calculating a terrain-based value. */
112  struct parameters;
113 
114  explicit terrain_info(const parameters & params,
115  const terrain_info * fallback);
116  terrain_info(const config & cfg, const parameters & params,
117  const terrain_info * fallback);
118  ~terrain_info() override;
119 
120  // Instead of the standard copy and move constructors, there are ones
121  // that copy the data but require the caller to specify the fallback.
122  terrain_info(const terrain_info & that) = delete;
123  terrain_info(terrain_info && that) = delete;
124  explicit terrain_info(terrain_info && that,
125  const terrain_info * fallback);
126  terrain_info(const terrain_info & that,
127  const terrain_info * fallback);
128 
129  // Similarly to the copy and move constructors, the default assignments
130  // are deleted, because the caller needs to know about the siblings.
131  terrain_info & operator=(const terrain_info & that) = delete;
132  terrain_info & operator=(terrain_info && that) = delete;
133  void copy_data(const movetype::terrain_info & that);
134  void swap_data(movetype::terrain_info & that);
135 
136  /** Returns whether or not our data is empty. */
137  bool empty() const;
138  /** Merges the given config over the existing values. */
139  void merge(const config & new_values, bool overwrite,
140  const std::vector<movetype::terrain_info *> & dependants);
141 
142  // Implementation of terrain_costs
143  int value(const t_translation::terrain_code & terrain) const override;
144  void write(config & cfg, const std::string & child_name="", bool merged=true) const override;
145  std::unique_ptr<terrain_costs> make_standalone() const override;
146 
147  private:
148  /**
149  * Move data to an immutable copy in shared_data_, no-op if the data
150  * is already in shared_data_.
151  */
152  void make_data_shareable() const;
153  /**
154  * Copy the immutable data back to unique_data_, no-op if the data
155  * is already in unique_data_.
156  */
157  void make_data_writable() const;
158  /**
159  * Returns either *unique_data_ or *shared_data_, choosing the one that
160  * currently holds the data.
161  */
162  const data & get_data() const;
163 
164  private:
165  std::unique_ptr<data> unique_data_;
166  std::shared_ptr<const data> shared_data_;
167  const terrain_info * const fallback_;
168  };
169 
170 
171 public:
172  /**
173  * Magic value that signifies a hex is unreachable.
174  * The UNREACHABLE macro in the data tree should match this value.
175  */
176  static const int UNREACHABLE = 99;
177 
178  /** Stores a set of defense levels. */
180  {
183 
184  public:
185  terrain_defense() : min_(params_min_, nullptr), max_(params_max_, nullptr) {}
186  explicit terrain_defense(const config & cfg) :
187  min_(cfg, params_min_, nullptr), max_(cfg, params_max_, nullptr)
188  {}
189  terrain_defense(const terrain_defense & that);
191  terrain_defense & operator=(const terrain_defense & that);
192  terrain_defense & operator=(terrain_defense && that);
193 
194  /** Returns the defense associated with the given terrain. */
195  int defense(const t_translation::terrain_code & terrain) const
196  { return std::max(min_.value(terrain), max_.value(terrain)); }
197  /** Returns whether there is a defense cap associated to this terrain. */
198  bool capped(const t_translation::terrain_code & terrain) const
199  { return min_.value(terrain) != 0; }
200  /**
201  * Merges the given config over the existing costs.
202  * (Not overwriting implies adding.)
203  */
204  void merge(const config & new_data, bool overwrite);
205  /**
206  * Writes our data to a config, as a child if @a child_name is specified.
207  * (No child is created if there is no data.)
208  */
209  void write(config & cfg, const std::string & child_name="") const
210  { max_.write(cfg, child_name, false); }
211 
213 
214  private:
215  // There will be duplication of the config here, but it is a small
216  // config, and the duplication allows greater code sharing.
219  };
220 
221  /** Stores a set of resistances. */
223  {
224  public:
225  resistances() : cfg_() {}
226  explicit resistances(const config & cfg) : cfg_(cfg) {}
227 
228  /** Returns a map from attack types to resistances. */
229  utils::string_map damage_table() const;
230  /** Returns the resistance against the indicated attack. */
231  int resistance_against(const attack_type & attack) const;
232  /** Returns the resistance against the indicated damage type. */
233  int resistance_against(const std::string & damage_type) const;
234  /** Merges the given config over the existing costs. */
235  void merge(const config & new_data, bool overwrite);
236  /** Writes our data to a config, as a child if @a child_name is specified. */
237  void write(config & out_cfg, const std::string & child_name="") const;
238 
239  private:
241  };
242 
243 private:
245 
246 public:
247  movetype();
248  explicit movetype(const config & cfg);
249  movetype(const movetype & that);
250  movetype(movetype && that);
251  movetype &operator=(const movetype & that);
252  movetype &operator=(movetype && that);
253  // The default destructor is sufficient, despite the Rule of Five.
254  // The copy and assignment functions handle the pointers between
255  // terrain_cost_impl instances, but all of these instances are owned
256  // by this instance of movetype.
257  ~movetype() = default;
258 
259  friend void swap(movetype & a, movetype & b);
261 
262  // This class is basically just a holder for its various pieces, so
263  // provide access to those pieces on demand. There's no non-const
264  // getters for terrain_costs, as that's now an interface with only
265  // const functions in it, and because the logic for how the cascade and
266  // fallback mechanism works would be easier to handle in movetype itself.
267  terrain_defense & get_defense() { return defense_; }
268  resistances & get_resistances() { return resist_; }
269  // And const access:
270  const terrain_costs & get_movement() const { return movement_; }
271  const terrain_costs & get_vision() const { return vision_; }
272  const terrain_costs & get_jamming() const { return jamming_; }
273  const terrain_defense & get_defense() const { return defense_; }
274  const resistances & get_resistances() const { return resist_; }
275 
276  /** Returns whether or not *this is flagged as a flying movement type. */
277  bool is_flying() const { return flying_; }
278  /** Sets whether or not *this is flagged as a flying movement type. */
279  void set_flying(bool flies=true) { flying_ = flies; }
280 
281  /** Returns the cost to move through the indicated terrain. */
282  int movement_cost(const t_translation::terrain_code & terrain, bool slowed=false) const
283  { return movement_.cost(terrain, slowed); }
284  /** Returns the cost to see through the indicated terrain. */
285  int vision_cost(const t_translation::terrain_code & terrain, bool slowed=false) const
286  { return vision_.cost(terrain, slowed); }
287  /** Returns the cost to "jam" through the indicated terrain. */
288  int jamming_cost(const t_translation::terrain_code & terrain, bool slowed=false) const
289  { return jamming_.cost(terrain, slowed); }
290 
291  /** Returns the defensive value of the indicated terrain. */
293  { return defense_.defense(terrain); }
294 
295  /** Returns the resistance against the indicated attack. */
296  int resistance_against(const attack_type & attack) const
297  { return resist_.resistance_against(attack); }
298  /** Returns the resistance against the indicated damage type. */
299  int resistance_against(const std::string & damage_type) const
300  { return resist_.resistance_against(damage_type); }
301  /** Returns a map from attack types to resistances. */
303  { return resist_.damage_table(); }
304 
305  /** Returns whether or not there are any terrain caps with respect to a set of terrains. */
306  bool has_terrain_defense_caps(const std::set<t_translation::terrain_code> & ts) const;
307  /** Returns whether or not there are any vision-specific costs. */
308  bool has_vision_data() const { return !vision_.empty(); }
309  /** Returns whether or not there are any jamming-specific costs. */
310  bool has_jamming_data() const { return !jamming_.empty(); }
311 
312  /**
313  * Merges the given config over the existing data, the config should have zero or more
314  * children named "movement_costs", "defense", etc. Only those children will be affected
315  * (in the case of movement and vision the cascaded values in vision and jamming will also
316  * be affected).
317  *
318  * If @a overwrite is true, the new values will replace the old ones. If it's false, the
319  * new values are relative improvements or maluses which will be applied on top of the old
320  * values.
321  *
322  * If the old values included defense caps and @a overwrite is false, the calculations are
323  * done with absolute values and then changed back to the old sign. This means that merge()
324  * doesn't create or remove defense caps when @a overwrite is false.
325  *
326  * If @a new_cfg["flying"] is provided, it overrides the old value, regardless of the value
327  * of @a overwrite.
328  *
329  * This neither adds nor removes special notes. One purpose of this function is to have
330  * [unit_type][movement_costs] partially overwrite data from [unit_type]movetype=, and it
331  * would be unhelpful if an unrelated [unit_type][special_note] cleared the movetype's
332  * special notes.
333  */
334  void merge(const config & new_cfg, bool overwrite=true);
335 
336  /**
337  * Merges the given config over the existing data.
338  * @a applies_to which type of movement to change ("movement_costs", etc)
339  * @a new_cfg data which could be one of the children of the config for the two-argument form of this function.
340  */
341  void merge(const config & new_cfg, const std::string & applies_to, bool overwrite=true);
342 
343  /** The set of applicable effects for movement types */
344  static const std::set<std::string> effects;
345 
346  /** Contents of any [special_note] tags */
347  const std::vector<t_string>& special_notes() const { return special_notes_; }
348 
349  /** Writes the movement type data to the provided config. */
350  void write(config & cfg) const;
351 
352 private:
358 
359  bool flying_;
360  std::vector<t_string> special_notes_;
361 };
int defense_modifier(const t_translation::terrain_code &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:292
void write(config &cfg, const std::string &child_name="") const
Writes our data to a config, as a child if child_name is specified.
Definition: movetype.hpp:209
int vision_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to see through the indicated terrain.
Definition: movetype.hpp:285
A const-only interface for how many (movement, vision, or "jamming") points a unit needs for each hex...
Definition: movetype.hpp:53
bool has_jamming_data() const
Returns whether or not there are any jamming-specific costs.
Definition: movetype.hpp:310
resistances & get_resistances()
Definition: movetype.hpp:268
std::map< std::string, t_string > string_map
const terrain_info *const fallback_
Definition: movetype.hpp:167
terrain_defense(const config &cfg)
Definition: movetype.hpp:186
terrain_info jamming_
Definition: movetype.hpp:355
std::unique_ptr< data > unique_data_
Definition: movetype.hpp:165
const resistances & get_resistances() const
Definition: movetype.hpp:274
const terrain_costs & get_jamming() const
Definition: movetype.hpp:272
int cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost associated with the given terrain.
Definition: movetype.hpp:69
#define a
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:176
bool has_vision_data() const
Returns whether or not there are any vision-specific costs.
Definition: movetype.hpp:308
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:296
const terrain_defense & get_defense() const
Definition: movetype.hpp:273
int resistance_against(const std::string &damage_type) const
Returns the resistance against the indicated damage type.
Definition: movetype.hpp:299
const std::vector< t_string > & special_notes() const
Contents of any [special_note] tags.
Definition: movetype.hpp:347
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:44
bool capped(const t_translation::terrain_code &terrain) const
Returns whether there is a defense cap associated to this terrain.
Definition: movetype.hpp:198
Definitions for the interface to Wesnoth Markup Language (WML).
resistances(const config &cfg)
Definition: movetype.hpp:226
int jamming_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to "jam" through the indicated terrain.
Definition: movetype.hpp:288
#define b
void write(std::ostream &out, const configr_of &cfg, unsigned int level)
Definition: parser.cpp:764
const terrain_costs & get_movement() const
Definition: movetype.hpp:270
terrain_defense & get_defense()
Definition: movetype.hpp:267
const terrain_costs & get_vision() const
Definition: movetype.hpp:271
static const std::set< std::string > effects
The set of applicable effects for movement types.
Definition: movetype.hpp:344
Stores a set of resistances.
Definition: movetype.hpp:222
Stores a set of data based on terrain, in some cases with raw pointers to other instances of terrain_...
Definition: movetype.hpp:104
utils::string_map damage_table() const
Returns a map from attack types to resistances.
Definition: movetype.hpp:302
std::shared_ptr< const data > shared_data_
Definition: movetype.hpp:166
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1456
terrain_info movement_
Definition: movetype.hpp:353
bool flying_
Definition: movetype.hpp:359
static const terrain_info::parameters params_max_
Definition: movetype.hpp:182
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition: movetype.hpp:282
bool is_flying() const
Returns whether or not *this is flagged as a flying movement type.
Definition: movetype.hpp:277
resistances resist_
Definition: movetype.hpp:357
terrain_defense defense_
Definition: movetype.hpp:356
terrain_info vision_
Definition: movetype.hpp:354
static const terrain_info::parameters mvj_params_
Limits for movement, vision and jamming.
Definition: movetype.hpp:244
The parameters used when calculating a terrain-based value.
Definition: movetype.cpp:55
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
int defense(const t_translation::terrain_code &terrain) const
Returns the defense associated with the given terrain.
Definition: movetype.hpp:195
void set_flying(bool flies=true)
Sets whether or not *this is flagged as a flying movement type.
Definition: movetype.hpp:279
Stores a set of defense levels.
Definition: movetype.hpp:179
static const terrain_info::parameters params_min_
Definition: movetype.hpp:181
std::vector< t_string > special_notes_
Definition: movetype.hpp:360