unit_types.cpp

Go to the documentation of this file.
00001 /* $Id: unit_types.cpp 54123 2012-05-08 14:00:27Z anonymissimus $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.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  *  Handle unit-type specific attributes, animations, advancement.
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include "unit_types.hpp"
00024 
00025 #include "foreach.hpp"
00026 #include "game_config.hpp"
00027 #include "gettext.hpp"
00028 #include "loadscreen.hpp"
00029 #include "log.hpp"
00030 #include "map.hpp"
00031 
00032 
00033 static lg::log_domain log_config("config");
00034 #define ERR_CF LOG_STREAM(err, log_config)
00035 #define WRN_CF LOG_STREAM(warn, log_config)
00036 #define LOG_CONFIG LOG_STREAM(info, log_config)
00037 #define DBG_CF LOG_STREAM(debug, log_config)
00038 
00039 static lg::log_domain log_unit("unit");
00040 #define DBG_UT LOG_STREAM(debug, log_unit)
00041 
00042 attack_type::attack_type(const config& cfg) :
00043     aloc_(),
00044     dloc_(),
00045     attacker_(false),
00046     unitmap_(NULL),
00047     other_attack_(NULL),
00048     cfg_(cfg),
00049     description_(cfg["description"].t_str()),
00050     id_(cfg["name"]),
00051     type_(cfg["type"]),
00052     icon_(cfg["icon"]),
00053     range_(cfg["range"]),
00054     damage_(cfg["damage"]),
00055     num_attacks_(cfg["number"]),
00056     attack_weight_(cfg["attack_weight"].to_double(1.0)),
00057     defense_weight_(cfg["defense_weight"].to_double(1.0)),
00058     accuracy_(cfg["accuracy"]),
00059     parry_(cfg["parry"])
00060 
00061 {
00062     if (description_.empty())
00063         description_ = egettext(id_.c_str());
00064 
00065     if(icon_.empty()){
00066         if (id_ != "")
00067             icon_ = "attacks/" + id_ + ".png";
00068         else
00069             icon_ = "attacks/blank-attack.png";
00070     }
00071 }
00072 
00073 attack_type::~attack_type()
00074 {
00075 }
00076 
00077 std::string attack_type::accuracy_parry_description() const
00078 {
00079     if(accuracy_ == 0 && parry_ == 0) {
00080         return "";
00081     }
00082 
00083     std::ostringstream s;
00084     s << utils::signed_percent(accuracy_);
00085 
00086     if(parry_ != 0) {
00087         s << "/" << utils::signed_percent(parry_);
00088     }
00089 
00090     return s.str();
00091 }
00092 
00093 bool attack_type::matches_filter(const config& cfg,bool self) const
00094 {
00095     const std::vector<std::string>& filter_range = utils::split(cfg["range"]);
00096     const std::string& filter_damage = cfg["damage"];
00097     const std::vector<std::string> filter_name = utils::split(cfg["name"]);
00098     const std::vector<std::string> filter_type = utils::split(cfg["type"]);
00099     const std::string filter_special = cfg["special"];
00100 
00101     if(filter_range.empty() == false && std::find(filter_range.begin(),filter_range.end(),range()) == filter_range.end())
00102             return false;
00103 
00104     if(filter_damage.empty() == false && !in_ranges(damage(), utils::parse_ranges(filter_damage))) {
00105         return false;
00106     }
00107 
00108     if(filter_name.empty() == false && std::find(filter_name.begin(),filter_name.end(),id()) == filter_name.end())
00109         return false;
00110 
00111     if(filter_type.empty() == false && std::find(filter_type.begin(),filter_type.end(),type()) == filter_type.end())
00112         return false;
00113 
00114     if(!self && filter_special.empty() == false && !get_special_bool(filter_special,true))
00115         return false;
00116 
00117     return true;
00118 }
00119 
00120 bool attack_type::apply_modification(const config& cfg,std::string* description)
00121 {
00122     if(!matches_filter(cfg,0))
00123         return false;
00124 
00125     const std::string& set_name = cfg["set_name"];
00126     const t_string& set_desc = cfg["set_description"];
00127     const std::string& set_type = cfg["set_type"];
00128     const std::string& del_specials = cfg["remove_specials"];
00129     const config &set_specials = cfg.child("set_specials");
00130     const std::string& increase_damage = cfg["increase_damage"];
00131     const std::string& increase_attacks = cfg["increase_attacks"];
00132     const std::string& set_attack_weight = cfg["attack_weight"];
00133     const std::string& set_defense_weight = cfg["defense_weight"];
00134     const std::string& increase_accuracy = cfg["increase_accuracy"];
00135     const std::string& increase_parry = cfg["increase_parry"];
00136 
00137     std::stringstream desc;
00138 
00139     if(set_name.empty() == false) {
00140         id_ = set_name;
00141         cfg_["name"] = id_;
00142     }
00143 
00144     if(set_desc.empty() == false) {
00145         description_ = set_desc;
00146         cfg_["description"] = description_;
00147     }
00148 
00149     if(set_type.empty() == false) {
00150         type_ = set_type;
00151         cfg_["type"] = type_;
00152     }
00153 
00154     if(del_specials.empty() == false) {
00155         const std::vector<std::string>& dsl = utils::split(del_specials);
00156         if (config &specials = cfg_.child("specials"))
00157         {
00158             config new_specials;
00159             foreach (const config::any_child &vp, specials.all_children_range()) {
00160                 std::vector<std::string>::const_iterator found_id =
00161                     std::find(dsl.begin(), dsl.end(), vp.cfg["id"].str());
00162                 if (found_id == dsl.end()) {
00163                     new_specials.add_child(vp.key, vp.cfg);
00164                 }
00165             }
00166             cfg_.clear_children("specials");
00167             cfg_.add_child("specials",new_specials);
00168         }
00169     }
00170 
00171     if (set_specials) {
00172         const std::string &mode = set_specials["mode"];
00173         if (mode != "append") {
00174             cfg_.clear_children("specials");
00175         }
00176         config &new_specials = cfg_.child_or_add("specials");
00177         foreach (const config::any_child &value, set_specials.all_children_range()) {
00178             new_specials.add_child(value.key, value.cfg);
00179         }
00180     }
00181 
00182     if(increase_damage.empty() == false) {
00183         damage_ = utils::apply_modifier(damage_, increase_damage, 0);
00184         if (damage_ < 0) {
00185             damage_ = 0;
00186         }
00187         cfg_["damage"] = damage_;
00188 
00189         if(description != NULL) {
00190             int inc_damage = lexical_cast<int>(increase_damage);
00191             desc << utils::signed_value(inc_damage) << " "
00192                  << _n("damage","damage", inc_damage);
00193         }
00194     }
00195 
00196     if(increase_attacks.empty() == false) {
00197         num_attacks_ = utils::apply_modifier(num_attacks_, increase_attacks, 1);
00198         cfg_["number"] = num_attacks_;
00199 
00200         if(description != NULL) {
00201             int inc_attacks = lexical_cast<int>(increase_attacks);
00202             desc << utils::signed_value(inc_attacks) << " "
00203                  << _n("strike", "strikes", inc_attacks);
00204         }
00205     }
00206 
00207     if(increase_accuracy.empty() == false) {
00208         accuracy_ = utils::apply_modifier(accuracy_, increase_accuracy, 1);
00209         cfg_["accuracy"] = accuracy_;
00210 
00211         if(description != NULL) {
00212             int inc_acc = lexical_cast<int>(increase_accuracy);
00213             // Help xgettext with a directive to recognize the string as a non C printf-like string
00214             // xgettext:no-c-format
00215             desc << utils::signed_value(inc_acc) << _("% accuracy");
00216         }
00217     }
00218 
00219     if(increase_parry.empty() == false) {
00220         parry_ = utils::apply_modifier(parry_, increase_parry, 1);
00221         cfg_["parry"] = parry_;
00222 
00223         if(description != NULL) {
00224             int inc_parry = lexical_cast<int>(increase_parry);
00225             // xgettext:no-c-format
00226             desc << utils::signed_value(inc_parry) << _("% parry");
00227         }
00228     }
00229 
00230     if(set_attack_weight.empty() == false) {
00231         attack_weight_ = lexical_cast_default<double>(set_attack_weight,1.0);
00232         cfg_["attack_weight"] = attack_weight_;
00233     }
00234 
00235     if(set_defense_weight.empty() == false) {
00236         defense_weight_ = lexical_cast_default<double>(set_defense_weight,1.0);
00237         cfg_["defense_weight"] = defense_weight_;
00238     }
00239 
00240     if(description != NULL) {
00241         *description = desc.str();
00242     }
00243 
00244     return true;
00245 }
00246 
00247 // Same as above, except only update the descriptions
00248 bool attack_type::describe_modification(const config& cfg,std::string* description)
00249 {
00250     if(!matches_filter(cfg,0))
00251         return false;
00252 
00253     const std::string& increase_damage = cfg["increase_damage"];
00254     const std::string& increase_attacks = cfg["increase_attacks"];
00255 
00256     std::stringstream desc;
00257 
00258     if(increase_damage.empty() == false) {
00259         if(description != NULL) {
00260             int inc_damage = lexical_cast<int>(increase_damage);
00261             desc << utils::signed_value(inc_damage) << " "
00262                  << _n("damage","damage", inc_damage);
00263         }
00264     }
00265 
00266     if(increase_attacks.empty() == false) {
00267         if(description != NULL) {
00268             int inc_attacks = lexical_cast<int>(increase_attacks);
00269             desc << utils::signed_value(inc_attacks) << " "
00270                  << _n("strike", "strikes", inc_attacks);
00271         }
00272     }
00273 
00274     if(description != NULL) {
00275         *description = desc.str();
00276     }
00277 
00278     return true;
00279 }
00280 
00281 unit_movement_type::unit_movement_type(const config& cfg, const unit_movement_type* parent) :
00282     moveCosts_(),
00283     visionCosts_(),
00284     jammingCosts_(),
00285     defenseMods_(),
00286     parent_(parent),
00287     cfg_()
00288 {
00289     //the unit_type give its whole cfg, we don't need all that.
00290     //so we filter to keep only keys related to movement_type
00291     //FIXME: This helps but it's still not clean, both cfg use a "name" key
00292 
00293     const t_string& name = cfg["name"];
00294     if (!name.empty())
00295         cfg_["name"]= cfg["name"];
00296 
00297     const t_string& flies = cfg["flies"];
00298     if (!flies.empty())
00299         cfg_["flies"]= cfg["flies"];
00300 
00301     if (const config &movement_costs = cfg.child("movement_costs"))
00302         cfg_.add_child("movement_costs", movement_costs);
00303 
00304     if (const config &vision_costs = cfg.child("vision_costs"))
00305         cfg_.add_child("vision_costs", vision_costs);
00306 
00307     if (const config &jamming_costs = cfg.child("jamming_costs"))
00308         cfg_.add_child("jamming_costs", jamming_costs);
00309 
00310     if (const config &defense = cfg.child("defense"))
00311         cfg_.add_child("defense", defense);
00312 
00313     if (const config &resistance = cfg.child("resistance"))
00314         cfg_.add_child("resistance", resistance);
00315 }
00316 
00317 unit_movement_type::unit_movement_type(): moveCosts_(), visionCosts_(), jammingCosts_(), defenseMods_(), parent_(NULL), cfg_()
00318 {}
00319 
00320 std::string unit_movement_type::name() const
00321 {
00322     if (!cfg_.has_attribute("name") && parent_)
00323         return parent_->name();
00324     else
00325         return cfg_["name"];
00326 }
00327 
00328 int unit_movement_type::resistance_against(const attack_type& attack) const
00329 {
00330     bool result_found = false;
00331     int res = 100;
00332 
00333     if (const config &resistance = cfg_.child("resistance"))
00334     {
00335         if (const::config::attribute_value *val = resistance.get(attack.type())) {
00336             res = *val;
00337             result_found = true;
00338         }
00339     }
00340 
00341     if(!result_found && parent_ != NULL) {
00342         res = parent_->resistance_against(attack);
00343     }
00344 
00345     return res;
00346 }
00347 
00348 utils::string_map unit_movement_type::damage_table() const
00349 {
00350     utils::string_map res;
00351     if(parent_ != NULL)
00352         res = parent_->damage_table();
00353 
00354     if (const config &resistance = cfg_.child("resistance"))
00355     {
00356         foreach (const config::attribute &i, resistance.attribute_range()) {
00357             res[i.first] = i.second;
00358         }
00359     }
00360 
00361     return res;
00362 }
00363 
00364 bool unit_movement_type::is_flying() const
00365 {
00366     if (!cfg_.has_attribute("flies") && parent_)
00367         return parent_->is_flying();
00368 
00369     return cfg_["flies"].to_bool();
00370 }
00371 
00372 int movement_cost_internal(std::map<t_translation::t_terrain, int>& move_costs,
00373         const config& cfg, const unit_movement_type* parent,
00374         const gamemap& map, t_translation::t_terrain terrain, int recurse_count)
00375 {
00376     const int impassable = unit_movement_type::UNREACHABLE;
00377 
00378     const std::map<t_translation::t_terrain, int>::const_iterator i = move_costs.find(terrain);
00379 
00380     if (i != move_costs.end()) return i->second;
00381 
00382     // If this is an alias, then select the best of all underlying terrains.
00383     const t_translation::t_list& underlying = map.underlying_mvt_terrain(terrain);
00384     assert(!underlying.empty());
00385 
00386     if (underlying.size() != 1 || underlying.front() != terrain) {
00387         bool revert = (underlying.front() == t_translation::MINUS ? true : false);
00388         if (recurse_count >= 100) {
00389             ERR_CF << "infinite movement_cost recursion: "
00390                 << t_translation::write_terrain_code(terrain)
00391                 << " depth " << recurse_count << "\n";
00392             move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, impassable));
00393             return impassable;
00394         }
00395 
00396         int ret_value = revert ? 0 : impassable;
00397         for (t_translation::t_list::const_iterator i = underlying.begin();
00398                 i != underlying.end(); ++i)
00399         {
00400             if (*i == t_translation::PLUS) {
00401                 revert = false;
00402                 continue;
00403             } else if (*i == t_translation::MINUS) {
00404                 revert = true;
00405                 continue;
00406             }
00407             const int value = movement_cost_internal(move_costs, cfg,
00408                     parent, map, *i, recurse_count + 1);
00409 
00410             if (value < ret_value && !revert) {
00411                 ret_value = value;
00412             } else if (value > ret_value && revert) {
00413                 ret_value = value;
00414             }
00415         }
00416 
00417         move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, ret_value));
00418         return ret_value;
00419     }
00420 
00421     bool result_found = false;
00422     int res = impassable;
00423 
00424     if (cfg) {
00425         if (underlying.size() != 1) {
00426             ERR_CF << "Terrain '" << terrain << "' has "
00427                 << underlying.size() << " underlying names - 0 expected.\n";
00428 
00429             move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, impassable));
00430             return impassable;
00431         }
00432 
00433         const std::string& id = map.get_terrain_info(underlying.front()).id();
00434         if (const config::attribute_value *val = cfg.get(id)) {
00435             res = *val;
00436             result_found = true;
00437         }
00438     }
00439 
00440     if (!result_found && parent != NULL) {
00441         res = parent->movement_cost(map, terrain);
00442     }
00443 
00444     if (res <= 0) {
00445         WRN_CF << "Terrain '" << terrain << "' has a movement cost of '"
00446             << res << "' which is '<= 0'; resetting to 1.\n";
00447         res = 1;
00448     }
00449 
00450     move_costs.insert(std::pair<t_translation::t_terrain, int>(terrain, res));
00451     return res;
00452 }
00453 
00454 const defense_range &defense_range_modifier_internal(defense_cache &defense_mods,
00455         const config& cfg, const unit_movement_type* parent,
00456         const gamemap& map, t_translation::t_terrain terrain, int recurse_count)
00457 {
00458     defense_range dummy = { 0, 100 };
00459     std::pair<defense_cache::iterator, bool> ib =
00460         defense_mods.insert(defense_cache::value_type(terrain, dummy));
00461     if (!ib.second) return ib.first->second;
00462 
00463     defense_range &res = ib.first->second;
00464 
00465     // If this is an alias, then select the best of all underlying terrains.
00466     const t_translation::t_list& underlying = map.underlying_def_terrain(terrain);
00467     assert(!underlying.empty());
00468 
00469     if (underlying.size() != 1 || underlying.front() != terrain) {
00470         bool revert = underlying.front() == t_translation::MINUS;
00471         if(recurse_count >= 90) {
00472             ERR_CF << "infinite defense_modifier recursion: "
00473                 << t_translation::write_terrain_code(terrain)
00474                 << " depth " << recurse_count << "\n";
00475         }
00476         if (recurse_count >= 100) {
00477             return res;
00478         }
00479 
00480         if (revert) {
00481             res.max_ = 0;
00482             res.min_ = 100;
00483         }
00484 
00485         for (t_translation::t_list::const_iterator i = underlying.begin();
00486                 i != underlying.end(); ++i) {
00487 
00488             if (*i == t_translation::PLUS) {
00489                 revert = false;
00490                 continue;
00491             } else if (*i == t_translation::MINUS) {
00492                 revert = true;
00493                 continue;
00494             }
00495             const defense_range &inh = defense_range_modifier_internal
00496                 (defense_mods, cfg, parent, map, *i, recurse_count + 1);
00497 
00498             if (!revert) {
00499                 if (inh.max_ < res.max_) res.max_ = inh.max_;
00500                 if (inh.min_ > res.min_) res.min_ = inh.min_;
00501             } else {
00502                 if (inh.max_ > res.max_) res.max_ = inh.max_;
00503                 if (inh.min_ < res.min_) res.min_ = inh.min_;
00504             }
00505         }
00506 
00507         goto check;
00508     }
00509 
00510     if (const config& defense = cfg.child("defense"))
00511     {
00512         const std::string& id = map.get_terrain_info(underlying.front()).id();
00513         if (const config::attribute_value *val = defense.get(id)) {
00514             int def = *val;
00515             if (def >= 0) res.max_ = def;
00516             else res.max_ = res.min_ = -def;
00517             goto check;
00518         }
00519     }
00520 
00521     if (parent) {
00522         /* Assign to the reference res to put the value in the defense_cache. */
00523         res = parent->defense_range_modifier(map, terrain);
00524         return res;
00525     }
00526 
00527     check:
00528 
00529     if (res.min_ < 0) {
00530         WRN_CF << "Defense '" << res.min_ << "' is '< 0' reset to 0 (100% defense).\n";
00531         res.min_ = 0;
00532     }
00533     if (res.max_ > 100) {
00534         WRN_CF << "Defense '" << res.max_ << "' is '> 100' reset to 100 (0% defense).\n";
00535         res.max_ = 100;
00536     }
00537 
00538     return res;
00539 }
00540 
00541 int defense_modifier_internal(defense_cache &defense_mods,
00542     const config &cfg, const unit_movement_type *parent,
00543     const gamemap &map, t_translation::t_terrain terrain, int recurse_count)
00544 {
00545     const defense_range &def = defense_range_modifier_internal(defense_mods,
00546         cfg, parent, map, terrain, recurse_count);
00547     return (std::max)(def.max_, def.min_);
00548 }
00549 
00550 static const unit_race& dummy_race(){
00551     static unit_race ur;
00552     return ur;
00553 }
00554 
00555 
00556 #ifdef _MSC_VER
00557 #pragma warning(push)
00558 //silence "elements of array will be default initialized" warnings
00559 #pragma warning(disable:4351)
00560 #endif
00561 
00562 #ifdef _MSC_VER
00563 #pragma warning(pop)
00564 #endif
00565 
00566 unit_type::unit_type(const unit_type& o) :
00567     cfg_(o.cfg_),
00568     id_(o.id_),
00569     type_name_(o.type_name_),
00570     description_(o.description_),
00571     hitpoints_(o.hitpoints_),
00572     level_(o.level_),
00573     movement_(o.movement_),
00574     vision_(o.vision_),
00575     jamming_(o.jamming_),
00576     max_attacks_(o.max_attacks_),
00577     cost_(o.cost_),
00578     usage_(o.usage_),
00579     undead_variation_(o.undead_variation_),
00580     image_(o.image_),
00581     icon_(o.icon_),
00582     small_profile_(o.small_profile_),
00583     big_profile_(o.big_profile_),
00584     flag_rgb_(o.flag_rgb_),
00585     num_traits_(o.num_traits_),
00586     variations_(o.variations_),
00587     race_(o.race_),
00588     alpha_(o.alpha_),
00589     abilities_(o.abilities_),
00590     adv_abilities_(o.adv_abilities_),
00591     ability_tooltips_(o.ability_tooltips_),
00592     adv_ability_tooltips_(o.adv_ability_tooltips_),
00593     zoc_(o.zoc_),
00594     hide_help_(o.hide_help_),
00595     advances_to_(o.advances_to_),
00596     experience_needed_(o.experience_needed_),
00597     in_advancefrom_(o.in_advancefrom_),
00598     alignment_(o.alignment_),
00599     movementType_(o.movementType_),
00600     possibleTraits_(o.possibleTraits_),
00601     genders_(o.genders_),
00602     animations_(o.animations_),
00603     build_status_(o.build_status_),
00604     portraits_(o.portraits_)
00605 {
00606     gender_types_[0] = o.gender_types_[0] != NULL ? new unit_type(*o.gender_types_[0]) : NULL;
00607     gender_types_[1] = o.gender_types_[1] != NULL ? new unit_type(*o.gender_types_[1]) : NULL;
00608 
00609     for(variations_map::const_iterator i = o.variations_.begin(); i != o.variations_.end(); ++i) {
00610         variations_[i->first] = new unit_type(*i->second);
00611     }
00612 }
00613 
00614 
00615 unit_type::unit_type(config &cfg) :
00616     cfg_(cfg),
00617     id_(cfg["id"]),
00618     type_name_(cfg["name"].t_str()),
00619     description_(),
00620     hitpoints_(0),
00621     level_(0),
00622     movement_(0),
00623     vision_(-1),
00624     jamming_(0),
00625     max_attacks_(0),
00626     cost_(0),
00627     usage_(),
00628     undead_variation_(),
00629     image_(cfg["image"].str()),
00630     icon_(),
00631     small_profile_(),
00632     big_profile_(),
00633     flag_rgb_(),
00634     num_traits_(0),
00635     gender_types_(),
00636     variations_(),
00637     race_(&dummy_race()),
00638     alpha_(),
00639     abilities_(),
00640     adv_abilities_(),
00641     ability_tooltips_(),
00642     adv_ability_tooltips_(),
00643     zoc_(false),
00644     hide_help_(false),
00645     advances_to_(),
00646     experience_needed_(0),
00647     in_advancefrom_(false),
00648     alignment_(),
00649     movementType_(),
00650     possibleTraits_(),
00651     genders_(),
00652     animations_(),
00653     build_status_(NOT_BUILT),
00654     portraits_()
00655 {
00656     gender_types_[0] = NULL;
00657     gender_types_[1] = NULL;
00658 }
00659 
00660 unit_type::~unit_type()
00661 {
00662     delete gender_types_[0];
00663     delete gender_types_[1];
00664 
00665     for(variations_map::iterator i = variations_.begin(); i != variations_.end(); ++i) {
00666         delete i->second;
00667     }
00668 }
00669 
00670 void unit_type::build_full(const movement_type_map &mv_types,
00671     const race_map &races, const config::const_child_itors &traits)
00672 {
00673     if (build_status_ == NOT_BUILT || build_status_ == CREATED)
00674         build_help_index(mv_types, races, traits);
00675 
00676     config &cfg = cfg_;
00677 
00678     movementType_ = unit_movement_type(cfg);
00679     alpha_ = ftofxp(1.0);
00680 
00681     foreach (const config &t, traits)
00682     {
00683         possibleTraits_.add_child("trait", t);
00684     }
00685     foreach (config &var_cfg, cfg.child_range("variation"))
00686     {
00687         if (var_cfg["inherit"].to_bool()) {
00688             config nvar_cfg(cfg);
00689             nvar_cfg.merge_with(var_cfg);
00690             nvar_cfg.clear_children("variation");
00691             var_cfg.swap(nvar_cfg);
00692         }
00693         unit_type *ut = new unit_type(var_cfg);
00694         ut->build_full(mv_types, races, traits);
00695         variations_.insert(std::make_pair(var_cfg["variation_name"], ut));
00696     }
00697 
00698     for (int i = 0; i < 2; ++i) {
00699         if (gender_types_[i])
00700             gender_types_[i]->build_full(mv_types, races, traits);
00701     }
00702 
00703     const std::string& align = cfg["alignment"];
00704     if(align == "lawful")
00705         alignment_ = LAWFUL;
00706     else if(align == "chaotic")
00707         alignment_ = CHAOTIC;
00708     else if(align == "neutral")
00709         alignment_ = NEUTRAL;
00710     else if(align == "liminal")
00711         alignment_ = LIMINAL;
00712     else {
00713         ERR_CF << "Invalid alignment found for " << id() << ": '" << align << "'\n";
00714         alignment_ = NEUTRAL;
00715     }
00716 
00717     if (race_ != &dummy_race())
00718     {
00719         if (!race_->uses_global_traits()) {
00720             possibleTraits_.clear();
00721         }
00722         if (cfg["ignore_race_traits"].to_bool()) {
00723             possibleTraits_.clear();
00724         } else {
00725             foreach (const config &t, race_->additional_traits())
00726             {
00727                 if (alignment_ != NEUTRAL || t["id"] != "fearless")
00728                     possibleTraits_.add_child("trait", t);
00729             }
00730         }
00731         if (undead_variation_.empty()) {
00732             undead_variation_ = race_->undead_variation();
00733         }
00734     }
00735 
00736     // Insert any traits that are just for this unit type
00737     foreach (const config &trait, cfg.child_range("trait"))
00738     {
00739         possibleTraits_.add_child("trait", trait);
00740     }
00741 
00742     zoc_ = cfg["zoc"].to_bool(level_ > 0);
00743 
00744     const std::string& alpha_blend = cfg["alpha"];
00745     if(alpha_blend.empty() == false) {
00746         alpha_ = ftofxp(atof(alpha_blend.c_str()));
00747     }
00748 
00749     const std::string& move_type = cfg["movement_type"];
00750 
00751     const movement_type_map::const_iterator it = mv_types.find(move_type);
00752 
00753     if(it != mv_types.end()) {
00754         DBG_UT << "setting parent for movement_type " << move_type << "\n";
00755         movementType_.set_parent(&(it->second));
00756     }
00757     else{
00758         DBG_UT << "no parent found for movement_type " << move_type << "\n";
00759     }
00760 
00761     flag_rgb_ = cfg["flag_rgb"].str();
00762     game_config::add_color_info(cfg);
00763 
00764 
00765     foreach (const config &portrait, cfg_.child_range("portrait")) {
00766         portraits_.push_back(tportrait(portrait));
00767     }
00768 
00769     // Deprecation messages, only seen when unit is parsed for the first time.
00770 
00771     build_status_ = FULL;
00772 }
00773 
00774 void unit_type::build_help_index(const movement_type_map &mv_types,
00775     const race_map &races, const config::const_child_itors &traits)
00776 {
00777     if (build_status_ == NOT_BUILT)
00778         build_created(mv_types, races, traits);
00779 
00780     const config &cfg = cfg_;
00781 
00782     type_name_ = cfg_["name"];
00783     description_ = cfg_["description"];
00784     hitpoints_ = cfg["hitpoints"].to_int(1);
00785     level_ = cfg["level"];
00786     movement_ = cfg["movement"].to_int(1);
00787     vision_ = cfg["vision"].to_int(-1);
00788     jamming_ = cfg["jamming"].to_int(0);
00789     max_attacks_ = cfg["attacks"].to_int(1);
00790     cost_ = cfg["cost"].to_int(1);
00791     usage_ = cfg_["usage"].str();
00792     undead_variation_ = cfg_["undead_variation"].str();
00793     image_ = cfg_["image"].str();
00794     icon_ = cfg_["image_icon"].str();
00795     small_profile_ = cfg_["small_profile"].str();
00796     big_profile_ = cfg_["profile"].str();
00797     adjust_profile(small_profile_, big_profile_, image_);
00798 
00799     for (int i = 0; i < 2; ++i) {
00800         if (gender_types_[i])
00801             gender_types_[i]->build_help_index(mv_types, races, traits);
00802     }
00803 
00804     const race_map::const_iterator race_it = races.find(cfg["race"]);
00805     if(race_it != races.end()) {
00806         race_ = &race_it->second;
00807     } else {
00808         race_ = &dummy_race();
00809     }
00810 
00811     // if num_traits is not defined, we use the num_traits from race
00812     num_traits_ = cfg["num_traits"].to_int(race_->num_traits());
00813 
00814     const std::vector<std::string> genders = utils::split(cfg["gender"]);
00815     for(std::vector<std::string>::const_iterator g = genders.begin(); g != genders.end(); ++g) {
00816         genders_.push_back(string_gender(*g));
00817     }
00818     if(genders_.empty()) {
00819         genders_.push_back(unit_race::MALE);
00820     }
00821 
00822     if (const config &abil_cfg = cfg.child("abilities"))
00823     {
00824         foreach (const config::any_child &ab, abil_cfg.all_children_range()) {
00825             const config::attribute_value &name = ab.cfg["name"];
00826             if (!name.empty()) {
00827                 abilities_.push_back(name.t_str());
00828                 ability_tooltips_.push_back( ab.cfg["description"].t_str() );
00829             }
00830         }
00831     }
00832 
00833     foreach (const config &adv, cfg.child_range("advancement"))
00834     {
00835         foreach (const config &effect, adv.child_range("effect"))
00836         {
00837             const config &abil_cfg = effect.child("abilities");
00838             if (!abil_cfg || effect["apply_to"] != "new_ability") {
00839                 continue;
00840             }
00841             foreach (const config::any_child &ab, abil_cfg.all_children_range()) {
00842                 const config::attribute_value &name = ab.cfg["name"];
00843                 if (!name.empty()) {
00844                     adv_abilities_.push_back(name.t_str());
00845                     adv_ability_tooltips_.push_back( ab.cfg["description"].t_str() );
00846                 }
00847             }
00848         }
00849     }
00850 
00851     hide_help_= cfg["hide_help"].to_bool();
00852 
00853     build_status_ = HELP_INDEX;
00854 }
00855 
00856 void unit_type::build_created(const movement_type_map &mv_types,
00857     const race_map &races, const config::const_child_itors &traits)
00858 {
00859     gender_types_[0] = NULL;
00860     gender_types_[1] = NULL;
00861 
00862     config &cfg = cfg_;
00863 
00864     if (config &male_cfg = cfg.child("male"))
00865     {
00866         if (male_cfg["inherit"].to_bool(true)) {
00867             config m_cfg(cfg);
00868             m_cfg.merge_with(male_cfg);
00869             male_cfg.swap(m_cfg);
00870         }
00871         male_cfg.clear_children("male");
00872         male_cfg.clear_children("female");
00873         gender_types_[0] = new unit_type(male_cfg);
00874     }
00875 
00876     if (config &female_cfg = cfg.child("female"))
00877     {
00878         if (female_cfg["inherit"].to_bool(true)) {
00879             config f_cfg(cfg);
00880             f_cfg.merge_with(female_cfg);
00881             female_cfg.swap(f_cfg);
00882         }
00883         female_cfg.clear_children("male");
00884         female_cfg.clear_children("female");
00885         gender_types_[1] = new unit_type(female_cfg);
00886     }
00887 
00888     for (int i = 0; i < 2; ++i) {
00889         if (gender_types_[i])
00890             gender_types_[i]->build_created(mv_types, races, traits);
00891     }
00892 
00893     const std::string& advances_to_val = cfg["advances_to"];
00894     if(advances_to_val != "null" && advances_to_val != "")
00895         advances_to_ = utils::split(advances_to_val);
00896     DBG_UT << "unit_type '" << id_ << "' advances to : " << advances_to_val << "\n";
00897 
00898     experience_needed_ = cfg["experience"].to_int(500);
00899 
00900     build_status_ = CREATED;
00901 }
00902 
00903 const unit_type& unit_type::get_gender_unit_type(std::string gender) const
00904 {
00905     if (gender == "female") return get_gender_unit_type(unit_race::FEMALE);
00906     else if (gender == "male") return get_gender_unit_type(unit_race::MALE);
00907     else return *this;
00908 }
00909 
00910 const unit_type& unit_type::get_gender_unit_type(unit_race::GENDER gender) const
00911 {
00912     const size_t i = gender;
00913     if(i < sizeof(gender_types_)/sizeof(*gender_types_)
00914     && gender_types_[i] != NULL) {
00915         return *gender_types_[i];
00916     }
00917 
00918     return *this;
00919 }
00920 
00921 const unit_type& unit_type::get_variation(const std::string& name) const
00922 {
00923     const variations_map::const_iterator i = variations_.find(name);
00924     if(i != variations_.end()) {
00925         return *i->second;
00926     } else {
00927         return *this;
00928     }
00929 }
00930 
00931 const t_string unit_type::unit_description() const
00932 {
00933     if(description_.empty()) {
00934         return (_("No description available."));
00935     } else {
00936         return description_;
00937     }
00938 }
00939 
00940 const std::vector<unit_animation>& unit_type::animations() const {
00941     if (animations_.empty()) {
00942         unit_animation::fill_initial_animations(animations_,cfg_);
00943     }
00944 
00945     return animations_;
00946 }
00947 
00948 std::vector<attack_type> unit_type::attacks() const
00949 {
00950     std::vector<attack_type> res;
00951     foreach (const config &att, cfg_.child_range("attack")) {
00952         res.push_back(attack_type(att));
00953     }
00954 
00955     return res;
00956 }
00957 
00958 
00959 namespace {
00960     int experience_modifier = 100;
00961 }
00962 
00963 unit_type::experience_accelerator::experience_accelerator(int modifier) : old_value_(experience_modifier)
00964 {
00965     experience_modifier = modifier;
00966 }
00967 
00968 unit_type::experience_accelerator::~experience_accelerator()
00969 {
00970     experience_modifier = old_value_;
00971 }
00972 
00973 int unit_type::experience_accelerator::get_acceleration()
00974 {
00975     return experience_modifier;
00976 }
00977 
00978 int unit_type::experience_needed(bool with_acceleration) const
00979 {
00980     if(with_acceleration) {
00981         int exp = (experience_needed_ * experience_modifier + 50) /100;
00982         if(exp < 1) exp = 1;
00983         return exp;
00984     }
00985     return experience_needed_;
00986 }
00987 
00988 const char* unit_type::alignment_description(unit_type::ALIGNMENT align, unit_race::GENDER gender)
00989 {
00990     static const char* aligns[] = { N_("lawful"), N_("neutral"), N_("chaotic"), N_("liminal") };
00991     static const char* aligns_female[] = { N_("female^lawful"), N_("female^neutral"), N_("female^chaotic"), N_("female^liminal") };
00992     const char** tlist = (gender == unit_race::MALE ? aligns : aligns_female);
00993 
00994     return (sgettext(tlist[align]));
00995 }
00996 
00997 const char* unit_type::alignment_id(unit_type::ALIGNMENT align)
00998 {
00999     static const char* aligns[] = { "lawful", "neutral", "chaotic", "liminal" };
01000     return (aligns[align]);
01001 }
01002 
01003 bool unit_type::has_ability_by_id(const std::string& ability) const
01004 {
01005     if (const config &abil = cfg_.child("abilities"))
01006     {
01007         foreach (const config::any_child &ab, abil.all_children_range()) {
01008             if (ab.cfg["id"] == ability)
01009                 return true;
01010         }
01011     }
01012     return false;
01013 }
01014 
01015 std::vector<std::string> unit_type::get_ability_list() const
01016 {
01017     std::vector<std::string> res;
01018 
01019     const config &abilities = cfg_.child("abilities");
01020     if (!abilities) return res;
01021 
01022     foreach (const config::any_child &ab, abilities.all_children_range()) {
01023         const std::string &id = ab.cfg["id"];
01024         if (!id.empty())
01025             res.push_back(id);
01026     }
01027 
01028     return res;
01029 }
01030 
01031 bool unit_type::hide_help() const {
01032     return hide_help_ || unit_types.hide_help(id_, race_->id());
01033 }
01034 
01035 void unit_type::add_advancement(const unit_type &to_unit,int xp)
01036 {
01037     const std::string &to_id =  to_unit.cfg_["id"];
01038     const std::string &from_id =  cfg_["id"];
01039 
01040     // Add extra advancement path to this unit type
01041     LOG_CONFIG << "adding advancement from " << from_id << " to " << to_id << "\n";
01042     if(std::find(advances_to_.begin(), advances_to_.end(), to_id) == advances_to_.end()) {
01043         advances_to_.push_back(to_id);
01044     } else {
01045         LOG_CONFIG << "advancement from " << from_id
01046                    << " to " << to_id << " already known, ignoring.\n";
01047         return;
01048     }
01049 
01050     if(xp > 0) {
01051         //xp is 0 in case experience= wasn't given.
01052         if(!in_advancefrom_) {
01053             //This function is called for and only for an [advancefrom] tag in a unit_type referencing this unit_type.
01054             in_advancefrom_ = true;
01055             experience_needed_ = xp;
01056             DBG_UT << "Changing experience_needed from " << experience_needed_ << " to " << xp << " due to (first) [advancefrom] of " << to_id << "\n";
01057         }
01058         else if(experience_needed_ > xp) {
01059             experience_needed_ = xp;
01060             DBG_UT << "Lowering experience_needed from " << experience_needed_ << " to " << xp << " due to (multiple, lower) [advancefrom] of " << to_id << "\n";
01061         }
01062         else
01063             DBG_UT << "Ignoring experience_needed change from " << experience_needed_ << " to " << xp << " due to (multiple, higher) [advancefrom] of " << to_id << "\n";
01064     }
01065 
01066     // Add advancements to gendered subtypes, if supported by to_unit
01067     for(int gender=0; gender<=1; ++gender) {
01068         if(gender_types_[gender] == NULL) continue;
01069         if(to_unit.gender_types_[gender] == NULL) {
01070             WRN_CF << to_id << " does not support gender " << gender << "\n";
01071             continue;
01072         }
01073         LOG_CONFIG << "gendered advancement " << gender << ": ";
01074         gender_types_[gender]->add_advancement(*(to_unit.gender_types_[gender]),xp);
01075     }
01076 
01077     // Add advancements to variation subtypes.
01078     // Since these are still a rare and special-purpose feature,
01079     // we assume that the unit designer knows what they're doing,
01080     // and don't block advancements that would remove a variation.
01081     for(variations_map::iterator v=variations_.begin();
01082         v!=variations_.end(); ++v) {
01083         LOG_CONFIG << "variation advancement: ";
01084         v->second->add_advancement(to_unit,xp);
01085     }
01086 }
01087 
01088 static void advancement_tree_internal(const std::string& id, std::set<std::string>& tree)
01089 {
01090     const unit_type *ut = unit_types.find(id);
01091     if (!ut)
01092         return;
01093 
01094     foreach(const std::string& adv, ut->advances_to()) {
01095         if (tree.insert(adv).second) {
01096             // insertion succeed, expand the new type
01097             advancement_tree_internal(adv, tree);
01098         }
01099     }
01100 }
01101 
01102 std::set<std::string> unit_type::advancement_tree() const
01103 {
01104     std::set<std::string> tree;
01105     advancement_tree_internal(id_, tree);
01106     return tree;
01107 }
01108 
01109 const std::vector<std::string> unit_type::advances_from() const
01110 {
01111     // currently not needed (only help call us and already did it)
01112     unit_types.build_all(unit_type::HELP_INDEX);
01113 
01114     std::vector<std::string> adv_from;
01115     foreach (const unit_type_data::unit_type_map::value_type &ut, unit_types.types())
01116     {
01117         foreach(const std::string& adv, ut.second.advances_to()) {
01118             if (adv == id_)
01119                 adv_from.push_back(ut.second.id());
01120         }
01121     }
01122     return adv_from;
01123 }
01124 
01125 unit_type_data::unit_type_data() :
01126     types_(),
01127     movement_types_(),
01128     races_(),
01129     hide_help_all_(false),
01130     hide_help_type_(),
01131     hide_help_race_(),
01132     unit_cfg_(NULL),
01133     build_status_(unit_type::NOT_BUILT)
01134 {
01135 }
01136 
01137 void unit_type_data::set_config(config &cfg)
01138 {
01139     DBG_UT << "unit_type_data::set_config, name: " << cfg["name"] << "\n";
01140 
01141     clear();
01142     set_unit_config(cfg);
01143 
01144     foreach (const config &mt, cfg.child_range("movetype"))
01145     {
01146         const unit_movement_type move_type(mt);
01147         movement_types_.insert(
01148             std::pair<std::string,unit_movement_type>(move_type.name(), move_type));
01149         loadscreen::increment_progress();
01150     }
01151 
01152     foreach (const config &r, cfg.child_range("race"))
01153     {
01154         const unit_race race(r);
01155         races_.insert(std::pair<std::string,unit_race>(race.id(),race));
01156         loadscreen::increment_progress();
01157     }
01158 
01159     foreach (config &ut, cfg.child_range("unit_type"))
01160     {
01161         std::string id = ut["id"];
01162         std::vector<std::string> base_tree;
01163         base_tree.push_back(id);
01164         while (const config &bu = ut.child("base_unit"))
01165         {
01166             if (std::find(base_tree.begin(), base_tree.end(), bu["id"].str()) != base_tree.end()) {
01167                 // If you want to allow diamond-style inheritance, replace the config::error throw with a continue
01168 
01169                 std::stringstream ss;
01170                 ss << "[base_unit] recursion loop in [unit_type] ";
01171                 foreach(std::string &step, base_tree) {
01172                     ss << step << "->";
01173                 }
01174                 ss << bu["id"];
01175                 ERR_CF << ss.str() << '\n';
01176                 throw config::error(ss.str());
01177             } else {
01178                 base_tree.push_back(bu["id"]);
01179             }
01180 
01181             // Derive a new unit type from existing base unit and its ancestors.
01182             config merge_cfg = find_config(bu["id"]);
01183             ut.remove_child("base_unit", 0);
01184             merge_cfg.merge_with(ut);
01185             ut.swap(merge_cfg);
01186         }
01187         if(ut["id"].empty()) {
01188             ERR_CF << "[unit_type] with empty id=, ignoring:\n" << ut.debug();
01189         } else {
01190             // We insert an empty unit_type and build it after the copy (for performance).
01191             insert(std::make_pair(id, unit_type(ut)));
01192             LOG_CONFIG << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
01193         }
01194         loadscreen::increment_progress();
01195     }
01196 
01197     build_all(unit_type::CREATED);
01198 
01199     if (const config &hide_help = cfg.child("hide_help")) {
01200         hide_help_all_ = hide_help["all"].to_bool();
01201         read_hide_help(hide_help);
01202     }
01203 }
01204 
01205 const unit_type *unit_type_data::find(const std::string& key, unit_type::BUILD_STATUS status) const
01206 {
01207     if (key.empty() || key == "random") return NULL;
01208 
01209     DBG_CF << "trying to find " << key  << " in unit_type list (unit_type_data.unit_types)\n";
01210     const unit_type_map::iterator itor = types_.find(key);
01211 
01212     //This might happen if units of another era are requested (for example for savegames)
01213     if (itor == types_.end()){
01214         /*
01215         for (unit_type_map::const_iterator ut = types_.begin(); ut != types_.end(); ut++)
01216             DBG_UT << "Known unit_types: key = '" << ut->first << "', id = '" << ut->second.id() << "'\n";
01217         */
01218         return NULL;
01219     }
01220 
01221     //check if the unit_type is constructed and build it if necessary
01222     build_unit_type(itor, status);
01223 
01224     return &itor->second;
01225 }
01226 
01227 void unit_type_data::check_types(const std::vector<std::string>& types) const
01228 {
01229     foreach(const std::string& type, types) {
01230         if(!find(type)) throw game::game_error("unknown unit type: " + type);
01231     }
01232 }
01233 
01234 const config& unit_type_data::find_config(const std::string& key) const
01235 {
01236     const config &cfg = unit_cfg_->find_child("unit_type", "id", key);
01237 
01238     if (cfg)
01239         return cfg;
01240 
01241     ERR_CF << "unit type not found: " << key << "\n";
01242     ERR_CF << *unit_cfg_ << "\n";
01243 
01244     throw config::error("unit type not found: "+key);
01245 }
01246 
01247 void unit_type_data::clear()
01248 {
01249     types_.clear();
01250     movement_types_.clear();
01251     races_.clear();
01252     build_status_ = unit_type::NOT_BUILT;
01253 
01254     hide_help_all_ = false;
01255     hide_help_race_.clear();
01256     hide_help_type_.clear();
01257 }
01258 
01259 void unit_type_data::build_all(unit_type::BUILD_STATUS status)
01260 {
01261     if (int(status) <= int(build_status_)) return;
01262     assert(unit_cfg_ != NULL);
01263 
01264     for (unit_type_map::iterator u = types_.begin(), u_end = types_.end(); u != u_end; ++u) {
01265         build_unit_type(u, status);
01266         loadscreen::increment_progress();
01267     }
01268     for (unit_type_map::iterator u = types_.begin(), u_end = types_.end(); u != u_end; ++u) {
01269         add_advancement(u->second);
01270     }
01271 
01272     build_status_ = status;
01273 }
01274 
01275 unit_type &unit_type_data::build_unit_type(const unit_type_map::iterator &ut, unit_type::BUILD_STATUS status) const
01276 {
01277     DBG_UT << "Building unit type " << ut->first << ", level " << status << '\n';
01278 
01279     if (int(status) <= int(ut->second.build_status()))
01280         return ut->second;
01281 
01282     switch (status) {
01283     case unit_type::CREATED:
01284         ut->second.build_created(movement_types_, races_, unit_cfg_->child_range("trait"));
01285         break;
01286     case unit_type::HELP_INDEX:
01287         // Build the data needed to feed the help index.
01288         ut->second.build_help_index(movement_types_, races_, unit_cfg_->child_range("trait"));
01289         break;
01290     default:
01291         ut->second.build_full(movement_types_, races_, unit_cfg_->child_range("trait"));
01292     }
01293 
01294     return ut->second;
01295 }
01296 
01297 void unit_type_data::read_hide_help(const config& cfg)
01298 {
01299     if (!cfg)
01300         return;
01301 
01302     hide_help_race_.push_back(std::set<std::string>());
01303     hide_help_type_.push_back(std::set<std::string>());
01304 
01305     std::vector<std::string> races = utils::split(cfg["race"]);
01306     hide_help_race_.back().insert(races.begin(), races.end());
01307 
01308     std::vector<std::string> types = utils::split(cfg["type"]);
01309     hide_help_type_.back().insert(types.begin(), types.end());
01310 
01311     std::vector<std::string> trees = utils::split(cfg["type_adv_tree"]);
01312     hide_help_type_.back().insert(trees.begin(), trees.end());
01313     foreach(const std::string& t_id, trees) {
01314         unit_type_map::iterator ut = types_.find(t_id);
01315         if (ut != types_.end()) {
01316             std::set<std::string> adv_tree = ut->second.advancement_tree();
01317             hide_help_type_.back().insert(adv_tree.begin(), adv_tree.end());
01318         }
01319     }
01320 
01321     // we call recursively all the imbricated [not] tags
01322     read_hide_help(cfg.child("not"));
01323 }
01324 
01325 bool unit_type_data::hide_help(const std::string& type, const std::string& race) const
01326 {
01327     bool res = hide_help_all_;
01328     int lvl = hide_help_all_ ? 1 : 0; // first level is covered by 'all=yes'
01329 
01330     // supposed to be equal but let's be cautious
01331     int lvl_nb = std::min(hide_help_race_.size(), hide_help_type_.size());
01332 
01333     for (; lvl < lvl_nb; ++lvl) {
01334         if (hide_help_race_[lvl].count(race) || hide_help_type_[lvl].count(type))
01335             res = !res; // each level is a [not]
01336     }
01337     return res;
01338 }
01339 
01340 void unit_type_data::add_advancement(unit_type& to_unit) const
01341 {
01342     const config& cfg = to_unit.get_cfg();
01343 
01344     foreach (const config &af, cfg.child_range("advancefrom"))
01345     {
01346         const std::string &from = af["unit"];
01347         int xp = af["experience"];
01348 
01349         unit_type_data::unit_type_map::iterator from_unit = types_.find(from);
01350 
01351         if (from_unit == types_.end()) {
01352             std::ostringstream msg;
01353             msg << "unit type '" << from << "' not found when resolving [advancefrom] tag for '"
01354                 << to_unit.id() << "'";
01355             throw config::error(msg.str());
01356         }
01357 
01358         // Fix up advance_from references
01359         from_unit->second.add_advancement(to_unit, xp);
01360 
01361         DBG_UT << "Added advancement ([advancefrom]) from " << from << " to " << to_unit.id() << "\n";
01362     }
01363 }
01364 
01365 const unit_race *unit_type_data::find_race(const std::string &key) const
01366 {
01367     race_map::const_iterator i = races_.find(key);
01368     return i != races_.end() ? &i->second : NULL;
01369 }
01370 
01371 // This function is only meant to return the likely state of not_living
01372 // for a new recruit of this type. It should not be used to check if
01373 // a particular unit is living or not, use get_state("not_living") for that.
01374 bool unit_type::not_living() const
01375 {
01376     // If a unit hasn't been modified it starts out as living.
01377     bool not_living = false;
01378 
01379     // Look at all of the "musthave" traits to see if the not_living
01380     // status gets changed. In the unlikely event it gets changed
01381     // multiple times, we want to try to do it in the same order
01382     // that unit::apply_modifications does things.
01383     foreach (const config &mod, possible_traits())
01384     {
01385         if (mod["availability"] != "musthave")
01386             continue;
01387 
01388         foreach (const config &effect, mod.child_range("effect"))
01389         {
01390             // See if the effect only applies to
01391             // certain unit types But don't worry
01392             // about gender checks, since we don't
01393             // know what the gender of the
01394             // hypothetical recruit is.
01395             const std::string &ut = effect["unit_type"];
01396             if (!ut.empty()) {
01397                 const std::vector<std::string> &types = utils::split(ut);
01398                 if(std::find(types.begin(), types.end(), id()) == types.end())
01399                     continue;
01400             }
01401 
01402             // We're only interested in status changes.
01403             if (effect["apply_to"] != "status") {
01404                 continue;
01405             }
01406             if (effect["add"] == "not_living") {
01407                 not_living = true;
01408             }
01409             if (effect["remove"] == "not_living") {
01410                 not_living = false;
01411             }
01412         }
01413     }
01414 
01415     return not_living;
01416 }
01417 
01418 bool unit_type::has_random_traits() const
01419 {
01420     if (num_traits() == 0) return false;
01421     config::const_child_itors t = possible_traits();
01422     while(t.first != t.second) {
01423         const config::attribute_value& availability = (*t.first)["availability"];
01424         if(availability.blank()) return true;
01425         if(strcmp(availability.str().c_str(), "musthave") != 0) return true;
01426         ++t.first;
01427     }
01428     return false;
01429 }
01430 
01431 unit_type_data unit_types;
01432 
01433 void adjust_profile(std::string &small, std::string &big, std::string const &def)
01434 {
01435     if (big.empty())
01436     {
01437         // No profile data; use the default image.
01438         small = def;
01439         big = def;
01440     }
01441     else if (small.empty())
01442     {
01443         // No small profile; use the current profile for it and
01444         // try to infer the big one.
01445         small = big;
01446         std::string::size_type offset = big.find('~');
01447         offset = big.find_last_of('/', offset);
01448         if (offset != std::string::npos) {
01449             big.insert(offset, "/transparent");
01450         } else {
01451             big = "transparent/" + big;
01452         }
01453         if (!image::locator(big).file_exists())
01454             big = small;
01455     }
01456 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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