diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 82e07f542..20647e28d 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -75,6 +75,8 @@ protected: float *radius) const; virtual void update (float delta); virtual void setSteering (float angle, float dt); + // ------------------------------------------------------------------------ + /** Return true if AI can skid now. */ virtual bool canSkid(float steer_fraction) = 0; public: diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 931ce326e..b667d17fa 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -74,6 +74,7 @@ void ArenaAI::reset() /** This is the main entry point for the AI. * It is called once per frame for each AI and determines the behaviour of * the AI, e.g. steering, accelerating/braking, firing. + * \param dt Time step size. */ void ArenaAI::update(float dt) { @@ -170,10 +171,7 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point) m_debug_sphere_next->setVisible(false); #endif - // Notice: we use the point ahead of kart to determine next node, - // to compensate the time difference between steering - m_current_forward_point = - m_kart->getTrans()(Vec3(0, 0, m_kart->getKartLength())); + m_current_forward_point = m_kart->getTrans()(Vec3(0, 0, m_kart_length)); m_turn_radius = 0.0f; std::vector* test_nodes = NULL; @@ -238,7 +236,7 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point) } // updateAimingPosition //----------------------------------------------------------------------------- -/** This function config the steering of AI. +/** This function config the steering (\ref m_steering_angle) of AI. */ void ArenaAI::configSteering() { @@ -294,6 +292,11 @@ void ArenaAI::configSteering() } // configSteering //----------------------------------------------------------------------------- +/** Determine whether AI is stuck, by checking if it stays on the same node for + * a long period of time (see \ref m_on_node), or \ref isStuck() is true. + * \param dt Time step size. + * \return True if AI is stuck + */ void ArenaAI::checkIfStuck(const float dt) { if (m_is_stuck) return; @@ -312,8 +315,7 @@ void ArenaAI::checkIfStuck(const float dt) && m_on_node.size() < 2 && !m_is_uturn && fabsf(m_kart->getSpeed()) < 3.0f) || isStuck() == true) { - // Check whether a kart stay on the same node for a period of time - // Or crashed 3 times + // AI is stuck, reset now and try to get unstuck at next frame m_on_node.clear(); m_time_since_driving = 0.0f; AIBaseController::reset(); @@ -355,6 +357,8 @@ void ArenaAI::configSpeed() } // configSpeed //----------------------------------------------------------------------------- +/** Make AI reverse so that it faces in front of the last target point. + */ void ArenaAI::doUTurn(const float dt) { float turn_angle = atan2f(m_target_point_lc.x(), @@ -377,6 +381,10 @@ void ArenaAI::doUTurn(const float dt) } // doUTurn //----------------------------------------------------------------------------- +/** Function to let AI get unstuck. + * \param dt Time step size. + * \return True if getting stuck is needed to be done. + */ bool ArenaAI::gettingUnstuck(const float dt) { if (!m_is_stuck || m_is_uturn) return false; @@ -398,6 +406,10 @@ bool ArenaAI::gettingUnstuck(const float dt) } // gettingUnstuck //----------------------------------------------------------------------------- +/** Determine how AI should use its item, different \ref m_cur_difficulty will + * have a corresponding strategy. + * \param dt Time step size. + */ void ArenaAI::useItems(const float dt) { m_controls->setFire(false); @@ -406,7 +418,7 @@ void ArenaAI::useItems(const float dt) return; // Find a closest kart again, this time we ignore difficulty - findClosestKart(false/*use_difficulty*/, false/*find_sta*/); + findClosestKart(false/*consider_difficulty*/, false/*find_sta*/); if (!m_closest_kart) return; Vec3 closest_kart_point_lc = @@ -558,7 +570,12 @@ void ArenaAI::useItems(const float dt) } // useItems //----------------------------------------------------------------------------- -void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const +/** Try to collect item in arena, if no suitable item is found, like they are + * swapped, it will follow closest kart instead. + * \param[out] aim_point Location of item. + * \param[out] target_node The node which item lied on. + */ +void ArenaAI::tryCollectItem(Vec3* aim_point, int* target_node) const { float distance = 999999.9f; Item* selected = (*target_node == Graph::UNKNOWN_SECTOR ? NULL : @@ -611,13 +628,15 @@ void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const *aim_point = m_closest_kart_point; *target_node = m_closest_kart_node; } -} // collectItemInArena +} // tryCollectItem //----------------------------------------------------------------------------- +/** Determine if AI should skid: When it's close to target, but not straight + * ahead, in front of it, same steering side and with suitable difficulties + * which are in expert and supertux only. + */ void ArenaAI::doSkiddingTest() { - // Skid when close to target, but not straight ahead, in front of it, same - // steering side and with suitable difficulties. const float abs_angle = atan2f(fabsf(m_target_point_lc.x()), fabsf(m_target_point_lc.z())); if ((m_cur_difficulty == RaceManager::DIFFICULTY_HARD || @@ -636,7 +655,7 @@ void ArenaAI::doSkiddingTest() /** Determine if the path to target needs to be changed to avoid bad items, it * will also set the turn radius based on the new path if necessary. * \param forward Forward node of current AI position. - * \param path Default path to target. + * \param[in,out] path Default path to follow, will be changed if needed. */ void ArenaAI::determinePath(int forward, std::vector* path) { diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 68cc29fb7..fee41d97b 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -32,7 +32,6 @@ class ArenaGraph; namespace irr { namespace scene { class ISceneNode; } - namespace video { class ITexture; } } /** A base class for AI that use navmesh to work. @@ -41,12 +40,16 @@ namespace irr class ArenaAI : public AIBaseController { protected: + /** Pointer to the \ref ArenaGraph. */ ArenaGraph* m_graph; /** Pointer to the closest kart around this kart. */ AbstractKart *m_closest_kart; + /** The \ref ArenaNode at which the closest kart located on. */ int m_closest_kart_node; + + /** The closest kart location. */ Vec3 m_closest_kart_point; /** Holds the current difficulty. */ @@ -55,25 +58,39 @@ protected: /** For debugging purpose: a sphere indicating where the AI * is targeting at. */ irr::scene::ISceneNode *m_debug_sphere; + + /** For debugging purpose: a sphere indicating where the first + * turning corner is located. */ irr::scene::ISceneNode *m_debug_sphere_next; - /** The node(quad) at which the target point lies in. */ + /** The \ref ArenaNode at which the target point located on. */ int m_target_node; - /** The target point. */ + /** The coordinates of target point. */ Vec3 m_target_point; + /** True if AI can skid, currently only do when close to target, see + * \ref doSkiddingTest(). */ bool m_mini_skid; - void collectItemInArena(Vec3*, int*) const; - virtual void findClosestKart(bool use_difficulty, bool find_sta) = 0; + // ------------------------------------------------------------------------ + void tryCollectItem(Vec3* aim_point, int* target_node) const; + // ------------------------------------------------------------------------ + /** Find the closest kart around this AI, implemented by sub-class. + * \param consider_difficulty If take current difficulty into account. + * \param find_sta If find \ref SpareTireAI only. */ + virtual void findClosestKart(bool consider_difficulty, bool find_sta) = 0; + private: + /** Local coordinates of current target point. */ Vec3 m_target_point_lc; + /** Save the last target point before reversing, so AI will end reversing + * until facing in front of it. */ Vec3 m_reverse_point; - /** Indicates that the kart is currently stuck, and m_time_since_reversing is - * counting down. */ + /** Indicates that the kart is currently stuck, and m_time_since_reversing + * is counting down. */ bool m_is_stuck; /** Indicates that the kart need a uturn to reach a node behind, and @@ -99,39 +116,79 @@ private: /** This is a timer that counts when the kart start going off road. */ float m_time_since_off_road; + /** Used to determine braking and nitro usage. */ float m_turn_radius; + /** Used to determine if skidding can be done. */ float m_steering_angle; + /** The point in front of the AI which distance is \ref m_kart_length, used + * to compensate the time difference between steering when finding next + * node. */ Vec3 m_current_forward_point; + /** The \ref ArenaNode at which the forward point located on. */ int m_current_forward_node; void configSpeed(); + // ------------------------------------------------------------------------ void configSteering(); + // ------------------------------------------------------------------------ void checkIfStuck(const float dt); + // ------------------------------------------------------------------------ void determinePath(int forward, std::vector* path); + // ------------------------------------------------------------------------ void doSkiddingTest(); + // ------------------------------------------------------------------------ void doUTurn(const float dt); + // ------------------------------------------------------------------------ bool gettingUnstuck(const float dt); + // ------------------------------------------------------------------------ bool updateAimingPosition(Vec3* target_point); + // ------------------------------------------------------------------------ void useItems(const float dt); + // ------------------------------------------------------------------------ virtual bool canSkid(float steer_fraction) OVERRIDE - { return m_mini_skid; } + { return m_mini_skid; } + // ------------------------------------------------------------------------ + /** Find a suitable target for this frame, implemented by sub-class. */ virtual void findTarget() = 0; - virtual bool forceBraking() { return false; } + // ------------------------------------------------------------------------ + /** If true, AI will always try to brake for this frame. */ + virtual bool forceBraking() { return false; } + // ------------------------------------------------------------------------ + /** Return the current \ref ArenaNode the AI located on. */ virtual int getCurrentNode() const = 0; + // ------------------------------------------------------------------------ + /** Return the distance based on graph distance matrix to any kart. + * \param kart \ref AbstractKart to check. */ virtual float getKartDistance(const AbstractKart* kart) const = 0; - virtual bool ignorePathFinding() { return false; } + // ------------------------------------------------------------------------ + /** If true, AI will drive directly to target without path finding. */ + virtual bool ignorePathFinding() { return false; } + // ------------------------------------------------------------------------ + /** If true, AI will stop moving. */ virtual bool isWaiting() const = 0; + // ------------------------------------------------------------------------ + /** If true, AI stays on the \ref ArenaNode correctly, otherwise + * \ref RescueAnimation will be done after sometime. */ virtual bool isKartOnRoad() const = 0; - virtual void resetAfterStop() {}; + // ------------------------------------------------------------------------ + /** Overridden if any action is needed to be done when AI stopped + * moving or changed driving direction. */ + virtual void resetAfterStop() {} + public: ArenaAI(AbstractKart *kart); - virtual ~ArenaAI() {}; + // ------------------------------------------------------------------------ + virtual ~ArenaAI() {} + // ------------------------------------------------------------------------ virtual void update (float delta) OVERRIDE; + // ------------------------------------------------------------------------ virtual void reset () OVERRIDE; + // ------------------------------------------------------------------------ virtual void newLap (int lap) OVERRIDE {} + }; #endif diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index c2278e9c4..2aba6e350 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -56,7 +56,6 @@ BattleAI::BattleAI(AbstractKart *kart) } // BattleAI //----------------------------------------------------------------------------- - BattleAI::~BattleAI() { #ifdef AI_DEBUG @@ -66,7 +65,12 @@ BattleAI::~BattleAI() } // ~BattleAI //----------------------------------------------------------------------------- -void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) +/** Find the closest kart around this AI, if consider_difficulty is true, AI + * will try to follow human players more or less depends on difficulty. + * \param consider_difficulty If take current difficulty into account. + * \param find_sta If find \ref SpareTireAI only. + */ +void BattleAI::findClosestKart(bool consider_difficulty, bool find_sta) { float distance = 99999.9f; int closest_kart_num = 0; @@ -88,7 +92,8 @@ void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) // Test whether takes current difficulty into account for closest kart // Notice: it don't affect aiming, this function will be called once // more when use items, which ignore difficulty. - if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && use_difficulty) + if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && + consider_difficulty) { // Skip human players for novice mode unless only they are left const AbstractKart* temp = m_world->getKart(start_id); @@ -98,7 +103,7 @@ void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) continue; } else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && - use_difficulty) + consider_difficulty) { // Skip AI players for supertux mode const AbstractKart* temp = m_world->getKart(start_id); @@ -122,33 +127,44 @@ void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) } // findClosestKart //----------------------------------------------------------------------------- +/** Find a suitable target to follow, it will find the closest kart first, it's + * used as fallback if no item is found. It takes the current difficulty into + * account, also collect life from \ref SpareTireAI depends on current + * difficulty if actually they are spawned: + * \li Novice and intermediate - collect them only AI has 1 life only. + * \li Expert and supertux - collect them if AI dones't have 3 lives. + */ void BattleAI::findTarget() { - // Find the closest kart first, it's used as fallback if no item is found. - // It takes the current difficulty into account, also collect life from - // spare tire karts when neccessary + bool find_sta = false; + if (m_world->spareTireKartsSpawned()) + { + switch (m_cur_difficulty) + { + case RaceManager::DIFFICULTY_EASY: + case RaceManager::DIFFICULTY_MEDIUM: + { + find_sta = m_world->getKartLife(m_kart->getWorldKartId()) == 1; + break; + } + case RaceManager::DIFFICULTY_HARD: + case RaceManager::DIFFICULTY_BEST: + { + find_sta = m_world->getKartLife(m_kart->getWorldKartId()) != 3; + break; + } + default: assert(false); + } + } - // Collect life depends on current difficulty: - // Novice and intermediate - collect them only AI has 1 life only - // Expert and supertux - collect them if AI dones't have 3 lives - // Also when actually spare tire karts are spawned - bool find_sta = m_world->spareTireKartsSpawned() ? - ((m_cur_difficulty == RaceManager::DIFFICULTY_EASY || - m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM) && - m_world->getKartLife(m_kart->getWorldKartId()) == 1 ? - true : - (m_cur_difficulty == RaceManager::DIFFICULTY_HARD || - m_cur_difficulty == RaceManager::DIFFICULTY_BEST) && - m_world->getKartLife(m_kart->getWorldKartId()) != 3 ? - true : false) : false; - - findClosestKart(find_sta ? false : true/*use_difficulty*/, find_sta); + bool consider_difficulty = !find_sta; + findClosestKart(consider_difficulty, find_sta); // Find a suitable target to drive to, either powerup or kart if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER && !find_sta) - collectItemInArena(&m_target_point , &m_target_node); + tryCollectItem(&m_target_point , &m_target_node); else { m_target_point = m_closest_kart_point; diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 915f34d15..90ec63463 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -33,16 +33,28 @@ class BattleAI : public ArenaAI protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; - virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; + + // ------------------------------------------------------------------------ + virtual void findClosestKart(bool consider_difficulty, + bool find_sta) OVERRIDE; + // ------------------------------------------------------------------------ virtual int getCurrentNode() const OVERRIDE; + private: + // ------------------------------------------------------------------------ virtual void findTarget() OVERRIDE; + // ------------------------------------------------------------------------ virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; + // ------------------------------------------------------------------------ virtual bool isKartOnRoad() const OVERRIDE; + // ------------------------------------------------------------------------ virtual bool isWaiting() const OVERRIDE; + public: BattleAI(AbstractKart *kart); + // ------------------------------------------------------------------------ ~BattleAI(); + }; #endif diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index af4e6925a..c8ed2d3f7 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -129,7 +129,7 @@ void SoccerAI::update(float dt) } // update //----------------------------------------------------------------------------- -void SoccerAI::findClosestKart(bool use_difficulty, bool find_sta) +void SoccerAI::findClosestKart(bool consider_difficulty, bool find_sta) { float distance = 99999.9f; const unsigned int n = m_world->getNumKarts(); @@ -165,7 +165,7 @@ void SoccerAI::findClosestKart(bool use_difficulty, bool find_sta) //----------------------------------------------------------------------------- void SoccerAI::findTarget() { - findClosestKart(true/*use_difficulty*/, false/*find_sta*/); + findClosestKart(true/*consider_difficulty*/, false/*find_sta*/); // Check if this AI kart is the one who will chase the ball if (m_world->getBallChaser(m_cur_team) == (signed)m_kart->getWorldKartId()) { @@ -181,7 +181,7 @@ void SoccerAI::findTarget() if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER) { - collectItemInArena(&m_target_point , &m_target_node); + tryCollectItem(&m_target_point , &m_target_node); } else if (m_world->getAttacker(m_cur_team) == (signed)m_kart ->getWorldKartId()) diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index a93832781..f47937f98 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -65,7 +65,7 @@ private: virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } - virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; + virtual void findClosestKart(bool consider_difficulty, bool find_sta) OVERRIDE; virtual void findTarget() OVERRIDE; virtual bool forceBraking() OVERRIDE { return m_force_brake; } virtual int getCurrentNode() const OVERRIDE;