unit_abilities.cpp

Go to the documentation of this file.
00001 /* $Id: unit_abilities.cpp 53851 2012-04-08 07:37:29Z mordante $ */
00002 /*
00003    Copyright (C) 2006 - 2012 by Dominic Bolin <dominic.bolin@exong.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  *  @file
00018  *  Manage unit-abilities, like heal, cure, and weapon_specials.
00019  */
00020 
00021 #include "foreach.hpp"
00022 #include "gamestatus.hpp"
00023 #include "log.hpp"
00024 #include "resources.hpp"
00025 #include "terrain_filter.hpp"
00026 #include "unit.hpp"
00027 #include "team.hpp"
00028 #include "unit_abilities.hpp"
00029 
00030 static lg::log_domain log_engine("engine");
00031 #define ERR_NG LOG_STREAM(err, log_engine)
00032 
00033 
00034 
00035 /*
00036  *
00037  * [abilities]
00038  * ...
00039  *
00040  * [heals]
00041  *  value=4
00042  *  max_value=8
00043  *  cumulative=no
00044  *  affect_allies=yes
00045  *  name= _ "heals"
00046 // *    name_inactive=null
00047  *  description=  _ "Heals:
00048 Allows the unit to heal adjacent friendly units at the beginning of each turn.
00049 
00050 A unit cared for by a healer may heal up to 4 HP per turn.
00051 A poisoned unit cannot be cured of its poison by a healer, and must seek the care of a village or a unit that can cure."
00052 // *    description_inactive=null
00053  *  icon="misc/..."
00054 // *    icon_inactive=null
00055  *  [adjacent_description]
00056  *      name= _ "heals"
00057 // *        name_inactive=null
00058  *      description=  _ "Heals:
00059 Allows the unit to heal adjacent friendly units at the beginning of each turn.
00060 
00061 A unit cared for by a healer may heal up to 4 HP per turn.
00062 A poisoned unit cannot be cured of its poison by a healer, and must seek the care of a village or a unit that can cure."
00063 // *        description_inactive=null
00064  *      icon="misc/..."
00065 // *        icon_inactive=null
00066  *  [/adjacent_description]
00067  *
00068  *  affect_self=yes
00069  *  [filter] // SUF
00070  *      ...
00071  *  [/filter]
00072  *  [filter_location]
00073  *      terrain=f
00074  *      tod=lawful
00075  *  [/filter_location]
00076  *  [filter_self] // SUF
00077  *      ...
00078  *  [/filter_self]
00079  *  [filter_adjacent] // SUF
00080  *      adjacent=n,ne,nw
00081  *      ...
00082  *  [/filter_adjacent]
00083  *  [filter_adjacent_location]
00084  *      adjacent=n,ne,nw
00085  *      ...
00086  *  [/filter_adjacent]
00087  *  [affect_adjacent]
00088  *      adjacent=n,ne,nw
00089  *      [filter] // SUF
00090  *          ...
00091  *      [/filter]
00092  *  [/affect_adjacent]
00093  *  [affect_adjacent]
00094  *      adjacent=s,se,sw
00095  *      [filter] // SUF
00096  *          ...
00097  *      [/filter]
00098  *  [/affect_adjacent]
00099  *
00100  * [/heals]
00101  *
00102  * ...
00103  * [/abilities]
00104  *
00105  */
00106 
00107 
00108 namespace unit_abilities {
00109 
00110 static bool affects_side(const config& cfg, const std::vector<team>& teams, size_t side, size_t other_side)
00111 {
00112     if (side == other_side)
00113         return cfg["affect_allies"].to_bool(true);
00114     if (teams[side - 1].is_enemy(other_side))
00115         return cfg["affect_enemies"].to_bool();
00116     else
00117         return cfg["affect_allies"].to_bool();
00118 }
00119 
00120 }
00121 
00122 
00123 bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc) const
00124 {
00125     if (const config &abilities = cfg_.child("abilities"))
00126     {
00127         foreach (const config &i, abilities.child_range(tag_name)) {
00128             if (ability_active(tag_name, i, loc) &&
00129                 ability_affects_self(tag_name, i, loc))
00130                 return true;
00131         }
00132     }
00133 
00134     const unit_map& units = *resources::units;
00135     map_location adjacent[6];
00136     get_adjacent_tiles(loc,adjacent);
00137     for(int i = 0; i != 6; ++i) {
00138         const unit_map::const_iterator it = units.find(adjacent[i]);
00139         if (it == units.end() || it->incapacitated())
00140             continue;
00141         const config &adj_abilities = it->cfg_.child("abilities");
00142         if (!adj_abilities)
00143             continue;
00144         foreach (const config &j, adj_abilities.child_range(tag_name)) {
00145             if (unit_abilities::affects_side(j, teams_manager::get_teams(), side(), it->side()) &&
00146                 it->ability_active(tag_name, j, adjacent[i]) &&
00147                 ability_affects_adjacent(tag_name,  j, i, loc))
00148                 return true;
00149         }
00150     }
00151 
00152 
00153     return false;
00154 }
00155 unit_ability_list unit::get_abilities(const std::string& tag_name, const map_location& loc) const
00156 {
00157     unit_ability_list res;
00158 
00159     if (const config &abilities = cfg_.child("abilities"))
00160     {
00161         foreach (const config &i, abilities.child_range(tag_name)) {
00162             if (ability_active(tag_name, i, loc) &&
00163                 ability_affects_self(tag_name, i, loc))
00164                 res.cfgs.push_back(std::pair<const config *, map_location>(&i, loc));
00165         }
00166     }
00167 
00168     const unit_map& units = *resources::units;
00169     map_location adjacent[6];
00170     get_adjacent_tiles(loc,adjacent);
00171     for(int i = 0; i != 6; ++i) {
00172         const unit_map::const_iterator it = units.find(adjacent[i]);
00173         if (it == units.end() || it->incapacitated())
00174             continue;
00175         const config &adj_abilities = it->cfg_.child("abilities");
00176         if (!adj_abilities)
00177             continue;
00178         foreach (const config &j, adj_abilities.child_range(tag_name)) {
00179             if (unit_abilities::affects_side(j, teams_manager::get_teams(), side(), it->side()) &&
00180                 it->ability_active(tag_name, j, adjacent[i]) &&
00181                 ability_affects_adjacent(tag_name, j, i, loc))
00182                 res.cfgs.push_back(std::pair<const config *, map_location>(&j, adjacent[i]));
00183         }
00184     }
00185 
00186 
00187     return res;
00188 }
00189 
00190 std::vector<std::string> unit::get_ability_list() const
00191 {
00192     std::vector<std::string> res;
00193 
00194     const config &abilities = cfg_.child("abilities");
00195     if (!abilities) return res;
00196     foreach (const config::any_child &ab, abilities.all_children_range()) {
00197         std::string const &id = ab.cfg["id"];
00198         if (!id.empty())
00199             res.push_back(id);
00200     }
00201     return res;
00202 }
00203 
00204 std::vector<boost::tuple<t_string,t_string,t_string> > unit::ability_tooltips(bool force_active) const
00205 {
00206     std::vector<boost::tuple<t_string,t_string,t_string> > res;
00207 
00208     const config &abilities = cfg_.child("abilities");
00209     if (!abilities) return res;
00210 
00211     foreach (const config::any_child &ab, abilities.all_children_range())
00212     {
00213         if (force_active || ability_active(ab.key, ab.cfg, loc_))
00214         {
00215             t_string const &name =
00216                 gender_ == unit_race::MALE || ab.cfg["female_name"].empty() ?
00217                 ab.cfg["name"].t_str() : ab.cfg["female_name"].t_str();
00218 
00219             if (!name.empty()) {
00220                 res.push_back(boost::make_tuple(
00221                         ab.cfg["name"].t_str(),
00222                         name,
00223                         ab.cfg["description"].t_str()));
00224             }
00225         }
00226         else
00227         {
00228             t_string const &name =
00229                 gender_ == unit_race::MALE || ab.cfg["female_name_inactive"].empty() ?
00230                 ab.cfg["name_inactive"].t_str() : ab.cfg["female_name_inactive"].t_str();
00231 
00232             if (!name.empty()) {
00233                 res.push_back(boost::make_tuple(
00234                         ab.cfg["name_inactive"].t_str(),
00235                         name,
00236                         ab.cfg["description_inactive"].t_str()));
00237             }
00238         }
00239     }
00240     return res;
00241 }
00242 
00243 /*
00244  *
00245  * cfg: an ability WML structure
00246  *
00247  */
00248 static bool cache_illuminates(int &cache, std::string const &ability)
00249 {
00250     if (cache < 0)
00251         cache = (ability == "illuminates");
00252     return (cache != 0);
00253 }
00254 
00255 bool unit::ability_active(const std::string& ability,const config& cfg,const map_location& loc) const
00256 {
00257     int illuminates = -1;
00258     assert(resources::units && resources::game_map && resources::teams && resources::tod_manager);
00259 
00260     if (const config &afilter = cfg.child("filter"))
00261         if (!matches_filter(vconfig(afilter), loc, cache_illuminates(illuminates, ability)))
00262             return false;
00263 
00264     map_location adjacent[6];
00265     get_adjacent_tiles(loc,adjacent);
00266     const unit_map& units = *resources::units;
00267 
00268     foreach (const config &i, cfg.child_range("filter_adjacent"))
00269     {
00270         foreach (const std::string &j, utils::split(i["adjacent"]))
00271         {
00272             map_location::DIRECTION index =
00273                 map_location::parse_direction(j);
00274             if (index == map_location::NDIRECTIONS)
00275                 continue;
00276             unit_map::const_iterator unit = units.find(adjacent[index]);
00277             if (unit == units.end())
00278                 return false;
00279             if (!unit->matches_filter(vconfig(i), unit->get_location(),
00280                 cache_illuminates(illuminates, ability)))
00281                 return false;
00282         }
00283     }
00284 
00285     foreach (const config &i, cfg.child_range("filter_adjacent_location"))
00286     {
00287         foreach (const std::string &j, utils::split(i["adjacent"]))
00288         {
00289             map_location::DIRECTION index = map_location::parse_direction(j);
00290             if (index == map_location::NDIRECTIONS) {
00291                 continue;
00292             }
00293             terrain_filter adj_filter(vconfig(i), units);
00294             adj_filter.flatten(cache_illuminates(illuminates, ability));
00295             if(!adj_filter.match(adjacent[index])) {
00296                 return false;
00297             }
00298         }
00299     }
00300     return true;
00301 }
00302 /*
00303  *
00304  * cfg: an ability WML structure
00305  *
00306  */
00307 bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc) const
00308 {
00309     int illuminates = -1;
00310 
00311     assert(dir >=0 && dir <= 5);
00312     static const std::string adjacent_names[6] = {"n","ne","se","s","sw","nw"};
00313     foreach (const config &i, cfg.child_range("affect_adjacent"))
00314     {
00315         std::vector<std::string> dirs = utils::split(i["adjacent"]);
00316         if(std::find(dirs.begin(),dirs.end(),adjacent_names[dir]) != dirs.end()) {
00317             if (const config &filter = i.child("filter")) {
00318                 if (matches_filter(vconfig(filter), loc,
00319                     cache_illuminates(illuminates, ability)))
00320                     return true;
00321             } else
00322                 return true;
00323         }
00324     }
00325     return false;
00326 }
00327 /*
00328  *
00329  * cfg: an ability WML structure
00330  *
00331  */
00332 bool unit::ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const
00333 {
00334     int illuminates = -1;
00335     const config &filter = cfg.child("filter_self");
00336     bool affect_self = cfg["affect_self"].to_bool(true);
00337     if (!filter || !affect_self) return affect_self;
00338     return matches_filter(vconfig(filter), loc,cache_illuminates(illuminates, ability));
00339 }
00340 
00341 bool unit::has_ability_type(const std::string& ability) const
00342 {
00343     if (const config &list = cfg_.child("abilities")) {
00344         config::const_child_itors itors = list.child_range(ability);
00345         return itors.first != itors.second;
00346     }
00347     return false;
00348 }
00349 
00350 
00351 bool unit_ability_list::empty() const
00352 {
00353     return cfgs.empty();
00354 }
00355 
00356 std::pair<int,map_location> unit_ability_list::highest(const std::string& key, int def) const
00357 {
00358     if (cfgs.empty()) {
00359         return std::make_pair(def, map_location());
00360     }
00361     // The returned location is the best non-cumulative one, if any,
00362     // the best absolute cumulative one otherwise.
00363     map_location best_loc;
00364     bool only_cumulative = true;
00365     int abs_max = 0;
00366     int flat = 0;
00367     int stack = 0;
00368     typedef std::pair<const config *, map_location> pt;
00369     foreach (pt const &p, cfgs)
00370     {
00371         int value = (*p.first)[key].to_int(def);
00372         if ((*p.first)["cumulative"].to_bool()) {
00373             stack += value;
00374             if (value < 0) value = -value;
00375             if (only_cumulative && value >= abs_max) {
00376                 abs_max = value;
00377                 best_loc = p.second;
00378             }
00379         } else if (only_cumulative || value > flat) {
00380             only_cumulative = false;
00381             flat = value;
00382             best_loc = p.second;
00383         }
00384     }
00385     return std::make_pair(flat + stack, best_loc);
00386 }
00387 
00388 std::pair<int,map_location> unit_ability_list::lowest(const std::string& key, int def) const
00389 {
00390     if (cfgs.empty()) {
00391         return std::make_pair(def, map_location());
00392     }
00393     // The returned location is the best non-cumulative one, if any,
00394     // the best absolute cumulative one otherwise.
00395     map_location best_loc;
00396     bool only_cumulative = true;
00397     int abs_max = 0;
00398     int flat = 0;
00399     int stack = 0;
00400     typedef std::pair<const config *, map_location> pt;
00401     foreach (pt const &p, cfgs)
00402     {
00403         int value = (*p.first)[key].to_int(def);
00404         if ((*p.first)["cumulative"].to_bool()) {
00405             stack += value;
00406             if (value < 0) value = -value;
00407             if (only_cumulative && value <= abs_max) {
00408                 abs_max = value;
00409                 best_loc = p.second;
00410             }
00411         } else if (only_cumulative || value < flat) {
00412             only_cumulative = false;
00413             flat = value;
00414             best_loc = p.second;
00415         }
00416     }
00417     return std::make_pair(flat + stack, best_loc);
00418 }
00419 
00420 /*
00421  *
00422  * [special]
00423  * [swarm]
00424  *  name= _ "swarm"
00425  *  name_inactive= _ ""
00426  *  description= _ ""
00427  *  description_inactive= _ ""
00428  *  cumulative=no
00429  *  apply_to=self  #self,opponent,defender,attacker
00430  *  #active_on=defend  .. offense
00431  *
00432  *  attacks_max=4
00433  *  attacks_min=2
00434  *
00435  *  [filter_self] // SUF
00436  *      ...
00437  *  [/filter_self]
00438  *  [filter_opponent] // SUF
00439  *  [filter_attacker] // SUF
00440  *  [filter_defender] // SUF
00441  *  [filter_adjacent] // SAUF
00442  *  [filter_adjacent_location] // SAUF + locs
00443  * [/swarm]
00444  * [/special]
00445  *
00446  */
00447 
00448 namespace {
00449     bool get_special_children(std::vector<const config*>& result, const config& parent,
00450                                const std::string& id, bool just_peeking=false) {
00451         foreach (const config::any_child &sp, parent.all_children_range())
00452         {
00453             if (sp.key == id || sp.cfg["id"] == id) {
00454                 if(just_peeking) {
00455                     return true; // peek succeeded, abort
00456                 } else {
00457                     result.push_back(&sp.cfg);
00458                 }
00459             }
00460         }
00461         return false;
00462     }
00463 }
00464 
00465 bool attack_type::get_special_bool(const std::string& special,bool force) const
00466 {
00467 //  log_scope("get_special_bool");
00468     if (const config &specials = cfg_.child("specials"))
00469     {
00470         std::vector<const config*> list;
00471         if (get_special_children(list, specials, special, force)) return true;
00472         for (std::vector<const config*>::iterator i = list.begin(),
00473              i_end = list.end(); i != i_end; ++i) {
00474             if (special_active(**i, true))
00475                 return true;
00476         }
00477     }
00478     if (force || !other_attack_) return false;
00479     if (const config &specials = other_attack_->cfg_.child("specials"))
00480     {
00481         std::vector<const config*> list;
00482         get_special_children(list, specials, special);
00483         for (std::vector<const config*>::iterator i = list.begin(),
00484              i_end = list.end(); i != i_end; ++i) {
00485             if (other_attack_->special_active(**i, false))
00486                 return true;
00487         }
00488     }
00489     return false;
00490 }
00491 
00492 unit_ability_list attack_type::get_specials(const std::string& special) const
00493 {
00494 //  log_scope("get_specials");
00495     unit_ability_list res;
00496     if (const config &specials = cfg_.child("specials"))
00497     {
00498         foreach (const config &i, specials.child_range(special)) {
00499             if (special_active(i, true))
00500                 res.cfgs.push_back(std::pair<const config *, map_location>
00501                     (&i, attacker_ ? aloc_ : dloc_));
00502         }
00503     }
00504     if (!other_attack_) return res;
00505     if (const config &specials = other_attack_->cfg_.child("specials"))
00506     {
00507         foreach (const config &i, specials.child_range(special)) {
00508             if (other_attack_->special_active(i, false))
00509                 res.cfgs.push_back(std::pair<const config *, map_location>
00510                     (&i, attacker_ ? dloc_ : aloc_));
00511         }
00512     }
00513     return res;
00514 }
00515 std::vector<t_string> attack_type::special_tooltips(bool force) const
00516 {
00517 //  log_scope("special_tooltips");
00518     std::vector<t_string> res;
00519     const config &specials = cfg_.child("specials");
00520     if (!specials) return res;
00521 
00522     foreach (const config::any_child &sp, specials.all_children_range())
00523     {
00524         if (force || special_active(sp.cfg, true)) {
00525             const t_string &name = sp.cfg["name"];
00526             if (!name.empty()) {
00527                 res.push_back(name);
00528                 res.push_back(sp.cfg["description"]);
00529             }
00530         } else {
00531             t_string const &name = sp.cfg["name_inactive"];
00532             if (!name.empty()) {
00533                 res.push_back(name);
00534                 res.push_back(sp.cfg["description_inactive"]);
00535             }
00536         }
00537     }
00538     return res;
00539 }
00540 std::string attack_type::weapon_specials(bool force) const
00541 {
00542 //  log_scope("weapon_specials");
00543     std::string res;
00544     const config &specials = cfg_.child("specials");
00545     if (!specials) return res;
00546 
00547     foreach (const config::any_child &sp, specials.all_children_range())
00548     {
00549         char const *s = force || special_active(sp.cfg, true) ?
00550             "name" : "name_inactive";
00551         std::string const &name = sp.cfg[s];
00552 
00553         if (!name.empty()) {
00554             if (!res.empty()) res += ',';
00555             res += name;
00556         }
00557     }
00558 
00559     return res;
00560 }
00561 
00562 
00563 
00564 /*
00565  *
00566  * cfg: a weapon special WML structure
00567  *
00568  */
00569 bool attack_type::special_active(const config& cfg, bool self) const
00570 {
00571 //  log_scope("special_active");
00572     assert(unitmap_ != NULL);
00573     unit_map::const_iterator att = unitmap_->find(aloc_);
00574     unit_map::const_iterator def = unitmap_->find(dloc_);
00575 
00576     if(self) {
00577         if(!special_affects_self(cfg)) {
00578             return false;
00579         }
00580     } else {
00581         if(!special_affects_opponent(cfg)) {
00582             return false;
00583         }
00584     }
00585 
00586     if(attacker_) {
00587         {
00588             std::string const &active = cfg["active_on"];
00589             if (!active.empty() && active != "offense")
00590                 return false;
00591         }
00592         if (const config &filter_self = cfg.child("filter_self"))
00593         {
00594             if (att == unitmap_->end() ||
00595                 !att->matches_filter(vconfig(filter_self), aloc_))
00596                 return false;
00597             if (const config &filter_weapon = filter_self.child("filter_weapon")) {
00598                 if (!matches_filter(filter_weapon, true))
00599                     return false;
00600             }
00601         }
00602         if (const config &filter_opponent = cfg.child("filter_opponent"))
00603         {
00604             if (def == unitmap_->end() ||
00605                 !def->matches_filter(vconfig(filter_opponent), dloc_))
00606                 return false;
00607             if (const config &filter_weapon = filter_opponent.child("filter_weapon")) {
00608                 if (!other_attack_ ||
00609                     !other_attack_->matches_filter(filter_weapon, true))
00610                     return false;
00611             }
00612         }
00613     } else {
00614         {
00615             std::string const &active = cfg["active_on"];
00616             if (!active.empty() && active != "defense")
00617                 return false;
00618         }
00619         if (const config &filter_self = cfg.child("filter_self"))
00620         {
00621             if (def == unitmap_->end() ||
00622                 !def->matches_filter(vconfig(filter_self), dloc_))
00623                 return false;
00624             if (const config &filter_weapon = filter_self.child("filter_weapon")) {
00625                 if (!matches_filter(filter_weapon, true))
00626                     return false;
00627             }
00628         }
00629         if (const config &filter_opponent = cfg.child("filter_opponent"))
00630         {
00631             if (att == unitmap_->end() ||
00632                 !att->matches_filter(vconfig(filter_opponent), aloc_))
00633                 return false;
00634             if (const config &filter_weapon = filter_opponent.child("filter_weapon")) {
00635                 if (!other_attack_ ||
00636                     !other_attack_->matches_filter(filter_weapon, true))
00637                     return false;
00638             }
00639         }
00640     }
00641     if (const config &filter_attacker = cfg.child("filter_attacker"))
00642     {
00643         if (att == unitmap_->end() ||
00644             !att->matches_filter(vconfig(filter_attacker), aloc_))
00645             return false;
00646         if (const config &filter_weapon = filter_attacker.child("filter_weapon"))
00647         {
00648             if (attacker_) {
00649                 if (!matches_filter(filter_weapon, true))
00650                     return false;
00651             } else {
00652                 if (!other_attack_ ||
00653                     !other_attack_->matches_filter(filter_weapon, true))
00654                     return false;
00655             }
00656         }
00657     }
00658     if (const config &filter_defender = cfg.child("filter_defender"))
00659     {
00660         if (def == unitmap_->end() ||
00661             !def->matches_filter(vconfig(filter_defender), dloc_))
00662             return false;
00663         if (const config &filter_weapon = filter_defender.child("filter_weapon"))
00664         {
00665             if (!attacker_) {
00666                 if(!matches_filter(filter_weapon, true))
00667                     return false;
00668             } else {
00669                 if (!other_attack_ ||
00670                     !other_attack_->matches_filter(filter_weapon, true))
00671                     return false;
00672             }
00673         }
00674     }
00675     map_location adjacent[6];
00676     if(attacker_) {
00677         get_adjacent_tiles(aloc_,adjacent);
00678     } else {
00679         get_adjacent_tiles(dloc_,adjacent);
00680     }
00681 
00682     foreach (const config &i, cfg.child_range("filter_adjacent"))
00683     {
00684         foreach (const std::string &j, utils::split(i["adjacent"]))
00685         {
00686             map_location::DIRECTION index =
00687                 map_location::parse_direction(j);
00688             if (index == map_location::NDIRECTIONS)
00689                 continue;
00690             unit_map::const_iterator unit = unitmap_->find(adjacent[index]);
00691             if (unit == unitmap_->end() ||
00692                 !unit->matches_filter(vconfig(i), unit->get_location()))
00693                 return false;
00694         }
00695     }
00696 
00697     foreach (const config &i, cfg.child_range("filter_adjacent_location"))
00698     {
00699         foreach (const std::string &j, utils::split(i["adjacent"]))
00700         {
00701             map_location::DIRECTION index =
00702                 map_location::parse_direction(j);
00703             if (index == map_location::NDIRECTIONS)
00704                 continue;
00705             terrain_filter adj_filter(vconfig(i), *unitmap_);
00706             if(!adj_filter.match(adjacent[index])) {
00707                 return false;
00708             }
00709         }
00710     }
00711     return true;
00712 }
00713 /*
00714  *
00715  * cfg: a weapon special WML structure
00716  *
00717  */
00718 bool attack_type::special_affects_opponent(const config& cfg) const
00719 {
00720 //  log_scope("special_affects_opponent");
00721     std::string const &apply_to = cfg["apply_to"];
00722     if (apply_to.empty())
00723         return false;
00724     if (apply_to == "both")
00725         return true;
00726     if (apply_to == "opponent")
00727         return true;
00728     if (attacker_ && apply_to == "defender")
00729         return true;
00730     if (!attacker_ && apply_to == "attacker")
00731         return true;
00732     return false;
00733 }
00734 /*
00735  *
00736  * cfg: a weapon special WML structure
00737  *
00738  */
00739 bool attack_type::special_affects_self(const config& cfg) const
00740 {
00741 //  log_scope("special_affects_self");
00742     std::string const &apply_to = cfg["apply_to"];
00743     if (apply_to.empty())
00744         return true;
00745     if (apply_to == "both")
00746         return true;
00747     if (apply_to == "self")
00748         return true;
00749     if (attacker_ && apply_to == "attacker")
00750         return true;
00751     if (!attacker_ && apply_to == "defender")
00752         return true;
00753     return false;
00754 }
00755 void attack_type::set_specials_context(const map_location& aloc,const map_location& dloc,
00756     const unit_map &unitmap, bool attacker, const attack_type *other_attack) const
00757 {
00758     aloc_ = aloc;
00759     dloc_ = dloc;
00760     unitmap_ = &unitmap;
00761     attacker_ = attacker;
00762     other_attack_ = other_attack;
00763 }
00764 
00765 void attack_type::set_specials_context(const map_location& loc, const map_location& dloc, const unit& /*un*/, bool attacker) const
00766 {
00767     aloc_ = loc;
00768     dloc_ = dloc;
00769     unitmap_ = resources::units;
00770     attacker_ = attacker;
00771     other_attack_ = NULL;
00772 }
00773 
00774 
00775 
00776 
00777 namespace unit_abilities
00778 {
00779 
00780 void individual_effect::set(value_modifier t, int val, const config *abil, const map_location &l)
00781 {
00782     type=t;
00783     value=val;
00784     ability=abil;
00785     loc=l;
00786 }
00787 
00788 bool filter_base_matches(const config& cfg, int def)
00789 {
00790     if (const config &apply_filter = cfg.child("filter_base_value")) {
00791         config::attribute_value cond_eq = apply_filter["equals"];
00792         config::attribute_value cond_ne = apply_filter["not_equals"];
00793         config::attribute_value cond_lt = apply_filter["less_than"];
00794         config::attribute_value cond_gt = apply_filter["greater_than"];
00795         config::attribute_value cond_ge = apply_filter["greater_than_equal_to"];
00796         config::attribute_value cond_le = apply_filter["less_than_equal_to"];
00797         return  (cond_eq.empty() || def == cond_eq.to_int()) &&
00798             (cond_ne.empty() || def != cond_ne.to_int()) &&
00799             (cond_lt.empty() || def <  cond_lt.to_int()) &&
00800             (cond_gt.empty() || def >  cond_gt.to_int()) &&
00801             (cond_ge.empty() || def >= cond_ge.to_int()) &&
00802             (cond_le.empty() || def <= cond_le.to_int());
00803     }
00804     return true;
00805 }
00806 
00807 effect::effect(const unit_ability_list& list, int def, bool backstab) :
00808     effect_list_(),
00809     composite_value_(0)
00810 {
00811 
00812     int value_set = def;
00813     bool value_is_set = false;
00814     std::map<std::string,individual_effect> values_add;
00815     std::map<std::string,individual_effect> values_mul;
00816     std::map<std::string,individual_effect> values_div;
00817 
00818     individual_effect set_effect;
00819 
00820     for (std::vector< std::pair<const config *, map_location> >::const_iterator
00821          i = list.cfgs.begin(), i_end = list.cfgs.end(); i != i_end; ++i) {
00822         const config& cfg = (*i->first);
00823         std::string const &effect_id = cfg[cfg["id"].empty() ? "name" : "id"];
00824 
00825         if (!backstab && cfg["backstab"].to_bool())
00826             continue;
00827         if (!filter_base_matches(cfg, def))
00828             continue;
00829 
00830         if (const config::attribute_value *v = cfg.get("value")) {
00831             int value = *v;
00832             bool cumulative = cfg["cumulative"].to_bool();
00833             if (!value_is_set && !cumulative) {
00834                 value_set = value;
00835                 set_effect.set(SET, value, i->first, i->second);
00836             } else {
00837                 if (cumulative) value_set = std::max<int>(value_set, def);
00838                 if (value > value_set) {
00839                     value_set = value;
00840                     set_effect.set(SET, value, i->first, i->second);
00841                 }
00842             }
00843             value_is_set = true;
00844         }
00845 
00846         if (const config::attribute_value *v = cfg.get("add")) {
00847             int add = *v;
00848             std::map<std::string,individual_effect>::iterator add_effect = values_add.find(effect_id);
00849             if(add_effect == values_add.end() || add > add_effect->second.value) {
00850                 values_add[effect_id].set(ADD,add,i->first,i->second);
00851             }
00852         }
00853         if (const config::attribute_value *v = cfg.get("sub")) {
00854             int sub = - *v;
00855             std::map<std::string,individual_effect>::iterator sub_effect = values_add.find(effect_id);
00856             if(sub_effect == values_add.end() || sub > sub_effect->second.value) {
00857                 values_add[effect_id].set(ADD,sub,i->first,i->second);
00858             }
00859         }
00860         if (const config::attribute_value *v = cfg.get("multiply")) {
00861             int multiply = int(v->to_double() * 100);
00862             std::map<std::string,individual_effect>::iterator mul_effect = values_mul.find(effect_id);
00863             if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
00864                 values_mul[effect_id].set(MUL,multiply,i->first,i->second);
00865             }
00866         }
00867         if (const config::attribute_value *v = cfg.get("divide")) {
00868             if (*v == 0) {
00869                 ERR_NG << "division by zero with divide= in ability/weapon special " << effect_id << "\n";
00870             }
00871             else {
00872                 int divide = int(v->to_double() * 100);
00873                 std::map<std::string,individual_effect>::iterator div_effect = values_div.find(effect_id);
00874                 if(div_effect == values_div.end() || divide > div_effect->second.value) {
00875                     values_div[effect_id].set(DIV,divide,i->first,i->second);
00876                 }
00877             }
00878         }
00879     }
00880 
00881     if(value_is_set && set_effect.type != NOT_USED) {
00882         effect_list_.push_back(set_effect);
00883     }
00884 
00885     /* Do multiplication with floating point values rather than integers
00886      * We want two places of precision for each multiplier
00887      * Using integers multiplied by 100 to keep precision causes overflow
00888      *   after 3-4 abilities for 32-bit values and ~8 for 64-bit
00889      * Avoiding the overflow by dividing after each step introduces rounding errors
00890      *   that may vary depending on the order effects are applied
00891      * As the final values are likely <1000 (always true for mainline), loss of less significant digits is not an issue
00892      */
00893     double multiplier = 1.0;
00894     double divisor = 1.0;
00895     std::map<std::string,individual_effect>::const_iterator e, e_end;
00896     for (e = values_mul.begin(), e_end = values_mul.end(); e != e_end; ++e) {
00897         multiplier *= e->second.value/100.0;
00898         effect_list_.push_back(e->second);
00899     }
00900     for (e = values_div.begin(), e_end = values_div.end(); e != e_end; ++e) {
00901         divisor *= e->second.value/100.0;
00902         effect_list_.push_back(e->second);
00903     }
00904     int addition = 0;
00905     for (e = values_add.begin(), e_end = values_add.end(); e != e_end; ++e) {
00906         addition += e->second.value;
00907         effect_list_.push_back(e->second);
00908     }
00909 
00910     composite_value_ = int((value_set + addition) * multiplier / divisor);
00911 }
00912 
00913 } // end namespace unit_abilities
00914 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:13 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs