Back to index

supertuxkart  0.5+dfsg1
default_robot.cpp
Go to the documentation of this file.
00001 //  SuperTuxKart - a fun racing game with go-kart
00002 //  Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
00003 //  Copyright (C) 2006-2007 Eduardo Hernandez Munoz
00004 //
00005 //  This program is free software; you can redistribute it and/or
00006 //  modify it under the terms of the GNU General Public License
00007 //  as published by the Free Software Foundation; either version 2
00008 //  of the License, or (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 
00020 //The AI debugging works best with just 1 AI kart, so set the number of karts
00021 //to 2 in main.cpp with quickstart and run supertuxkart with the arg -N.
00022 #undef AI_DEBUG
00023 
00024 #ifdef AI_DEBUG
00025 #define SHOW_FUTURE_PATH //If defined, it will put a bunch of spheres when it
00026 //checks for crashes with the outside of the track.
00027 #define ERASE_PATH   //If not defined, the spheres drawn in the future path
00028 //won't be erased the next time the function is called.
00029 #define SHOW_NON_CRASHING_POINT //If defined, draws a green sphere where the
00030 //n farthest non-crashing point is.
00031 #include <plib/ssgAux.h>
00032 #endif
00033 
00034 #include <cstdlib>
00035 #include <ctime>
00036 #include <cstdio>
00037 #include <iostream>
00038 #include "constants.hpp"
00039 #include "scene.hpp"
00040 #include "world.hpp"
00041 #include "race_manager.hpp"
00042 
00043 #include "default_robot.hpp"
00044 
00045 DefaultRobot::DefaultRobot(const std::string& kart_name,
00046                            int position, sgCoord init_pos ) :
00047     AutoKart( kart_name, position, init_pos )
00048 {
00049     reset();
00050 
00051     switch( race_manager->getDifficulty())
00052     {
00053     case RaceManager::RD_EASY:
00054         m_wait_for_players = true;
00055         m_max_handicap_accel = 0.9f;
00056         m_fallback_tactic = FT_AVOID_TRACK_CRASH;
00057         m_use_wheelies = false;
00058         m_wheelie_check_dist = 0.0f;
00059         m_item_tactic = IT_TEN_SECONDS;
00060         m_max_start_delay = 0.5f;
00061         m_min_steps = 0;
00062         break;
00063     case RaceManager::RD_MEDIUM:
00064         m_wait_for_players = true;
00065         m_max_handicap_accel = 0.95f;
00066         m_fallback_tactic = FT_PARALLEL;
00067         m_use_wheelies = true;
00068         m_wheelie_check_dist = 0.8f;
00069         m_item_tactic = IT_CALCULATE;
00070         m_max_start_delay = 0.4f;
00071         m_min_steps = 1;
00072         break;
00073     case RaceManager::RD_HARD:
00074     case RaceManager::RD_SKIDDING:
00075         m_wait_for_players = false;
00076         m_max_handicap_accel = 1.0f;
00077         m_fallback_tactic = FT_FAREST_POINT;
00078         m_use_wheelies = true;
00079         m_wheelie_check_dist = 1.0f;
00080         m_item_tactic = IT_CALCULATE;
00081         m_max_start_delay = 0.1f;
00082         m_min_steps = 2;
00083         break;
00084     }
00085 }
00086 
00087 //-----------------------------------------------------------------------------
00088 //TODO: if the AI is crashing constantly, make it move backwards in a straight
00089 //line, then move forward while turning.
00090 void DefaultRobot::update( float delta )
00091 {
00092     if( world->isStartPhase())
00093     {
00094         handle_race_start();
00095         AutoKart::update( delta );
00096         return;
00097     }
00098 
00099     /*Get information that is needed by more than 1 of the handling funcs*/
00100     //Detect if we are going to crash with the track and/or kart
00101     int steps = 0;
00102 
00103     // This should not happen (anymore :) ), but it keeps the game running
00104     // in case that m_future_sector becomes undefined.
00105     if(m_future_sector == Track::UNKNOWN_SECTOR )
00106     {
00107 #ifdef DEBUG
00108         fprintf(stderr,"DefaultRobot: m_future_sector is undefined.\n");
00109         fprintf(stderr,"This shouldn't happen, but can be ignored.\n");
00110 #endif
00111         forceRescue();
00112         m_future_sector = 0;
00113     }
00114     else
00115     {
00116         steps = calc_steps();
00117     }
00118 
00119     check_crashes( steps, m_curr_pos.xyz );
00120     find_curve();
00121 
00122     /*Response handling functions*/
00123     handle_acceleration( delta );
00124     handle_steering();
00125     handle_items( delta, steps );
00126     handle_rescue( delta );
00127     handle_wheelie( steps );
00128     handle_braking();
00129     //TODO:handle jumping
00130 
00131     /*And obviously general kart stuff*/
00132     AutoKart::update( delta );
00133 }   // update
00134 
00135 //-----------------------------------------------------------------------------
00136 void DefaultRobot::handle_wheelie( const int STEPS )
00137 {
00138     if( m_use_wheelies )
00139     {
00140         m_controls.wheelie = do_wheelie( STEPS );
00141     }
00142 }
00143 
00144 //-----------------------------------------------------------------------------
00145 void DefaultRobot::handle_braking()
00146 {
00147     // In follow the leader mode, the kart should brake if they are ahead of
00148     // the leader (and not the leader, i.e. don't have initial position 1)
00149     if(race_manager->getRaceMode()==RaceManager::RM_FOLLOW_LEADER &&
00150         getPosition()<world->getKart(0)->getPosition()            &&
00151         getInitialPosition()>1                                       )
00152     {
00153         m_controls.brake = true;
00154         return;
00155     }
00156     const float MIN_SPEED = world->m_track->getWidth()[m_track_sector];
00157 
00158     //We may brake if we are about to get out of the road, but only if the
00159     //kart is on top of the road, and if we won't slow down below a certain
00160     //limit.
00161     if ( m_crashes.m_road && m_on_road && getVelocityLC().getY() > MIN_SPEED)
00162     {
00163         float kart_ang_diff = world->m_track->m_angle[m_track_sector] -
00164             m_curr_pos.hpr[0];
00165         kart_ang_diff = normalize_angle(kart_ang_diff);
00166         kart_ang_diff = fabsf(kart_ang_diff);
00167 
00168         const float MIN_TRACK_ANGLE = 20.0f;
00169         const float CURVE_INSIDE_PERC = 0.25f;
00170 
00171         //Brake only if the road does not goes somewhat straight.
00172         if(m_curve_angle > MIN_TRACK_ANGLE) //Next curve is left
00173         {
00174             //Avoid braking if the kart is in the inside of the curve, but
00175             //if the curve angle is bigger than what the kart can steer, brake
00176             //even if we are in the inside, because the kart would be 'thrown'
00177             //out of the curve.
00178             if(!(m_curr_track_coords[0] > world->m_track->
00179                 getWidth()[m_track_sector] * -CURVE_INSIDE_PERC ||
00180                 m_curve_angle > getMaxSteerAngle()))
00181             {
00182                 m_controls.brake = false;
00183                 return;
00184             }
00185         }
00186         else if( m_curve_angle < -MIN_TRACK_ANGLE ) //Next curve is right
00187         {
00188             if(!(m_curr_track_coords[0] < world->m_track->
00189                 getWidth()[m_track_sector] * CURVE_INSIDE_PERC ||
00190                 m_curve_angle < -getMaxSteerAngle()))
00191             {
00192                 m_controls.brake = false;
00193                 return;
00194             }
00195         }
00196 
00197         //Brake if the kart's speed is bigger than the speed we need
00198         //to go through the curve at the widest angle, or if the kart
00199         //is not going straight in relation to the road.
00200         float angle_adjust = world->m_track->getAIAngleAdjustment();
00201         float speed_adjust = world->m_track->getAICurveSpeedAdjustment();
00202         if(getVelocityLC().getY() > speed_adjust*m_curve_target_speed ||
00203            kart_ang_diff          > angle_adjust*MIN_TRACK_ANGLE         )
00204         {
00205 #ifdef AI_DEBUG
00206         std::cout << "BRAKING" << std::endl;
00207 #endif
00208             m_controls.brake = true;
00209             return;
00210         }
00211 
00212     }
00213 
00214     m_controls.brake = false;
00215 }
00216 
00217 //-----------------------------------------------------------------------------
00218 void DefaultRobot::handle_steering()
00219 {
00220     const unsigned int DRIVELINE_SIZE = (unsigned int)world->m_track->m_driveline.size();
00221     const size_t NEXT_SECTOR = (unsigned int)m_track_sector + 1 < DRIVELINE_SIZE ?
00222         m_track_sector + 1 : 0;
00223     float steer_angle = 0.0f;
00224 
00225     /*The AI responds based on the information we just gathered, using a
00226      *finite state machine.
00227      */
00228     //Reaction to being outside of the road
00229     if( fabsf(m_curr_track_coords[0]) + 0.5 >
00230         world->m_track->getWidth()[m_track_sector] )
00231     {
00232         steer_angle = steer_to_point( world->m_track->
00233             m_driveline[NEXT_SECTOR] );
00234 
00235 #ifdef AI_DEBUG
00236         std::cout << "- Outside of road: steer to center point." <<
00237             std::endl;
00238 #endif
00239     }
00240     //If we are going to crash against a kart, avoid it if it doesn't
00241     //drives the kart out of the road
00242     else if( m_crashes.m_kart != -1 && !m_crashes.m_road )
00243     {
00244         //-1 = left, 1 = right, 0 = no crash.
00245         if( m_start_kart_crash_direction == 1 )
00246         {
00247             steer_angle = steer_to_angle( NEXT_SECTOR, -90.0f );
00248             m_start_kart_crash_direction = 0;
00249         }
00250         else if(m_start_kart_crash_direction == -1)
00251         {
00252             steer_angle = steer_to_angle( NEXT_SECTOR, 90.0f );
00253             m_start_kart_crash_direction = 0;
00254         }
00255         else
00256         {
00257             if(m_curr_track_coords[0] > world->getKart(m_crashes.m_kart)->
00258                getDistanceToCenter())
00259             {
00260                 steer_angle = steer_to_angle( NEXT_SECTOR, -90.0f );
00261                 m_start_kart_crash_direction = 1;
00262             }
00263             else
00264             {
00265                 steer_angle = steer_to_angle( NEXT_SECTOR, 90.0f );
00266                 m_start_kart_crash_direction = -1;
00267             }
00268         }
00269 
00270 #ifdef AI_DEBUG
00271         std::cout << "- Velocity vector crashes with kart and doesn't " <<
00272             "crashes with road : steer 90 degrees away from kart." <<
00273             std::endl;
00274 #endif
00275 
00276     }
00277     else
00278     {
00279         switch( m_fallback_tactic )
00280         {
00281         case FT_FAREST_POINT:
00282             {
00283                 sgVec2 straight_point;
00284                 find_non_crashing_point( straight_point );
00285                 steer_angle = steer_to_point( straight_point );
00286             }
00287             break;
00288 
00289         case FT_PARALLEL:
00290             steer_angle = steer_to_angle( NEXT_SECTOR, 0.0f );
00291             break;
00292 
00293         case FT_AVOID_TRACK_CRASH:
00294             if( m_crashes.m_road )
00295             {
00296                 steer_angle = steer_to_angle( m_track_sector, 0.0f );
00297             }
00298             else steer_angle = 0.0f;
00299 
00300             break;
00301         }
00302 
00303 #ifdef AI_DEBUG
00304         std::cout << "- Fallback."  << std::endl;
00305 #endif
00306 
00307     }
00308     // avoid steer vibrations
00309     if (fabsf(steer_angle) < 2.0f)
00310         steer_angle = 0.f;
00311 
00312     m_controls.lr = angle_to_control( steer_angle );
00313 }
00314 
00315 //-----------------------------------------------------------------------------
00316 void DefaultRobot::handle_items( const float DELTA, const int STEPS )
00317 {
00318     m_controls.fire = false;
00319 
00320     if(isRescue() )
00321     {
00322         return;
00323     }
00324 
00325     m_time_since_last_shot += DELTA;
00326     if( m_collectable.getType() != COLLECT_NOTHING )
00327     {
00328         switch( m_item_tactic )
00329         {
00330         case IT_TEN_SECONDS:
00331             if( m_time_since_last_shot > 10.0f )
00332             {
00333                 m_controls.fire = true;
00334                 m_time_since_last_shot = 0.0f;
00335             }
00336             break;
00337         case IT_CALCULATE:
00338             switch( m_collectable.getType() )
00339             {
00340             case COLLECT_ZIPPER:
00341                 {
00342                     const float ANGLE_DIFF = fabsf( normalize_angle(
00343                         world->m_track->m_angle[m_track_sector]-
00344                         m_curr_pos.hpr[0] ) );
00345 
00346                     if( m_time_since_last_shot > 10.0f && ANGLE_DIFF <
00347                         15.0f && !m_crashes.m_road && STEPS > 8 )
00348                     {
00349                         m_controls.fire = true;
00350                         m_time_since_last_shot = 0.0f;
00351                     }
00352                 }
00353                 break;
00354 
00355             case COLLECT_MISSILE:
00356             case COLLECT_HOMING:
00357                 if( m_time_since_last_shot > 5.0f && m_crashes.m_kart != -1 )
00358                 {
00359                     if( sgDistanceVec2( m_curr_pos.xyz,
00360                         world->getKart(m_crashes.m_kart)->getCoord()->xyz ) >
00361                         m_kart_properties->getKartLength() * 2.5f )
00362                     {
00363                         m_controls.fire = true;
00364                         m_time_since_last_shot = 0.0f;
00365                     }
00366                 }
00367                 break;
00368 
00369             case COLLECT_SPARK:
00370                 if ( m_time_since_last_shot > 3.0f && m_crashes.m_kart != -1 )
00371                 {
00372                     m_controls.fire = true;
00373                     m_time_since_last_shot = 0.0f;
00374                 }
00375                 break;
00376             default:
00377                 m_controls.fire = true;
00378                 m_time_since_last_shot = 0.0f;
00379                 return;
00380             }
00381             break;
00382         }
00383     }
00384 
00385     if( m_controls.fire )
00386     {
00387         m_collectable.use() ;
00388     }
00389     return;
00390 }
00391 
00392 //-----------------------------------------------------------------------------
00393 void DefaultRobot::handle_acceleration( const float DELTA )
00394 {
00395     //Do not accelerate until we have delayed the start enough
00396     if( m_time_till_start > 0.0f )
00397     {
00398         m_time_till_start -= DELTA;
00399         m_controls.accel = 0.0f;
00400         return;
00401     }
00402 
00403     if( m_controls.brake == true )
00404     {
00405         m_controls.accel = 0.0f;
00406         return;
00407     }
00408 
00409     if( m_wait_for_players )
00410     {
00411         //Find if any player is ahead of this kart
00412         bool player_winning = false;
00413         for(unsigned int i = 0; i < race_manager->getNumPlayers(); ++i )
00414             if( m_race_position > world->getPlayerKart(i)->getPosition() )
00415             {
00416                 player_winning = true;
00417                 break;
00418             }
00419 
00420         if( player_winning )
00421         {
00422             m_controls.accel = m_max_handicap_accel;
00423             return;
00424         }
00425     }
00426 
00427     m_controls.accel = 1.0f;
00428 }
00429 
00430 //-----------------------------------------------------------------------------
00431 bool DefaultRobot::do_wheelie ( const int STEPS )
00432 {
00433     if( m_crashes.m_road ) return false;
00434     if( m_crashes.m_kart != -1 ) return false;
00435 
00436     //We have to be careful with normalizing, because if the source argument
00437     //has both the X and Y axis set to 0, it returns nan to the destiny.
00438     const btVector3 &VEL=getVelocity();
00439     btVector3        vel_normal(VEL.getX(), VEL.getY(), 0.0);
00440     float            len=vel_normal.length();
00441     // Too slow for wheelies, and it avoids normalisation problems.
00442     if(len<getMaxSpeed()*getWheelieMaxSpeedRatio()) return false;
00443     vel_normal/=len;
00444 
00445     sgVec2 step_coord;
00446     sgVec3 step_track_coord;
00447     float distance;
00448 
00449     //FIXME: instead of using 1.5, it should find out how much time it
00450     //will pass to stop doing the wheelie completely from the current state.
00451     const float CHECK_DIST = 1.5f * m_wheelie_check_dist;
00452 
00453     /* The following method of finding if a position is outside of the track
00454        is less accurate than calling findRoadSector(), but a lot faster.
00455      */
00456     const int WHEELIE_STEPS = int(( getVelocityLC().getY() * CHECK_DIST )/
00457         m_kart_properties->getKartLength() );
00458 
00459     for( int i = WHEELIE_STEPS; i > STEPS - 1; --i )
00460     {
00461         sgAddScaledVec2( step_coord, m_curr_pos.xyz, vel_normal,
00462             m_kart_properties->getKartLength() * i );
00463 
00464         world->m_track->spatialToTrack( step_track_coord, step_coord,
00465             m_future_sector );
00466 
00467         distance = step_track_coord[0] > 0.0f ?  step_track_coord[0]
00468                    : -step_track_coord[0];
00469 
00470         if( distance > world->m_track->getWidth()[m_track_sector] )
00471         {
00472             return false;
00473         }
00474     }
00475 
00476     return true;
00477 }
00478 
00479 //-----------------------------------------------------------------------------
00480 void DefaultRobot::handle_race_start()
00481 {
00482     //FIXME: make karts able to get a penalty for accelerating too soon
00483     //like players, should happen to about 20% of the karts in easy,
00484     //5% in medium and less than 1% of the karts in hard.
00485     if( m_time_till_start <  0.0f )
00486     {
00487         srand(( unsigned ) time( 0 ));
00488 
00489         //Each kart starts at a different, random time, and the time is
00490         //smaller depending on the difficulty.
00491         m_time_till_start = ( float ) rand() / RAND_MAX * m_max_start_delay;
00492     }
00493 }
00494 
00495 //-----------------------------------------------------------------------------
00496 void DefaultRobot::handle_rescue(const float DELTA)
00497 {
00498     //TODO: check if we collided against a dynamic object (ej.:kart) or
00499     //against the track's static object.
00500     //The m_crash_time measures if a kart has been crashing for too long
00501     m_crash_time += (m_collided && isOnGround()) ? 3.0f * DELTA : -0.25f * DELTA;
00502 
00503     if( m_crash_time < 0.0f ) m_crash_time = 0.0f;
00504 
00505     //Reaction to being stuck
00506     if( m_crash_time > 3.0f )
00507     {
00508         forceRescue();
00509         m_crash_time = 0.0f;
00510     }
00511 
00512 
00513     // check if kart is stuck
00514     if(getVehicle()->getRigidBody()->getLinearVelocity().length()<2.0f &&
00515        !isRescue() && !world->isStartPhase())
00516     {
00517         m_time_since_stuck += DELTA;
00518         if(m_time_since_stuck > 2.0f)
00519         {
00520             forceRescue();
00521             m_time_since_stuck=0.0f;
00522         }   // m_time_since_stuck > 2.0f
00523     }
00524     else
00525     {
00526         m_time_since_stuck = 0.0f;
00527     }
00528 }
00529 
00530 //-----------------------------------------------------------------------------
00531 float DefaultRobot::steer_to_angle (const size_t SECTOR, const float ANGLE)
00532 {
00533     float angle = world->m_track->m_angle[SECTOR];
00534 
00535     //Desired angle minus current angle equals how many angles to turn
00536     float steer_angle = angle - m_curr_pos.hpr[0];
00537 
00538     steer_angle += ANGLE;
00539     steer_angle = normalize_angle( steer_angle );
00540 
00541 
00542     return steer_angle;
00543 }
00544 
00545 //-----------------------------------------------------------------------------
00546 float DefaultRobot::steer_to_point( const sgVec2 POINT )
00547 {
00548     const SGfloat ADJACENT_LINE = POINT[0] - m_curr_pos.xyz[0];
00549     const SGfloat OPPOSITE_LINE = POINT[1] - m_curr_pos.xyz[1];
00550     SGfloat theta;
00551 
00552     //Protection from division by zero
00553     if( ADJACENT_LINE > 0.0000001 || ADJACENT_LINE < -0.0000001 )
00554     {
00555         theta = sgATan( OPPOSITE_LINE / ADJACENT_LINE );
00556     }
00557     else theta = 0;
00558 
00559     //The real value depends on the side of the track that the kart is
00560     theta += ADJACENT_LINE < 0.0f ? 90.0f : -90.0f;
00561 
00562     float steer_angle = theta - getCoord()->hpr[0];
00563     steer_angle = normalize_angle( steer_angle );
00564 
00565     return steer_angle;
00566 }
00567 
00568 //-----------------------------------------------------------------------------
00569 void DefaultRobot::check_crashes( const int STEPS, sgVec3 const pos )
00570 {
00571     //Right now there are 2 kind of 'crashes': with other karts and another
00572     //with the track. The sight line is used to find if the karts crash with
00573     //each other, but the first step is twice as big as other steps to avoid
00574     //having karts too close in any direction. The crash with the track can
00575     //tell when a kart is going to get out of the track so it steers.
00576 
00577     btVector3 vel_normal;
00578        //in this func we use it as a 2d-vector, but later on it is passed
00579        //to world->m_track->findRoadSector, there it is used as a 3d-vector
00580        //to find distance to plane, so z must be initialized to zero
00581        sgVec3 step_coord;
00582     SGfloat kart_distance;
00583 
00584        step_coord[2] = 0.0;
00585 
00586     m_crashes.clear();
00587 
00588     const size_t NUM_KARTS = race_manager->getNumKarts();
00589 
00590     //Protection against having vel_normal with nan values
00591     const btVector3 &VEL = getVelocity();
00592     vel_normal.setValue(VEL.getX(), VEL.getY(), 0.0);
00593     float len=vel_normal.length();
00594     if(len>0.0f)
00595     {
00596         vel_normal/=len;
00597     }
00598     else
00599     {
00600         vel_normal.setValue(0.0, 0.0, 0.0);
00601     }
00602 
00603     for(int i = 1; STEPS > i; ++i)
00604     {
00605         sgAddScaledVec3( step_coord, pos, vel_normal, m_kart_properties->getKartLength() * i );
00606 
00607         /* Find if we crash with any kart, as long as we haven't found one
00608          * yet
00609          */
00610         if( m_crashes.m_kart == -1 )
00611         {
00612             for( unsigned int j = 0; j < NUM_KARTS; ++j )
00613             {
00614                 const Kart* kart=world->getKart(j);
00615                 if(kart==this||kart->isEliminated()) continue;   // ignore eliminated karts
00616 
00617                 kart_distance = sgDistanceVec2( step_coord,
00618                     world->getKart(j)->getCoord()->xyz );
00619 
00620                 if( kart_distance < m_kart_properties->getKartLength() + 0.125f * i )
00621                     if( getVelocityLC().getY() > world->getKart(j)->
00622                        getVelocityLC().getY() * 0.75f ) m_crashes.m_kart = j;
00623             }
00624         }
00625 
00626         /*Find if we crash with the drivelines*/
00627         world->m_track->findRoadSector(step_coord, &m_sector);
00628 
00629 #ifdef SHOW_FUTURE_PATH
00630 
00631         ssgaSphere *sphere = new ssgaSphere;
00632 
00633 #ifdef ERASE_PATH
00634         static ssgaSphere *last_sphere = 0;
00635 
00636         if( last_sphere ) scene->remove( last_sphere );
00637 
00638         last_sphere = sphere;
00639 #endif
00640 
00641         sgVec3 center;
00642         center[0] = step_coord[0];
00643         center[1] = step_coord[1];
00644         center[2] = pos[2];
00645         sphere->setCenter( center );
00646         sphere->setSize( m_kart_properties->getKartLength() );
00647         if( m_sector == Track::UNKNOWN_SECTOR )
00648         {
00649             sgVec4 colour;
00650             colour[0] = colour[3] = 255;
00651             colour[1] = colour[2] = 0;
00652             sphere->setColour(colour);
00653         }
00654         else if( i == 1 )
00655         {
00656             sgVec4 colour;
00657             colour[0] = colour[1] = colour[2] = 0;
00658             colour[3] = 255;
00659             sphere->setColour( colour );
00660         }
00661         scene->add( sphere );
00662 #endif
00663 
00664         m_future_location[0] = step_coord[0]; m_future_location[1] =
00665             step_coord[1];
00666 
00667         if( m_sector == Track::UNKNOWN_SECTOR )
00668         {
00669             m_future_sector = world->getTrack()->findOutOfRoadSector( step_coord,
00670                 Track::RS_DONT_KNOW, m_future_sector );
00671             m_crashes.m_road = true;
00672             break;
00673         }
00674         else
00675         {
00676             m_future_sector = m_sector;
00677         }
00678 
00679 
00680     }
00681 }
00682 
00683 //-----------------------------------------------------------------------------
00689 void DefaultRobot::find_non_crashing_point( sgVec2 result )
00690 {
00691     const unsigned int DRIVELINE_SIZE = (unsigned int)world->m_track->m_driveline.size();
00692 
00693     unsigned int sector = (unsigned int)m_track_sector + 1 < DRIVELINE_SIZE ?
00694         m_track_sector + 1 : 0;
00695     int target_sector;
00696 
00697     sgVec2 direction;
00698     sgVec3 step_track_coord;
00699     SGfloat distance;
00700     int steps;
00701 
00702     //We exit from the function when we have found a solution
00703     while( 1 )
00704     {
00705         //target_sector is the sector at the longest distance that we can drive
00706         //to without crashing with the track.
00707         target_sector = sector + 1 < DRIVELINE_SIZE ? sector + 1 : 0;
00708 
00709         //direction is a vector from our kart to the sectors we are testing
00710         sgSubVec2( direction, world->m_track->m_driveline[target_sector],
00711             m_curr_pos.xyz );
00712 
00713         float len=sgLengthVec2(direction);
00714         steps = int( len / m_kart_properties->getKartLength() );
00715         if( steps < 3 ) steps = 3;
00716 
00717         //Protection against having vel_normal with nan values
00718         if(len>0.0f) {
00719             sgScaleVec2(direction, 1.0f/len);
00720         }
00721 
00722         sgVec2 step_coord;
00723         //Test if we crash if we drive towards the target sector
00724         for( int i = 2; i < steps; ++i )
00725         {
00726             sgAddScaledVec2( step_coord, m_curr_pos.xyz, direction,
00727                 m_kart_properties->getKartLength() * i );
00728 
00729             world->m_track->spatialToTrack( step_track_coord, step_coord,
00730                 sector );
00731 
00732             distance = step_track_coord[0] > 0.0f ? step_track_coord[0]
00733                        : -step_track_coord[0];
00734 
00735             //If we are outside, the previous sector is what we are looking for
00736             if ( distance + m_kart_properties->getKartLength() * 0.5f > world->
00737                 m_track->getWidth()[sector] )
00738             {
00739                 sgCopyVec2( result, world->m_track->m_driveline[sector] );
00740 
00741 #ifdef SHOW_NON_CRASHING_POINT
00742                 ssgaSphere *sphere = new ssgaSphere;
00743 
00744                 static ssgaSphere *last_sphere = 0;
00745 
00746                 if(last_sphere) scene->remove( last_sphere );
00747 
00748                 last_sphere = sphere;
00749 
00750                 sgVec3 center;
00751                 center[0] = result[0];
00752                 center[1] = result[1];
00753                 center[2] = m_curr_pos.xyz[2];
00754                 sphere->setCenter( center );
00755                 sphere->setSize( 0.5f );
00756 
00757                 sgVec4 colour;
00758                 colour[1] = colour[3] = 255;
00759                 colour[0] = colour[2] = 0;
00760                 sphere->setColour( colour );
00761 
00762                 scene->add( sphere );
00763 #endif
00764 
00765                 return;
00766             }
00767         }
00768         sector = target_sector;
00769     }
00770 }
00771 
00772 //-----------------------------------------------------------------------------
00773 void DefaultRobot::reset()
00774 {
00775     m_time_since_last_shot = 0.0f;
00776     m_start_kart_crash_direction = 0;
00777 
00778     m_sector      = Track::UNKNOWN_SECTOR;
00779     m_inner_curve = 0;
00780     m_curve_target_speed = getMaxSpeed();
00781     m_curve_angle = 0.0;
00782 
00783     m_future_location[0] = 0.0;
00784     m_future_location[1] = 0.0;
00785 
00786     m_future_sector = 0;
00787     m_time_till_start = -1.0f;
00788 
00789     m_crash_time = 0.0f;
00790 
00791     m_time_since_stuck     = 0.0f;
00792 
00793     AutoKart::reset();
00794 }   // reset
00795 
00796 //-----------------------------------------------------------------------------
00797 inline float DefaultRobot::normalize_angle( float angle )
00798 {
00799     while( angle > 360.0 ) angle -= 360;
00800     while( angle < -360.0 ) angle += 360;
00801 
00802     if( angle > 180.0 ) angle -= 360.0;
00803     else if( angle < -180.0 ) angle += 360.0;
00804 
00805     return angle;
00806 }
00807 
00808 //-----------------------------------------------------------------------------
00813 int DefaultRobot::calc_steps()
00814 {
00815     int steps = int( getVelocityLC().getY() / m_kart_properties->getKartLength() );
00816     if( steps < m_min_steps ) steps = m_min_steps;
00817 
00818     //Increase the steps depending on the width, if we steering hard,
00819     //mostly for curves.
00820     if( fabsf(m_controls.lr) > 0.95 )
00821     {
00822         const int WIDTH_STEPS = 
00823             (int)( world->m_track->getWidth()[m_future_sector] 
00824                    /( m_kart_properties->getKartLength() * 2.0 ) );
00825 
00826         steps += WIDTH_STEPS;
00827     }
00828 
00829     return steps;
00830 }
00831 
00832 //-----------------------------------------------------------------------------
00836 float DefaultRobot::angle_to_control( float angle ) const
00837 {
00838     angle *= 180.0f / ( getMaxSteerAngle() * M_PI ) ;
00839 
00840     if(angle > 1.0f) return 1.0f;
00841     else if(angle < -1.0f) return -1.0f;
00842 
00843     return angle;
00844 }
00845 
00846 //-----------------------------------------------------------------------------
00855 float DefaultRobot::get_approx_radius(const int START, const int END) const
00856 {
00857     const int MIDDLE = (START + END) / 2;
00858 
00859     //If the START and END sectors are very close, their average will be one
00860     //of them, and using twice the same point just generates a huge radius
00861     //(too big to be of any use) but it also can generate a division by zero,
00862     //so here is some special handling for that case.
00863     if (MIDDLE == START || MIDDLE == END ) return 99999.0f;
00864 
00865     float X1, Y1, X2, Y2, X3, Y3;
00866 
00867     //The next line is just to avoid compiler warnings.
00868     X1 = X2 = X3 = Y1 = Y2 = Y3 = 0.0;
00869 
00870 
00871     if(m_inner_curve == -1)
00872     {
00873 
00874     X1 = world->m_track->m_left_driveline[START][0];
00875     Y1 = world->m_track->m_left_driveline[START][1];
00876 
00877     X2 = world->m_track->m_left_driveline[MIDDLE][0];
00878     Y2 = world->m_track->m_left_driveline[MIDDLE][1];
00879 
00880     X3 = world->m_track->m_left_driveline[END][0];
00881     Y3 = world->m_track->m_left_driveline[END][1];
00882     }else if (m_inner_curve == 0)
00883     {
00884     X1 = world->m_track->m_driveline[START][0];
00885     Y1 = world->m_track->m_driveline[START][1];
00886 
00887     X2 = world->m_track->m_driveline[MIDDLE][0];
00888     Y2 = world->m_track->m_driveline[MIDDLE][1];
00889 
00890     X3 = world->m_track->m_driveline[END][0];
00891     Y3 = world->m_track->m_driveline[END][1];
00892     }else if (m_inner_curve == 1)
00893     {
00894     X1 = world->m_track->m_right_driveline[START][0];
00895     Y1 = world->m_track->m_right_driveline[START][1];
00896 
00897     X2 = world->m_track->m_right_driveline[MIDDLE][0];
00898     Y2 = world->m_track->m_right_driveline[MIDDLE][1];
00899 
00900     X3 = world->m_track->m_right_driveline[END][0];
00901     Y3 = world->m_track->m_right_driveline[END][1];
00902     }
00903 
00904     const float A = X2 - X1;
00905     const float B = Y2 - Y1;
00906     const float C = X3 - X1;
00907     const float D = Y3 - Y1;
00908 
00909     const float E = A * ( X1 + X2) + B * (Y1 + Y2);
00910     const float F = C * ( X1 + X3) + D * (Y1 + Y3);
00911 
00912     const float G = 2.0f * ( A*( Y3-Y2 ) - B*( X3 - X2 ) );
00913 
00914     const float pX = ( D*E - B*F) / G;
00915     const float pY = ( A*F - C*E) / G;
00916 
00917     const float radius = sqrt( ( X1 - pX) * ( X1 - pX) + (Y1 - pY) * (Y1 - pY) );
00918 
00919     return radius;
00920 }
00921 
00922 //-----------------------------------------------------------------------------
00929 void DefaultRobot::find_curve()
00930 {
00931     const int DRIVELINE_SIZE = (unsigned int)world->m_track->m_driveline.size();
00932     float total_dist = 0.0f;
00933     int next_hint = m_track_sector;
00934     int i;
00935 
00936     for(i = m_track_sector; total_dist < getVelocityLC().getY(); i = next_hint)
00937     {
00938         next_hint = i + 1 < DRIVELINE_SIZE ? i + 1 : 0;
00939         total_dist += sgDistanceVec2(world->m_track->m_driveline[i], world->m_track->m_driveline[next_hint]);
00940     }
00941 
00942 
00943     m_curve_angle = normalize_angle(world->m_track->m_angle[i] - world->m_track->m_angle[m_track_sector]);
00944     m_inner_curve = m_curve_angle > 0.0 ? -1 : 1;
00945     // FIXME: 0.9 is the tire grip - but this was never used. For now this
00946     // 0.9 is left in place to reproduce the same results and AI behaviour,
00947     // but this function should be updated to bullet physics
00948     m_curve_target_speed = sgSqrt(get_approx_radius(m_track_sector, i) * world->m_track->getGravity() * 0.9f);
00949 }