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 "game_errors.hpp"
00024 #include "game_preferences.hpp"
00025 #include "gettext.hpp"
00026 #include "log.hpp"
00027 #include "pathfind.hpp"
00028 #include "random.hpp"
00029 #include "unit.hpp"
00030 #include "unit_types.hpp"
00031 #include "unit_abilities.hpp"
00032 #include "util.hpp"
00033 #include "serialization/string_utils.hpp"
00034 #include "halo.hpp"
00035 #include "game_display.hpp"
00036 #include "gamestatus.hpp"
00037 #include "actions.hpp"
00038 #include "game_events.hpp"
00039 #include "sound.hpp"
00040 #include "sdl_utils.hpp"
00041 #include "terrain_filter.hpp"
00042 #include "variable.hpp"
00043 #include "callable_objects.hpp"
00044 #include "formula.hpp"
00045
00046 #include <cassert>
00047 #include <climits>
00048 #include <ctime>
00049 #include <algorithm>
00050 #include <cstdlib>
00051 #include <iostream>
00052 #include <sstream>
00053 #include <iterator>
00054
00055 #define DBG_UT LOG_STREAM(debug, engine)
00056 #define LOG_UT LOG_STREAM(info, engine)
00057 #define ERR_UT LOG_STREAM(err, engine)
00058 #define ERR_CONFIG LOG_STREAM(err, config)
00059
00060 namespace {
00061 const std::string ModificationTypes[] = { "advance", "trait", "object" };
00062 const size_t NumModificationTypes = sizeof(ModificationTypes)/
00063 sizeof(*ModificationTypes);
00064
00065
00066
00067
00068
00069
00070 static std::vector<const unit *> units_with_cache;
00071 }
00072
00073 static bool compare_unit_values(unit const &a, unit const &b)
00074 {
00075 const int lvla = a.level();
00076 const int lvlb = b.level();
00077
00078 const int xpa = a.max_experience() - a.experience();
00079 const int xpb = b.max_experience() - b.experience();
00080
00081 return lvla > lvlb || (lvla == lvlb && xpa < xpb);
00082 }
00083
00084 void sort_units(std::vector< unit > &units)
00085 {
00086 std::sort(units.begin(), units.end(), compare_unit_values);
00087 }
00088
00089
00090 unit::unit(const unit& o):
00091 cfg_(o.cfg_),
00092 movement_b_(o.movement_b_),
00093 defense_b_(o.defense_b_),
00094 resistance_b_(o.resistance_b_),
00095 abilities_b_(o.abilities_b_),
00096
00097 advances_to_(o.advances_to_),
00098 type_(o.type_),
00099 race_(o.race_),
00100 id_(o.id_),
00101 name_(o.name_),
00102 underlying_id_(o.underlying_id_),
00103 type_name_(o.type_name_),
00104 undead_variation_(o.undead_variation_),
00105 variation_(o.variation_),
00106
00107 hit_points_(o.hit_points_),
00108 max_hit_points_(o.max_hit_points_),
00109 max_hit_points_b_(o.max_hit_points_b_),
00110 experience_(o.experience_),
00111 max_experience_(o.max_experience_),
00112 max_experience_b_(o.max_experience_b_),
00113 level_(o.level_),
00114 alignment_(o.alignment_),
00115 flag_rgb_(o.flag_rgb_),
00116 image_mods_(o.image_mods_),
00117
00118 unrenamable_(o.unrenamable_),
00119 side_(o.side_),
00120 gender_(o.gender_),
00121
00122 alpha_(o.alpha_),
00123
00124 unit_formula_(o.unit_formula_),
00125 formula_vars_(o.formula_vars_ ? new game_logic::map_formula_callable(*o.formula_vars_) : o.formula_vars_),
00126
00127 recruits_(o.recruits_),
00128
00129 movement_(o.movement_),
00130 max_movement_(o.max_movement_),
00131 max_movement_b_(o.max_movement_b_),
00132 movement_costs_(o.movement_costs_),
00133 defense_mods_(o.defense_mods_),
00134 hold_position_(o.hold_position_),
00135 end_turn_(o.end_turn_),
00136 resting_(o.resting_),
00137 attacks_left_(o.attacks_left_),
00138 max_attacks_(o.max_attacks_),
00139
00140 states_(o.states_),
00141 variables_(o.variables_),
00142 emit_zoc_(o.emit_zoc_),
00143 state_(o.state_),
00144
00145 overlays_(o.overlays_),
00146
00147 role_(o.role_),
00148 ai_special_(o.ai_special_),
00149 attacks_(o.attacks_),
00150 attacks_b_(o.attacks_b_),
00151 facing_(o.facing_),
00152
00153 traits_description_(o.traits_description_),
00154 unit_value_(o.unit_value_),
00155 goto_(o.goto_),
00156 interrupted_move_(o.interrupted_move_),
00157 flying_(o.flying_),
00158 is_fearless_(o.is_fearless_),
00159 is_healthy_(o.is_healthy_),
00160
00161 modification_descriptions_(o.modification_descriptions_),
00162
00163 animations_(o.animations_),
00164
00165 anim_(NULL),
00166
00167 frame_begin_time_(o.frame_begin_time_),
00168 unit_halo_(o.unit_halo_),
00169 unit_anim_halo_(o.unit_anim_halo_),
00170 getsHit_(o.getsHit_),
00171 refreshing_(o.refreshing_),
00172 hidden_(o.hidden_),
00173 draw_bars_(o.draw_bars_),
00174
00175 modifications_(o.modifications_),
00176 units_(o.units_),
00177 map_(o.map_),
00178 gamestatus_(o.gamestatus_),
00179 teams_(o.teams_)
00180 {
00181 next_idling_ = 0;
00182 unit_halo_ = halo::NO_HALO;
00183 unit_anim_halo_ = halo::NO_HALO;
00184 }
00185
00186 unit::unit(unit_map* unitmap, const gamemap* map,
00187 const gamestatus* game_status, const std::vector<team>* teams,const config& cfg,
00188 bool use_traits, game_state* state) :
00189 movement_(0),
00190 hold_position_(false),
00191 resting_(false),
00192 state_(STATE_STANDING),
00193 facing_(gamemap::location::SOUTH_EAST),
00194 flying_(false),
00195 anim_(NULL),
00196 next_idling_(0),
00197 frame_begin_time_(0),
00198 unit_halo_(halo::NO_HALO),
00199 unit_anim_halo_(halo::NO_HALO),
00200 draw_bars_(false),
00201 units_(unitmap),
00202 map_(map),
00203 gamestatus_(game_status),
00204 teams_(teams)
00205 {
00206 read(cfg, use_traits, state);
00207 getsHit_=0;
00208 end_turn_ = false;
00209 refreshing_ = false;
00210 hidden_ = false;
00211 game_config::add_color_info(cfg);
00212 }
00213
00214 unit::unit(const config& cfg,bool use_traits) : movement_(0),
00215 hold_position_(false), resting_(false), state_(STATE_STANDING),
00216 facing_(gamemap::location::SOUTH_EAST),
00217 flying_(false),anim_(NULL),next_idling_(0),frame_begin_time_(0),
00218 unit_halo_(halo::NO_HALO),unit_anim_halo_(halo::NO_HALO),draw_bars_(false),
00219 units_(NULL),map_(NULL), gamestatus_(NULL)
00220 {
00221 read(cfg,use_traits);
00222 getsHit_=0;
00223 end_turn_ = false;
00224 refreshing_ = false;
00225 hidden_ = false;
00226 }
00227
00228 void unit::clear_status_caches()
00229 {
00230 for(std::vector<const unit *>::const_iterator itor = units_with_cache.begin();
00231 itor != units_with_cache.end(); ++itor) {
00232 (*itor)->clear_visibility_cache();
00233 }
00234
00235 units_with_cache.clear();
00236 }
00237
00238 unit_race::GENDER unit::generate_gender(const unit_type& type, bool gen, game_state* state)
00239 {
00240 const std::vector<unit_race::GENDER>& genders = type.genders();
00241
00242
00243 cfg_["random_gender"] = "no";
00244 if(genders.empty() == false) {
00245 if(state) {
00246 return gen ? genders[state->rng().get_random() % genders.size()] : genders.front();
00247 } else {
00248 return gen ? genders[get_random() % genders.size()] : genders.front();
00249 }
00250 } else {
00251 return unit_race::MALE;
00252 }
00253 }
00254
00255 unit::unit(unit_map* unitmap, const gamemap* map,
00256 const gamestatus* game_status, const std::vector<team>* teams, const unit_type* t,
00257 int side, bool use_traits, bool dummy_unit, unit_race::GENDER gender, std::string variation) :
00258 variation_(variation), gender_(dummy_unit ? gender : generate_gender(*t,use_traits)), resting_(false),
00259 state_(STATE_STANDING), facing_(gamemap::location::SOUTH_EAST),draw_bars_(false),
00260 units_(unitmap),map_(map),gamestatus_(game_status),teams_(teams)
00261 {
00262 goto_ = gamemap::location();
00263 side_ = side;
00264 movement_ = 0;
00265 attacks_left_ = 0;
00266 experience_ = 0;
00267 cfg_["upkeep"]="full";
00268 advance_to(&t->get_gender_unit_type(gender_));
00269 if(dummy_unit == false) validate_side(side_);
00270 if(use_traits) {
00271
00272
00273
00274 name_ = generate_name();
00275 }
00276 generate_traits(!use_traits);
00277 apply_modifications();
00278 set_underlying_id();
00279
00280 unrenamable_ = false;
00281 anim_ = NULL;
00282 getsHit_=0;
00283 end_turn_ = false;
00284 hold_position_ = false;
00285 next_idling_ = 0;
00286 frame_begin_time_ = 0;
00287 unit_halo_ = halo::NO_HALO;
00288 unit_anim_halo_ = halo::NO_HALO;
00289 }
00290 unit::unit(const unit_type* t, int side, bool use_traits, bool dummy_unit, unit_race::GENDER gender, std::string variation) :
00291 variation_(variation),gender_(dummy_unit ? gender : generate_gender(*t,use_traits)),
00292 state_(STATE_STANDING),facing_(gamemap::location::SOUTH_EAST),draw_bars_(false),
00293 units_(NULL),map_(NULL),gamestatus_(NULL),teams_(NULL)
00294 {
00295 goto_ = gamemap::location();
00296 side_ = side;
00297 movement_ = 0;
00298 attacks_left_ = 0;
00299 experience_ = 0;
00300 cfg_["upkeep"]="full";
00301 advance_to(&t->get_gender_unit_type(gender_));
00302 if(dummy_unit == false) validate_side(side_);
00303 if(use_traits) {
00304
00305
00306
00307 name_ = generate_name();
00308 }
00309 generate_traits(!use_traits);
00310 apply_modifications();
00311 set_underlying_id();
00312
00313 unrenamable_ = false;
00314 next_idling_ = 0;
00315 frame_begin_time_ = 0;
00316 anim_ = NULL;
00317 getsHit_=0;
00318 end_turn_ = false;
00319 hold_position_ = false;
00320 }
00321
00322 unit::~unit()
00323 {
00324 clear_haloes();
00325
00326 delete anim_;
00327
00328
00329 std::vector<const unit *>::iterator itor =
00330 std::find(units_with_cache.begin(), units_with_cache.end(), this);
00331
00332 if(itor != units_with_cache.end()) {
00333 units_with_cache.erase(itor);
00334 }
00335 }
00336
00337
00338
00339 unit& unit::operator=(const unit& u)
00340 {
00341
00342 if (this != &u) {
00343 this->~unit();
00344 new (this) unit(u) ;
00345 }
00346 return *this ;
00347 }
00348
00349
00350
00351 void unit::set_game_context(unit_map* unitmap, const gamemap* map, const gamestatus* game_status, const std::vector<team>* teams)
00352 {
00353 units_ = unitmap;
00354 map_ = map;
00355 gamestatus_ = game_status;
00356 teams_ = teams;
00357
00358
00359 game_events::add_events(cfg_.get_children("event"),type_);
00360 }
00361
00362
00363 void unit::add_trait(std::string )
00364 {
00365
00366 apply_modifications();
00367 }
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381 void unit::generate_traits(bool musthaveonly, game_state* state)
00382 {
00383 LOG_UT << "Generating a trait for unit type " << type_id() << " with musthaveonly " << musthaveonly << "\n";
00384 const unit_type_data::unit_type_map::const_iterator type = unit_type_data::types().find(type_id());
00385
00386 if (type == unit_type_data::types().end()) {
00387 std::string error_message = _("Unknown unit type '$type|'");
00388 utils::string_map symbols;
00389 symbols["type"] = type_id();
00390 error_message = utils::interpolate_variables_into_string(error_message, &symbols);
00391 LOG_STREAM(err, engine) << "unit of type " << type_id() << " not found!\n";
00392 throw game::game_error(error_message);
00393 }
00394 std::vector<config*> candidate_traits = type->second.possible_traits();
00395 std::vector<config*> traits;
00396
00397
00398
00399 size_t t = 0;
00400 config::child_list const &mods = modifications_.get_children("trait");
00401 for(config::child_list::const_iterator j = mods.begin(), j_end = mods.end(); j != j_end; ++j) {
00402 ++t;
00403 size_t m = 0;
00404 for(size_t n = 0; n < candidate_traits.size(); ++n) {
00405 if((**(candidate_traits.begin()+m))["id"] == (**j)["id"]) {
00406 candidate_traits.erase(candidate_traits.begin()+m);
00407 } else {
00408 ++m;
00409 }
00410 }
00411 }
00412
00413
00414
00415
00416
00417 size_t num_traits = candidate_traits.size();
00418 size_t m = 0;
00419 for(size_t n = 0; n < num_traits; ++n) {
00420 if(!(**(candidate_traits.begin()+m))["availability"].empty() &&
00421 (**(candidate_traits.begin()+m))["availability"] == "musthave") {
00422 traits.push_back(candidate_traits[m]);
00423 candidate_traits.erase(candidate_traits.begin()+m);
00424 ++t;
00425 } else {
00426 ++m;
00427 }
00428 }
00429
00430
00431 if(!musthaveonly) {
00432
00433
00434 if(can_recruit()) {
00435 num_traits = candidate_traits.size();
00436 m = 0;
00437 for(size_t n = 0; n < num_traits; ++n) {
00438 if(!(**(candidate_traits.begin()+m))["availability"].empty() ||
00439 (**(candidate_traits.begin()+m))["availability"] != "any") {
00440 candidate_traits.erase(candidate_traits.begin()+m);
00441 } else {
00442 ++m;
00443 }
00444 }
00445 }
00446
00447
00448
00449 num_traits = type->second.num_traits();
00450 for(size_t n = t; n < num_traits && candidate_traits.empty() == false; ++n) {
00451 const size_t num =
00452 (state ? state->rng().get_random() : get_random()) % candidate_traits.size();
00453 traits.push_back(candidate_traits[num]);
00454 candidate_traits.erase(candidate_traits.begin()+num);
00455 }
00456
00457
00458
00459 cfg_["random_traits"]="no";
00460 }
00461
00462 for(std::vector<config*>::const_iterator j2 = traits.begin(); j2 != traits.end(); ++j2) {
00463 modifications_.add_child("trait",**j2);
00464 }
00465 }
00466
00467 void unit::advance_to(const unit_type* t, bool use_traits, game_state* state)
00468 {
00469 t = &t->get_gender_unit_type(gender_).get_variation(variation_);
00470 reset_modifications();
00471
00472 cfg_.clear_children("animation");
00473
00474 cfg_.clear_children("attack");
00475 cfg_.clear_children("abilities");
00476
00477 movement_costs_.clear();
00478 defense_mods_.clear();
00479
00480 if(t->movement_type().get_parent()) {
00481 cfg_.merge_with(t->movement_type().get_parent()->get_cfg());
00482 }
00483
00484 bool specific_profile = false;
00485 std::string profile;
00486 if (type() != NULL)
00487 {
00488 specific_profile = (cfg_["profile"] != type()->get_gender_unit_type(gender_).get_variation(variation_).cfg_["profile"]);
00489
00490 if (specific_profile)
00491 {
00492 profile = cfg_["profile"];
00493 }
00494 }
00495 cfg_.merge_with(t->cfg_);
00496 if (specific_profile)
00497 {
00498 cfg_["profile"] = profile;
00499 }
00500 cfg_.clear_children("male");
00501 cfg_.clear_children("female");
00502
00503 advances_to_ = t->advances_to();
00504
00505 race_ = t->race_;
00506 type_name_ = t->type_name();
00507 cfg_["description"] = t->unit_description();
00508 undead_variation_ = t->undead_variation();
00509 max_experience_ = t->experience_needed(false);
00510 level_ = t->level();
00511 alignment_ = t->alignment();
00512 alpha_ = t->alpha();
00513 hit_points_ = t->hitpoints();
00514 max_hit_points_ = t->hitpoints();
00515 max_movement_ = t->movement();
00516 emit_zoc_ = t->level();
00517 attacks_ = t->attacks();
00518 unit_value_ = t->cost();
00519 flying_ = t->movement_type().is_flying();
00520
00521 max_attacks_ = lexical_cast_default<int>(t->cfg_["attacks"],1);
00522
00523 animations_ = t->animations();
00524
00525 flag_rgb_ = t->flag_rgb();
00526
00527 backup_state();
00528
00529 bool do_heal = false;
00530
00531 if(utils::string_bool(cfg_["random_gender"], false)) {
00532 cfg_["gender"] = gender_string(generate_gender(*t,true));
00533 }
00534
00535 if(type_id()!=t->id()) {
00536 do_heal = true;
00537 type_ = t->id();
00538 }
00539
00540 if(utils::string_bool(cfg_["random_traits"], true)) {
00541 generate_traits(!use_traits, state);
00542 } else {
00543
00544
00545
00546
00547 generate_traits(true);
00548 }
00549
00550
00551
00552
00553
00554 apply_modifications();
00555
00556
00557
00558 if (do_heal) {
00559 heal_all();
00560 }
00561
00562 game_events::add_events(cfg_.get_children("event"),type_);
00563
00564 set_state("poisoned","");
00565 set_state("slowed","");
00566 set_state("stoned","");
00567 end_turn_ = false;
00568 refreshing_ = false;
00569 hidden_ = false;
00570 }
00571
00572 const unit_type* unit::type() const
00573 {
00574 std::map<std::string,unit_type>::const_iterator i = unit_type_data::types().find(type_id());
00575 if(i != unit_type_data::types().end()) {
00576 return &i->second;
00577 }
00578 if (!type_id().empty()) {
00579 LOG_STREAM(err, engine) << "type not found for nonempty unit " << type_id() << ", returning NULL!\n";
00580 }
00581
00582 return NULL;
00583 }
00584
00585 const std::string& unit::profile() const
00586 {
00587 if(cfg_["profile"] != "" && cfg_["profile"] != "unit_image") {
00588 return cfg_["profile"];
00589 }
00590 return absolute_image();
00591 }
00592
00593 SDL_Colour unit::hp_color() const
00594 {
00595 double unit_energy = 0.0;
00596 SDL_Color energy_colour = {0,0,0,0};
00597
00598 if(max_hitpoints() > 0) {
00599 unit_energy = double(hitpoints())/double(max_hitpoints());
00600 }
00601
00602 if(1.0 == unit_energy){
00603 energy_colour.r = 33;
00604 energy_colour.g = 225;
00605 energy_colour.b = 0;
00606 } else if(unit_energy > 1.0) {
00607 energy_colour.r = 100;
00608 energy_colour.g = 255;
00609 energy_colour.b = 100;
00610 } else if(unit_energy >= 0.75) {
00611 energy_colour.r = 170;
00612 energy_colour.g = 255;
00613 energy_colour.b = 0;
00614 } else if(unit_energy >= 0.5) {
00615 energy_colour.r = 255;
00616 energy_colour.g = 155;
00617 energy_colour.b = 0;
00618 } else if(unit_energy >= 0.25) {
00619 energy_colour.r = 255;
00620 energy_colour.g = 175;
00621 energy_colour.b = 0;
00622 } else {
00623 energy_colour.r = 255;
00624 energy_colour.g = 0;
00625 energy_colour.b = 0;
00626 }
00627 return energy_colour;
00628 }
00629
00630 SDL_Colour unit::xp_color() const
00631 {
00632 const SDL_Color near_advance_colour = {255,255,255,0};
00633 const SDL_Color mid_advance_colour = {150,255,255,0};
00634 const SDL_Color far_advance_colour = {0,205,205,0};
00635 const SDL_Color normal_colour = {0,160,225,0};
00636 const SDL_Color near_amla_colour = {225,0,255,0};
00637 const SDL_Color mid_amla_colour = {169,30,255,0};
00638 const SDL_Color far_amla_colour = {139,0,237,0};
00639 const SDL_Color amla_colour = {100,0,150,0};
00640 const bool near_advance = max_experience() - experience() <= game_config::kill_experience;
00641 const bool mid_advance = max_experience() - experience() <= game_config::kill_experience*2;
00642 const bool far_advance = max_experience() - experience() <= game_config::kill_experience*3;
00643
00644 SDL_Color colour=normal_colour;
00645 if(advances_to().size()){
00646 if(near_advance){
00647 colour=near_advance_colour;
00648 } else if(mid_advance){
00649 colour=mid_advance_colour;
00650 } else if(far_advance){
00651 colour=far_advance_colour;
00652 }
00653 } else if (get_modification_advances().size()){
00654 if(near_advance){
00655 colour=near_amla_colour;
00656 } else if(mid_advance){
00657 colour=mid_amla_colour;
00658 } else if(far_advance){
00659 colour=far_amla_colour;
00660 } else {
00661 colour=amla_colour;
00662 }
00663 }
00664 return(colour);
00665 }
00666
00667 void unit::set_movement(int moves)
00668 {
00669 hold_position_ = false;
00670 end_turn_ = false;
00671 movement_ = maximum<int>(0,minimum<int>(moves,max_movement_));
00672 }
00673
00674 void unit::new_turn()
00675 {
00676 end_turn_ = false;
00677 movement_ = total_movement();
00678 attacks_left_ = max_attacks_;
00679 set_state("hides","yes");
00680
00681 if (hold_position_) {
00682 end_turn_ = true;
00683 }
00684 }
00685 void unit::end_turn()
00686 {
00687 set_state("slowed","");
00688 if((movement_ != total_movement()) && !utils::string_bool(get_state("not_moved")) && (!is_healthy_ || attacks_left_ < max_attacks_)) {
00689 resting_ = false;
00690 }
00691 set_state("not_moved","");
00692
00693 set_interrupted_move(gamemap::location());
00694 }
00695 void unit::new_level()
00696 {
00697 role_ = "";
00698 ai_special_ = "";
00699
00700
00701 goto_ = gamemap::location();
00702
00703 remove_temporary_modifications();
00704
00705
00706 apply_modifications();
00707
00708 heal_all();
00709 set_state("slowed","");
00710 set_state("poisoned","");
00711 set_state("stoned","");
00712 }
00713 void unit::remove_temporary_modifications()
00714 {
00715 for(unsigned int i = 0; i != NumModificationTypes; ++i) {
00716 const std::string& mod = ModificationTypes[i];
00717 const config::child_list& mods = modifications_.get_children(mod);
00718 for(size_t j = 0; j != mods.size(); ++j) {
00719 if((*mods[j])["duration"] != "forever" && (*mods[j])["duration"] != "") {
00720 modifications_.remove_child(mod,j);
00721 --j;
00722 }
00723 }
00724 }
00725 }
00726
00727 void unit::heal(int amount)
00728 {
00729 int max_hp = max_hitpoints();
00730 if (hit_points_ < max_hp) {
00731 hit_points_ += amount;
00732 if (hit_points_ > max_hp) {
00733 hit_points_ = max_hp;
00734 }
00735 }
00736 if(hit_points_<1) {
00737 hit_points_ = 1;
00738 }
00739 }
00740
00741 const std::string unit::get_state(const std::string& state) const
00742 {
00743 std::map<std::string,std::string>::const_iterator i = states_.find(state);
00744 if(i != states_.end()) {
00745 return i->second;
00746 }
00747 return "";
00748 }
00749 void unit::set_state(const std::string& state, const std::string& value)
00750 {
00751 if(value == "") {
00752 std::map<std::string,std::string>::iterator i = states_.find(state);
00753 if(i != states_.end()) {
00754 states_.erase(i);
00755 }
00756 } else {
00757 states_[state] = value;
00758 }
00759 }
00760
00761
00762 bool unit::has_ability_by_id(const std::string& ability) const
00763 {
00764 const config* abil = cfg_.child("abilities");
00765 if(abil) {
00766 for(config::child_map::const_iterator i = abil->all_children().begin(); i != abil->all_children().end(); ++i) {
00767 for(config::child_list::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
00768 if((**j)["id"] == ability) {
00769 return true;
00770 }
00771 }
00772 }
00773 }
00774 return false;
00775 }
00776
00777 void unit::remove_ability_by_id(const std::string &ability)
00778 {
00779 config* abil = cfg_.child("abilities");
00780 if(abil) {
00781 config::all_children_iterator i = abil->ordered_begin();
00782 while(i != abil->ordered_end()) {
00783 if(i.get_child()["id"] == ability) {
00784 i = abil->erase(i);
00785 } else {
00786 ++i;
00787 }
00788 }
00789 }
00790 }
00791
00792 bool unit::matches_filter(const vconfig& cfg, const gamemap::location& loc, bool use_flat_tod) const
00793 {
00794 bool matches = true;
00795
00796 if(loc.valid()) {
00797 assert(units_ != NULL);
00798 scoped_xy_unit auto_store("this_unit", loc.x, loc.y, *units_);
00799 matches = internal_matches_filter(cfg, loc, use_flat_tod);
00800 } else {
00801
00802 matches = internal_matches_filter(cfg, loc, use_flat_tod);
00803 }
00804
00805
00806 vconfig::all_children_iterator cond = cfg.ordered_begin();
00807 vconfig::all_children_iterator cond_end = cfg.ordered_end();
00808 while(cond != cond_end)
00809 {
00810
00811 const std::string& cond_name = cond.get_key();
00812 const vconfig& cond_filter = cond.get_child();
00813
00814
00815 if(cond_name == "and") {
00816 matches = matches && matches_filter(cond_filter,loc,use_flat_tod);
00817 }
00818
00819 else if(cond_name == "or") {
00820 matches = matches || matches_filter(cond_filter,loc,use_flat_tod);
00821 }
00822
00823 else if(cond_name == "not") {
00824 matches = matches && !matches_filter(cond_filter,loc,use_flat_tod);
00825 }
00826
00827 ++cond;
00828 }
00829 return matches;
00830 }
00831
00832 bool unit::internal_matches_filter(const vconfig& cfg, const gamemap::location& loc, bool use_flat_tod) const
00833 {
00834 const t_string& t_id = cfg["id"];
00835 const t_string& t_description = cfg["description"];
00836 const t_string& t_speaker = cfg["speaker"];
00837 const t_string& t_type = cfg["type"];
00838 const t_string& t_ability = cfg["ability"];
00839 const t_string& t_side = cfg["side"];
00840 const t_string& t_weapon = cfg["has_weapon"];
00841 const t_string& t_role = cfg["role"];
00842 const t_string& t_ai_special = cfg["ai_special"];
00843 const t_string& t_race = cfg["race"];
00844 const t_string& t_gender = cfg["gender"];
00845 const t_string& t_canrecruit = cfg["canrecruit"];
00846 const t_string& t_level = cfg["level"];
00847 const t_string& t_defense = cfg["defense"];
00848 const t_string& t_movement_cost = cfg["movement_cost"];
00849
00850 const std::string& id = t_id;
00851 const std::string& description = t_description;
00852 const std::string& speaker = t_speaker;
00853 const std::string& type = t_type;
00854 const std::string& ability = t_ability;
00855 const std::string& side = t_side;
00856 const std::string& weapon = t_weapon;
00857 const std::string& role = t_role;
00858 const std::string& ai_special = t_ai_special;
00859 const std::string& race = t_race;
00860 const std::string& gender = t_gender;
00861 const std::string& canrecruit = t_canrecruit;
00862 const std::string& level = t_level;
00863 const std::string& defense = t_defense;
00864 const std::string& mvt_cost = t_movement_cost;
00865
00866
00867
00868
00869
00870
00871
00872 if(description.empty() == false && description != this->underlying_id() && description != name_) {
00873 return false;
00874 }
00875
00876 if(id.empty() == false && id != this->underlying_id()) {
00877 return false;
00878 }
00879
00880
00881 if(speaker.empty() == false && speaker != this->underlying_id()) {
00882 return false;
00883 }
00884
00885 if(cfg.has_child("filter_location")) {
00886 assert(map_ != NULL);
00887 assert(gamestatus_ != NULL);
00888 assert(units_ != NULL);
00889 const vconfig& t_cfg = cfg.child("filter_location");
00890 terrain_filter t_filter(t_cfg, *map_, *gamestatus_, *units_, use_flat_tod);
00891 if(!t_filter.match(loc)) {
00892 return false;
00893 }
00894 }
00895
00896 const t_string& cfg_x = cfg["x"];
00897 const t_string& cfg_y = cfg["y"];
00898 if(!cfg_x.empty() || !cfg_y.empty()){
00899 if(cfg_x == "recall" && cfg_y == "recall") {
00900
00901 if((!map_ && loc.valid()) || (map_ && map_->on_board(loc))) {
00902 return false;
00903 }
00904 } else if(!loc.matches_range(cfg_x, cfg_y)) {
00905 return false;
00906 }
00907 }
00908
00909 const std::string& this_type = type_id();
00910
00911
00912 if(type.empty() == false && type != this_type) {
00913
00914
00915
00916
00917 if(std::find(type.begin(),type.end(),',') != type.end() &&
00918 std::search(type.begin(),type.end(),this_type.begin(),
00919 this_type.end()) != type.end()) {
00920 const std::vector<std::string>& vals = utils::split(type);
00921
00922 if(std::find(vals.begin(),vals.end(),this_type) == vals.end()) {
00923 return false;
00924 }
00925 } else {
00926 return false;
00927 }
00928 }
00929
00930 if(ability.empty() == false && has_ability_by_id(ability) == false) {
00931 if(std::find(ability.begin(),ability.end(),',') != ability.end()) {
00932 const std::vector<std::string>& vals = utils::split(ability);
00933 bool has_ability = false;
00934 for(std::vector<std::string>::const_iterator this_ability = vals.begin(); this_ability != vals.end(); ++this_ability) {
00935 if(has_ability_by_id(*this_ability)) {
00936 has_ability = true;
00937 break;
00938 }
00939 }
00940 if(!has_ability) {
00941 return false;
00942 }
00943 } else {
00944 return false;
00945 }
00946 }
00947
00948 if(race.empty() == false && race_->id() != race) {
00949 return false;
00950 }
00951
00952 if(gender.empty() == false) {
00953 if(string_gender(gender) != this->gender()) {
00954 return false;
00955 }
00956 }
00957
00958 if(side.empty() == false && this->side() != lexical_cast_default<unsigned>(side)) {
00959 if(std::find(side.begin(),side.end(),',') != side.end()) {
00960 const std::vector<std::string>& vals = utils::split(side);
00961
00962 std::ostringstream s;
00963 s << (this->side());
00964 if(std::find(vals.begin(),vals.end(),s.str()) == vals.end()) {
00965 return false;
00966 }
00967 } else {
00968 return false;
00969 }
00970 }
00971
00972 if(weapon.empty() == false) {
00973 bool has_weapon = false;
00974 const std::vector<attack_type>& attacks = this->attacks();
00975 for(std::vector<attack_type>::const_iterator i = attacks.begin();
00976 i != attacks.end(); ++i) {
00977 if(i->id() == weapon) {
00978 has_weapon = true;
00979 }
00980 }
00981
00982 if(!has_weapon) {
00983 return false;
00984 }
00985 }
00986
00987 if(role.empty() == false && role_ != role) {
00988 return false;
00989 }
00990
00991 if(ai_special.empty() == false && ai_special_ != ai_special) {
00992 return false;
00993 }
00994
00995 if(canrecruit.empty() == false && utils::string_bool(canrecruit) != can_recruit()) {
00996 return false;
00997 }
00998
00999 if(level.empty() == false && level_ != lexical_cast_default<int>(level,-1)) {
01000 return false;
01001 }
01002
01003 if(defense.empty() == false && defense_modifier(map_->get_terrain(loc)) != lexical_cast_default<int>(defense,-1)) {
01004 return false;
01005 }
01006
01007 if(mvt_cost.empty() == false && movement_cost(map_->get_terrain(loc)) != lexical_cast_default<int>(mvt_cost,-1)) {
01008 return false;
01009 }
01010
01011
01012
01013
01014
01015 const vconfig::child_list& wmlcfgs = cfg.get_children("filter_wml");
01016 if (!wmlcfgs.empty()) {
01017 config unit_cfg;
01018 write(unit_cfg);
01019
01020 for(unsigned int i=0; i < wmlcfgs.size(); ++i) {
01021 if(!unit_cfg.matches(wmlcfgs[i].get_parsed_config())) {
01022 return false;
01023 }
01024 }
01025 }
01026
01027 const vconfig::child_list& wmlcfgs_old = cfg.get_children("wml_filter");
01028 if (!wmlcfgs_old.empty()) {
01029 config unit_cfg;
01030 write(unit_cfg);
01031
01032 for(unsigned int i=0; i < wmlcfgs_old.size(); ++i) {
01033 if(!unit_cfg.matches(wmlcfgs_old[i].get_parsed_config())) {
01034 return false;
01035 }
01036 }
01037 }
01038
01039 if (cfg.has_child("filter_vision")) {
01040 const vconfig::child_list& vis_filt = cfg.get_children("filter_vision");
01041 vconfig::child_list::const_iterator i, i_end = vis_filt.end();
01042 for (i = vis_filt.begin(); i != i_end; ++i) {
01043 bool visible = utils::string_bool((*i)["visible"], true);
01044 std::set<int> viewers;
01045 if (i->has_attribute("viewing_side")) {
01046 std::vector<std::pair<int,int> > ranges = utils::parse_ranges((*i)["viewing_side"]);
01047 std::vector<std::pair<int,int> >::const_iterator range, range_end = ranges.end();
01048 for (range = ranges.begin(); range != range_end; ++range) {
01049 for (int i=range->first; i<=range->second; ++i) {
01050 if (i > 0 && static_cast<size_t>(i) <= teams_->size()) {
01051 viewers.insert(i);
01052 }
01053 }
01054 }
01055 } else {
01056
01057 const team& my_team = (*teams_)[this->side()-1];
01058 for (size_t i = 1; i <= teams_->size(); ++i) {
01059 if (my_team.is_enemy(i)) {
01060 viewers.insert(i);
01061 }
01062 }
01063 }
01064 if (viewers.empty()) {
01065 return false;
01066 }
01067 std::set<int>::const_iterator viewer, viewer_end = viewers.end();
01068 for (viewer = viewers.begin(); viewer != viewer_end; ++viewer) {
01069 bool not_fogged = !(*teams_)[*viewer - 1].fogged(loc);
01070 bool not_hiding = !this->invisible(loc, *units_, *teams_ );
01071 if (visible != not_fogged && not_hiding) {
01072 return false;
01073 }
01074 }
01075 }
01076 }
01077
01078 if (cfg.has_child("filter_adjacent")) {
01079 assert(units_ && map_ && gamestatus_);
01080 gamemap::location adjacent[6];
01081 get_adjacent_tiles(loc, adjacent);
01082 vconfig::child_list::const_iterator i, i_end;
01083 const vconfig::child_list& adj_filt = cfg.get_children("filter_adjacent");
01084 for (i = adj_filt.begin(), i_end = adj_filt.end(); i != i_end; ++i) {
01085 int match_count=0;
01086 static std::vector<gamemap::location::DIRECTION> default_dirs
01087 = gamemap::location::parse_directions("n,ne,se,s,sw,nw");
01088 std::vector<gamemap::location::DIRECTION> dirs = (*i).has_attribute("adjacent")
01089 ? gamemap::location::parse_directions((*i)["adjacent"]) : default_dirs;
01090 std::vector<gamemap::location::DIRECTION>::const_iterator j, j_end = dirs.end();
01091 for (j = dirs.begin(); j != j_end; ++j) {
01092 unit_map::const_iterator unit_itor = units_->find(adjacent[*j]);
01093 if (unit_itor == units_->end()
01094 || !unit_itor->second.matches_filter(*i, unit_itor->first, use_flat_tod)) {
01095 continue;
01096 }
01097 if (!(*i).has_attribute("is_enemy")
01098 || utils::string_bool((*i)["is_enemy"]) == (*teams_)[this->side()-1].is_enemy(unit_itor->second.side())) {
01099 ++match_count;
01100 }
01101 }
01102 static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6");
01103 std::vector<std::pair<int,int> > counts = (*i).has_attribute("count")
01104 ? utils::parse_ranges((*i)["count"]) : default_counts;
01105 std::vector<std::pair<int,int> >::const_iterator count, count_end = counts.end();
01106 bool count_matches = false;
01107 for (count = counts.begin(); count != count_end && !count_matches; ++count) {
01108 if(count->first <= match_count && match_count <= count->second) {
01109 count_matches = true;
01110 }
01111 }
01112 if(!count_matches) {
01113 return false;
01114 }
01115 }
01116 }
01117
01118 if(cfg.has_attribute("find_in")) {
01119
01120 assert(gamestatus_ != NULL);
01121 variable_info vi(cfg["find_in"], false, variable_info::TYPE_CONTAINER);
01122 if(!vi.is_valid) return false;
01123 if(vi.explicit_index) {
01124 if(id_ != (vi.vars->get_children(vi.key)[vi.index])->get_attribute("id")) {
01125 return false;
01126 }
01127 } else {
01128 config::child_itors ch_itors = vi.vars->child_range(vi.key);
01129 for(; ch_itors.first != ch_itors.second; ++ch_itors.first) {
01130 if(id_ == (*ch_itors.first)->get_attribute("id")) {
01131 break;
01132 }
01133 }
01134 if(ch_itors.first == ch_itors.second) {
01135 return false;
01136 }
01137 }
01138 }
01139 if(cfg.has_attribute("formula")) {
01140 const unit_callable callable(std::pair<gamemap::location, unit>(loc,*this));
01141 const game_logic::formula form(cfg["formula"]);
01142 if(!form.execute(callable).as_bool()) {
01143 return false;
01144 }
01145 }
01146
01147 return true;
01148 }
01149
01150 void unit::read(const config& cfg, bool use_traits, game_state* state)
01151 {
01152 if(cfg["type"].empty()) {
01153 throw game::load_game_failed("Attempt to de-serialize a unit with no 'type' field (probably empty)");
01154 }
01155 std::map<std::string,unit_type>::const_iterator uti = unit_type_data::types().find(cfg["type"]);
01156 if(uti == unit_type_data::types().end()) {
01157 std::string error_message = _("Unknown unit type '$type|'");
01158 utils::string_map symbols;
01159 symbols["type"] = cfg["type"];
01160 error_message = utils::interpolate_variables_into_string(error_message, &symbols);
01161 LOG_STREAM(err, engine) << "unit of type " << cfg["type"] << " not found!\n";
01162 throw game::game_error(error_message);
01163 }
01164 type_ = cfg["type"];
01165
01166 cfg_ = cfg;
01167 side_ = lexical_cast_default<int>(cfg["side"]);
01168 if(side_ <= 0) {
01169 side_ = 1;
01170 }
01171
01172 validate_side(side_);
01173
01174
01175 hit_points_=1;
01176
01177
01178 max_hit_points_ = lexical_cast_default<int>(cfg["max_hitpoints"], 1);
01179 max_movement_ = lexical_cast_default<int>(cfg["max_moves"]);
01180 max_experience_ = lexical_cast_default<int>(cfg["max_experience"]);
01181
01182 if(cfg["gender"].empty()) {
01183 gender_ = generate_gender(uti->second, utils::string_bool(cfg_["random_gender"], false), state);
01184 } else {
01185 gender_ = string_gender(cfg["gender"]);
01186 }
01187
01188 variation_ = cfg["variation"];
01189
01190 id_ = cfg["id"];
01191 name_ = cfg["name"];
01192
01193 if (id_.empty())
01194 id_ = cfg["description"];
01195
01196 if (!cfg["user_description"].empty()) {
01197 name_ = cfg["user_description"];
01198 }
01199 std::string custom_unit_desc = cfg["description"];
01200
01201 if (custom_unit_desc.empty())
01202 custom_unit_desc = cfg["unit_description"];
01203
01204 underlying_id_ = id_;
01205 if(id_.empty()) {
01206 set_underlying_id();
01207 id_ = cfg["type"].c_str();
01208 }
01209
01210 role_ = cfg["role"];
01211 ai_special_ = cfg["ai_special"];
01212 overlays_ = utils::split(cfg["overlays"]);
01213 if(overlays_.size() == 1 && overlays_.front() == "") {
01214 overlays_.clear();
01215 }
01216 const config* const variables = cfg.child("variables");
01217 if(variables != NULL) {
01218 variables_ = *variables;
01219 cfg_.remove_child("variables",0);
01220 } else {
01221 variables_.clear();
01222 }
01223
01224 advances_to_ = utils::split(cfg["advances_to"]);
01225 if(advances_to_.size() == 1 && advances_to_.front() == "") {
01226 advances_to_.clear();
01227 }
01228
01229 type_name_ = cfg["language_name"];
01230 undead_variation_ = cfg["undead_variation"];
01231
01232 flag_rgb_ = cfg["flag_rgb"];
01233 alpha_ = lexical_cast_default<fixed_t>(cfg["alpha"]);
01234
01235 level_ = lexical_cast_default<int>(cfg["level"]);
01236 unit_value_ = lexical_cast_default<int>(cfg["value"]);
01237
01238 facing_ = gamemap::location::parse_direction(cfg["facing"]);
01239 if(facing_ == gamemap::location::NDIRECTIONS) facing_ = gamemap::location::SOUTH_EAST;
01240
01241 recruits_ = utils::split(cfg["recruits"]);
01242 if(recruits_.size() == 1 && recruits_.front() == "") {
01243 recruits_.clear();
01244 }
01245
01246 const config* mods = cfg.child("modifications");
01247 if(mods) {
01248 modifications_ = *mods;
01249 cfg_.remove_child("modifications",0);
01250 }
01251
01252 advance_to(&uti->second.get_gender_unit_type(gender_), use_traits, state);
01253 if(cfg["race"] != "") {
01254 const race_map::const_iterator race_it = unit_type_data::types().races().find(cfg["race"]);
01255 if(race_it != unit_type_data::types().races().end()) {
01256 race_ = &race_it->second;
01257 } else {
01258 static const unit_race dummy_race;
01259 race_ = &dummy_race;
01260 }
01261 }
01262 variation_ = cfg["variation"];
01263
01264 if(cfg["max_attacks"] != "") {
01265 max_attacks_ = minimum<int>(1,lexical_cast_default<int>(cfg["max_attacks"]));
01266 }
01267 attacks_left_ = lexical_cast_default<int>(cfg["attacks_left"], max_attacks_);
01268
01269 if(cfg["zoc"] != "") {
01270 emit_zoc_ = lexical_cast_default<int>(cfg["zoc"]);
01271 }
01272 if(cfg["flying"] != "") {
01273 flying_ = utils::string_bool(cfg["flying"]);
01274 }
01275 if(custom_unit_desc != "") {
01276 cfg_["description"] = custom_unit_desc;
01277 }
01278
01279 if(cfg["profile"] != "") {
01280 cfg_["profile"] = cfg["profile"];
01281 }
01282
01283
01284 unit_formula_ = cfg["formula"];
01285
01286 const config* ai_vars = cfg.child("ai_vars");
01287 if (ai_vars)
01288 {
01289 formula_vars_ = new game_logic::map_formula_callable;
01290
01291 variant var;
01292 for(string_map::const_iterator i = ai_vars->values.begin(); i != ai_vars->values.end(); ++i)
01293 {
01294 var.serialize_from_string(i->second);
01295 formula_vars_->add(i->first, var);
01296 }
01297 } else {
01298 formula_vars_ = game_logic::map_formula_callable_ptr();
01299 }
01300
01301 cfg_.clear_children("ai_vars");
01302
01303 cfg_.clear_children("attack");
01304 const config* status_flags = cfg.child("status");
01305 if(status_flags) {
01306 for(string_map::const_iterator st = status_flags->values.begin(); st != status_flags->values.end(); ++st) {
01307
01308 if(st->first == "stone") {
01309 states_["stoned"] = st->second;
01310 } else {
01311 states_[st->first] = st->second;
01312 }
01313 }
01314 cfg_.remove_child("status",0);
01315 }
01316 if(cfg["ai_special"] == "guardian") {
01317 set_state("guardian","yes");
01318 }
01319
01320
01321 unit_animation::fill_initial_animations(animations_,cfg_);
01322
01323 cfg_.clear_children("animation");
01324 cfg_.clear_children("defend");
01325 cfg_.clear_children("teleport_anim");
01326 cfg_.clear_children("extra_anim");
01327 cfg_.clear_children("death");
01328 cfg_.clear_children("movement_anim");
01329 cfg_.clear_children("standing_anim");
01330 cfg_.clear_children("healing_anim");
01331 cfg_.clear_children("victory_anim");
01332 cfg_.clear_children("idle_anim");
01333 cfg_.clear_children("levelin_anim");
01334 cfg_.clear_children("levelout_anim");
01335 cfg_.clear_children("healed_anim");
01336 cfg_.clear_children("poison_anim");
01337
01338 if(cfg["hitpoints"] != "") {
01339 hit_points_ = lexical_cast_default<int>(cfg["hitpoints"]);
01340 } else {
01341 hit_points_ = max_hit_points_;
01342 }
01343 goto_.x = lexical_cast_default<int>(cfg["goto_x"]) - 1;
01344 goto_.y = lexical_cast_default<int>(cfg["goto_y"]) - 1;
01345 if(cfg["moves"] != "") {
01346 movement_ = lexical_cast_default<int>(cfg["moves"]);
01347 if(movement_ < 0) {
01348 attacks_left_ = 0;
01349 movement_ = 0;
01350 }
01351 } else {
01352 movement_ = max_movement_;
01353 }
01354 experience_ = lexical_cast_default<int>(cfg["experience"]);
01355 resting_ = utils::string_bool(cfg["resting"]);
01356 unrenamable_ = utils::string_bool(cfg["unrenamable"]);
01357 if(cfg["alignment"]=="lawful") {
01358 alignment_ = unit_type::LAWFUL;
01359 } else if(cfg["alignment"]=="neutral") {
01360 alignment_ = unit_type::NEUTRAL;
01361 } else if(cfg["alignment"]=="chaotic") {
01362 alignment_ = unit_type::CHAOTIC;
01363 } else if(cfg["type"]=="") {
01364 alignment_ = unit_type::NEUTRAL;
01365 }
01366
01367 if(utils::string_bool(cfg["generate_name"]) || utils::string_bool(cfg["generate_description"])) {
01368 name_ = generate_name(state ? &(state->rng()) : 0);
01369 cfg_["generate_name"] = "";
01370 }
01371
01372 game_events::add_events(cfg_.get_children("event"),type_);
01373
01374 if(cfg_["upkeep"].empty()) {
01375 cfg_["upkeep"] = "full";
01376 }
01377 }
01378 void unit::write(config& cfg) const
01379 {
01380
01381 std::string x = cfg["x"];
01382 std::string y = cfg["y"];
01383 cfg.append(cfg_);
01384 cfg.clear_children("movement_costs");
01385 cfg.clear_children("defense");
01386 cfg.clear_children("resistance");
01387 cfg.clear_children("abilities");
01388 cfg.add_child("movement_costs",movement_b_);
01389 cfg.add_child("defense",defense_b_);
01390 cfg.add_child("resistance",resistance_b_);
01391 cfg.add_child("abilities",abilities_b_);
01392 cfg["x"] = x;
01393 cfg["y"] = y;
01394 std::map<std::string,unit_type>::const_iterator uti = unit_type_data::types().find(type_id());
01395 const unit_type* ut = NULL;
01396 if(uti != unit_type_data::types().end()) {
01397 ut = &uti->second.get_gender_unit_type(gender_).get_variation(variation_);
01398 }
01399 if(ut && cfg["description"] == ut->unit_description()) {
01400 cfg["description"] = "";
01401 }
01402
01403 std::stringstream hp;
01404 hp << hit_points_;
01405 cfg["hitpoints"] = hp.str();
01406 std::stringstream hpm;
01407 hpm << max_hit_points_b_;
01408 cfg["max_hitpoints"] = hpm.str();
01409
01410 std::stringstream xp;
01411 xp << experience_;
01412 cfg["experience"] = xp.str();
01413 std::stringstream xpm;
01414 xpm << max_experience_b_;
01415 cfg["max_experience"] = xpm.str();
01416
01417 std::stringstream sd;
01418 sd << side_;
01419 cfg["side"] = sd.str();
01420
01421 cfg["type"] = type_id();
01422
01423
01424 if (has_formula())
01425 cfg["formula"] = unit_formula_;
01426
01427
01428 if (formula_vars_ && formula_vars_->empty() == false)
01429 {
01430 cfg.add_child("ai_vars");
01431 config* ai_vars = cfg.child("ai_vars");
01432
01433 std::string str;
01434 for(game_logic::map_formula_callable::const_iterator i = formula_vars_->begin(); i != formula_vars_->end(); ++i)
01435 {
01436 i->second.serialize_to_string(str);
01437 if (!str.empty())
01438 {
01439 (*ai_vars)[i->first] = str;
01440 str.clear();
01441 }
01442 }
01443 }
01444
01445
01446 cfg["gender"] = gender_string(gender_);
01447
01448 cfg["variation"] = variation_;
01449
01450 cfg["role"] = role_;
01451 cfg["ai_special"] = ai_special_;
01452 cfg["flying"] = flying_ ? "yes" : "no";
01453
01454 config status_flags;
01455 for(std::map<std::string,std::string>::const_iterator st = states_.begin(); st != states_.end(); ++st) {
01456 status_flags[st->first] = st->second;
01457 }
01458
01459 cfg.clear_children("variables");
01460 cfg.add_child("variables",variables_);
01461 cfg.clear_children("status");
01462 cfg.add_child("status",status_flags);
01463
01464 cfg["overlays"] = utils::join(overlays_);
01465
01466 cfg["name"] = name_;
01467 cfg["id"] = underlying_id_;
01468
01469 if(can_recruit())
01470 cfg["canrecruit"] = "yes";
01471
01472 cfg["facing"] = gamemap::location::write_direction(facing_);
01473
01474 cfg["goto_x"] = lexical_cast_default<std::string>(goto_.x+1);
01475 cfg["goto_y"] = lexical_cast_default<std::string>(goto_.y+1);
01476
01477 cfg["moves"] = lexical_cast_default<std::string>(movement_);
01478 cfg["max_moves"] = lexical_cast_default<std::string>(max_movement_b_);
01479
01480 cfg["resting"] = resting_ ? "yes" : "no";
01481
01482 cfg["advances_to"] = utils::join(advances_to_);
01483
01484 cfg["race"] = race_->id();
01485 cfg["language_name"] = type_name_;
01486 cfg["undead_variation"] = undead_variation_;
01487 cfg["variation"] = variation_;
01488 cfg["level"] = lexical_cast_default<std::string>(level_);
01489 switch(alignment_) {
01490 case unit_type::LAWFUL:
01491 cfg["alignment"] = "lawful";
01492 break;
01493 case unit_type::NEUTRAL:
01494 cfg["alignment"] = "neutral";
01495 break;
01496 case unit_type::CHAOTIC:
01497 cfg["alignment"] = "chaotic";
01498 break;
01499 default:
01500 cfg["alignment"] = "neutral";
01501 }
01502 cfg["flag_rgb"] = flag_rgb_;
01503 cfg["unrenamable"] = unrenamable_ ? "yes" : "no";
01504 cfg["alpha"] = lexical_cast_default<std::string>(alpha_);
01505
01506 cfg["recruits"] = utils::join(recruits_);
01507 cfg["attacks_left"] = lexical_cast_default<std::string>(attacks_left_);
01508 cfg["max_attacks"] = lexical_cast_default<std::string>(max_attacks_);
01509 cfg["zoc"] = lexical_cast_default<std::string>(emit_zoc_);
01510 cfg.clear_children("attack");
01511 for(std::vector<attack_type>::const_iterator i = attacks_b_.begin(); i != attacks_b_.end(); ++i) {
01512 cfg.add_child("attack",i->get_cfg());
01513 }
01514 cfg["value"] = lexical_cast_default<std::string>(unit_value_);
01515 cfg["cost"] = lexical_cast_default<std::string>(unit_value_);
01516 cfg.clear_children("modifications");
01517 cfg.add_child("modifications",modifications_);
01518
01519 }
01520
01521
01522 const surface unit::still_image(bool scaled) const
01523 {
01524 image::locator image_loc;
01525
01526 #ifdef LOW_MEM
01527 image_loc = image::locator(absolute_image());
01528 #else
01529 std::string mods=image_mods();
01530 if(mods.size()){
01531 image_loc = image::locator(absolute_image(),mods);
01532 } else {
01533 image_loc = image::locator(absolute_image());
01534 }
01535 #endif
01536
01537 surface unit_image(image::get_image(image_loc, scaled ? image::SCALED_TO_ZOOM : image::UNSCALED));
01538 return unit_image;
01539 }
01540
01541 void unit::set_standing(const gamemap::location& loc, bool with_bars)
01542 {
01543 game_display * disp = game_display::get_singleton();
01544 gamemap::location arr[6];
01545 get_adjacent_tiles(loc, arr);
01546 for (unsigned int i = 0; i < 6; i++) {
01547 disp->invalidate(arr[i]);
01548 }
01549 if (preferences::show_standing_animations()) {
01550 start_animation(INT_MAX,loc,choose_animation(*disp,loc,"standing"),with_bars,true,"",0,STATE_STANDING);
01551 } else {
01552 start_animation(INT_MAX,loc,choose_animation(*disp,loc,"_disabled_"),with_bars,true,"",0,STATE_STANDING);
01553 }
01554 }
01555
01556
01557
01558
01559 void unit::set_idling(const game_display &disp,const gamemap::location& loc)
01560 {
01561 start_animation(INT_MAX,loc,choose_animation(disp,loc,"idling"),true,false,"",0,STATE_FORGET);
01562 }
01563
01564 void unit::set_selecting(const game_display &disp,const gamemap::location& loc)
01565 {
01566 if (preferences::show_standing_animations()) {
01567 start_animation(INT_MAX,loc,choose_animation(disp,loc,"selected"),true,false,"",0,STATE_FORGET);
01568 } else {
01569 start_animation(INT_MAX,loc,choose_animation(disp,loc,"_disabled_selected_"),true,false,"",0,STATE_STANDING);
01570 }
01571 }
01572
01573 void unit::start_animation(const int start_time, const gamemap::location &loc,const unit_animation * animation,bool with_bars,bool cycles,const std::string text, const Uint32 text_color,STATE state)
01574 {
01575 const game_display * disp = game_display::get_singleton();
01576
01577 const bool accelerate = (state != STATE_FORGET && state != STATE_STANDING);
01578 if(!animation) {
01579 set_standing(loc,with_bars);
01580 return ;
01581 }
01582 state_ =state;
01583 draw_bars_ = with_bars;
01584 if(anim_) delete anim_;
01585 anim_ = new unit_animation(*animation);
01586 const int real_start_time = start_time == INT_MAX ? anim_->get_begin_time() : start_time;
01587 anim_->start_animation(real_start_time,loc, loc.get_direction(facing_), cycles,text,text_color,accelerate);
01588 frame_begin_time_ = anim_->get_begin_time() -1;
01589 if (disp->idle_anim()) {
01590 next_idling_ = get_current_animation_tick()
01591 + static_cast<int>((20000 + rand() % 20000) * disp->idle_anim_rate());
01592 } else {
01593 next_idling_ = INT_MAX;
01594 }
01595 }
01596
01597
01598 void unit::set_facing(gamemap::location::DIRECTION dir) {
01599 if(dir != gamemap::location::NDIRECTIONS) {
01600 facing_ = dir;
01601 }
01602
01603 }
01604
01605 void unit::redraw_unit(game_display& disp, const gamemap::location& loc, const bool fake)
01606 {
01607 const gamemap & map = disp.get_map();
01608 if(!loc.valid() || hidden_ || disp.fogged(loc)
01609 || (invisible(loc,disp.get_units(),disp.get_teams())
01610 && disp.get_teams()[disp.viewing_team()].is_enemy(side())))
01611 {
01612 clear_haloes();
01613 if(anim_) {
01614 anim_->update_last_draw_time();
01615 }
01616 return;
01617 }
01618 if(refreshing_) {
01619 return;
01620 }
01621 refreshing_ = true;
01622
01623
01624 if(!anim_) {
01625 set_standing(loc);
01626 }
01627 anim_->update_last_draw_time();
01628 frame_parameters params;
01629 const t_translation::t_terrain terrain = map.get_terrain(loc);
01630 const terrain_type& terrain_info = map.get_terrain_info(terrain);
01631
01632 params.submerge= is_flying() ? 0.01 : terrain_info.unit_submerge();
01633
01634 if(invisible(loc,disp.get_units(),disp.get_teams()) &&
01635 params.highlight_ratio > 0.5) {
01636 params.highlight_ratio = 0.5;
01637 }
01638 if(loc == disp.selected_hex() && params.highlight_ratio == 1.0) {
01639 params.highlight_ratio = 1.5;
01640 }
01641 int height_adjust = static_cast<int>(terrain_info.unit_height_adjust() * disp.get_zoom_factor());
01642 if (is_flying() && height_adjust < 0) {
01643 height_adjust = 0;
01644 }
01645 params.y -= height_adjust;
01646 params.halo_y -= height_adjust;
01647 if (utils::string_bool(get_state("poisoned")) ){
01648 params.blend_with = disp.rgb(0,255,0);
01649 params.blend_ratio = 0.25;
01650 }
01651 params.image_mod = image_mods();
01652
01653 const frame_parameters adjusted_params = anim_->get_current_params(params,true);
01654
01655
01656
01657 #ifndef LOW_MEM
01658 bool facing_west = facing_ == gamemap::location::NORTH_WEST || facing_ == gamemap::location::SOUTH_WEST;
01659 #else
01660 bool facing_west = false;
01661 #endif
01662 const gamemap::location dst = loc.get_direction(facing_);
01663 const int xsrc = disp.get_location_x(loc);
01664 const int ysrc = disp.get_location_y(loc);
01665 const int xdst = disp.get_location_x(dst);
01666 const int ydst = disp.get_location_y(dst);
01667 const int drawing_order = gamemap::get_drawing_order(loc);
01668
01669
01670 if(frame_begin_time_ != anim_->get_current_frame_begin_time()) {
01671 frame_begin_time_ = anim_->get_current_frame_begin_time();
01672 if(!adjusted_params.sound.empty()) {
01673 sound::play_sound(adjusted_params.sound);
01674 }
01675 if(!adjusted_params.text.empty() ) {
01676 game_display::get_singleton()->float_label(loc,adjusted_params.text,
01677 (adjusted_params.text_color & 0x00FF0000) >> 16,
01678 (adjusted_params.text_color & 0x0000FF00) >> 8,
01679 (adjusted_params.text_color & 0x000000FF) >> 0);
01680 }
01681 }
01682
01683 int d2 = disp.hex_size() / 2;
01684 const int x = static_cast<int>(adjusted_params.offset * xdst + (1.0-adjusted_params.offset) * xsrc) + d2;
01685 const int y = static_cast<int>(adjusted_params.offset * ydst + (1.0-adjusted_params.offset) * ysrc) + d2;
01686
01687
01688 if(unit_halo_ == halo::NO_HALO && !image_halo().empty()) {
01689 unit_halo_ = halo::add(0, 0, image_halo(), gamemap::location(-1, -1));
01690 }
01691 if(unit_halo_ != halo::NO_HALO) {
01692 halo::set_location(unit_halo_, x, y);
01693 }
01694
01695 if(unit_anim_halo_ != halo::NO_HALO) {
01696 halo::remove(unit_anim_halo_);
01697 unit_anim_halo_ = halo::NO_HALO;
01698 }
01699 if(!adjusted_params.halo.empty()) {
01700 int dx = static_cast<int>(adjusted_params.halo_x * disp.get_zoom_factor());
01701 int dy = static_cast<int>(adjusted_params.halo_y * disp.get_zoom_factor());
01702 if (facing_west) dx = -dx;
01703 unit_anim_halo_ = halo::add(x + dx, y+ dy,
01704 adjusted_params.halo, gamemap::location(-1, -1),
01705 facing_west ? halo::HREVERSE : halo::NORMAL);
01706 }
01707
01708
01709 image::locator image_loc = image::locator();
01710 #ifndef LOW_MEM
01711 if(facing_ != gamemap::location::NORTH && facing_ != gamemap::location::SOUTH) {
01712 image_loc = adjusted_params.image_diagonal;
01713 }
01714 if(image_loc.is_void()|| image_loc.get_filename() == "") {
01715 image_loc = adjusted_params.image;
01716 }
01717 if(image_loc.is_void()|| image_loc.get_filename() == "") {
01718 image_loc = absolute_image();
01719 }
01720 image_loc = image::locator(image_loc,adjusted_params.image_mod);
01721 #else
01722 image_loc = absolute_image();
01723 #endif
01724
01725
01726
01727 surface image(image::get_image(image_loc, image::SCALED_TO_ZOOM, true));
01728
01729 if(image == NULL) {
01730 image = still_image(true);
01731 }
01732
01733 bool stoned = utils::string_bool(get_state("stoned"));
01734
01735
01736
01737
01738 bool draw_bars = draw_bars_ && !fake;
01739 if (draw_bars) {
01740 const int d = disp.hex_size();
01741 SDL_Rect unit_rect = {xsrc, ysrc +adjusted_params.y, d, d};
01742 draw_bars = rects_overlap(unit_rect, disp.map_outside_area());
01743 }
01744
01745 surface ellipse_front(NULL);
01746 surface ellipse_back(NULL);
01747 int ellipse_floating = 0;
01748 if(draw_bars && preferences::show_side_colours()) {
01749
01750
01751
01752 ellipse_floating = static_cast<int>(adjusted_params.submerge * disp.hex_size() / 2);
01753
01754 std::string ellipse=image_ellipse();
01755 if(ellipse.empty()){
01756 ellipse="misc/ellipse";
01757 }
01758
01759 const char* const selected = disp.selected_hex() == loc ? "selected-" : "";
01760
01761
01762 char buf[100];
01763 std::string tc=team::get_side_colour_index(side_);
01764
01765 snprintf(buf,sizeof(buf),"%s-%stop.png~RC(ellipse_red>%s)",ellipse.c_str(),selected,tc.c_str());
01766 ellipse_back.assign(image::get_image(image::locator(buf), image::SCALED_TO_ZOOM));
01767 snprintf(buf,sizeof(buf),"%s-%sbottom.png~RC(ellipse_red>%s)",ellipse.c_str(),selected,tc.c_str());
01768 ellipse_front.assign(image::get_image(image::locator(buf), image::SCALED_TO_ZOOM));
01769 }
01770
01771
01772
01773
01774 if (ellipse_back != NULL) {
01775 disp.drawing_buffer_add(display::LAYER_UNIT_BG, drawing_order,
01776 display::tblit(xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_back));
01777 }
01778
01779 if (image != NULL) {
01780 const int tmp_x = adjusted_params.x +x - image->w/2;
01781 const int tmp_y = adjusted_params.y +y - image->h/2;
01782 disp.render_unit_image(tmp_x, tmp_y, fake, drawing_order, image, facing_west, stoned,
01783 ftofxp(adjusted_params.highlight_ratio), adjusted_params.blend_with, adjusted_params.blend_ratio, adjusted_params.submerge);
01784 }
01785
01786 if (ellipse_front != NULL) {
01787 disp.drawing_buffer_add(display::LAYER_UNIT_FG, drawing_order,
01788 display::tblit(xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_front));
01789 }
01790
01791 if(draw_bars) {
01792 const std::string* movement_file = NULL;
01793 const std::string* energy_file = &game_config::energy_image;
01794
01795 if(size_t(side()) != disp.viewing_team()+1) {
01796 if(disp.team_valid() &&
01797 disp.get_teams()[disp.viewing_team()].is_enemy(side())) {
01798 movement_file = &game_config::enemy_ball_image;
01799 } else {
01800 movement_file = &game_config::ally_ball_image;
01801 }
01802 } else {
01803 movement_file = &game_config::moved_ball_image;
01804 if(disp.playing_team() == disp.viewing_team() && !user_end_turn()) {
01805 if (movement_left() == total_movement()) {
01806 movement_file = &game_config::unmoved_ball_image;
01807
01808 } else if(unit_can_move(loc,disp.get_units(),map,disp.get_teams())) {
01809 movement_file = &game_config::partmoved_ball_image;
01810 }
01811 }
01812 }
01813
01814 surface orb(image::get_image(*movement_file,image::SCALED_TO_ZOOM));
01815 if (orb != NULL) {
01816 disp.drawing_buffer_add(display::LAYER_UNIT_BAR,
01817 drawing_order, display::tblit(xsrc, ysrc +adjusted_params.y, orb));
01818 }
01819
01820 double unit_energy = 0.0;
01821 if(max_hitpoints() > 0) {
01822 unit_energy = double(hitpoints())/double(max_hitpoints());
01823 }
01824 #ifdef USE_TINY_GUI
01825 const int bar_shift = static_cast<int>(-2.5*disp.get_zoom_factor());
01826 #else
01827 const int bar_shift = static_cast<int>(-5*disp.get_zoom_factor());
01828 #endif
01829 const int hp_bar_height = static_cast<int>(max_hitpoints()*game_config::hp_bar_scaling);
01830
01831 const fixed_t bar_alpha = (loc == disp.mouseover_hex() || loc == disp.selected_hex()) ? ftofxp(1.0): ftofxp(0.8);
01832
01833 disp.draw_bar(*energy_file, xsrc+bar_shift, ysrc +adjusted_params.y,
01834 drawing_order, hp_bar_height, unit_energy,hp_color(), bar_alpha);
01835
01836 if(experience() > 0 && can_advance()) {
01837 const double filled = double(experience())/double(max_experience());
01838
01839 const int xp_bar_height = static_cast<int>(max_experience()*game_config::xp_bar_scaling / maximum<int>(level_,1));
01840
01841 SDL_Color colour=xp_color();
01842 disp.draw_bar(*energy_file, xsrc, ysrc +adjusted_params.y,
01843 drawing_order, xp_bar_height, filled, colour, bar_alpha);
01844 }
01845
01846 if (can_recruit()) {
01847 surface crown(image::get_image("misc/leader-crown.png",image::SCALED_TO_ZOOM));
01848 if(!crown.null()) {
01849
01850
01851
01852 disp.drawing_buffer_add(display::LAYER_UNIT_BAR,
01853 drawing_order, display::tblit(xsrc, ysrc +adjusted_params.y, crown));
01854 }
01855 }
01856
01857 for(std::vector<std::string>::const_iterator ov = overlays().begin(); ov != overlays().end(); ++ov) {
01858 const surface ov_img(image::get_image(*ov, image::SCALED_TO_ZOOM));
01859 if(ov_img != NULL) {
01860 disp.drawing_buffer_add(display::LAYER_UNIT_BAR,
01861 drawing_order, display::tblit(xsrc, ysrc +adjusted_params.y, ov_img));
01862 }
01863 }
01864 }
01865
01866 anim_->redraw(params);
01867 refreshing_ = false;
01868 }
01869
01870 void unit::clear_haloes()
01871 {
01872 if(unit_halo_ != halo::NO_HALO) {
01873 halo::remove(unit_halo_);
01874 unit_halo_ = halo::NO_HALO;
01875 }
01876 if(unit_anim_halo_ != halo::NO_HALO) {
01877 halo::remove(unit_anim_halo_);
01878 unit_anim_halo_ = halo::NO_HALO;
01879 }
01880 }
01881
01882 std::set<gamemap::location> unit::overlaps(const gamemap::location &loc) const
01883 {
01884 std::set<gamemap::location> over;
01885
01886
01887 if(get_animation()) {
01888 frame_parameters params;
01889 game_display * disp = game_display::get_singleton();
01890 const gamemap & map = disp->get_map();
01891 const t_translation::t_terrain terrain = map.get_terrain(loc);
01892 const terrain_type& terrain_info = map.get_terrain_info(terrain);
01893 if(!params.submerge) params.submerge= is_flying() ? 0.0 : terrain_info.unit_submerge();
01894
01895 if(invisible(loc,disp->get_units(),disp->get_teams()) &&
01896 params.highlight_ratio > 0.5) {
01897 params.highlight_ratio = 0.5;
01898 }
01899 if(loc == disp->selected_hex() && params.highlight_ratio == 1.0) {
01900 params.highlight_ratio = 1.5;
01901 }
01902 int height_adjust = static_cast<int>(terrain_info.unit_height_adjust() * disp->get_zoom_factor());
01903 if (is_flying() && height_adjust < 0) {
01904 height_adjust = 0;
01905 }
01906 params.y -= height_adjust;
01907 params.halo_y -= height_adjust;
01908 if (utils::string_bool(get_state("poisoned")) ){
01909 params.blend_with = disp->rgb(0,255,0);
01910 params.blend_ratio = 0.25;
01911 }
01912 params.image_mod = image_mods();
01913
01914 frame_parameters adjusted_params= anim_->get_current_params(params);
01915
01916 if(adjusted_params.offset != 0) {
01917 gamemap::location::DIRECTION dir = (adjusted_params.offset > 0) ? facing_ : loc.get_opposite_dir(facing_);
01918 gamemap::location adj_loc = loc.get_direction(dir);
01919 over.insert(adj_loc);
01920 gamemap::location arr[6];
01921 get_adjacent_tiles(adj_loc, arr);
01922 for (unsigned int i = 0; i < 6; i++) {
01923 over.insert(arr[i]);
01924 }
01925 }
01926 get_animation()->invalidate(params);
01927 }
01928
01929
01930 if (abilities_affects_adjacent())
01931 {
01932 gamemap::location arr[6];
01933 get_adjacent_tiles(loc, arr);
01934 for (unsigned int i = 0; i < 6; i++) {
01935 over.insert(arr[i]);
01936 }
01937 }
01938
01939 return over;
01940 }
01941
01942 int unit::upkeep() const
01943 {
01944
01945 if(can_recruit()) {
01946 return 0;
01947 }
01948 if(cfg_["upkeep"] == "full") {
01949 return level();
01950 }
01951 if(cfg_["upkeep"] == "loyal") {
01952 return 0;
01953 }
01954 return lexical_cast_default<int>(cfg_["upkeep"]);
01955 }
01956
01957 int unit::movement_cost_internal(const t_translation::t_terrain terrain, const int recurse_count) const
01958 {
01959 const int impassable = 10000000;
01960
01961 const std::map<t_translation::t_terrain,int>::const_iterator i =
01962 movement_costs_.find(terrain);
01963
01964 if(i != movement_costs_.end()) {
01965 return i->second;
01966 }
01967
01968 assert(map_ != NULL);
01969
01970 const t_translation::t_list& underlying = map_->underlying_mvt_terrain(terrain);
01971
01972 assert(!underlying.empty());
01973 if(underlying.size() != 1 || underlying.front() != terrain) {
01974 bool revert = (underlying.front() == t_translation::MINUS ? true : false);
01975 if(recurse_count >= 100) {
01976 return impassable;
01977 }
01978
01979 int ret_value = revert ? 0: impassable;
01980 for(t_translation::t_list::const_iterator i = underlying.begin();
01981 i != underlying.end(); ++i) {
01982 if(*i == t_translation::PLUS) {
01983 revert = false;
01984 continue;
01985 } else if(*i == t_translation::MINUS) {
01986 revert = true;
01987 continue;
01988 }
01989 const int value = movement_cost_internal(*i,recurse_count+1);
01990 if(value < ret_value && !revert) {
01991 ret_value = value;
01992 } else if(value > ret_value && revert) {
01993 ret_value = value;
01994 }
01995 }
01996
01997 movement_costs_.insert(std::pair<t_translation::t_terrain, int>(terrain, ret_value));
01998 return ret_value;
01999 }
02000
02001 const config* movement_costs = cfg_.child("movement_costs");
02002
02003 int res = -1;
02004 if(movement_costs != NULL) {
02005 if(underlying.size() != 1) {
02006 LOG_STREAM(err, config) << "terrain '" << terrain << "' has "
02007 << underlying.size() << " underlying names - 0 expected\n";
02008 return impassable;
02009 }
02010 const std::string& id = map_->get_terrain_info(underlying.front()).id();
02011 const std::string& val = (*movement_costs)[id];
02012 if(val != "") {
02013 res = atoi(val.c_str());
02014 }
02015 }
02016
02017 if(res <= 0) {
02018 res = impassable;
02019 }
02020
02021 movement_costs_.insert(std::pair<t_translation::t_terrain, int>(terrain,res));
02022 return res;
02023 }
02024
02025 int unit::movement_cost(const t_translation::t_terrain terrain) const
02026 {
02027 const int res = movement_cost_internal(terrain, 0);
02028 if(utils::string_bool(get_state("slowed"))) {
02029 return res*2;
02030 }
02031 return res;
02032 }
02033
02034 int unit::defense_modifier(t_translation::t_terrain terrain, int recurse_count) const
02035 {
02036 const std::map<t_translation::t_terrain,int>::const_iterator d = defense_mods_.find(terrain);
02037 if(d != defense_mods_.end()) {
02038 return d->second;
02039 }
02040
02041 assert(map_ != NULL);
02042
02043 const t_translation::t_list& underlying = map_->underlying_def_terrain(terrain);
02044 assert(underlying.size() > 0);
02045 if(underlying.size() != 1 || underlying.front() != terrain) {
02046 bool revert = (underlying.front() == t_translation::MINUS ? true : false);
02047 if(recurse_count >= 90) {
02048 LOG_STREAM(err, config) << "infinite defense_modifier recursion: " << t_translation::write_terrain_code(terrain) << " depth " << recurse_count << "\n";
02049 }
02050 if(recurse_count >= 100) {
02051 return 100;
02052 }
02053
02054 int ret_value = revert?0:100;
02055 t_translation::t_list::const_iterator i = underlying.begin();
02056 for(; i != underlying.end(); ++i) {
02057 if(*i == t_translation::PLUS) {
02058 revert = false;
02059 continue;
02060 } else if(*i == t_translation::MINUS) {
02061 revert = true;
02062 continue;
02063 }
02064 const int value = defense_modifier(*i,recurse_count+1);
02065 if(value < ret_value && !revert) {
02066 ret_value = value;
02067 } else if(value > ret_value && revert) {
02068 ret_value = value;
02069 }
02070 }
02071
02072 defense_mods_.insert(std::pair<t_translation::t_terrain,int>(terrain,ret_value));
02073 return ret_value;
02074 }
02075
02076 int res = -1;
02077
02078 const config* const defense = cfg_.child("defense");
02079
02080 if(defense != NULL) {
02081 if(underlying.size() != 1) {
02082 ERR_CONFIG << "terrain '" << terrain << "' has "
02083 << underlying.size() << " underlying names - 0 expected\n";
02084 return 100;
02085 }
02086
02087 const std::string& id = map_->get_terrain_info(underlying.front()).id();
02088 const std::string& val = (*defense)[id];
02089 if(val != "") {
02090 res = atoi(val.c_str());
02091 }
02092 }
02093 if(res < 0) {
02094 ERR_CONFIG << "Defence '" << res << "' is '< 0' reset to 0 (100% defence).\n";
02095 res = 0;
02096 }
02097
02098 defense_mods_.insert(std::pair<t_translation::t_terrain,int>(terrain,res));
02099 return res;
02100 }
02101
02102 bool unit::resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name) const
02103 {
02104 if(!(cfg["active_on"]=="" || (attacker && cfg["active_on"]=="offense") || (!attacker && cfg["active_on"]=="defense"))) {
02105 return false;
02106 }
02107 const std::string& apply_to = cfg["apply_to"];
02108 if(!apply_to.empty()) {
02109 if(damage_name != apply_to) {
02110 if(std::find(apply_to.begin(),apply_to.end(),',') != apply_to.end() &&
02111 std::search(apply_to.begin(),apply_to.end(),
02112 damage_name.begin(),damage_name.end()) != apply_to.end()) {
02113 const std::vector<std::string>& vals = utils::split(apply_to);
02114 if(std::find(vals.begin(),vals.end(),damage_name) == vals.end()) {
02115 return false;
02116 }
02117 } else {
02118 return false;
02119 }
02120 }
02121 }
02122 return true;
02123 }
02124
02125
02126 int unit::resistance_against(const std::string& damage_name,bool attacker,const gamemap::location& loc) const
02127 {
02128 int res = 0;
02129
02130 const config* const resistance = cfg_.child("resistance");
02131 if(resistance != NULL) {
02132 const std::string& val = (*resistance)[damage_name];
02133 if(val != "") {
02134 res = 100 - lexical_cast_default<int>(val);
02135 }
02136 }
02137
02138 unit_ability_list resistance_abilities = get_abilities("resistance",loc);
02139 for(std::vector<std::pair<config*,gamemap::location> >::iterator i = resistance_abilities.cfgs.begin(); i != resistance_abilities.cfgs.end();) {
02140 if(!resistance_filter_matches(*i->first,attacker,damage_name)) {
02141 i = resistance_abilities.cfgs.erase(i);
02142 } else {
02143 ++i;
02144 }
02145 }
02146 if(!resistance_abilities.empty()) {
02147 unit_abilities::effect resist_effect(resistance_abilities,res,false);
02148
02149 res = minimum<int>(resist_effect.get_composite_value(),resistance_abilities.highest("max_value").first);
02150 }
02151 return 100 - res;
02152 }
02153
02154 string_map unit::get_base_resistances() const
02155 {
02156 const config* const resistance = cfg_.child("resistance");
02157 if(resistance != NULL) {
02158 return resistance->values;
02159 }
02160 return string_map();
02161 }
02162
02163 #if 0
02164 std::map<terrain_type::TERRAIN,int> unit::movement_type() const
02165 {
02166 return movement_costs_;
02167 }
02168 #endif
02169
02170 std::map<std::string,std::string> unit::advancement_icons() const
02171 {
02172 std::map<std::string,std::string> temp;
02173 std::string image;
02174 if(can_advance()){
02175 if(advances_to_.empty()==false){
02176 std::stringstream tooltip;
02177 image=game_config::level_image;
02178 std::vector<std::string> adv=advances_to();
02179 for(std::vector<std::string>::const_iterator i=adv.begin();i!=adv.end();i++){
02180 if((*i).size()){
02181 tooltip<<(*i).c_str()<<"\n";
02182 }
02183 }
02184 temp[image]=tooltip.str();
02185 }
02186 const config::child_list mods=get_modification_advances();
02187 for(config::child_list::const_iterator i = mods.begin(); i != mods.end(); ++i) {
02188 image=(**i)["image"];
02189 if(image.size()){
02190 std::stringstream tooltip;
02191 tooltip<<temp[image];
02192 std::string tt=(**i)["description"];
02193 if(tt.size()){
02194 tooltip<<tt<<"\n";
02195 }
02196 temp[image]=tooltip.str();
02197 }
02198 }
02199 }
02200 return(temp);
02201 }
02202 std::vector<std::pair<std::string,std::string> > unit::amla_icons() const
02203 {
02204 std::vector<std::pair<std::string,std::string> > temp;
02205 std::pair<std::string,std::string> icon;
02206
02207 const config::child_list& advances = get_modification_advances();
02208 for(config::child_list::const_iterator i = advances.begin(); i != advances.end(); ++i) {
02209 icon.first=(**i)["icon"];
02210 icon.second=(**i)["description"];
02211
02212 for(unsigned int j=0;j<(modification_count("advance",(**i)["id"]));j++) {
02213
02214 temp.push_back(icon);
02215 }
02216 }
02217 return(temp);
02218 }
02219
02220 void unit::reset_modifications()
02221 {
02222 max_hit_points_ = max_hit_points_b_;
02223 max_experience_ = max_experience_b_;
02224 max_movement_ = max_movement_b_;
02225 attacks_ = attacks_b_;
02226 cfg_.clear_children("movement_costs");
02227 cfg_.clear_children("defense");
02228 cfg_.clear_children("resistance");
02229 cfg_.clear_children("abilities");
02230 cfg_.add_child("movement_costs",movement_b_);
02231 cfg_.add_child("defense",defense_b_);
02232 cfg_.add_child("resistance",resistance_b_);
02233 cfg_.add_child("abilities",abilities_b_);
02234 }
02235 void unit::backup_state()
02236 {
02237 max_hit_points_b_ = max_hit_points_;
02238 max_experience_b_ = max_experience_;
02239 max_movement_b_ = max_movement_;
02240 attacks_b_ = attacks_;
02241 if(cfg_.child("movement_costs")) {
02242 movement_b_ = *cfg_.child("movement_costs");
02243 } else {
02244 movement_b_ = config();
02245 }
02246 if(cfg_.child("defense")) {
02247 defense_b_ = *cfg_.child("defense");
02248 } else {
02249 defense_b_ = config();
02250 }
02251 if(cfg_.child("resistance")) {
02252 resistance_b_ = *cfg_.child("resistance");
02253 } else {
02254 resistance_b_ = config();
02255 }
02256 if(cfg_.child("abilities")) {
02257 abilities_b_ = *cfg_.child("abilities");
02258 } else {
02259 abilities_b_ = config();
02260 }
02261
02262 }
02263
02264 config::child_list unit::get_modification_advances() const
02265 {
02266 config::child_list res;
02267 const config::child_list& advances = modification_advancements();
02268 for(config::child_list::const_iterator i = advances.begin(); i != advances.end(); ++i) {
02269 if (!utils::string_bool((**i)["strict_amla"]) || advances_to_.empty()) {
02270 if(modification_count("advance",(**i)["id"]) < lexical_cast_default<size_t>((**i)["max_times"],1)) {
02271 std::vector<std::string> temp = utils::split((**i)["require_amla"]);
02272 if(temp.size()){
02273 std::sort(temp.begin(),temp.end());
02274 std::vector<std::string> uniq;
02275 std::unique_copy(temp.begin(),temp.end(),std::back_inserter(uniq));
02276 bool requirements_done=true;
02277 for(std::vector<std::string>::const_iterator ii = uniq.begin(); ii != uniq.end(); ii++){
02278 int required_num = std::count(temp.begin(),temp.end(),*ii);
02279 int mod_num = modification_count("advance",*ii);
02280 if(required_num>mod_num){
02281 requirements_done=false;
02282 }
02283 }
02284 if(requirements_done){
02285 res.push_back(*i);
02286 }
02287 }else{
02288 res.push_back(*i);
02289 }
02290 }
02291 }
02292 }
02293
02294 return res;
02295 }
02296
02297 size_t unit::modification_count(const std::string& type, const std::string& id) const
02298 {
02299 size_t res = 0;
02300 const config::child_list& items = modifications_.get_children(type);
02301 for(config::child_list::const_iterator i = items.begin(); i != items.end(); ++i) {
02302 if((**i)["id"] == id) {
02303 ++res;
02304 }
02305 }
02306
02307 return res;
02308 }
02309
02310
02311 static void mod_mdr_merge(config& dst, const config& mod, bool delta)
02312 {
02313 string_map::const_iterator iter = mod.values.begin();
02314 string_map::const_iterator end = mod.values.end();
02315 for (; iter != end; iter++) {
02316 dst[iter->first] =
02317 lexical_cast_default<std::string>(
02318 (delta == true)*lexical_cast_default<int>(dst[iter->first])
02319 + lexical_cast_default<int>(iter->second)
02320 );
02321 }
02322 }
02323
02324 void unit::add_modification(const std::string& type, const config& mod, bool no_add)
02325 {
02326 if(no_add == false) {
02327 modifications_.add_child(type,mod);
02328 }
02329
02330 std::vector<t_string> effects_description;
02331 for(config::const_child_itors i = mod.child_range("effect");
02332 i.first != i.second; ++i.first) {
02333
02334
02335 const std::string& type_filter = (**i.first)["unit_type"];
02336 if(type_filter.empty() == false) {
02337 const std::vector<std::string>& types = utils::split(type_filter);
02338 if(std::find(types.begin(),types.end(),type_id()) == types.end()) {
02339 continue;
02340 }
02341 }
02342
02343 const std::string& gender_filter = (**i.first)["unit_gender"];
02344 if(gender_filter.empty() == false) {
02345 const std::string& gender = gender_string(gender_);
02346 const std::vector<std::string>& genders = utils::split(gender_filter);
02347 if(std::find(genders.begin(),genders.end(),gender) == genders.end()) {
02348 continue;
02349 }
02350 }
02351
02352 const std::string& apply_to = (**i.first)["apply_to"];
02353 const std::string& apply_times = (**i.first)["times"];
02354 int times = 1;
02355 t_string description;
02356
02357 if (apply_times == "per level")
02358 times = level_;
02359 if (times) {
02360 while (times > 0) {
02361 times --;
02362
02363
02364
02365 if(apply_to == "variation" && no_add == false) {
02366 variation_ = (**i.first)["name"];
02367 const unit_type_data::unit_type_map::const_iterator var = unit_type_data::types().find(type_id());
02368 if(var == unit_type_data::types().end()) {
02369 std::string error_message = _("Unknown unit type '$type|'");
02370 utils::string_map symbols;
02371 symbols["type"] = type_id();
02372 error_message = utils::interpolate_variables_into_string(error_message, &symbols);
02373 LOG_STREAM(err, engine) << "unit of type " << type_id() << " not found!\n";
02374 throw game::game_error(error_message);
02375 }
02376 advance_to(&var->second.get_variation(variation_));
02377 } else if(apply_to == "profile") {
02378 const std::string& portrait = (**i.first)["portrait"];
02379 const std::string& description = (**i.first)["description"];
02380 if(!portrait.empty()) cfg_["profile"] = portrait;
02381 if(!description.empty()) cfg_["description"] = description;
02382
02383 } else if(apply_to == "new_attack") {
02384 attacks_.push_back(attack_type(**i.first));
02385 } else if(apply_to == "remove_attacks") {
02386 int num_attacks= attacks_.size();
02387 for(std::vector<attack_type>::iterator a = attacks_.begin(); a != attacks_.end(); ++a) {
02388 if (a->matches_filter(**i.first,false)) {
02389 attacks_.erase(a--);
02390 num_attacks--;
02391 }
02392 }
02393 } else if(apply_to == "attack") {
02394
02395 bool first_attack = true;
02396
02397 std::string attack_names;
02398 std::string desc;
02399 for(std::vector<attack_type>::iterator a = attacks_.begin();
02400 a != attacks_.end(); ++a) {
02401 bool affected = a->apply_modification(**i.first,&desc);
02402 if(affected && desc != "") {
02403 if(first_attack) {
02404 first_attack = false;
02405 } else {
02406 if (!times)
02407 attack_names += t_string(N_(" and "), "wesnoth");
02408 }
02409
02410 if (!times)
02411 attack_names += t_string(a->name(), "wesnoth");
02412 }
02413 }
02414 if (attack_names.empty() == false) {
02415 utils::string_map symbols;
02416 symbols["attack_list"] = attack_names;
02417 symbols["effect_description"] = desc;
02418 description += vgettext("$attack_list|: $effect_description", symbols);
02419 }
02420 } else if(apply_to == "hitpoints") {
02421 LOG_UT << "applying hitpoint mod..." << hit_points_ << "/" << max_hit_points_ << "\n";
02422 const std::string& increase_hp = (**i.first)["increase"];
02423 const std::string& heal_full = (**i.first)["heal_full"];
02424 const std::string& increase_total = (**i.first)["increase_total"];
02425 const std::string& set_hp = (**i.first)["set"];
02426 const std::string& set_total = (**i.first)["set_total"];
02427
02428
02429 const std::string& violate_max = (**i.first)["violate_maximum"];
02430
02431 if(set_hp.empty() == false) {
02432 if(set_hp[set_hp.size()-1] == '%') {
02433 hit_points_ = lexical_cast_default<int>(set_hp)*max_hit_points_/100;
02434 } else {
02435 hit_points_ = lexical_cast_default<int>(set_hp);
02436 }
02437 }
02438 if(set_total.empty() == false) {
02439 if(set_total[set_total.size()-1] == '%') {
02440 max_hit_points_ = lexical_cast_default<int>(set_total)*max_hit_points_/100;
02441 } else {
02442 max_hit_points_ = lexical_cast_default<int>(set_total);
02443 }
02444 }
02445
02446 if(increase_total.empty() == false) {
02447 if (!times)
02448 description += (increase_total[0] != '-' ? "+" : "") +
02449 increase_total + " " +
02450 t_string(N_("HP"), "wesnoth");
02451
02452
02453 max_hit_points_ = utils::apply_modifier(max_hit_points_, increase_total);
02454 }
02455
02456 if(max_hit_points_ < 1)
02457 max_hit_points_ = 1;
02458
02459 if(heal_full.empty() == false && utils::string_bool(heal_full,true)) {
02460 heal_all();
02461 }
02462
02463 if(increase_hp.empty() == false) {
02464 hit_points_ = utils::apply_modifier(hit_points_, increase_hp);
02465 }
02466
02467 LOG_UT << "modded to " << hit_points_ << "/" << max_hit_points_ << "\n";
02468 if(hit_points_ > max_hit_points_ && violate_max.empty()) {
02469 LOG_UT << "resetting hp to max\n";
02470 hit_points_ = max_hit_points_;
02471 }
02472
02473 if(hit_points_ < 1)
02474 hit_points_ = 1;
02475 } else if(apply_to == "movement") {
02476 const std::string& increase = (**i.first)["increase"];
02477 const std::string& set_to = (**i.first)["set"];
02478
02479 if(increase.empty() == false) {
02480 if (!times)
02481 description += (increase[0] != '-' ? "+" : "") +
02482 increase + " " +
02483 t_string(N_("moves"), "wesnoth");
02484
02485 max_movement_ = utils::apply_modifier(max_movement_, increase, 1);
02486 }
02487
02488 if(set_to.empty() == false) {
02489 max_movement_ = atoi(set_to.c_str());
02490 }
02491
02492 if(movement_ > max_movement_)
02493 movement_ = max_movement_;
02494 } else if(apply_to == "max_experience") {
02495 const std::string& increase = (**i.first)["increase"];
02496
02497 if(increase.empty() == false) {
02498 if (!times)
02499 description += (increase[0] != '-' ? "+" : "") +
02500 increase + " " +
02501 t_string(N_("XP to advance"), "wesnoth");
02502
02503 max_experience_ = utils::apply_modifier(max_experience_, increase, 1);
02504 }
02505
02506 } else if(apply_to == "loyal") {
02507 cfg_["upkeep"] = "loyal";
02508 } else if(apply_to == "status") {
02509 const std::string& add = (**i.first)["add"];
02510 const std::string& remove = (**i.first)["remove"];
02511
02512 if(add.empty() == false) {
02513 set_state(add,"yes");
02514 }
02515
02516 if(remove.empty() == false) {
02517 set_state(remove,"");
02518 }
02519 } else if (apply_to == "movement_costs") {
02520 config *mv = cfg_.child("movement_costs");
02521 config *ap = (**i.first).child("movement_costs");
02522 const std::string& replace = (**i.first)["replace"];
02523 if(!mv) {
02524 mv = &cfg_.add_child("movement_costs");
02525 }
02526 if (ap) {
02527 mod_mdr_merge(*mv, *ap, !utils::string_bool(replace));
02528 }
02529 movement_costs_.clear();
02530 } else if (apply_to == "defense") {
02531 config *mv = cfg_.child("defense");
02532 config *ap = (**i.first).child("defense");
02533 const std::string& replace = (**i.first)["replace"];
02534 if(!mv) {
02535 mv = &cfg_.add_child("defense");
02536 }
02537 if (ap) {
02538 mod_mdr_merge(*mv, *ap, !utils::string_bool(replace));
02539 }
02540 defense_mods_.clear();
02541 } else if (apply_to == "resistance") {
02542 config *mv = cfg_.child("resistance");
02543 config *ap = (**i.first).child("resistance");
02544 const std::string& replace = (**i.first)["replace"];
02545 if(!mv) {
02546 mv = &cfg_.add_child("resistance");
02547 }
02548 if (ap) {
02549 mod_mdr_merge(*mv, *ap, !utils::string_bool(replace));
02550 }
02551 } else if (apply_to == "zoc") {
02552 const std::string& zoc_value = (**i.first)["value"];
02553 if(!zoc_value.empty()) {
02554 emit_zoc_ = lexical_cast_default<int>(zoc_value);
02555 }
02556 } else if (apply_to == "new_ability") {
02557 config *ab_effect;
02558 config *ab = cfg_.child("abilities");
02559 if(!ab) {
02560 ab = &cfg_.add_child("abilities");
02561 }
02562 ab_effect = (**i.first).child("abilities");
02563 if (ab_effect) {
02564 config to_append;
02565 config::all_children_iterator j, j_end = ab_effect->ordered_end();
02566 for(j = ab_effect->ordered_begin(); j != j_end; ++j) {
02567 if(!has_ability_by_id(j.get_child()["id"])) {
02568 to_append.add_child(j.get_key(), j.get_child());
02569 }
02570 }
02571 ab->append(to_append);
02572 }
02573 } else if (apply_to == "remove_ability") {
02574 config *ab_effect = (**i.first).child("abilities");
02575 if (ab_effect) {
02576 config::all_children_iterator j, j_end = ab_effect->ordered_end();
02577 for(j = ab_effect->ordered_begin(); j != j_end; ++j) {
02578 remove_ability_by_id(j.get_child()["id"]);
02579 }
02580 }
02581 } else if (apply_to == "image_mod") {
02582 LOG_UT << "applying image_mod \n";
02583 std::string mod = (**i.first)["replace"];
02584 if (!mod.empty()){
02585 image_mods_ = mod;
02586 }
02587 LOG_UT << "applying image_mod \n";
02588 mod = (**i.first)["add"];
02589 if (!mod.empty()){
02590 image_mods_ += mod;
02591 }
02592
02593 game_config::add_color_info(**i.first);
02594 LOG_UT << "applying image_mod \n";
02595 } else if (apply_to == "new_animation") {
02596 unit_animation::add_anims(animations_,**i.first);
02597 }
02598 }
02599 } else {
02600 if(apply_to == "attack") {
02601
02602 bool first_attack = true;
02603
02604 for(std::vector<attack_type>::iterator a = attacks_.begin();
02605 a != attacks_.end(); ++a) {
02606 std::string desc;
02607 bool affected = a->describe_modification(**i.first,&desc);
02608 if(affected && desc != "") {
02609 if(first_attack) {
02610 first_attack = false;
02611 } else {
02612 description += t_string(N_(" and "), "wesnoth");
02613 }
02614
02615 description += t_string(a->name(), "wesnoth") + ": " + desc;
02616 }
02617 }
02618 } else if(apply_to == "hitpoints") {
02619 const std::string& increase_total = (**i.first)["increase_total"];
02620
02621 if(increase_total.empty() == false) {
02622 description += (increase_total[0] != '-' ? "+" : "") +
02623 increase_total + " " +
02624 t_string(N_("HP"), "wesnoth");
02625 }
02626 } else if(apply_to == "movement") {
02627 const std::string& increase = (**i.first)["increase"];
02628
02629 if(increase.empty() == false) {
02630 description += (increase[0] != '-' ? "+" : "") +
02631 increase + t_string(N_(" move"), "wesnoth");
02632 }
02633 } else if(apply_to == "max_experience") {
02634 const std::string& increase = (**i.first)["increase"];
02635
02636 if(increase.empty() == false) {
02637 description += (increase[0] != '-' ? "+" : "") +
02638 increase + " " +
02639 t_string(N_("XP to advance"), "wesnoth");
02640 }
02641 }
02642 }
02643
02644 if (apply_times == "per level" && !times) {
02645 utils::string_map symbols;
02646 symbols["effect_description"] = description;
02647 description = vgettext("$effect_description per level", symbols);
02648 }
02649 if(!description.empty())
02650 effects_description.push_back(description);
02651
02652 }
02653
02654 t_string& description = modification_descriptions_[type];
02655 t_string trait_description;
02656
02657
02658
02659 if (!mod["description"].empty()) {
02660 trait_description += mod["description"] + " ";
02661 }
02662 if(effects_description.empty() == false) {
02663
02664 for(std::vector<t_string>::const_iterator i = effects_description.begin();
02665 i != effects_description.end(); ++i) {
02666 trait_description += *i;
02667 if(i+1 != effects_description.end())
02668 trait_description += t_string(N_(" and "), "wesnoth");
02669 }
02670
02671 }
02672
02673 if (!mod["name"].empty()) {
02674 utils::string_map symbols;
02675 symbols["trait_name"] = mod["name"];
02676 symbols["trait_description"] = trait_description;
02677 description += vgettext("$trait_name|: $trait_description ", symbols);
02678 } else if (!trait_description.empty()) {
02679 description += trait_description;
02680 }
02681
02682 description += "\n";
02683 }
02684
02685 const t_string& unit::modification_description(const std::string& type) const
02686 {
02687 const string_map::const_iterator i = modification_descriptions_.find(type);
02688 if(i == modification_descriptions_.end()) {
02689 static const t_string empty_string;
02690 return empty_string;
02691 } else {
02692 return i->second;
02693 }
02694 }
02695
02696
02697
02698 const unit_animation* unit::choose_animation(const game_display& disp, const gamemap::location& loc,const std::string& event,const int value,const unit_animation::hit_type hit,const attack_type* attack,const attack_type* second_attack, int swing_num) const
02699 {
02700
02701 std::vector<const unit_animation*> options;
02702 int max_val = unit_animation::MATCH_FAIL;
02703 for(std::vector<unit_animation>::const_iterator i = animations_.begin(); i != animations_.end(); ++i) {
02704 int matching = i->matches(disp,loc,this,event,value,hit,attack,second_attack,swing_num);
02705 if(matching > unit_animation::MATCH_FAIL && matching == max_val) {
02706 options.push_back(&*i);
02707 } else if(matching > max_val) {
02708 max_val = matching;
02709 options.erase(options.begin(),options.end());
02710 options.push_back(&*i);
02711 }
02712 }
02713
02714 if(max_val == unit_animation::MATCH_FAIL) {
02715 return NULL;
02716 }
02717 return options[rand()%options.size()];
02718 }
02719
02720
02721 void unit::apply_modifications()
02722 {
02723 log_scope("apply mods");
02724 reset_modifications();
02725 modification_descriptions_.clear();
02726
02727 traits_description_ = "";
02728
02729 std::vector< t_string > traits;
02730 is_fearless_ = false;
02731 is_healthy_ = false;
02732 config::child_list const &mods = modifications_.get_children("trait");
02733 for(config::child_list::const_iterator j = mods.begin(), j_end = mods.end(); j != j_end; ++j) {
02734 is_fearless_ = is_fearless_ || (**j)["id"] == "fearless";
02735 is_healthy_ = is_healthy_ || (**j)["id"] == "healthy";
02736 const std::string gender_string = gender_ == unit_race::FEMALE ? "female_name" : "male_name";
02737 t_string const &gender_specific_name = (**j)[gender_string];
02738 if (!gender_specific_name.empty()) {
02739 traits.push_back(gender_specific_name);
02740 (**j)["name"] = gender_specific_name;
02741 } else {
02742 t_string const &name = (**j)["name"];
02743 if (!name.empty()) {
02744 traits.push_back(name);
02745 }
02746 }
02747 }
02748
02749 for(size_t i = 0; i != NumModificationTypes; ++i) {
02750 const std::string& mod = ModificationTypes[i];
02751 const config::child_list& mods = modifications_.get_children(mod);
02752 for(config::child_list::const_iterator j = mods.begin(); j != mods.end(); ++j) {
02753 log_scope("add mod");
02754 add_modification(ModificationTypes[i],**j,true);
02755 }
02756 }
02757
02758 std::vector< t_string >::iterator k = traits.begin(), k_end = traits.end();
02759 if (k != k_end) {
02760
02761
02762
02763
02764 for(;;) {
02765 traits_description_ += *(k++);
02766 if (k == k_end) break;
02767 traits_description_ += ", ";
02768 }
02769 }
02770 }
02771
02772 bool unit::invisible(const gamemap::location& loc,
02773 const unit_map& units,const std::vector<team>& teams, bool see_all) const
02774 {
02775
02776
02777
02778
02779
02780 if(see_all) {
02781 std::map<gamemap::location, bool>::const_iterator itor = invisibility_cache_.find(loc);
02782 if(itor != invisibility_cache_.end()) {
02783 return itor->second;
02784 }
02785 }
02786
02787
02788 static const std::string hides("hides");
02789 bool is_inv = (utils::string_bool(get_state(hides)) && get_ability_bool(hides,loc));
02790 if(is_inv){
02791 for(unit_map::const_iterator u = units.begin(); u != units.end(); ++u) {
02792 if(teams[side_-1].is_enemy(u->second.side()) && tiles_adjacent(loc,u->first)) {
02793
02794
02795 if(see_all) {
02796 is_inv = false;
02797 break;
02798 } else if(!teams[side_-1].fogged(u->first)
02799 && !u->second.invisible(u->first, units,teams,true)) {
02800 is_inv = false;
02801 break;
02802 }
02803 }
02804 }
02805 }
02806
02807 if(see_all) {
02808
02809 if(invisibility_cache_.empty()) {
02810 units_with_cache.push_back(this);
02811 }
02812 invisibility_cache_[loc] = is_inv;
02813 }
02814
02815 return is_inv;
02816 }
02817
02818 void unit::set_underlying_id() {
02819 if(underlying_id_.empty()){
02820 std::stringstream id;
02821
02822 if(!name_.empty()){
02823 id << type()->id() << "-" << get_random() << get_random() << "-" << name_;
02824 } else {
02825 id << type()->id() << "-" << get_random() << get_random();
02826 }
02827 underlying_id_ = id.str();
02828 }
02829 }
02830
02831 int team_units(const unit_map& units, unsigned int side)
02832 {
02833 int res = 0;
02834 for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
02835 if(i->second.side() == side) {
02836 ++res;
02837 }
02838 }
02839
02840 return res;
02841 }
02842
02843 int team_upkeep(const unit_map& units, unsigned int side)
02844 {
02845 int res = 0;
02846 for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
02847 if(i->second.side() == side) {
02848 res += i->second.upkeep();
02849 }
02850 }
02851
02852 return res;
02853 }
02854
02855 unit_map::const_iterator team_leader(unsigned int side, const unit_map& units)
02856 {
02857 for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
02858 if(i->second.can_recruit() && i->second.side() == side) {
02859 return i;
02860 }
02861 }
02862
02863 return units.end();
02864 }
02865
02866 unit_map::iterator find_visible_unit(unit_map& units,
02867 const gamemap::location loc,
02868 const gamemap& map,
02869 const std::vector<team>& teams, const team& current_team,
02870 bool see_all)
02871 {
02872 unit_map::iterator u = units.find(loc);
02873 if(map.on_board(loc) && !see_all){
02874 if(u != units.end()){
02875 if(current_team.fogged(loc)){
02876 return units.end();
02877 }
02878 if(current_team.is_enemy(u->second.side()) &&
02879 u->second.invisible(loc,units,teams)) {
02880 return units.end();
02881 }
02882 }
02883 }
02884 return u;
02885 }
02886
02887 unit_map::const_iterator find_visible_unit(const unit_map& units,
02888 const gamemap::location loc,
02889 const gamemap& map,
02890 const std::vector<team>& teams, const team& current_team,
02891 bool see_all)
02892 {
02893 unit_map::const_iterator u = units.find(loc);
02894 if(map.on_board(loc) && !see_all){
02895 if(u != units.end()){
02896 if(current_team.fogged(loc)){
02897 return units.end();
02898 }
02899 if(current_team.is_enemy(u->second.side()) &&
02900 u->second.invisible(loc,units,teams)) {
02901 return units.end();
02902 }
02903 }
02904 }
02905 return u;
02906 }
02907
02908 team_data calculate_team_data(const team& tm, int side, const unit_map& units)
02909 {
02910 team_data res;
02911 res.units = team_units(units,side);
02912 res.upkeep = team_upkeep(units,side);
02913 res.villages = tm.villages().size();
02914 res.expenses = maximum<int>(0,res.upkeep - res.villages);
02915 res.net_income = tm.income() - res.expenses;
02916 res.gold = tm.gold();
02917 res.teamname = tm.user_team_name();
02918 return res;
02919 }
02920
02921 temporary_unit_placer::temporary_unit_placer(unit_map& m, const gamemap::location& loc, const unit& u)
02922 : m_(m), loc_(loc), temp_(m.extract(loc))
02923 {
02924 m.add(new std::pair<gamemap::location,unit>(loc,u));
02925 }
02926
02927 temporary_unit_placer::~temporary_unit_placer()
02928 {
02929 m_.erase(loc_);
02930 if(temp_) {
02931 m_.add(temp_);
02932 }
02933 }
02934
02935 std::string unit::image_mods() const{
02936 std::stringstream modifier;
02937 if(flag_rgb_.size()){
02938 modifier << "~RC("<< flag_rgb_ << ">" << team::get_side_colour_index(side()) << ")";
02939 }
02940 if(image_mods_.size()){
02941 modifier << "~" << image_mods_;
02942 }
02943 return modifier.str();
02944 }
02945
02946
02947
02948 void unit::set_hidden(bool state) {
02949 hidden_ = state;
02950 if(!state) return;
02951
02952 clear_haloes();
02953 }
02954
02955
02956
02957
02958
02959
02960
02961
02962
02963
02964
02965
02966
02967
02968
02969
02970
02971
02972
02973
02974
02975
02976
02977
02978
02979
02980
02981
02982
02983
02984
02985
02986
02987
02988
02989
02990 std::string get_checksum(const unit& u) {
02991 config unit_config;
02992 config wcfg;
02993 u.write(unit_config);
02994 const std::string main_keys[] =
02995 { "advanceto",
02996 "alignment",
02997 "cost",
02998 "experience",
02999 "gender",
03000 "hitpoints",
03001 "ignore_race_traits",
03002 "ignore_global_traits",
03003 "level",
03004 "max_attacks",
03005 "max_experience",
03006 "max_hitpoints",
03007 "max_moves",
03008 "movement",
03009 "movement_type",
03010 "race",
03011 "random_traits",
03012 "resting",
03013 "undead_variation",
03014 "upkeep",
03015 "value",
03016 "zoc",
03017 ""};
03018
03019 int i;
03020
03021 for (i = 0; !main_keys[i].empty(); ++i)
03022 {
03023 wcfg[main_keys[i]] = unit_config[main_keys[i]];
03024 }
03025 const std::string attack_keys[] =
03026 { "name",
03027 "type",
03028 "range",
03029 "damage",
03030 "number",
03031 ""};
03032 const config::child_list& attacks = unit_config.get_children("attack");
03033 for (config::child_list::const_iterator att = attacks.begin(); att != attacks.end(); ++att)
03034 {
03035 config& child = wcfg.add_child("attack");
03036 for (i = 0; !attack_keys[i].empty(); ++i)
03037 {
03038 child[attack_keys[i]] = (**att)[attack_keys[i]];
03039 }
03040 const config::child_list& specials = (*att)->get_children("specials");
03041
03042 for (config::child_list::const_iterator spec = specials.begin(); spec != specials.end(); ++spec)
03043 {
03044 config& child_spec = child.add_child("specials", **spec);
03045 child_spec.recursive_clear_value("description");
03046
03047 }
03048
03049 }
03050
03051 const config::child_list& abilities = unit_config.get_children("abilities");
03052 for (config::child_list::const_iterator abi = abilities.begin(); abi != abilities.end(); ++abi)
03053 {
03054 config& child = wcfg.add_child("abilities", **abi);
03055 child.recursive_clear_value("description");
03056 child.recursive_clear_value("description_inactive");
03057 child.recursive_clear_value("name");
03058 child.recursive_clear_value("name_inactive");
03059 }
03060
03061 const config::child_list& traits = unit_config.get_children("trait");
03062 for (config::child_list::const_iterator trait = traits.begin(); trait != traits.end(); ++trait)
03063 {
03064 config& child = wcfg.add_child("trait", **trait);
03065 child.recursive_clear_value("description");
03066 child.recursive_clear_value("male_name");
03067 child.recursive_clear_value("female_name");
03068 child.recursive_clear_value("name");
03069 }
03070
03071 const std::string child_keys[] = {"advance_from", "defense", "movement_cost", "resistance",""};
03072
03073 for (i = 0; !child_keys[i].empty(); ++i)
03074 {
03075 const config::child_list& children = unit_config.get_children(child_keys[i]);
03076 for (config::child_list::const_iterator c = children.begin(); c != children.end(); ++c)
03077 {
03078 wcfg.add_child(child_keys[i], **c);
03079 }
03080 }
03081 DBG_UT << wcfg;
03082
03083 return wcfg.hash();
03084
03085 unit_config["controller"] = "";
03086
03087 unit_config["moves"] = "";
03088
03089 unit_config["alpha"] = "";
03090 unit_config["description"] = "";
03091 unit_config["die_sound"] = "";
03092 unit_config["ellipse"] = "";
03093 unit_config["facing"] = "";
03094 unit_config["flag_rgb"] = "";
03095 unit_config["image"] = "";
03096 unit_config["language_name"] = "";
03097 unit_config["name"] = "";
03098 unit_config["overlays"] = "";
03099
03100 unit_config.clear_children("animation");
03101 unit_config.clear_children("attack_anim");
03102 unit_config.clear_children("comment");
03103 unit_config.clear_children("defend");
03104 unit_config.clear_children("death");
03105 unit_config.clear_children("extra_anim");
03106 unit_config.clear_children("idle_anim");
03107 unit_config.clear_children("healed_anim");
03108 unit_config.clear_children("healing_anim");
03109 unit_config.clear_children("level_in");
03110 unit_config.clear_children("level_out");
03111 unit_config.clear_children("movement_anim");
03112 unit_config.clear_children("poison_anim");
03113 unit_config.clear_children("standing_anim");
03114 unit_config.clear_children("victory_anim");
03115
03116 return unit_config.hash();
03117 }
03118