unit.cpp

Go to the documentation of this file.
00001 /* $Id: unit.cpp 26761 2008-05-21 19:57:09Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 /**
00016  *  @file unit.cpp
00017  *  Routines to manage units.
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      * Pointers to units which have data in their internal caches. The
00067      * destructor of an unit removes itself from the cache, so the pointers are
00068      * always valid.
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 // Copy constructor
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     // Once random gender is used, don't do it again.
00242     // Such as when restoring a saved character.
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         // Units that don't have traits generated are just
00272         // generic units, so they shouldn't get a description
00273         // either.
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         // Units that don't have traits generated are just
00305         // generic units, so they shouldn't get a description
00306         // either.
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     // Remove us from the status cache
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     // Use copy constructor to make sure we are coherant
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     // In case the unit carries EventWML, apply it now
00359     game_events::add_events(cfg_.get_children("event"),type_);
00360 }
00361 
00362 
00363 void unit::add_trait(std::string /*trait*/)
00364 {
00365     //modifications_.add_child("trait", cfg);
00366     apply_modifications();
00367 }
00368 
00369 // Apply mandatory traits (e.g. undead, mechanical) to a unit and then
00370 // fill out with avaiable (leaders have a restircted set of available traits)
00371 // traits until no more are available or the unit has its maximum number
00372 // of traits.
00373 // This routine does not apply the effects of added traits to a unit.
00374 // That must be done by the caller.
00375 // Note that random numbers used in config files don't work in multiplayer,
00376 // so that leaders should be barred from all random traits until that
00377 // is fixed. Later the restrictions will be based on play balance.
00378 // musthavepnly is true when you don't want to generate random traits or
00379 // you don't want to give any optional traits to a unit.
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     // Calculate the unit's traits
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     // First remove traits the unit already has from consideration.
00398     // And count them so that we can figure out how many more are needed.
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     // Next add in any mandatory traits. These aren't limited by the
00414     // number of traits allowed for a unit. They also don't use
00415     // any random numbers for assignment. (And hence don't cause
00416     // problems for multiplayer.)
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     // If musthaveonly then don't generate any random/optional traits
00431     if(!musthaveonly) {
00432         // Next for leaders remove any traits that are not available to
00433         // the "any" category.
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         // Now randomly fill out to the number of traits required or until
00448         // there aren't any more traits.
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         // Once random traits are added, don't do it again.
00458         // Such as when restoring a saved character.
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     // Remove old animations
00472     cfg_.clear_children("animation");
00473 
00474     cfg_.clear_children("attack");
00475     cfg_.clear_children("abilities");
00476     // Clear cache of movement costs and defense mods
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     // If unit has specific profile, remember it and keep it after advancing
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; // Track whether unit should get fully healed.
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; // Can't heal until after mods applied.
00537         type_ = t->id();
00538     }
00539 
00540     if(utils::string_bool(cfg_["random_traits"], true)) {
00541         generate_traits(!use_traits, state);
00542     } else {
00543         // This will add any "musthave" traits to the new unit that it doesn't already have.
00544         // This covers the Dark Sorcerer advancing to Lich and gaining the "undead" trait,
00545         // but random and/or optional traits are not added,
00546         // and neither are inappropiate traits removed.
00547         generate_traits(true);
00548     }
00549 
00550     // Apply modifications etc, refresh the unit.
00551     // This needs to be after type and gender are fixed,
00552     // since there can be filters on the modifications
00553     // that may result in different effects after the advancement.
00554     apply_modifications();
00555 
00556     // Not that the unit has all of its modifications applied, it is
00557     // OK to heal it.
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     // Clear interrupted move
00693     set_interrupted_move(gamemap::location());
00694 }
00695 void unit::new_level()
00696 {
00697     role_ = "";
00698     ai_special_ = "";
00699 
00700     // Set the goto-command to be going to no-where
00701     goto_ = gamemap::location();
00702 
00703     remove_temporary_modifications();
00704 
00705     // Re-apply all permanent modifications
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         // If loc is invalid, then this is a recall list unit (already been scoped)
00802         matches = internal_matches_filter(cfg, loc, use_flat_tod);
00803     }
00804 
00805     // Handle [and], [or], and [not] with in-order precedence
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         // Handle [and]
00815         if(cond_name == "and") {
00816             matches = matches && matches_filter(cond_filter,loc,use_flat_tod);
00817         }
00818         // Handle [or]
00819         else if(cond_name == "or") {
00820             matches = matches || matches_filter(cond_filter,loc,use_flat_tod);
00821         }
00822         // Handle [not]
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     // FIXME OBSOLETE To be altered in 1.5.3
00867     // description= can match either the id or the custom unit description.
00868     // This is deliberate as a backward-compatibility-hack to
00869     // accommodate pre-1.5.1 versions, but it may produce odd results.
00870     // if (as in THoT) several units are deliberately given the same
00871     // user description.  After 1.5.3 the middle clause should go.
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     // Allow 'speaker' as an alternative to id, since people use it so often
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     // Also allow filtering on location ranges outside of the location filter
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             //locations on the map are considered to not be on a recall list
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     // The type could be a comma separated list of types
00912     if(type.empty() == false && type != this_type) {
00913 
00914         // We only do the full CSV search if we find a comma in there,
00915         // and if the subsequence is found within the main sequence.
00916         // This is because doing the full CSV split is expensive.
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     // Now start with the new WML based comparison.
01012     // If a key is in the unit and in the filter, they should match
01013     // filter only => not for us
01014     // unit only => not filtered
01015     const vconfig::child_list& wmlcfgs = cfg.get_children("filter_wml");
01016     if (!wmlcfgs.empty()) {
01017         config unit_cfg;
01018         write(unit_cfg);
01019         // Now, match the kids, WML based
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     // FIXME OBSOLETE: Remove in 1.5.3
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         // Now, match the kids, WML based
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                 //if viewing_side is not defined, default to all enemies
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_ /*, false(?) */);
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         // Allow filtering by searching a stored variable of units
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     // Prevent un-initialized variables
01175     hit_points_=1;
01176 
01177     // Collect these early so they can be modified by traits.
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     // FIXME OBSOLETE This will go away in 1.5.3
01193     if (id_.empty())
01194         id_ = cfg["description"];
01195     // FIXME OBSOLETE This will go away in 1.5.3
01196     if (!cfg["user_description"].empty()) {
01197         name_ = cfg["user_description"];
01198     }
01199     std::string custom_unit_desc = cfg["description"];
01200     // FIXME OBSOLETE This will go away in 1.5.3
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     //support for unit formulas and unit-specyfic variables in [ai_vars]
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     //remove ai_vars from private cfg
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             // backwards compatibility
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     // Attach animations for this unit to the in-core object
01321     unit_animation::fill_initial_animations(animations_,cfg_);
01322     // Remove animations from private cfg, they're not needed there now
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     // FIXME OBSOLETE Remove in 1.5.3
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     // Make the default upkeep "full"
01374     if(cfg_["upkeep"].empty()) {
01375         cfg_["upkeep"] = "full";
01376     }
01377 }
01378 void unit::write(config& cfg) const
01379 {
01380     // If a location has been saved in the config, keep it
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     //support for unit formulas and unit-specyfic variables in [ai_vars]
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     // everything except standing select and idle
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     // Else look at yourself (not available so continue to face the same direction)
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     // do not set to 0 so we can distinguih the flying from the "not on submerge terrain"
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() == "") { // invalid diag image, or not diagonal
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     // The caching used to be disabled for the lowmem case but that actually
01726     // resulted in a higher memory usage see bug ##11022.
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     // We draw bars only if wanted, visible on the map view and not a fake unit
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         // The division by 2 seems to have no real meaning,
01750         // It just works fine with the current center of ellipse
01751         // and prevent a too large adjust if submerge = 1.0
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         // Load the ellipse parts recolored to match team color
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     // NOTE: A possible a hack is to draw ellipses in the unit layer
01772     // but with a different drawing_order, so it's rendered behind/above unit
01773     // disp.drawing_buffer_add(display::LAYER_UNIT_FIRST, drawing_order-10,
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                     // unit_can_move assumes that it's not a fake unit (= in unit_map)
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                 //if(bar_alpha != ftofxp(1.0)) {
01850                 //  crown = adjust_surface_alpha(crown, bar_alpha);
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     // Very early calls, anim not initialized yet
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         // Invalidate adjacent neighbours if we don't stay in our hex
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     // Leaders do not incur upkeep.
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     // If this is an alias, then select the best of all underlying terrains
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) { // We fail here, but first test underlying_mvt_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     // If this is an alias, then select the best of all underlying terrains
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; //<image,tooltip>
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 /** Helper function for add_modifications */
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         // See if the effect only applies to certain unit types
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         // See if the effect only applies to certain genders
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                 // Apply variations -- only apply if we are adding this
02364                 // for the first time.
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                     //help::unit_topic_generator(*this, (**i.first)["help_topic"]);
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                     // If the hitpoints are allowed to end up greater than max hitpoints
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                         // A percentage on the end means increase by that many percent
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             } // end while
02599         } else { // for times = per level & level = 0 we still need to rebuild the descriptions
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     // Punctuation should be translatable: not all languages use latin punctuation.
02658     // (However, there maybe is a better way to do it)
02659     if (!mod["description"].empty()) {
02660         trait_description += mod["description"] + " ";
02661     }
02662     if(effects_description.empty() == false) {
02663         //trait_description += t_string(N_("("), "wesnoth");
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         //trait_description += t_string(N_(")"), "wesnoth");
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     // Select one of the matching animations at random
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         // We want to make sure the traits always have a consistent ordering.
02761             // Try out not sorting, since quick,resilient can give different HP
02762             // to resilient,quick so rather preserve order
02763         // std::sort(k, k_end);
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     // Fetch from cache
02776     /**
02777      * @todo FIXME: We use the cache only when using the default see_all=true
02778      * Maybe add a second cache if the see_all=false become more frequent.
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     // Test hidden status
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                 // Enemy spotted in adjacent tiles, check if we can see him.
02794                 // Watch out to call invisible with see_all=true to avoid infinite recursive calls!
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         // Add to caches
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     // We need to get rid of haloes immediately to avoid display glitches
02952     clear_haloes();
02953 }
02954 /**
02955  * advanceto
02956  * alignment
02957  * cost
02958  * experience
02959  * gender
02960  * hitpoints
02961  * level
02962  * max_attacks
02963  * max_experience
02964  * max_hitpoints
02965  * max_moves
02966  * movement
02967  * movement_type
02968  * race
02969  * random_traits
02970  * resting
02971  * undead_variation
02972  * upkeep
02973  * value
02974  * zoc
02975  * [attack]
02976  *         name
02977  *         type
02978  *         range
02979  *         damage
02980  *         number
02981  *         [specials]
02982  *      *
02983  *  [/special]
02984  * [/attack]
02985  * anything in: [abilities], [advance_from], [defense], [movement_cost], [resistance], [trait]
02986  * remove description, description_inactive, name, name_inactive from all tags under [abilities]
02987  * remove description from all tags under [specials]
02988  * remove description, male_name, female_name, name from [trait]
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     // Since the ai messes up the 'moves' attribute, ignore that for the checksum
03087     unit_config["moves"] = "";
03088     // Non-critical attributes to ignore.
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     // Non-critical tags to ignore.
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 

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