src/weapon/weapon.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *  Wormux is a convivial mass murder game.
00003  *  Copyright (C) 2001-2004 Lawrence Azzoug.
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00018  ******************************************************************************
00019  * Virtual class to handle weapon in wormux.
00020  * Weapon projectile are handled in WeaponLauncher (see launcher.cpp and launcher.h).
00021  *****************************************************************************/
00022 
00023 #include "weapon.h"
00024 #include <SDL.h>
00025 #include <SDL_rotozoom.h>
00026 #include <SDL_gfxPrimitives.h>
00027 #include <iostream>
00028 #include <sstream>
00029 #include "explosion.h"
00030 #include "../character/move.h"
00031 #include "../game/time.h"
00032 #include "../game/game_loop.h"
00033 #include "../graphic/video.h"
00034 #include "../graphic/sprite.h"
00035 #include "../gui/progress_bar.h"
00036 #include "../include/app.h"
00037 #include "../include/action_handler.h"
00038 #include "../graphic/font.h"
00039 #include "../interface/interface.h"
00040 #include "../map/camera.h"
00041 #include "../object/objects_list.h"
00042 #include "../team/macro.h"
00043 #include "../team/team.h"
00044 #include "../tool/debug.h"
00045 #include "../tool/i18n.h"
00046 #include "../tool/math_tools.h"
00047 #include "../tool/point.h"
00048 #include "../tool/resource_manager.h"
00049 #include "../tool/xml_document.h"
00050 
00051 const int INFINITE_AMMO = -1;
00052 const uint MAX_TIME_LOADING = 2000;
00053 
00054 const uint UNIT_BOX_WIDTH = 22;
00055 const uint UNIT_BOX_HEIGHT = 20;
00056 const uint UNIT_BOX_GAP = 6;
00057 
00058 const uint WEAPON_BOX_BUTTON_DX = 20;
00059 const uint WEAPON_BOX_BUTTON_DY = 50;
00060 
00061 const uint ANIM_DISPLAY_TIME = 400;
00062 
00063 extern WeaponStrengthBar weapon_strength_bar;
00064 
00065 
00066 Weapon::Weapon(Weapon_type type,
00067                const std::string &id,
00068                EmptyWeaponConfig * params,
00069                weapon_visibility_t visibility)
00070 {
00071   m_type = type;
00072   m_id = id;
00073 
00074   m_is_active = false;
00075 
00076   m_time_anim_begin = Time::GetInstance()->Read();
00077   m_initial_nb_ammo = INFINITE_AMMO;
00078   m_initial_nb_unit_per_ammo = 1;
00079   use_unit_on_first_shoot = true;
00080   can_be_used_on_closed_map = true;
00081 
00082   m_strength = 0;
00083   m_first_time_loading = 0;
00084   m_last_fire_time = 0;
00085   m_fire_remanence_time = 100;
00086   max_strength = min_angle = max_angle = 0;
00087   use_flipping = true;
00088 
00089   override_keys = false ;
00090   force_override_keys = false;
00091 
00092   origin = weapon_origin_HAND;
00093 
00094   m_can_change_weapon = false;
00095 
00096   m_visibility = visibility;
00097   m_unit_visibility = ALWAYS_VISIBLE;
00098 
00099   m_image = NULL;
00100   m_weapon_fire = NULL;
00101 
00102   channel_load = -1;
00103 
00104   if (!use_flipping and !EgalZero(min_angle - max_angle))
00105     use_flipping = true;
00106 
00107   extra_params = params;
00108 
00109   if (m_visibility != NEVER_VISIBLE)
00110   {
00111     m_image = new Sprite( resource_manager.LoadImage(weapons_res_profile, m_id));
00112     if(!EgalZero(min_angle - max_angle))
00113       m_image->cache.EnableLastFrameCache();
00114   }
00115 
00116   icon = new Sprite(resource_manager.LoadImage(weapons_res_profile,m_id+"_ico"));
00117 
00118   mouse_character_selection = true;
00119 }
00120 
00121 Weapon::~Weapon()
00122 {
00123   if(extra_params) delete extra_params;
00124   if(icon) delete icon;
00125 }
00126 
00127 void Weapon::p_Select ()
00128 {
00129   m_last_fire_time = 0;
00130 }
00131 void Weapon::p_Deselect () {}
00132 void Weapon::HandleKeyEvent(Action::Action_t action, Keyboard::Key_Event_t event_type) {}
00133 
00134 void Weapon::Select()
00135 {
00136   MSG_DEBUG("weapon", "Select %s", m_name.c_str());
00137 
00138   m_time_anim_begin = Time::GetInstance()->Read();
00139   m_is_active = false;
00140   m_strength = 0;
00141   ActiveTeam().ResetNbUnits();
00142 
00143   ActiveCharacter().SetWeaponClothe();
00144 
00145   // is there a crosshair ?
00146   if (!EgalZero(min_angle - max_angle))
00147     ActiveTeam().crosshair.enable = true;
00148 
00149   p_Select();
00150 
00151   if (max_strength == 0) return ;
00152 
00153   // prepare the strength bar
00154   weapon_strength_bar.InitVal (0, 0, uint(max_strength*100));
00155 
00156   // init stamp on the stength_bar
00157   double val = ActiveCharacter().previous_strength;
00158   weapon_strength_bar.ResetTag();
00159   if (0 < val && val < max_strength)
00160   weapon_strength_bar.AddTag (uint(val*100), primary_red_color);
00161 }
00162 
00163 void Weapon::Deselect()
00164 {
00165   ActiveTeam().crosshair.enable = false;
00166   MSG_DEBUG("weapon", "Deselect %s", m_name.c_str());
00167   p_Deselect();
00168 }
00169 
00170 void Weapon::Manage()
00171 {
00172   if (!m_is_active)
00173     return ;
00174 
00175   Refresh();
00176 
00177   GameLoop * game_loop = GameLoop::GetInstance();
00178 
00179   if (game_loop->ReadState() != GameLoop::PLAYING)
00180     return;
00181 
00182   if ( (ActiveTeam().ReadNbUnits() == 0) )
00183     {
00184       Deselect();
00185 
00186       if (m_can_change_weapon)
00187         Select();
00188       else
00189         game_loop->SetState(GameLoop::HAS_PLAYED);
00190     }
00191 }
00192 
00193 bool Weapon::CanChangeWeapon() const
00194 {
00195   if ( !ActiveTeam().IsLocal() )
00196     return false;
00197 
00198   if ( (ActiveTeam().ReadNbUnits() != m_initial_nb_unit_per_ammo) &&
00199        (m_can_change_weapon == false))
00200     return false;
00201 
00202   return true;
00203 }
00204 
00205 void Weapon::NewActionShoot() const
00206 {
00207   assert(ActiveTeam().IsLocal() || ActiveTeam().IsLocalAI());
00208   Action a_begin_sync(Action::ACTION_SYNC_BEGIN);
00209   network.SendAction(&a_begin_sync);
00210   SendCharacterPosition();
00211   Action * shoot = new Action(Action::ACTION_SHOOT,
00212                               m_strength,
00213                               ActiveCharacter().GetAbsFiringAngle());
00214   shoot->StoreActiveCharacter();
00215   ActionHandler::GetInstance()->NewAction(shoot);
00216   Action a_end_sync(Action::ACTION_SYNC_END);
00217   network.SendAction(&a_end_sync);
00218 
00219 }
00220 
00221 void Weapon::PrepareShoot(double strength, double angle)
00222 {
00223   MSG_DEBUG("weapon_shoot", "Try to shoot");
00224   ActiveCharacter().SetFiringAngle(angle);
00225   m_strength = strength;
00226   StopLoading();
00227 
00228   ActiveCharacter().PrepareShoot();
00229 }
00230 
00231 bool Weapon::Shoot()
00232 {
00233   MSG_DEBUG("weapon_shoot", "Enough ammo ? %d", EnoughAmmo() );
00234   MSG_DEBUG("weapon_shoot", "Enough ammo unit ? %d", EnoughAmmoUnit() );
00235   MSG_DEBUG("weapon_shoot", "Use unit on 1st shoot ? %d", use_unit_on_first_shoot );
00236 
00237 
00238   {
00239     // WARNING: The following commented code is wrong! Please see explanation following
00240     //   if (!EnoughAmmo()
00241     //       || (use_unit_on_first_shoot && !EnoughAmmoUnit()))
00242     //     return false;
00243 
00244 
00245     // Gentildemon : YES the following code seems strange!
00246     // BUT when have only one ammo left, you shoot, then nb_ammo == 0
00247     // then you need to be able to use the left ammo units
00248 
00249     if (use_unit_on_first_shoot && !EnoughAmmoUnit())
00250       return false;
00251 
00252     if (!EnoughAmmo())
00253       if ( ! (ActiveTeam().ReadNbAmmos() == 0
00254               && use_unit_on_first_shoot && EnoughAmmoUnit()) )
00255         return false;
00256   }
00257 
00258   MSG_DEBUG("weapon_shoot", "Enough ammo");
00259 
00260   if (!p_Shoot()) return false;
00261   m_last_fire_time = Time::GetInstance()->Read();
00262 
00263   MSG_DEBUG("weapon_shoot", "shoot!");
00264 
00265   // Is this the first shoot for this ammo use ?
00266   if (ActiveTeam().ReadNbUnits() == m_initial_nb_unit_per_ammo) {
00267     UseAmmo();
00268   }
00269 
00270   if (use_unit_on_first_shoot){
00271     UseAmmoUnit();
00272   }
00273 
00274   m_is_active = true;
00275 
00276   if (max_strength != 0) ActiveCharacter().previous_strength = m_strength;
00277 
00278   GameLoop::GetInstance()->character_already_chosen = true;
00279 
00280   return true;
00281 }
00282 
00283 // Calcule la position de l'image de l'arme
00284 void Weapon::PosXY (int &x, int &y) const
00285 {
00286   if(origin == weapon_origin_HAND)
00287   {
00288     Point2i handPos = ActiveCharacter().GetHandPosition();
00289     y = handPos.y + position.y;
00290     if (ActiveCharacter().GetDirection() == 1)
00291       x = handPos.x + position.x;
00292     else
00293       x = handPos.x - position.x;
00294 
00295     if(ActiveCharacter().GetDirection()==-1)
00296       x -= m_image->GetWidth();
00297   }
00298   else
00299   if(origin == weapon_origin_OVER)
00300   {
00301     x = ActiveCharacter().GetCenterX() - m_image->GetWidth() / 2 + position.x;
00302     y = ActiveCharacter().GetY()       - m_image->GetHeight()    + position.y;
00303   }
00304   else
00305     assert(false);
00306 }
00307 
00308 const Point2i Weapon::GetGunHolePosition()
00309 {
00310   const Point2i &pos = ActiveCharacter().GetHandPosition();
00311   Point2i hole(pos +  hole_delta);
00312   double dst = pos.Distance(hole);
00313   double angle = pos.ComputeAngle(hole);
00314   return pos + Point2i(static_cast<int>(dst * cos(angle + ActiveCharacter().GetFiringAngle())),
00315                        static_cast<int>(dst * sin(angle + ActiveCharacter().GetFiringAngle())));
00316 }
00317 
00318 bool Weapon::EnoughAmmo() const
00319 {
00320   int ammo = ActiveTeam().ReadNbAmmos();
00321   return ((ammo == INFINITE_AMMO) || (0 < ammo));
00322 }
00323 
00324 void Weapon::UseAmmo()
00325 {
00326   // Use one ammo...
00327   int *ammo = &ActiveTeam().AccessNbAmmos();
00328   if (*ammo != INFINITE_AMMO) (*ammo)--;
00329 
00330   assert (*ammo >= 0 || *ammo == INFINITE_AMMO);
00331 }
00332 
00333 bool Weapon::EnoughAmmoUnit() const
00334 {
00335   int unit = ActiveTeam().ReadNbUnits();
00336   return (unit > 0);
00337 }
00338 
00339 void Weapon::UseAmmoUnit()
00340 {
00341   // Use one ammo unit.
00342   int *unit = &ActiveTeam().AccessNbUnits();
00343   (*unit)--;
00344 
00345   assert (unit >= 0);
00346 }
00347 
00348 int Weapon::ReadInitialNbAmmo() const{
00349   return m_initial_nb_ammo;
00350 }
00351 
00352 int Weapon::ReadInitialNbUnit() const{
00353   return m_initial_nb_unit_per_ammo;
00354 }
00355 
00356 bool Weapon::CanBeUsedOnClosedMap() const{
00357   return can_be_used_on_closed_map;
00358 }
00359 
00360 const std::string& Weapon::GetName() const {
00361   assert (!m_name.empty());
00362   return m_name;
00363 }
00364 
00365 const std::string& Weapon::GetID() const {
00366   assert (!m_name.empty());
00367   return m_id;
00368 }
00369 
00370 Weapon::Weapon_type Weapon::GetType() const {
00371   return m_type;
00372 }
00373 
00374 void Weapon::UpdateStrength(){
00375   if( max_strength == 0 || m_first_time_loading == 0 )
00376     return ;
00377 
00378   uint time = Time::GetInstance()->Read() - m_first_time_loading;
00379   double val = (max_strength * time) / MAX_TIME_LOADING;
00380 
00381   m_strength = BorneDouble (val, 0.0, max_strength);
00382 
00383   weapon_strength_bar.UpdateValue ((int)(m_strength*100));
00384 }
00385 
00386 bool Weapon::IsReady() const{
00387   return EnoughAmmo() ;
00388 }
00389 
00390 void Weapon::InitLoading(){
00391   // no loading for weapon with max_strength = 0
00392   if (max_strength == 0)
00393     return ;
00394 
00395   channel_load = jukebox.Play("share","weapon/load");
00396 
00397   m_first_time_loading = Time::GetInstance()->Read();
00398 
00399   m_strength = 0;
00400 
00401   GameLoop::GetInstance()->character_already_chosen = true;
00402 }
00403 
00404 void Weapon::StopLoading(){
00405   m_first_time_loading = 0 ;
00406 
00407   jukebox.Stop(channel_load);
00408 }
00409 
00410 void Weapon::Draw(){
00411   if(GameLoop::GetInstance()->ReadState() != GameLoop::PLAYING &&
00412      m_last_fire_time + 100 < Time::GetInstance()->Read())
00413     return;
00414 
00415   if (m_last_fire_time + m_fire_remanence_time > Time::GetInstance()->Read())
00416     DrawWeaponFire();
00417   weapon_strength_bar.visible = false;
00418 
00419   switch (m_unit_visibility)
00420     {
00421       case VISIBLE_ONLY_WHEN_ACTIVE:
00422         if (!m_is_active)
00423           break;
00424 
00425       default:
00426         if (m_initial_nb_unit_per_ammo > 1)
00427           DrawUnit(ActiveTeam().ReadNbUnits());
00428     }
00429 
00430   // Do we need to draw strength_bar ? (real draw is done by class Interface
00431   if (max_strength != 0 && IsReady() && !m_is_active)
00432     weapon_strength_bar.visible = true;
00433 
00434   switch (m_visibility)
00435     {
00436       case ALWAYS_VISIBLE:
00437         break ;
00438 
00439       case NEVER_VISIBLE:
00440         return ;
00441 
00442       case VISIBLE_ONLY_WHEN_ACTIVE:
00443         if (!m_is_active)
00444           return ;
00445         break;
00446 
00447       case VISIBLE_ONLY_WHEN_INACTIVE:
00448         if (m_is_active)
00449           return ;
00450         break;
00451 
00452       default:
00453         printf ("Hum... there is a problem !!!\n");
00454         break;
00455     }
00456 
00457   if(ActiveCharacter().IsGhost()
00458   || ActiveCharacter().IsDrowned()
00459   || ActiveCharacter().IsDead())
00460     return;
00461 
00462   // Reset the Sprite:
00463   m_image->SetRotation_rad(0.0);
00464   m_image->Scale(1.0,1.0);
00465 
00466   // rotate weapon if needed
00467   if (!EgalZero(min_angle - max_angle))
00468   {
00469     if(ActiveCharacter().GetDirection() == 1)
00470       m_image->SetRotation_rad (ActiveCharacter().GetFiringAngle());
00471     else
00472       m_image->SetRotation_rad (ActiveCharacter().GetFiringAngle() - M_PI);
00473   }
00474 
00475   // flip image if needed
00476   if (use_flipping)
00477   {
00478     m_image->Scale(ActiveCharacter().GetDirection(), 1.0);
00479   }
00480 
00481   // Calculate position of the image
00482   int x,y;
00483   PosXY (x, y);
00484 
00485   // Animate the display of the weapon:
00486   if( m_time_anim_begin + ANIM_DISPLAY_TIME > Time::GetInstance()->Read())
00487   {
00488     if (!EgalZero(min_angle - max_angle))
00489     {
00490       double angle = m_image->GetRotation_rad();
00491       angle += sin( M_PI_2 * double(Time::GetInstance()->Read() - m_time_anim_begin) /(double) ANIM_DISPLAY_TIME) * 2 * M_PI;
00492       m_image->SetRotation_rad (angle);
00493     }
00494     else
00495     {
00496       float scale = sin( 1.5 * M_PI_2 * double(Time::GetInstance()->Read() - m_time_anim_begin) /(double) ANIM_DISPLAY_TIME) / sin(1.5 * M_PI_2);
00497       m_image->Scale(ActiveCharacter().GetDirection() * scale,scale);
00498 
00499       if(origin == weapon_origin_OVER) PosXY(x,y); //Recompute position to get the icon centered over the skin
00500     }
00501   }
00502 
00503   if ( m_image )
00504     m_image->Blit( AppWormux::GetInstance()->video.window, Point2i(x, y) - camera.GetPosition());
00505 }
00506 
00507 // Draw the weapon fire when firing
00508 void Weapon::DrawWeaponFire()
00509 {
00510   if (m_weapon_fire == NULL) return;
00511   Point2i size = m_weapon_fire->GetSize();
00512   size.x = (ActiveCharacter().GetDirection() == Body::DIRECTION_RIGHT ? 0 : size.x);
00513   size.y /= 2;
00514   m_weapon_fire->SetRotation_rad (ActiveCharacter().GetFiringAngle());
00515   m_weapon_fire->Draw( GetGunHolePosition() - size );
00516 }
00517 
00518 void Weapon::DrawUnit(int unit) const
00519 {
00520   Rectanglei rect;
00521 
00522   std::ostringstream ss;
00523 
00524   ss << unit;
00525 
00526   DrawTmpBoxText(*Font::GetInstance(Font::FONT_SMALL),
00527                  Point2i( ActiveCharacter().GetCenterX(), 
00528                           ActiveCharacter().GetY() - UNIT_BOX_HEIGHT / 2 - UNIT_BOX_GAP )
00529                  - camera.GetPosition(),
00530                  ss.str());
00531 }
00532 
00533 Sprite & Weapon::GetIcon() const
00534 {
00535   return *icon;
00536 }
00537 
00538 bool Weapon::LoadXml(xmlpp::Element * weapon)
00539 {
00540   xmlpp::Element *elem = XmlReader::GetMarker(weapon, m_id);
00541   if (elem == NULL)
00542   {
00543       std::cout << Format(_("No element <%s> found in the xml config file!"),
00544                           m_id.c_str())
00545                 << std::endl;
00546     return false;
00547   }
00548 
00549   xmlpp::Element *pos_elem = XmlReader::GetMarker(elem, "position");
00550   if (pos_elem != NULL) {
00551     // E.g. <position origin="hand" x="-1" y="0" />
00552     std::string origin_xml;
00553     XmlReader::ReadIntAttr (pos_elem, "x", position.x);
00554     XmlReader::ReadIntAttr (pos_elem, "y", position.y);
00555     XmlReader::ReadStringAttr (pos_elem, "origin", origin_xml);
00556     if (origin_xml == "over")
00557       origin = weapon_origin_OVER;
00558     else
00559       origin = weapon_origin_HAND;
00560   }
00561 
00562   pos_elem = XmlReader::GetMarker(elem, "hole");
00563   if (pos_elem != NULL) {
00564     // E.g. <hole dx="-1" dy="0" />
00565     XmlReader::ReadIntAttr(pos_elem, "dx", hole_delta.x);
00566     XmlReader::ReadIntAttr(pos_elem, "dy", hole_delta.y);
00567   }
00568 
00569   XmlReader::ReadInt(elem, "nb_ammo", m_initial_nb_ammo);
00570   XmlReader::ReadInt(elem, "unit_per_ammo", m_initial_nb_unit_per_ammo);
00571 
00572   // max strength
00573   // if max_strength = 0, no strength_bar !
00574   XmlReader::ReadDouble(elem, "max_strength", max_strength);
00575 
00576   // change weapon after ? (for the ninja cord = true)
00577   XmlReader::ReadBool(elem, "change_weapon", m_can_change_weapon);
00578 
00579   // angle of weapon when drawing
00580   // if (min_angle == max_angle) no cross_hair !
00581   // between -90 to 90 degrees
00582   int min_angle_deg = 0, max_angle_deg = 0;
00583   XmlReader::ReadInt(elem, "min_angle", min_angle_deg);
00584   XmlReader::ReadInt(elem, "max_angle", max_angle_deg);
00585   min_angle = static_cast<double>(min_angle_deg) * M_PI / 180.0;
00586   max_angle = static_cast<double>(max_angle_deg) * M_PI / 180.0;
00587 
00588   // Load extra parameters if existing
00589   if (extra_params != NULL) extra_params->LoadXml(elem);
00590 
00591   if (m_visibility != NEVER_VISIBLE && origin == weapon_origin_HAND)
00592     m_image->SetRotation_HotSpot(-position);
00593 
00594   return true;
00595 }
00596 
00597 bool Weapon::IsActive() const{
00598   return m_is_active;
00599 }
00600 
00601 const double Weapon::ReadStrength() const{
00602   return m_strength;
00603 }
00604 
00605 bool Weapon::IsLoading() const{
00606   return m_first_time_loading;
00607 }
00608 
00609 void Weapon::ChooseTarget(Point2i mouse_pos){
00610 }
00611 
00612 void Weapon::SignalTurnEnd(){
00613 }
00614 
00615 void Weapon::ActionUp(){ //called by mousse.cpp when mousewhellup
00616 }
00617 
00618 
00619 void Weapon::ActionDown(){//called by mousse.cpp when mousewhelldown
00620 }
00621 
00622 void Weapon::ActionStopUse()
00623 {
00624   assert(false);
00625 }

Generated on Mon Jan 1 13:11:00 2007 for Wormux by  doxygen 1.4.7