The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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"
25#include "terrain/type_data.hpp"
26
27static 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
35namespace { // 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. */
56 /** The largest allowable 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. */
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{
91public:
92 /**
93 * Constructor.
94 * @a params must be long-lived (typically a static variable).
95 */
96 explicit data(const parameters & 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. */
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
133private:
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
141private:
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. */
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 */
197void 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 */
422std::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 */
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 */
547void 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 */
585void 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 */
604std::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 */
710void 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{
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 */
736int 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 */
746void 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 */
765void 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 */
846bool 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
854void 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
870void 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 */
895const std::set<std::string> movetype::effects {"movement_costs",
896 "vision_costs", "jamming_costs", "defense", "resistance"};
897
898void 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
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
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
const parameters & params() const
Read-only access to our parameters.
Definition movetype.cpp:122
data(const parameters &params)
Constructor.
Definition movetype.cpp:96
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:895
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:78
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.
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
#define ERR_CF
Definition config.cpp:37
const config * cfg
const formula_callable * fallback_
Definition function.cpp:820
static std::string _(const char *str)
Definition gettext.hpp:100
Standard logging facilities (interface).
static lg::log_domain log_config("config")
#define WRN_CF
STL namespace.
const terrain_code MINUS
std::vector< terrain_code > ter_list
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
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition sound.cpp:139
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...
#define i
#define b