src/ai/ai_shoot_module.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  * Artificial intelligence Shoot module
00020  *****************************************************************************/
00021 
00022 #include "ai_shoot_module.h"
00023 #include "../include/action_handler.h"
00024 #include "../interface/game_msg.h"
00025 #include "../map/map.h"
00026 #include "../network/randomsync.h"
00027 #include "../team/macro.h"
00028 #include "../tool/error.h"
00029 #include "../tool/math_tools.h"
00030 #include "../tool/string_tools.h"
00031 
00032 #include <iostream>
00033 // =================================================
00034 // Try to find an enemy which is shootable by
00035 // weapons like gun, shotgun, m16
00036 // =================================================
00037 bool AIShootModule::FindShootableEnemy()
00038 {
00039   FOR_ALL_LIVING_ENEMIES(team, character) {
00040     if ( IsDirectlyShootable(*character) ) {
00041       m_enemy = &(*character);
00042       return true;
00043     }
00044   }
00045 
00046   return false;
00047 }
00048 
00049 // =================================================
00050 // Return true if there is a straight line with no
00051 // collision between the active character and a
00052 // potential enemy
00053 // =================================================
00054 // This method is not perfect
00055 // It tests from the Center of the current Character controlled by the AI
00056 // and not from the gun hole
00057 // =================================================
00058 bool AIShootModule::IsDirectlyShootable(Character& character)
00059 {
00060   Point2i pos = ActiveCharacter().GetCenter();
00061   Point2i arrival = character.GetCenter();
00062   Point2i departure = pos;
00063   Point2i delta_pos;
00064 
00065   double original_angle = pos.ComputeAngle(arrival);
00066 
00067   // compute to see if there any part of ground between the 2 characters
00068   // While test is not finished
00069   while (pos != arrival) {
00070 
00071     // is there a collision on the ground ??
00072     if ( !world.EstDansVide(pos.x, pos.y)) {
00073       return false;
00074     }
00075 
00076     // the point is outside the map
00077     if ( world.EstHorsMondeX(pos.x) || world.EstHorsMondeY(pos.y) ) {
00078       break;
00079     }
00080 
00081     // is there a collision with another character ?
00082     FOR_ALL_CHARACTERS(team, other_character) {
00083       if ( &(*other_character) != &ActiveCharacter()
00084            && &(*other_character) != &character ) {
00085 
00086         if ( other_character->GetTestRect().Contains(pos) )
00087           return false;
00088 
00089       }
00090     }
00091 
00092     // next step
00093     int diff_x = pos.x - arrival.x;
00094     int diff_y = pos.y - arrival.y;
00095 
00096     delta_pos.x = 0;
00097     delta_pos.y = 0;
00098 
00099     if (abs(diff_x) > abs(diff_y)) {
00100       if (pos.x < arrival.x)
00101         delta_pos.x = 1;   //Increment x
00102       else
00103         delta_pos.x = -1;
00104     } else {
00105       if (pos.y < arrival.y)
00106         delta_pos.y = 1;
00107       else
00108         delta_pos.y = -1;
00109     }
00110 
00111     pos += delta_pos;
00112   }
00113 
00114   m_angle = original_angle;
00115 
00116   // Set direction
00117   if (departure.x > arrival.x) {
00118     ActiveCharacter().SetDirection(Body::DIRECTION_LEFT);
00119     m_angle = InverseAngleRad(m_angle);
00120   } else {
00121     ActiveCharacter().SetDirection(Body::DIRECTION_RIGHT);
00122   }
00123 
00124   // Prepare game message
00125   std::string s = "Try to shoot "+character.GetName();
00126   char buff[3];
00127   sprintf(buff, "%f", m_angle); // to manage angle equals to 0
00128   s += " with angle ";
00129   s += buff;
00130   GameMessages::GetInstance()->Add(s);
00131 
00132   return true;
00133 }
00134 
00135 // =================================================
00136 // Try to find an enemy which is shootable by
00137 // weapons like dynamite, mine, ...
00138 // =================================================
00139 bool AIShootModule::FindProximityEnemy()
00140 {
00141   FOR_ALL_LIVING_ENEMIES(team, character) {
00142     if ( IsNear(*character) ) {
00143       m_enemy = &(*character);
00144       return true;
00145     }
00146   }
00147 
00148   return false;
00149   //     if (m_nearest_enemy == NULL
00150   //    || ( character->GetCenter().Distance( ActiveCharacter().GetCenter()) <
00151   //         m_nearest_enemy->GetCenter().Distance( ActiveCharacter().GetCenter()))
00152   //    )
00153   //       m_nearest_enemy = &(*character);
00154   //   }
00155   //   assert(m_nearest_enemy != NULL);
00156 }
00157 
00158 // =================================================
00159 // Return true if character seems to be accessible
00160 // =================================================
00161 // This method is not perfect!!
00162 // =================================================
00163 bool AIShootModule::IsNear(Character& character)
00164 {
00165   uint delta_x = abs(character.GetX() - ActiveCharacter().GetX());
00166   uint delta_y = abs(character.GetY() - ActiveCharacter().GetY());
00167 
00168   if (delta_x > 300)
00169     return false;
00170 
00171   if (delta_y > 100)
00172     return false;
00173 
00174   return true;
00175 }
00176 
00177 // =================================================
00178 // Shoot!
00179 // =================================================
00180 void AIShootModule::Shoot()
00181 {
00182   if (m_current_time > m_last_shoot_time + 1 ||
00183       m_last_shoot_time == 0) {
00184     ActiveTeam().GetWeapon().NewActionShoot();
00185     m_last_shoot_time = m_current_time;
00186   }
00187 
00188   if (!(ActiveTeam().GetWeapon().EnoughAmmoUnit())) {
00189     m_has_finished = true;
00190     ActiveCharacter().body->StartWalk();
00191   }
00192 }
00193 
00194 Character* AIShootModule::FindEnemy()
00195 {
00196   if (m_has_finished) {
00197     return NULL;
00198   }
00199 
00200   if (m_enemy != NULL && !(m_enemy->IsDead())) {
00201     return m_enemy;
00202   }
00203 
00204   m_current_strategy = NO_STRATEGY;
00205 
00206   if (FindProximityEnemy()) {
00207     GameMessages::GetInstance()->Add(ActiveCharacter().GetName()+" has decided to injured "
00208                                      + m_enemy->GetName());
00209 
00210     m_current_strategy = NEAR_FROM_ENEMY;
00211 
00212     // we choose between dynamite, mine, polecart and gnu
00213     uint selected = uint(randomSync.GetDouble(0.0, 3.5));
00214 
00215     switch (selected) {
00216     case 0:
00217     case 1:
00218       ActiveTeam().SetWeapon(Weapon::WEAPON_DYNAMITE);
00219       if (ActiveTeam().GetWeapon().EnoughAmmo()) break;
00220 
00221 //     case 1:
00222 //       ActiveTeam().SetWeapon(Weapon::WEAPON_GNU);
00223 //       if (ActiveTeam().GetWeapon().EnoughAmmo()) break;
00224 
00225 //     case 2:
00226 //       ActiveTeam().SetWeapon(Weapon::WEAPON_POLECAT);
00227 //       if (ActiveTeam().GetWeapon().EnoughAmmo()) break;
00228 
00229     case 3:
00230     default:
00231       ActiveTeam().SetWeapon(Weapon::WEAPON_MINE);
00232     }
00233     m_angle = 0;
00234     ActiveCharacter().SetFiringAngle(m_angle);
00235   }
00236   else if (FindShootableEnemy()) {
00237 
00238     m_current_strategy = SHOOT_FROM_POINT;
00239     GameMessages::GetInstance()->Add(ActiveCharacter().GetName()+" will shoot "
00240                                      + m_enemy->GetName());
00241 
00242     // we choose between gun, sniper_rifle, shotgun and submachine gun
00243     uint selected = uint(randomSync.GetDouble(0.0, 3.5));
00244     switch (selected) {
00245     case 0:
00246       ActiveTeam().SetWeapon(Weapon::WEAPON_SHOTGUN);
00247       if (ActiveTeam().GetWeapon().EnoughAmmo()) break;
00248     case 1:
00249       ActiveTeam().SetWeapon(Weapon::WEAPON_SNIPE_RIFLE);
00250       if (ActiveTeam().GetWeapon().EnoughAmmo()) break;
00251     case 2:
00252       //ActiveTeam().SetWeapon(WEAPON_SUBMACHINE_GUN);
00253       //if (ActiveTeam().GetWeapon().EnoughAmmo()) break;
00254     case 3:
00255     default:
00256       ActiveTeam().SetWeapon(Weapon::WEAPON_GUN);
00257     }
00258 
00259     double angle = BorneDouble(m_angle, - (ActiveTeam().GetWeapon().GetMaxAngle()),
00260                                - (ActiveTeam().GetWeapon().GetMinAngle()) );
00261 
00262     if (AbsReel(angle-m_angle) < 0.08726/* 5 degree */) {
00263       ActiveCharacter().SetFiringAngle(m_angle);
00264     } else {
00265       GameMessages::GetInstance()->Add("Angle is too wide!");
00266 
00267       m_current_strategy = NO_STRATEGY;
00268       m_angle = 0;
00269       m_enemy = NULL;
00270 
00271       return m_enemy;
00272     }
00273   }
00274 
00275   // not enough ammo !!
00276   if ( ! ActiveTeam().GetWeapon().EnoughAmmo() ) {
00277     ActiveTeam().SetWeapon(Weapon::WEAPON_SKIP_TURN);
00278   }
00279 
00280   ChooseDirection();
00281 
00282   return m_enemy;
00283 }
00284 
00285 void AIShootModule::ChooseDirection()
00286 {
00287   if ( m_enemy ) {
00288 
00289     if ( ActiveCharacter().GetCenterX() < m_enemy->GetCenterX())
00290       ActiveCharacter().SetDirection(Body::DIRECTION_RIGHT);
00291     else
00292       ActiveCharacter().SetDirection(Body::DIRECTION_LEFT);
00293 
00294   }
00295 }
00296 
00297 bool AIShootModule::Refresh(uint current_time)
00298 {
00299   if (m_has_finished) {
00300     return true;
00301   }
00302 
00303   m_current_time = current_time;
00304 
00305   FindEnemy();
00306 
00307   switch (m_current_strategy) {
00308 
00309   case NO_STRATEGY:
00310     //ActiveTeam().SetWeapon(Weapon::WEAPON_SKIP_TURN);
00311     //Shoot();
00312     break;
00313 
00314   case NEAR_FROM_ENEMY:
00315     // We are near enough of an enemy (perhaps not the first one we have choosen)
00316     FOR_ALL_LIVING_ENEMIES(team, character) {
00317       if ( abs((*character).GetX() - ActiveCharacter().GetX()) <= 10 &&
00318            abs ((*character).GetY() - ActiveCharacter().GetY()) < 60 ) {
00319       //if ( (*character).GetCenter().Distance( ActiveCharacter().GetCenter()) < 50) {
00320         if (&(*character) != m_enemy) {
00321           GameMessages::GetInstance()->Add(ActiveCharacter().GetName()+" changes target : "
00322                                            + (*character).GetName());
00323         }
00324         m_enemy = &(*character);
00325         Shoot();
00326       }
00327     }
00328     break;
00329 
00330   case SHOOT_FROM_POINT:
00331     Shoot();
00332     return false;
00333     break;
00334   }
00335 
00336   return true;
00337 }
00338 
00339 // =================================================
00340 // Initialize Shoot module when changing
00341 // character to control
00342 // =================================================
00343 void AIShootModule::BeginTurn()
00344 {
00345   m_enemy = NULL;
00346   m_last_shoot_time = 0;
00347   m_angle = 0;
00348   m_current_strategy = NO_STRATEGY;
00349   m_has_finished = false;
00350 
00351   // Choose random direction for the moment
00352   ActiveCharacter().SetDirection( randomSync.GetBool()?Body::DIRECTION_LEFT:Body::DIRECTION_RIGHT );
00353 }
00354 
00355 AIShootModule::AIShootModule()
00356 {
00357   std::cout << "o Artificial Intelligence Shoot module initialization" << std::endl;
00358 }

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