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.
This commit is contained in:
Guillaume Melquiond 2010-09-12 18:37:58 +00:00
parent afdf522273
commit 846f06de11
5 changed files with 87 additions and 80 deletions

View file

@ -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

View file

@ -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]

View file

@ -399,7 +399,7 @@ private:
int movement_;
int max_movement_;
mutable std::map<t_translation::t_terrain, int> movement_costs_; // movement cost cache
mutable std::map<t_translation::t_terrain, int> defense_mods_; // defense modifiers cache
mutable defense_cache defense_mods_; // defense modifiers cache
bool hold_position_;
bool end_turn_;
bool resting_;

View file

@ -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<t_translation::t_terrain, int>& move_costs,
return res;
}
int defense_modifier_internal(std::map<t_translation::t_terrain, int>& 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<t_translation::t_terrain, int>::const_iterator i = defense_mods.find(terrain);
if (i != defense_mods.end()) return i->second;
defense_range dummy = { 0, 100 };
std::pair<defense_cache::iterator, bool> 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<t_translation::t_terrain, int>(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<t_translation::t_terrain, int>& 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<t_translation::t_terrain, int>(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<t_translation::t_terrain, int>(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<t_translation::t_terrain, int>(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;

View file

@ -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<t_translation::t_terrain, defense_range> 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<t_translation::t_terrain, int> &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<t_translation::t_terrain, int>& movement_costs() const { return moveCosts_; }
const std::map<t_translation::t_terrain, int>& 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<t_translation::t_terrain, int> moveCosts_;
mutable std::map<t_translation::t_terrain, int> defenseMods_;
mutable defense_cache defenseMods_;
const unit_movement_type* parent_;
config cfg_;
};
int movement_cost_internal(std::map<t_translation::t_terrain, int>& 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<t_translation::t_terrain, int>& 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<std::string,unit_movement_type> movement_type_map;
class unit_type