unit_types.cpp

Go to the documentation of this file.
00001 /* $Id: unit_types.cpp 26759 2008-05-21 19:09:44Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 /**
00016  *  @file unit_types.cpp
00017  *  Handle unit-type specific attributes, animations, advancement.
00018  */
00019 
00020 #include "global.hpp"
00021 
00022 #include "game_config.hpp"
00023 #include "gettext.hpp"
00024 #include "loadscreen.hpp"
00025 #include "log.hpp"
00026 #include "unit_types.hpp"
00027 #include "util.hpp"
00028 #include "serialization/string_utils.hpp"
00029 #include "color_range.hpp"
00030 #include "game_display.hpp"
00031 
00032 #include <algorithm>
00033 #include <cassert>
00034 #include <cstdlib>
00035 #include <iostream>
00036 
00037 #define ERR_CONFIG LOG_STREAM(err, config)
00038 #define DBG_UT LOG_STREAM(debug, engine)
00039 #define LOG_UT LOG_STREAM(info, engine)
00040 #define ERR_UT LOG_STREAM(err, engine)
00041 
00042 attack_type::attack_type(const config& cfg)
00043 {
00044     cfg_ = cfg;
00045     id_ = cfg["name"];
00046     description_ = cfg["description"];
00047     if (description_.empty())
00048         description_ = egettext(id_.c_str());
00049 
00050     type_ = cfg["type"];
00051     icon_ = cfg["icon"];
00052     if(icon_.empty()){
00053         if (id_ != "")
00054             icon_ = "attacks/" + id_ + ".png";
00055         else
00056             icon_ = "attacks/blank-attack.png";
00057     }
00058 
00059     accuracy_ = atol(cfg["accuracy"].c_str());
00060     parry_ = atol(cfg["parry"].c_str());
00061     range_ = cfg["range"].base_str();
00062     damage_ = atol(cfg["damage"].c_str());
00063     num_attacks_ = atol(cfg["number"].c_str());
00064 
00065     attack_weight_ = lexical_cast_default<double>(cfg["attack_weight"],1.0);
00066     defense_weight_ = lexical_cast_default<double>(cfg["defense_weight"],1.0);
00067 
00068     unitmap_=NULL;
00069     map_=NULL;
00070     game_status_=NULL;
00071     teams_=NULL;
00072     other_attack_=NULL;
00073 
00074 }
00075 
00076 std::string attack_type::accuracy_parry_description() const
00077 {
00078     if(accuracy_ == 0 && parry_ == 0) {
00079         return "";
00080     }
00081 
00082     std::ostringstream s;
00083     if(accuracy_ > 0) {
00084         s << "+";
00085     }
00086 
00087     s << accuracy_ << "%";
00088 
00089     if(parry_ != 0) {
00090         s << "/";
00091         if(parry_ > 0) {
00092             s << "+";
00093         }
00094 
00095         s << parry_ << "%";
00096     }
00097 
00098     return s.str();
00099 }
00100 
00101 bool attack_type::matches_filter(const config& cfg,bool self) const
00102 {
00103     const std::vector<std::string>& filter_range = utils::split(cfg["range"]);
00104     const std::vector<std::string> filter_name = utils::split(cfg["name"]);
00105     const std::vector<std::string> filter_type = utils::split(cfg["type"]);
00106     const std::string filter_special = cfg["special"];
00107 
00108     if(filter_range.empty() == false && std::find(filter_range.begin(),filter_range.end(),range()) == filter_range.end())
00109             return false;
00110 
00111     if(filter_name.empty() == false && std::find(filter_name.begin(),filter_name.end(),id()) == filter_name.end())
00112         return false;
00113 
00114     if(filter_type.empty() == false && std::find(filter_type.begin(),filter_type.end(),type()) == filter_type.end())
00115         return false;
00116 
00117     if(!self && filter_special.empty() == false && !get_special_bool(filter_special,true))
00118         return false;
00119 
00120     return true;
00121 }
00122 
00123 bool attack_type::apply_modification(const config& cfg,std::string* description)
00124 {
00125     if(!matches_filter(cfg,0))
00126         return false;
00127 
00128     const std::string& set_name = cfg["set_name"];
00129     const t_string& set_desc = cfg["set_description"];
00130     const std::string& set_type = cfg["set_type"];
00131     const std::string& del_specials = cfg["remove_specials"];
00132     const config* set_specials = cfg.child("set_specials");
00133     const std::string& increase_damage = cfg["increase_damage"];
00134     const std::string& increase_attacks = cfg["increase_attacks"];
00135     const std::string& set_attack_weight = cfg["attack_weight"];
00136     const std::string& set_defense_weight = cfg["defense_weight"];
00137     const std::string& increase_accuracy = cfg["increase_accuracy"];
00138     const std::string& increase_parry = cfg["increase_parry"];
00139 
00140     std::stringstream desc;
00141 
00142     if(set_name.empty() == false) {
00143         id_ = set_name;
00144     }
00145 
00146     if(set_desc.empty() == false) {
00147         description_ = set_desc;
00148     }
00149 
00150     if(set_type.empty() == false) {
00151         type_ = set_type;
00152     }
00153 
00154     if(del_specials.empty() == false) {
00155         const std::vector<std::string>& dsl = utils::split(del_specials);
00156         config* specials = cfg_.child("specials");
00157         if (specials != NULL) {
00158             config new_specials;
00159             for(config::all_children_iterator s = specials->ordered_begin(); s != specials->ordered_end(); ++s) {
00160                 const std::pair<const std::string*,const config*>& vp = *s;
00161                 std::vector<std::string>::const_iterator found_id =
00162                     std::find(dsl.begin(),dsl.end(),vp.second->get_attribute("id"));
00163                 if (found_id == dsl.end()) {
00164                     new_specials.add_child(*vp.first,*vp.second);
00165                 }
00166             }
00167             cfg_.clear_children("specials");
00168             cfg_.add_child("specials",new_specials);
00169         }
00170     }
00171 
00172     if (set_specials) {
00173         const std::string& mode = set_specials->get_attribute("mode");
00174         if ( mode != "append") {
00175             cfg_.clear_children("specials");
00176         }
00177         config* new_specials = cfg_.child("specials");
00178         if (new_specials == NULL) {
00179             cfg_.add_child("specials");
00180             new_specials = cfg_.child("specials");
00181         }
00182         for(config::all_children_iterator s = set_specials->ordered_begin(); s != set_specials->ordered_end(); ++s) {
00183             const std::pair<const std::string*,const config*>& value = *s;
00184             new_specials->add_child(*value.first,*value.second);
00185         }
00186     }
00187 
00188     if(increase_damage.empty() == false) {
00189         damage_ = utils::apply_modifier(damage_, increase_damage, 1);
00190 
00191         if(description != NULL) {
00192             desc << (increase_damage[0] == '-' ? "" : "+") << increase_damage
00193                 << " " << _n("damage","damage",lexical_cast<int>(increase_damage));
00194         }
00195     }
00196 
00197     if(increase_attacks.empty() == false) {
00198         num_attacks_ = utils::apply_modifier(num_attacks_, increase_attacks, 1);
00199 
00200         if(description != NULL) {
00201             desc << (increase_attacks[0] == '-' ? "" : "+") << increase_attacks
00202                 << " " << _n("strike","strikes",lexical_cast<int>(increase_attacks));
00203         }
00204     }
00205 
00206     if(increase_accuracy.empty() == false) {
00207         accuracy_ = utils::apply_modifier(accuracy_, increase_accuracy, 1);
00208 
00209         if(description != NULL) {
00210             desc << (increase_accuracy[0] == '-' ? "" : "+") << increase_accuracy << " " << _n("accuracy","accuracy",lexical_cast<int>(increase_accuracy));
00211         }
00212     }
00213 
00214     if(increase_parry.empty() == false) {
00215         parry_ = utils::apply_modifier(parry_, increase_parry, 1);
00216 
00217         if(description != NULL) {
00218             desc << (increase_parry[0] == '-' ? "" : "+") << increase_parry << " " << _n("parry","parry",lexical_cast<int>(increase_parry));
00219         }
00220     }
00221 
00222     if(set_attack_weight.empty() == false) {
00223         attack_weight_ = lexical_cast_default<double>(set_attack_weight,1.0);
00224     }
00225 
00226     if(set_defense_weight.empty() == false) {
00227         defense_weight_ = lexical_cast_default<double>(set_defense_weight,1.0);
00228     }
00229 
00230     if(description != NULL) {
00231         *description = desc.str();
00232     }
00233 
00234     return true;
00235 }
00236 
00237 // Same as above, except only update the descriptions
00238 bool attack_type::describe_modification(const config& cfg,std::string* description)
00239 {
00240     if(!matches_filter(cfg,0))
00241         return false;
00242 
00243     const std::string& increase_damage = cfg["increase_damage"];
00244     const std::string& increase_attacks = cfg["increase_attacks"];
00245 
00246     std::stringstream desc;
00247 
00248     if(increase_damage.empty() == false) {
00249         if(description != NULL) {
00250             desc << (increase_damage[0] == '-' ? "" : "+") << increase_damage
00251                 << " " << _n("damage","damage",lexical_cast<int>(increase_damage));
00252         }
00253     }
00254 
00255     if(increase_attacks.empty() == false) {
00256         if(description != NULL) {
00257             desc << (increase_attacks[0] == '-' ? "" : "+") << increase_attacks
00258                 << " " << _n("strike","strikes",lexical_cast<int>(increase_attacks));
00259         }
00260     }
00261 
00262     if(description != NULL) {
00263         *description = desc.str();
00264     }
00265 
00266     return true;
00267 }
00268 bool attack_type::has_special_by_id(const std::string& special) const
00269 {
00270     const config* abil = cfg_.child("specials");
00271     if(abil) {
00272         for(config::child_map::const_iterator i = abil->all_children().begin(); i != abil->all_children().end(); ++i) {
00273             for(config::child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
00274                 if((**j)["id"] == special) {
00275                     return true;
00276                 }
00277             }
00278         }
00279     }
00280     return false;
00281 }
00282 
00283 unit_movement_type::unit_movement_type(const config& cfg, const unit_movement_type* parent) : moveCosts_(), defenseMods_(), parent_(parent)
00284 {
00285     //the unit_type give its whole cfg, we don't need all that.
00286     //so we filter to keep only keys related to movement_type
00287     //FIXME: This helps but it's still not clean, both cfg use a "name" key
00288 
00289     const t_string& name = cfg["name"];
00290     if (!name.empty())
00291         cfg_["name"]= cfg["name"];
00292 
00293     const t_string& flies = cfg["flies"];
00294     if (!flies.empty())
00295         cfg_["flies"]= cfg["flies"];
00296 
00297     const config* movement_costs = cfg.child("movement_costs");
00298     if (movement_costs!=NULL)
00299         cfg_.add_child("movement_costs", *movement_costs);
00300 
00301     const config* defense = cfg.child("defense");
00302     if (defense!=NULL)
00303         cfg_.add_child("defense", *defense);
00304 
00305     const config* resistance = cfg.child("resistance");
00306     if (resistance!=NULL)
00307         cfg_.add_child("resistance", *resistance);
00308 }
00309 
00310 unit_movement_type::unit_movement_type(): moveCosts_(), defenseMods_(), parent_(NULL), cfg_()
00311 {}
00312 
00313 const t_string& unit_movement_type::name() const
00314 {
00315     const t_string& res = cfg_.get_attribute("name");
00316     if(res == "" && parent_ != NULL)
00317         return parent_->name();
00318     else
00319         return res;
00320 }
00321 
00322 int unit_movement_type::movement_cost(const gamemap& map,
00323         t_translation::t_terrain terrain, int recurse_count) const
00324 {
00325     const int impassable = 10000000;
00326 
00327     const std::map<t_translation::t_terrain, int>::const_iterator i =
00328         moveCosts_.find(terrain);
00329 
00330     if(i != moveCosts_.end()) {
00331         return i->second;
00332     }
00333 
00334     // If this is an alias, then select the best of all underlying terrains.
00335     const t_translation::t_list& underlying = map.underlying_mvt_terrain(terrain);
00336     if(underlying.size() != 1 || underlying.front() != terrain) {
00337         bool revert = (underlying.front() == t_translation::MINUS ? true : false);
00338         if(recurse_count >= 100) {
00339             return impassable;
00340         }
00341 
00342         int ret_value = revert?0:impassable;
00343         for(t_translation::t_list::const_iterator i = underlying.begin();
00344                 i != underlying.end(); ++i) {
00345 
00346             if(*i == t_translation::PLUS) {
00347                 revert = false;
00348                 continue;
00349             } else if(*i == t_translation::MINUS) {
00350                 revert = true;
00351                 continue;
00352             }
00353             const int value = movement_cost(map,*i,recurse_count+1);
00354             if(value < ret_value && !revert) {
00355                 ret_value = value;
00356             } else if(value > ret_value && revert) {
00357                 ret_value = value;
00358             }
00359         }
00360 
00361         moveCosts_.insert(std::pair<t_translation::t_terrain, int>(terrain,ret_value));
00362 
00363         return ret_value;
00364     }
00365 
00366     const config* movement_costs = cfg_.child("movement_costs");
00367 
00368     int res = -1;
00369 
00370     if(movement_costs != NULL) {
00371         if(underlying.size() != 1) {
00372             LOG_STREAM(err, config) << "terrain '" << terrain << "' has "
00373                 << underlying.size() << " underlying names - 0 expected\n";
00374 
00375             return impassable;
00376         }
00377 
00378         const std::string& id = map.get_terrain_info(underlying.front()).id();
00379 
00380         const std::string& val = (*movement_costs)[id];
00381 
00382         if(val != "") {
00383             res = atoi(val.c_str());
00384         }
00385     }
00386 
00387     if(res <= 0 && parent_ != NULL) {
00388         res = parent_->movement_cost(map,terrain);
00389     }
00390 
00391     if(res <= 0) {
00392         res = impassable;
00393     }
00394 
00395     moveCosts_.insert(std::pair<t_translation::t_terrain, int>(terrain,res));
00396 
00397     return res;
00398 }
00399 
00400 int unit_movement_type::defense_modifier(const gamemap& map,
00401         t_translation::t_terrain terrain, int recurse_count) const
00402 {
00403     const std::map<t_translation::t_terrain, int>::const_iterator i =
00404         defenseMods_.find(terrain);
00405 
00406     if(i != defenseMods_.end()) {
00407         return i->second;
00408     }
00409 
00410     // If this is an alias, then select the best of all underlying terrains.
00411     const t_translation::t_list& underlying =
00412         map.underlying_def_terrain(terrain);
00413 
00414     if(underlying.size() != 1 || underlying.front() != terrain) {
00415         bool revert = (underlying.front() == t_translation::MINUS ? true : false);
00416         if(recurse_count >= 100) {
00417             return 100;
00418         }
00419 
00420         int ret_value = revert?0:100;
00421         for(t_translation::t_list::const_iterator i = underlying.begin();
00422                 i != underlying.end(); ++i) {
00423 
00424             if(*i == t_translation::PLUS) {
00425                 revert = false;
00426                 continue;
00427             } else if(*i == t_translation::MINUS) {
00428                 revert = true;
00429                 continue;
00430             }
00431             const int value = defense_modifier(map,*i,recurse_count+1);
00432             if(value < ret_value && !revert) {
00433                 ret_value = value;
00434             } else if(value > ret_value && revert) {
00435                 ret_value = value;
00436             }
00437         }
00438 
00439         defenseMods_.insert(std::pair<t_translation::t_terrain, int>(terrain, ret_value));
00440 
00441         return ret_value;
00442     }
00443 
00444     int res = -1;
00445 
00446     const config* const defense = cfg_.child("defense");
00447 
00448     if(defense != NULL) {
00449         if(underlying.size() != 1) {
00450             LOG_STREAM(err, config) << "terrain '" << terrain << "' has "
00451                 << underlying.size() << " underlying names - 0 expected\n";
00452 
00453             return 100;
00454         }
00455 
00456         const std::string& id = map.get_terrain_info(underlying.front()).id();
00457         const std::string& val = (*defense)[id];
00458 
00459         if(val != "") {
00460             res = atoi(val.c_str());
00461         }
00462     }
00463 
00464     if(res <= 0 && parent_ != NULL) {
00465         res = parent_->defense_modifier(map,terrain);
00466     }
00467 
00468     if(res < 0) {
00469         ERR_CONFIG << "Defence '" << res << "' is '< 0' reset to 0 (100% defence).\n";
00470         res = 0;
00471     }
00472 
00473     defenseMods_.insert(std::pair<t_translation::t_terrain, int>(terrain, res));
00474 
00475     return res;
00476 }
00477 
00478 int unit_movement_type::resistance_against(const attack_type& attack) const
00479 {
00480     bool result_found = false;
00481     int res = 100;
00482 
00483     const config* const resistance = cfg_.child("resistance");
00484     if(resistance != NULL) {
00485         const std::string& val = (*resistance)[attack.type()];
00486         if(val != "") {
00487             res = atoi(val.c_str());
00488             result_found = true;
00489         }
00490     }
00491 
00492     if(!result_found && parent_ != NULL) {
00493         res = parent_->resistance_against(attack);
00494     }
00495 
00496     return res;
00497 }
00498 
00499 string_map unit_movement_type::damage_table() const
00500 {
00501     string_map res;
00502     if(parent_ != NULL)
00503         res = parent_->damage_table();
00504 
00505     const config* const resistance = cfg_.child("resistance");
00506     if(resistance != NULL) {
00507         for(string_map::const_iterator i = resistance->values.begin(); i != resistance->values.end(); ++i) {
00508             res[i->first] = i->second;
00509         }
00510     }
00511 
00512     return res;
00513 }
00514 
00515 bool unit_movement_type::is_flying() const
00516 {
00517     const std::string& flies = cfg_["flies"];
00518     if(flies == "" && parent_ != NULL)
00519         return parent_->is_flying();
00520 
00521     return utils::string_bool(flies);
00522 }
00523 
00524 unit_type::unit_type()
00525 {
00526     DBG_UT << "unit_type default constructor\n";
00527     build_status_ = NOT_BUILT;
00528     gender_types_[0] = NULL;
00529     gender_types_[1] = NULL;
00530 }
00531 
00532 unit_type::unit_type(const unit_type& o)
00533     : cfg_(o.cfg_), id_(o.id_), type_name_(o.type_name_), description_(o.description_),
00534       hitpoints_(o.hitpoints_), level_(o.level_), movement_(o.movement_), cost_(o.cost_),
00535       usage_(o.usage_), undead_variation_(o.undead_variation_),
00536       image_(o.image_), image_profile_(o.image_profile_), flag_rgb_(o.flag_rgb_),
00537       num_traits_(o.num_traits_), variations_(o.variations_), race_(o.race_),
00538       alpha_(o.alpha_), abilities_(o.abilities_),ability_tooltips_(o.ability_tooltips_),
00539       hide_help_(o.hide_help_), advances_to_(o.advances_to_), advances_from_(o.advances_from_),
00540       experience_needed_(o.experience_needed_), alignment_(o.alignment_),
00541       movementType_(o.movementType_), possibleTraits_(o.possibleTraits_),
00542       genders_(o.genders_), animations_(o.animations_)
00543 {
00544     DBG_UT << "unit_type copy-constructor\n";
00545     build_status_ = o.build_status_;
00546     gender_types_[0] = o.gender_types_[0] != NULL ? new unit_type(*o.gender_types_[0]) : NULL;
00547     gender_types_[1] = o.gender_types_[1] != NULL ? new unit_type(*o.gender_types_[1]) : NULL;
00548 
00549     for(variations_map::const_iterator i = o.variations_.begin(); i != o.variations_.end(); ++i) {
00550         variations_[i->first] = new unit_type(*i->second);
00551     }
00552 }
00553 
00554 
00555 unit_type::unit_type(const config& cfg, const movement_type_map& mv_types,
00556                      const race_map& races, const std::vector<config*>& traits)
00557 {
00558     DBG_UT << "unit_type constructor cfg, mv_types, races, traits\n";
00559     build_status_ = NOT_BUILT;
00560     build_full(cfg, mv_types, races, traits);
00561 }
00562 
00563 unit_type::~unit_type()
00564 {
00565     DBG_UT << "unit_type destructor\n";
00566     delete gender_types_[unit_race::MALE];
00567     delete gender_types_[unit_race::FEMALE];
00568 
00569     for(variations_map::iterator i = variations_.begin(); i != variations_.end(); ++i) {
00570         delete i->second;
00571     }
00572 }
00573 
00574 void unit_type::build_full(const config& cfg, const movement_type_map& mv_types,
00575                      const race_map& races, const std::vector<config*>& traits)
00576 {
00577     if (build_status_ == NOT_BUILT)
00578         build_help_index(cfg, races);
00579 
00580     movementType_ = unit_movement_type(cfg);
00581     alpha_ = ftofxp(1.0);
00582 
00583     config::const_child_iterator i;
00584     for(i=traits.begin(); i != traits.end(); ++i)
00585     {
00586         possibleTraits_.add_child("trait", **i);
00587     }
00588     const config::child_list& variations = cfg.get_children("variation");
00589     for(config::child_list::const_iterator var = variations.begin(); var != variations.end(); ++var) {
00590         const config& var_cfg = **var;
00591         if(utils::string_bool(var_cfg["inherit"])) {
00592             config nvar_cfg(cfg);
00593             nvar_cfg.merge_with(var_cfg);
00594             nvar_cfg.clear_children("variation");
00595             variations_.insert(std::pair<std::string,unit_type*>(nvar_cfg["variation_name"],
00596                                                  new unit_type(nvar_cfg,mv_types,races,traits)));
00597         } else {
00598             variations_.insert(std::pair<std::string,unit_type*>((**var)["variation_name"],
00599                                                  new unit_type(**var,mv_types,races,traits)));
00600         }
00601     }
00602 
00603     gender_types_[0] = NULL;
00604     gender_types_[1] = NULL;
00605 
00606     const config* const male_cfg = cfg.child("male");
00607     if(male_cfg != NULL) {
00608         config m_cfg;
00609         if(!utils::string_bool((*male_cfg)["inherit"], true)) {
00610             m_cfg = *male_cfg;
00611         } else {
00612             m_cfg = cfg;
00613             m_cfg.merge_with(*male_cfg);
00614         }
00615         m_cfg.clear_children("male");
00616         m_cfg.clear_children("female");
00617         gender_types_[unit_race::MALE] = new unit_type(m_cfg,mv_types,races,traits);
00618     }
00619 
00620     const config* const female_cfg = cfg.child("female");
00621     if(female_cfg != NULL) {
00622         config f_cfg;
00623         if(!utils::string_bool((*female_cfg)["inherit"], true)) {
00624             f_cfg = *female_cfg;
00625         } else {
00626             f_cfg = cfg;
00627             f_cfg.merge_with(*female_cfg);
00628         }
00629         f_cfg.clear_children("male");
00630         f_cfg.clear_children("female");
00631         gender_types_[unit_race::FEMALE] = new unit_type(f_cfg,mv_types,races,traits);
00632     }
00633 
00634     const std::string& align = cfg["alignment"];
00635     if(align == "lawful")
00636         alignment_ = LAWFUL;
00637     else if(align == "chaotic")
00638         alignment_ = CHAOTIC;
00639     else if(align == "neutral")
00640         alignment_ = NEUTRAL;
00641     else {
00642         LOG_STREAM(err, config) << "Invalid alignment found for " << id() << ": '" << align << "'\n";
00643         alignment_ = NEUTRAL;
00644     }
00645 
00646     const race_map::const_iterator race_it = races.find(cfg["race"]);
00647     if(race_it != races.end()) {
00648         race_ = &race_it->second;
00649         if(race_ != NULL) {
00650             if(race_->uses_global_traits() == false) {
00651                 possibleTraits_.clear();
00652             }
00653             if(utils::string_bool(cfg["ignore_race_traits"])) {
00654                 possibleTraits_.clear();
00655             } else {
00656                 const config::child_list& traits = race_->additional_traits();
00657                 for(i=traits.begin(); i != traits.end(); ++i)
00658                 {
00659                     if(alignment_ != NEUTRAL || ((**i)["id"]) != "fearless")
00660                         possibleTraits_.add_child("trait", **i);
00661                 }
00662             }
00663         }
00664     } else {
00665         static const unit_race dummy_race;
00666         race_ = &dummy_race;
00667     }
00668 
00669     // Insert any traits that are just for this unit type
00670     const config::child_list& unit_traits = cfg.get_children("trait");
00671     for(i=unit_traits.begin(); i != unit_traits.end(); ++i)
00672     {
00673         possibleTraits_.add_child("trait", **i);
00674     }
00675 
00676     if(cfg["zoc"] == "") {
00677         zoc_ = lexical_cast_default<int>(cfg["level"]) > 0;
00678     } else {
00679         zoc_ = false;
00680         if(utils::string_bool(cfg["zoc"])) {
00681             zoc_ = true;
00682         }
00683     }
00684 
00685 
00686     const std::string& alpha_blend = cfg["alpha"];
00687     if(alpha_blend.empty() == false) {
00688         alpha_ = ftofxp(atof(alpha_blend.c_str()));
00689     }
00690 
00691     const std::string& move_type = cfg["movement_type"];
00692 
00693     const movement_type_map::const_iterator it = mv_types.find(move_type);
00694 
00695     if(it != mv_types.end()) {
00696         DBG_UT << "setting parent for movement_type " << move_type << "\n";
00697         movementType_.set_parent(&(it->second));
00698     }
00699     else{
00700         DBG_UT << "no parent found for movement_type " << move_type << "\n";
00701     }
00702 
00703     const std::string& advance_to_val = cfg["advanceto"];
00704     if(advance_to_val != "null" && advance_to_val != "")
00705         advances_to_ = utils::split(advance_to_val);
00706 
00707     experience_needed_=lexical_cast_default<int>(cfg["experience"],500);
00708 
00709     flag_rgb_ = cfg["flag_rgb"];
00710     game_config::add_color_info(cfg);
00711     // Deprecation messages, only seen when unit is parsed for the first time.
00712 
00713     build_status_ = FULL;
00714 }
00715 
00716 void unit_type::build_help_index(const config& cfg, const race_map& races)
00717 {
00718     cfg_ = cfg;
00719 
00720     id_ = cfg_["id"];
00721     type_name_ = cfg_["name"];
00722     description_ = cfg_["description"];
00723     hitpoints_ = atoi(cfg_["hitpoints"].c_str());
00724     level_ = atoi(cfg_["level"].c_str());
00725     movement_ = atoi(cfg_["movement"].c_str());
00726     cost_ = atoi(cfg_["cost"].c_str());
00727     usage_ = cfg_["usage"];
00728     undead_variation_ = cfg_["undead_variation"];
00729     image_ = cfg_["image"];
00730     image_profile_ = cfg_["profile"];
00731     num_traits_ = atoi(cfg_["num_traits"].c_str());
00732 
00733     const race_map::const_iterator race_it = races.find(cfg["race"]);
00734     if(race_it != races.end()) {
00735         race_ = &race_it->second;
00736     } else {
00737         static const unit_race dummy_race;
00738         race_ = &dummy_race;
00739     }
00740 
00741     const std::vector<std::string> genders = utils::split(cfg["gender"]);
00742     for(std::vector<std::string>::const_iterator g = genders.begin(); g != genders.end(); ++g) {
00743         genders_.push_back(string_gender(*g));
00744     }
00745     if(genders_.empty()) {
00746         genders_.push_back(unit_race::MALE);
00747     }
00748 
00749     const config* abil_cfg = cfg.child("abilities");
00750     if(abil_cfg) {
00751         const config::child_map& abi = abil_cfg->all_children();
00752         for(config::child_map::const_iterator j = abi.begin(); j != abi.end(); ++j) {
00753             for(config::child_list::const_iterator k = j->second.begin(); k != j->second.end(); ++k) {
00754                 if((**k)["name"] != "") {
00755                     abilities_.push_back((**k)["name"]);
00756                     ability_tooltips_.push_back((**k)["description"]);
00757                 }
00758             }
00759         }
00760     }
00761 
00762     hide_help_= utils::string_bool(cfg["hide_help"],false);
00763 
00764     build_status_ = HELP_INDEX;
00765 }
00766 
00767 const unit_type& unit_type::get_gender_unit_type(unit_race::GENDER gender) const
00768 {
00769     const size_t i = gender;
00770     if(i < sizeof(gender_types_)/sizeof(*gender_types_) && gender_types_[i] != NULL) {
00771         return *gender_types_[i];
00772     }
00773 
00774     return *this;
00775 }
00776 
00777 const unit_type& unit_type::get_variation(const std::string& name) const
00778 {
00779     const variations_map::const_iterator i = variations_.find(name);
00780     if(i != variations_.end()) {
00781         return *i->second;
00782     } else {
00783         return *this;
00784     }
00785 }
00786 
00787 const std::string& unit_type::image_profile() const
00788 {
00789     if(image_profile_.size() == 0)
00790         return image_;
00791     else
00792         return image_profile_;
00793 }
00794 
00795 const t_string& unit_type::unit_description() const
00796 {
00797     static const t_string default_val("No description available");
00798 
00799     if(description_.empty())
00800         return default_val;
00801     else
00802         return description_;
00803 }
00804 
00805 const std::vector<unit_animation>& unit_type::animations() const {
00806     if (animations_.empty()) {
00807         unit_animation::fill_initial_animations(animations_,cfg_);
00808     }
00809 
00810     return animations_;
00811 }
00812 
00813 std::vector<attack_type> unit_type::attacks() const
00814 {
00815     std::vector<attack_type> res;
00816     for(config::const_child_itors range = cfg_.child_range("attack");
00817         range.first != range.second; ++range.first) {
00818         res.push_back(attack_type(**range.first));
00819     }
00820 
00821     return res;
00822 }
00823 
00824 
00825 namespace {
00826     int experience_modifier = 100;
00827 }
00828 
00829 unit_type::experience_accelerator::experience_accelerator(int modifier) : old_value_(experience_modifier)
00830 {
00831     experience_modifier = modifier;
00832 }
00833 
00834 unit_type::experience_accelerator::~experience_accelerator()
00835 {
00836     experience_modifier = old_value_;
00837 }
00838 
00839 int unit_type::experience_accelerator::get_acceleration()
00840 {
00841     return experience_modifier;
00842 }
00843 
00844 int unit_type::experience_needed(bool with_acceleration) const
00845 {
00846     if(with_acceleration) {
00847         int exp = (experience_needed_ * experience_modifier + 50) /100;
00848         if(exp < 1) exp = 1;
00849         return exp;
00850     }
00851     return experience_needed_;
00852 }
00853 
00854 
00855 
00856 const char* unit_type::alignment_description(unit_type::ALIGNMENT align)
00857 {
00858     static const char* aligns[] = { N_("lawful"), N_("neutral"), N_("chaotic") };
00859     return (gettext(aligns[align]));
00860 }
00861 
00862 const char* unit_type::alignment_id(unit_type::ALIGNMENT align)
00863 {
00864     static const char* aligns[] = { "lawful", "neutral", "chaotic" };
00865     return (aligns[align]);
00866 }
00867 
00868 bool unit_type::has_ability(const std::string& ability) const
00869 {
00870     const config* abil = cfg_.child("abilities");
00871     if(abil) {
00872         return (abil->get_children(ability).size() > 0);
00873     }
00874     return false;
00875 }
00876 bool unit_type::has_ability_by_id(const std::string& ability) const
00877 {
00878     const config* abil = cfg_.child("abilities");
00879     if(abil) {
00880         for(config::child_map::const_iterator i = abil->all_children().begin(); i != abil->all_children().end(); ++i) {
00881             for(config::child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
00882                 if((**j)["id"] == ability) {
00883                     return true;
00884                 }
00885             }
00886         }
00887     }
00888     return false;
00889 }
00890 
00891 
00892 const std::string& unit_type::race() const
00893 {
00894     if(race_ == NULL) {
00895         static const std::string empty_string;
00896         return empty_string;
00897     }
00898 
00899     return race_->id();
00900 }
00901 
00902 // Allow storing "advances from" info for convenience in Help.
00903 void unit_type::add_advancesfrom(std::string unit_id)
00904 {
00905     if (find(advances_from_.begin(), advances_from_.end(), unit_id) == advances_from_.end())
00906         advances_from_.push_back(unit_id);
00907 }
00908 
00909 
00910 void unit_type::add_advancement(const unit_type &to_unit,int xp)
00911 {
00912     const std::string &to_id =  to_unit.cfg_["id"];
00913     const std::string &from_id =  cfg_["id"];
00914 
00915     // Add extra advancement path to this unit type
00916     lg::info(lg::config) << "adding advancement from " << from_id << " to " << to_id << "\n";
00917     advances_to_.push_back(to_id);
00918     if(xp>0 && experience_needed_>xp) experience_needed_=xp;
00919 
00920     // Add advancements to gendered subtypes, if supported by to_unit
00921     for(int gender=0; gender<=1; ++gender) {
00922         if(gender_types_[gender] == NULL) continue;
00923         if(to_unit.gender_types_[gender] == NULL) {
00924             lg::warn(lg::config) << to_id << " does not support gender " << gender << "\n";
00925             continue;
00926         }
00927         lg::info(lg::config) << "gendered advancement " << gender << ": ";
00928         gender_types_[gender]->add_advancement(*(to_unit.gender_types_[gender]),xp);
00929     }
00930 
00931     // Add advancements to variation subtypes.
00932     // Since these are still a rare and special-purpose feature,
00933     // we assume that the unit designer knows what they're doing,
00934     // and don't block advancements that would remove a variation.
00935     for(variations_map::iterator v=variations_.begin();
00936         v!=variations_.end(); ++v) {
00937         lg::info(lg::config) << "variation advancement: ";
00938         v->second->add_advancement(to_unit,xp);
00939     }
00940 }
00941 
00942 unit_type_data* unit_type_data::instance_ = NULL;
00943 
00944 unit_type_data::unit_type_data()
00945 {}
00946 
00947 unit_type_data::unit_type_map_wrapper::unit_type_map_wrapper()
00948 {
00949     unit_cfg_ = NULL;
00950     dummy_unit_map_.insert(std::pair<const std::string,unit_type>("dummy_unit", unit_type()));
00951 }
00952 
00953 void unit_type_data::unit_type_map_wrapper::set_config(const config& cfg)
00954 {
00955     DBG_UT << "unit_type_data::set_config, cfg:\n" << cfg;
00956 
00957     clear();
00958     set_unit_config(cfg);
00959     set_unit_traits(cfg.get_children("trait"));
00960 
00961     config::const_child_itors i;
00962     for(i = cfg.child_range("movetype"); i.first != i.second; ++i.first)
00963     {
00964         const unit_movement_type move_type(**i.first);
00965         movement_types_.insert(
00966             std::pair<std::string,unit_movement_type>(move_type.name(), move_type));
00967         increment_set_config_progress();
00968     }
00969 
00970     for(i = cfg.child_range("race"); i.first != i.second; ++i.first)
00971     {
00972         const unit_race race(**i.first);
00973         races_.insert(std::pair<std::string,unit_race>(race.id(),race));
00974         increment_set_config_progress();
00975     }
00976 
00977     for(i = cfg.child_range("unit_type"); i.first != i.second; ++i.first)
00978     {
00979         std::string id = (**i.first)["id"];
00980         if((**i.first).child("base_unit"))
00981         {
00982             // Derive a new unit type from an existing base unit id
00983             const std::string based_from = (*(**i.first).child("base_unit"))["id"];
00984             config from_cfg = find_config(based_from);
00985 
00986             config merge_cfg = from_cfg;
00987             merge_cfg.merge_with(**i.first);
00988             merge_cfg.clear_children("base_unit");
00989             std::string id = merge_cfg["id"];
00990             if(id.empty()) {
00991                 id = from_cfg["name"];
00992             }
00993 
00994             (**i.first) = merge_cfg;
00995             (**i.first)["id"] = id;
00996             /*
00997             //merge the base_unit config into this one
00998             (**i.first).merge_and_keep(from_cfg);
00999             (**i.first).clear_children("base_unit");
01000             (**i.first)["id"] = id;
01001             */
01002         }
01003         // we insert an empty unit_type and build it after the copy (for performance)
01004         std::pair<unit_type_map::iterator,bool> insertion =
01005             insert(std::pair<const std::string,unit_type>(id,unit_type()));
01006         //  if (!insertion.second)
01007         // TODO: else { warning for multiple units with same id}
01008         lg::info(lg::config) << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
01009     }
01010 
01011     // FIXME OBSOLETE compatibility hack to be removed in 1.5.3
01012     for(i = cfg.child_range("unit"); i.first != i.second; ++i.first)
01013     {
01014         std::string id = (**i.first)["id"];
01015         if((**i.first).child("base_unit"))
01016         {
01017             // Derive a new unit type from an existing base unit id
01018             const std::string based_from = (*(**i.first).child("base_unit"))["id"];
01019             config from_cfg = find_config(based_from);
01020 
01021             //merge the base_unit config into this one
01022             //ugly hack, but couldn't think of anything better for the moment
01023             from_cfg.merge_with(**i.first);
01024             (**i.first).merge_with(from_cfg);
01025 
01026             (**i.first).clear_children("base_unit");
01027             (**i.first)["id"] = id;
01028         }
01029         // we insert an empty unit_type and build it after the copy (for performance)
01030         std::pair<unit_type_map::iterator,bool> insertion =
01031             insert(std::pair<const std::string,unit_type>(id,unit_type()));
01032         //  if (!insertion.second)
01033         // TODO: else { warning for multiple units with same id}
01034         lg::info(lg::config) << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
01035         std::cerr << "warning: UnitWML [unit] tag will be removed in 1.5.3, run wmllint on WML defining " << id << " to convert it to using [unit_type]" << std::endl;
01036     }
01037 }
01038 
01039 unit_type_data::unit_type_map::const_iterator unit_type_data::unit_type_map_wrapper::find(const std::string& key, unit_type::BUILD_STATUS status) const
01040 {
01041     if (key.empty() || (key == "random"))
01042         return types_.end();
01043 
01044     unit_type_map::iterator itor = types_.find(key);
01045 
01046     lg::info(lg::config) << "trying to find " << key  << " in unit_type list (unit_type_data.unit_types)\n";
01047 
01048     //This might happen if units of another era are requested (for example for savegames)
01049     if (itor == types_.end()){
01050         lg::info(lg::config) << "key not found, returning dummy_unit\n";
01051         itor = dummy_unit_map_.find("dummy_unit");
01052         assert(itor != dummy_unit_map_.end());
01053         return itor;
01054     }
01055 
01056     //check if the unit_type is constructed and build it if necessary
01057     build_unit_type(key, status);
01058 
01059     return types_.find(key);
01060 }
01061 
01062 const config& unit_type_data::unit_type_map_wrapper::find_config(const std::string& key) const
01063 {
01064     const config* cfg = unit_cfg_->find_child("unit_type", "id", key);
01065 
01066     if (cfg != NULL)
01067         return *cfg;
01068 
01069     DBG_UT << "unit type not found: " << key << "\n";
01070     DBG_UT << *unit_cfg_ << "\n";
01071 
01072     // FIXME OBSOLETE compatibility hack to be removed in 1.5.3
01073     cfg = unit_cfg_->find_child("unit", "id", key);
01074 
01075     assert(cfg != NULL);
01076     return *cfg;
01077 }
01078 
01079 void unit_type_data::unit_type_map_wrapper::build_all(unit_type::BUILD_STATUS status) const
01080 {
01081     assert(unit_cfg_ != NULL);
01082 
01083     for (unit_type_map::const_iterator u = types_.begin(); u != types_.end(); u++){
01084         build_unit_type(u->first, status);
01085     }
01086 }
01087 
01088 unit_type& unit_type_data::unit_type_map_wrapper::build_unit_type(const std::string& key, unit_type::BUILD_STATUS status) const
01089 {
01090     unit_type_map::iterator ut = types_.find(key);
01091 
01092     if (key == "dummy_unit")
01093         return ut->second;
01094 
01095     const config& unit_cfg = find_config(key);
01096 
01097     switch (status){
01098         case unit_type::HELP_INDEX:
01099             //build the stuff that is needed to feed the help index
01100             if (ut->second.build_status() == unit_type::NOT_BUILT)
01101                 ut->second.build_help_index(unit_cfg, races_);
01102 
01103             add_advancefrom(unit_cfg);
01104             break;
01105         case unit_type::WITHOUT_ANIMATIONS:
01106         case unit_type::FULL:
01107             if ( (ut->second.build_status() == unit_type::NOT_BUILT) ||
01108                 (ut->second.build_status() == unit_type::HELP_INDEX) )
01109             {
01110                 ut->second.build_full(unit_cfg, movement_types_, races_, unit_traits_);
01111 
01112                 if (ut->second.build_status() == unit_type::NOT_BUILT)
01113                     add_advancefrom(unit_cfg);
01114                 add_advancement(unit_cfg, ut->second);
01115             }
01116             break;
01117         default:
01118             break;
01119     }
01120 
01121     return ut->second;
01122 }
01123 
01124 void unit_type_data::unit_type_map_wrapper::add_advancefrom(const config& unit_cfg) const
01125 {
01126     //find the units this one can advance into and add advancefrom information for them
01127     std::vector<std::string> advance_to = utils::split(unit_cfg["advanceto"]);
01128     if ( (advance_to.size() > 0) && (advance_to[0] != "null") ){
01129         int count = 0;
01130         for (std::vector<std::string>::const_iterator i_adv = advance_to.begin(); i_adv != advance_to.end(); i_adv++){
01131             count++;
01132             DBG_UT << "Unit: " << unit_cfg["id"] << ", AdvanceTo " << count << ": " << *i_adv << "\n";
01133             unit_type_map::iterator itor_advanceto = types_.find(*i_adv);
01134             itor_advanceto->second.add_advancesfrom(unit_cfg["id"]);
01135         }
01136     }
01137 }
01138 
01139 void unit_type_data::unit_type_map_wrapper::add_advancement(const config& cfg, unit_type& to_unit) const
01140 {
01141     config::const_child_itors af;
01142     for(af = cfg.child_range("advancefrom"); af.first != af.second; ++af.first)
01143     {
01144         const std::string &from = (**af.first)["unit"];
01145         const int xp = lexical_cast_default<int>((**af.first)["experience"],0);
01146 
01147         unit_type_data::unit_type_map::iterator from_unit = types_.find(from);
01148 
01149         // Fix up advance_from references
01150         from_unit->second.add_advancement(to_unit, xp);
01151 
01152         // Store what unit this type advances from
01153         to_unit.add_advancesfrom(from);
01154     }
01155 }
01156 
01157 // This function is only meant to return the likely state of not_living
01158 // for a new recruit of this type. It should not be used to check if
01159 // a particular unit is living or not, use get_state("not_living") for that.
01160 bool unit_type::not_living() const
01161 {
01162         // If a unit hasn't been modified it starts out as living.
01163         bool not_living = false;
01164 
01165         // Look at all of the "musthave" traits to see if the not_living
01166         // status gets changed. In the unlikely event it gets changed
01167         // multiple times, we want to try to do it in the same order
01168         // that unit::apply_modifications does things.
01169         config::child_list const &mods = possible_traits();
01170         config::child_list::const_iterator j, j_end = mods.end();
01171         for(j = mods.begin(); j != j_end; ++j) {
01172             const string_map *vals = &((**j).values);
01173             string_map::const_iterator temp = vals->find("availability");
01174             if (temp == vals->end() || (*temp).second != "musthave") {
01175                 continue;
01176             }
01177             config::const_child_itors i;
01178             for(i = (**j).child_range("effect"); i.first != i.second; ++i.first) {
01179 
01180                 // See if the effect only applies to
01181                 // certain unit types But don't worry
01182                 // about gender checks, since we don't
01183                 // know what the gender of the
01184                 // hypothetical recruit is.
01185                 vals = &((**i.first).values);
01186                 temp = vals->find("unit_type");
01187                 //FIXME OBSOLETE Remove in 1.5.3
01188                 if(temp == vals->end())
01189                     temp = vals->find("unit");
01190                 if(temp != vals->end()) {
01191                     const std::vector<std::string>& types = utils::split((*temp).second);
01192                     if(std::find(types.begin(),types.end(),id()) == types.end()) {
01193                             continue;
01194                     }
01195                 }
01196 
01197                 // We're only interested in status changes.
01198                 temp = vals->find("apply_to");
01199                 if (temp == vals->end() || (*temp).second != "status") {
01200                     continue;
01201                 }
01202                 temp = vals->find("add");
01203                 if (temp != vals->end() && (*temp).second == "not_living") {
01204                     not_living = true;
01205                 }
01206                 temp = vals->find("remove");
01207                 if (temp != vals->end() && (*temp).second == "not_living") {
01208                     not_living = false;
01209                 }
01210             }
01211         }
01212 
01213         return not_living;
01214 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs