unit_display.cpp

Go to the documentation of this file.
00001 /* $Id: unit_display.cpp 26777 2008-05-22 17:40:20Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 /** @file unit_display.cpp */
00016 
00017 #include "global.hpp"
00018 
00019 #include "actions.hpp"
00020 #include "game_display.hpp"
00021 #include "game_preferences.hpp"
00022 #include "events.hpp"
00023 #include "game_config.hpp"
00024 #include "gamestatus.hpp"
00025 #include "halo.hpp"
00026 #include "image.hpp"
00027 #include "log.hpp"
00028 #include "scoped_resource.hpp"
00029 #include "sound.hpp"
00030 #include "unit_display.hpp"
00031 #include "util.hpp"
00032 #include "mouse_events.hpp"
00033 
00034 #include <cassert>
00035 #include <climits>
00036 
00037 #define LOG_DP LOG_STREAM(info, display)
00038 
00039 static void teleport_unit_between( const gamemap::location& a, const gamemap::location& b, unit& temp_unit)
00040 {
00041     game_display* disp = game_display::get_singleton();
00042     if(!disp || disp->video().update_locked() || (disp->fogged(a) && disp->fogged(b))) {
00043         return;
00044     }
00045     disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true);
00046 
00047     if (!disp->fogged(a)) { // teleport
00048         disp->place_temporary_unit(temp_unit,a);
00049         unit_animator animator;
00050         animator.add_animation(&temp_unit,"pre_teleport",a);
00051         animator.start_animations();
00052         animator.wait_for_end();
00053     }
00054     if (!disp->fogged(b)) { // teleport
00055         disp->place_temporary_unit(temp_unit,b);
00056         disp->scroll_to_tiles(b,a,game_display::ONSCREEN,true);
00057         unit_animator animator;
00058         animator.add_animation(&temp_unit,"post_teleport",b);
00059         animator.start_animations();
00060         animator.wait_for_end();
00061     }
00062     temp_unit.set_standing(b);
00063     disp->update_display();
00064     events::pump();
00065 }
00066 
00067 static void move_unit_between(const gamemap::location& a, const gamemap::location& b, unit& temp_unit)
00068 {
00069     game_display* disp = game_display::get_singleton();
00070     if(!disp || disp->video().update_locked() || (disp->fogged(a) && disp->fogged(b))) {
00071         return;
00072     }
00073 
00074 
00075     disp->place_temporary_unit(temp_unit,a);
00076     temp_unit.set_facing(a.get_relative_dir(b));
00077     unit_animator animator;
00078     animator.replace_anim_if_invalid(&temp_unit,"movement",a);
00079     animator.start_animations();
00080         animator.pause_animation();
00081     disp->scroll_to_tiles(a,b,game_display::ONSCREEN);
00082         animator.restart_animation();
00083     int target_time = animator.get_animation_time_potential();
00084     target_time += 150;
00085     target_time -= target_time%150;
00086     if(  target_time - animator.get_animation_time_potential() < 100 ) target_time +=150;
00087     animator.wait_until(target_time);
00088     gamemap::location arr[6];
00089     get_adjacent_tiles(a, arr);
00090     unsigned int i;
00091     for (i = 0; i < 6; i++) {
00092         disp->invalidate(arr[i]);
00093     }
00094     get_adjacent_tiles(b, arr);
00095     for (i = 0; i < 6; i++) {
00096         disp->invalidate(arr[i]);
00097     }
00098 }
00099 
00100 namespace unit_display
00101 {
00102 
00103 bool unit_visible_on_path( const std::vector<gamemap::location>& path, const unit& u, const unit_map& units, const std::vector<team>& teams)
00104 {
00105     game_display* disp = game_display::get_singleton();
00106     assert(disp);
00107     for(size_t i = 0; i+1 < path.size(); ++i) {
00108         const bool invisible = teams[u.side()-1].is_enemy(int(disp->viewing_team()+1)) &&
00109                  u.invisible(path[i],units,teams) &&
00110                  u.invisible(path[i+1],units,teams);
00111         if(!invisible) {
00112             return true;
00113         }
00114     }
00115 
00116     return false;
00117 }
00118 
00119 void move_unit(const std::vector<gamemap::location>& path, unit& u, const std::vector<team>& teams)
00120 {
00121     game_display* disp = game_display::get_singleton();
00122     assert(!path.empty());
00123     assert(disp);
00124     // One hex path (strange), nothing to do
00125     if (path.size()==1) return;
00126 
00127     const unit_map& units = disp->get_units();
00128 
00129     bool invisible = teams[u.side()-1].is_enemy(int(disp->viewing_team()+1)) &&
00130         u.invisible(path[0],units,teams);
00131 
00132     if(!invisible) {
00133         // Scroll to the path, but only if it fully fits on screen.
00134         // If it does not fit we might be able to do a better scroll later.
00135         disp->scroll_to_tiles(path, game_display::ONSCREEN, true, true);
00136     }
00137 
00138     bool was_hidden = u.get_hidden();
00139     // Original unit is usually hidden (but still on map, so count is correct)
00140     unit temp_unit = u;
00141     u.set_hidden(true);
00142     temp_unit.set_hidden(false);
00143         disp->draw();
00144     for(size_t i = 0; i+1 < path.size(); ++i) {
00145 
00146         invisible = teams[temp_unit.side()-1].is_enemy(int(disp->viewing_team()+1)) &&
00147                 temp_unit.invisible(path[i],units,teams) &&
00148                 temp_unit.invisible(path[i+1],units,teams);
00149 
00150         if(!invisible) {
00151             if (!disp->tile_on_screen(path[i]) || !disp->tile_on_screen(path[i+1])) {
00152                 // prevent the unit from dissappearing if we scroll here with i == 0
00153                 disp->place_temporary_unit(temp_unit,path[i]);
00154                 // scroll in as much of the remaining path as possible
00155                 std::vector<gamemap::location> remaining_path;
00156                 for(size_t j = i; j < path.size(); j++) {
00157                     remaining_path.push_back(path[j]);
00158                 }
00159                 disp->scroll_to_tiles(remaining_path);
00160             }
00161 
00162             if( !tiles_adjacent(path[i], path[i+1])) {
00163                 teleport_unit_between(path[i],path[i+1],temp_unit);
00164             } else {
00165                 move_unit_between(path[i],path[i+1],temp_unit);
00166             }
00167         }
00168     }
00169     disp->remove_temporary_unit();
00170     u.set_facing(path[path.size()-2].get_relative_dir(path[path.size()-1]));
00171     u.set_standing(path[path.size()-1]);
00172 
00173     u.set_hidden(was_hidden);
00174     disp->invalidate_unit_after_move(path[0], path[path.size()-1]);
00175 
00176     events::mouse_handler* mousehandler = events::mouse_handler::get_singleton();
00177     if (mousehandler) {
00178         mousehandler->invalidate_reachmap();
00179     }
00180 }
00181 
00182 void unit_die(const gamemap::location& loc, unit& loser,
00183         const attack_type* attack,const attack_type* secondary_attack, unit* winner)
00184 {
00185     game_display* disp = game_display::get_singleton();
00186     if(!disp ||disp->video().update_locked() || disp->fogged(loc) || preferences::show_combat() == false) {
00187         return;
00188     }
00189     unit_animator animator;
00190     // hide the hp/xp bars of the loser (useless and prevent bars around an erased unit)
00191     animator.add_animation(&loser,"death",loc,0,false,false,"",0,unit_animation::KILL,attack,secondary_attack,0);
00192     // but show the bars of the winner (avoid blinking and show its xp gain)
00193     animator.add_animation(winner,"victory",loc.get_direction(loser.facing()),0,true,false,"",0,
00194             unit_animation::KILL,secondary_attack,attack,0);
00195     animator.start_animations();
00196     animator.wait_for_end();
00197 
00198     events::mouse_handler* mousehandler = events::mouse_handler::get_singleton();
00199     if (mousehandler) {
00200         mousehandler->invalidate_reachmap();
00201     }
00202 }
00203 
00204 
00205 void unit_attack(
00206                  const gamemap::location& a, const gamemap::location& b, int damage,
00207                  const attack_type& attack, const attack_type* secondary_attack,
00208           int swing,std::string hit_text,bool drain,std::string att_text)
00209 {
00210     game_display* disp = game_display::get_singleton();
00211     if(!disp || preferences::show_combat() == false) return;
00212     unit_map& units = disp->get_units();
00213     disp->select_hex(gamemap::location::null_location);
00214     const bool hide = disp->video().update_locked() || (disp->fogged(a) && disp->fogged(b));
00215 
00216     if(!hide) {
00217         // scroll such that there is at least half a hex spacing around fighters
00218         disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5);
00219     }
00220 
00221     log_scope("unit_attack");
00222 
00223     const unit_map::iterator att = units.find(a);
00224     assert(att != units.end());
00225     unit& attacker = att->second;
00226 
00227     const unit_map::iterator def = units.find(b);
00228     assert(def != units.end());
00229     unit& defender = def->second;
00230 
00231     att->second.set_facing(a.get_relative_dir(b));
00232     def->second.set_facing(b.get_relative_dir(a));
00233 
00234 
00235     unit_animator animator;
00236     const gamemap::location leader_loc = under_leadership(units,a);
00237     unit_map::iterator leader = units.end();
00238 
00239     {
00240         std::string text ;
00241         if(damage) text = lexical_cast<std::string>(damage);
00242         if(!hit_text.empty()) {
00243             text.insert(text.begin(),hit_text.size()/2,' ');
00244             text = text + "\n" + hit_text;
00245         }
00246 
00247         std::string text_2 ;
00248         if(drain && damage) text_2 = lexical_cast<std::string>(minimum<int>(damage,defender.hitpoints())/2);
00249         if(!att_text.empty()) {
00250             text_2.insert(text_2.begin(),att_text.size()/2,' ');
00251             text_2 = text_2 + "\n" + att_text;
00252         }
00253 
00254         unit_animation::hit_type hit_type;
00255         if(damage >= defender.hitpoints()) {
00256             hit_type = unit_animation::KILL;
00257         } else if(damage > 0) {
00258             hit_type = unit_animation::HIT;
00259         }else {
00260             hit_type = unit_animation::MISS;
00261         }
00262         animator.add_animation(&attacker,"attack",att->first,damage,true,false,text_2,display::rgb(0,255,0),hit_type,&attack,secondary_attack,swing);
00263         animator.add_animation(&defender,"defend",def->first,damage,true,false,text  ,display::rgb(255,0,0),hit_type,&attack,secondary_attack,swing);
00264 
00265         if(leader_loc.valid() && leader_loc != att->first && leader_loc != def->first){
00266             leader = units.find(leader_loc);
00267             leader->second.set_facing(leader_loc.get_relative_dir(a));
00268             assert(leader != units.end());
00269             animator.add_animation(&leader->second,"leading",leader_loc,damage,true,false,"",0,hit_type,&attack,secondary_attack,swing);
00270         }
00271     }
00272 
00273     animator.start_animations();
00274     animator.wait_for_end();
00275 
00276     if(leader_loc.valid() && leader_loc != att->first && leader_loc != def->first) leader->second.set_standing(leader_loc);
00277     att->second.set_standing(a);
00278     def->second.set_standing(b);
00279 }
00280 
00281 
00282 void unit_recruited(gamemap::location& loc)
00283 {
00284     game_display* disp = game_display::get_singleton();
00285     if(!disp || disp->video().update_locked() ||disp->fogged(loc)) return;
00286     unit_map::iterator u = disp->get_units().find(loc);
00287     if(u == disp->get_units().end()) return;
00288 
00289     u->second.set_hidden(true);
00290     disp->scroll_to_tile(loc,game_display::ONSCREEN);
00291     disp->draw();
00292     u->second.set_hidden(false);
00293     u->second.set_facing(static_cast<gamemap::location::DIRECTION>(rand()%gamemap::location::NDIRECTIONS));
00294     unit_animator animator;
00295     animator.add_animation(&u->second,"recruited",loc);
00296     animator.start_animations();
00297     animator.wait_for_end();
00298     u->second.set_standing(loc);
00299     if (loc==disp->mouseover_hex()) disp->invalidate_unit();
00300 }
00301 
00302 void unit_healing(unit& healed,gamemap::location& healed_loc, std::vector<unit_map::iterator> healers, int healing)
00303 {
00304     game_display* disp = game_display::get_singleton();
00305     if(!disp || disp->video().update_locked() || disp->fogged(healed_loc)) return;
00306     if(healing==0) return;
00307     // This is all the pretty stuff.
00308     disp->scroll_to_tile(healed_loc, game_display::ONSCREEN);
00309     disp->display_unit_hex(healed_loc);
00310     unit_animator animator;
00311 
00312     for(std::vector<unit_map::iterator>::iterator heal_anim_it = healers.begin(); heal_anim_it != healers.end(); ++heal_anim_it) {
00313         (*heal_anim_it)->second.set_facing((*heal_anim_it)->first.get_relative_dir(healed_loc));
00314         animator.add_animation(&(*heal_anim_it)->second,"healing",(*heal_anim_it)->first,healing);
00315     }
00316     if (healing < 0) {
00317         animator.add_animation(&healed,"poisoned",healed_loc,-healing,false,false,lexical_cast<std::string>(-healing), display::rgb(255,0,0));
00318     } else {
00319         animator.add_animation(&healed,"healed",healed_loc,healing,false,false,lexical_cast<std::string>(healing), display::rgb(0,255,0));
00320     }
00321     animator.start_animations();
00322     animator.wait_for_end();
00323 
00324     healed.set_standing(healed_loc);
00325     for(std::vector<unit_map::iterator>::iterator heal_sanim_it = healers.begin(); heal_sanim_it != healers.end(); ++heal_sanim_it) {
00326         (*heal_sanim_it)->second.set_standing((*heal_sanim_it)->first);
00327     }
00328 
00329 }
00330 
00331 } // end unit_display namespace

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