00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
00154 weapon_strength_bar.InitVal (0, 0, uint(max_strength*100));
00155
00156
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
00240
00241
00242
00243
00244
00245
00246
00247
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
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
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
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
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
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
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
00463 m_image->SetRotation_rad(0.0);
00464 m_image->Scale(1.0,1.0);
00465
00466
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
00476 if (use_flipping)
00477 {
00478 m_image->Scale(ActiveCharacter().GetDirection(), 1.0);
00479 }
00480
00481
00482 int x,y;
00483 PosXY (x, y);
00484
00485
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);
00500 }
00501 }
00502
00503 if ( m_image )
00504 m_image->Blit( AppWormux::GetInstance()->video.window, Point2i(x, y) - camera.GetPosition());
00505 }
00506
00507
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
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
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
00573
00574 XmlReader::ReadDouble(elem, "max_strength", max_strength);
00575
00576
00577 XmlReader::ReadBool(elem, "change_weapon", m_can_change_weapon);
00578
00579
00580
00581
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
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(){
00616 }
00617
00618
00619 void Weapon::ActionDown(){
00620 }
00621
00622 void Weapon::ActionStopUse()
00623 {
00624 assert(false);
00625 }