From e7b0874274072471f08e8630dffff5631041ebdf Mon Sep 17 00:00:00 2001 From: hikerstk Date: Sun, 10 Jun 2012 19:52:11 +0000 Subject: [PATCH] Applied a (slightly modified) version of jskij7's patch to create an AI that actively collects items. This is (for now) the new default AI - feedback welcome! git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@11291 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- sources.cmake | 28 +- src/ide/vc9/supertuxkart.vcproj | 8 + src/items/item.hpp | 10 +- src/items/item_manager.hpp | 9 +- src/karts/controller/present_ai.cpp | 1096 +++++++++++++++++++++++++++ src/karts/controller/present_ai.hpp | 314 ++++++++ src/modes/world.cpp | 10 +- 7 files changed, 1454 insertions(+), 21 deletions(-) create mode 100644 src/karts/controller/present_ai.cpp create mode 100644 src/karts/controller/present_ai.hpp diff --git a/sources.cmake b/sources.cmake index 46dab2234..0c6cb1135 100644 --- a/sources.cmake +++ b/sources.cmake @@ -94,14 +94,15 @@ src/items/projectile_manager.cpp src/items/rubber_ball.cpp src/items/rubber_band.cpp src/items/swatter.cpp -src/karts/abstract_kart.cpp src/karts/abstract_kart_animation.cpp +src/karts/abstract_kart.cpp src/karts/cannon_animation.cpp src/karts/controller/ai_base_controller.cpp src/karts/controller/controller.cpp src/karts/controller/default_ai_controller.cpp src/karts/controller/end_controller.cpp src/karts/controller/player_controller.cpp +src/karts/controller/present_ai.cpp src/karts/controller/skidding_ai.cpp src/karts/explosion_animation.cpp src/karts/ghost_kart.cpp @@ -184,13 +185,13 @@ src/states_screens/kart_selection.cpp src/states_screens/main_menu_screen.cpp src/states_screens/minimal_race_gui.cpp src/states_screens/options_screen_audio.cpp -src/states_screens/options_screen_input.cpp src/states_screens/options_screen_input2.cpp +src/states_screens/options_screen_input.cpp src/states_screens/options_screen_players.cpp src/states_screens/options_screen_ui.cpp src/states_screens/options_screen_video.cpp -src/states_screens/race_gui.cpp src/states_screens/race_gui_base.cpp +src/states_screens/race_gui.cpp src/states_screens/race_gui_overworld.cpp src/states_screens/race_result_gui.cpp src/states_screens/race_setup_screen.cpp @@ -251,8 +252,8 @@ src/animations/billboard_animation.hpp src/animations/ipo.hpp src/animations/three_d_animation.hpp src/audio/dummy_sfx.hpp -src/audio/music.hpp src/audio/music_dummy.hpp +src/audio/music.hpp src/audio/music_information.hpp src/audio/music_manager.hpp src/audio/music_ogg.hpp @@ -260,8 +261,8 @@ src/audio/sfx_base.hpp src/audio/sfx_buffer.hpp src/audio/sfx_manager.hpp src/audio/sfx_openal.hpp -src/challenges/challenge.hpp src/challenges/challenge_data.hpp +src/challenges/challenge.hpp src/challenges/game_slot.hpp src/challenges/unlock_manager.hpp src/config/device_config.hpp @@ -302,11 +303,11 @@ src/guiengine/scalable_font.hpp src/guiengine/screen.hpp src/guiengine/skin.hpp src/guiengine/widget.hpp -src/guiengine/widgets.hpp src/guiengine/widgets/bubble_widget.hpp src/guiengine/widgets/button_widget.hpp src/guiengine/widgets/check_box_widget.hpp src/guiengine/widgets/dynamic_ribbon_widget.hpp +src/guiengine/widgets.hpp src/guiengine/widgets/icon_button_widget.hpp src/guiengine/widgets/label_widget.hpp src/guiengine/widgets/list_widget.hpp @@ -317,8 +318,8 @@ src/guiengine/widgets/spinner_widget.hpp src/guiengine/widgets/text_box_widget.hpp src/input/binding.hpp src/input/device_manager.hpp -src/input/input.hpp src/input/input_device.hpp +src/input/input.hpp src/input/input_manager.hpp src/input/wiimote_manager.hpp src/io/file_manager.hpp @@ -339,8 +340,8 @@ src/items/projectile_manager.hpp src/items/rubber_ball.hpp src/items/rubber_band.hpp src/items/swatter.hpp -src/karts/abstract_kart.hpp src/karts/abstract_kart_animation.hpp +src/karts/abstract_kart.hpp src/karts/cannon_animation.hpp src/karts/controller/ai_base_controller.hpp src/karts/controller/controller.hpp @@ -348,11 +349,12 @@ src/karts/controller/default_ai_controller.hpp src/karts/controller/end_controller.hpp src/karts/controller/kart_control.hpp src/karts/controller/player_controller.hpp +src/karts/controller/present_ai.hpp src/karts/controller/skidding_ai.hpp src/karts/explosion_animation.hpp src/karts/ghost_kart.hpp -src/karts/kart.hpp src/karts/kart_gfx.hpp +src/karts/kart.hpp src/karts/kart_model.hpp src/karts/kart_properties.hpp src/karts/kart_properties_manager.hpp @@ -442,13 +444,13 @@ src/states_screens/kart_selection.hpp src/states_screens/main_menu_screen.hpp src/states_screens/minimal_race_gui.hpp src/states_screens/options_screen_audio.hpp -src/states_screens/options_screen_input.hpp src/states_screens/options_screen_input2.hpp +src/states_screens/options_screen_input.hpp src/states_screens/options_screen_players.hpp src/states_screens/options_screen_ui.hpp src/states_screens/options_screen_video.hpp -src/states_screens/race_gui.hpp src/states_screens/race_gui_base.hpp +src/states_screens/race_gui.hpp src/states_screens/race_gui_overworld.hpp src/states_screens/race_result_gui.hpp src/states_screens/race_setup_screen.hpp @@ -477,8 +479,8 @@ src/tracks/check_sphere.hpp src/tracks/check_structure.hpp src/tracks/graph_node.hpp src/tracks/lod_node_loader.hpp -src/tracks/quad.hpp src/tracks/quad_graph.hpp +src/tracks/quad.hpp src/tracks/quad_set.hpp src/tracks/terrain_info.hpp src/tracks/track.hpp @@ -486,8 +488,8 @@ src/tracks/track_manager.hpp src/tracks/track_object.hpp src/tracks/track_object_manager.hpp src/tracks/track_sector.hpp -src/tutorial/tutorial.hpp src/tutorial/tutorial_data.hpp +src/tutorial/tutorial.hpp src/tutorial/tutorial_manager.hpp src/utils/aligned_array.hpp src/utils/constants.hpp diff --git a/src/ide/vc9/supertuxkart.vcproj b/src/ide/vc9/supertuxkart.vcproj index 0685b2a4c..2f85be1d8 100644 --- a/src/ide/vc9/supertuxkart.vcproj +++ b/src/ide/vc9/supertuxkart.vcproj @@ -923,6 +923,10 @@ RelativePath="..\..\karts\controller\player_controller.cpp" > + + @@ -2074,6 +2078,10 @@ RelativePath="..\..\karts\moveable.hpp" > + + diff --git a/src/items/item.hpp b/src/items/item.hpp index c4ce3319e..a7aafb927 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -147,6 +147,10 @@ public: virtual ~Item (); void update (float delta); virtual void collected(const AbstractKart *kart, float t=2.0f); + void setParent(AbstractKart* parent); + void reset(); + void switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh); + void switchBack(); // ------------------------------------------------------------------------ /** Returns true if the Kart is close enough to hit this item, the item is @@ -192,10 +196,8 @@ public: /** Returns the time the item is disabled for. */ float getDisableTime() const { return m_time_till_return; } // ------------------------------------------------------------------------ - void setParent(AbstractKart* parent); - void reset(); - void switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh); - void switchBack(); + /** Returns the XYZ position of the item. */ + const Vec3& getXYZ() const { return m_xyz; } }; // class Item #endif diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index 5816567e9..ee6c5bbe0 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -76,9 +76,16 @@ public: int add_info=-1); void switchItems (); void setSwitchItems(const std::vector &switch_items); + // ------------------------------------------------------------------------ scene::IMesh* getItemModel (Item::ItemType type) {return m_item_mesh[type];} -}; + // ------------------------------------------------------------------------ + /** Returns the number of items. */ + unsigned int getNumberOfItems() const { return m_all_items.size(); } + // ------------------------------------------------------------------------ + /** Returns a pointer to the n-th item. */ + const Item * getItem(unsigned int n) const { return m_all_items[n]; }; +}; // ItemManager extern ItemManager* item_manager; diff --git a/src/karts/controller/present_ai.cpp b/src/karts/controller/present_ai.cpp new file mode 100644 index 000000000..914d32957 --- /dev/null +++ b/src/karts/controller/present_ai.cpp @@ -0,0 +1,1096 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2004-2005 Steve Baker +// Copyright (C) 2006-2007 Eduardo Hernandez Munoz +// Copyright (C) 2008 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +//The AI debugging works best with just 1 AI kart, so set the number of karts +//to 2 in main.cpp with quickstart and run supertuxkart with the arg -N. + +#undef AI_DEBUG + +#include "karts/controller/present_ai.hpp" + +#ifdef AI_DEBUG +# include "irrlicht.h" + using namespace irr; +#endif + +#include +#include +#include +#include + +#ifdef AI_DEBUG +# include "graphics/irr_driver.hpp" +#endif +#include "graphics/slip_stream.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/controller/kart_control.hpp" +#include "karts/kart_properties.hpp" +#include "karts/max_speed.hpp" +#include "karts/rescue_animation.hpp" +#include "items/item.hpp" +#include "items/attachment.hpp" +#include "items/powerup.hpp" +#include "items/item_manager.hpp" +#include "modes/linear_world.hpp" +#include "network/network_manager.hpp" +#include "race/race_manager.hpp" +#include "tracks/quad_graph.hpp" +#include "tracks/track.hpp" +#include "utils/constants.hpp" + +PresentAI::PresentAI(AbstractKart *kart) + : AIBaseController(kart) +{ + reset(); + + switch( race_manager->getDifficulty()) + { + case RaceManager::RD_EASY: + m_wait_for_players = true; + m_make_use_of_slipstream = false; + m_max_handicap_speed = 0.9f; + m_item_tactic = IT_TEN_SECONDS; + m_false_start_probability = 0.08f; + m_min_start_delay = 0.3f; + m_max_start_delay = 0.5f; + m_min_steps = 1; + m_nitro_level = NITRO_NONE; + m_handle_bomb = false; + setSkiddingFraction(4.0f); + break; + case RaceManager::RD_MEDIUM: + m_wait_for_players = true; + m_make_use_of_slipstream = false; + m_max_handicap_speed = 0.95f; + m_item_tactic = IT_CALCULATE; + m_false_start_probability = 0.04f; + m_min_start_delay = 0.25f; + m_max_start_delay = 0.4f; + m_min_steps = 1; + m_nitro_level = NITRO_SOME; + m_handle_bomb = true; + setSkiddingFraction(3.0f); + break; + case RaceManager::RD_HARD: + m_wait_for_players = false; + m_make_use_of_slipstream = true; + m_max_handicap_speed = 1.0f; + m_item_tactic = IT_CALCULATE; + m_false_start_probability = 0.01f; + // See http://www.humanbenchmark.com/tests/reactiontime/stats.php + // Average reaction time is around 0.215 s, so using .15 as minimum + // gives an AI average slightly above the human average + m_min_start_delay = 0.15f; + m_max_start_delay = 0.28f; + m_min_steps = 2; + m_nitro_level = NITRO_ALL; + m_handle_bomb = true; + setSkiddingFraction(2.0f); + break; + } + +#ifdef AI_DEBUG + m_debug_sphere = irr_driver->getSceneManager()->addSphereSceneNode(1); +#endif + setControllerName("Present"); +} // PresentAI + +//----------------------------------------------------------------------------- +/** The destructor deletes the shared TrackInfo objects if no more PresentAI + * instances are around. + */ +PresentAI::~PresentAI() +{ +#ifdef AI_DEBUG + irr_driver->removeNode(m_debug_sphere); +#endif +} // ~PresentAI + +//----------------------------------------------------------------------------- +void PresentAI::reset() +{ + m_time_since_last_shot = 0.0f; + m_start_kart_crash_direction = 0; + m_curve_target_speed = m_kart->getCurrentMaxSpeed(); + m_curve_angle = 0.0; + m_start_delay = -1.0f; + m_time_since_stuck = 0.0f; + m_kart_ahead = NULL; + m_distance_ahead = 0.0f; + m_kart_behind = NULL; + m_distance_behind = 0.0f; + + AIBaseController::reset(); + m_track_node = QuadGraph::UNKNOWN_SECTOR; + QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); + if(m_track_node==QuadGraph::UNKNOWN_SECTOR) + { + fprintf(stderr, + "Invalid starting position for '%s' - not on track" + " - can be ignored.\n", + m_kart->getIdent().c_str()); + m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + } + + AIBaseController::reset(); +} // reset + +//----------------------------------------------------------------------------- +/** Returns the pre-computed successor of a graph node. + * \parameter index The index of the graph node for which the successor + * is searched. + */ +unsigned int PresentAI::getNextSector(unsigned int index) +{ + return m_successor_index[index]; +} // getNextSector + +//----------------------------------------------------------------------------- +//TODO: if the AI is crashing constantly, make it move backwards in a straight +//line, then move forward while turning. +void PresentAI::update(float dt) +{ + // This is used to enable firing an item backwards. + m_controls->m_look_back = false; + m_controls->m_nitro = false; + + // Don't do anything if there is currently a kart animations shown. + if(m_kart->getKartAnimation()) + return; + + // Having a non-moving AI can be useful for debugging, e.g. aiming + // or slipstreaming. +#undef AI_DOES_NOT_MOVE_FOR_DEBUGGING +#ifdef AI_DOES_NOT_MOVE_FOR_DEBUGGING + m_controls->m_accel = 0; + m_controls->m_steer = 0; + return; +#endif + + // The client does not do any AI computations. + if(network_manager->getMode()==NetworkManager::NW_CLIENT) + { + AIBaseController::update(dt); + return; + } + + // If the kart needs to be rescued, do it now (and nothing else) + if(isStuck() && !m_kart->getKartAnimation()) + { + new RescueAnimation(m_kart); + AIBaseController::update(dt); + return; + } + + if( m_world->isStartPhase() ) + { + handleRaceStart(); + AIBaseController::update(dt); + return; + } + + /*Get information that is needed by more than 1 of the handling funcs*/ + //Detect if we are going to crash with the track and/or kart + int steps = 0; + + steps = calcSteps(); + + computeNearestKarts(); + checkCrashes( steps, m_kart->getXYZ() ); + findCurve(); + + // Special behaviour if we have a bomb attach: try to hit the kart ahead + // of us. + bool commands_set = false; + if(m_handle_bomb && + m_kart->getAttachment()->getType()==Attachment::ATTACH_BOMB && + m_kart_ahead ) + { + // Use nitro if the kart is far ahead, or faster than this kart + m_controls->m_nitro = m_distance_ahead>10.0f || + m_kart_ahead->getSpeed() > m_kart->getSpeed(); + // If we are close enough, try to hit this kart + if(m_distance_ahead<=10) + { + Vec3 target = m_kart_ahead->getXYZ(); + + // If we are faster, try to predict the point where we will hit + // the other kart + if(m_kart_ahead->getSpeed() < m_kart->getSpeed()) + { + float time_till_hit = m_distance_ahead + / (m_kart->getSpeed()-m_kart_ahead->getSpeed()); + target += m_kart_ahead->getVelocity()*time_till_hit; + } + float steer_angle = steerToPoint(m_kart_ahead->getXYZ()); + setSteering(steer_angle, dt); + commands_set = true; + } + handleRescue(dt); + } + if(!commands_set) + { + /*Response handling functions*/ + handleAcceleration(dt); + handleSteering(dt); + handleItems(dt); + handleRescue(dt); + handleBraking(); + // If a bomb is attached, nitro might already be set. + if(!m_controls->m_nitro) + handleNitroAndZipper(); + } + // If we are supposed to use nitro, but have a zipper, + // use the zipper instead + if(m_controls->m_nitro && + m_kart->getPowerup()->getType()==PowerupManager::POWERUP_ZIPPER && + m_kart->getSpeed()>1.0f && + m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0) + { + // Make sure that not all AI karts use the zipper at the same + // time in time trial at start up, so during the first 5 seconds + // this is done at random only. + if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_TIME_TRIAL || + (m_world->getTime()<3.0f && rand()%50==1) ) + { + m_controls->m_nitro = false; + m_controls->m_fire = true; + } + } + + /*And obviously general kart stuff*/ + AIBaseController::update(dt); +} // update + +//----------------------------------------------------------------------------- +void PresentAI::handleBraking() +{ + // In follow the leader mode, the kart should brake if they are ahead of + // the leader (and not the leader, i.e. don't have initial position 1) + if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER && + m_kart->getPosition() < m_world->getKart(0)->getPosition() && + m_kart->getInitialPosition()>1 ) + { + m_controls->m_brake = true; + return; + } + + const float MIN_SPEED = 5.0f; + //We may brake if we are about to get out of the road, but only if the + //kart is on top of the road, and if we won't slow down below a certain + //limit. + if (m_crashes.m_road && m_kart->getVelocityLC().getZ() > MIN_SPEED && + m_world->isOnRoad(m_kart->getWorldKartId()) ) + { + float kart_ang_diff = + QuadGraph::get()->getAngleToNext(m_track_node, + m_successor_index[m_track_node]) + - m_kart->getHeading(); + kart_ang_diff = normalizeAngle(kart_ang_diff); + kart_ang_diff = fabsf(kart_ang_diff); + + // FIXME: The original min_track_angle value of 20 degrees + // resulted in way too much braking. Is this test + // actually necessary at all??? + const float MIN_TRACK_ANGLE = DEGREE_TO_RAD*60.0f; + const float CURVE_INSIDE_PERC = 0.25f; + + //Brake only if the road does not goes somewhat straight. + if(m_curve_angle > MIN_TRACK_ANGLE) //Next curve is left + { + //Avoid braking if the kart is in the inside of the curve, but + //if the curve angle is bigger than what the kart can steer, brake + //even if we are in the inside, because the kart would be 'thrown' + //out of the curve. + if(!(m_world->getDistanceToCenterForKart(m_kart->getWorldKartId()) + > QuadGraph::get()->getNode(m_track_node).getPathWidth() * + -CURVE_INSIDE_PERC || + m_curve_angle > RAD_TO_DEGREE*m_kart->getMaxSteerAngle()) ) + { + m_controls->m_brake = false; + return; + } + } + else if( m_curve_angle < -MIN_TRACK_ANGLE ) //Next curve is right + { + if(!(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ) + < QuadGraph::get()->getNode(m_track_node).getPathWidth() * + CURVE_INSIDE_PERC || + m_curve_angle < -RAD_TO_DEGREE*m_kart->getMaxSteerAngle())) + { + m_controls->m_brake = false; + return; + } + } + + //Brake if the kart's speed is bigger than the speed we need + //to go through the curve at the widest angle, or if the kart + //is not going straight in relation to the road. + if(m_kart->getVelocityLC().getZ() > m_curve_target_speed || + kart_ang_diff > MIN_TRACK_ANGLE ) + { +#ifdef AI_DEBUG + std::cout << "BRAKING" << std::endl; +#endif + m_controls->m_brake = true; + return; + } + + } + + m_controls->m_brake = false; +} // handleBraking + +//----------------------------------------------------------------------------- +void PresentAI::handleSteering(float dt) +{ + const int next = m_next_node_index[m_track_node]; + + float steer_angle = 0.0f; + + /*The AI responds based on the information we just gathered, using a + *finite state machine. + */ + //Reaction to being outside of the road + float side_dist = + m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ); + if( fabsf(side_dist) > + 0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f ) + { + // If the speed is negative, the kart is most likely being pushed + // away from a collision with the terrain, and this most likely means + // that the kart is off track. In this case, steer so that the kart + // will rotate towards the center of the track. E.g. if the kart is + // to the right, steer towards the right. +#ifdef XX + if(m_kart->getSpeed()<0) + { + steer_angle = side_dist > 0 ? -m_kart->getMaxSteerAngle() + : m_kart->getMaxSteerAngle(); + } + else +#endif + steer_angle = steerToPoint(QuadGraph::get()->getQuadOfNode(next) + .getCenter()); + +#ifdef AI_DEBUG + m_debug_sphere->setPosition(QuadGraph::get()->getQuadOfNode(next) + .getCenter().toIrrVector()); + std::cout << "- Outside of road: steer to center point." << + std::endl; +#endif + } + //If we are going to crash against a kart, avoid it if it doesn't + //drives the kart out of the road + else if( m_crashes.m_kart != -1 && !m_crashes.m_road ) + { + //-1 = left, 1 = right, 0 = no crash. + if( m_start_kart_crash_direction == 1 ) + { + steer_angle = steerToAngle(next, -M_PI*0.5f ); + m_start_kart_crash_direction = 0; + } + else if(m_start_kart_crash_direction == -1) + { + steer_angle = steerToAngle(next, M_PI*0.5f); + m_start_kart_crash_direction = 0; + } + else + { + if(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ) > + m_world->getDistanceToCenterForKart( m_crashes.m_kart )) + { + steer_angle = steerToAngle(next, -M_PI*0.5f ); + m_start_kart_crash_direction = 1; + } + else + { + steer_angle = steerToAngle(next, M_PI*0.5f ); + m_start_kart_crash_direction = -1; + } + } + +#ifdef AI_DEBUG + std::cout << "- Velocity vector crashes with kart and doesn't " << + "crashes with road : steer 90 degrees away from kart." << + std::endl; +#endif + + } + else + { + m_start_kart_crash_direction = 0; + Vec3 straight_point; + findNonCrashingPoint(&straight_point); + + + Vec3 me = m_kart->getXYZ(); + Vec3 minpos = straight_point; + float mindist = -1000; + int mintype=-100; + for (unsigned int i = 0; i < item_manager->getNumberOfItems(); i++) + { + const Item *item = item_manager->getItem(i); + if (item && !item->wasCollected() && + (item->getType() == Item::ITEM_BONUS_BOX || + item->getType() == Item::ITEM_NITRO_BIG || + item->getType() == Item::ITEM_NITRO_SMALL)) + { + const Vec3 &pos = item->getXYZ(); + Vec3 dir = pos - me; + float dist = dir.length2_2d(); + if (dist < mindist || mindist < -100) + { + item->getType(); + mindist = dist; + minpos = pos; + mintype = item->getType(); + } // if dist < mindist || mindist<-100 + } // if item && ... + } // for i getNumberOfItems() + float w = QuadGraph::get()->getNode(m_track_node).getPathWidth(); + w *= w; + if ((btAngle(straight_point - me, minpos - me) < M_PI / 8 && mindist < w * 8) || + (btAngle(straight_point - me, minpos - me) < M_PI / 4 && + btAngle(m_kart->getVelocity(), minpos - me) < M_PI / 6 && mindist < w * 4)) { + straight_point = minpos; + } + + + + +#ifdef AI_DEBUG + m_debug_sphere->setPosition(straight_point.toIrrVector()); + std::cout << "- Velocity vector crashes with kart and doesn't " << + "crashes with road : steer 90 degrees away from kart." << + std::endl; +#endif + steer_angle = steerToPoint(straight_point); + } + + setSteering(steer_angle, dt); +} // handleSteering + +//----------------------------------------------------------------------------- +/** Handle all items depending on the chosen strategy: Either (low level AI) + * just use an item after 10 seconds, or do a much better job on higher level + * AI - e.g. aiming at karts ahead/behind, wait an appropriate time before + * using multiple items etc. + */ +void PresentAI::handleItems(const float dt) +{ + m_controls->m_fire = false; + if(m_kart->getKartAnimation() || + m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING ) + return; + + m_time_since_last_shot += dt; + + // Tactic 1: wait ten seconds, then use item + // ----------------------------------------- + if(m_item_tactic==IT_TEN_SECONDS) + { + if( m_time_since_last_shot > 10.0f ) + { + m_controls->m_fire = true; + m_time_since_last_shot = 0.0f; + } + return; + } + + // Tactic 2: calculate + // ------------------- + switch( m_kart->getPowerup()->getType() ) + { + case PowerupManager::POWERUP_BUBBLEGUM: + // Avoid dropping all bubble gums one after another + if( m_time_since_last_shot <3.0f) break; + + // Either use the bubble gum after 10 seconds, or if the next kart + // behind is 'close' but not too close (too close likely means that the + // kart is not behind but more to the side of this kart and so won't + // be hit by the bubble gum anyway). Should we check the speed of the + // kart as well? I.e. only drop if the kart behind is faster? Otoh + // this approach helps preventing an overtaken kart to overtake us + // again. + m_controls->m_fire = (m_distance_behind < 15.0f && + m_distance_behind > 3.0f ); + break; // POWERUP_BUBBLEGUM + + // All the thrown/fired items might be improved by considering the angle + // towards m_kart_ahead. + case PowerupManager::POWERUP_CAKE: + { + // Leave some time between shots + if(m_time_since_last_shot<3.0f) break; + // Since cakes can be fired all around, just use a sane distance + // with a bit of extra for backwards, as enemy will go towards cake + bool fire_backwards = (m_kart_behind && m_kart_ahead && + m_distance_behind < m_distance_ahead) || + !m_kart_ahead; + float distance = fire_backwards ? m_distance_behind + : m_distance_ahead; + m_controls->m_fire = (fire_backwards && distance < 25.0f) || + (!fire_backwards && distance < 20.0f); + if(m_controls->m_fire) + m_controls->m_look_back = fire_backwards; + break; + } // POWERUP_CAKE + + case PowerupManager::POWERUP_BOWLING: + { + // Leave more time between bowling balls, since they are + // slower, so it should take longer to hit something which + // can result in changing our target. + if(m_time_since_last_shot < 5.0f) break; + // Bowling balls are slower, so only fire on closer karts - but when + // firing backwards, the kart can be further away, since the ball + // acts a bit like a mine (and the kart is racing towards it, too) + bool fire_backwards = (m_kart_behind && m_kart_ahead && + m_distance_behind < m_distance_ahead) || + !m_kart_ahead; + float distance = fire_backwards ? m_distance_behind + : m_distance_ahead; + m_controls->m_fire = ( (fire_backwards && distance < 30.0f) || + (!fire_backwards && distance <10.0f) ) && + m_time_since_last_shot > 3.0f; + if(m_controls->m_fire) + m_controls->m_look_back = fire_backwards; + break; + } // POWERUP_BOWLING + + case PowerupManager::POWERUP_ZIPPER: + // Do nothing. Further up a zipper is used if nitro should be selected, + // saving the (potential more valuable nitro) for later + break; // POWERUP_ZIPPER + + case PowerupManager::POWERUP_PLUNGER: + { + // Leave more time after a plunger, since it will take some + // time before a plunger effect becomes obvious. + if(m_time_since_last_shot < 5.0f) break; + + // Plungers can be fired backwards and are faster, + // so allow more distance for shooting. + bool fire_backwards = (m_kart_behind && m_kart_ahead && + m_distance_behind < m_distance_ahead) || + !m_kart_ahead; + float distance = fire_backwards ? m_distance_behind + : m_distance_ahead; + m_controls->m_fire = distance < 30.0f || + m_time_since_last_shot > 10.0f; + if(m_controls->m_fire) + m_controls->m_look_back = fire_backwards; + break; + } // POWERUP_PLUNGER + + case PowerupManager::POWERUP_SWITCH: + // For now don't use a switch if this kart is first (since it's more + // likely that this kart then gets a good iteam), otherwise use it + // after a waiting an appropriate time + if(m_kart->getPosition()>1 && + m_time_since_last_shot > stk_config->m_item_switch_time+2.0f) + m_controls->m_fire = true; + break; // POWERUP_SWITCH + + case PowerupManager::POWERUP_PARACHUTE: + // Wait one second more than a previous parachute + if(m_time_since_last_shot > stk_config->m_parachute_time_other+1.0f) + m_controls->m_fire = true; + break; // POWERUP_PARACHUTE + + case PowerupManager::POWERUP_ANVIL: + // Wait one second more than a previous anvil + if(m_time_since_last_shot < stk_config->m_anvil_time+1.0f) break; + + if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER) + { + m_controls->m_fire = m_world->getTime()<1.0f && + m_kart->getPosition()>2; + } + else + { + m_controls->m_fire = m_time_since_last_shot > 3.0f && + m_kart->getPosition()>1; + } + break; // POWERUP_ANVIL + + case PowerupManager::POWERUP_SWATTER: + { + // Squared distance for which the swatter works + float d2 = m_kart->getKartProperties()->getSwatterDistance2(); + // Fire if the closest kart ahead or to the back is not already + // squashed and close enough. + // FIXME: this can be improved on, since more than one kart might + // be hit, and a kart ahead might not be at an angle at + // which the glove can be used. + if( ( m_kart_ahead && !m_kart_ahead->isSquashed() && + (m_kart_ahead->getXYZ()-m_kart->getXYZ()).length2()getSpeed() < m_kart->getSpeed() ) || + ( m_kart_behind && !m_kart_behind->isSquashed() && + (m_kart_behind->getXYZ()-m_kart->getXYZ()).length2()m_fire = true; + break; + } + case PowerupManager::POWERUP_RUBBERBALL: + // Perhaps some more sophisticated algorithm might be useful. + // For now: fire if there is a kart ahead (which means that + // this kart is certainly not the first kart) + m_controls->m_fire = m_kart_ahead != NULL; + break; + default: + printf("Invalid or unhandled powerup '%d' in default AI.\n", + m_kart->getPowerup()->getType()); + assert(false); + } + if(m_controls->m_fire) m_time_since_last_shot = 0.0f; +} // handleItems + +//----------------------------------------------------------------------------- +/** Determines the closest karts just behind and in front of this kart. The + * 'closeness' is for now simply based on the position, i.e. if a kart is + * more than one lap behind or ahead, it is not considered to be closest. + */ +void PresentAI::computeNearestKarts() +{ + bool need_to_check = false; + int my_position = m_kart->getPosition(); + // See if the kart ahead has changed: + if( ( m_kart_ahead && m_kart_ahead->getPosition()+1!=my_position ) || + (!m_kart_ahead && my_position>1 ) ) + need_to_check = true; + // See if the kart behind has changed: + if( ( m_kart_behind && m_kart_behind->getPosition()-1!=my_position ) || + (!m_kart_behind && my_position<(int)m_world->getCurrentNumKarts()) ) + need_to_check = true; + if(!need_to_check) return; + + m_kart_behind = m_kart_ahead = NULL; + m_distance_ahead = m_distance_behind = 9999999.9f; + float my_dist = m_world->getDistanceDownTrackForKart(m_kart->getWorldKartId()); + for(unsigned int i=0; igetNumKarts(); i++) + { + AbstractKart *k = m_world->getKart(i); + if(k->isEliminated() || k->hasFinishedRace() || k==m_kart) continue; + if(k->getPosition()==my_position+1) + { + m_kart_behind = k; + m_distance_behind = my_dist - m_world->getDistanceDownTrackForKart(i); + if(m_distance_behind<0.0f) + m_distance_behind += m_track->getTrackLength(); + } + else + if(k->getPosition()==my_position-1) + { + m_kart_ahead = k; + m_distance_ahead = m_world->getDistanceDownTrackForKart(i) - my_dist; + if(m_distance_ahead<0.0f) + m_distance_ahead += m_track->getTrackLength(); + } + } // for igetNumKarts() +} // computeNearestKarts + +//----------------------------------------------------------------------------- +void PresentAI::handleAcceleration( const float dt) +{ + //Do not accelerate until we have delayed the start enough + if( m_start_delay > 0.0f ) + { + m_start_delay -= dt; + m_controls->m_accel = 0.0f; + return; + } + + if( m_controls->m_brake == true ) + { + m_controls->m_accel = 0.0f; + return; + } + + if(m_kart->hasViewBlockedByPlunger()) + { + if(!(m_kart->getSpeed() > m_kart->getCurrentMaxSpeed() / 2)) + m_controls->m_accel = 0.05f; + else + m_controls->m_accel = 0.0f; + return; + } + + + // FIXME: this needs to be rewritten, it doesn't make any sense: + // wait for players triggers the opposite (if a player is ahead + // of this AI, go full speed). Besides, it's going to use full + // speed anyway. + if( m_wait_for_players ) + { + //Find if any player is ahead of this kart + bool player_winning = false; + for(unsigned int i = 0; i < race_manager->getNumPlayers(); ++i ) + if( m_kart->getPosition() > m_world->getPlayerKart(i)->getPosition() ) + { + player_winning = true; + break; + } + + if( player_winning ) + { + m_controls->m_accel = m_max_handicap_speed; + return; + } + } + + m_controls->m_accel = stk_config->m_ai_acceleration; +} // handleAcceleration + +//----------------------------------------------------------------------------- +void PresentAI::handleRaceStart() +{ + if( m_start_delay < 0.0f ) + { + // Each kart starts at a different, random time, and the time is + // smaller depending on the difficulty. + m_start_delay = m_min_start_delay + + (float) rand() / RAND_MAX * (m_max_start_delay-m_min_start_delay); + + // Now check for a false start. If so, add 1 second penalty time. + if(rand() < RAND_MAX * m_false_start_probability) + { + m_start_delay+=stk_config->m_penalty_time; + return; + } + } +} // handleRaceStart + +//----------------------------------------------------------------------------- +void PresentAI::handleRescue(const float dt) +{ + // check if kart is stuck + if(m_kart->getSpeed()<2.0f && !m_kart->getKartAnimation() && + !m_world->isStartPhase()) + { + m_time_since_stuck += dt; + if(m_time_since_stuck > 2.0f) + { + new RescueAnimation(m_kart); + m_time_since_stuck=0.0f; + } // m_time_since_stuck > 2.0f + } + else + { + m_time_since_stuck = 0.0f; + } +} // handleRescue + +//----------------------------------------------------------------------------- +/** Decides wether to use nitro or not. + */ +void PresentAI::handleNitroAndZipper() +{ + m_controls->m_nitro = false; + // If we are already very fast, save nitro. + if(m_kart->getSpeed() > 0.95f*m_kart->getCurrentMaxSpeed()) + return; + // Don't use nitro when the AI has a plunger in the face! + if(m_kart->hasViewBlockedByPlunger()) return; + + // Don't use nitro if the kart doesn't have any or is not on ground. + if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return; + + // Don't compute nitro usage if we don't have nitro or are not supposed + // to use it, and we don't have a zipper or are not supposed to use + // it (calculated). + if( (m_kart->getEnergy()==0 || m_nitro_level==NITRO_NONE) && + (m_kart->getPowerup()->getType()!=PowerupManager::POWERUP_ZIPPER || + m_item_tactic==IT_TEN_SECONDS ) ) + return; + + // If a parachute or anvil is attached, the nitro doesn't give much + // benefit. Better wait till later. + const bool has_slowdown_attachment = + m_kart->getAttachment()->getType()==Attachment::ATTACH_PARACHUTE || + m_kart->getAttachment()->getType()==Attachment::ATTACH_ANVIL; + if(has_slowdown_attachment) return; + + // If the kart is very slow (e.g. after rescue), use nitro + if(m_kart->getSpeed()<5) + { + m_controls->m_nitro = true; + return; + } + + // If this kart is the last kart, and we have enough + // (i.e. more than 2) nitro, use it. + // ------------------------------------------------- + const unsigned int num_karts = m_world->getCurrentNumKarts(); + if(m_kart->getPosition()== (int)num_karts && m_kart->getEnergy()>2.0f) + { + m_controls->m_nitro = true; + return; + } + + // On the last track shortly before the finishing line, use nitro + // anyway. Since the kart is faster with nitro, estimate a 50% time + // decrease (additionally some nitro will be saved when top speed + // is reached). + if(m_world->getLapForKart(m_kart->getWorldKartId())==race_manager->getNumLaps()-1 && + m_nitro_level == NITRO_ALL) + { + float finish = m_world->getEstimatedFinishTime(m_kart->getWorldKartId()); + if( 1.5f*m_kart->getEnergy() >= finish - m_world->getTime() ) + { + m_controls->m_nitro = true; + return; + } + } + + // A kart within this distance is considered to be overtaking (or to be + // overtaken). + const float overtake_distance = 10.0f; + + // Try to overtake a kart that is close ahead, except + // when we are already much faster than that kart + // -------------------------------------------------- + if(m_kart_ahead && + m_distance_ahead < overtake_distance && + m_kart_ahead->getSpeed()+5.0f > m_kart->getSpeed() ) + { + m_controls->m_nitro = true; + return; + } + + if(m_kart_behind && + m_distance_behind < overtake_distance && + m_kart_behind->getSpeed() > m_kart->getSpeed() ) + { + // Only prevent overtaking on highest level + m_controls->m_nitro = m_nitro_level==NITRO_ALL; + return; + } + +} // handleNitroAndZipper + +//----------------------------------------------------------------------------- +void PresentAI::checkCrashes(int steps, const Vec3& pos ) +{ + //Right now there are 2 kind of 'crashes': with other karts and another + //with the track. The sight line is used to find if the karts crash with + //each other, but the first step is twice as big as other steps to avoid + //having karts too close in any direction. The crash with the track can + //tell when a kart is going to get out of the track so it steers. + m_crashes.clear(); + + // If slipstream should be handled actively, trigger overtaking the + // kart which gives us slipstream if slipstream is ready + const SlipStream *slip=m_kart->getSlipstream(); + if(m_make_use_of_slipstream && slip->isSlipstreamReady() && + slip->getSlipstreamTarget()) + { + //printf("%s overtaking %s\n", m_kart->getIdent().c_str(), + // m_kart->getSlipstreamKart()->getIdent().c_str()); + // FIXME: we might define a minimum distance, and if the target kart + // is too close break first - otherwise the AI hits the kart when + // trying to overtake it, actually speeding the other kart up. + m_crashes.m_kart = slip->getSlipstreamTarget()->getWorldKartId(); + } + + const size_t NUM_KARTS = m_world->getNumKarts(); + + //Protection against having vel_normal with nan values + const Vec3 &VEL = m_kart->getVelocity(); + Vec3 vel_normal(VEL.getX(), 0.0, VEL.getZ()); + float speed=vel_normal.length(); + // If the velocity is zero, no sense in checking for crashes in time + if(speed==0) return; + + // Time it takes to drive for m_kart_length units. + float dt = m_kart_length / speed; + vel_normal/=speed; + + int current_node = m_track_node; + if(steps<1 || steps>1000) + { + printf("Warning, incorrect STEPS=%d. kart_length %f velocity %f\n", + steps, m_kart_length, m_kart->getVelocityLC().getZ()); + steps=1000; + } + for(int i = 1; steps > i; ++i) + { + Vec3 step_coord = pos + vel_normal* m_kart_length * float(i); + + /* Find if we crash with any kart, as long as we haven't found one + * yet + */ + if( m_crashes.m_kart == -1 ) + { + for( unsigned int j = 0; j < NUM_KARTS; ++j ) + { + const AbstractKart* kart = m_world->getKart(j); + // Ignore eliminated karts + if(kart==m_kart||kart->isEliminated()) continue; + const AbstractKart *other_kart = m_world->getKart(j); + // Ignore karts ahead that are faster than this kart. + if(m_kart->getVelocityLC().getZ() < other_kart->getVelocityLC().getZ()) + continue; + Vec3 other_kart_xyz = other_kart->getXYZ() + other_kart->getVelocity()*(i*dt); + float kart_distance = (step_coord - other_kart_xyz).length_2d(); + + if( kart_distance < m_kart_length) + m_crashes.m_kart = j; + } + } + + /*Find if we crash with the drivelines*/ + if(current_node!=QuadGraph::UNKNOWN_SECTOR && + m_next_node_index[current_node]!=-1) + QuadGraph::get()->findRoadSector(step_coord, ¤t_node, + /* sectors to test*/ &m_all_look_aheads[current_node]); + + if( current_node == QuadGraph::UNKNOWN_SECTOR) + { + m_crashes.m_road = true; + return; + } + } +} // checkCrashes + +//----------------------------------------------------------------------------- +/** Find the sector that at the longest distance from the kart, that can be + * driven to without crashing with the track, then find towards which of + * the two edges of the track is closest to the next curve after wards, + * and return the position of that edge. + */ +void PresentAI::findNonCrashingPoint(Vec3 *result) +{ + unsigned int sector = m_next_node_index[m_track_node]; + int target_sector; + + Vec3 direction; + Vec3 step_track_coord; + + // The original while(1) loop is replaced with a for loop to avoid + // infinite loops (which we had once or twice). Usually the number + // of iterations in the while loop is less than 7. + for(unsigned int i=0; i<100; i++) + { + //target_sector is the sector at the longest distance that we can drive + //to without crashing with the track. + target_sector = m_next_node_index[sector]; + + //direction is a vector from our kart to the sectors we are testing + direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() + - m_kart->getXYZ(); + + float len=direction.length_2d(); + unsigned int steps = (unsigned int)( len / m_kart_length ); + if( steps < 3 ) steps = 3; + + // That shouldn't happen, but since we had one instance of + // STK hanging, add an upper limit here (usually it's at most + // 20 steps) + if( steps>1000) steps = 1000; + + //Protection against having vel_normal with nan values + if(len>0.0f) { + direction*= 1.0f/len; + } + + Vec3 step_coord; + //Test if we crash if we drive towards the target sector + for(unsigned int i = 2; i < steps; ++i ) + { + step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); + + QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + sector ); + + float distance = fabsf(step_track_coord[0]); + + //If we are outside, the previous sector is what we are looking for + if ( distance + m_kart_width * 0.5f + > QuadGraph::get()->getNode(sector).getPathWidth() ) + { + *result = QuadGraph::get()->getQuadOfNode(sector).getCenter(); + return; + } + } + sector = target_sector; + } // for i<100 + *result = QuadGraph::get()->getQuadOfNode(sector).getCenter(); +} // findNonCrashingPoint + +//----------------------------------------------------------------------------- +/** calc_steps() divides the velocity vector by the lenght of the kart, + * and gets the number of steps to use for the sight line of the kart. + * The calling sequence guarantees that m_future_sector is not UNKNOWN. + */ +int PresentAI::calcSteps() +{ + int steps = int( m_kart->getVelocityLC().getZ() / m_kart_length ); + if( steps < m_min_steps ) steps = m_min_steps; + + //Increase the steps depending on the width, if we steering hard, + //mostly for curves. +#if 0 + // FIXME: I don't understand this: if we are steering hard, we check + // for more steps if we hit another kart?? If we steer hard, + // the approximation used (pos + velocity*dt) will be even + // worse, since it doesn't take steering into account. + if( fabsf(m_controls->m_steer) > 0.95 ) + { + const int WIDTH_STEPS = + (int)( QuadGraph::get()->getNode(m_future_sector).getPathWidth() + /( m_kart_length * 2.0 ) ); + + steps += WIDTH_STEPS; + } +#endif + // The AI is driving significantly better with more steps, so for now + // add 5 additional steps. + return steps+5; +} // calcSteps + +//----------------------------------------------------------------------------- +/**FindCurve() gathers info about the closest sectors ahead: the curve + * angle, the direction of the next turn, and the optimal speed at which the + * curve can be travelled at it's widest angle. + * + * The number of sectors that form the curve is dependant on the kart's speed. + */ +void PresentAI::findCurve() +{ + float total_dist = 0.0f; + int i; + for(i = m_track_node; total_dist < m_kart->getVelocityLC().getZ(); + i = m_next_node_index[i]) + { + total_dist += QuadGraph::get()->getDistanceToNext(i, + m_successor_index[i]); + } + + + m_curve_angle = + normalizeAngle(QuadGraph::get()->getAngleToNext(i, + m_successor_index[i]) + -QuadGraph::get()->getAngleToNext(m_track_node, + m_successor_index[m_track_node]) ); + + m_curve_target_speed = m_kart->getCurrentMaxSpeed(); +} // findCurve diff --git a/src/karts/controller/present_ai.hpp b/src/karts/controller/present_ai.hpp new file mode 100644 index 000000000..5d00d5381 --- /dev/null +++ b/src/karts/controller/present_ai.hpp @@ -0,0 +1,314 @@ + +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2004-2005 Steve Baker +// Copyright (C) 2006-2007 Eduardo Hernandez Munoz +// Copyright (C) 2010 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_PRESENT_AI_HPP +#define HEADER_PRESENT_AI_HPP + +#include "karts/controller/ai_base_controller.hpp" + +class Track; +class LinearWorld; +class QuadGraph; + +namespace irr +{ + namespace scene + { + class ISceneNode; + } +} + +/** + * \ingroup controller + */ +class PresentAI : public AIBaseController +{ +private: + /** How the AI uses nitro. */ + enum {NITRO_NONE, NITRO_SOME, NITRO_ALL} m_nitro_level; + enum ItemTactic + { + IT_TEN_SECONDS, //Fire after 10 seconds have passed, since the item + //was grabbed. + IT_CALCULATE //Aim carefully, check for enough space for boosters, + //and that other conditions are meet before firing. + }; + + class CrashTypes + { + public: + + bool m_road; //true if we are going to 'crash' with the bounds of the road + int m_kart; //-1 if no crash, pos numbers are the kart it crashes with + CrashTypes() : m_road(false), m_kart(-1) {}; + void clear() {m_road = false; m_kart = -1;} + } m_crashes; + + /*Difficulty handling variables*/ + /** Chance of a false start. */ + float m_false_start_probability; + /** The minimum delay time before a AI kart starts. */ + float m_min_start_delay; + /** The maximum delay time before an AI kart starts. */ + float m_max_start_delay; + /** The actual start delay used. */ + float m_start_delay; + + /** Minimum number of steps to check. If 0, the AI doesn't even has check + * around the kart, if 1, it checks around the kart always, and more + * than that will check the remaining number of steps in front of the + * kart, always. */ + int m_min_steps; + /** If true, the acceleration is decreased when the AI is in a better + * position than all the human players. */ + bool m_wait_for_players; + + /** The allowed maximum speed in percent of the kart's maximum speed. */ + float m_max_handicap_speed; + + /** How are items going to be used? */ + ItemTactic m_item_tactic; + + /** True if the kart should try to pass on a bomb to another kart. */ + bool m_handle_bomb; + + /** True if the AI should avtively try to make use of slipstream. */ + bool m_make_use_of_slipstream; + + /*General purpose variables*/ + + /** Pointer to the closest kart ahead of this kart. NULL if this + * kart is first. */ + AbstractKart *m_kart_ahead; + + /** Distance to the kart ahead. */ + float m_distance_ahead; + + /** Pointer to the closest kart behind this kart. NULL if this kart + * is last. */ + AbstractKart *m_kart_behind; + + /** Distance to the kard behind. */ + float m_distance_behind; + + /** Time an item has been collected and not used. */ + float m_time_since_last_shot; + + float m_curve_target_speed; + float m_curve_angle; + + float m_time_since_stuck; + + int m_start_kart_crash_direction; //-1 = left, 1 = right, 0 = no crash. + + /** For debugging purpose: a sphere indicating where the AI + * is targeting at. */ + irr::scene::ISceneNode *m_debug_sphere; + + /*Functions called directly from update(). They all represent an action + *that can be done, and end up setting their respective m_controls + *variable, except handle_race_start() that isn't associated with any + *specific action (more like, associated with inaction). + */ + void handleRaceStart(); + void handleAcceleration(const float dt); + void handleSteering(float dt); + void handleItems(const float dt); + void handleRescue(const float dt); + void handleBraking(); + void handleNitroAndZipper(); + void computeNearestKarts(); + + void checkCrashes(int steps, const Vec3& pos); + void findNonCrashingPoint(Vec3 *result); + + int calcSteps(); + void findCurve(); +protected: + virtual unsigned int getNextSector(unsigned int index); + +public: + PresentAI(AbstractKart *kart); + ~PresentAI(); + virtual void update (float delta); + virtual void reset (); +}; // PresentAI + +#endif + +/* EOF */ + +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2004-2005 Steve Baker +// Copyright (C) 2006-2007 Eduardo Hernandez Munoz +// Copyright (C) 2010 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_PRESENT_AI_HPP +#define HEADER_PRESENT_AI_HPP + +#include "karts/controller/ai_base_controller.hpp" + +class Track; +class LinearWorld; +class QuadGraph; + +namespace irr +{ + namespace scene + { + class ISceneNode; + } +} + +/** + * \ingroup controller + */ +class PresentAI : public AIBaseController +{ +private: + /** How the AI uses nitro. */ + enum {NITRO_NONE, NITRO_SOME, NITRO_ALL} m_nitro_level; + enum ItemTactic + { + IT_TEN_SECONDS, //Fire after 10 seconds have passed, since the item + //was grabbed. + IT_CALCULATE //Aim carefully, check for enough space for boosters, + //and that other conditions are meet before firing. + }; + + class CrashTypes + { + public: + + bool m_road; //true if we are going to 'crash' with the bounds of the road + int m_kart; //-1 if no crash, pos numbers are the kart it crashes with + CrashTypes() : m_road(false), m_kart(-1) {}; + void clear() {m_road = false; m_kart = -1;} + } m_crashes; + + /*Difficulty handling variables*/ + /** Chance of a false start. */ + float m_false_start_probability; + /** The minimum delay time before a AI kart starts. */ + float m_min_start_delay; + /** The maximum delay time before an AI kart starts. */ + float m_max_start_delay; + /** The actual start delay used. */ + float m_start_delay; + + /** Minimum number of steps to check. If 0, the AI doesn't even has check + * around the kart, if 1, it checks around the kart always, and more + * than that will check the remaining number of steps in front of the + * kart, always. */ + int m_min_steps; + /** If true, the acceleration is decreased when the AI is in a better + * position than all the human players. */ + bool m_wait_for_players; + + /** The allowed maximum speed in percent of the kart's maximum speed. */ + float m_max_handicap_speed; + + /** How are items going to be used? */ + ItemTactic m_item_tactic; + + /** True if the kart should try to pass on a bomb to another kart. */ + bool m_handle_bomb; + + /** True if the AI should avtively try to make use of slipstream. */ + bool m_make_use_of_slipstream; + + /*General purpose variables*/ + + /** Pointer to the closest kart ahead of this kart. NULL if this + * kart is first. */ + AbstractKart *m_kart_ahead; + + /** Distance to the kart ahead. */ + float m_distance_ahead; + + /** Pointer to the closest kart behind this kart. NULL if this kart + * is last. */ + AbstractKart *m_kart_behind; + + /** Distance to the kard behind. */ + float m_distance_behind; + + /** Time an item has been collected and not used. */ + float m_time_since_last_shot; + + float m_curve_target_speed; + float m_curve_angle; + + float m_time_since_stuck; + + int m_start_kart_crash_direction; //-1 = left, 1 = right, 0 = no crash. + + /** For debugging purpose: a sphere indicating where the AI + * is targeting at. */ + irr::scene::ISceneNode *m_debug_sphere; + + /*Functions called directly from update(). They all represent an action + *that can be done, and end up setting their respective m_controls + *variable, except handle_race_start() that isn't associated with any + *specific action (more like, associated with inaction). + */ + void handleRaceStart(); + void handleAcceleration(const float dt); + void handleSteering(float dt); + void handleItems(const float dt); + void handleRescue(const float dt); + void handleBraking(); + void handleNitroAndZipper(); + void computeNearestKarts(); + + void checkCrashes(int steps, const Vec3& pos); + void findNonCrashingPoint(Vec3 *result); + + int calcSteps(); + void findCurve(); +protected: + virtual unsigned int getNextSector(unsigned int index); + +public: + PresentAI(AbstractKart *kart); + ~PresentAI(); + virtual void update (float delta) ; + virtual void reset (); +}; + +#endif + +/* EOF */ diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 37d0a1eb2..ac001df0f 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -37,6 +37,7 @@ #include "karts/controller/player_controller.hpp" #include "karts/controller/end_controller.hpp" #include "karts/controller/skidding_ai.hpp" +#include "karts/controller/present_ai.hpp" #include "karts/kart.hpp" #include "karts/kart_properties_manager.hpp" #include "modes/profile_world.hpp" @@ -243,11 +244,11 @@ Controller* World::loadAIController(AbstractKart *kart) // const int NUM_ROBOTS = 1; // For now: instead of random switching, use each // robot in turns: switch(m_random.get(NUM_ROBOTS)) - // static int turn=1; - // turn=1-turn; + //static int turn=0; + //turn=2-turn; // For now disable the new AI. - int turn=0; + int turn=2; switch(turn) { case 0: @@ -256,6 +257,9 @@ Controller* World::loadAIController(AbstractKart *kart) case 1: controller = new SkiddingAI(kart); break; + case 2: + controller = new PresentAI(kart); + break; default: fprintf(stderr, "Warning: Unknown robot, using default.\n"); controller = new DefaultAIController(kart);