00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00214
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
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
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
00290
00291
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
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
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
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
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
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
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
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
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
01052 if(!in_advancefrom_) {
01053
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
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
01078
01079
01080
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
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
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
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
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
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
01213 if (itor == types_.end()){
01214
01215
01216
01217
01218 return NULL;
01219 }
01220
01221
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
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
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;
01329
01330
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;
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
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
01372
01373
01374 bool unit_type::not_living() const
01375 {
01376
01377 bool not_living = false;
01378
01379
01380
01381
01382
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
01391
01392
01393
01394
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
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
01438 small = def;
01439 big = def;
01440 }
01441 else if (small.empty())
01442 {
01443
01444
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 }