From 846f06de11ddf9853e86cf8649f41e8e891dd910 Mon Sep 17 00:00:00 2001 From: Guillaume Melquiond Date: Sun, 12 Sep 2010 18:37:58 +0000 Subject: [PATCH] Allowed negative defense values as a way to set upper bounds. Example: [defense] village=-60 means that the unit cannot have less than 60 def (more than 40% def) on terrains containing villages. --- changelog | 3 ++ data/scenario-test.cfg | 22 +++------- src/unit.hpp | 2 +- src/unit_types.cpp | 96 +++++++++++++++++++++--------------------- src/unit_types.hpp | 44 +++++++++++++------ 5 files changed, 87 insertions(+), 80 deletions(-) diff --git a/changelog b/changelog index 3dbd0fc2c0b..5b729299f0b 100644 --- a/changelog +++ b/changelog @@ -109,6 +109,9 @@ Version 1.9.0+svn: floating-point divide. * Allow time_area to define local time of day on map border (bug #16508) * Fix time of day not changing in time area (bug #16584) + * Allowed negative defense values as a way to set upper bounds, + e.g. village=-60 means that a unit cannot have less than 60 def (more + than 40% def) on terrains containing villages. * Miscellaneous and bug fixes: * Removed: statistics upload code. * Changed: compiler mode set to c++98 diff --git a/data/scenario-test.cfg b/data/scenario-test.cfg index a11c69dc792..3a30c994138 100644 --- a/data/scenario-test.cfg +++ b/data/scenario-test.cfg @@ -93,23 +93,13 @@ Xu , Xu , Qxu , Qxu , Ql , Ql availability="musthave" male_name="feral" female_name="female^feral" - description="Receive only 40% defense in villages" + description="Receive at most 40% defense in villages" [effect] - apply_to=new_ability - [abilities] - [defense] - id=feral - name="" - description="" - value=60 - cumulative=no - [filter_self] - [filter_location] - terrain=*^V* - [/filter_location] - [/filter_self] - [/defense] - [/abilities] + apply_to=defense + replace=yes + [defense] + village=-60 + [/defense] [/effect] [/trait] [/modifications] diff --git a/src/unit.hpp b/src/unit.hpp index 0862b393115..a9bf26e24b3 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -399,7 +399,7 @@ private: int movement_; int max_movement_; mutable std::map movement_costs_; // movement cost cache - mutable std::map defense_mods_; // defense modifiers cache + mutable defense_cache defense_mods_; // defense modifiers cache bool hold_position_; bool end_turn_; bool resting_; diff --git a/src/unit_types.cpp b/src/unit_types.cpp index efed2a7c9f5..ed0d741e6c5 100644 --- a/src/unit_types.cpp +++ b/src/unit_types.cpp @@ -315,19 +315,6 @@ std::string unit_movement_type::name() const return cfg_["name"]; } -int unit_movement_type::movement_cost(const gamemap& map, - t_translation::t_terrain terrain) const -{ - int res = movement_cost_internal(moveCosts_, cfg_, parent_, map, terrain); - return res; -} - -int unit_movement_type::defense_modifier(const gamemap& map, - t_translation::t_terrain terrain) const -{ - return defense_modifier_internal(defenseMods_, cfg_, parent_, map, terrain); -} - int unit_movement_type::resistance_against(const attack_type& attack) const { bool result_found = false; @@ -454,33 +441,37 @@ int movement_cost_internal(std::map& move_costs, return res; } -int defense_modifier_internal(std::map& defense_mods, +const defense_range &defense_range_modifier_internal(defense_cache &defense_mods, const config& cfg, const unit_movement_type* parent, const gamemap& map, t_translation::t_terrain terrain, int recurse_count) { - const std::map::const_iterator i = defense_mods.find(terrain); - if (i != defense_mods.end()) return i->second; + defense_range dummy = { 0, 100 }; + std::pair ib = + defense_mods.insert(defense_cache::value_type(terrain, dummy)); + if (!ib.second) return ib.first->second; - bool result_found = false; - int res = 100; + defense_range &res = ib.first->second; // If this is an alias, then select the best of all underlying terrains. const t_translation::t_list& underlying = map.underlying_def_terrain(terrain); assert(!underlying.empty()); if (underlying.size() != 1 || underlying.front() != terrain) { - bool revert = (underlying.front() == t_translation::MINUS ? true : false); + bool revert = underlying.front() == t_translation::MINUS; if(recurse_count >= 90) { ERR_CF << "infinite defense_modifier recursion: " << t_translation::write_terrain_code(terrain) << " depth " << recurse_count << "\n"; } if (recurse_count >= 100) { - defense_mods.insert(std::pair(terrain, res)); return res; } - res = revert ? 0 : 100; + if (revert) { + res.max_ = 0; + res.min_ = 100; + } + for (t_translation::t_list::const_iterator i = underlying.begin(); i != underlying.end(); ++i) { @@ -491,52 +482,59 @@ int defense_modifier_internal(std::map& defense_m revert = true; continue; } - const int value = defense_modifier_internal(defense_mods, cfg, parent, - map, *i, recurse_count + 1); + const defense_range &inh = defense_range_modifier_internal + (defense_mods, cfg, parent, map, *i, recurse_count + 1); - if (value < res && !revert) { - res = value; - } else if (value > res && revert) { - res = value; + if (!revert) { + if (inh.max_ < res.max_) res.max_ = inh.max_; + if (inh.min_ > res.min_) res.min_ = inh.min_; + } else { + if (inh.max_ > res.max_) res.max_ = inh.max_; + if (inh.min_ < res.min_) res.min_ = inh.min_; } } - defense_mods.insert(std::pair(terrain, res)); - return res; + goto check; } - if (const config& defense = cfg.child("defense")) { - if (underlying.size() != 1) { - ERR_CF << "Terrain '" << terrain << "' has " - << underlying.size() << " underlying names - 0 expected.\n"; - - defense_mods.insert(std::pair(terrain, res)); - return res; - } - + if (const config& defense = cfg.child("defense")) + { const std::string& id = map.get_terrain_info(underlying.front()).id(); if (const config::attribute_value *val = defense.get(id)) { - res = *val; - result_found = true; + int def = *val; + if (def >= 0) res.max_ = def; + else res.max_ = res.min_ = -def; + goto check; } } - if (!result_found && parent != NULL) { - res = parent->defense_modifier(map, terrain); + if (parent) { + return parent->defense_range_modifier(map, terrain); } - if (res < 0) { - WRN_CF << "Defense '" << res << "' is '< 0' reset to 0 (100% defense).\n"; - res = 0; - } else if (res > 100) { - WRN_CF << "Defense '" << res << "' is '> 100' reset to 100 (0% defense).\n"; - res = 100; + check: + + if (res.min_ < 0) { + WRN_CF << "Defense '" << res.min_ << "' is '< 0' reset to 0 (100% defense).\n"; + res.min_ = 0; + } + if (res.max_ > 100) { + WRN_CF << "Defense '" << res.max_ << "' is '> 100' reset to 100 (0% defense).\n"; + res.max_ = 100; } - defense_mods.insert(std::pair(terrain, res)); return res; } +int defense_modifier_internal(defense_cache &defense_mods, + const config &cfg, const unit_movement_type *parent, + const gamemap &map, t_translation::t_terrain terrain, int recurse_count) +{ + const defense_range &def = defense_range_modifier_internal(defense_mods, + cfg, parent, map, terrain, recurse_count); + return (std::max)(def.max_, def.min_); +} + static const unit_race& dummy_race(){ static unit_race ur; return ur; diff --git a/src/unit_types.hpp b/src/unit_types.hpp index 1a7bc257516..8ab19b73023 100644 --- a/src/unit_types.hpp +++ b/src/unit_types.hpp @@ -103,6 +103,29 @@ private: class unit_movement_type; +/** + * Possible range of the defense. When a single value is needed, #max_ + * (maximum defense) is selected, unless #min_ is bigger. + */ +struct defense_range +{ + int min_, max_; +}; + +typedef std::map defense_cache; + +const defense_range &defense_range_modifier_internal(defense_cache &defense_mods, + const config &cfg, const unit_movement_type *parent, + const gamemap &map, t_translation::t_terrain terrain, int recurse_count = 0); + +int defense_modifier_internal(defense_cache &defense_mods, + const config &cfg, const unit_movement_type *parent, + const gamemap &map, t_translation::t_terrain terrain, int recurse_count = 0); + +int movement_cost_internal(std::map &move_costs, + const config &cfg, const unit_movement_type *parent, + const gamemap &map, t_translation::t_terrain terrain, int recurse_count = 0); + //the 'unit movement type' is the basic size of the unit - flying, small land, //large land, etc etc. class unit_movement_type @@ -119,8 +142,12 @@ public: unit_movement_type(); std::string name() const; - int movement_cost(const gamemap& map, t_translation::t_terrain terrain) const; - int defense_modifier(const gamemap& map, t_translation::t_terrain terrain) const; + int movement_cost(const gamemap &map, t_translation::t_terrain terrain) const + { return movement_cost_internal(moveCosts_, cfg_, parent_, map, terrain); } + int defense_modifier(const gamemap &map, t_translation::t_terrain terrain) const + { return defense_modifier_internal(defenseMods_, cfg_, parent_, map, terrain); } + const defense_range &defense_range_modifier(const gamemap &map, t_translation::t_terrain terrain) const + { return defense_range_modifier_internal(defenseMods_, cfg_, parent_, map, terrain); } int damage_against(const attack_type& attack) const { return resistance_against(attack); } int resistance_against(const attack_type& attack) const; @@ -129,29 +156,18 @@ public: void set_parent(const unit_movement_type* parent) { parent_ = parent; } bool is_flying() const; - const std::map& movement_costs() const { return moveCosts_; } - const std::map& defense_mods() const { return defenseMods_; } const config& get_cfg() const { return cfg_; } const unit_movement_type* get_parent() const { return parent_; } private: mutable std::map moveCosts_; - mutable std::map defenseMods_; + mutable defense_cache defenseMods_; const unit_movement_type* parent_; config cfg_; }; -int movement_cost_internal(std::map& move_costs, - const config& cfg, const unit_movement_type* parent, - const gamemap& map, t_translation::t_terrain terrain, int recurse_count = 0); - -int defense_modifier_internal(std::map& defense_mods, - const config& cfg, const unit_movement_type* parent, - const gamemap& map, t_translation::t_terrain terrain, int recurse_count = 0); - - typedef std::map movement_type_map; class unit_type