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