src/character/character.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  * Refresh d'un ver de terre.
00020  *****************************************************************************/
00021 
00022 #include "character.h"
00023 #include <SDL.h>
00024 #include <sstream>
00025 #include <iostream>
00026 #include "body.h"
00027 #include "../team/macro.h"
00028 #include "move.h"
00029 #include "../game/game.h"
00030 #include "../game/game_mode.h"
00031 #include "../game/game_loop.h"
00032 #include "../game/time.h"
00033 #include "../game/config.h"
00034 #include "../graphic/text.h"
00035 #include "../graphic/font.h"
00036 #include "../include/action_handler.h"
00037 #include "../include/app.h"
00038 #include "../include/constant.h"
00039 #include "../interface/interface.h"
00040 #include "../interface/cursor.h"
00041 #include "../map/camera.h"
00042 #include "../map/map.h"
00043 #include "../map/water.h"
00044 #include "../network/network.h"
00045 #include "../network/randomsync.h"
00046 #include "../sound/jukebox.h"
00047 #include "../tool/debug.h"
00048 #include "../tool/random.h"
00049 #include "../tool/math_tools.h"
00050 #include "../weapon/suicide.h"
00051 #include "../weapon/crosshair.h"
00052 #include "../weapon/explosion.h"
00053 
00054 const uint HAUT_FONT_MIX = 13;
00055 
00056 // Space between the name, the skin and the energy bar
00057 const uint ESPACE = 3; // pixels
00058 const uint do_nothing_timeout = 5000;
00059 const double MIN_SPEED_TO_FLY = 15.0;
00060 
00061 // Pause for the animation
00062 #ifdef DEBUG
00063 //#define ANIME_VITE
00064 #endif
00065 #ifdef ANIME_VITE
00066   const uint ANIM_PAUSE_MIN = 100;
00067   const uint ANIM_PAUSE_MAX = 150;
00068 #else
00069   const uint ANIM_PAUSE_MIN = 5*1000;
00070   const uint ANIM_PAUSE_MAX = 60*1000;
00071 #endif
00072 
00073 #ifdef DEBUG
00074 //#define DEBUG_STATS
00075 #endif
00076 
00077 // Energy bar
00078 const uint LARG_ENERGIE = 40;
00079 const uint HAUT_ENERGIE = 6;
00080 
00081 Body * Character::GetBody() const
00082 {
00083   return body;
00084 }
00085 
00086 void Character::SetBody(Body* char_body)
00087 {
00088   body = char_body;
00089   body->SetOwner(this);
00090   SetClothe("normal");
00091   SetMovement("walk");
00092 
00093   SetDirection(randomSync.GetBool() ? Body::DIRECTION_LEFT : Body::DIRECTION_RIGHT);
00094   body->SetFrame(0);
00095   SetSize(body->GetSize());
00096 }
00097 
00098 Character::Character (Team& my_team, const std::string &name, Body *char_body) :
00099   PhysicalObj("character"), m_team(my_team), bubble_engine(500)
00100 {
00101   SetCollisionModel(false, true, true);
00102   /* body stuff */
00103   assert(char_body);
00104   SetBody(char_body);
00105 
00106   step_sound_played = true;
00107   pause_bouge_dg = 0;
00108   previous_strength = 0;
00109   channel_step = -1;
00110   hidden = false;
00111   do_nothing_time = 0;
00112   m_allow_negative_y = true;
00113   animation_time = Time::GetInstance()->Read() + randomObj.GetLong(ANIM_PAUSE_MIN,ANIM_PAUSE_MAX);
00114   prepare_shoot = false;
00115   back_jumping = false;
00116   death_explosion = true;
00117   firing_angle = 0.;
00118 
00119   // Damage count
00120   damage_other_team = 0;
00121   damage_own_team = 0;
00122   max_damage = 0;
00123   current_total_damage = 0;
00124 
00125   // Disease damage
00126   disease_damage_per_turn = 0;
00127   disease_duration = 0;
00128 
00129   // Survivals
00130   survivals = 0;
00131 
00132   character_name = name;
00133 
00134   ResetConstants();
00135 
00136   // Name Text object
00137   if (Config::GetInstance()->GetDisplayNameCharacter())
00138     name_text = new Text(character_name);
00139   else
00140     name_text = NULL;
00141 
00142   // Energy
00143   life_points = GameMode::GetInstance()->character.init_energy;
00144   energy_bar.InitVal (GameMode::GetInstance()->character.init_energy,
00145                       0,
00146                       GameMode::GetInstance()->character.init_energy);
00147   energy_bar.InitPos (0,0, LARG_ENERGIE, HAUT_ENERGIE);
00148   energy_bar.SetBorderColor( black_color );
00149   energy_bar.SetBackgroundColor( gray_color );
00150 
00151   SetEnergy(GameMode::GetInstance()->character.init_energy);
00152   lost_energy = 0;
00153   MSG_DEBUG("character", "Load character %s", character_name.c_str());
00154 }
00155 
00156 Character::Character (const Character& acharacter) : PhysicalObj(acharacter),
00157                                                      m_team(acharacter.m_team),
00158                                                      bubble_engine(250)
00159 {
00160   character_name       = acharacter.character_name;
00161   step_sound_played    = acharacter.step_sound_played;
00162   prepare_shoot        = acharacter.prepare_shoot;
00163   back_jumping         = acharacter.back_jumping;
00164   damage_other_team    = acharacter.damage_other_team;
00165   damage_own_team      = acharacter.damage_own_team;
00166   max_damage           = acharacter.max_damage;
00167   death_explosion      = acharacter.death_explosion;
00168   firing_angle         = acharacter.firing_angle;
00169   current_total_damage = acharacter.current_total_damage;
00170   energy_bar           = acharacter.energy_bar;
00171   survivals            = acharacter.survivals;
00172   pause_bouge_dg       = acharacter.pause_bouge_dg;
00173   do_nothing_time      = acharacter.do_nothing_time;
00174   animation_time       = acharacter.animation_time;
00175   lost_energy          = acharacter.lost_energy;
00176   hidden               = acharacter.hidden;
00177   channel_step         = acharacter.channel_step ;
00178   previous_strength    = acharacter.previous_strength;
00179   disease_damage_per_turn = acharacter.disease_damage_per_turn;
00180   disease_duration     = acharacter.disease_duration;
00181 
00182   if (acharacter.body)
00183     SetBody(new Body(acharacter.body));
00184   if(acharacter.name_text)
00185     name_text = new Text(*acharacter.name_text);
00186 }
00187 
00188 Character::~Character()
00189 {
00190   MSG_DEBUG("character", "Unload character %s", character_name.c_str());
00191   if(body)
00192     delete body;
00193   if(name_text)
00194     delete name_text;
00195   body      = NULL;
00196   name_text = NULL;
00197 }
00198 
00199 void Character::SignalDrowning()
00200 {
00201   SetEnergy(0);
00202   SetMovement("drowned");
00203 
00204   jukebox.Play (GetTeam().GetSoundProfile(),"sink");
00205   GameLoop::GetInstance()->SignalCharacterDeath (this);
00206 }
00207 
00208 // Si un ver devient un fantome, il meurt ! Signale sa mort
00209 void Character::SignalGhostState (bool was_dead)
00210 {
00211   // Report to damage performer this character lost all of its energy
00212   ActiveCharacter().MadeDamage(GetEnergy(), *this);
00213 
00214   MSG_DEBUG("character", "ghost");
00215 
00216   // Signal the death
00217   if (!was_dead) GameLoop::GetInstance()->SignalCharacterDeath (this);
00218 }
00219 
00220 void Character::SetDirection (Body::Direction_t nv_direction)
00221 {
00222   body->SetDirection(nv_direction);
00223   uint l,r,t,b;
00224   body->GetTestRect(l,r,t,b);
00225   SetTestRect(l,r,t,b);
00226   m_team.crosshair.Refresh(GetFiringAngle());
00227 }
00228 
00229 void Character::DrawEnergyBar(int dy)
00230 {
00231   if( IsDead() )
00232         return;
00233 
00234   energy_bar.DrawXY( Point2i( GetCenterX() - energy_bar.GetWidth() / 2, GetY() + dy)
00235                   - camera.GetPosition() );
00236 }
00237 
00238 void Character::DrawName (int dy) const
00239 {
00240   if(IsDead()) return;
00241 
00242   const int x =  GetCenterX();
00243   const int y = GetY()+dy;
00244 
00245   if (Config::GetInstance()->GetDisplayNameCharacter())
00246   {
00247     name_text->DrawCenterTopOnMap(x,y);
00248   }
00249 }
00250 
00251 void Character::SetEnergyDelta (int delta, bool do_report)
00252 {
00253   // If already dead, do nothing
00254   if (IsDead()) return;
00255 
00256   // Report damage to damage performer
00257   if (do_report)
00258     ActiveCharacter().MadeDamage(-delta, *this);
00259 
00260   uint saved_life_points = GetEnergy();
00261 
00262   // Update energy
00263   SetEnergy(GetEnergy() + delta);
00264 
00265   if(IsDead()) return;
00266 
00267   // Compute energy lost
00268   if (delta < 0) {
00269     lost_energy += (int)GetEnergy() - (int)saved_life_points;
00270 
00271     if ( lost_energy > -33 )
00272       jukebox.Play (GetTeam().GetSoundProfile(), "injured_light");
00273     else if ( lost_energy > -66 )
00274       jukebox.Play (GetTeam().GetSoundProfile(), "injured_medium");
00275     else
00276       jukebox.Play (GetTeam().GetSoundProfile(), "injured_high");
00277   } else
00278     lost_energy = 0;
00279 
00280   // "Friendly fire !!"
00281   if ( !IsActiveCharacter() && ActiveTeam().IsSameAs(m_team) )
00282     jukebox.Play (GetTeam().GetSoundProfile(), "friendly_fire");
00283 }
00284 
00285 void Character::SetEnergy(int new_energy)
00286 {
00287   if(!network.IsLocal())
00288   {
00289     if( m_alive == DEAD && new_energy > 0)
00290     {
00291       printf("%s have been resurrected\n", GetName().c_str());
00292       m_alive = ALIVE;
00293       SetClothe("normal");
00294       SetMovement("walk");
00295     }
00296   }
00297 
00298   //assert( m_alive != DEAD );
00299   if(IsDead()) return;
00300 
00301   // Change energy
00302   long energy = BorneLong((int)new_energy, 0,
00303                           GameMode::GetInstance()->character.max_energy);
00304   life_points = energy;
00305   energy_bar.Actu (energy);
00306 
00307   // Dead character ?
00308   if (GetEnergy() <= 0) Die();
00309 }
00310 
00311 bool Character::GotInjured() const
00312 {
00313   return lost_energy < 0;
00314 }
00315 
00316 void Character::DisableDeathExplosion()
00317 {
00318   death_explosion = false;
00319 }
00320 
00321 void Character::Die()
00322 {
00323   assert (m_alive == ALIVE || m_alive == DROWNED);
00324 
00325   MSG_DEBUG("character", "Dying");
00326 
00327   if (m_alive != DROWNED)
00328   {
00329     m_alive = DEAD;
00330 
00331     SetEnergy(0);
00332 
00333     jukebox.Play(GetTeam().GetSoundProfile(),"death");
00334     SetClothe("dead");
00335     SetMovement("dead");
00336 
00337     if(death_explosion)
00338       ApplyExplosion(GetCenter(), GameMode::GetInstance()->death_explosion_cfg);
00339     assert(IsDead());
00340 
00341     // Signal the death
00342     GameLoop::GetInstance()->SignalCharacterDeath (this);
00343   }
00344 }
00345 
00346 bool Character::IsDiseased() const
00347 {
00348   return disease_duration > 0 && !IsDead();
00349 }
00350 
00351 void Character::SetDiseaseDamage(const uint damage_per_turn, const uint duration)
00352 {
00353   disease_damage_per_turn = damage_per_turn;
00354   disease_duration = duration;
00355 }
00356 
00357 // Keep almost 1 in energy
00358 uint Character::GetDiseaseDamage() const
00359 {
00360   if (disease_damage_per_turn < static_cast<uint>(GetEnergy()))
00361     return disease_damage_per_turn;
00362   return GetEnergy() - 1;
00363 }
00364 
00365 uint Character::GetDiseaseDuration() const
00366 {
00367   return disease_duration;
00368 }
00369 
00370 void Character::DecDiseaseDuration()
00371 {
00372   if (disease_duration > 0) disease_duration--;
00373   else disease_damage_per_turn = 0;
00374 }
00375 
00376 void Character::Draw()
00377 {
00378   if (hidden) return;
00379 
00380   // Gone in another world ?
00381   if (IsGhost()) return;
00382 
00383   bool dessine_perte = (lost_energy != 0);
00384   if ((&ActiveCharacter() == this
00385     && GameLoop::GetInstance()->ReadState() != GameLoop::END_TURN)
00386       //&& (game_loop.ReadState() != jeuANIM_FIN_TOUR)
00387     || IsDead()
00388      )
00389     dessine_perte = false;
00390 
00391   if(GameLoop::GetInstance()->ReadState() == GameLoop::END_TURN && body->IsWalking())
00392     body->ResetWalk();
00393 
00394   if(Time::GetInstance()->Read() > animation_time && &ActiveCharacter()!=this && !IsDead()
00395   && body->GetMovement().substr(0,9) != "animation"
00396   &&  body->GetClothe().substr(0,9) != "animation")
00397   {
00398     body->PlayAnimation();
00399     animation_time = Time::GetInstance()->Read() + body->GetMovementDuration() + randomObj.GetLong(ANIM_PAUSE_MIN,ANIM_PAUSE_MAX);
00400   }
00401 
00402   // Stop the animation or the black skin if we are playing
00403   if(&ActiveCharacter() == this
00404   &&(body->GetMovement().substr(0,9) == "animation"
00405   || body->GetClothe().substr(0,9) == "animation"
00406   || body->GetClothe() == "black"))
00407   {
00408     SetClothe("normal");
00409     SetMovement("walk");
00410   }
00411 
00412   // Stop flying if we don't go fast enough
00413   double n, a;
00414   GetSpeed(n, a);
00415   if(body->GetMovement() == "fly" && n < MIN_SPEED_TO_FLY)
00416     SetMovement("walk");
00417 
00418 
00419   Point2i pos = GetPosition();
00420 
00421   if(prepare_shoot)
00422   {
00423     body->Build(); // Refresh the body
00424     if(body->GetMovement() != "weapon-" + ActiveTeam().GetWeapon().GetID() + "-begin-shoot")
00425     {
00426       // if the movement is finnished, shoot !
00427       DoShoot();
00428       prepare_shoot = false;
00429     }
00430   }
00431 
00432   body->Draw(pos);
00433 
00434    // Draw energy bar
00435   int dy = -ESPACE;
00436   bool est_ver_actif = (this == &ActiveCharacter());
00437   Config * config = Config::GetInstance();
00438   bool display_energy = config->GetDisplayEnergyCharacter();
00439   display_energy &= !est_ver_actif || (GameLoop::GetInstance()->ReadState() != GameLoop::PLAYING);
00440   display_energy |= dessine_perte;
00441   display_energy &= !IsDead();
00442   if (display_energy)
00443   {
00444     dy -= HAUT_ENERGIE;
00445     DrawEnergyBar (dy);
00446     dy -= ESPACE;
00447   }
00448 
00449   // Draw name
00450   if (config->GetDisplayNameCharacter() && !est_ver_actif)
00451   {
00452     dy -= HAUT_FONT_MIX;
00453     DrawName (dy);
00454     dy -= ESPACE;
00455   }
00456 
00457   // Draw lost energy
00458   if (dessine_perte)
00459   {
00460     std::ostringstream ss;
00461     ss << lost_energy;
00462     dy -= HAUT_FONT_MIX;
00463     (*Font::GetInstance(Font::FONT_SMALL)).WriteCenterTop (
00464                         GetPosition() - camera.GetPosition() + Point2i( GetWidth()/2, dy),
00465                         ss.str(), white_color);
00466   }
00467 
00468 }
00469 
00470 void Character::Jump(double strength, double angle /*in radian */)
00471 {
00472   do_nothing_time = Time::GetInstance()->Read();
00473 
00474   if (!CanJump() && ActiveTeam().IsLocal()) return;
00475 
00476   SetMovement("jump");
00477 
00478   // Jump !
00479   if (GetDirection() == Body::DIRECTION_LEFT) angle = InverseAngle(angle);
00480   SetSpeed (strength, angle);
00481 }
00482 
00483 void Character::Jump()
00484 {
00485   MSG_DEBUG("character", "Jump");
00486   jukebox.Play (ActiveTeam().GetSoundProfile(), "jump");
00487   Jump(GameMode::GetInstance()->character.jump_strength,
00488        GameMode::GetInstance()->character.jump_angle);
00489 }
00490 
00491 void Character::HighJump()
00492 {
00493   MSG_DEBUG("character", "HighJump");
00494   jukebox.Play (ActiveTeam().GetSoundProfile(), "superjump");
00495   Jump(GameMode::GetInstance()->character.super_jump_strength,
00496        GameMode::GetInstance()->character.super_jump_angle);
00497 }
00498 
00499 void Character::BackJump()
00500 {
00501   MSG_DEBUG("character", "BackJump");
00502   back_jumping = true;
00503   jukebox.Play (ActiveTeam().GetSoundProfile(), "jump");
00504   Jump(GameMode::GetInstance()->character.back_jump_strength,
00505        GameMode::GetInstance()->character.back_jump_angle);
00506 }
00507 
00508 void Character::PrepareShoot()
00509 {
00510   SetMovementOnce("weapon-" + ActiveTeam().GetWeapon().GetID() + "-begin-shoot");
00511   if(body->GetMovement() != "weapon-" + ActiveTeam().GetWeapon().GetID() + "-begin-shoot")
00512   {
00513     // If a movement is defined for this weapon, just shoot
00514     DoShoot();
00515   }
00516   else
00517     prepare_shoot = true;
00518 }
00519 
00520 void Character::DoShoot()
00521 {
00522   SetMovementOnce("weapon-" + ActiveTeam().GetWeapon().GetID() + "-end-shoot");
00523   ActiveTeam().AccessWeapon().Shoot();
00524 }
00525 
00526 void Character::HandleShoot(Keyboard::Key_Event_t event_type)
00527 {
00528   if(prepare_shoot)
00529     return;
00530 
00531   switch (event_type) {
00532     case Keyboard::KEY_PRESSED:
00533       if (ActiveTeam().GetWeapon().max_strength == 0)
00534         ActiveTeam().GetWeapon().NewActionShoot();
00535       else
00536       if ( (GameLoop::GetInstance()->ReadState() == GameLoop::PLAYING)
00537          && ActiveTeam().GetWeapon().IsReady() )
00538         ActiveTeam().AccessWeapon().InitLoading();
00539       break ;
00540 
00541     case Keyboard::KEY_RELEASED:
00542       if (ActiveTeam().GetWeapon().IsLoading())
00543         ActiveTeam().GetWeapon().NewActionShoot();
00544       break ;
00545 
00546     case Keyboard::KEY_REFRESH:
00547       if ( ActiveTeam().GetWeapon().IsLoading() )
00548         {
00549           // Strength == max strength -> Fire !!!
00550           if (ActiveTeam().GetWeapon().ReadStrength() >=
00551               ActiveTeam().GetWeapon().max_strength)
00552             ActiveTeam().GetWeapon().NewActionShoot();
00553           else
00554             {
00555               // still pressing the Space key
00556               ActiveTeam().AccessWeapon().UpdateStrength();
00557             }
00558         }
00559       break ;
00560 
00561     default:
00562       break ;
00563   }
00564 }
00565 
00566 void Character::HandleKeyEvent(Action::Action_t action, Keyboard::Key_Event_t event_type)
00567 {
00568   // The character cannot move anymove if the turn is over...
00569   if (GameLoop::GetInstance()->ReadState() == GameLoop::END_TURN)
00570     return ;
00571 
00572   if (ActiveCharacter().IsDead())
00573     return;
00574 
00575   if (action == Action::ACTION_SHOOT)
00576     {
00577       HandleShoot(event_type);
00578       do_nothing_time = Time::GetInstance()->Read();
00579       CharacterCursor::GetInstance()->Hide();
00580       return;
00581     }
00582 
00583   ActionHandler * action_handler = ActionHandler::GetInstance();
00584 
00585   if(action <= Action::ACTION_NEXT_CHARACTER)
00586     {
00587       switch (event_type)
00588       {
00589         case Keyboard::KEY_REFRESH:
00590           switch (action) {
00591             case Action::ACTION_MOVE_LEFT:
00592               if(ActiveCharacter().IsImmobile())
00593                 MoveCharacterLeft(ActiveCharacter());
00594               HideGameInterface();
00595               return;
00596             case Action::ACTION_MOVE_RIGHT:
00597               if(ActiveCharacter().IsImmobile())
00598                 MoveCharacterRight(ActiveCharacter());
00599               HideGameInterface();
00600               return;
00601             default:
00602               break ;
00603           }
00604           //no break!! -> it's normal
00605         case Keyboard::KEY_PRESSED:
00606           switch (action)
00607           {
00608             case Action::ACTION_UP:
00609               HideGameInterface();
00610               if(ActiveCharacter().IsImmobile())
00611               {
00612                 if (ActiveTeam().crosshair.enable)
00613                 {
00614                   do_nothing_time = Time::GetInstance()->Read();
00615                   CharacterCursor::GetInstance()->Hide();
00616                   action_handler->NewAction (new Action(Action::ACTION_UP));
00617                 }
00618               }
00619               break ;
00620 
00621             case Action::ACTION_DOWN:
00622               HideGameInterface();
00623               if(ActiveCharacter().IsImmobile())
00624               {
00625                 if (ActiveTeam().crosshair.enable)
00626                 {
00627                   do_nothing_time = Time::GetInstance()->Read();
00628                   CharacterCursor::GetInstance()->Hide();
00629                   action_handler->NewAction (new Action(Action::ACTION_DOWN));
00630                 }
00631               }
00632               break ;
00633             case Action::ACTION_MOVE_LEFT:
00634             case Action::ACTION_MOVE_RIGHT:
00635               HideGameInterface();
00636               InitMouvementDG(PAUSE_MOVEMENT);
00637               body->StartWalk();
00638               break;
00639             // WARNING!! ALL JUMP KEYS NEEDS TO BE PROCESSED AFTER ANY MOVEMENT KEYS
00640             // OTHERWISE, THE JUMP ACTION WILL BYPASSED ON DISTANT COMPUTERS BYE THE REFRESH
00641             // OF THE WALK
00642             case Action::ACTION_JUMP:
00643               HideGameInterface();
00644               if(ActiveCharacter().IsImmobile())
00645                 action_handler->NewAction (new Action(Action::ACTION_JUMP));
00646               return ;
00647             case Action::ACTION_HIGH_JUMP:
00648               HideGameInterface();
00649               if(ActiveCharacter().IsImmobile())
00650                 action_handler->NewAction (new Action(Action::ACTION_HIGH_JUMP));
00651               return ;
00652             case Action::ACTION_BACK_JUMP:
00653               HideGameInterface();
00654               if(ActiveCharacter().IsImmobile())
00655                 action_handler->NewAction (new Action(Action::ACTION_BACK_JUMP));
00656               return ;
00657             default:
00658               break;
00659           }
00660           break;
00661 
00662         case Keyboard::KEY_RELEASED:
00663           switch (action) {
00664             case Action::ACTION_MOVE_LEFT:
00665             case Action::ACTION_MOVE_RIGHT:
00666                body->StopWalk();
00667                SendCharacterPosition();
00668                break;
00669             default:
00670                break;
00671             }
00672         default: break;
00673       }
00674     }
00675 }
00676 
00677 void Character::Refresh()
00678 {
00679   if (IsGhost()) return;
00680 
00681   UpdatePosition ();
00682   Time * global_time = Time::GetInstance();
00683 
00684   if(IsDiseased())
00685   {
00686     Point2i bubble_pos = GetPosition();
00687     if(GetDirection() == Body::DIRECTION_LEFT)
00688       bubble_pos.x += GetWidth();
00689     bubble_engine.AddPeriodic(bubble_pos, particle_ILL_BUBBLE, false,
00690                               - M_PI_2 - (float)GetDirection() * M_PI_4, 20.0);
00691   }
00692 
00693   if( IsActiveCharacter() && GameLoop::GetInstance()->ReadState() == GameLoop::PLAYING)
00694   {
00695     if(do_nothing_time + do_nothing_timeout < global_time->Read())
00696       CharacterCursor::GetInstance()->FollowActiveCharacter();
00697   }
00698 
00699   if(body->IsWalking())
00700   {
00701     // Play the step sound only twice during the walking animation
00702     uint frame_nbr = body->GetFrameCount();
00703     uint cur = body->GetFrame();
00704     frame_nbr /= 2;
00705     cur %= frame_nbr;
00706 
00707     if(cur < frame_nbr / 2 && !step_sound_played)
00708     {
00709       step_sound_played = true;
00710       jukebox.Play (GetTeam().GetSoundProfile(),"step");
00711     }
00712 
00713     if(cur > frame_nbr / 2)
00714       step_sound_played = false;
00715   }
00716 
00717   if(back_jumping)
00718   {
00719     assert(&ActiveCharacter() == this);
00720     double rotation;
00721     static double speed_init = GameMode::GetInstance()->character.back_jump_strength *
00722        sin(GameMode::GetInstance()->character.back_jump_angle);
00723 
00724     Point2d speed;
00725     GetSpeedXY(speed);
00726     rotation = -M_PI * speed.y / speed_init;
00727     body->SetRotation(rotation);
00728   }
00729 }
00730 
00731 // Prepare a new turn
00732 void Character::PrepareTurn()
00733 {
00734   HandleMostDamage();
00735   lost_energy = 0;
00736   pause_bouge_dg = Time::GetInstance()->Read();
00737 }
00738 
00739 const Team &Character::GetTeam() const
00740 {
00741   return m_team;
00742 }
00743 
00744 bool Character::MouvementDG_Autorise() const
00745 {
00746   if (!IsImmobile() || IsFalling()) return false;
00747   return pause_bouge_dg < Time::GetInstance()->Read();
00748 }
00749 
00750 bool Character::CanJump() const
00751 {
00752         return MouvementDG_Autorise();
00753 }
00754 
00755 void Character::InitMouvementDG(uint pause)
00756 {
00757   do_nothing_time = Time::GetInstance()->Read();
00758   CharacterCursor::GetInstance()->Hide();
00759   step_sound_played = true;
00760   pause_bouge_dg = Time::GetInstance()->Read()+pause;
00761 }
00762 
00763 bool Character::CanStillMoveDG(uint pause)
00764 {
00765   if(pause_bouge_dg+pause<Time::GetInstance()->Read())
00766   {
00767     pause_bouge_dg += pause;
00768     return true;
00769   }
00770   return false;
00771 }
00772 
00773 // Signal the end of a fall
00774 void Character::SignalCollision()
00775 {
00776   // Do not manage dead characters.
00777   if (IsDead()) return;
00778 
00779   pause_bouge_dg = Time::GetInstance()->Read();
00780   back_jumping = false;
00781   double norme, degat;
00782   Point2d speed_vector;
00783   GameMode * game_mode = GameMode::GetInstance();
00784   SetMovement("walk");
00785   SetMovementOnce("soft-land");
00786 
00787   GetSpeedXY (speed_vector);
00788   norme = speed_vector.Norm();
00789   if (norme > game_mode->safe_fall && speed_vector.y>0.0)
00790   {
00791     norme -= game_mode->safe_fall;
00792     degat = norme * game_mode->damage_per_fall_unit;
00793     SetEnergyDelta (-(int)degat);
00794     GameLoop::GetInstance()->SignalCharacterDamage(this);
00795     SetMovement("walk");
00796     SetMovementOnce("hard-land");
00797   }
00798 }
00799 
00800 void Character::SignalExplosion()
00801 {
00802   if(IsDead()) return;
00803 
00804   double n, a;
00805   GetSpeed(n, a);
00806   SetRebounding(true);
00807   if(n > MIN_SPEED_TO_FLY)
00808     SetMovement("fly");
00809   else
00810   {
00811     SetClotheOnce("black");
00812     SetMovementOnce("black");
00813     if(body->GetClothe() == "black"
00814     && body->GetMovement() != "black")
00815       std::cerr << "Error: the clothe \"black\" of the character " << GetName() << " is set, but the skin have no \"black\" movement !!!" << std::endl;
00816   }
00817   // bug #7056 : When we are hit by an explosion while using ninja rope, we broke the rope.
00818   if (IsActiveCharacter()) {
00819     ActiveTeam().AccessWeapon().Deselect();
00820     // Select the weapon back. If not, we cannot move the crosshair.
00821     ActiveTeam().AccessWeapon().Select();
00822     // End of turn
00823     GameLoop::GetInstance()->SignalCharacterDamage(this);
00824   }
00825 }
00826 
00827 Body::Direction_t Character::GetDirection() const
00828 {
00829   return body->GetDirection();
00830 }
00831 
00832 // End of turn or change of character
00833 void Character::StopPlaying()
00834 {
00835   if(IsDead()) return;
00836   SetClothe("normal");
00837   SetMovement("walk");
00838   body->ResetWalk();
00839   SetRebounding(true);
00840 }
00841 
00842 // Begining of turn or changed to this character
00843 void Character::StartPlaying()
00844 {
00845   assert (!IsGhost());
00846   SetWeaponClothe();
00847   ActiveTeam().crosshair.Draw();
00848  // SetRebounding(false);
00849   ShowGameInterface();
00850   m_team.crosshair.Refresh(GetFiringAngle());
00851 }
00852 
00853 bool Character::IsActiveCharacter() const
00854 {
00855   return this == &ActiveCharacter();
00856 }
00857 
00858 // Hand position
00859 const Point2i & Character::GetHandPosition() const {
00860   return body->GetHandPosition();
00861 }
00862 
00863 double Character::GetFiringAngle() const {
00864   if (GetDirection() == Body::DIRECTION_LEFT)
00865     return InverseAngleRad(firing_angle);
00866   return firing_angle;
00867 }
00868 
00869 double Character::GetAbsFiringAngle() const {
00870   return firing_angle;
00871 }
00872 
00873 void Character::SetFiringAngle(double angle) {
00874   /*while(angle > 2 * M_PI)
00875     angle -= 2 * M_PI;
00876   while(angle <= -2 * M_PI)
00877     angle += 2 * M_PI;*/
00878   angle = BorneDouble(angle, -(ActiveTeam().GetWeapon().GetMaxAngle()),
00879                              -(ActiveTeam().GetWeapon().GetMinAngle()));
00880   firing_angle = angle;
00881   m_team.crosshair.Refresh(GetFiringAngle());
00882 }
00883 
00884 void Character::AddFiringAngle(double angle) {
00885   SetFiringAngle(firing_angle + angle);
00886 }
00887 
00888 void Character::HandleMostDamage()
00889 {
00890   if (current_total_damage > max_damage)
00891   {
00892     max_damage = current_total_damage;
00893   }
00894 #ifdef DEBUG_STATS
00895   std::cerr << m_name << " most damage: " << max_damage << std::endl;
00896 #endif
00897   current_total_damage = 0;
00898 }
00899 
00900 void Character::Hide() { hidden = true; }
00901 void Character::Show() { hidden = false; }
00902 
00903 void Character::MadeDamage(const int Dmg, const Character &other)
00904 {
00905   if (m_team.IsSameAs(other.GetTeam()))
00906   {
00907 #ifdef DEBUG_STATS
00908     std::cerr << m_name << " damaged own team with " << Dmg << std::endl;
00909 #endif
00910     if (Character::IsSameAs(other))
00911       damage_own_team += Dmg;
00912   }
00913   else
00914   {
00915 #ifdef DEBUG_STATS
00916     std::cerr << m_name << " damaged other team with " << Dmg << std::endl;
00917 #endif
00918     damage_other_team += Dmg;
00919   }
00920 
00921   current_total_damage += Dmg;
00922 }
00923 
00924 void Character::SetWeaponClothe()
00925 {
00926   SetClothe("weapon-" + m_team.GetWeapon().GetID());
00927   if(body->GetClothe() != "weapon-" + m_team.GetWeapon().GetID())
00928     SetClothe("normal");
00929 }
00930 
00931 void Character::SetMovement(std::string name)
00932 {
00933   if(IsDead()) return;
00934   MSG_DEBUG("body","Character %s -> SetMovement : %s",character_name.c_str(),name.c_str());
00935   body->SetMovement(name);
00936   uint l,r,t,b;
00937   body->GetTestRect(l,r,t,b);
00938   SetTestRect(l,r,t,b);
00939 }
00940 
00941 void Character::SetMovementOnce(std::string name)
00942 {
00943   if(IsDead()) return;
00944   MSG_DEBUG("body","Character %s -> SetMovementOnce : %s",character_name.c_str(),name.c_str());
00945   body->SetMovementOnce(name);
00946   uint l,r,t,b;
00947   body->GetTestRect(l,r,t,b);
00948   SetTestRect(l,r,t,b);
00949 }
00950 
00951 void Character::SetClothe(std::string name)
00952 {
00953   if(IsDead() && name!="dead") return;
00954   MSG_DEBUG("body","Character %s -> SetClothe : %s",character_name.c_str(),name.c_str());
00955   body->SetClothe(name);
00956 }
00957 
00958 void Character::SetClotheOnce(std::string name)
00959 {
00960   if(IsDead()) return;
00961   MSG_DEBUG("body","Character %s -> SetClotheOnce : %s",character_name.c_str(),name.c_str());
00962   body->SetClotheOnce(name);
00963 }
00964 
00965 uint Character::GetTeamIndex()
00966 {
00967   uint index = 0;
00968   teams_list.FindPlayingById( GetTeam().GetId(), index);
00969   return index;
00970 }
00971 
00972 uint Character::GetCharacterIndex()
00973 {
00974   uint index = 0;
00975   for(Team::iterator it = m_team.begin();
00976                      it != m_team.end() ; ++it, ++index )
00977   {
00978     if( &(*it) == this)
00979       return index;
00980   }
00981   assert(false);
00982   return 0;
00983 }

Generated on Mon Jan 1 13:10:55 2007 for Wormux by  doxygen 1.4.7