src/object/physical_obj.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  * Abstract class used for physical object (object with a size, mass,
00020  * etc.). This object can have differents state : ready, is moving, or ghost
00021  * (is outside of the world).
00022  *
00023  * You can : make the object move (with collision test), change state, etc.
00024  * If the object go outside of the world, it become a ghost.
00025  *****************************************************************************/
00026 
00027 #include <iostream>
00028 #include "physical_obj.h"
00029 #include "physics.h"
00030 #include "objects_list.h"
00031 #include "../game/config.h"
00032 #include "../game/time.h"
00033 #include "../map/map.h"
00034 #include "../network/randomsync.h"
00035 #include "../team/macro.h"
00036 #include "../team/teams_list.h"
00037 #include "../tool/debug.h"
00038 #include "../tool/math_tools.h"
00039 #include "../tool/point.h"
00040 #include "../tool/rectangle.h"
00041 #include "../weapon/ninja_rope.h"
00042 #include "../weapon/launcher.h"
00043 
00044 const int Y_OBJET_MIN = -10000;
00045 const int WATER_RESIST_FACTOR = 40;
00046 
00047 const double PIXEL_PER_METER = 40;
00048 
00049 double MeterDistance (const Point2i &p1, const Point2i &p2)
00050 {
00051   return p1.Distance(p2) / PIXEL_PER_METER;
00052 }
00053 
00054 PhysicalObj::PhysicalObj (const std::string &name, const std::string &xml_config) :
00055   m_name(name),
00056   m_width(0),
00057   m_height(0)
00058 {
00059   life_points = -1; // Disable lifepoints
00060   m_goes_through_wall = false;
00061   m_collides_with_characters = false;
00062   m_collides_with_objects = false;
00063 
00064   // No collision with this object until we have gone out of his collision rectangle
00065   m_overlapping_object = NULL;
00066 
00067   m_allow_negative_y = false;
00068   m_alive = ALIVE;
00069 
00070   m_rebound_sound = "";
00071 
00072   m_test_left = 0;
00073   m_test_right = 0;
00074   m_test_top = 0;
00075   m_test_bottom = 0;
00076 
00077   m_rebound_position = Point2i(-1, -1);
00078 
00079   m_cfg.LoadXml(m_name,xml_config);  // Load physics constants from the xml file
00080   ResetConstants();       // Set physics constants from the xml file
00081 }
00082 
00083 PhysicalObj::~PhysicalObj ()
00084 {}
00085 
00086 //---------------------------------------------------------------------------//
00087 //--                         Class Parameters SET/GET                      --//
00088 //---------------------------------------------------------------------------//
00089 
00090 void PhysicalObj::SetX (int x){
00091   SetXY( Point2i(x, GetY()) );
00092 }
00093 
00094 void PhysicalObj::SetY (int y){
00095   SetXY( Point2i(GetX(), y) );
00096 }
00097 
00098 void PhysicalObj::SetXY(const Point2i &position)
00099 {
00100   CheckOverlapping();
00101 
00102   if( IsOutsideWorldXY( position ) )
00103   {
00104     Point2d physPos(position.x, position.y);
00105     SetPhysXY( physPos / PIXEL_PER_METER );
00106     Ghost();
00107     SignalOutOfMap();
00108   }
00109   else
00110   {
00111     Point2d physPos(position.x, position.y);
00112     SetPhysXY( physPos / PIXEL_PER_METER );
00113     if( FootsInVacuum() ) StartMoving();
00114   }
00115 }
00116 
00117 const Point2i PhysicalObj::GetPosition() const
00118 {
00119   return Point2i(GetX(), GetY());
00120 }
00121 
00122 int PhysicalObj::GetX() const
00123 {
00124   return (int)round(GetPhysX() * PIXEL_PER_METER);
00125 }
00126 
00127 int PhysicalObj::GetY() const
00128 {
00129   return (int)round(GetPhysY() * PIXEL_PER_METER);
00130 }
00131 
00132 int PhysicalObj::GetCenterX() const
00133 {
00134   return GetX() +m_test_left +GetTestWidth()/2;
00135 }
00136 
00137 int PhysicalObj::GetCenterY() const
00138 {
00139   return GetY() +m_test_top +GetTestHeight()/2;
00140 }
00141 
00142 const Point2i PhysicalObj::GetCenter() const
00143 {
00144   return Point2i(GetCenterX(), GetCenterY());
00145 }
00146 
00147 void PhysicalObj::SetSize(const Point2i &newSize){
00148   if( newSize == Point2i(0, 0) )
00149           Error( "New size of (0, 0) !");
00150   m_width = newSize.x;
00151   m_height = newSize.y;
00152   SetPhysSize( (double)newSize.x / PIXEL_PER_METER, (double)newSize.y/PIXEL_PER_METER );
00153 }
00154 
00155 // Lit les dimensions de l'objet
00156 int PhysicalObj::GetWidth() const{
00157   assert (m_width != 0);
00158   return m_width;
00159 }
00160 
00161 int PhysicalObj::GetHeight() const{
00162   assert (m_height != 0);
00163   return m_height;
00164 }
00165 
00166 Point2i PhysicalObj::GetSize() const{
00167         return Point2i(m_width, m_height);
00168 }
00169 
00170 void PhysicalObj::SetOverlappingObject(PhysicalObj* obj)
00171 {
00172   m_overlapping_object = obj;
00173   MSG_DEBUG( "physic.overlapping", "\"%s\" doesn't check any collision with \"%s\" anymore", GetName().c_str(), obj->GetName().c_str());
00174   CheckOverlapping();
00175 }
00176 
00177 void PhysicalObj::CheckOverlapping()
00178 {
00179   if(m_overlapping_object == NULL)
00180     return;
00181 
00182   // Check if we are still overlapping with this object
00183   if (!m_overlapping_object->GetTestRect().Intersect( GetTestRect() ))
00184   {
00185     MSG_DEBUG( "physic.overlapping", "\"%s\" just stopped overlapping with \"%s\"", GetName().c_str(), m_overlapping_object->GetName().c_str());
00186     m_overlapping_object = NULL;
00187   }
00188   else
00189   {
00190     MSG_DEBUG( "physic.overlapping", "\"%s\" is overlapping with \"%s\"", GetName().c_str(), m_overlapping_object->GetName().c_str());
00191   }
00192 }
00193 
00194 void PhysicalObj::SetTestRect (uint left, uint right, uint top, uint bottom)
00195 {
00196   m_test_left =  left;
00197   m_test_right = right;
00198   m_test_top = top;
00199   m_test_bottom = bottom;
00200 }
00201 
00202 int PhysicalObj::GetTestWidth() const
00203 {
00204   return m_width -m_test_left -m_test_right;
00205 }
00206 
00207 int PhysicalObj::GetTestHeight() const
00208 {
00209   return m_height -m_test_top -m_test_bottom;
00210 }
00211 
00212 const Rectanglei PhysicalObj::GetRect() const
00213 {
00214   return Rectanglei( GetX(), GetY(), m_width, m_height);
00215 }
00216 
00217 const Rectanglei PhysicalObj::GetTestRect() const
00218 {
00219   return Rectanglei(GetX()+m_test_left,
00220                     GetY()+m_test_top,
00221                     m_width-m_test_right-m_test_left,
00222                     m_height-m_test_bottom-m_test_top);
00223 }
00224 
00225 void PhysicalObj::AddDamage(uint damage_points)
00226 {
00227   if(life_points == -1)
00228     return;
00229   life_points -= damage_points;
00230   if(life_points <= 0 && !IsGhost())
00231   {
00232     Ghost();
00233     life_points = -1;
00234   }
00235 }
00236 // Move to a point with collision test
00237 // Return true if collision occured
00238 void PhysicalObj::NotifyMove(Point2d oldPos, Point2d newPos)
00239 {
00240   if (IsGhost()) return;
00241   Point2d pos, offset;
00242   PhysicalObj* collided_obj = NULL;
00243 
00244   typedef enum {
00245     NO_COLLISION = 0,
00246     COLLISION_ON_GROUND,
00247     COLLISION_ON_OBJECT
00248   } collision_t;
00249   collision_t collision = NO_COLLISION;
00250 
00251   if(IsGhost())
00252     return;
00253 
00254   // Convert meters to pixels.
00255   oldPos *= PIXEL_PER_METER;
00256   newPos *= PIXEL_PER_METER;
00257 
00258   // Compute distance between old and new position.
00259   double lg = oldPos.Distance( newPos);
00260 
00261   if (lg == 0)
00262     return;
00263 
00264   // Compute increments to move the object step by step from the old
00265   // to the new position.
00266   offset = (newPos - oldPos) / lg;
00267 
00268   // First iteration position.
00269   pos = oldPos + offset;
00270 
00271   if (m_goes_through_wall || IsInWater())
00272   {
00273     Point2i tmpPos( lround(newPos.x), lround(newPos.y) );
00274     SetXY(tmpPos);
00275     return;
00276   }
00277 
00278   do
00279   {
00280     Point2i tmpPos( lround(pos.x), lround(pos.y) );
00281 
00282     // Check if we exit the world. If so, we stop moving and return.
00283     if( IsOutsideWorldXY(tmpPos) ){
00284 
00285       if( !Config::GetInstance()->GetExterieurMondeVide() ){
00286         tmpPos.x = BorneLong(tmpPos.x, 0, world.GetWidth() - GetWidth() - 1);
00287         tmpPos.y = BorneLong(tmpPos.y, 0, world.GetHeight() - GetHeight() - 1);
00288         MSG_DEBUG( "physic.state", "%s - DeplaceTestCollision touche un bord : %d, %d",  m_name.c_str(), tmpPos.x, tmpPos.y );
00289         collision = COLLISION_ON_GROUND;
00290         break;
00291       }
00292 
00293       SetXY( tmpPos );
00294       return;
00295     }
00296 
00297     // Test if we collide...
00298     collided_obj = CollidedObjectXY(tmpPos);
00299     if( collided_obj != NULL)
00300       MSG_DEBUG( "physic.state", "%s collide on %s", m_name.c_str(), collided_obj->GetName().c_str() );
00301 
00302     if( collided_obj != NULL)
00303       collision = COLLISION_ON_OBJECT;
00304     else
00305     if( ! IsInVacuumXY(tmpPos, false) )
00306       collision = COLLISION_ON_GROUND;
00307 
00308     if( collision != NO_COLLISION ) // Nothing more to do!
00309     {
00310       MSG_DEBUG( "physic.state", "%s - Collision at %d,%d : %s", m_name.c_str(), tmpPos.x, tmpPos.y,
00311           collision == COLLISION_ON_GROUND ? "on ground" : "on an object");
00312 
00313       // Set the object position to the current position.
00314       SetXY( Point2i( lround(pos.x - offset.x), lround(pos.y - offset.y)) );
00315       break;
00316     }
00317 
00318     // Next motion step
00319     pos += offset;
00320     lg -= 1.0 ;
00321   } while (0 < lg);
00322 
00323 
00324   // Only for ninja rope... TO REMOVE!!
00325   if (ActiveTeam().GetWeaponType() == Weapon::WEAPON_NINJA_ROPE &&
00326       ActiveTeam().GetWeapon().IsActive()) {
00327     Weapon& tmp = ActiveTeam().AccessWeapon();
00328     NinjaRope * ninjarope = (NinjaRope *)(&tmp);
00329     ninjarope->NotifyMove(collision) ;
00330   }
00331 
00332   if ( collision == NO_COLLISION ) // Nothing more to do!
00333     return;
00334   if ( collision == COLLISION_ON_GROUND ) {
00335       // Find the contact point and collision angle.
00336 //       // !!! ContactPoint(...) _can_ return false when CollisionTest(...) is true !!!
00337 //       // !!! WeaponProjectiles collide on objects, so computing the tangeante to the ground leads
00338 //       // !!! uninitialised values of cx and cy!!
00339 //       if( ContactPoint(cx, cy) ){
00340     int cx, cy;
00341     Point2d contactPos;
00342     double ground_angle;
00343 
00344     if (ContactPoint(cx, cy)) {
00345       ground_angle = world.ground.Tangeante(cx, cy);
00346       contactPos.x = (double)cx / PIXEL_PER_METER;
00347       contactPos.y = (double)cy / PIXEL_PER_METER;
00348     } else {
00349       ground_angle = - GetSpeedAngle();
00350       contactPos = pos;
00351     }
00352 
00353     SignalGroundCollision();
00354     SignalCollision();
00355     // Make it rebound on the ground !!
00356     Rebound(contactPos, ground_angle);
00357     CheckRebound();
00358   } else if ( collision == COLLISION_ON_OBJECT ) {
00359     SignalObjectCollision(collided_obj);
00360     collided_obj->SignalObjectCollision(this);
00361 
00362     // Get the current speed
00363     double v1, v2, mass1, angle1, angle2, mass2;
00364     collided_obj->GetSpeed(v1, angle1);
00365     GetSpeed(v2, angle2);
00366     mass1 = GetMass();
00367     mass2 = collided_obj->GetMass();
00368 
00369     // Give speed to the other object
00370     // thanks to physic and calculations about chocs, we know that :
00371     //
00372     // v'1 =  ((m1 - m2) * v1 + 2m1 *v2) / (m1 + m2)
00373     // v'2 =  ((m2 - m1) * v2 + 2m1 *v1) / (m1 + m2)
00374     SignalCollision();
00375 
00376     collided_obj->SetSpeed(((mass1 - mass2) * v1 + 2 * mass1 *v2 * m_cfg.m_rebound_factor) / (mass1 + mass2),
00377                            angle1);
00378     SetSpeed(((mass2 - mass1) * v2 + 2 * mass1 *v1 * m_cfg.m_rebound_factor) / (mass1 + mass2), angle2);
00379 
00380     // Rebound on the object
00381     double contact_angle = - GetSpeedAngle();
00382     Point2d contactPos = pos;
00383     Rebound(contactPos, contact_angle);
00384     CheckRebound();
00385   }
00386   return;
00387 }
00388 
00389 void PhysicalObj::UpdatePosition ()
00390 {
00391   // No ghost allowed here !
00392   if (IsGhost()) return;
00393 
00394   if ( !m_goes_through_wall )
00395     {
00396       // object is not moving and has no reason to move
00397       if ( !IsMoving() && !FootsInVacuum() && !IsInWater() ) return;
00398 
00399       // object is not moving BUT it should fall !
00400       if ( !IsMoving() && FootsInVacuum() ) StartMoving();
00401     }
00402 
00403   // Compute new position.
00404   RunPhysicalEngine();
00405 
00406   // Test if object is still inside the world
00407   if( IsOutsideWorldXY(GetPosition()) )
00408     Ghost();
00409 
00410   if (IsGhost()) return;
00411 
00412   // Classical object sometimes sinks in water and sometimes goes out of water!
00413   if ( !m_goes_through_wall )
00414     {
00415       if ( IsInWater() && m_alive != DROWNED ) Drown();
00416       else if ( !IsInWater() && m_alive == DROWNED ) GoOutOfWater();
00417     }
00418 
00419 }
00420 
00421 bool PhysicalObj::PutOutOfGround(double direction)
00422 {
00423   if(IsOutsideWorld(Point2i(0, 0)))
00424     return false;
00425 
00426   const int max_step = 30;
00427 
00428   if( IsInVacuum(Point2i(0, 0), false) )
00429     return true;
00430 
00431   double dx = cos(direction);
00432   double dy = sin(direction);
00433 
00434   int step=1;
00435   while(step<max_step && !IsInVacuum(
00436                           Point2i((int)(dx * (double)step),(int)(dy * (double)step)), false ))
00437     step++;
00438 
00439   if(step<max_step)
00440     SetXY( Point2i((int)(dx * (double)step)+GetX(),(int)(dy * (double)step)+GetY()) );
00441   else
00442     return false; //Can't put the object out of the ground
00443 
00444   return true;
00445 }
00446 
00447 bool PhysicalObj::PutOutOfGround()
00448 {
00449   if(IsOutsideWorld(Point2i(0, 0)))
00450     return false;
00451 
00452   if( IsInVacuum(Point2i(0, 0)) )
00453     return true;
00454 
00455   bool left,right,top,bottom;
00456   left   = world.IsInVacuum_left(*this, 0, 0);
00457   right  = world.IsInVacuum_right(*this, 0, 0);
00458   top    = world.EstDansVide_haut(*this, 0, 0);
00459   bottom = world.EstDansVide_bas(*this, 0, 0);
00460 
00461   int dx = (int)GetTestRect().GetSizeX() * (right-left);
00462   int dy = (int)GetTestRect().GetSizeY() * (top-bottom);
00463 
00464   if( dx == 0 && dy == 0 )
00465     return false; //->Don't know in which direction we should go...
00466 
00467   Point2i b(dx, dy);
00468 
00469   double dir = b.ComputeAngle();
00470   return PutOutOfGround(dir);
00471 }
00472 
00473 void PhysicalObj::Init()
00474 {
00475   if (m_alive != ALIVE)
00476     MSG_DEBUG( "physic.state", "%s - Init.", m_name.c_str());
00477   m_alive = ALIVE;
00478   m_overlapping_object = NULL;
00479   StopMoving();
00480 }
00481 
00482 void PhysicalObj::Ghost ()
00483 {
00484   if (m_alive == GHOST)
00485     return;
00486 
00487   bool was_dead = IsDead();
00488   m_alive = GHOST;
00489   MSG_DEBUG("physic.state", "%s - Ghost, was_dead = %d", m_name.c_str(), was_dead);
00490 
00491   // The object became a gost
00492   StopMoving();
00493 
00494   SignalGhostState(was_dead);
00495 }
00496 
00497 void PhysicalObj::Drown()
00498 {
00499   assert (m_alive != DROWNED);
00500   MSG_DEBUG("physic.state", "%s - Drowned...", m_name.c_str());
00501   m_alive = DROWNED;
00502 
00503   // Set the air grab to water resist factor.
00504   SetAirResistFactor(WATER_RESIST_FACTOR * GetAirResistFactor());
00505   StopMoving();
00506   StartMoving();
00507   SignalDrowning();
00508 }
00509 
00510 void PhysicalObj::GoOutOfWater()
00511 {
00512   assert (m_alive == DROWNED);
00513   MSG_DEBUG("physic.state", "%s - Go out of water!...", m_name.c_str());
00514   m_alive = ALIVE;
00515 
00516   // Set the air grab to normal air resist factor.
00517   SetAirResistFactor(m_cfg.m_air_resist_factor);
00518 
00519   StartMoving();
00520 }
00521 
00522 bool PhysicalObj::IsImmobile() const
00523 {
00524   return (!IsMoving() && !FootsInVacuum())||(m_alive == GHOST);
00525 }
00526 
00527 bool PhysicalObj::IsDead () const
00528 { return ((m_alive == GHOST) || (m_alive == DROWNED) || (m_alive == DEAD)); }
00529 
00530 bool PhysicalObj::IsGhost() const
00531 { return (m_alive == GHOST); }
00532 
00533 bool PhysicalObj::IsDrowned() const
00534 { return (m_alive == DROWNED); }
00535 
00536 void PhysicalObj::SignalRebound()
00537 {
00538   // TO CLEAN...
00539    if (!m_rebound_sound.empty())
00540      jukebox.Play("share", m_rebound_sound) ;
00541 }
00542 
00543 void PhysicalObj::SignalObjectCollision(PhysicalObj * obj) {}
00544 
00545 void PhysicalObj::SignalGroundCollision() {}
00546 
00547 void PhysicalObj::SignalCollision() {}
00548 
00549 void PhysicalObj::SignalOutOfMap() {}
00550 
00551 void PhysicalObj::SetCollisionModel(bool goes_through_wall,
00552                                     bool collides_with_characters,
00553                                     bool collides_with_objects)
00554 {
00555   m_goes_through_wall = goes_through_wall;
00556   m_collides_with_characters = collides_with_characters;
00557   m_collides_with_objects = collides_with_objects;
00558 
00559   // Check boolean values
00560   {
00561     if (m_collides_with_characters || m_collides_with_objects)
00562       assert(m_goes_through_wall == false);
00563 
00564     if (m_goes_through_wall) {
00565       assert(m_collides_with_characters == false);
00566       assert(m_collides_with_objects == false);
00567     }
00568   }
00569 }
00570 
00571 void PhysicalObj::CheckRebound()
00572 {
00573   // If we bounce twice in a row at the same place, stop bouncing
00574   // cause it's almost sure this object is stuck bouncing indefinitely
00575   if( m_rebound_position != Point2i( -1, -1) )
00576   {
00577     if ( m_rebound_position == GetPosition() )
00578     {
00579       MSG_DEBUG("physic.state", "%s seems to be stuck in ground. Stop moving!", m_name.c_str());
00580       StopMoving();
00581     }
00582   }
00583   m_rebound_position = GetPosition();
00584 }
00585 
00586 bool PhysicalObj::IsOutsideWorldXY(Point2i position) const{
00587   int x = position.x + m_test_left;
00588   int y = position.y + m_test_top;
00589 
00590   if( world.EstHorsMondeXlarg(x, GetTestWidth()) )
00591           return true;
00592   if( world.EstHorsMondeYhaut(y, GetTestHeight()) ){
00593     if( m_allow_negative_y )
00594       if( (Y_OBJET_MIN <= y) && (y + GetTestHeight() - 1 < 0) )
00595                   return false;
00596     return true;
00597   }
00598   return false;
00599 }
00600 
00601 bool PhysicalObj::IsOutsideWorld(const Point2i &offset) const
00602 {
00603   return IsOutsideWorldXY( GetPosition() + offset );
00604 }
00605 
00606 bool PhysicalObj::FootsOnFloor(int y) const
00607 {
00608   // If outside is empty, the object can't hit the ground !
00609   if ( Config::GetInstance()->GetExterieurMondeVide() ) return false;
00610 
00611   const int y_max = world.GetHeight()-m_height +m_test_bottom;
00612   return (y_max <= y);
00613 }
00614 
00615 bool PhysicalObj::IsOverlapping(const PhysicalObj* obj) const
00616 {
00617   return m_overlapping_object == obj;
00618 }
00619 
00620 bool PhysicalObj::IsInVacuum(const Point2i &offset, bool check_object) const
00621 {
00622   return IsInVacuumXY(GetPosition() + offset, check_object);
00623 }
00624 
00625 bool PhysicalObj::IsInVacuumXY(const Point2i &position, bool check_object) const
00626 {
00627   if( IsOutsideWorldXY(position) )
00628     return Config::GetInstance()->GetExterieurMondeVide();
00629 
00630   if( FootsOnFloor(position.y - 1) )
00631     return false;
00632 
00633   if( check_object && CollidedObjectXY(position) )
00634     return false;
00635 
00636   Rectanglei rect(position.x + m_test_left, position.y + m_test_top,
00637                   m_width - m_test_right - m_test_left, m_height -m_test_bottom - m_test_top);
00638 
00639   return world.RectEstDansVide (rect);
00640 }
00641 
00642 PhysicalObj* PhysicalObj::CollidedObject(const Point2i &offset) const
00643 {
00644   return CollidedObjectXY(GetPosition() + offset);
00645 }
00646 
00647 PhysicalObj* PhysicalObj::CollidedObjectXY(const Point2i & position) const
00648 {
00649   if( IsOutsideWorldXY(position) )
00650     return NULL;
00651 
00652   Rectanglei rect(position.x + m_test_left, position.y + m_test_top,
00653                  m_width - m_test_right - m_test_left, m_height - m_test_bottom - m_test_top);
00654 
00655   if (m_collides_with_characters)
00656     {
00657       FOR_ALL_LIVING_CHARACTERS(team,character)
00658       {
00659         // We check both objet if one overlapse the other
00660         if (&(*character) != this && !IsOverlapping(&(*character)) && !character->IsOverlapping(this)
00661         && character->GetTestRect().Intersect( rect ))
00662           return (PhysicalObj*) &(*character);
00663       }
00664     }
00665 
00666   if (m_collides_with_objects)
00667     {
00668       FOR_EACH_OBJECT(it)
00669       {
00670         PhysicalObj * object=*it;
00671         // We check both objet if one overlapse the other
00672         if (object != this && !IsOverlapping(object) && !object->IsOverlapping(this)
00673             && object->m_collides_with_objects
00674             && object->GetTestRect().Intersect(rect) )
00675           return object;
00676       }
00677     }
00678   return NULL;
00679 }
00680 
00681 bool PhysicalObj::FootsInVacuum() const
00682 {
00683   return FootsInVacuumXY(GetPosition());
00684 }
00685 
00686 bool PhysicalObj::FootsInVacuumXY(const Point2i &position) const
00687 {
00688   if( IsOutsideWorldXY(position) ){
00689     MSG_DEBUG("physical", "%s - physobj is outside the world", m_name.c_str());
00690     return Config::GetInstance()->GetExterieurMondeVide();
00691   }
00692 
00693   if( FootsOnFloor(position.y) ){
00694     MSG_DEBUG("physical", "%s - physobj is on floor", m_name.c_str());
00695     return false;
00696   }
00697 
00698   int y_test = position.y + m_height - m_test_bottom;
00699 
00700   Rectanglei rect( position.x + m_test_left, y_test,
00701                  m_width - m_test_right - m_test_left, 1);
00702 
00703   if( m_allow_negative_y && rect.GetPositionY() < 0){
00704     int b = rect.GetPositionY() + rect.GetSizeY();
00705 
00706     rect.SetPositionY( 0 );
00707     rect.SetSizeY( ( b > 0 ) ? b - rect.GetPositionY() : 0 );
00708   }
00709 
00710   if(CollidedObjectXY( position + Point2i(0, 1)) != NULL )
00711     return false;
00712 
00713   return world.RectEstDansVide (rect);
00714 }
00715 
00716 bool PhysicalObj::IsInWater () const
00717 {
00718   assert (!IsGhost());
00719   if (!world.water.IsActive()) return false;
00720   int x = BorneLong(GetCenterX(), 0, world.GetWidth()-1);
00721   return (int)world.water.GetHeight(x) < GetCenterY();
00722 }
00723 
00724 void PhysicalObj::DirectFall()
00725 {
00726   while (!IsGhost() && !IsInWater() && FootsInVacuum())
00727       SetY(GetY()+1);
00728 }
00729 
00730 bool PhysicalObj::ContactPoint (int & contact_x, int & contact_y)
00731 {
00732   int x1, x2, y1, y2;
00733 
00734   // On cherche un point de contact en bas de l'objet:
00735   y1 = (GetY()+m_height-m_test_bottom);
00736   y2 = y1-1;
00737   for (uint x=GetX()+ m_test_left; x<=(GetX()+m_width)-m_test_right; x++)
00738   {
00739     if(!world.EstHorsMonde(Point2i(x,y1)) && !world.EstHorsMonde(Point2i(x,y2))
00740     && world.ground.IsEmpty(Point2i(x,y2)) && !world.ground.IsEmpty(Point2i(x,y1)))
00741     {
00742       contact_x = x;
00743       contact_y = GetY() +m_height-m_test_bottom;
00744       return true;
00745     }
00746   }
00747 
00748   // On cherche un point de contact �gauche de l'objet:
00749   x1 = GetX()+m_test_left;
00750   x2 = x1+1;
00751   for(uint y=GetY()+m_test_top;y<=GetY()+m_height-m_test_bottom;y++)
00752   {
00753     if(!world.EstHorsMonde(Point2i(x1,y)) && !world.EstHorsMonde(Point2i(x2,y))
00754     && !world.ground.IsEmpty(Point2i(x1,y)) &&  world.ground.IsEmpty(Point2i(x2,y)))
00755     {
00756       contact_x = GetX() +m_test_left;
00757       contact_y = y;
00758       return true;
00759     }
00760   }
00761 
00762   // On cherche un point de contact �droite de l'objet:
00763   x1 = (GetX()+m_width-m_test_right);
00764   x2 = x1-1;
00765   for(uint y=GetY()+m_test_top;y<=GetY()+m_height-m_test_bottom;y++)
00766   {
00767     if(!world.EstHorsMonde(Point2i(x1, y)) && !world.EstHorsMonde(Point2i(x2, y))
00768        && !world.ground.IsEmpty(Point2i(x1, y)) && world.ground.IsEmpty(Point2i(x2, y)))
00769     {
00770       contact_x = GetX() + m_width - m_test_right;
00771       contact_y = y;
00772       return true;
00773     }
00774   }
00775 
00776   // On cherche un point de contact en haut de l'objet:
00777   y1 = GetY()+m_test_top;
00778   y2 = y1 - 1;
00779   for(uint x=GetX()+m_test_left;x<=GetX()+m_width-m_test_right;x++)
00780   {
00781     if(!world.EstHorsMonde(Point2i(x,y1)) && !world.EstHorsMonde(Point2i(x,y2))
00782     && !world.ground.IsEmpty(Point2i(x, y1)) && world.ground.IsEmpty(Point2i(x, y2)))
00783     {
00784       contact_x =x;
00785       contact_y = GetY() +m_test_top;
00786       return true;
00787     }
00788   }
00789   return false;
00790 }
00791 
00792 // Est-ce que deux objets se touchent ? (utilise les rectangles de test)
00793 bool PhysicalObj::ObjTouche(const PhysicalObj &b) const
00794 {
00795   return GetTestRect().Intersect( b.GetTestRect() );
00796 }
00797 
00798 // Est-ce que le point p touche l'objet a ?
00799 bool PhysicalObj::ObjTouche(const Point2i &p) const
00800 {
00801    return  GetTestRect().Contains( p );
00802 }
00803 
00804 bool PhysicalObj::PutRandomly(bool on_top_of_world, double min_dst_with_characters)
00805 {
00806   uint bcl=0;
00807   uint NB_MAX_TRY = 60;
00808   bool ok;
00809   Point2i position;
00810 
00811   MSG_DEBUG("physic.position", "%s - Search a position...", m_name.c_str());
00812 
00813   do
00814   {
00815     bcl++;
00816     ok = true;
00817     Init();
00818 
00819     if (bcl >= NB_MAX_TRY) {
00820       MSG_DEBUG("physic.position", "%s - Impossible to find an initial position !!", m_name.c_str());
00821       return false;
00822     }
00823 
00824     if (on_top_of_world) {
00825       // Placement au hasard en X
00826       position.x = randomSync.GetLong(0, world.GetWidth() - GetWidth());
00827       position.y = -GetHeight()+1;
00828     } else {
00829       position = randomSync.GetPoint(world.GetSize() - GetSize() + 1);
00830     }
00831     SetXY(position);
00832     MSG_DEBUG("physic.position", "%s - Test in %d, %d",  m_name.c_str(), position.x, position.y);
00833 
00834     // Check physical object is not in the ground
00835     ok &= !IsGhost() && world.ParanoiacRectIsInVacuum(GetTestRect())  && IsInVacuum( Point2i(0, 1) );
00836     if (!ok) {
00837       MSG_DEBUG("physic.position", "%s - Put it in the ground -> try again !", m_name.c_str());
00838       continue;
00839     }
00840 
00841     // Check object does not go in water or outside the map
00842     DirectFall();
00843     ok &= !IsGhost() && !IsInWater() && (GetY() < static_cast<int>(world.GetHeight() - (WATER_INITIAL_HEIGHT + 30)));
00844 
00845     if (!ok) {
00846       MSG_DEBUG("physic.position", "%s - Put in outside the map or in water -> try again", m_name.c_str());
00847       continue;
00848     }
00849 
00850     // Check distance with characters
00851     FOR_ALL_LIVING_CHARACTERS(equipe, ver) if (&(*ver) != this)
00852     {
00853       if (min_dst_with_characters == 0) {
00854 
00855         if( ObjTouche(*ver) ) {
00856             MSG_DEBUG("physic.position", "%s - Object is too close from character %s", m_name.c_str(), (*ver).m_name.c_str());
00857             ok = false;
00858         }
00859       } else {
00860         Point2i p1 = ver->GetCenter();
00861         Point2i p2 = GetCenter();
00862         double dst = p1.Distance( p2 );
00863 
00864         // ok this test is not perfect but quite efficient ;-)
00865         // else we need to check each distance between each "corner"
00866         if (dst < min_dst_with_characters) ok = false;
00867       }
00868     }
00869 
00870     if (ok && on_top_of_world) SetXY(position);
00871   } while (!ok);
00872 
00873   MSG_DEBUG("physic.position", "Putted after  %d try", m_name.c_str(), bcl);
00874 
00875   return true;
00876 }

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