00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "global.hpp"
00019 #include "unit_display.hpp"
00020
00021 #include "foreach.hpp"
00022 #include "game_preferences.hpp"
00023 #include "game_events.hpp"
00024 #include "log.hpp"
00025 #include "mouse_events.hpp"
00026 #include "resources.hpp"
00027 #include "terrain_filter.hpp"
00028
00029
00030 #define LOG_DP LOG_STREAM(info, display)
00031
00032 static void teleport_unit_between( const map_location& a, const map_location& b, unit& temp_unit)
00033 {
00034 display* disp = display::get_singleton();
00035 if(!disp || disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b))) {
00036 return;
00037 }
00038 disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.0,false);
00039
00040 temp_unit.set_location(a);
00041 if (!disp->fogged(a)) {
00042 disp->invalidate(temp_unit.get_location());
00043 temp_unit.set_facing(a.get_relative_dir(b));
00044 unit_animator animator;
00045 animator.add_animation(&temp_unit,"pre_teleport",a);
00046 animator.start_animations();
00047 animator.wait_for_end();
00048 }
00049
00050 temp_unit.set_location(b);
00051 if (!disp->fogged(b)) {
00052 disp->invalidate(temp_unit.get_location());
00053 temp_unit.set_facing(a.get_relative_dir(b));
00054 disp->scroll_to_tiles(b,a,game_display::ONSCREEN,true,0.0,false);
00055 unit_animator animator;
00056 animator.add_animation(&temp_unit,"post_teleport",b);
00057 animator.start_animations();
00058 animator.wait_for_end();
00059 }
00060
00061 temp_unit.set_standing();
00062 disp->update_display();
00063 events::pump();
00064 }
00065
00066 static void move_unit_between(const map_location& a, const map_location& b, unit& temp_unit,unsigned int step_num,unsigned int step_left)
00067 {
00068 display* disp = display::get_singleton();
00069 if(!disp || disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b))) {
00070 return;
00071 }
00072
00073 temp_unit.set_location(a);
00074 disp->invalidate(temp_unit.get_location());
00075 temp_unit.set_facing(a.get_relative_dir(b));
00076 unit_animator animator;
00077 animator.replace_anim_if_invalid(&temp_unit,"movement",a,b,step_num,
00078 false,"",0,unit_animation::INVALID,NULL,NULL,step_left);
00079 animator.start_animations();
00080 animator.pause_animation();
00081 disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.0,false);
00082 animator.restart_animation();
00083
00084
00085
00086
00087 int target_time = animator.get_animation_time_potential();
00088
00089
00090
00091
00092 target_time += 200;
00093 target_time -= target_time%200;
00094
00095
00096
00097
00098
00099 animator.wait_until(target_time);
00100
00101
00102 map_location arr[6];
00103 get_adjacent_tiles(a, arr);
00104 unsigned int i;
00105 for (i = 0; i < 6; ++i) {
00106 disp->invalidate(arr[i]);
00107 }
00108 get_adjacent_tiles(b, arr);
00109 for (i = 0; i < 6; ++i) {
00110 disp->invalidate(arr[i]);
00111 }
00112 }
00113
00114 namespace unit_display
00115 {
00116
00117 void move_unit(const std::vector<map_location>& path, unit& u,
00118 const std::vector<team>& teams, bool animate,
00119 map_location::DIRECTION dir)
00120 {
00121 game_display* disp = game_display::get_singleton();
00122 assert(!path.empty());
00123 assert(disp);
00124 if(!disp || disp->video().update_locked() || disp->video().faked())
00125 return;
00126
00127 if(path.size() == 1)
00128 return;
00129 if(dir == map_location::NDIRECTIONS)
00130 dir = path[path.size()-2].get_relative_dir(path.back());
00131
00132 if(!animate) {
00133 u.set_facing(dir);
00134 disp->invalidate(path.front());
00135 disp->invalidate(path.back());
00136 return;
00137 }
00138
00139 bool was_hidden = u.get_hidden();
00140
00141 game_display::fake_unit temp_unit(u);
00142 u.set_hidden(true);
00143 temp_unit.set_standing(false);
00144 temp_unit.set_hidden(false);
00145 temp_unit.place_on_game_display(disp);
00146
00147 move_unit_start(path, temp_unit, teams[temp_unit.side()-1]);
00148
00149 for(size_t i = 0; i+1 < path.size(); ++i) {
00150 move_unit_step(path, i, temp_unit, teams[temp_unit.side()-1]);
00151 }
00152
00153 move_unit_finish(path, temp_unit);
00154
00155 temp_unit.remove_from_game_display();
00156
00157 u.set_facing(dir);
00158 u.set_hidden(was_hidden);
00159 }
00160
00161 void move_unit_start(const std::vector<map_location>& path, unit& temp_unit,
00162 const team& tm)
00163 {
00164 game_display* disp = game_display::get_singleton();
00165 assert(!path.empty());
00166 assert(disp);
00167 if(!disp || disp->video().update_locked() || disp->video().faked())
00168 return;
00169
00170 if(path.size() == 1)
00171 return;
00172
00173 bool invisible = tm.is_enemy(int(disp->viewing_team()+1)) &&
00174 temp_unit.invisible(path[0]);
00175 if(!invisible) {
00176
00177
00178 disp->scroll_to_tiles(path, game_display::ONSCREEN, true, true,0.0,false);
00179 }
00180
00181
00182
00183
00184
00185
00186 disp->draw(false);
00187 disp->draw(false);
00188
00189
00190
00191
00192
00193 disp->draw(true);
00194
00195
00196 temp_unit.set_location(path[0]);
00197 disp->invalidate(temp_unit.get_location());
00198 temp_unit.set_facing(path[0].get_relative_dir(path[1]));
00199
00200 unit_animator animator;
00201 animator.add_animation(&temp_unit,"pre_movement",path[0],path[1]);
00202 animator.start_animations();
00203 animator.wait_for_end();
00204 }
00205
00206 void move_unit_step(const std::vector<map_location>& path, size_t i,
00207 unit& temp_unit, const team& tm)
00208 {
00209 game_display* disp = game_display::get_singleton();
00210 assert(path.size() > i+1);
00211 assert(disp);
00212 if(!disp || disp->video().update_locked() || disp->video().faked())
00213 return;
00214
00215 if(path.size() <= i+1)
00216 return;
00217
00218 bool invisible = tm.is_enemy(int(disp->viewing_team()+1)) &&
00219 temp_unit.invisible(path[i]) &&
00220 temp_unit.invisible(path[i+1]);
00221 if(invisible)
00222 return;
00223
00224 if (!disp->tile_fully_on_screen(path[i]) || !disp->tile_fully_on_screen(path[i+1])) {
00225
00226 temp_unit.set_location(path[i]);
00227 disp->invalidate(temp_unit.get_location());
00228
00229 std::vector<map_location> remaining_path;
00230 for(size_t j = i; j < path.size(); ++j) {
00231 remaining_path.push_back(path[j]);
00232 }
00233 temp_unit.get_animation()->pause_animation();
00234 disp->scroll_to_tiles(remaining_path,
00235 game_display::ONSCREEN, true,false,0.0,false);
00236 temp_unit.get_animation()->restart_animation();
00237 }
00238
00239 if(tiles_adjacent(path[i], path[i+1])) {
00240 move_unit_between(path[i],path[i+1],temp_unit,i,path.size()-2-i);
00241 } else if (path[i] != path[i+1]) {
00242 teleport_unit_between(path[i],path[i+1],temp_unit);
00243 } else {
00244
00245 }
00246 }
00247
00248 void move_unit_finish(const std::vector<map_location>& path, unit& temp_unit)
00249 {
00250 game_display* disp = game_display::get_singleton();
00251 assert(!path.empty());
00252 assert(disp);
00253 if(!disp || disp->video().update_locked() || disp->video().faked())
00254 return;
00255
00256 if(path.size() == 1)
00257 return;
00258
00259 temp_unit.set_location(path[path.size() - 1]);
00260 temp_unit.set_facing(path[path.size()-2].get_relative_dir(path[path.size()-1]));
00261
00262 unit_animator animator;
00263 animator.add_animation(&temp_unit,"post_movement",path[path.size()-1],map_location::null_location);
00264 animator.start_animations();
00265 animator.wait_for_end();
00266
00267 events::mouse_handler* mousehandler = events::mouse_handler::get_singleton();
00268 if (mousehandler) {
00269 mousehandler->invalidate_reachmap();
00270 }
00271
00272 disp->invalidate(path.front());
00273 disp->invalidate(path.back());
00274 }
00275
00276 void reset_helpers(const unit *attacker,const unit *defender);
00277
00278 void unit_draw_weapon(const map_location& loc, unit& attacker,
00279 const attack_type* attack,const attack_type* secondary_attack, const map_location& defender_loc,unit* defender)
00280 {
00281 display* disp = display::get_singleton();
00282 if(!disp ||disp->video().update_locked() || disp->video().faked() || disp->fogged(loc) || preferences::show_combat() == false) {
00283 return;
00284 }
00285 unit_animator animator;
00286 animator.add_animation(&attacker,"draw_weapon",loc,defender_loc,0,false,"",0,unit_animation::HIT,attack,secondary_attack,0);
00287 animator.add_animation(defender,"draw_weapon",defender_loc,loc,0,false,"",0,unit_animation::MISS,secondary_attack,attack,0);
00288 animator.start_animations();
00289 animator.wait_for_end();
00290
00291 }
00292
00293
00294 void unit_sheath_weapon(const map_location& primary_loc, unit* primary_unit,
00295 const attack_type* primary_attack,const attack_type* secondary_attack, const map_location& secondary_loc,unit* secondary_unit)
00296 {
00297 display* disp = display::get_singleton();
00298 if(!disp ||disp->video().update_locked() || disp->video().faked() || disp->fogged(primary_loc) || preferences::show_combat() == false) {
00299 return;
00300 }
00301 unit_animator animator;
00302 if(primary_unit) {
00303 animator.add_animation(primary_unit,"sheath_weapon",primary_loc,secondary_loc,0,false,"",0,unit_animation::INVALID,primary_attack,secondary_attack,0);
00304 }
00305 if(secondary_unit) {
00306 animator.add_animation(secondary_unit,"sheath_weapon",secondary_loc,primary_loc,0,false,"",0,unit_animation::INVALID,secondary_attack,primary_attack,0);
00307 }
00308
00309 if(primary_unit || secondary_unit) {
00310 animator.start_animations();
00311 animator.wait_for_end();
00312 }
00313 if(primary_unit) {
00314 primary_unit->set_standing();
00315 }
00316 if(secondary_unit) {
00317 secondary_unit->set_standing();
00318 }
00319 reset_helpers(primary_unit,secondary_unit);
00320
00321 }
00322
00323
00324 void unit_die(const map_location& loc, unit& loser,
00325 const attack_type* attack,const attack_type* secondary_attack, const map_location& winner_loc,unit* winner)
00326 {
00327 display* disp = display::get_singleton();
00328 if(!disp ||disp->video().update_locked() || disp->video().faked() || disp->fogged(loc) || preferences::show_combat() == false) {
00329 return;
00330 }
00331 unit_animator animator;
00332
00333 animator.add_animation(&loser,"death",loc,winner_loc,0,false,"",0,unit_animation::KILL,attack,secondary_attack,0);
00334
00335 animator.add_animation(winner,"victory",winner_loc,loc,0,true,"",0,
00336 unit_animation::KILL,secondary_attack,attack,0);
00337 animator.start_animations();
00338 animator.wait_for_end();
00339
00340 reset_helpers(winner,&loser);
00341 events::mouse_handler* mousehandler = events::mouse_handler::get_singleton();
00342 if (mousehandler) {
00343 mousehandler->invalidate_reachmap();
00344 }
00345 }
00346
00347
00348 void unit_attack(
00349 const map_location& a, const map_location& b, int damage,
00350 const attack_type& attack, const attack_type* secondary_attack,
00351 int swing,std::string hit_text,int drain_amount,std::string att_text)
00352 {
00353 display* disp = display::get_singleton();
00354 if(!disp ||disp->video().update_locked() || disp->video().faked() ||
00355 (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) {
00356 return;
00357 }
00358 unit_map& units = disp->get_units();
00359 disp->select_hex(map_location::null_location);
00360
00361
00362 disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false);
00363
00364 log_scope("unit_attack");
00365
00366 const unit_map::iterator att = units.find(a);
00367 assert(att != units.end());
00368 unit& attacker = *att;
00369
00370 const unit_map::iterator def = units.find(b);
00371 assert(def != units.end());
00372 unit &defender = *def;
00373 int def_hitpoints = defender.hitpoints();
00374
00375 att->set_facing(a.get_relative_dir(b));
00376 def->set_facing(b.get_relative_dir(a));
00377 defender.set_facing(b.get_relative_dir(a));
00378
00379
00380 unit_animator animator;
00381 unit_ability_list leaders = attacker.get_abilities("leadership");
00382 unit_ability_list helpers = defender.get_abilities("resistance");
00383
00384 std::string text ;
00385 if(damage) text = lexical_cast<std::string>(damage);
00386 if(!hit_text.empty()) {
00387 text.insert(text.begin(),hit_text.size()/2,' ');
00388 text = text + "\n" + hit_text;
00389 }
00390
00391 std::string text_2 ;
00392 if(drain_amount) text_2 = lexical_cast<std::string>(drain_amount > 0 ? drain_amount : -drain_amount);
00393 if(!att_text.empty()) {
00394 text_2.insert(text_2.begin(),att_text.size()/2,' ');
00395 text_2 = text_2 + "\n" + att_text;
00396 }
00397
00398 unit_animation::hit_type hit_type;
00399 if(damage >= defender.hitpoints()) {
00400 hit_type = unit_animation::KILL;
00401 } else if(damage > 0) {
00402 hit_type = unit_animation::HIT;
00403 }else {
00404 hit_type = unit_animation::MISS;
00405 }
00406 animator.add_animation(&attacker, "attack", att->get_location(),
00407 def->get_location(), damage, true, text_2,
00408 (drain_amount >= 0) ? display::rgb(0, 255, 0) : display::rgb(255, 0, 0),
00409 hit_type, &attack, secondary_attack, swing);
00410
00411
00412 const unit_animation *defender_anim = def->choose_animation(*disp,
00413 def->get_location(), "defend", att->get_location(), damage,
00414 hit_type, &attack, secondary_attack, swing);
00415 animator.add_animation(&defender, defender_anim, def->get_location(),
00416 true, text , display::rgb(255, 0, 0));
00417
00418 for (std::vector<std::pair<const config *, map_location> >::iterator itor = leaders.cfgs.begin(); itor != leaders.cfgs.end(); ++itor) {
00419 if(itor->second == a) continue;
00420 if(itor->second == b) continue;
00421 unit_map::iterator leader = units.find(itor->second);
00422 assert(leader != units.end());
00423 leader->set_facing(itor->second.get_relative_dir(a));
00424 animator.add_animation(&*leader, "leading", itor->second,
00425 att->get_location(), damage, true, "", 0,
00426 hit_type, &attack, secondary_attack, swing);
00427 }
00428 for (std::vector<std::pair<const config *, map_location> >::iterator itor = helpers.cfgs.begin(); itor != helpers.cfgs.end(); ++itor) {
00429 if(itor->second == a) continue;
00430 if(itor->second == b) continue;
00431 unit_map::iterator helper = units.find(itor->second);
00432 assert(helper != units.end());
00433 helper->set_facing(itor->second.get_relative_dir(b));
00434 animator.add_animation(&*helper, "resistance", itor->second,
00435 def->get_location(), damage, true, "", 0,
00436 hit_type, &attack, secondary_attack, swing);
00437 }
00438
00439
00440 animator.start_animations();
00441 animator.wait_until(0);
00442 int damage_left = damage;
00443 while(damage_left > 0 && !animator.would_end()) {
00444 int step_left = (animator.get_end_time() - animator.get_animation_time() )/50;
00445 if(step_left < 1) step_left = 1;
00446 int removed_hp = damage_left/step_left ;
00447 if(removed_hp < 1) removed_hp = 1;
00448 defender.take_hit(removed_hp);
00449 damage_left -= removed_hp;
00450 animator.wait_until(animator.get_animation_time_potential() +50);
00451 }
00452 animator.wait_for_end();
00453
00454 def->start_animation(animator.get_end_time(), defender_anim, true);
00455 reset_helpers(&*att, &*def);
00456 def->set_hitpoints(def_hitpoints);
00457 }
00458
00459
00460 void reset_helpers(const unit *attacker,const unit *defender)
00461 {
00462 display* disp = display::get_singleton();
00463 unit_map& units = disp->get_units();
00464 if(attacker) {
00465 unit_ability_list leaders = attacker->get_abilities("leadership");
00466 for (std::vector<std::pair<const config *, map_location> >::iterator itor = leaders.cfgs.begin(); itor != leaders.cfgs.end(); ++itor) {
00467 unit_map::iterator leader = units.find(itor->second);
00468 assert(leader != units.end());
00469 leader->set_standing();
00470 }
00471 }
00472
00473 if(defender) {
00474 unit_ability_list helpers = defender->get_abilities("resistance");
00475 for (std::vector<std::pair<const config *, map_location> >::iterator itor = helpers.cfgs.begin(); itor != helpers.cfgs.end(); ++itor) {
00476 unit_map::iterator helper = units.find(itor->second);
00477 assert(helper != units.end());
00478 helper->set_standing();
00479 }
00480 }
00481 }
00482
00483 void unit_recruited(const map_location& loc,const map_location& leader_loc)
00484 {
00485 game_display* disp = game_display::get_singleton();
00486 if(!disp || disp->video().update_locked() || disp->video().faked() ||disp->fogged(loc)) return;
00487 unit_map::iterator u = disp->get_units().find(loc);
00488 if(u == disp->get_units().end()) return;
00489 u->set_hidden(true);
00490
00491 unit_animator animator;
00492 if(leader_loc != map_location::null_location) {
00493 unit_map::iterator leader = disp->get_units().find(leader_loc);
00494 if(leader == disp->get_units().end()) return;
00495 disp->scroll_to_tiles(loc,leader_loc,game_display::ONSCREEN,true,0.0,false);
00496 leader->set_facing(leader_loc.get_relative_dir(loc));
00497 animator.add_animation(&*leader, "recruiting", leader_loc, loc, 0, true);
00498 } else {
00499 disp->scroll_to_tile(loc,game_display::ONSCREEN,true,false);
00500 }
00501
00502 disp->draw();
00503 u->set_hidden(false);
00504 animator.add_animation(&*u, "recruited", loc, leader_loc);
00505 animator.start_animations();
00506 animator.wait_for_end();
00507 animator.set_all_standing();
00508 if (loc==disp->mouseover_hex()) disp->invalidate_unit();
00509 }
00510
00511 void unit_healing(unit &healed, const map_location &healed_loc,
00512 const std::vector<unit *> &healers, int healing)
00513 {
00514 game_display* disp = game_display::get_singleton();
00515 if(!disp || disp->video().update_locked() || disp->video().faked() || disp->fogged(healed_loc)) return;
00516 if(healing==0) return;
00517
00518 disp->scroll_to_tile(healed_loc, game_display::ONSCREEN,true,false);
00519 disp->display_unit_hex(healed_loc);
00520 unit_animator animator;
00521
00522 foreach (unit *h, healers) {
00523 h->set_facing(h->get_location().get_relative_dir(healed_loc));
00524 animator.add_animation(h, "healing", h->get_location(),
00525 healed_loc, healing);
00526 }
00527 if (healing < 0) {
00528 animator.add_animation(&healed,"poisoned",healed_loc,map_location::null_location,-healing,false,lexical_cast<std::string>(-healing), display::rgb(255,0,0));
00529 } else {
00530 animator.add_animation(&healed,"healed",healed_loc,map_location::null_location,healing,false,lexical_cast<std::string>(healing), display::rgb(0,255,0));
00531 }
00532 animator.start_animations();
00533 animator.wait_for_end();
00534 animator.set_all_standing();
00535
00536 }
00537
00538 void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location = map_location::null_location);
00539
00540 void wml_animation(const vconfig &cfg, const map_location &default_location)
00541 {
00542 game_display &disp = *resources::screen;
00543 if (disp.video().update_locked() || disp.video().faked()) return;
00544 unit_animator animator;
00545 wml_animation_internal(animator, cfg, default_location);
00546 animator.start_animations();
00547 animator.wait_for_end();
00548 animator.set_all_standing();
00549 }
00550
00551 void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const map_location &default_location)
00552 {
00553 unit_map::iterator u = resources::units->find(default_location);
00554
00555
00556
00557 vconfig filter = cfg.child("filter");
00558 if(!filter.null()) {
00559 for (u = resources::units->begin(); u != resources::units->end(); ++u) {
00560 if (game_events::unit_matches_filter(*u, filter))
00561 break;
00562 }
00563 }
00564
00565
00566 if (u.valid() && !resources::screen->fogged(u->get_location()))
00567 {
00568 attack_type *primary = NULL;
00569 attack_type *secondary = NULL;
00570 Uint32 text_color;
00571 unit_animation::hit_type hits= unit_animation::INVALID;
00572 std::vector<attack_type> attacks = u->attacks();
00573 std::vector<attack_type>::iterator itor;
00574
00575 filter = cfg.child("primary_attack");
00576 if(!filter.null()) {
00577 for(itor = attacks.begin(); itor != attacks.end(); ++itor){
00578 if(itor->matches_filter(filter.get_parsed_config())) {
00579 primary = &*itor;
00580 break;
00581 }
00582 }
00583 }
00584
00585 filter = cfg.child("secondary_attack");
00586 if(!filter.null()) {
00587 for(itor = attacks.begin(); itor != attacks.end(); ++itor){
00588 if(itor->matches_filter(filter.get_parsed_config())) {
00589 secondary = &*itor;
00590 break;
00591 }
00592 }
00593 }
00594
00595 if(cfg["hits"] == "yes" || cfg["hits"] == "hit") {
00596 hits = unit_animation::HIT;
00597 }
00598 if(cfg["hits"] == "no" || cfg["hits"] == "miss") {
00599 hits = unit_animation::MISS;
00600 }
00601 if( cfg["hits"] == "kill" ) {
00602 hits = unit_animation::KILL;
00603 }
00604 if(cfg["red"].empty() && cfg["green"].empty() && cfg["blue"].empty()) {
00605 text_color = display::rgb(0xff,0xff,0xff);
00606 } else {
00607 text_color = display::rgb(cfg["red"], cfg["green"], cfg["blue"]);
00608 }
00609 resources::screen->scroll_to_tile(u->get_location(), game_display::ONSCREEN, true, false);
00610 vconfig t_filter = cfg.child("facing");
00611 map_location secondary_loc = map_location::null_location;
00612 if(!t_filter.empty()) {
00613 terrain_filter filter(t_filter, *resources::units);
00614 std::set<map_location> locs;
00615 filter.get_locations(locs);
00616 if (!locs.empty() && u->get_location() != *locs.begin()) {
00617 map_location::DIRECTION dir =u->get_location().get_relative_dir(*locs.begin());
00618 u->set_facing(dir);
00619 secondary_loc = u->get_location().get_direction(dir);
00620 }
00621 }
00622 animator.add_animation(&*u, cfg["flag"], u->get_location(),
00623 secondary_loc, cfg["value"], cfg["with_bars"].to_bool(),
00624 cfg["text"], text_color, hits, primary, secondary,
00625 cfg["value_second"]);
00626 }
00627 const vconfig::child_list sub_anims = cfg.get_children("animate");
00628 vconfig::child_list::const_iterator anim_itor;
00629 for(anim_itor = sub_anims.begin(); anim_itor != sub_anims.end();++anim_itor) {
00630 wml_animation_internal(animator, *anim_itor);
00631 }
00632
00633 }
00634 }