00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00063
00064
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
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
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
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
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
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
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
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
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
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
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
00520 if(cfg_["upkeep"].empty()) {
00521 cfg_["upkeep"] = "full";
00522 }
00523
00524 set_recruits(utils::split(cfg["extra_recruit"]));
00525
00526
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
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
00663 movement_ = max_movement_;
00664 hit_points_ = max_hit_points_;
00665 attacks_left_ = max_attacks_;
00666
00667
00668
00669
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
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
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
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
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
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
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
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
00771
00772 if (!musthaveonly && (!can_recruit() || avl == "any"))
00773 candidate_traits.push_back(t);
00774 }
00775
00776 if (musthaveonly) return;
00777
00778
00779
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
00791
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
00814 trait_names_.clear();
00815 trait_descriptions_.clear(),
00816 is_fearless_ = false;
00817 is_healthy_ = false;
00818
00819
00820 modification_descriptions_.clear();
00821 movement_costs_.clear();
00822 vision_costs_.clear();
00823 jamming_costs_.clear();
00824 defense_mods_.clear();
00825
00826
00827
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
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
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;
00898
00899 if(type_id()!=t->id()) {
00900 do_heal = true;
00901 type_ = t->id();
00902 }
00903
00904 if (cfg_["random_traits"].to_bool(true)) {
00905 generate_traits(!use_traits, state);
00906 } else {
00907
00908
00909
00910
00911 generate_traits(true);
00912 }
00913
00914
00915
00916
00917
00918 apply_modifications();
00919
00920
00921
00922 if (do_heal) {
00923 heal_all();
00924 }
00925
00926
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
01049
01050
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
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
01126 set_interrupted_move(map_location());
01127 }
01128 void unit::new_scenario()
01129 {
01130
01131
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
01286 matches = internal_matches_filter(cfg, loc, use_flat_tod);
01287 }
01288
01289
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
01299 if(cond_name == "and") {
01300 matches = matches && matches_filter(cond_filter,loc,use_flat_tod);
01301 }
01302
01303 else if(cond_name == "or") {
01304 matches = matches || matches_filter(cond_filter,loc,use_flat_tod);
01305 }
01306
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
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
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
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
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
01392
01393
01394 if(type == this_type) {
01395
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
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
01509
01510
01511
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
01519
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
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);
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
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()) {
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
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
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
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
01914
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
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
01952
01953
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
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
02006
02007
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
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
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
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
02100
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
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
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
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
02230
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;
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
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
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
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
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
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
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
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 }
02688 } else {
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
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
02771
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
02782 if(type == "trait") {
02783 add_trait_description(mod, description);
02784 }
02785
02786
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
02795
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
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
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
02854
02855
02856
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
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
02875
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
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
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
02962
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
02976
02977
02978 std::vector<unit>::iterator find_if_matches_id(
02979 std::vector<unit> &unit_list,
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
02988
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
03000
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 ¤t_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
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
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
03195 clear_haloes();
03196 }
03197
03198
03199
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