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