00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
00286
00287
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
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
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
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
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
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
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
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
00932
00933
00934
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
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
00998
00999
01000
01001
01002 }
01003
01004 std::pair<unit_type_map::iterator,bool> insertion =
01005 insert(std::pair<const std::string,unit_type>(id,unit_type()));
01006
01007
01008 lg::info(lg::config) << "added " << id << " to unit_type list (unit_type_data.unit_types)\n";
01009 }
01010
01011
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
01018 const std::string based_from = (*(**i.first).child("base_unit"))["id"];
01019 config from_cfg = find_config(based_from);
01020
01021
01022
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
01030 std::pair<unit_type_map::iterator,bool> insertion =
01031 insert(std::pair<const std::string,unit_type>(id,unit_type()));
01032
01033
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
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
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
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
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
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
01150 from_unit->second.add_advancement(to_unit, xp);
01151
01152
01153 to_unit.add_advancesfrom(from);
01154 }
01155 }
01156
01157
01158
01159
01160 bool unit_type::not_living() const
01161 {
01162
01163 bool not_living = false;
01164
01165
01166
01167
01168
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
01181
01182
01183
01184
01185 vals = &((**i.first).values);
01186 temp = vals->find("unit_type");
01187
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
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 }