The Battle for Wesnoth  1.19.0-dev
movetype.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
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  * Handle movement types.
19  */
20 
21 #include "movetype.hpp"
22 
23 #include "game_config_manager.hpp"
24 #include "log.hpp"
25 #include "terrain/translation.hpp"
26 #include "units/types.hpp" // for attack_type
27 
28 static lg::log_domain log_config("config");
29 #define ERR_CF LOG_STREAM(err, log_config)
30 #define WRN_CF LOG_STREAM(warn, log_config)
31 
32 
33 /* *** parameters *** */
34 
35 
36 namespace { // Some functions for use with parameters::eval.
37 
38  /** Converts config defense values to a "max" value. */
39  int config_to_max(int value)
40  {
41  return value < 0 ? -value : value;
42  }
43 
44  /** Converts config defense values to a "min" value. */
45  int config_to_min(int value)
46  {
47  return value < 0 ? -value : 0;
48  }
49 }
50 
51 
52 /** The parameters used when calculating a terrain-based value. */
54 {
55  /** The smallest allowable value. */
56  int min_value;
57  /** The largest allowable value. */
58  int max_value;
59  /** The default value (if no data is available). */
61 
62  /** Converter for values taken from a config. May be nullptr. */
63  int (*eval)(int);
64 
65  /** Whether to look at underlying movement or defense terrains. */
66  bool use_move;
67  /** Whether we are looking for highest or lowest (unless inverted by the underlying terrain). */
69 
70  parameters(int min, int max, int (*eval_fun)(int)=nullptr, bool move=true, bool high=false) :
71  min_value(min), max_value(max), default_value(high ? min : max),
72  eval(eval_fun), use_move(move), high_is_good(high)
73  {}
74 };
75 
76 
77 /** Limits for movement, vision and jamming */
80 
82  movetype::terrain_defense::params_min_(0, 100, config_to_min, false, true);
84  movetype::terrain_defense::params_max_(0, 100, config_to_max, false, false);
85 
86 
87 /* *** data *** */
88 
89 
91 {
92 public:
93  /**
94  * Constructor.
95  * @a params must be long-lived (typically a static variable).
96  */
97  explicit data(const parameters & params) :
98  cfg_(), cache_(), params_(params)
99  {}
100  /**
101  * Constructor.
102  * @a params must be long-lived (typically a static variable).
103  */
104  data(const config & cfg, const parameters & params) :
105  cfg_(cfg), cache_(), params_(params)
106  {}
107 
108  // The copy constructor does not bother copying the cache since
109  // typically the cache will be cleared shortly after the copy.
110  data(const data & that) :
111  cfg_(that.cfg_), cache_(), params_(that.params_)
112  {}
113 
114  /** Clears the cached data (presumably our fallback has changed). */
115  void clear_cache() const;
116  /** Tests if merging @a new_values would result in changes. */
117  bool config_has_changes(const config & new_values, bool overwrite) const;
118  /** Tests for no data in this object. */
119  bool empty() const { return cfg_.empty(); }
120  /** Merges the given config over the existing costs. */
121  void merge(const config & new_values, bool overwrite);
122  /** Read-only access to our parameters. */
123  const parameters & params() const { return params_; }
124  /** Returns the value associated with the given terrain. */
125  int value(const t_translation::terrain_code & terrain,
126  const terrain_info * fallback) const
127  { return value(terrain, fallback, 0); }
128  /** If there is data, writes it to the config. */
129  void write(config & out_cfg, const std::string & child_name) const;
130  /** If there is (merged) data, writes it to the config. */
131  void write(config & out_cfg, const std::string & child_name,
132  const terrain_info * fallback) const;
133 
134 private:
135  /** Calculates the value associated with the given terrain. */
136  int calc_value(const t_translation::terrain_code & terrain,
137  const terrain_info * fallback, unsigned recurse_count) const;
138  /** Returns the value associated with the given terrain (possibly cached). */
139  int value(const t_translation::terrain_code & terrain,
140  const terrain_info * fallback, unsigned recurse_count) const;
141 
142 private:
143  typedef std::map<t_translation::terrain_code, int> cache_t;
144 
145  /** Config describing the terrain values. */
147  /** Cache of values based on the config. */
148  mutable cache_t cache_;
149  /** Various parameters used when calculating values. */
151 };
152 
153 
154 /**
155  * Clears the cached data (presumably our fallback has changed).
156  */
158 {
159  cache_.clear();
160 }
161 
162 
163 /**
164  * Tests if merging @a new_values would result in changes.
165  * This allows the shared data to actually work, as otherwise each unit created
166  * via WML (including unstored units) would "overwrite" its movement data with
167  * a usually identical copy and thus break the sharing.
168  */
170  bool overwrite) const
171 {
172  if ( overwrite ) {
173  for (const config::attribute & a : new_values.attribute_range())
174  if ( a.second != cfg_[a.first] )
175  return true;
176  }
177  else {
178  for (const config::attribute & a : new_values.attribute_range())
179  if ( a.second.to_int() != 0 )
180  return true;
181  }
182 
183  // If we make it here, new_values has no changes for us.
184  return false;
185 }
186 
187 
188 /**
189  * Merges the given config over the existing costs.
190  *
191  * After calling this function, the caller must call clear_cache on any
192  * terrain_info that uses this one as a fallback.
193  *
194  * @param[in] new_values The new values.
195  * @param[in] overwrite If true, the new values overwrite the old.
196  * If false, the new values are added to the old.
197  */
198 void movetype::terrain_info::data::merge(const config & new_values, bool overwrite)
199 {
200  if ( overwrite )
201  // We do not support child tags here, so do not copy any that might
202  // be in the input. (If in the future we need to support child tags,
203  // change "merge_attributes" to "merge_with".)
204  cfg_.merge_attributes(new_values);
205  else {
206  for (const config::attribute & a : new_values.attribute_range()) {
207  config::attribute_value & dest = cfg_[a.first];
208  int old = dest.to_int(params_.max_value);
209 
210  // The new value is the absolute value of the old plus the
211  // provided value, capped between minimum and maximum, then
212  // given the sign of the old value.
213  // (Think defenses for why we might have negative values.)
214  int value = std::abs(old) + a.second.to_int(0);
215  value = std::max(params_.min_value, std::min(value, params_.max_value));
216  if ( old < 0 )
217  value = -value;
218 
219  dest = value;
220  }
221  }
222 
223  // The new data has invalidated the cache.
224  clear_cache();
225 }
226 
227 
228 /**
229  * If there is data, writes it to a config.
230  * @param[out] out_cfg The config that will receive the data.
231  * @param[in] child_name If not empty, create and write to a child config with this tag.
232  * This child will *not* be created if there is no data to write.
233  */
235  config & out_cfg, const std::string & child_name) const
236 {
237  if ( cfg_.empty() )
238  return;
239 
240  if ( child_name.empty() )
241  out_cfg.merge_with(cfg_);
242  else
243  out_cfg.add_child(child_name, cfg_);
244 }
245 
246 
247 /**
248  * Writes merged data to a config.
249  * @param[out] out_cfg The config that will receive the data.
250  * @param[in] child_name If not empty, create and write to a child config with this tag.
251  * This *will* be created even if there is no data to write.
252  * @param[in] fallback If not nullptr, its data will be merged with ours for the write.
253  */
255  config & out_cfg, const std::string & child_name, const terrain_info * fallback) const
256 {
257  // Get a place to write to.
258  config & merged = child_name.empty() ? out_cfg : out_cfg.add_child(child_name);
259 
260  if ( fallback )
261  fallback->write(merged, "", true);
262  merged.merge_with(cfg_);
263 }
264 
265 
266 /**
267  * Calculates the value associated with the given terrain.
268  * This is separate from value() to separate the calculating of the
269  * value from the caching of it.
270  * @param[in] terrain The terrain whose value is requested.
271  * @param[in] fallback Consulted if we are missing data.
272  * @param[in] recurse_count Detects (probable) infinite recursion.
273  */
275  const t_translation::terrain_code & terrain,
276  const terrain_info * fallback,
277  unsigned recurse_count) const
278 {
279  // Infinite recursion detection:
280  if ( recurse_count > 100 ) {
281  ERR_CF << "infinite terrain_info recursion on "
282  << (params_.use_move ? "movement" : "defense") << ": "
284  << " depth " << recurse_count;
285  return params_.default_value;
286  }
287 
288  std::shared_ptr<terrain_type_data> tdata;
290  tdata = game_config_manager::get()->terrain_types(); //This permits to get terrain info in unit help pages from the help in title screen, even if there is no residual gamemap object
291  }
292  assert(tdata);
293 
294  // Get a list of underlying terrains.
295  const t_translation::ter_list & underlying = params_.use_move ?
296  tdata->underlying_mvt_terrain(terrain) :
297  tdata->underlying_def_terrain(terrain);
298 
299  if (terrain_type::is_indivisible(terrain, underlying))
300  {
301  // This is not an alias; get the value directly.
302  int result = params_.default_value;
303 
304  const std::string & id = tdata->get_terrain_info(terrain).id();
305  if (const config::attribute_value *val = cfg_.get(id)) {
306  // Read the value from our config.
307  result = val->to_int(params_.default_value);
308  if ( params_.eval != nullptr )
309  result = params_.eval(result);
310  }
311  else if ( fallback != nullptr ) {
312  // Get the value from our fallback.
313  result = fallback->value(terrain);
314  }
315 
316  // Validate the value.
317  if ( result < params_.min_value ) {
318  WRN_CF << "Terrain '" << terrain << "' has evaluated to " << result
319  << " (" << (params_.use_move ? "cost" : "defense")
320  << "), which is less than " << params_.min_value
321  << "; resetting to " << params_.min_value << ".";
322  result = params_.min_value;
323  }
324  if ( result > params_.max_value ) {
325  WRN_CF << "Terrain '" << terrain << "' has evaluated to " << result
326  << " (" << (params_.use_move ? "cost" : "defense")
327  << "), which is more than " << params_.max_value
328  << "; resetting to " << params_.max_value << ".";
329  result = params_.max_value;
330  }
331 
332  return result;
333  }
334  else
335  {
336  // This is an alias; select the best of all underlying terrains.
337  bool prefer_high = params_.high_is_good;
338  int result = params_.default_value;
339  if ( underlying.front() == t_translation::MINUS )
340  // Use the other value as the initial value.
341  result = result == params_.max_value ? params_.min_value :
342  params_.max_value;
343 
344  // Loop through all underlying terrains.
345  t_translation::ter_list::const_iterator i;
346  for ( i = underlying.begin(); i != underlying.end(); ++i )
347  {
348  if ( *i == t_translation::PLUS ) {
349  // Prefer what is good.
350  prefer_high = params_.high_is_good;
351  }
352  else if ( *i == t_translation::MINUS ) {
353  // Prefer what is bad.
354  prefer_high = !params_.high_is_good;
355  }
356  else {
357  // Test the underlying terrain's value against the best so far.
358  const int num = value(*i, fallback, recurse_count + 1);
359 
360  if ( ( prefer_high && num > result) ||
361  (!prefer_high && num < result) )
362  result = num;
363  }
364  }
365 
366  return result;
367  }
368 }
369 
370 
371 /**
372  * Returns the value associated with the given terrain (possibly cached).
373  * @param[in] terrain The terrain whose value is requested.
374  * @param[in] fallback Consulted if we are missing data.
375  * @param[in] recurse_count Detects (probable) infinite recursion.
376  */
378  const t_translation::terrain_code & terrain,
379  const terrain_info * fallback,
380  unsigned recurse_count) const
381 {
382  // Check the cache.
383  std::pair<cache_t::iterator, bool> cache_it =
384  cache_.emplace(terrain, -127); // Bogus value that should never be seen.
385  if ( cache_it.second )
386  // The cache did not have an entry for this terrain, so calculate the value.
387  cache_it.first->second = calc_value(terrain, fallback, recurse_count);
388 
389  return cache_it.first->second;
390 }
391 
392 
393 /* *** terrain_info *** */
394 
395 
396 /**
397  * Constructor.
398  * @param[in] params The parameters to use when calculating values.
399  * This is stored as a reference, so it must be long-lived (typically a static variable).
400  * @param[in] fallback Used as a backup in case we are asked for data we do not have (think vision costs falling back to movement costs).
401  */
403  const terrain_info * fallback) :
404  unique_data_(new data(params)),
405  fallback_(fallback)
406 {
407 }
408 
409 
410 /**
411  * Constructor.
412  * @param[in] cfg An initial data set.
413  * @param[in] params The parameters to use when calculating values.
414  * This is stored as a reference, so it must be long-lived (typically a static variable).
415  * @param[in] fallback Used as a backup in case we are asked for data we do not have (think vision costs falling back to movement costs).
416  */
418  const terrain_info * fallback) :
419  unique_data_(new data(cfg, params)),
420  fallback_(fallback)
421 {
422 }
423 
424 /**
425  * Reverse of terrain_costs::write. Never returns nullptr.
426  * @param[in] cfg An initial data set
427  */
428 std::unique_ptr<movetype::terrain_costs> movetype::read_terrain_costs(const config & cfg)
429 {
430  return std::make_unique<terrain_info> (cfg, movetype::mvj_params_, nullptr);
431 }
432 
433 /**
434  * Copy constructor for callers that handle the fallback and cascade. This is
435  * intended for terrain_defense or movetype's copy constructors, where a
436  * similar set of terrain_infos will be created, complete with the same
437  * relationships between parts of the set.
438  *
439  * @param[in] that The terrain_info to copy.
440  * @param[in] fallback Used as a backup in case we are asked for data we do not have (think vision costs falling back to movement costs).
441  */
443  const terrain_info * fallback) :
444  fallback_(fallback)
445 {
446  assert(fallback ? !! that.fallback_ : ! that.fallback_);
447  copy_data(that);
448 }
449 
451  const terrain_info * fallback) :
452  fallback_(fallback)
453 {
454  assert(fallback ? !! that.fallback_ : ! that.fallback_);
455  swap_data(that);
456 }
457 
458 /**
459  * Destructor
460  *
461  * While this is simply the default destructor, it needs
462  * to be defined in this file so that it knows about ~data(), which
463  * is called from the smart pointers' destructor.
464  */
466 
467 /**
468  * This is only expected to be called either when
469  * 1) both this and @a that have no siblings, as happens when terrain_defense is copied, or
470  * 2) all of the siblings are being copied, as happens when movetype is copied.
471  */
473 {
474  that.make_data_shareable();
475  this->unique_data_.reset();
476  this->shared_data_ = that.shared_data_;
477 }
478 
479 /**
480  * Swap function for the terrain_info class
481  *
482  * This is only expected to be called either when
483  * 1) both this and @a that have no siblings, as happens when swapping two terrain_defenses, or
484  * 2) all of the siblings are being swapped, as happens when two movetypes are swapped.
485  */
487 {
488  // It doesn't matter whether they're both unique, both shared, or
489  // one unique with the other shared.
490  std::swap(this->unique_data_, that.unique_data_);
491  std::swap(this->shared_data_, that.shared_data_);
492 }
493 /**
494  * Swap function for the terrain_defense class
495  *
496  * This relies on all of the terrain_infos having no fallback and no cascade,
497  * an assumption which is provided by terrain_defense's constructors.
498  */
500 {
501  a.min_.swap_data(b.min_);
502  a.max_.swap_data(b.max_);
503 }
504 
505 /**
506  * Swap function for the movetype class, including its terrain_info members
507  *
508  * This relies on the two sets of the terrain_infos having their movement,
509  * vision and jamming cascaded in the same way. This assumption is provided by
510  * movetype's constructors.
511  */
513 {
514  a.movement_.swap_data(b.movement_);
515  a.vision_.swap_data(b.vision_);
516  a.jamming_.swap_data(b.jamming_);
517  swap(a.defense_, b.defense_);
518  std::swap(a.resist_, b.resist_);
519  std::swap(a.flying_, b.flying_);
520  std::swap(a.special_notes_, b.special_notes_);
521 }
522 
524 {
525  movetype m(that);
526  swap(*this, m);
527  return *this;
528 }
529 
531 {
532  swap(*this, that);
533  return *this;
534 }
535 
536 /**
537  * Returns whether or not our data is empty.
538  */
540 {
541  return get_data().empty();
542 }
543 
544 
545 /**
546  * Merges the given config over the existing values.
547  * @param[in] new_values The new values.
548  * @param[in] overwrite If true, the new values overwrite the old.
549  * If false, the new values are added to the old.
550  * @param[in] dependants Other instances that use this as a fallback.
551  */
552 void movetype::terrain_info::merge(const config & new_values, bool overwrite,
553  const std::vector<movetype::terrain_info * > & dependants)
554 {
555  if ( !get_data().config_has_changes(new_values, overwrite) )
556  // Nothing will change, so skip the copy-on-write.
557  return;
558 
559  // Copy-on-write.
560  //
561  // We also need to make our cascade writeable, because changes to this
562  // instance will change data that they receive when using this as their
563  // fallback. However, it's no problem for a writable instance to have a
564  // shareable instance as its fallback.
565  make_data_writable();
566  for (auto & dependant : dependants) {
567  // This will automatically clear the dependant's cache
568  dependant->make_data_writable();
569  }
570 
571  unique_data_->merge(new_values, overwrite);
572 }
573 
574 
575 /**
576  * Returns the value associated with the given terrain.
577  */
579 {
580  return get_data().value(terrain, fallback_);
581 }
582 
583 /**
584  * Writes our data to a config.
585  * @param[out] cfg The config that will receive the data.
586  * @param[in] child_name If not empty, create and write to a child config with this tag.
587  * @param[in] merged If true, our data will be merged with our fallback's, and it is possible an empty child will be created.
588  * If false, data will not be merged, and an empty child will not be created.
589  */
590 void movetype::terrain_info::write(config & cfg, const std::string & child_name,
591  bool merged) const
592 {
593  if ( !merged )
594  get_data().write(cfg, child_name);
595  else
596  get_data().write(cfg, child_name, fallback_);
597 }
598 
599 
600 /**
601  * Does a sufficiently deep copy so that the returned object's lifespan
602  * is independent of other objects' lifespan. Never returns nullptr.
603  *
604  * This implements terrain_costs's virtual method for getting an instance that
605  * doesn't depend on the lifespan of a terrain_defense or movetype object.
606  * This will do a deep copy of the data (with fallback_ already merged) if
607  * needed.
608  */
609 std::unique_ptr<movetype::terrain_costs> movetype::terrain_info::make_standalone() const
610 {
611  std::unique_ptr<terrain_costs> t;
612  if(!fallback_) {
613  // Call the copy constructor, which will make_data_shareable().
614  t = std::make_unique<terrain_info>(*this, nullptr);
615  }
616  else if(get_data().empty()) {
617  // Pure fallback.
618  t = fallback_->make_standalone();
619  }
620  else {
621  // Need to merge data.
622  config merged;
623  write(merged, "", true);
624  t = std::make_unique<terrain_info>(merged, get_data().params(), nullptr);
625  }
626  return t;
627 }
628 
630 {
631  assert(unique_data_ || shared_data_);
632  assert(! (unique_data_ && shared_data_));
633  if(unique_data_)
634  return *unique_data_;
635  return *shared_data_;
636 }
637 
638 /**
639  * Copy the immutable data back to unique_data_, no-op if the data
640  * is already in unique_data_.
641  *
642  * Ensures our data is not shared, and therefore that changes only
643  * affect this instance of terrain_info (and any instances using it
644  * as a fallback).
645  *
646  * This does not need to affect the fallback - it's no problem if a
647  * writable instance has a fallback to a shareable instance, although
648  * a shareable instance must not fallback to a writable instance.
649  */
651 {
652  if(!unique_data_)
653  {
654  // Const hack because this is not really changing the data.
655  auto t = const_cast<terrain_info *>(this);
656  t->unique_data_.reset(new data(*shared_data_));
657  t->shared_data_.reset();
658  }
659 
660  // As we're about to write data, invalidate the cache
661  unique_data_->clear_cache();
662 }
663 
664 /**
665  * Move data to an immutable copy in shared_data_, no-op if the data
666  * is already in shared_data_.
667  *
668  * This is recursive on the fallback chain, because if the data shouldn't be
669  * writable then the data shouldn't be writable via the fallback either.
670  */
672 {
673  if(!unique_data_)
674  return;
675 
676  if(fallback_)
677  fallback_->make_data_shareable();
678 
679  // Const hack because this is not really changing the data.
680  auto t = const_cast<terrain_info *>(this);
681  t->shared_data_ = std::move(t->unique_data_);
682 }
683 
684 /* *** terrain_defense *** */
685 
687  min_(that.min_, nullptr),
688  max_(that.max_, nullptr)
689 {
690 }
691 
693  min_(std::move(that.min_), nullptr),
694  max_(std::move(that.max_), nullptr)
695 {
696 }
697 
699 {
700  min_.copy_data(that.min_);
701  max_.copy_data(that.max_);
702  return *this;
703 }
704 
706 {
707  min_.swap_data(that.min_);
708  max_.swap_data(that.max_);
709  return *this;
710 }
711 /**
712  * Merges the given config over the existing costs.
713  * (Not overwriting implies adding.)
714  */
715 void movetype::terrain_defense::merge(const config & new_data, bool overwrite)
716 {
717  min_.merge(new_data, overwrite, {});
718  max_.merge(new_data, overwrite, {});
719 }
720 
721 /* *** resistances *** */
722 
723 
724 /**
725  * Returns a map from attack types to resistances.
726  */
728 {
729  utils::string_map_res result;
730 
731  for (const config::attribute & attrb : cfg_.attribute_range()) {
732  result[attrb.first] = attrb.second;
733  }
734 
735  return result;
736 }
737 
738 
739 /**
740  * Returns the resistance against the indicated attack.
741  */
743 {
744  std::pair<std::string, std::string> types = attack.damage_type();
745  int res = resistance_against(types.first);
746  if(!(types.second).empty()){
747  res = std::max(res, resistance_against(types.second));
748  }
749  return res;
750 }
751 
752 
753 /**
754  * Returns the resistance against the indicated damage type.
755  */
756 int movetype::resistances::resistance_against(const std::string & damage_type) const
757 {
758  return cfg_[damage_type].to_int(100);
759 }
760 
761 
762 /**
763  * Merges the given config over the existing costs.
764  * If @a overwrite is false, the new values will be added to the old.
765  */
766 void movetype::resistances::merge(const config & new_data, bool overwrite)
767 {
768  if ( overwrite )
769  // We do not support child tags here, so do not copy any that might
770  // be in the input. (If in the future we need to support child tags,
771  // change "merge_attributes" to "merge_with".)
772  cfg_.merge_attributes(new_data);
773  else
774  for (const config::attribute & a : new_data.attribute_range()) {
775  config::attribute_value & dest = cfg_[a.first];
776  dest = std::max(0, dest.to_int(100) + a.second.to_int(0));
777  }
778 }
779 
780 
781 /**
782  * Writes our data to a config, as a child if @a child_name is specified.
783  * (No child is created if there is no data.)
784  */
785 void movetype::resistances::write(config & out_cfg, const std::string & child_name) const
786 {
787  if ( cfg_.empty() )
788  return;
789 
790  if ( child_name.empty() )
791  out_cfg.merge_with(cfg_);
792  else
793  out_cfg.add_child(child_name, cfg_);
794 }
795 
796 
797 /* *** movetype *** */
798 
799 
800 /**
801  * Default constructor
802  */
804  movement_(mvj_params_, nullptr),
807  defense_(),
808  resist_(),
809  flying_(false),
811 {
812 }
813 
814 
815 /**
816  * Constructor from a config
817  */
819  movement_(cfg.child_or_empty("movement_costs"), mvj_params_, nullptr),
820  vision_(cfg.child_or_empty("vision_costs"), mvj_params_, &movement_),
821  jamming_(cfg.child_or_empty("jamming_costs"), mvj_params_, &vision_),
822  defense_(cfg.child_or_empty("defense")),
823  resist_(cfg.child_or_empty("resistance")),
824  flying_(cfg["flies"].to_bool(false))
825 {
826  // 1.15 will support both "flying" and "flies", with "flies" being deprecated
827  flying_ = cfg["flying"].to_bool(flying_);
828 
829  for(const config& sn : cfg.child_range("special_note")) {
830  special_notes_.push_back(sn["note"]);
831  }
832 }
833 
834 
835 /**
836  * Copy constructor
837  */
839  movement_(that.movement_, nullptr),
840  vision_(that.vision_, &movement_),
841  jamming_(that.jamming_, &vision_),
842  defense_(that.defense_),
843  resist_(that.resist_),
844  flying_(that.flying_),
845  special_notes_(that.special_notes_)
846 {
847 }
848 
849 /**
850  * Move constructor.
851  */
853  movement_(std::move(that.movement_), nullptr),
854  vision_(std::move(that.vision_), &movement_),
855  jamming_(std::move(that.jamming_), &vision_),
856  defense_(std::move(that.defense_)),
857  resist_(std::move(that.resist_)),
858  flying_(std::move(that.flying_)),
859  special_notes_(std::move(that.special_notes_))
860 {
861 }
862 
863 /**
864  * Checks if we have a defense cap (nontrivial min value) for any of the given terrain types.
865  */
866 bool movetype::has_terrain_defense_caps(const std::set<t_translation::terrain_code> & ts) const {
867  for (const t_translation::terrain_code & t : ts) {
868  if (defense_.capped(t))
869  return true;
870  }
871  return false;
872 }
873 
874 void movetype::merge(const config & new_cfg, bool overwrite)
875 {
876  for (const auto & applies_to : movetype::effects) {
877  for (const config & child : new_cfg.child_range(applies_to)) {
878  merge(child, applies_to, overwrite);
879  }
880  }
881 
882  // "flies" is used when WML defines a movetype.
883  // "flying" is used when WML defines a unit.
884  // It's easier to support both than to track which case we are in.
885  // Note: in 1.15 "flies" is deprecated, with "flying" preferred in movetype too.
886  flying_ = new_cfg["flies"].to_bool(flying_);
887  flying_ = new_cfg["flying"].to_bool(flying_);
888 }
889 
890 void movetype::merge(const config & new_cfg, const std::string & applies_to, bool overwrite)
891 {
892  if(applies_to == "movement_costs") {
893  movement_.merge(new_cfg, overwrite, {&vision_, &jamming_});
894  }
895  else if(applies_to == "vision_costs") {
896  vision_.merge(new_cfg, overwrite, {&jamming_});
897  }
898  else if(applies_to == "jamming_costs") {
899  jamming_.merge(new_cfg, overwrite, {});
900  }
901  else if(applies_to == "defense") {
902  defense_.merge(new_cfg, overwrite);
903  }
904  else if(applies_to == "resistance") {
905  resist_.merge(new_cfg, overwrite);
906  }
907  else {
908  ERR_CF << "movetype::merge with unknown applies_to: " << applies_to;
909  }
910 }
911 
912 /**
913  * The set of strings defining effects which apply to movetypes.
914  */
915 const std::set<std::string> movetype::effects {"movement_costs",
916  "vision_costs", "jamming_costs", "defense", "resistance"};
917 
918 void movetype::write(config& cfg, bool include_notes) const
919 {
920  movement_.write(cfg, "movement_costs", false);
921  vision_.write(cfg, "vision_costs", false);
922  jamming_.write(cfg, "jamming_costs", false);
923  defense_.write(cfg, "defense");
924  resist_.write(cfg, "resistance");
925 
926  if(flying_)
927  cfg["flying"] = true;
928 
929  if(include_notes) {
930  for(const auto& note : special_notes_) {
931  cfg.add_child("special_note", config{"note", note});
932  }
933  }
934 }
double t
Definition: astarsearch.cpp:63
std::pair< std::string, std::string > damage_type() const
return a modified damage type and/or add a secondary_type for hybrid use if special is active.
Definition: abilities.cpp:1198
Variant for storing WML attributes.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const_attr_itors attribute_range() const
Definition: config.cpp:763
void merge_with(const config &c)
Merge config 'c' into this config, overwriting this config's values.
Definition: config.cpp:1126
child_itors child_range(config_key_type key)
Definition: config.cpp:273
attribute_map::value_type attribute
Definition: config.hpp:299
bool empty() const
Definition: config.cpp:852
config & add_child(config_key_type key)
Definition: config.cpp:441
static game_config_manager * get()
const std::shared_ptr< terrain_type_data > & terrain_types() const
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.cpp:742
void merge(const config &new_data, bool overwrite)
Merges the given config over the existing costs.
Definition: movetype.cpp:766
utils::string_map_res damage_table() const
Returns a map from attack types to resistances.
Definition: movetype.cpp:727
void write(config &out_cfg, const std::string &child_name="") const
Writes our data to a config, as a child if child_name is specified.
Definition: movetype.cpp:785
Stores a set of defense levels.
Definition: movetype.hpp:179
terrain_defense & operator=(const terrain_defense &that)
Definition: movetype.cpp:698
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:208
bool capped(const t_translation::terrain_code &terrain) const
Returns whether there is a defense cap associated to this terrain.
Definition: movetype.hpp:197
static const terrain_info::parameters params_max_
Definition: movetype.hpp:181
static const terrain_info::parameters params_min_
Definition: movetype.hpp:180
void merge(const config &new_data, bool overwrite)
Merges the given config over the existing costs.
Definition: movetype.cpp:715
bool config_has_changes(const config &new_values, bool overwrite) const
Tests if merging new_values would result in changes.
Definition: movetype.cpp:169
const parameters & params() const
Read-only access to our parameters.
Definition: movetype.cpp:123
void write(config &out_cfg, const std::string &child_name) const
If there is data, writes it to the config.
Definition: movetype.cpp:234
data(const config &cfg, const parameters &params)
Constructor.
Definition: movetype.cpp:104
config cfg_
Config describing the terrain values.
Definition: movetype.cpp:146
std::map< t_translation::terrain_code, int > cache_t
Definition: movetype.cpp:143
bool empty() const
Tests for no data in this object.
Definition: movetype.cpp:119
void merge(const config &new_values, bool overwrite)
Merges the given config over the existing costs.
Definition: movetype.cpp:198
cache_t cache_
Cache of values based on the config.
Definition: movetype.cpp:148
void clear_cache() const
Clears the cached data (presumably our fallback has changed).
Definition: movetype.cpp:157
int calc_value(const t_translation::terrain_code &terrain, const terrain_info *fallback, unsigned recurse_count) const
Calculates the value associated with the given terrain.
Definition: movetype.cpp:274
int value(const t_translation::terrain_code &terrain, const terrain_info *fallback) const
Returns the value associated with the given terrain.
Definition: movetype.cpp:125
data(const parameters &params)
Constructor.
Definition: movetype.cpp:97
data(const data &that)
Definition: movetype.cpp:110
const parameters & params_
Various parameters used when calculating values.
Definition: movetype.cpp:150
Stores a set of data based on terrain, in some cases with raw pointers to other instances of terrain_...
Definition: movetype.hpp:104
void make_data_writable() const
Copy the immutable data back to unique_data_, no-op if the data is already in unique_data_.
Definition: movetype.cpp:650
const data & get_data() const
Returns either *unique_data_ or *shared_data_, choosing the one that currently holds the data.
Definition: movetype.cpp:629
std::unique_ptr< terrain_costs > make_standalone() const override
Does a sufficiently deep copy so that the returned object's lifespan is independent of other objects'...
Definition: movetype.cpp:609
std::shared_ptr< const data > shared_data_
Definition: movetype.hpp:165
void swap_data(movetype::terrain_info &that)
Swap function for the terrain_info class.
Definition: movetype.cpp:486
void copy_data(const movetype::terrain_info &that)
This is only expected to be called either when 1) both this and that have no siblings,...
Definition: movetype.cpp:472
bool empty() const
Returns whether or not our data is empty.
Definition: movetype.cpp:539
terrain_info(const parameters &params, const terrain_info *fallback)
Constructor.
Definition: movetype.cpp:402
void write(config &cfg, const std::string &child_name="", bool merged=true) const override
Writes our data to a config.
Definition: movetype.cpp:590
int value(const t_translation::terrain_code &terrain) const override
Returns the value associated with the given terrain.
Definition: movetype.cpp:578
~terrain_info() override
Destructor.
void make_data_shareable() const
Move data to an immutable copy in shared_data_, no-op if the data is already in shared_data_.
Definition: movetype.cpp:671
const terrain_info *const fallback_
Definition: movetype.hpp:166
void merge(const config &new_values, bool overwrite, const std::vector< movetype::terrain_info * > &dependants)
Merges the given config over the existing values.
Definition: movetype.cpp:552
std::unique_ptr< data > unique_data_
Definition: movetype.hpp:164
The basic "size" of the unit - flying, small land, large land, etc.
Definition: movetype.hpp:45
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:175
void write(config &cfg, bool include_notes) const
Writes the movement type data to the provided config.
Definition: movetype.cpp:918
movetype()
Default constructor.
Definition: movetype.cpp:803
static const std::set< std::string > effects
The set of applicable effects for movement types.
Definition: movetype.hpp:346
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:874
bool has_terrain_defense_caps(const std::set< t_translation::terrain_code > &ts) const
Returns whether or not there are any terrain caps with respect to a set of terrains.
Definition: movetype.cpp:866
resistances resist_
Definition: movetype.hpp:374
terrain_info movement_
Definition: movetype.hpp:370
movetype & operator=(const movetype &that)
Definition: movetype.cpp:523
bool flying_
Definition: movetype.hpp:376
terrain_defense defense_
Definition: movetype.hpp:373
static const terrain_info::parameters mvj_params_
Limits for movement, vision and jamming.
Definition: movetype.hpp:243
static std::unique_ptr< terrain_costs > read_terrain_costs(const config &cfg)
Reverse of terrain_costs::write.
Definition: movetype.cpp:428
friend void swap(movetype &a, movetype &b)
Swap function for the movetype class, including its terrain_info members.
Definition: movetype.cpp:512
std::vector< t_string > special_notes_
Definition: movetype.hpp:377
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:295
terrain_info jamming_
Definition: movetype.hpp:372
terrain_info vision_
Definition: movetype.hpp:371
bool is_indivisible() const
Returns true if this terrain has no underlying types other than itself.
Definition: terrain.hpp:110
const formula_callable * fallback_
Definition: function.cpp:756
std::size_t i
Definition: function.cpp:968
Standard logging facilities (interface).
void swap(movetype::terrain_defense &a, movetype::terrain_defense &b)
Swap function for the terrain_defense class.
Definition: movetype.cpp:499
#define ERR_CF
Definition: movetype.cpp:29
#define WRN_CF
Definition: movetype.cpp:30
static lg::log_domain log_config("config")
const terrain_code MINUS
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
const terrain_code PLUS
std::string write_terrain_code(const terrain_code &tcode)
Writes a single terrain code to a string.
std::map< std::string, t_string, res_compare > string_map_res
std::string_view data
Definition: picture.cpp:194
The parameters used when calculating a terrain-based value.
Definition: movetype.cpp:54
int default_value
The default value (if no data is available).
Definition: movetype.cpp:60
bool use_move
Whether to look at underlying movement or defense terrains.
Definition: movetype.cpp:66
parameters(int min, int max, int(*eval_fun)(int)=nullptr, bool move=true, bool high=false)
Definition: movetype.cpp:70
int max_value
The largest allowable value.
Definition: movetype.cpp:58
bool high_is_good
Whether we are looking for highest or lowest (unless inverted by the underlying terrain).
Definition: movetype.cpp:68
int(* eval)(int)
Converter for values taken from a config.
Definition: movetype.cpp:63
int min_value
The smallest allowable value.
Definition: movetype.cpp:56
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
#define a
#define b