unit.cpp

Go to the documentation of this file.
00001 /* $Id: unit.cpp 54122 2012-05-08 00:05:33Z fendrin $ */
00002 /*
00003    Copyright (C) 2003 - 2012 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 as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  *  @file
00018  *  Routines to manage units.
00019  */
00020 
00021 #include "unit.hpp"
00022 
00023 #include "callable_objects.hpp"
00024 #include "foreach.hpp"
00025 #include "formula.hpp"
00026 #include "game_display.hpp"
00027 #include "game_preferences.hpp"
00028 #include "gamestatus.hpp"
00029 #include "gettext.hpp"
00030 #include "halo.hpp"
00031 #include "log.hpp"
00032 #include "resources.hpp"
00033 #include "unit_id.hpp"
00034 #include "unit_abilities.hpp"
00035 #include "terrain_filter.hpp"
00036 #include "formula_string_utils.hpp"
00037 #include "scripting/lua.hpp"
00038 #include "side_filter.hpp"
00039 #include "play_controller.hpp"
00040 
00041 #include <boost/bind.hpp>
00042 
00043 static lg::log_domain log_unit("unit");
00044 #define DBG_UT LOG_STREAM(debug, log_unit)
00045 #define LOG_UT LOG_STREAM(info, log_unit)
00046 #define WRN_UT LOG_STREAM(warn, log_unit)
00047 #define ERR_UT LOG_STREAM(err, log_unit)
00048 
00049 static lg::log_domain log_engine("engine");
00050 #define ERR_NG LOG_STREAM(err, log_engine)
00051 
00052 static lg::log_domain log_config("config");
00053 #define WRN_CF LOG_STREAM(warn, log_config)
00054 #define ERR_CONFIG LOG_STREAM(err, log_config)
00055 
00056 namespace {
00057     const std::string ModificationTypes[] = { "advance", "trait", "object" };
00058     const size_t NumModificationTypes = sizeof(ModificationTypes)/
00059                                         sizeof(*ModificationTypes);
00060 
00061     /**
00062      * Pointers to units which have data in their internal caches. The
00063      * destructor of an unit removes itself from the cache, so the pointers are
00064      * always valid.
00065      */
00066     static std::vector<const unit *> units_with_cache;
00067 }
00068 
00069 static const unit_type &get_unit_type(const std::string &type_id)
00070 {
00071     const unit_type *i = unit_types.find(type_id);
00072     if (!i) throw game::game_error("unknown unit type: " + type_id);
00073     return *i;
00074 }
00075 
00076 static unit_race::GENDER generate_gender(const std::string &type_id, bool random_gender, game_state *state)
00077 {
00078     const unit_type &type = get_unit_type(type_id);
00079     const std::vector<unit_race::GENDER>& genders = type.genders();
00080 
00081     if(genders.empty()) {
00082         return unit_race::MALE;
00083     } else if(random_gender == false || genders.size() == 1) {
00084         return genders.front();
00085     } else {
00086         int random = state ? state->rng().get_next_random() : get_random_nocheck();
00087         return genders[random % genders.size()];
00088     }
00089 }
00090 
00091 static unit_race::GENDER generate_gender(const config &cfg, game_state *state)
00092 {
00093     const std::string& gender = cfg["gender"];
00094     if(!gender.empty())
00095         return string_gender(gender);
00096     const std::string &type = cfg["type"];
00097     if (type.empty())
00098         return unit_race::MALE;
00099 
00100     bool random_gender = cfg["random_gender"].to_bool();
00101     return generate_gender(type, random_gender, state);
00102 }
00103 
00104 // Copy constructor
00105 unit::unit(const unit& o):
00106            cfg_(o.cfg_),
00107            loc_(o.loc_),
00108            advances_to_(o.advances_to_),
00109            type_(o.type_),
00110            race_(o.race_),
00111            id_(o.id_),
00112            name_(o.name_),
00113            underlying_id_(o.underlying_id_),
00114            type_name_(o.type_name_),
00115            undead_variation_(o.undead_variation_),
00116            variation_(o.variation_),
00117 
00118            hit_points_(o.hit_points_),
00119            max_hit_points_(o.max_hit_points_),
00120            experience_(o.experience_),
00121            max_experience_(o.max_experience_),
00122            level_(o.level_),
00123            canrecruit_(o.canrecruit_),
00124            recruit_list_(o.recruit_list_),
00125            alignment_(o.alignment_),
00126            flag_rgb_(o.flag_rgb_),
00127            image_mods_(o.image_mods_),
00128 
00129            unrenamable_(o.unrenamable_),
00130            side_(o.side_),
00131            gender_(o.gender_),
00132 
00133            alpha_(o.alpha_),
00134 
00135            unit_formula_(o.unit_formula_),
00136            unit_loop_formula_(o.unit_loop_formula_),
00137            unit_priority_formula_(o.unit_priority_formula_),
00138            formula_vars_(o.formula_vars_ ? new game_logic::map_formula_callable(*o.formula_vars_) : o.formula_vars_),
00139 
00140            movement_(o.movement_),
00141            max_movement_(o.max_movement_),
00142            movement_costs_(o.movement_costs_),
00143            vision_(o.vision_),
00144            vision_costs_(o.vision_costs_),
00145            jamming_(o.jamming_),
00146            jamming_costs_(o.jamming_costs_),
00147            defense_mods_(o.defense_mods_),
00148            hold_position_(o.hold_position_),
00149            end_turn_(o.end_turn_),
00150            resting_(o.resting_),
00151            attacks_left_(o.attacks_left_),
00152            max_attacks_(o.max_attacks_),
00153 
00154            states_(o.states_),
00155            known_boolean_states_(o.known_boolean_states_),
00156            variables_(o.variables_),
00157            events_(o.events_),
00158            filter_recall_(o.filter_recall_),
00159            emit_zoc_(o.emit_zoc_),
00160            state_(o.state_),
00161 
00162            overlays_(o.overlays_),
00163 
00164            role_(o.role_),
00165            attacks_(o.attacks_),
00166            facing_(o.facing_),
00167 
00168            trait_names_(o.trait_names_),
00169            trait_descriptions_(o.trait_descriptions_),
00170            unit_value_(o.unit_value_),
00171            goto_(o.goto_),
00172            interrupted_move_(o.interrupted_move_),
00173            flying_(o.flying_),
00174            is_fearless_(o.is_fearless_),
00175            is_healthy_(o.is_healthy_),
00176 
00177            modification_descriptions_(o.modification_descriptions_),
00178 
00179            animations_(o.animations_),
00180 
00181            anim_(NULL),
00182            next_idling_(0),
00183 
00184            frame_begin_time_(o.frame_begin_time_),
00185            unit_halo_(halo::NO_HALO),
00186            getsHit_(o.getsHit_),
00187            refreshing_(o.refreshing_),
00188            hidden_(o.hidden_),
00189            draw_bars_(o.draw_bars_),
00190 
00191            modifications_(o.modifications_),
00192            invisibility_cache_()
00193 {
00194 }
00195 
00196 unit::unit(const config &cfg, bool use_traits, game_state* state, const vconfig* vcfg) :
00197     cfg_(),
00198     loc_(cfg["x"] - 1, cfg["y"] - 1),
00199     advances_to_(),
00200     type_(cfg["type"]),
00201     race_(NULL),
00202     id_(cfg["id"]),
00203     name_(cfg["name"].t_str()),
00204     underlying_id_(0),
00205     type_name_(),
00206     undead_variation_(),
00207     variation_(cfg["variation"]),
00208     hit_points_(1),
00209     max_hit_points_(0),
00210     experience_(0),
00211     max_experience_(0),
00212     level_(0),
00213     canrecruit_(cfg["canrecruit"].to_bool()),
00214     recruit_list_(),
00215     alignment_(),
00216     flag_rgb_(),
00217     image_mods_(),
00218     unrenamable_(false),
00219     side_(0),
00220     gender_(generate_gender(cfg, state)),
00221     alpha_(),
00222     unit_formula_(),
00223     unit_loop_formula_(),
00224     unit_priority_formula_(),
00225     formula_vars_(),
00226     movement_(0),
00227     max_movement_(0),
00228     movement_costs_(),
00229     vision_(-1),
00230     vision_costs_(),
00231     jamming_(0),
00232     jamming_costs_(),
00233     defense_mods_(),
00234     hold_position_(false),
00235     end_turn_(false),
00236     resting_(false),
00237     attacks_left_(0),
00238     max_attacks_(0),
00239     states_(),
00240     known_boolean_states_(known_boolean_state_names_.size(),false),
00241     variables_(),
00242     events_(),
00243     filter_recall_(),
00244     emit_zoc_(0),
00245     state_(STATE_STANDING),
00246     overlays_(),
00247     role_(cfg["role"]),
00248     attacks_(),
00249     facing_(map_location::NDIRECTIONS),
00250     trait_names_(),
00251     trait_descriptions_(),
00252     unit_value_(),
00253     goto_(),
00254     interrupted_move_(),
00255     flying_(false),
00256     is_fearless_(false),
00257     is_healthy_(false),
00258     modification_descriptions_(),
00259     animations_(),
00260     anim_(NULL),
00261     next_idling_(0),
00262     frame_begin_time_(0),
00263     unit_halo_(halo::NO_HALO),
00264     getsHit_(0),
00265     refreshing_(false),
00266     hidden_(false),
00267     draw_bars_(false),
00268     modifications_(),
00269     invisibility_cache_()
00270 {
00271     if (type_.empty()) {
00272         throw game::game_error("creating unit with an empty type field");
00273     }
00274 
00275     side_ = cfg["side"];
00276     if(side_ <= 0) {
00277         side_ = 1;
00278     }
00279 
00280     validate_side(side_);
00281 
00282     underlying_id_ = cfg["underlying_id"];
00283     set_underlying_id();
00284 
00285     overlays_ = utils::parenthetical_split(cfg["overlays"], ',');
00286     if(overlays_.size() == 1 && overlays_.front() == "") {
00287         overlays_.clear();
00288     }
00289     if (const config &variables = cfg.child("variables")) {
00290         variables_ = variables;
00291     }
00292 
00293     if(vcfg) {
00294         const vconfig& filter_recall = vcfg->child("filter_recall");
00295         if(!filter_recall.null())
00296             filter_recall_ = filter_recall.get_config();
00297 
00298         const vconfig::child_list& events = vcfg->get_children("event");
00299         foreach(const vconfig& e, events) {
00300             events_.add_child("event", e.get_config());
00301         }
00302     }
00303     else
00304     {
00305         filter_recall_ = cfg.child_or_empty("filter_recall");
00306 
00307         foreach(const config& unit_event, cfg.child_range("event")) {
00308             events_.add_child("event", unit_event);
00309         }
00310     }
00311     game_events::add_events(events_.child_range("event"));
00312 
00313 
00314     facing_ = map_location::parse_direction(cfg["facing"]);
00315     if(facing_ == map_location::NDIRECTIONS) facing_ = static_cast<map_location::DIRECTION>(rand()%map_location::NDIRECTIONS);
00316 
00317     if (const config &mods = cfg.child("modifications")) {
00318         modifications_ = mods;
00319     }
00320 
00321     advance_to(cfg, type(), use_traits, state);
00322     if (const config::attribute_value *v = cfg.get("race")) {
00323         if (const unit_race *r = unit_types.find_race(*v)) {
00324             race_ = r;
00325         } else {
00326             static const unit_race dummy_race;
00327             race_ = &dummy_race;
00328         }
00329     }
00330     level_ = cfg["level"].to_int(level_);
00331     if (const config::attribute_value *v = cfg.get("undead_variation")) {
00332         undead_variation_ = v->str();
00333     }
00334     if(const config::attribute_value *v = cfg.get("max_attacks")) {
00335         max_attacks_ = std::max(0, v->to_int(1));
00336     }
00337     attacks_left_ = std::max(0, cfg["attacks_left"].to_int(max_attacks_));
00338 
00339     if (const config::attribute_value *v = cfg.get("alpha")) {
00340         alpha_ = lexical_cast_default<fixed_t>(*v);
00341     }
00342     if (const config::attribute_value *v = cfg.get("zoc")) {
00343         emit_zoc_ = v->to_bool(level_ > 0);
00344     }
00345     if (const config::attribute_value *v = cfg.get("flying")) {
00346         flying_ = v->to_bool();
00347     }
00348     if (const config::attribute_value *v = cfg.get("description")) {
00349         cfg_["description"] = *v;
00350     }
00351     if (const config::attribute_value *v = cfg.get("cost")) {
00352         unit_value_ = *v;
00353     }
00354     if (const config::attribute_value *v = cfg.get("halo")) {
00355         clear_haloes();
00356         cfg_["halo"] = *v;
00357     }
00358     if (const config::attribute_value *v = cfg.get("profile")) {
00359         std::string big = *v, small = cfg["small_profile"];
00360         adjust_profile(small, big, "");
00361         cfg_["profile"] = big;
00362         cfg_["small_profile"] = small;
00363     }
00364     max_hit_points_ = std::max(1, cfg["max_hitpoints"].to_int(max_hit_points_));
00365     max_movement_ = std::max(0, cfg["max_moves"].to_int(max_movement_));
00366     max_experience_ = std::max(1, cfg["max_experience"].to_int(max_experience_));
00367 
00368     std::vector<std::string> temp_advances = utils::split(cfg["advances_to"]);
00369     if(temp_advances.size() == 1 && temp_advances.front() == "null") {
00370         advances_to_.clear();
00371     }else if(temp_advances.size() >= 1 && temp_advances.front() != "") {
00372         advances_to_ = temp_advances;
00373     }
00374 
00375     if (const config &ai = cfg.child("ai"))
00376     {
00377         unit_formula_ = ai["formula"].str();
00378         unit_loop_formula_ = ai["loop_formula"].str();
00379         unit_priority_formula_ = ai["priority"].str();
00380 
00381         if (const config &ai_vars = ai.child("vars"))
00382         {
00383             formula_vars_ = new game_logic::map_formula_callable;
00384 
00385             variant var;
00386             foreach (const config::attribute &i, ai_vars.attribute_range()) {
00387                 var.serialize_from_string(i.second);
00388                 formula_vars_->add(i.first, var);
00389             }
00390         } else {
00391             formula_vars_ = game_logic::map_formula_callable_ptr();
00392         }
00393     }
00394 
00395     //don't use the unit_type's attacks if this config has its own defined
00396     config::const_child_itors cfg_range = cfg.child_range("attack");
00397     if(cfg_range.first != cfg_range.second) {
00398         attacks_.clear();
00399         do {
00400             attacks_.push_back(attack_type(*cfg_range.first));
00401         } while(++cfg_range.first != cfg_range.second);
00402     }
00403 
00404     //don't use the unit_type's abilities if this config has its own defined
00405     cfg_range = cfg.child_range("abilities");
00406     if(cfg_range.first != cfg_range.second) {
00407         cfg_.clear_children("abilities");
00408         config &target = cfg_.add_child("abilities");
00409         do {
00410             target.append(*cfg_range.first);
00411         } while(++cfg_range.first != cfg_range.second);
00412     }
00413 
00414     //adjust the unit_type's defense if this config has its own defined
00415     cfg_range = cfg.child_range("defense");
00416     if(cfg_range.first != cfg_range.second) {
00417         config &target = cfg_.child_or_add("defense");
00418         do {
00419             target.append(*cfg_range.first);
00420         } while(++cfg_range.first != cfg_range.second);
00421     }
00422 
00423     //adjust the unit_type's movement costs if this config has its own defined
00424     cfg_range = cfg.child_range("movement_costs");
00425     if(cfg_range.first != cfg_range.second) {
00426         config &target = cfg_.child_or_add("movement_costs");
00427         do {
00428             target.append(*cfg_range.first);
00429         } while(++cfg_range.first != cfg_range.second);
00430     }
00431 
00432     //adjust the unit_type's vision costs if this config has its own defined
00433     cfg_range = cfg.child_range("vision_costs");
00434     if(cfg_range.first != cfg_range.second) {
00435         config &target = cfg_.child_or_add("vision_costs");
00436         do {
00437             target.append(*cfg_range.first);
00438         } while(++cfg_range.first != cfg_range.second);
00439     }
00440 
00441     //adjust the unit_type's jamming costs if this config has its own defined
00442     cfg_range = cfg.child_range("jamming_costs");
00443     if(cfg_range.first != cfg_range.second) {
00444         config &target = cfg_.child_or_add("jamming_costs");
00445         do {
00446             target.append(*cfg_range.first);
00447         } while(++cfg_range.first != cfg_range.second);
00448     }
00449 
00450     //adjust the unit_type's resistance if this config has its own defined
00451     cfg_range = cfg.child_range("resistance");
00452     if(cfg_range.first != cfg_range.second) {
00453         config &target = cfg_.child_or_add("resistance");
00454         do {
00455             target.append(*cfg_range.first);
00456         } while(++cfg_range.first != cfg_range.second);
00457     }
00458 
00459     if (const config &status_flags = cfg.child("status"))
00460     {
00461         foreach (const config::attribute &st, status_flags.attribute_range()) {
00462             if (st.first == "healable") {
00463                 ///@deprecated 1.9.2 'healable' instead of 'unhealable'
00464                 ERR_UT << "Usage of 'healable' is deprecated, use 'unhealable' instead, "
00465                     "support will be removed in 1.9.2.\n";
00466                 if (!st.second.to_bool(true))
00467                     set_state("unhealable", true);
00468             } else if (st.second.to_bool()) {
00469                 set_state(st.first, true);
00470             }
00471         }
00472     }
00473     if(cfg["ai_special"] == "guardian") {
00474         set_state(STATE_GUARDIAN, true);
00475     }
00476 
00477     // Remove animations from private cfg, they're not needed there now
00478     foreach(const std::string& tag_name, unit_animation::all_tag_names()) {
00479         cfg_.clear_children(tag_name);
00480     }
00481 
00482     if (const config::attribute_value *v = cfg.get("hitpoints")) {
00483         hit_points_ = *v;
00484     } else {
00485         hit_points_ = max_hit_points_;
00486     }
00487 
00488     goto_.x = cfg["goto_x"].to_int() - 1;
00489     goto_.y = cfg["goto_y"].to_int() - 1;
00490 
00491     if (const config::attribute_value *v = cfg.get("moves")) {
00492         movement_ = *v;
00493         if(movement_ < 0) {
00494             attacks_left_ = 0;
00495             movement_ = 0;
00496         }
00497     } else {
00498         movement_ = max_movement_;
00499     }
00500     experience_ = cfg["experience"];
00501     resting_ = cfg["resting"].to_bool();
00502     unrenamable_ = cfg["unrenamable"].to_bool();
00503 
00504     const std::string& align = cfg["alignment"];
00505     if(align == "lawful") {
00506         alignment_ = unit_type::LAWFUL;
00507     } else if(align == "neutral") {
00508         alignment_ = unit_type::NEUTRAL;
00509     } else if(align == "chaotic") {
00510         alignment_ = unit_type::CHAOTIC;
00511     } else if(align == "liminal") {
00512         alignment_ = unit_type::LIMINAL;
00513     } else if(align.empty()==false){
00514         alignment_ = unit_type::NEUTRAL;
00515     }
00516 
00517     generate_name(state ? &(state->rng()) : 0);
00518 
00519     // Make the default upkeep "full"
00520     if(cfg_["upkeep"].empty()) {
00521         cfg_["upkeep"] = "full";
00522     }
00523 
00524     set_recruits(utils::split(cfg["extra_recruit"]));
00525 
00526     /** @todo Are these modified by read? if not they can be removed. */
00527     getsHit_=0;
00528     end_turn_ = false;
00529     refreshing_  = false;
00530     hidden_ = false;
00531     game_config::add_color_info(cfg);
00532 
00533     config input_cfg;
00534     input_cfg.merge_attributes(cfg);
00535 
00536     static char const *internalized_attrs[] = { "type", "id", "name",
00537         "gender", "random_gender", "variation", "role", "ai_special",
00538         "side", "underlying_id", "overlays", "facing", "race",
00539         "level", "undead_variation", "max_attacks",
00540         "attacks_left", "alpha", "zoc", "flying", "cost",
00541         "max_hitpoints", "max_moves", "vision", "jamming", "max_experience",
00542         "advances_to", "hitpoints", "goto_x", "goto_y", "moves",
00543         "experience", "resting", "unrenamable", "alignment",
00544         "canrecruit", "extra_recruit", "x", "y", "placement",
00545         // Useless attributes created when saving units to WML:
00546         "flag_rgb", "language_name" };
00547     foreach (const char *attr, internalized_attrs) {
00548         input_cfg.remove_attribute(attr);
00549         cfg_.remove_attribute(attr);
00550     }
00551 
00552     static char const *raw_attrs[] = { "description", "halo",
00553         "profile", "small_profile", "upkeep", "usage", "ellipse",
00554         "image", "image_icon", "random_traits", "generate_name" };
00555     foreach (const char *attr, raw_attrs) {
00556         input_cfg.remove_attribute(attr);
00557     }
00558 
00559     foreach (const config::attribute &attr, input_cfg.attribute_range()) {
00560         if (attr.first == "do_not_list") continue;
00561         WRN_UT << "Unknown attribute '" << attr.first << "' discarded.\n";
00562     }
00563 
00564 }
00565 
00566 void unit::clear_status_caches()
00567 {
00568     for(std::vector<const unit *>::const_iterator itor = units_with_cache.begin();
00569             itor != units_with_cache.end(); ++itor) {
00570         (*itor)->clear_visibility_cache();
00571     }
00572 
00573     units_with_cache.clear();
00574 }
00575 
00576 unit::unit(const unit_type *t, int side, bool real_unit,
00577     unit_race::GENDER gender) :
00578     cfg_(),
00579     loc_(),
00580     advances_to_(),
00581     type_(),
00582     race_(NULL),
00583     id_(),
00584     name_(),
00585     underlying_id_(real_unit? 0: n_unit::id_manager::instance().next_fake_id()),
00586     type_name_(),
00587     undead_variation_(),
00588     variation_(),
00589     hit_points_(0),
00590     max_hit_points_(0),
00591     experience_(0),
00592     max_experience_(0),
00593     level_(0),
00594     canrecruit_(false),
00595     recruit_list_(),
00596     alignment_(),
00597     flag_rgb_(),
00598     image_mods_(),
00599     unrenamable_(false),
00600     side_(side),
00601     gender_(gender != unit_race::NUM_GENDERS ?
00602         gender : generate_gender(t->id(), real_unit, NULL)),
00603     alpha_(),
00604     unit_formula_(),
00605     unit_loop_formula_(),
00606     unit_priority_formula_(),
00607     formula_vars_(),
00608     movement_(0),
00609     max_movement_(0),
00610     movement_costs_(),
00611     vision_(-1),
00612     vision_costs_(),
00613     jamming_(0),
00614     jamming_costs_(),
00615     defense_mods_(),
00616     hold_position_(false),
00617     end_turn_(false),
00618     resting_(false),
00619     attacks_left_(0),
00620     max_attacks_(0),
00621     states_(),
00622     known_boolean_states_(known_boolean_state_names_.size(),false),
00623     variables_(),
00624     events_(),
00625     filter_recall_(),
00626     emit_zoc_(0),
00627     state_(STATE_STANDING),
00628     overlays_(),
00629     role_(),
00630     attacks_(),
00631     facing_(static_cast<map_location::DIRECTION>(rand()%map_location::NDIRECTIONS)),
00632     trait_names_(),
00633     trait_descriptions_(),
00634     unit_value_(),
00635     goto_(),
00636     interrupted_move_(),
00637     flying_(false),
00638     is_fearless_(false),
00639     is_healthy_(false),
00640     modification_descriptions_(),
00641     animations_(),
00642     anim_(NULL),
00643     next_idling_(0),
00644     frame_begin_time_(0),
00645     unit_halo_(halo::NO_HALO),
00646     getsHit_(0),
00647     refreshing_(false),
00648     hidden_(false),
00649     draw_bars_(false),
00650     modifications_(),
00651     invisibility_cache_()
00652 {
00653 
00654     cfg_["upkeep"]="full";
00655     advance_to(t, real_unit);
00656 
00657     if(real_unit) {
00658         generate_name();
00659     }
00660     set_underlying_id();
00661 
00662     // fill those after traits and modifs to have correct max
00663     movement_ = max_movement_;
00664     hit_points_ = max_hit_points_;
00665     attacks_left_ = max_attacks_;
00666 
00667     /**
00668      * @todo Test whether the calls above modify these values if not they can
00669      * removed, since already set in the initialization list.
00670      */
00671     unrenamable_ = false;
00672     anim_ = NULL;
00673     getsHit_ = 0;
00674     end_turn_ = false;
00675     hold_position_ = false;
00676     next_idling_ = 0;
00677     frame_begin_time_ = 0;
00678     unit_halo_ = halo::NO_HALO;
00679 
00680 }
00681 
00682 unit::~unit()
00683 {
00684     clear_haloes();
00685 
00686     delete anim_;
00687 
00688     // Remove us from the status cache
00689     std::vector<const unit *>::iterator itor =
00690     std::find(units_with_cache.begin(), units_with_cache.end(), this);
00691 
00692     if(itor != units_with_cache.end()) {
00693         units_with_cache.erase(itor);
00694     }
00695 }
00696 
00697 
00698 
00699 unit& unit::operator=(const unit& u)
00700 {
00701     // Use copy constructor to make sure we are coherant
00702     if (this != &u) {
00703         this->~unit();
00704         new (this) unit(u) ;
00705     }
00706     return *this ;
00707 }
00708 
00709 
00710 void unit::generate_name(rand_rng::simple_rng* rng)
00711 {
00712     if (!name_.empty() || !cfg_["generate_name"].to_bool(true)) return;
00713 
00714     name_ = race_->generate_name(gender_, rng);
00715     cfg_["generate_name"] = false;
00716 }
00717 
00718 // Apply mandatory traits (e.g. undead, mechanical) to a unit and then
00719 // fill out with available (leaders have a restricted set of available traits)
00720 // traits until no more are available or the unit has its maximum number
00721 // of traits.
00722 // This routine does not apply the effects of added traits to a unit.
00723 // That must be done by the caller.
00724 // Note that random numbers used in config files don't work in multiplayer,
00725 // so that leaders should be barred from all random traits until that
00726 // is fixed. Later the restrictions will be based on play balance.
00727 // @musthaveonly is true when you don't want to generate random traits or
00728 // you don't want to give any optional traits to a unit.
00729 
00730 void unit::generate_traits(bool musthaveonly, game_state* state)
00731 {
00732     LOG_UT << "Generating a trait for unit type " << type_id() << " with musthaveonly " << musthaveonly << "\n";
00733     const unit_type *type = unit_types.find(type_id());
00734     // Calculate the unit's traits
00735     if (!type) {
00736         std::string error_message = _("Unknown unit type '$type|' while generating traits");
00737         utils::string_map symbols;
00738         symbols["type"] = type_id();
00739         error_message = utils::interpolate_variables_into_string(error_message, &symbols);
00740         ERR_NG << "unit of type " << type_id() << " not found!\n";
00741         throw game::game_error(error_message);
00742     }
00743 
00744     config::const_child_itors current_traits = modifications_.child_range("trait");
00745     std::vector<config> candidate_traits;
00746 
00747     foreach (const config &t, type->possible_traits())
00748     {
00749         // Skip the trait if the unit already has it.
00750         const std::string &tid = t["id"];
00751         bool already = false;
00752         foreach (const config &mod, current_traits)
00753         {
00754             if (mod["id"] == tid) {
00755                 already = true;
00756                 break;
00757             }
00758         }
00759         if (already) continue;
00760 
00761         // Add the trait if it is mandatory.
00762         const std::string &avl = t["availability"];
00763         if (avl == "musthave")
00764         {
00765             modifications_.add_child("trait", t);
00766             current_traits = modifications_.child_range("trait");
00767             continue;
00768         }
00769 
00770         // The trait is still available, mark it as a candidate for randomizing.
00771         // For leaders, only traits with availability "any" are considered.
00772         if (!musthaveonly && (!can_recruit() || avl == "any"))
00773             candidate_traits.push_back(t);
00774     }
00775 
00776     if (musthaveonly) return;
00777 
00778     // Now randomly fill out to the number of traits required or until
00779     // there aren't any more traits.
00780     int nb_traits = std::distance(current_traits.first, current_traits.second);
00781     int max_traits = type->num_traits();
00782     for (; nb_traits < max_traits && !candidate_traits.empty(); ++nb_traits)
00783     {
00784         int num = (state ? state->rng().get_next_random() : get_random_nocheck())
00785                   % candidate_traits.size();
00786         modifications_.add_child("trait", candidate_traits[num]);
00787         candidate_traits.erase(candidate_traits.begin() + num);
00788     }
00789 
00790     // Once random traits are added, don't do it again.
00791     // Such as when restoring a saved character.
00792     cfg_["random_traits"] = false;
00793 }
00794 
00795 std::vector<std::string> unit::get_traits_list() const
00796 {
00797     std::vector<std::string> res;
00798 
00799     foreach (const config &mod, modifications_.child_range("trait"))
00800     {
00801             std::string const &id = mod["id"];
00802             if (!id.empty())
00803                 res.push_back(id);
00804     }
00805     return res;
00806 }
00807 
00808 void unit::advance_to(const config &old_cfg, const unit_type *t,
00809     bool use_traits, game_state *state)
00810 {
00811     t = &t->get_gender_unit_type(gender_).get_variation(variation_);
00812 
00813     // Reset the scalar values first
00814     trait_names_.clear();
00815     trait_descriptions_.clear(),
00816     is_fearless_ = false;
00817     is_healthy_ = false;
00818 
00819     // Clear modification-related caches
00820     modification_descriptions_.clear();
00821     movement_costs_.clear();
00822     vision_costs_.clear();
00823     jamming_costs_.clear();
00824     defense_mods_.clear();
00825 
00826     // Clear the stored config and replace it with the one from the unit type,
00827     // except for a few attributes.
00828     config new_cfg;
00829     static char const *persistent_attrs[] = { "upkeep", "ellipse",
00830         "image", "image_icon", "usage", "random_traits", "generate_name" };
00831     foreach (const char *attr, persistent_attrs) {
00832         if (const config::attribute_value *v = old_cfg.get(attr)) {
00833             new_cfg[attr] = *v;
00834         }
00835     }
00836 
00837     if(t->movement_type().get_parent()) {
00838         new_cfg.merge_with(t->movement_type().get_parent()->get_cfg());
00839     }
00840 
00841     new_cfg.merge_with(t->cfg_);
00842 
00843     // Remove pure unit_type attributes.
00844     static char const *unit_type_attrs[] = { "movement", "movement_type",
00845         "die_sound", "flies", "inherit", "variation_name",
00846         "ignore_race_traits", "hide_help" };
00847     foreach (const char *attr, unit_type_attrs) {
00848         new_cfg.remove_attribute(attr);
00849     }
00850 
00851     // If unit has specific profile, remember it and keep it after advancing
00852     const unit_type *u_type = type();
00853     std::string profile = old_cfg["profile"].str();
00854     if (!profile.empty() && (!u_type || profile != u_type->big_profile())) {
00855         new_cfg["profile"] = profile;
00856     } else if (t) {
00857         new_cfg["profile"] = t->big_profile();
00858     }
00859     profile = old_cfg["small_profile"].str();
00860     if (!profile.empty() && (!u_type || profile != u_type->small_profile())) {
00861         new_cfg["small_profile"] = profile;
00862     } else if (t) {
00863         new_cfg["small_profile"] = t->small_profile();
00864     }
00865 
00866     cfg_.swap(new_cfg);
00867     cfg_.clear_children("male");
00868     cfg_.clear_children("female");
00869 
00870     advances_to_ = t->advances_to();
00871 
00872     race_ = t->race_;
00873     type_name_ = t->type_name();
00874     cfg_["description"] = t->unit_description();
00875     undead_variation_ = t->undead_variation();
00876     max_experience_ = t->experience_needed(false);
00877     level_ = t->level();
00878     alignment_ = t->alignment();
00879     alpha_ = t->alpha();
00880     hit_points_ = t->hitpoints();
00881     max_hit_points_ = t->hitpoints();
00882     max_movement_ = t->movement();
00883     vision_ = t->vision();
00884     jamming_ = t->jamming();
00885     emit_zoc_ = t->has_zoc();
00886     attacks_ = t->attacks();
00887     unit_value_ = t->cost();
00888     flying_ = t->movement_type().is_flying();
00889 
00890     max_attacks_ = t->max_attacks();
00891 
00892     animations_ = t->animations();
00893 
00894     flag_rgb_ = t->flag_rgb();
00895 
00896 
00897     bool do_heal = false; // Track whether unit should get fully healed.
00898 
00899     if(type_id()!=t->id()) {
00900         do_heal = true; // Can't heal until after mods applied.
00901         type_ = t->id();
00902     }
00903 
00904     if (cfg_["random_traits"].to_bool(true)) {
00905         generate_traits(!use_traits, state);
00906     } else {
00907         // This will add any "musthave" traits to the new unit that it doesn't already have.
00908         // This covers the Dark Sorcerer advancing to Lich and gaining the "undead" trait,
00909         // but random and/or optional traits are not added,
00910         // and neither are inappropriate traits removed.
00911         generate_traits(true);
00912     }
00913 
00914     // Apply modifications etc, refresh the unit.
00915     // This needs to be after type and gender are fixed,
00916     // since there can be filters on the modifications
00917     // that may result in different effects after the advancement.
00918     apply_modifications();
00919 
00920     // Not that the unit has all of its modifications applied, it is
00921     // OK to heal it.
00922     if (do_heal) {
00923         heal_all();
00924     }
00925 
00926     // In case the unit carries EventWML, apply it now
00927     game_events::add_events(cfg_.child_range("event"), type_);
00928     cfg_.clear_children("event");
00929 
00930     refreshing_ = false;
00931     delete anim_;
00932     anim_ = NULL;
00933 }
00934 
00935 const unit_type* unit::type() const
00936 {
00937     if (type_.empty()) return NULL;
00938     const unit_type &i = get_unit_type(type_);
00939     return &i.get_gender_unit_type(gender_).get_variation(variation_);
00940 }
00941 
00942 std::string unit::big_profile() const
00943 {
00944     const std::string &prof = cfg_["profile"];
00945     if (!prof.empty() && prof != "unit_image") {
00946         return prof;
00947     }
00948     return absolute_image();
00949 }
00950 
00951 std::string unit::small_profile() const
00952 {
00953     const std::string &prof = cfg_["small_profile"];
00954     if (!prof.empty() && prof != "unit_image") {
00955         return prof;
00956     }
00957     return absolute_image();
00958 }
00959 
00960 static SDL_Color hp_color_(int hitpoints, int max_hitpoints)
00961 {
00962     double unit_energy = 0.0;
00963     SDL_Color energy_color = {0,0,0,0};
00964 
00965     if(max_hitpoints > 0) {
00966         unit_energy = double(hitpoints)/double(max_hitpoints);
00967     }
00968 
00969     if(1.0 == unit_energy){
00970         energy_color.r = 33;
00971         energy_color.g = 225;
00972         energy_color.b = 0;
00973     } else if(unit_energy > 1.0) {
00974         energy_color.r = 100;
00975         energy_color.g = 255;
00976         energy_color.b = 100;
00977     } else if(unit_energy >= 0.75) {
00978         energy_color.r = 170;
00979         energy_color.g = 255;
00980         energy_color.b = 0;
00981     } else if(unit_energy >= 0.5) {
00982         energy_color.r = 255;
00983         energy_color.g = 175;
00984         energy_color.b = 0;
00985     } else if(unit_energy >= 0.25) {
00986         energy_color.r = 255;
00987         energy_color.g = 155;
00988         energy_color.b = 0;
00989     } else {
00990         energy_color.r = 255;
00991         energy_color.g = 0;
00992         energy_color.b = 0;
00993     }
00994     return energy_color;
00995 }
00996 
00997 SDL_Color unit::hp_color() const
00998 {
00999     return hp_color_(hitpoints(), max_hitpoints());
01000 }
01001 
01002 SDL_Color unit::hp_color(int new_hitpoints) const
01003 {
01004     return hp_color_(new_hitpoints, hitpoints());
01005 }
01006 
01007 SDL_Color unit::xp_color() const
01008 {
01009     const SDL_Color near_advance_color = {255,255,255,0};
01010     const SDL_Color mid_advance_color  = {150,255,255,0};
01011     const SDL_Color far_advance_color  = {0,205,205,0};
01012     const SDL_Color normal_color      = {0,160,225,0};
01013     const SDL_Color near_amla_color   = {225,0,255,0};
01014     const SDL_Color mid_amla_color    = {169,30,255,0};
01015     const SDL_Color far_amla_color    = {139,0,237,0};
01016     const SDL_Color amla_color        = {170,0,255,0};
01017     const bool near_advance = max_experience() - experience() <= game_config::kill_experience;
01018     const bool mid_advance  = max_experience() - experience() <= game_config::kill_experience*2;
01019     const bool far_advance  = max_experience() - experience() <= game_config::kill_experience*3;
01020 
01021     SDL_Color color=normal_color;
01022     if(advances_to().size()){
01023         if(near_advance){
01024             color=near_advance_color;
01025         } else if(mid_advance){
01026             color=mid_advance_color;
01027         } else if(far_advance){
01028             color=far_advance_color;
01029         }
01030     } else if (get_modification_advances().size()){
01031         if(near_advance){
01032             color=near_amla_color;
01033         } else if(mid_advance){
01034             color=mid_amla_color;
01035         } else if(far_advance){
01036             color=far_amla_color;
01037         } else {
01038             color=amla_color;
01039         }
01040     }
01041     return(color);
01042 }
01043 
01044 void unit::set_recruits(const std::vector<std::string>& recruits)
01045 {
01046     unit_types.check_types(recruits);
01047     recruit_list_ = recruits;
01048     //TODO crab
01049     //info_.minimum_recruit_price = 0;
01050     //ai::manager::raise_recruit_list_changed();
01051 }
01052 
01053 const std::vector<std::string> unit::advances_to_translated() const
01054 {
01055     std::vector<std::string> result;
01056     foreach (std::string type_id, advances_to_)
01057     {
01058         const unit_type *type = unit_types.find(type_id);
01059         if (type)
01060             result.push_back(type->type_name());
01061         else
01062             WRN_UT << "unknown unit in advances_to list of type "
01063             << type_ << ": " << type_id << "\n";
01064     }
01065     return result;
01066 }
01067 
01068 void unit::set_advances_to(const std::vector<std::string>& advances_to)
01069 {
01070     unit_types.check_types(advances_to);
01071     advances_to_ = advances_to;
01072 }
01073 
01074 std::string unit::side_id() const {return teams_manager::get_teams()[side()-1].save_id(); }
01075 
01076 void unit::set_movement(int moves)
01077 {
01078     //FIXME: we shouldn't set those here, other code use this a simple setter.
01079     hold_position_ = false;
01080     end_turn_ = false;
01081     movement_ = std::max<int>(0, moves);
01082 }
01083 
01084 void unit::new_turn()
01085 {
01086     end_turn_ = false;
01087     movement_ = total_movement();
01088     attacks_left_ = max_attacks_;
01089     set_state(STATE_UNCOVERED, false);
01090 
01091     bool rebuild_from_type = false;
01092     for(unsigned int i = 0; i != NumModificationTypes; ++i) {
01093         const std::string& mod_name = ModificationTypes[i];
01094         for (int j = modifications_.child_count(mod_name) - 1; j >= 0; --j)
01095         {
01096             const config &mod = modifications_.child(mod_name, j);
01097             const std::string& duration = mod["duration"];
01098             if (duration == "turn") {
01099                 if (const config::attribute_value *v = mod.get("prev_type")) {
01100                     type_ = v->str();
01101                 }
01102                 modifications_.remove_child(mod_name, j);
01103                 rebuild_from_type = true;
01104             }
01105         }
01106     }
01107     if(rebuild_from_type) {
01108         int old_hp = hit_points_;
01109         advance_to(type());
01110         if(hit_points_ > old_hp)
01111             hit_points_ = old_hp;
01112     }
01113 
01114     if (hold_position_) {
01115         end_turn_ = true;
01116     }
01117 }
01118 void unit::end_turn()
01119 {
01120     set_state(STATE_SLOWED,false);
01121     if((movement_ != total_movement()) && !(get_state(STATE_NOT_MOVED))) {
01122         resting_ = false;
01123     }
01124     set_state(STATE_NOT_MOVED,false);
01125     // Clear interrupted move
01126     set_interrupted_move(map_location());
01127 }
01128 void unit::new_scenario()
01129 {
01130 
01131     // Set the goto-command to be going to no-where
01132     goto_ = map_location();
01133 
01134     bool rebuild_from_type = false;
01135 
01136     for(unsigned int i = 0; i != NumModificationTypes; ++i) {
01137         const std::string& mod_name = ModificationTypes[i];
01138         for (int j = modifications_.child_count(mod_name) - 1; j >= 0; --j)
01139         {
01140             const config &mod = modifications_.child(mod_name, j);
01141             const std::string& duration = mod["duration"];
01142             if (!duration.empty() && duration != "forever") {
01143                 if (const config::attribute_value *v = mod.get("prev_type")) {
01144                     type_ = v->str();
01145                 }
01146                 modifications_.remove_child(mod_name, j);
01147                 rebuild_from_type = true;
01148             }
01149         }
01150     }
01151     if(rebuild_from_type) {
01152         advance_to(type());
01153     }
01154 
01155     heal_all();
01156     set_state(STATE_SLOWED, false);
01157     set_state(STATE_POISONED, false);
01158     set_state(STATE_PETRIFIED, false);
01159     set_state(STATE_GUARDIAN, false);
01160 }
01161 
01162 void unit::heal(int amount)
01163 {
01164     int max_hp = max_hitpoints();
01165     if (hit_points_ < max_hp) {
01166         hit_points_ += amount;
01167         if (hit_points_ > max_hp) {
01168             hit_points_ = max_hp;
01169         }
01170     }
01171     if(hit_points_<1) {
01172         hit_points_ = 1;
01173     }
01174 }
01175 
01176 const std::map<std::string,std::string> unit::get_states() const
01177 {
01178     std::map<std::string, std::string> all_states;
01179     foreach (std::string const &s, states_) {
01180         all_states[s] = "yes";
01181     }
01182     for (std::map<std::string, state_t>::const_iterator i = known_boolean_state_names_.begin(),
01183          i_end = known_boolean_state_names_.end(); i != i_end; ++i)
01184     {
01185         if (get_state(i->second)) {
01186             all_states.insert(make_pair(i->first, "yes"));
01187         }
01188 
01189     }
01190     return all_states;
01191 }
01192 
01193 bool unit::get_state(const std::string &state) const
01194 {
01195     state_t known_boolean_state_id = get_known_boolean_state_id(state);
01196     if (known_boolean_state_id!=STATE_UNKNOWN){
01197         return get_state(known_boolean_state_id);
01198     }
01199     return states_.find(state) != states_.end();
01200 }
01201 
01202 void unit::set_state(state_t state, bool value)
01203 {
01204     known_boolean_states_[state] = value;
01205 }
01206 
01207 bool unit::get_state(state_t state) const
01208 {
01209     return known_boolean_states_[state];
01210 }
01211 
01212 unit::state_t unit::get_known_boolean_state_id(const std::string &state) {
01213     std::map<std::string, state_t>::const_iterator i = known_boolean_state_names_.find(state);
01214     if (i != known_boolean_state_names_.end()) {
01215         return i->second;
01216     }
01217     return STATE_UNKNOWN;
01218 }
01219 
01220 std::map<std::string, unit::state_t> unit::known_boolean_state_names_ = get_known_boolean_state_names();
01221 
01222 std::map<std::string, unit::state_t> unit::get_known_boolean_state_names()
01223 {
01224     std::map<std::string, state_t> known_boolean_state_names_map;
01225     known_boolean_state_names_map.insert(std::make_pair("slowed",STATE_SLOWED));
01226     known_boolean_state_names_map.insert(std::make_pair("poisoned",STATE_POISONED));
01227     known_boolean_state_names_map.insert(std::make_pair("petrified",STATE_PETRIFIED));
01228     known_boolean_state_names_map.insert(std::make_pair("uncovered", STATE_UNCOVERED));
01229     known_boolean_state_names_map.insert(std::make_pair("not_moved",STATE_NOT_MOVED));
01230     known_boolean_state_names_map.insert(std::make_pair("unhealable",STATE_UNHEALABLE));
01231     known_boolean_state_names_map.insert(std::make_pair("guardian",STATE_GUARDIAN));
01232     return known_boolean_state_names_map;
01233 }
01234 
01235 void unit::set_state(const std::string &state, bool value)
01236 {
01237     state_t known_boolean_state_id = get_known_boolean_state_id(state);
01238     if (known_boolean_state_id != STATE_UNKNOWN) {
01239         set_state(known_boolean_state_id, value);
01240         return;
01241     }
01242     if (value)
01243         states_.insert(state);
01244     else
01245         states_.erase(state);
01246 }
01247 
01248 
01249 bool unit::has_ability_by_id(const std::string& ability) const
01250 {
01251     if (const config &abil = cfg_.child("abilities"))
01252     {
01253         foreach (const config::any_child &ab, abil.all_children_range()) {
01254             if (ab.cfg["id"] == ability)
01255                 return true;
01256         }
01257     }
01258     return false;
01259 }
01260 
01261 void unit::remove_ability_by_id(const std::string &ability)
01262 {
01263     if (config &abil = cfg_.child("abilities"))
01264     {
01265         config::all_children_iterator i = abil.ordered_begin();
01266         while (i != abil.ordered_end()) {
01267             if (i->cfg["id"] == ability) {
01268                 i = abil.erase(i);
01269             } else {
01270                 ++i;
01271             }
01272         }
01273     }
01274 }
01275 
01276 bool unit::matches_filter(const vconfig& cfg, const map_location& loc, bool use_flat_tod) const
01277 {
01278     bool matches = true;
01279 
01280     if(loc.valid()) {
01281         assert(resources::units != NULL);
01282         scoped_xy_unit auto_store("this_unit", loc.x, loc.y, *resources::units);
01283         matches = internal_matches_filter(cfg, loc, use_flat_tod);
01284     } else {
01285         // If loc is invalid, then this is a recall list unit (already been scoped)
01286         matches = internal_matches_filter(cfg, loc, use_flat_tod);
01287     }
01288 
01289     // Handle [and], [or], and [not] with in-order precedence
01290     vconfig::all_children_iterator cond = cfg.ordered_begin();
01291     vconfig::all_children_iterator cond_end = cfg.ordered_end();
01292     while(cond != cond_end)
01293     {
01294 
01295         const std::string& cond_name = cond.get_key();
01296         const vconfig& cond_filter = cond.get_child();
01297 
01298         // Handle [and]
01299         if(cond_name == "and") {
01300             matches = matches && matches_filter(cond_filter,loc,use_flat_tod);
01301         }
01302         // Handle [or]
01303         else if(cond_name == "or") {
01304             matches = matches || matches_filter(cond_filter,loc,use_flat_tod);
01305         }
01306         // Handle [not]
01307         else if(cond_name == "not") {
01308             matches = matches && !matches_filter(cond_filter,loc,use_flat_tod);
01309         }
01310 
01311         ++cond;
01312     }
01313     return matches;
01314 }
01315 
01316 bool unit::internal_matches_filter(const vconfig& cfg, const map_location& loc, bool use_flat_tod) const
01317 {
01318     config::attribute_value cfg_name = cfg["name"];
01319     if (!cfg_name.blank() && cfg_name.str() != name_) {
01320         return false;
01321     }
01322 
01323     const config::attribute_value cfg_id = cfg["id"];
01324     if (!cfg_id.blank()) {
01325         const std::string& id = cfg_id;
01326         const std::string& this_id = this->id();
01327 
01328         if (id == this_id) {
01329         }
01330         else if (std::find(id.begin(), id.end(), ',') == id.end()){
01331             return false;
01332         }
01333         else {
01334             const std::vector<std::string>& ids = utils::split(id);
01335             if (std::find(ids.begin(), ids.end(), this_id) == ids.end()) {
01336                 return false;
01337             }
01338         }
01339     }
01340 
01341     // Allow 'speaker' as an alternative to id, since people use it so often
01342     config::attribute_value cfg_speaker = cfg["speaker"];
01343     if (!cfg_speaker.blank() && cfg_speaker.str() != id()) {
01344         return false;
01345     }
01346 
01347     if(cfg.has_child("filter_location")) {
01348         assert(resources::game_map != NULL);
01349         assert(resources::teams != NULL);
01350         assert(resources::tod_manager != NULL);
01351         assert(resources::units != NULL);
01352         const vconfig& t_cfg = cfg.child("filter_location");
01353         terrain_filter t_filter(t_cfg, *resources::units, use_flat_tod);
01354         if(!t_filter.match(loc)) {
01355             return false;
01356         }
01357     }
01358 
01359     const vconfig& filter_side = cfg.child("filter_side");
01360     if(!filter_side.null()) {
01361         side_filter s_filter(filter_side);
01362         if(!s_filter.match(this->side()))
01363             return false;
01364     }
01365 
01366     // Also allow filtering on location ranges outside of the location filter
01367     config::attribute_value cfg_x = cfg["x"];
01368     config::attribute_value cfg_y = cfg["y"];
01369     if (!cfg_x.blank() || !cfg_y.blank()){
01370         if(cfg_x == "recall" && cfg_y == "recall") {
01371             //locations on the map are considered to not be on a recall list
01372             if ((!resources::game_map && loc.valid()) ||
01373                 (resources::game_map && resources::game_map->on_board(loc)))
01374             {
01375                 return false;
01376             }
01377         } else if(cfg_x.empty() && cfg_y.empty()) {
01378             return false;
01379         } else if(!loc.matches_range(cfg_x, cfg_y)) {
01380             return false;
01381         }
01382     }
01383 
01384     // The type could be a comma separated list of types
01385     config::attribute_value cfg_type = cfg["type"];
01386     if (!cfg_type.blank())
01387     {
01388         std::string type = cfg_type;
01389         const std::string& this_type = type_id();
01390 
01391         // We only do the full CSV search if we find a comma in there,
01392         // and if the subsequence is found within the main sequence.
01393         // This is because doing the full CSV split is expensive.
01394         if(type == this_type) {
01395             // pass
01396         } else if(std::find(type.begin(),type.end(),',') != type.end() &&
01397            std::search(type.begin(),type.end(),this_type.begin(),
01398                        this_type.end()) != type.end()) {
01399             const std::vector<std::string>& vals = utils::split(type);
01400 
01401             if(std::find(vals.begin(),vals.end(),this_type) == vals.end()) {
01402                 return false;
01403             }
01404         } else {
01405             return false;
01406         }
01407     }
01408 
01409     config::attribute_value cfg_ability = cfg["ability"];
01410     if (!cfg_ability.blank())
01411     {
01412         std::string ability = cfg_ability;
01413         if(has_ability_by_id(ability)) {
01414             // pass
01415         } else if(std::find(ability.begin(),ability.end(),',') != ability.end()) {
01416             const std::vector<std::string>& vals = utils::split(ability);
01417             bool has_ability = false;
01418             for(std::vector<std::string>::const_iterator this_ability = vals.begin(); this_ability != vals.end(); ++this_ability) {
01419                 if(has_ability_by_id(*this_ability)) {
01420                     has_ability = true;
01421                     break;
01422                 }
01423             }
01424             if(!has_ability) {
01425                 return false;
01426             }
01427         } else {
01428             return false;
01429         }
01430     }
01431 
01432     config::attribute_value cfg_race = cfg["race"];
01433     if (!cfg_race.blank()) {
01434         std::string race = cfg_race;
01435 
01436         if(race != race_->id()) {
01437             const std::vector<std::string>& vals = utils::split(race);
01438             if(std::find(vals.begin(), vals.end(), race_->id()) == vals.end()) {
01439                 return false;
01440             }
01441         }
01442     }
01443 
01444     config::attribute_value cfg_gender = cfg["gender"];
01445     if (!cfg_gender.blank() && string_gender(cfg_gender) != gender()) {
01446         return false;
01447     }
01448 
01449     config::attribute_value cfg_side = cfg["side"];
01450     if (!cfg_side.blank() && cfg_side.to_int() != side()) {
01451         std::string side = cfg_side;
01452         if (std::find(side.begin(), side.end(), ',') == side.end()) {
01453             return false;
01454         }
01455         std::vector<std::string> vals = utils::split(side);
01456         if (std::find(vals.begin(), vals.end(), str_cast(side_)) == vals.end()) {
01457             return false;
01458         }
01459     }
01460 
01461     config::attribute_value cfg_has_weapon = cfg["has_weapon"];
01462     if (!cfg_has_weapon.blank()) {
01463         std::string weapon = cfg_has_weapon;
01464         bool has_weapon = false;
01465         const std::vector<attack_type>& attacks = this->attacks();
01466         for(std::vector<attack_type>::const_iterator i = attacks.begin();
01467             i != attacks.end(); ++i) {
01468             if(i->id() == weapon) {
01469                 has_weapon = true;
01470                 break;
01471             }
01472         }
01473         if(!has_weapon) {
01474             return false;
01475         }
01476     }
01477 
01478     config::attribute_value cfg_role = cfg["role"];
01479     if (!cfg_role.blank() && cfg_role.str() != role_) {
01480         return false;
01481     }
01482 
01483     config::attribute_value cfg_ai_special = cfg["ai_special"];
01484     if (!cfg_ai_special.blank() && ((cfg_ai_special.str() == "guardian")  != get_state(STATE_GUARDIAN))) {
01485         return false;
01486     }
01487 
01488     config::attribute_value cfg_canrecruit = cfg["canrecruit"];
01489     if (!cfg_canrecruit.blank() && cfg_canrecruit.to_bool() != can_recruit()) {
01490         return false;
01491     }
01492 
01493     config::attribute_value cfg_level = cfg["level"];
01494     if (!cfg_level.blank() && cfg_level.to_int(-1) != level_) {
01495         return false;
01496     }
01497 
01498     config::attribute_value cfg_defense = cfg["defense"];
01499     if (!cfg_defense.blank() && cfg_defense.to_int(-1) != defense_modifier(resources::game_map->get_terrain(loc))) {
01500         return false;
01501     }
01502 
01503     config::attribute_value cfg_movement = cfg["movement_cost"];
01504     if (!cfg_movement.blank() && cfg_movement.to_int(-1) != movement_cost(resources::game_map->get_terrain(loc))) {
01505         return false;
01506     }
01507 
01508     // Now start with the new WML based comparison.
01509     // If a key is in the unit and in the filter, they should match
01510     // filter only => not for us
01511     // unit only => not filtered
01512     const vconfig::child_list& wmlcfgs = cfg.get_children("filter_wml");
01513     if (!wmlcfgs.empty()) {
01514         config unit_cfg;
01515         for (unsigned i = 0; i < wmlcfgs.size(); ++i)
01516         {
01517             config fwml = wmlcfgs[i].get_parsed_config();
01518             /* Check if the filter only cares about variables.
01519                If so, no need to serialize the whole unit. */
01520             config::const_attr_itors ai = fwml.attribute_range();
01521             config::all_children_itors ci = fwml.all_children_range();
01522             if (std::distance(ai.first, ai.second) == 0 &&
01523                 std::distance(ci.first, ci.second) == 1 &&
01524                 ci.first->key == "variables") {
01525                 if (!variables_.matches(ci.first->cfg))
01526                     return false;
01527             } else {
01528                 if (unit_cfg.empty())
01529                     write(unit_cfg);
01530                 if (!unit_cfg.matches(fwml))
01531                     return false;
01532             }
01533         }
01534     }
01535 
01536     if (cfg.has_child("filter_vision")) {
01537         const vconfig::child_list& vis_filt = cfg.get_children("filter_vision");
01538         vconfig::child_list::const_iterator i, i_end = vis_filt.end();
01539         for (i = vis_filt.begin(); i != i_end; ++i) {
01540             bool visible = (*i)["visible"].to_bool(true);
01541             std::set<int> viewers;
01542             if (i->has_attribute("viewing_side")) {
01543                 ERR_NG << "[filter_vision]viewing_side= is deprecated, use side=\n";
01544                 std::vector<std::pair<int,int> > ranges = utils::parse_ranges((*i)["viewing_side"]);
01545                 std::vector<std::pair<int,int> >::const_iterator range, range_end = ranges.end();
01546                 for (range = ranges.begin(); range != range_end; ++range) {
01547                     for (int i=range->first; i<=range->second; ++i) {
01548                         if (i > 0 && static_cast<size_t>(i) <= teams_manager::get_teams().size()) {
01549                             viewers.insert(i);
01550                         }
01551                     }
01552                 }
01553             } else {
01554                 // Use standard side filter
01555                 side_filter ssf(*i);
01556                 std::vector<int> sides = ssf.get_teams();
01557                 viewers.insert(sides.begin(), sides.end());
01558             }
01559             if (viewers.empty()) {
01560                 return false;
01561             }
01562             std::set<int>::const_iterator viewer, viewer_end = viewers.end();
01563             for (viewer = viewers.begin(); viewer != viewer_end; ++viewer) {
01564                 bool not_fogged = !teams_manager::get_teams()[*viewer - 1].fogged(loc);
01565                 bool not_hiding = !this->invisible(loc/*, false(?) */);
01566                 if (visible != not_fogged && not_hiding) {
01567                     return false;
01568                 }
01569             }
01570         }
01571     }
01572 
01573     if (cfg.has_child("filter_adjacent")) {
01574         assert(resources::units && resources::game_map);
01575         const unit_map& units = *resources::units;
01576         map_location adjacent[6];
01577         get_adjacent_tiles(loc, adjacent);
01578         vconfig::child_list::const_iterator i, i_end;
01579         const vconfig::child_list& adj_filt = cfg.get_children("filter_adjacent");
01580         for (i = adj_filt.begin(), i_end = adj_filt.end(); i != i_end; ++i) {
01581             int match_count=0;
01582             static std::vector<map_location::DIRECTION> default_dirs
01583                 = map_location::parse_directions("n,ne,se,s,sw,nw");
01584             config::attribute_value i_adjacent = (*i)["adjacent"];
01585             std::vector<map_location::DIRECTION> dirs = !i_adjacent.blank() ?
01586                 map_location::parse_directions(i_adjacent) : default_dirs;
01587             std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
01588             for (j = dirs.begin(); j != j_end; ++j) {
01589                 unit_map::const_iterator unit_itor = units.find(adjacent[*j]);
01590                 if (unit_itor == units.end()
01591                 || !unit_itor->matches_filter(*i, unit_itor->get_location(), use_flat_tod)) {
01592                     continue;
01593                 }
01594                 config::attribute_value i_is_enemy = (*i)["is_enemy"];
01595                 if (i_is_enemy.blank() || i_is_enemy.to_bool() ==
01596                     teams_manager::get_teams()[this->side() - 1].is_enemy(unit_itor->side())) {
01597                     ++match_count;
01598                 }
01599             }
01600             static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6");
01601             config::attribute_value i_count = (*i)["count"];
01602             std::vector<std::pair<int,int> > counts = !i_count.blank()
01603                 ? utils::parse_ranges(i_count) : default_counts;
01604             if(!in_ranges(match_count, counts)) {
01605                 return false;
01606             }
01607         }
01608     }
01609 
01610     config::attribute_value cfg_find_in = cfg["find_in"];
01611     if (!cfg_find_in.blank()) {
01612         // Allow filtering by searching a stored variable of units
01613         variable_info vi(cfg_find_in, false, variable_info::TYPE_CONTAINER);
01614         if(!vi.is_valid) return false;
01615         if(vi.explicit_index) {
01616             config::const_child_iterator i = vi.vars->child_range(vi.key).first;
01617             std::advance(i, vi.index);
01618             if ((*i)["id"] != id_) {
01619                 return false;
01620             }
01621         } else {
01622             if (!vi.vars->find_child(vi.key, "id", id_))
01623                 return false;
01624         }
01625     }
01626     config::attribute_value cfg_formula = cfg["formula"];
01627     if (!cfg_formula.blank()) {
01628         const unit_callable callable(std::pair<map_location, unit>(loc,*this));
01629         const game_logic::formula form(cfg_formula);
01630         if(!form.evaluate(callable).as_bool()) {///@todo use formula_ai
01631             return false;
01632         }
01633     }
01634 
01635     config::attribute_value cfg_lua_function = cfg["lua_function"];
01636     if (!cfg_lua_function.blank()) {
01637         bool b = resources::lua_kernel->run_filter(cfg_lua_function.str().c_str(), *this);
01638         if (!b) return false;
01639     }
01640 
01641     return true;
01642 }
01643 
01644 void unit::write(config& cfg) const
01645 {
01646     cfg.append(cfg_);
01647     const unit_type *ut = unit_types.find(type_id());
01648     if (ut) {
01649         ut = &ut->get_gender_unit_type(gender_).get_variation(variation_);
01650     }
01651     if(ut && cfg["description"] == ut->unit_description()) {
01652         cfg.remove_attribute("description");
01653     }
01654 
01655     cfg["hitpoints"] = hit_points_;
01656     cfg["max_hitpoints"] = max_hit_points_;
01657 
01658     cfg["experience"] = experience_;
01659     cfg["max_experience"] = max_experience_;
01660 
01661     cfg["side"] = side_;
01662 
01663     cfg["type"] = type_id();
01664 
01665     //support for unit formulas in [ai] and unit-specific variables in [ai] [vars]
01666 
01667     if ( has_formula() || has_loop_formula() || (formula_vars_ && formula_vars_->empty() == false) ) {
01668 
01669         config &ai = cfg.add_child("ai");
01670 
01671         if (has_formula())
01672             ai["formula"] = unit_formula_;
01673 
01674         if (has_loop_formula())
01675             ai["loop_formula"] = unit_loop_formula_;
01676 
01677         if (has_priority_formula())
01678             ai["priority"] = unit_priority_formula_;
01679 
01680 
01681         if (formula_vars_ && formula_vars_->empty() == false)
01682         {
01683             config &ai_vars = ai.add_child("vars");
01684 
01685             std::string str;
01686             for(game_logic::map_formula_callable::const_iterator i = formula_vars_->begin(); i != formula_vars_->end(); ++i)
01687             {
01688                 i->second.serialize_to_string(str);
01689                 if (!str.empty())
01690                 {
01691                     ai_vars[i->first] = str;
01692                     str.clear();
01693                 }
01694             }
01695         }
01696     }
01697 
01698     cfg["gender"] = gender_string(gender_);
01699 
01700     cfg["variation"] = variation_;
01701 
01702     cfg["role"] = role_;
01703     cfg["flying"] = flying_;
01704 
01705     config status_flags;
01706     std::map<std::string,std::string> all_states = get_states();
01707     for(std::map<std::string,std::string>::const_iterator st = all_states.begin(); st != all_states.end(); ++st) {
01708         status_flags[st->first] = st->second;
01709     }
01710 
01711     cfg.clear_children("variables");
01712     cfg.add_child("variables",variables_);
01713     cfg.clear_children("events");
01714     cfg.append(events_);
01715     cfg.clear_children("filter_recall");
01716     cfg.add_child("filter_recall", filter_recall_);
01717     cfg.clear_children("status");
01718     cfg.add_child("status",status_flags);
01719 
01720     cfg["overlays"] = utils::join(overlays_);
01721 
01722     cfg["name"] = name_;
01723     cfg["id"] = id_;
01724     cfg["underlying_id"] = str_cast(underlying_id_);
01725 
01726     if(can_recruit())
01727         cfg["canrecruit"] = true;
01728 
01729     cfg["extra_recruit"] = utils::join(recruit_list_);
01730 
01731     cfg["facing"] = map_location::write_direction(facing_);
01732 
01733     cfg["goto_x"] = goto_.x + 1;
01734     cfg["goto_y"] = goto_.y + 1;
01735 
01736     cfg["moves"] = movement_;
01737     cfg["max_moves"] = max_movement_;
01738     cfg["vision"] = vision_;
01739     cfg["jamming"] = jamming_;
01740 
01741     cfg["resting"] = resting_;
01742 
01743     cfg["advances_to"] = utils::join(advances_to_);
01744 
01745     cfg["race"] = race_->id();
01746     cfg["language_name"] = type_name_;
01747     cfg["undead_variation"] = undead_variation_;
01748     cfg["variation"] = variation_;
01749     cfg["level"] = level_;
01750     switch(alignment_) {
01751         case unit_type::LAWFUL:
01752             cfg["alignment"] = "lawful";
01753             break;
01754         case unit_type::NEUTRAL:
01755             cfg["alignment"] = "neutral";
01756             break;
01757         case unit_type::CHAOTIC:
01758             cfg["alignment"] = "chaotic";
01759             break;
01760         case unit_type::LIMINAL:
01761             cfg["alignment"] = "liminal";
01762             break;
01763         default:
01764             cfg["alignment"] = "neutral";
01765     }
01766     cfg["flag_rgb"] = flag_rgb_;
01767     cfg["unrenamable"] = unrenamable_;
01768     cfg["alpha"] = str_cast(alpha_);
01769 
01770     cfg["attacks_left"] = attacks_left_;
01771     cfg["max_attacks"] = max_attacks_;
01772     cfg["zoc"] = emit_zoc_;
01773     cfg.clear_children("attack");
01774     for(std::vector<attack_type>::const_iterator i = attacks_.begin(); i != attacks_.end(); ++i) {
01775         cfg.add_child("attack",i->get_cfg());
01776     }
01777     cfg["cost"] = unit_value_;
01778     cfg.clear_children("modifications");
01779     cfg.add_child("modifications",modifications_);
01780 
01781 }
01782 
01783 void unit::add_formula_var(std::string str, variant var) {
01784     if(!formula_vars_) formula_vars_ = new game_logic::map_formula_callable;
01785     formula_vars_->add(str, var);
01786 }
01787 
01788 const surface unit::still_image(bool scaled) const
01789 {
01790     image::locator image_loc;
01791 
01792 #ifdef LOW_MEM
01793     image_loc = image::locator(absolute_image());
01794 #else
01795     std::string mods=image_mods();
01796     if(!mods.empty()){
01797         image_loc = image::locator(absolute_image(),mods);
01798     } else {
01799         image_loc = image::locator(absolute_image());
01800     }
01801 #endif
01802 
01803     surface unit_image(image::get_image(image_loc, scaled ? image::SCALED_TO_ZOOM : image::UNSCALED));
01804     return unit_image;
01805 }
01806 
01807 void unit::set_standing(bool with_bars)
01808 {
01809     display *disp = display::get_singleton();
01810     if (preferences::show_standing_animations()&& !incapacitated()) {
01811         start_animation(INT_MAX, choose_animation(*disp, loc_, "standing"),
01812             with_bars,  "", 0, STATE_STANDING);
01813     } else {
01814         start_animation(INT_MAX, choose_animation(*disp, loc_, "_disabled_"),
01815             with_bars,  "", 0, STATE_STANDING);
01816     }
01817 }
01818 
01819 void unit::set_ghosted(bool with_bars)
01820 {
01821     display *disp = display::get_singleton();
01822     start_animation(INT_MAX, choose_animation(*disp, loc_, "ghosted"),
01823             with_bars);
01824 }
01825 
01826 void unit::set_disabled_ghosted(bool with_bars)
01827 {
01828     display *disp = display::get_singleton();
01829     start_animation(INT_MAX, choose_animation(*disp, loc_, "disabled_ghosted"),
01830             with_bars);
01831 }
01832 
01833 void unit::set_idling()
01834 {
01835     display *disp = display::get_singleton();
01836     start_animation(INT_MAX, choose_animation(*disp, loc_, "idling"),
01837         true, "", 0, STATE_FORGET);
01838 }
01839 
01840 void unit::set_selecting()
01841 {
01842     const display *disp =  display::get_singleton();
01843     if (preferences::show_standing_animations() && !get_state(STATE_PETRIFIED)) {
01844         start_animation(INT_MAX, choose_animation(*disp, loc_, "selected"),
01845             true, "", 0, STATE_FORGET);
01846     } else {
01847         start_animation(INT_MAX, choose_animation(*disp, loc_, "_disabled_selected_"),
01848             true, "", 0, STATE_FORGET);
01849     }
01850 }
01851 
01852 void unit::start_animation(int start_time, const unit_animation *animation,
01853     bool with_bars,  const std::string &text, Uint32 text_color, STATE state)
01854 {
01855     const display * disp =  display::get_singleton();
01856     state_ = state;
01857     if (!animation) {
01858         if (state != STATE_STANDING)
01859             set_standing(with_bars);
01860         return ;
01861     }
01862     // everything except standing select and idle
01863     bool accelerate = (state != STATE_FORGET && state != STATE_STANDING);
01864     draw_bars_ =  with_bars;
01865     delete anim_;
01866     anim_ = new unit_animation(*animation);
01867     const int real_start_time = start_time == INT_MAX ? anim_->get_begin_time() : start_time;
01868     anim_->start_animation(real_start_time, loc_, loc_.get_direction(facing_),
01869          text, text_color, accelerate);
01870     frame_begin_time_ = anim_->get_begin_time() -1;
01871     if (disp->idle_anim()) {
01872         next_idling_ = get_current_animation_tick()
01873             + static_cast<int>((20000 + rand() % 20000) * disp->idle_anim_rate());
01874     } else {
01875         next_idling_ = INT_MAX;
01876     }
01877 }
01878 
01879 
01880 void unit::set_facing(map_location::DIRECTION dir) {
01881     if(dir != map_location::NDIRECTIONS) {
01882         facing_ = dir;
01883     }
01884     // Else look at yourself (not available so continue to face the same direction)
01885 }
01886 
01887 void unit::redraw_unit()
01888 {
01889     display &disp = *display::get_singleton();
01890     const gamemap &map = disp.get_map();
01891 
01892     if ( hidden_ || !is_visible_to_team(disp.get_teams()[disp.viewing_team()],disp.show_everything(),map) )
01893     {
01894         clear_haloes();
01895         if(anim_) {
01896             anim_->update_last_draw_time();
01897         }
01898         return;
01899     }
01900 
01901     if (!anim_) {
01902         set_standing();
01903         if (!anim_) return;
01904     }
01905 
01906     if (refreshing_) return;
01907     refreshing_ = true;
01908 
01909     anim_->update_last_draw_time();
01910     frame_parameters params;
01911     const t_translation::t_terrain terrain = map.get_terrain(loc_);
01912     const terrain_type& terrain_info = map.get_terrain_info(terrain);
01913     // do not set to 0 so we can distinguish the flying from the "not on submerge terrain"
01914     // instead use -1.0 (as in "negative depth", it will be ignored by rendering)
01915     params.submerge= is_flying() ? -1.0 : terrain_info.unit_submerge();
01916 
01917     if (invisible(loc_) &&
01918             params.highlight_ratio > 0.5) {
01919         params.highlight_ratio = 0.5;
01920     }
01921     if (loc_ == disp.selected_hex() && params.highlight_ratio == 1.0) {
01922         params.highlight_ratio = 1.5;
01923     }
01924     int height_adjust = static_cast<int>(terrain_info.unit_height_adjust() * disp.get_zoom_factor());
01925     if (is_flying() && height_adjust < 0) {
01926         height_adjust = 0;
01927     }
01928     params.y -= height_adjust;
01929     params.halo_y -= height_adjust;
01930 
01931     int red = 0,green = 0,blue = 0,tints = 0;
01932     double blend_ratio = 0;
01933     // Add future colored states here
01934     if(get_state(STATE_POISONED)) {
01935         green += 255;
01936         blend_ratio += 0.25;
01937         tints += 1;
01938     }
01939     if(get_state(STATE_SLOWED)) {
01940         red += 191;
01941         green += 191;
01942         blue += 255;
01943         blend_ratio += 0.25;
01944         tints += 1;
01945     }
01946     if(tints > 0) {
01947         params.blend_with = disp.rgb((red/tints),(green/tints),(blue/tints));
01948         params.blend_ratio = ((blend_ratio/tints));
01949     }
01950 
01951     //hackish : see unit_frame::merge_parameters
01952     // we use image_mod on the primary image
01953     // and halo_mod on secondary images and all haloes
01954     params.image_mod = image_mods();
01955     params.halo_mod = TC_image_mods();
01956     params.image= absolute_image();
01957 
01958 
01959     if(get_state(STATE_PETRIFIED)) params.image_mod +="~GS()";
01960     params.primary_frame = t_true;
01961 
01962     const frame_parameters adjusted_params = anim_->get_current_params(params);
01963 
01964 
01965 
01966     const map_location dst = loc_.get_direction(facing_);
01967     const int xsrc = disp.get_location_x(loc_);
01968     const int ysrc = disp.get_location_y(loc_);
01969     const int xdst = disp.get_location_x(dst);
01970     const int ydst = disp.get_location_y(dst);
01971     int d2 = disp.hex_size() / 2;
01972 
01973 
01974 
01975 
01976     const int x = static_cast<int>(adjusted_params.offset * xdst + (1.0-adjusted_params.offset) * xsrc) + d2;
01977     const int y = static_cast<int>(adjusted_params.offset * ydst + (1.0-adjusted_params.offset) * ysrc) + d2;
01978 
01979 
01980     if(unit_halo_ == halo::NO_HALO && !image_halo().empty()) {
01981         unit_halo_ = halo::add(0, 0, image_halo()+TC_image_mods(), map_location(-1, -1));
01982     }
01983     if(unit_halo_ != halo::NO_HALO && image_halo().empty()) {
01984         halo::remove(unit_halo_);
01985         unit_halo_ = halo::NO_HALO;
01986     } else if(unit_halo_ != halo::NO_HALO) {
01987         halo::set_location(unit_halo_, x, y - height_adjust);
01988     }
01989 
01990 
01991 
01992     // We draw bars only if wanted, visible on the map view
01993     bool draw_bars = draw_bars_ ;
01994     if (draw_bars) {
01995         const int d = disp.hex_size();
01996         SDL_Rect unit_rect = create_rect(xsrc, ysrc +adjusted_params.y, d, d);
01997         draw_bars = rects_overlap(unit_rect, disp.map_outside_area());
01998     }
01999 
02000     surface ellipse_front(NULL);
02001     surface ellipse_back(NULL);
02002     int ellipse_floating = 0;
02003     if(draw_bars && preferences::show_side_colors()) {
02004         if(adjusted_params.submerge > 0.0) {
02005             // The division by 2 seems to have no real meaning,
02006             // It just works fine with the current center of ellipse
02007             // and prevent a too large adjust if submerge = 1.0
02008             ellipse_floating = static_cast<int>(adjusted_params.submerge * disp.hex_size() / 2);
02009         }
02010 
02011         std::string ellipse=image_ellipse();
02012         if(ellipse.empty()){
02013             ellipse="misc/ellipse";
02014         }
02015 
02016         const char* const selected = disp.selected_hex() == loc_ ? "selected-" : "";
02017 
02018         // Load the ellipse parts recolored to match team color
02019         char buf[100];
02020         std::string tc=team::get_side_color_index(side_);
02021 
02022         snprintf(buf,sizeof(buf),"%s-%stop.png~RC(ellipse_red>%s)",ellipse.c_str(),selected,tc.c_str());
02023         ellipse_back.assign(image::get_image(image::locator(buf), image::SCALED_TO_ZOOM));
02024         snprintf(buf,sizeof(buf),"%s-%sbottom.png~RC(ellipse_red>%s)",ellipse.c_str(),selected,tc.c_str());
02025         ellipse_front.assign(image::get_image(image::locator(buf), image::SCALED_TO_ZOOM));
02026     }
02027 
02028     if (ellipse_back != NULL) {
02029         //disp.drawing_buffer_add(display::LAYER_UNIT_BG, loc,
02030         disp.drawing_buffer_add(display::LAYER_UNIT_FIRST, loc_,
02031             xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_back);
02032     }
02033 
02034     if (ellipse_front != NULL) {
02035         //disp.drawing_buffer_add(display::LAYER_UNIT_FG, loc,
02036         disp.drawing_buffer_add(display::LAYER_UNIT_FIRST, loc_,
02037             xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_front);
02038     }
02039     if(draw_bars) {
02040         const image::locator* orb_img = NULL;
02041         static const image::locator enemy_orb(game_config::images::enemy_orb);
02042         static const image::locator ally_orb(game_config::images::ally_orb);
02043         static const image::locator moved_orb(game_config::images::moved_orb);
02044         static const image::locator unmoved_orb(game_config::images::unmoved_orb);
02045         static const image::locator partmoved_orb(game_config::images::partmoved_orb);
02046 
02047         const std::string* energy_file = &game_config::images::energy;
02048 
02049         if(size_t(side()) != disp.viewing_team()+1) {
02050             if(disp.team_valid() &&
02051                disp.get_teams()[disp.viewing_team()].is_enemy(side())) {
02052                 orb_img = &enemy_orb;
02053             } else {
02054                 orb_img = &ally_orb;
02055             }
02056         } else {
02057             orb_img = &moved_orb;
02058             if(disp.playing_team() == disp.viewing_team() && !user_end_turn()) {
02059                 if (movement_left() == total_movement()) {
02060                     orb_img = &unmoved_orb;
02061                 } else if (unit_can_move(*this)) {
02062                     orb_img = &partmoved_orb;
02063                 }
02064             }
02065         }
02066 
02067         assert(orb_img != NULL);
02068         surface orb(image::get_image(*orb_img,image::SCALED_TO_ZOOM));
02069         if (orb != NULL) {
02070             disp.drawing_buffer_add(display::LAYER_UNIT_BAR,
02071                 loc_, xsrc, ysrc +adjusted_params.y, orb);
02072         }
02073 
02074         double unit_energy = 0.0;
02075         if(max_hitpoints() > 0) {
02076             unit_energy = double(hitpoints())/double(max_hitpoints());
02077         }
02078         const int bar_shift = static_cast<int>(-5*disp.get_zoom_factor());
02079         const int hp_bar_height = static_cast<int>(max_hitpoints()*game_config::hp_bar_scaling);
02080 
02081         const fixed_t bar_alpha = (loc_ == disp.mouseover_hex() || loc_ == disp.selected_hex()) ? ftofxp(1.0): ftofxp(0.8);
02082 
02083         disp.draw_bar(*energy_file, xsrc+bar_shift, ysrc +adjusted_params.y,
02084             loc_, hp_bar_height, unit_energy,hp_color(), bar_alpha);
02085 
02086         if(experience() > 0 && can_advance()) {
02087             const double filled = double(experience())/double(max_experience());
02088 
02089             const int xp_bar_height = static_cast<int>(max_experience()*game_config::xp_bar_scaling / std::max<int>(level_,1));
02090 
02091             SDL_Color color=xp_color();
02092             disp.draw_bar(*energy_file, xsrc, ysrc +adjusted_params.y,
02093                 loc_, xp_bar_height, filled, color, bar_alpha);
02094         }
02095 
02096         if (can_recruit()) {
02097             surface crown(image::get_image("misc/leader-crown.png",image::SCALED_TO_ZOOM));
02098             if(!crown.null()) {
02099                 //if(bar_alpha != ftofxp(1.0)) {
02100                 //  crown = adjust_surface_alpha(crown, bar_alpha);
02101                 //}
02102                 disp.drawing_buffer_add(display::LAYER_UNIT_BAR,
02103                     loc_, xsrc, ysrc +adjusted_params.y, crown);
02104             }
02105         }
02106 
02107         for(std::vector<std::string>::const_iterator ov = overlays().begin(); ov != overlays().end(); ++ov) {
02108             const surface ov_img(image::get_image(*ov, image::SCALED_TO_ZOOM));
02109             if(ov_img != NULL) {
02110                 disp.drawing_buffer_add(display::LAYER_UNIT_BAR,
02111                     loc_, xsrc, ysrc +adjusted_params.y, ov_img);
02112             }
02113         }
02114     }
02115 
02116     anim_->redraw(params);
02117     refreshing_ = false;
02118 }
02119 
02120 void unit::clear_haloes()
02121 {
02122     if(unit_halo_ != halo::NO_HALO) {
02123         halo::remove(unit_halo_);
02124         unit_halo_ = halo::NO_HALO;
02125     }
02126     if(anim_ ) anim_->clear_haloes();
02127 }
02128 bool unit::invalidate(const map_location &loc)
02129 {
02130     bool result = false;
02131 
02132     // Very early calls, anim not initialized yet
02133     if(get_animation()) {
02134         frame_parameters params;
02135         const display * disp =  display::get_singleton();
02136         const gamemap & map = disp->get_map();
02137         const t_translation::t_terrain terrain = map.get_terrain(loc);
02138         const terrain_type& terrain_info = map.get_terrain_info(terrain);
02139 
02140         int height_adjust = static_cast<int>(terrain_info.unit_height_adjust() * disp->get_zoom_factor());
02141         if (is_flying() && height_adjust < 0) {
02142             height_adjust = 0;
02143         }
02144         params.y -= height_adjust;
02145         params.halo_y -= height_adjust;
02146         params.image_mod = image_mods();
02147 
02148         result |= get_animation()->invalidate(params);
02149     }
02150 
02151     return result;
02152 
02153 }
02154 
02155 int unit::upkeep() const
02156 {
02157     // Leaders do not incur upkeep.
02158     if(can_recruit()) {
02159         return 0;
02160     }
02161     if(cfg_["upkeep"] == "full") {
02162         return level();
02163     }
02164     if(cfg_["upkeep"] == "loyal") {
02165         return 0;
02166     }
02167     if(cfg_["upkeep"] == "free") {
02168         return 0;
02169     }
02170     return cfg_["upkeep"];
02171 }
02172 
02173 bool unit::loyal() const
02174 {
02175     return cfg_["upkeep"] == "loyal" || cfg_["upkeep"] == "free";
02176 }
02177 
02178 int unit::movement_cost(const t_translation::t_terrain terrain) const
02179 {
02180     assert(resources::game_map != NULL);
02181     const int res = movement_cost_internal(movement_costs_,
02182             cfg_.child("movement_costs"), NULL, *resources::game_map, terrain);
02183 
02184     if (res == unit_movement_type::UNREACHABLE) {
02185         return res;
02186     } else if(get_state(STATE_SLOWED)) {
02187         return res*2;
02188     }
02189     return res;
02190 }
02191 
02192 int unit::vision_cost(const t_translation::t_terrain terrain) const
02193 {
02194     if (cfg_.child_count("vision_costs") == 0) return movement_cost(terrain);
02195 
02196     assert(resources::game_map != NULL);
02197     const int res = movement_cost_internal(vision_costs_,
02198             cfg_.child("vision_costs"), NULL, *resources::game_map, terrain);
02199 
02200     if (res == unit_movement_type::UNREACHABLE) {
02201         return res;
02202     } else if(get_state(STATE_SLOWED)) {
02203         return res*2;
02204     }
02205     return res;
02206 }
02207 
02208 int unit::jamming_cost(const t_translation::t_terrain terrain) const
02209 {
02210 //  if (cfg_.child_count("jamming_costs") == 0) return movement_cost(terrain);
02211 
02212     assert(resources::game_map != NULL);
02213     const int res = movement_cost_internal(jamming_costs_,
02214             cfg_.child("jamming_costs"), NULL, *resources::game_map, terrain);
02215 
02216     if (res == unit_movement_type::UNREACHABLE) {
02217         return res;
02218     } else if(get_state(STATE_SLOWED)) {
02219         return res*2;
02220     }
02221     return res;
02222 }
02223 
02224 int unit::defense_modifier(t_translation::t_terrain terrain) const
02225 {
02226     assert(resources::game_map != NULL);
02227     int def = defense_modifier_internal(defense_mods_, cfg_, NULL, *resources::game_map, terrain);
02228 #if 0
02229     // A [defense] ability is too costly and doesn't take into account target locations.
02230     // Left as a comment in case someone ever wonders why it isn't a good idea.
02231     unit_ability_list defense_abilities = get_abilities("defense");
02232     if (!defense_abilities.empty()) {
02233         unit_abilities::effect defense_effect(defense_abilities, def, false);
02234         def = defense_effect.get_composite_value();
02235     }
02236 #endif
02237     return def;
02238 }
02239 
02240 bool unit::resistance_filter_matches(const config& cfg, bool attacker, const std::string& damage_name, int res) const
02241 {
02242     if(!(cfg["active_on"]=="" || (attacker && cfg["active_on"]=="offense") || (!attacker && cfg["active_on"]=="defense"))) {
02243         return false;
02244     }
02245     const std::string& apply_to = cfg["apply_to"];
02246     if(!apply_to.empty()) {
02247         if(damage_name != apply_to) {
02248             if(std::find(apply_to.begin(),apply_to.end(),',') != apply_to.end() &&
02249                 std::search(apply_to.begin(),apply_to.end(),
02250                 damage_name.begin(),damage_name.end()) != apply_to.end()) {
02251                 const std::vector<std::string>& vals = utils::split(apply_to);
02252                 if(std::find(vals.begin(),vals.end(),damage_name) == vals.end()) {
02253                     return false;
02254                 }
02255             } else {
02256                 return false;
02257             }
02258         }
02259     }
02260     if (!unit_abilities::filter_base_matches(cfg, res)) return false;
02261     return true;
02262 }
02263 
02264 
02265 int unit::resistance_against(const std::string& damage_name,bool attacker,const map_location& loc) const
02266 {
02267     int res = 0;
02268 
02269     if (const config &resistance = cfg_.child("resistance")) {
02270         res = 100 - resistance[damage_name].to_int(100);
02271     }
02272 
02273     unit_ability_list resistance_abilities = get_abilities("resistance",loc);
02274     for (std::vector<std::pair<const config *,map_location> >::iterator i = resistance_abilities.cfgs.begin(); i != resistance_abilities.cfgs.end();) {
02275         if(!resistance_filter_matches(*i->first, attacker, damage_name, res)) {
02276             i = resistance_abilities.cfgs.erase(i);
02277         } else {
02278             ++i;
02279         }
02280     }
02281     if(!resistance_abilities.empty()) {
02282         unit_abilities::effect resist_effect(resistance_abilities,res,false);
02283 
02284         res = std::min<int>(resist_effect.get_composite_value(),resistance_abilities.highest("max_value").first);
02285     }
02286     return 100 - res;
02287 }
02288 
02289 utils::string_map unit::get_base_resistances() const
02290 {
02291     if (const config &resistance = cfg_.child("resistance"))
02292     {
02293         utils::string_map res;
02294         foreach (const config::attribute &i, resistance.attribute_range()) {
02295             res[i.first] = i.second;
02296         }
02297         return res;
02298     }
02299     return utils::string_map();
02300 }
02301 
02302 #if 0
02303 std::map<terrain_type::TERRAIN,int> unit::movement_type() const
02304 {
02305     return movement_costs_;
02306 }
02307 #endif
02308 
02309 std::map<std::string,std::string> unit::advancement_icons() const
02310 {
02311     std::map<std::string,std::string> temp;
02312     if (!can_advance())
02313         return temp;
02314 
02315     if (!advances_to_.empty())
02316     {
02317         std::ostringstream tooltip;
02318         const std::string &image = game_config::images::level;
02319         foreach (const std::string &s, advances_to())
02320         {
02321             if (!s.empty())
02322                 tooltip << s << '\n';
02323         }
02324         temp[image] = tooltip.str();
02325     }
02326 
02327     foreach (const config &adv, get_modification_advances())
02328     {
02329         const std::string &image = adv["image"];
02330         if (image.empty()) continue;
02331         std::ostringstream tooltip;
02332         tooltip << temp[image];
02333         const std::string &tt = adv["description"];
02334         if (!tt.empty())
02335             tooltip << tt << '\n';
02336         temp[image] = tooltip.str();
02337     }
02338     return(temp);
02339 }
02340 std::vector<std::pair<std::string,std::string> > unit::amla_icons() const
02341 {
02342     std::vector<std::pair<std::string,std::string> > temp;
02343     std::pair<std::string,std::string> icon; //<image,tooltip>
02344 
02345     foreach (const config &adv, get_modification_advances())
02346     {
02347         icon.first = adv["icon"].str();
02348         icon.second = adv["description"].str();
02349 
02350         for (unsigned j = 0, j_count = modification_count("advance", adv["id"]);
02351              j < j_count; ++j)
02352         {
02353             temp.push_back(icon);
02354         }
02355     }
02356     return(temp);
02357 }
02358 
02359 std::vector<config> unit::get_modification_advances() const
02360 {
02361     std::vector<config> res;
02362     foreach (const config &adv, modification_advancements())
02363     {
02364         if (adv["strict_amla"].to_bool() && !advances_to_.empty())
02365             continue;
02366         if (modification_count("advance", adv["id"]) >= unsigned(adv["max_times"].to_int(1)))
02367             continue;
02368 
02369         std::vector<std::string> temp = utils::split(adv["require_amla"]);
02370         if (temp.empty()) {
02371             res.push_back(adv);
02372             continue;
02373         }
02374 
02375         std::sort(temp.begin(), temp.end());
02376         std::vector<std::string> uniq;
02377         std::unique_copy(temp.begin(), temp.end(), std::back_inserter(uniq));
02378 
02379         bool requirements_done = true;
02380         foreach (const std::string &s, uniq)
02381         {
02382             int required_num = std::count(temp.begin(), temp.end(), s);
02383             int mod_num = modification_count("advance", s);
02384             if (required_num > mod_num) {
02385                 requirements_done = false;
02386                 break;
02387             }
02388         }
02389         if (requirements_done)
02390             res.push_back(adv);
02391     }
02392 
02393     return res;
02394 }
02395 
02396 size_t unit::modification_count(const std::string& type, const std::string& id) const
02397 {
02398     size_t res = 0;
02399     foreach (const config &item, modifications_.child_range(type)) {
02400         if (item["id"] == id) {
02401             ++res;
02402         }
02403     }
02404 
02405     return res;
02406 }
02407 
02408 /** Helper function for add_modifications */
02409 static void mod_mdr_merge(config& dst, const config& mod, bool delta)
02410 {
02411     foreach (const config::attribute &i, mod.attribute_range()) {
02412         int v = 0;
02413         if (delta) v = dst[i.first];
02414         dst[i.first] = v + i.second.to_int();
02415     }
02416 }
02417 
02418 void unit::add_modification(const std::string& type, const config& mod, bool no_add)
02419 {
02420     //some trait activate specific flags
02421     if(type == "trait") {
02422         const std::string& id = mod["id"];
02423         is_fearless_ = is_fearless_ || id == "fearless";
02424         is_healthy_ = is_healthy_ || id == "healthy";
02425     }
02426 
02427     config *new_child = NULL;
02428     if(no_add == false) {
02429         new_child = &modifications_.add_child(type,mod);
02430     }
02431     config last_effect;
02432     std::vector<t_string> effects_description;
02433     foreach (const config &effect, mod.child_range("effect"))
02434     {
02435         // Apply SUF.
02436         if (const config &afilter = effect.child("filter"))
02437             if (!matches_filter(vconfig(afilter), loc_)) continue;
02438 
02439         const std::string &apply_to = effect["apply_to"];
02440         const std::string &apply_times = effect["times"];
02441         int times = 1;
02442         t_string description;
02443 
02444         if (apply_times == "per level")
02445             times = level_;
02446         if (times) {
02447             while (times > 0) {
02448                 times --;
02449 
02450                 // Apply unit type/variation changes last to avoid double applying effects on advance.
02451                 if ((apply_to == "variation" || apply_to == "type") && no_add == false) {
02452                     last_effect = effect;
02453                 } else if(apply_to == "profile") {
02454                     if (const config::attribute_value *v = effect.get("portrait")) {
02455                         std::string big = *v, small = effect["small_portrait"];
02456                         adjust_profile(small, big, "");
02457                         cfg_["profile"] = big;
02458                         cfg_["small_profile"] = small;
02459                     }
02460                     if (const config::attribute_value *v = effect.get("description"))
02461                         cfg_["description"] = *v;
02462                     //help::unit_topic_generator(*this, (**i.first)["help_topic"]);
02463                 } else if(apply_to == "new_attack") {
02464                     attacks_.push_back(attack_type(effect));
02465                 } else if(apply_to == "remove_attacks") {
02466                     std::vector<attack_type>::iterator a = attacks_.begin();
02467                     while(a != attacks_.end()) {
02468                         if(a->matches_filter(effect, false)) {
02469                             a = attacks_.erase(a);
02470                             continue;
02471                         }
02472                         ++a;
02473                     }
02474                 } else if(apply_to == "attack") {
02475 
02476                     bool first_attack = true;
02477 
02478                     std::string attack_names;
02479                     std::string desc;
02480                     for(std::vector<attack_type>::iterator a = attacks_.begin();
02481                         a != attacks_.end(); ++a) {
02482                         bool affected = a->apply_modification(effect, &desc);
02483                         if(affected && desc != "") {
02484                             if(first_attack) {
02485                                 first_attack = false;
02486                             } else {
02487                                 if (!times)
02488                                     attack_names += t_string(N_(" and "), "wesnoth");
02489                             }
02490 
02491                             if (!times)
02492                                 attack_names += t_string(a->name(), "wesnoth");
02493                         }
02494                     }
02495                     if (attack_names.empty() == false) {
02496                         utils::string_map symbols;
02497                         symbols["attack_list"] = attack_names;
02498                         symbols["effect_description"] = desc;
02499                         description += vgettext("$attack_list|: $effect_description", symbols);
02500                     }
02501                 } else if(apply_to == "hitpoints") {
02502                     LOG_UT << "applying hitpoint mod..." << hit_points_ << "/" << max_hit_points_ << "\n";
02503                     const std::string &increase_hp = effect["increase"];
02504                     const std::string &increase_total = effect["increase_total"];
02505                     const std::string &set_hp = effect["set"];
02506                     const std::string &set_total = effect["set_total"];
02507 
02508                     // If the hitpoints are allowed to end up greater than max hitpoints
02509                     const bool violate_max = effect["violate_maximum"].to_bool();
02510 
02511                     if(set_hp.empty() == false) {
02512                         if(set_hp[set_hp.size()-1] == '%') {
02513                             hit_points_ = lexical_cast_default<int>(set_hp)*max_hit_points_/100;
02514                         } else {
02515                             hit_points_ = lexical_cast_default<int>(set_hp);
02516                         }
02517                     }
02518                     if(set_total.empty() == false) {
02519                         if(set_total[set_total.size()-1] == '%') {
02520                             max_hit_points_ = lexical_cast_default<int>(set_total)*max_hit_points_/100;
02521                         } else {
02522                             max_hit_points_ = lexical_cast_default<int>(set_total);
02523                         }
02524                     }
02525 
02526                     if(increase_total.empty() == false) {
02527                         if (!times)
02528                             description += utils::print_modifier(increase_total) + " " +
02529                                 t_string(N_("HP"), "wesnoth");
02530 
02531                         // A percentage on the end means increase by that many percent
02532                         max_hit_points_ = utils::apply_modifier(max_hit_points_, increase_total);
02533                     }
02534 
02535                     if(max_hit_points_ < 1)
02536                         max_hit_points_ = 1;
02537 
02538                     if (effect["heal_full"].to_bool()) {
02539                         heal_all();
02540                     }
02541 
02542                     if(increase_hp.empty() == false) {
02543                         hit_points_ = utils::apply_modifier(hit_points_, increase_hp);
02544                     }
02545 
02546                     LOG_UT << "modded to " << hit_points_ << "/" << max_hit_points_ << "\n";
02547                     if(hit_points_ > max_hit_points_ && !violate_max) {
02548                         LOG_UT << "resetting hp to max\n";
02549                         hit_points_ = max_hit_points_;
02550                     }
02551 
02552                     if(hit_points_ < 1)
02553                         hit_points_ = 1;
02554                 } else if(apply_to == "movement") {
02555                     const std::string &increase = effect["increase"];
02556 
02557                     if(increase.empty() == false) {
02558                         if (!times)
02559                             description += utils::print_modifier(increase) + " " +
02560                                 t_string(N_("moves"), "wesnoth");
02561 
02562                         max_movement_ = utils::apply_modifier(max_movement_, increase, 1);
02563                     }
02564 
02565                     max_movement_ = effect["set"].to_int(max_movement_);
02566 
02567                     if(movement_ > max_movement_)
02568                         movement_ = max_movement_;
02569                 } else if(apply_to == "max_experience") {
02570                     const std::string &increase = effect["increase"];
02571 
02572                     if(increase.empty() == false) {
02573                         if (!times)
02574                             description += utils::print_modifier(increase) + " " +
02575                                 t_string(N_("XP to advance"), "wesnoth");
02576 
02577                         max_experience_ = utils::apply_modifier(max_experience_, increase, 1);
02578                     }
02579 
02580                 } else if(apply_to == "loyal") {
02581                     cfg_["upkeep"] = "loyal";
02582                 } else if(apply_to == "status") {
02583                     const std::string &add = effect["add"];
02584                     const std::string &remove = effect["remove"];
02585 
02586                     if(add.empty() == false) {
02587                         set_state(add, true);
02588                     }
02589 
02590                     if(remove.empty() == false) {
02591                         set_state(remove, false);
02592                     }
02593                 } else if (apply_to == "movement_costs") {
02594                     config &mv = cfg_.child_or_add("movement_costs");
02595                     if (const config &ap = effect.child("movement_costs")) {
02596                         mod_mdr_merge(mv, ap, !effect["replace"].to_bool());
02597                     }
02598                     movement_costs_.clear();
02599                 } else if (apply_to == "vision_costs") {
02600                     config &vi = cfg_.child_or_add("vision_costs");
02601                     if (const config &ap = effect.child("vision_costs")) {
02602                         mod_mdr_merge(vi, ap, !effect["replace"].to_bool());
02603                     }
02604                     vision_costs_.clear();
02605                 } else if (apply_to == "jamming_costs") {
02606                     config &jm = cfg_.child_or_add("jamming_costs");
02607                     if (const config &ap = effect.child("jamming_costs")) {
02608                         mod_mdr_merge(jm, ap, !effect["replace"].to_bool());
02609                     }
02610                     jamming_costs_.clear();
02611                 } else if (apply_to == "defense") {
02612                     config &def = cfg_.child_or_add("defense");
02613                     if (const config &ap = effect.child("defense")) {
02614                         bool replace = effect["replace"].to_bool();
02615                         foreach (const config::attribute &i, ap.attribute_range()) {
02616                             int v = i.second.to_int();
02617                             config::attribute_value &dst = def[i.first];
02618                             if (!replace) {
02619                                 int w = dst.to_int(100);
02620                                 v += w;
02621                                 if ((w >= 0 && v < 0) || (w < 0 && v > 0)) v = 0;
02622                                 else if (v < -100) v = -100;
02623                                 else if (v > 100) v = 100;
02624                             }
02625                             dst = v;
02626                         }
02627                     }
02628                     defense_mods_.clear();
02629                 } else if (apply_to == "resistance") {
02630                     config &mv = cfg_.child_or_add("resistance");
02631                     if (const config &ap = effect.child("resistance")) {
02632                         mod_mdr_merge(mv, ap, !effect["replace"].to_bool());
02633                     }
02634                 } else if (apply_to == "zoc") {
02635                     if (const config::attribute_value *v = effect.get("value")) {
02636                         emit_zoc_ = v->to_bool();
02637                     }
02638                 } else if (apply_to == "new_ability") {
02639                     config &ab = cfg_.child_or_add("abilities");
02640                     if (const config &ab_effect = effect.child("abilities")) {
02641                         config to_append;
02642                         foreach (const config::any_child &ab, ab_effect.all_children_range()) {
02643                             if(!has_ability_by_id(ab.cfg["id"])) {
02644                                 to_append.add_child(ab.key, ab.cfg);
02645                             }
02646                         }
02647                         ab.append(to_append);
02648                     }
02649                 } else if (apply_to == "remove_ability") {
02650                     if (const config &ab_effect = effect.child("abilities")) {
02651                         foreach (const config::any_child &ab, ab_effect.all_children_range()) {
02652                             remove_ability_by_id(ab.cfg["id"]);
02653                         }
02654                     }
02655                 } else if (apply_to == "image_mod") {
02656                     LOG_UT << "applying image_mod \n";
02657                     std::string mod = effect["replace"];
02658                     if (!mod.empty()){
02659                         image_mods_ = mod;
02660                     }
02661                     LOG_UT << "applying image_mod \n";
02662                     mod = effect["add"].str();
02663                     if (!mod.empty()){
02664                         if(!image_mods_.empty()) {
02665                             image_mods_ += '~';
02666                         }
02667 
02668                         image_mods_ += mod;
02669                     }
02670 
02671                     game_config::add_color_info(effect);
02672                     LOG_UT << "applying image_mod \n";
02673                 } else if (apply_to == "new_animation") {
02674                     if(effect["id"].empty()) {
02675                         unit_animation::add_anims(animations_, effect);
02676                     } else {
02677                         std::vector<unit_animation> &built = resources::controller->animation_cache[effect["id"]];
02678                         if(built.empty()) {
02679                             unit_animation::add_anims(built, effect);
02680                         }
02681                         animations_.insert(animations_.end(),built.begin(),built.end());
02682                     }
02683 
02684                 } else if (apply_to == "ellipse") {
02685                     cfg_["ellipse"] = effect["ellipse"];
02686                 }
02687             } // end while
02688         } else { // for times = per level & level = 0 we still need to rebuild the descriptions
02689             if(apply_to == "attack") {
02690 
02691                 bool first_attack = true;
02692 
02693                 for(std::vector<attack_type>::iterator a = attacks_.begin();
02694                     a != attacks_.end(); ++a) {
02695                     std::string desc;
02696                     bool affected = a->describe_modification(effect, &desc);
02697                     if(affected && desc != "") {
02698                         if(first_attack) {
02699                             first_attack = false;
02700                         } else {
02701                             description += t_string(N_(" and "), "wesnoth");
02702                         }
02703 
02704                         description += t_string(a->name(), "wesnoth") + ": " + desc;
02705                     }
02706                 }
02707             } else if(apply_to == "hitpoints") {
02708                 const std::string &increase_total = effect["increase_total"];
02709 
02710                 if(increase_total.empty() == false) {
02711                     description += utils::print_modifier(increase_total) + " " +
02712                         t_string(N_("HP"), "wesnoth");
02713                 }
02714             } else if(apply_to == "movement") {
02715                 const std::string &increase = effect["increase"];
02716 
02717                 if(increase.empty() == false) {
02718                     description += utils::print_modifier(increase) + t_string(N_(" move"), "wesnoth");
02719                 }
02720             } else if(apply_to == "max_experience") {
02721                 const std::string &increase = effect["increase"];
02722 
02723                 if(increase.empty() == false) {
02724                     description += utils::print_modifier(increase) + " " +
02725                         t_string(N_("XP to advance"), "wesnoth");
02726                 }
02727             }
02728         }
02729 
02730         if (apply_times == "per level" && !times) {
02731             utils::string_map symbols;
02732             symbols["effect_description"] = description;
02733             description = vgettext("$effect_description per level", symbols);
02734         }
02735         if(!description.empty())
02736             effects_description.push_back(description);
02737 
02738     }
02739     // Apply variations -- only apply if we are adding this for the first time.
02740     if (!last_effect.empty() && no_add == false) {
02741         if ((last_effect)["apply_to"] == "variation") {
02742             variation_ = last_effect["name"].str();
02743             advance_to(this->type());
02744         } else if ((last_effect)["apply_to"] == "type") {
02745             config::attribute_value &prev_type = (*new_child)["prev_type"];
02746             if (prev_type.blank()) prev_type = type_id();
02747             const std::string& type_id = last_effect["name"];
02748             const unit_type* type = unit_types.find(type_id);
02749             if(type) {
02750                 const bool heal_full = last_effect["heal_full"].to_bool(false);
02751                 int hit_points = hit_points_;
02752                 advance_to(type);
02753                 preferences::encountered_units().insert(type_id);
02754                 if(!heal_full) {
02755                     hit_points_ = hit_points;
02756                 }
02757             } else {
02758                 WRN_UT << "unknown type= in [effect]apply_to=type, ignoring\n";
02759             }
02760         }
02761     }
02762 
02763     t_string description;
02764 
02765     const t_string& mod_description = mod["description"];
02766     if (!mod_description.empty()) {
02767         description = mod_description + " ";
02768     }
02769 
02770     // Punctuation should be translatable: not all languages use Latin punctuation.
02771     // (However, there maybe is a better way to do it)
02772     if(effects_description.empty() == false) {
02773         for(std::vector<t_string>::const_iterator i = effects_description.begin();
02774                 i != effects_description.end(); ++i) {
02775             description += *i;
02776             if(i+1 != effects_description.end())
02777                 description += t_string(N_(" and "), "wesnoth");
02778         }
02779     }
02780 
02781     // store trait info
02782     if(type == "trait") {
02783         add_trait_description(mod, description);
02784     }
02785 
02786     //NOTE: if not a trait, description is currently not used
02787 }
02788 
02789 void unit::add_trait_description(const config& trait, const t_string& description)
02790 {
02791     const std::string& gender_string = gender_ == unit_race::FEMALE ? "female_name" : "male_name";
02792     t_string const &gender_specific_name = trait[gender_string];
02793 
02794     // if this is a t_string& instead of a t_string, msvc9 compiled windows binaries
02795     // choke on the case where both gender_specific_name and trait["name"] are empty.
02796     const t_string name = gender_specific_name.empty() ?
02797          trait["name"] : gender_specific_name;
02798 
02799     if(!name.empty()) {
02800         trait_names_.push_back(name);
02801         trait_descriptions_.push_back(description);
02802     }
02803 }
02804 
02805 std::string unit::absolute_image() const {
02806     return cfg_["image_icon"].empty() ? cfg_["image"] : cfg_["image_icon"];
02807 }
02808 
02809 const unit_animation* unit::choose_animation(const display& disp, const map_location& loc,const std::string& event,
02810         const map_location& second_loc,const int value,const unit_animation::hit_type hit,
02811         const attack_type* attack, const attack_type* second_attack, int swing_num) const
02812 {
02813     // Select one of the matching animations at random
02814     std::vector<const unit_animation*> options;
02815     int max_val = unit_animation::MATCH_FAIL;
02816     for(std::vector<unit_animation>::const_iterator i = animations_.begin(); i != animations_.end(); ++i) {
02817         int matching = i->matches(disp,loc,second_loc,this,event,value,hit,attack,second_attack,swing_num);
02818         if(matching > unit_animation::MATCH_FAIL && matching == max_val) {
02819             options.push_back(&*i);
02820         } else if(matching > max_val) {
02821             max_val = matching;
02822             options.erase(options.begin(),options.end());
02823             options.push_back(&*i);
02824         }
02825     }
02826 
02827     if(max_val == unit_animation::MATCH_FAIL) {
02828         return NULL;
02829     }
02830     return options[rand()%options.size()];
02831 }
02832 
02833 
02834 void unit::apply_modifications()
02835 {
02836     log_scope("apply mods");
02837 
02838     for(size_t i = 0; i != NumModificationTypes; ++i) {
02839         const std::string& mod = ModificationTypes[i];
02840         foreach (const config &m, modifications_.child_range(mod)) {
02841             log_scope("add mod");
02842             add_modification(ModificationTypes[i], m, true);
02843         }
02844     }
02845 
02846     //apply the experience acceleration last
02847     int exp_accel = unit_type::experience_accelerator::get_acceleration();
02848     max_experience_ = std::max<int>(1, (max_experience_ * exp_accel + 50)/100);
02849 }
02850 
02851 bool unit::invisible(const map_location& loc, bool see_all) const
02852 {
02853     // Fetch from cache
02854     /**
02855      * @todo FIXME: We use the cache only when using the default see_all=true
02856      * Maybe add a second cache if the see_all=false become more frequent.
02857      */
02858     if(see_all) {
02859         std::map<map_location, bool>::const_iterator itor = invisibility_cache_.find(loc);
02860         if(itor != invisibility_cache_.end()) {
02861             return itor->second;
02862         }
02863     }
02864 
02865     // Test hidden status
02866     static const std::string hides("hides");
02867     bool is_inv = !get_state(STATE_UNCOVERED) && get_ability_bool(hides,loc);
02868     if(is_inv){
02869         const std::vector<team>& teams = *resources::teams;
02870         foreach (const unit &u, *resources::units)
02871         {
02872             const map_location &u_loc = u.get_location();
02873             if (teams[side_-1].is_enemy(u.side()) && tiles_adjacent(loc, u_loc)) {
02874                 // Enemy spotted in adjacent tiles, check if we can see him.
02875                 // Watch out to call invisible with see_all=true to avoid infinite recursive calls!
02876                 if(see_all) {
02877                     is_inv = false;
02878                     break;
02879                 } else if (!teams[side_-1].fogged(u_loc)
02880                 && !u.invisible(u_loc, true)) {
02881                     is_inv = false;
02882                     break;
02883                 }
02884             }
02885         }
02886     }
02887 
02888     if(see_all) {
02889         // Add to caches
02890         if(invisibility_cache_.empty()) {
02891             units_with_cache.push_back(this);
02892         }
02893         invisibility_cache_[loc] = is_inv;
02894     }
02895 
02896     return is_inv;
02897 }
02898 
02899 
02900 bool unit::is_visible_to_team(team const& team, bool const see_all, gamemap const& map) const
02901 {
02902     map_location const& loc = get_location();
02903     if (!map.on_board(loc))
02904         return false;
02905     if (see_all)
02906         return true;
02907     if (team.is_enemy(side()) && invisible(loc))
02908         return false;
02909     if (team.is_enemy(side()) && team.fogged(loc))
02910         return false;
02911     if (team.fogged(loc)  && !(*resources::teams)[side() - 1].share_view())
02912         return false;
02913 
02914     return true;
02915 }
02916 
02917 void unit::set_underlying_id() {
02918     if(underlying_id_ == 0){
02919         underlying_id_ = n_unit::id_manager::instance().next_id();
02920     }
02921     if (id_.empty()) {
02922         std::stringstream ss;
02923         ss << (type_.empty()?"Unit":type_) << "-" << underlying_id_;
02924         id_ = ss.str();
02925     }
02926 }
02927 
02928 unit& unit::clone(bool is_temporary)
02929 {
02930     if(is_temporary) {
02931         underlying_id_ = n_unit::id_manager::instance().next_fake_id();
02932     } else {
02933         underlying_id_ = n_unit::id_manager::instance().next_id();
02934         std::string::size_type pos = id_.find_last_of('-');
02935         if(pos != std::string::npos && pos+1 < id_.size()
02936         && id_.find_first_not_of("0123456789", pos+1) == std::string::npos) {
02937             // this appears to be a duplicate of a generic unit, so give it a new id
02938             WRN_UT << "assigning new id to clone of generic unit " << id_ << "\n";
02939             id_.clear();
02940             set_underlying_id();
02941         }
02942     }
02943     return *this;
02944 }
02945 
02946 
02947 unit_movement_resetter::unit_movement_resetter(unit &u, bool operate) :
02948     u_(u), moves_(u.movement_)
02949 {
02950     if (operate) {
02951         u.movement_ = u.total_movement();
02952     }
02953 }
02954 
02955 unit_movement_resetter::~unit_movement_resetter()
02956 {
02957     assert(resources::units);
02958 
02959     if(!resources::units->has_unit(&u_)) {
02960         /*
02961          * It might be valid that the unit is not in the unit map.
02962          * It might also mean a not longer valid unit will be assigned to.
02963          */
02964         DBG_UT << "The unit to be removed is not in the unit map.\n";
02965     }
02966     u_.movement_ = moves_;
02967 }
02968 
02969 bool unit::matches_id(const std::string& unit_id) const
02970 {
02971         return id_ == unit_id;
02972 }
02973 
02974 /**
02975  * Used to find units in vectors by their ID. (Convenience wrapper)
02976  * @returns what std::find_if() returns.
02977  */
02978 std::vector<unit>::iterator find_if_matches_id(
02979         std::vector<unit> &unit_list, // Not const so we can get a non-const iterator to return.
02980         const std::string &unit_id)
02981 {
02982     return std::find_if(unit_list.begin(), unit_list.end(),
02983                         boost::bind(&unit::matches_id, _1, unit_id));
02984 }
02985 
02986 /**
02987  * Used to find units in vectors by their ID. (Convenience wrapper; const version)
02988  * @returns what std::find_if() returns.
02989  */
02990 std::vector<unit>::const_iterator find_if_matches_id(
02991         const std::vector<unit> &unit_list,
02992         const std::string &unit_id)
02993 {
02994     return std::find_if(unit_list.begin(), unit_list.end(),
02995                         boost::bind(&unit::matches_id, _1, unit_id));
02996 }
02997 
02998 /**
02999  * Used to erase units from vectors by their ID. (Convenience wrapper)
03000  * @returns what std::vector<>::erase() returns.
03001  */
03002 std::vector<unit>::iterator erase_if_matches_id(
03003         std::vector<unit> &unit_list,
03004         const std::string &unit_id)
03005 {
03006     return unit_list.erase(std::remove_if(unit_list.begin(), unit_list.end(),
03007                                           boost::bind(&unit::matches_id, _1, unit_id)),
03008                            unit_list.end());
03009 }
03010 
03011 int side_units(int side)
03012 {
03013     int res = 0;
03014     foreach (const unit &u, *resources::units) {
03015         if (u.side() == side) ++res;
03016     }
03017     return res;
03018 }
03019 
03020 int side_units_cost(int side)
03021 {
03022     int res = 0;
03023     foreach (const unit &u, *resources::units) {
03024         if (u.side() == side) res += u.cost();
03025     }
03026     return res;
03027 }
03028 
03029 int side_upkeep(int side)
03030 {
03031     int res = 0;
03032     foreach (const unit &u, *resources::units) {
03033         if (u.side() == side) res += u.upkeep();
03034     }
03035     return res;
03036 }
03037 
03038 unit_map::iterator find_visible_unit(const map_location &loc,
03039     const team& current_team, bool see_all)
03040 {
03041     unit_map& units = *resources::units;
03042     if (!resources::game_map->on_board(loc)) return units.end();
03043     unit_map::iterator u = units.find(loc);
03044     if (!u.valid() || !u->is_visible_to_team(current_team, see_all))
03045         return units.end();
03046     return u;
03047 }
03048 
03049 unit *get_visible_unit(const map_location &loc,
03050     const team &current_team, bool see_all)
03051 {
03052     unit_map::iterator ui = find_visible_unit(loc,
03053         current_team, see_all);
03054     if (ui == resources::units->end()) return NULL;
03055     return &*ui;
03056 }
03057 
03058 void unit::refresh()
03059 {
03060     if (state_ == STATE_FORGET && anim_ && anim_->animation_finished_potential())
03061     {
03062         set_standing();
03063         return;
03064     }
03065     display &disp = *display::get_singleton();
03066     if (state_ != STATE_STANDING || get_current_animation_tick() < next_idling_ ||
03067         !disp.tile_nearly_on_screen(loc_) || incapacitated())
03068     {
03069         return;
03070     }
03071     if (get_current_animation_tick() > next_idling_ + 1000)
03072     {
03073         // prevent all units animating at the same time
03074         if (disp.idle_anim()) {
03075             next_idling_ = get_current_animation_tick()
03076                 + static_cast<int>((20000 + rand() % 20000) * disp.idle_anim_rate());
03077         } else {
03078             next_idling_ = INT_MAX;
03079         }
03080     } else {
03081         set_idling();
03082     }
03083 }
03084 
03085 team_data calculate_team_data(const team& tm, int side)
03086 {
03087     team_data res;
03088     res.units = side_units(side);
03089     res.upkeep = side_upkeep(side);
03090     res.villages = tm.villages().size();
03091     res.expenses = std::max<int>(0,res.upkeep - tm.support());
03092     res.net_income = tm.total_income() - res.expenses;
03093     res.gold = tm.gold();
03094     res.teamname = tm.user_team_name();
03095     return res;
03096 }
03097 
03098 temporary_unit_placer::temporary_unit_placer(unit_map& m, const map_location& loc, unit& u)
03099     : m_(m), loc_(loc), temp_(m.extract(loc))
03100 {
03101     u.clone();
03102     m.add(loc, u);
03103 }
03104 
03105 temporary_unit_placer::~temporary_unit_placer()
03106 {
03107     m_.erase(loc_);
03108     if(temp_) {
03109         m_.insert(temp_);
03110     }
03111 }
03112 
03113 temporary_unit_remover::temporary_unit_remover(unit_map& m, const map_location& loc)
03114     : m_(m), loc_(loc), temp_(m.extract(loc))
03115 {
03116 }
03117 
03118 temporary_unit_remover::~temporary_unit_remover()
03119 {
03120     if(temp_) {
03121         m_.insert(temp_);
03122     }
03123 }
03124 
03125 temporary_unit_mover::temporary_unit_mover(unit_map& m, const map_location& src,  const map_location& dst)
03126     : m_(m), src_(src), dst_(dst), temp_(m.extract(dst))
03127 {
03128     m.move(src_, dst_);
03129 }
03130 
03131 temporary_unit_mover::~temporary_unit_mover()
03132 {
03133     m_.move(dst_, src_);
03134     if(temp_) {
03135         m_.insert(temp_);
03136     }
03137 }
03138 
03139 std::string unit::TC_image_mods() const{
03140     std::stringstream modifier;
03141     if(!flag_rgb_.empty()){
03142         modifier << "~RC("<< flag_rgb_ << ">" << team::get_side_color_index(side()) << ")";
03143     }
03144     return modifier.str();
03145 }
03146 std::string unit::image_mods() const{
03147     std::stringstream modifier;
03148     if(!image_mods_.empty()){
03149         modifier << "~" << image_mods_;
03150     }
03151     modifier << TC_image_mods();
03152     return modifier.str();
03153 }
03154 
03155 const std::string& unit::effect_image_mods() const{
03156     return image_mods_;
03157 }
03158 
03159 const tportrait* unit::portrait(
03160         const unsigned size, const tportrait::tside side) const
03161 {
03162     foreach(const tportrait& portrait, (type()->portraits())) {
03163         if(portrait.size == size
03164                 && (side ==  portrait.side || portrait.side == tportrait::BOTH)) {
03165 
03166             return &portrait;
03167         }
03168     }
03169 
03170     return NULL;
03171 }
03172 
03173 void unit::remove_attacks_ai()
03174 {
03175     if (attacks_left_ == max_attacks_) {
03176         //TODO: add state_not_attacked
03177     }
03178     set_attacks(0);
03179 }
03180 
03181 
03182 void unit::remove_movement_ai()
03183 {
03184     if (movement_left() == total_movement()) {
03185         set_state(STATE_NOT_MOVED,true);
03186     }
03187     set_movement(0);
03188 }
03189 
03190 
03191 void unit::set_hidden(bool state) {
03192     hidden_ = state;
03193     if(!state) return;
03194     // We need to get rid of haloes immediately to avoid display glitches
03195     clear_haloes();
03196 }
03197 
03198 // Filters unimportant stats from the unit config and returns a checksum of
03199 // the remaining config.
03200 std::string get_checksum(const unit& u) {
03201     config unit_config;
03202     config wcfg;
03203     u.write(unit_config);
03204     const std::string main_keys[] =
03205         { "advances_to",
03206         "alignment",
03207         "cost",
03208         "experience",
03209         "gender",
03210         "hitpoints",
03211         "ignore_race_traits",
03212         "ignore_global_traits",
03213         "level",
03214         "max_attacks",
03215         "max_experience",
03216         "max_hitpoints",
03217         "max_moves",
03218         "movement",
03219         "movement_type",
03220         "race",
03221         "random_traits",
03222         "resting",
03223         "undead_variation",
03224         "upkeep",
03225         "zoc",
03226         ""};
03227 
03228     for (int i = 0; !main_keys[i].empty(); ++i)
03229     {
03230         wcfg[main_keys[i]] = unit_config[main_keys[i]];
03231     }
03232     const std::string attack_keys[] =
03233         { "name",
03234             "type",
03235             "range",
03236             "damage",
03237             "number",
03238         ""};
03239 
03240     foreach (const config &att, unit_config.child_range("attack"))
03241     {
03242         config& child = wcfg.add_child("attack");
03243         for (int i = 0; !attack_keys[i].empty(); ++i) {
03244             child[attack_keys[i]] = att[attack_keys[i]];
03245         }
03246         foreach (const config &spec, att.child_range("specials")) {
03247             config& child_spec = child.add_child("specials", spec);
03248             child_spec.recursive_clear_value("description");
03249         }
03250 
03251     }
03252 
03253     foreach (const config &abi, unit_config.child_range("abilities"))
03254     {
03255         config& child = wcfg.add_child("abilities", abi);
03256         child.recursive_clear_value("description");
03257         child.recursive_clear_value("description_inactive");
03258         child.recursive_clear_value("name");
03259         child.recursive_clear_value("name_inactive");
03260     }
03261 
03262     foreach (const config &trait, unit_config.child_range("trait"))
03263     {
03264         config& child = wcfg.add_child("trait", trait);
03265         child.recursive_clear_value("description");
03266         child.recursive_clear_value("male_name");
03267         child.recursive_clear_value("female_name");
03268         child.recursive_clear_value("name");
03269     }
03270 
03271     const std::string child_keys[] = {"advance_from", "defense", "movement_costs", "vision_costs", "jamming_costs" "resistance", ""};
03272 
03273     for (int i = 0; !child_keys[i].empty(); ++i)
03274     {
03275         foreach (const config &c, unit_config.child_range(child_keys[i])) {
03276             wcfg.add_child(child_keys[i], c);
03277         }
03278     }
03279     DBG_UT << wcfg;
03280 
03281     return wcfg.hash();
03282 }
03283 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:13 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs