1 //
2 //  SuperTuxKart - a fun racing game with go-kart
3 //  Copyright (C) 2004-2015 Steve Baker <sjbaker1@airmail.net>
4 //  Copyright (C) 2006-2015 Eduardo Hernandez Munoz
5 //  Copyright (C) 2008-2015 Joerg Henrichs
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 3
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 
21 
22 #include "karts/controller/skidding_ai.hpp"
23 
24 #ifdef AI_DEBUG
25 #  include "graphics/irr_driver.hpp"
26 #endif
27 #include "graphics/show_curve.hpp"
28 #include "graphics/slip_stream.hpp"
29 #include "items/attachment.hpp"
30 #include "items/item_manager.hpp"
31 #include "items/powerup.hpp"
32 #include "items/projectile_manager.hpp"
33 #include "karts/abstract_kart.hpp"
34 #include "karts/controller/kart_control.hpp"
35 #include "karts/controller/ai_properties.hpp"
36 #include "karts/kart_properties.hpp"
37 #include "karts/max_speed.hpp"
38 #include "karts/rescue_animation.hpp"
39 #include "karts/skidding.hpp"
40 #include "modes/linear_world.hpp"
41 #include "modes/profile_world.hpp"
42 #include "physics/triangle_mesh.hpp"
43 #include "race/race_manager.hpp"
44 #include "tracks/drive_graph.hpp"
45 #include "tracks/track.hpp"
46 #include "utils/constants.hpp"
47 #include "utils/log.hpp"
48 #include "utils/vs.hpp"
49 
50 #ifdef AI_DEBUG
51 #  include "irrlicht.h"
52    using namespace irr;
53 #endif
54 
55 #include <cmath>
56 #include <cstdlib>
57 #include <ctime>
58 #include <cstdio>
59 #include <iostream>
60 
SkiddingAI(AbstractKart * kart)61 SkiddingAI::SkiddingAI(AbstractKart *kart)
62                    : AIBaseLapController(kart)
63 {
64     m_item_manager = Track::getCurrentTrack()->getItemManager();
65     reset();
66     // Determine if this AI has superpowers, which happens e.g.
67     // for the final race challenge against nolok.
68     m_superpower = RaceManager::get()->getAISuperPower();
69 
70     m_point_selection_algorithm = PSA_DEFAULT;
71     setControllerName("Skidding");
72 
73     // Use this define in order to compare the different algorithms that
74     // select the next point to steer at.
75 #undef COMPARE_AIS
76 #ifdef COMPARE_AIS
77     std::string name("");
78     m_point_selection_algorithm = m_kart->getWorldKartId() % 2
79                                 ? PSA_DEFAULT : PSA_NEW;
80     switch(m_point_selection_algorithm)
81     {
82     case PSA_NEW     : name = "New";     break;
83     case PSA_DEFAULT : name = "Default"; break;
84     }
85     setControllerName(name);
86 #endif
87 
88 
89     // Draw various spheres on the track for an AI
90 #ifdef AI_DEBUG
91     for(unsigned int i=0; i<4; i++)
92     {
93         video::SColor col_debug(128, i==0 ? 128 : 0,
94                                      i==1 ? 128 : 0,
95                                      i==2 ? 128 : 0);
96         m_debug_sphere[i] = irr_driver->addSphere(1.0f, col_debug);
97         m_debug_sphere[i]->setVisible(false);
98     }
99     m_debug_sphere[m_point_selection_algorithm]->setVisible(true);
100     m_item_sphere  = irr_driver->addSphere(1.0f, video::SColor(255, 0, 255, 0));
101 
102 #define CURVE_PREDICT1   0
103 #define CURVE_KART       1
104 #define CURVE_LEFT       2
105 #define CURVE_RIGHT      3
106 #define CURVE_AIM        4
107 #define CURVE_QG         5
108 #define NUM_CURVES (CURVE_QG+1)
109 
110     m_curve   = new ShowCurve*[NUM_CURVES];
111     for(unsigned int i=0; i<NUM_CURVES; i++)
112         m_curve[i] = NULL;
113 #ifdef AI_DEBUG_CIRCLES
114     m_curve[CURVE_PREDICT1]  = new ShowCurve(0.05f, 0.5f,
115                                    irr::video::SColor(128,   0,   0, 128));
116 #endif
117 #ifdef AI_DEBUG_KART_HEADING
118     irr::video::SColor c;
119     c = irr::video::SColor(128,   0,   0, 128);
120     m_curve[CURVE_KART]      = new ShowCurve(0.5f, 0.5f, c);
121 #endif
122 #ifdef AI_DEBUG_NEW_FIND_NON_CRASHING
123     m_curve[CURVE_LEFT]      = new ShowCurve(0.5f, 0.5f,
124                                             video::SColor(128, 128,   0,   0));
125     m_curve[CURVE_RIGHT]     = new ShowCurve(0.5f, 0.5f,
126                                             video::SColor(128,   0, 128,   0));
127 #endif
128     m_curve[CURVE_QG]        = new ShowCurve(0.5f, 0.5f,
129                                             video::SColor(128,   0, 128,   0));
130 #ifdef AI_DEBUG_KART_AIM
131     irr::video::SColor c1;
132     c1 = irr::video::SColor(128,   0,   0, 128);
133 
134     m_curve[CURVE_AIM]       = new ShowCurve(0.5f, 0.5f, c1);
135 #endif
136 #endif
137 
138 }   // SkiddingAI
139 
140 //-----------------------------------------------------------------------------
141 /** Destructor, mostly to clean up debug data structures.
142  */
~SkiddingAI()143 SkiddingAI::~SkiddingAI()
144 {
145 #ifdef AI_DEBUG
146     for(unsigned int i=0; i<3; i++)
147         irr_driver->removeNode(m_debug_sphere[i]);
148     irr_driver->removeNode(m_item_sphere);
149     for(unsigned int i=0; i<NUM_CURVES; i++)
150     {
151         delete m_curve[i];
152     }
153     delete [] m_curve;
154 #endif
155 }   // ~SkiddingAI
156 
157 //-----------------------------------------------------------------------------
158 /** Resets the AI when a race is restarted.
159  */
reset()160 void SkiddingAI::reset()
161 {
162     m_time_since_last_shot       = 0.0f;
163     m_start_kart_crash_direction = 0;
164     m_start_delay                = -1;
165     m_time_since_stuck           = 0.0f;
166     m_kart_ahead                 = NULL;
167     m_distance_ahead             = 0.0f;
168     m_distance_leader            = 999.9f;
169     m_kart_behind                = NULL;
170     m_distance_behind            = 0.0f;
171     m_current_curve_radius       = 0.0f;
172     m_curve_center               = Vec3(0,0,0);
173     m_current_track_direction    = DriveNode::DIR_STRAIGHT;
174     m_item_to_collect            = NULL;
175     m_last_direction_node        = 0;
176     m_avoid_item_close           = false;
177     m_skid_probability_state     = SKID_PROBAB_NOT_YET;
178     m_last_item_random           = NULL;
179     m_burster                    = false;
180 
181     AIBaseLapController::reset();
182     m_track_node               = Graph::UNKNOWN_SECTOR;
183     DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
184     if(m_track_node==Graph::UNKNOWN_SECTOR)
185     {
186         Log::error(getControllerName().c_str(),
187                    "Invalid starting position for '%s' - not on track"
188                    " - can be ignored.",
189                    m_kart->getIdent().c_str());
190         m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
191     }
192 
193     AIBaseLapController::reset();
194 }   // reset
195 
196 //-----------------------------------------------------------------------------
197 /** Returns a name for the AI.
198  *  This is used in profile mode when comparing different AI implementations
199  *  to be able to distinguish them from each other.
200  */
getNamePostfix() const201 const irr::core::stringw& SkiddingAI::getNamePostfix() const
202 {
203     // Static to avoid returning the address of a temporary string.
204     static irr::core::stringw name="(default)";
205     return name;
206 }   // getNamePostfix
207 
208 //-----------------------------------------------------------------------------
209 /** Returns the pre-computed successor of a graph node.
210  *  \param index The index of the graph node for which the successor
211  *               is searched.
212  */
getNextSector(unsigned int index)213 unsigned int SkiddingAI::getNextSector(unsigned int index)
214 {
215     return m_successor_index[index];
216 }   // getNextSector
217 
218 //-----------------------------------------------------------------------------
219 /** This is the main entry point for the AI.
220  *  It is called once per frame for each AI and determines the behaviour of
221  *  the AI, e.g. steering, accelerating/braking, firing.
222  */
update(int ticks)223 void SkiddingAI::update(int ticks)
224 {
225     float dt = stk_config->ticks2Time(ticks);
226 
227     // Clear stored items if they were deleted (for example a switched nitro)
228     if (m_item_to_collect &&
229         !m_item_manager->itemExists(m_item_to_collect))
230         m_item_to_collect = NULL;
231     if (m_last_item_random &&
232         !m_item_manager->itemExists(m_last_item_random))
233         m_last_item_random = NULL;
234 
235     m_controls->setRescue(false);
236 
237     // This is used to enable firing an item backwards.
238     m_controls->setLookBack(false);
239     m_controls->setNitro(false);
240 
241     // Don't do anything if there is currently a kart animations shown.
242     if(m_kart->getKartAnimation())
243         return;
244 
245     if (m_superpower == RaceManager::SUPERPOWER_NOLOK_BOSS)
246     {
247         if (m_kart->getPowerup()->getType()==PowerupManager::POWERUP_NOTHING)
248         {
249             if (m_kart->getPosition() > 1)
250             {
251                 int r = rand() % 5;
252                 if (r == 0 || r == 1)
253                     m_kart->setPowerup(PowerupManager::POWERUP_ZIPPER, 1);
254                 else if (r == 2 || r == 3)
255                     m_kart->setPowerup(PowerupManager::POWERUP_BUBBLEGUM, 1);
256                 else
257                     m_kart->setPowerup(PowerupManager::POWERUP_SWATTER, 1);
258             }
259             else if (m_kart->getAttachment()->getType() == Attachment::ATTACH_SWATTER)
260             {
261                 int r = rand() % 4;
262                 if (r < 3)
263                     m_kart->setPowerup(PowerupManager::POWERUP_BUBBLEGUM, 1);
264                 else
265                     m_kart->setPowerup(PowerupManager::POWERUP_BOWLING, 1);
266             }
267             else
268             {
269                 int r = rand() % 5;
270                 if (r == 0 || r == 1)
271                     m_kart->setPowerup(PowerupManager::POWERUP_BUBBLEGUM, 1);
272                 else if (r == 2 || r == 3)
273                     m_kart->setPowerup(PowerupManager::POWERUP_SWATTER, 1);
274                 else
275                     m_kart->setPowerup(PowerupManager::POWERUP_BOWLING, 1);
276             }
277 
278             // also give him some free nitro
279             if (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM)
280             {
281                 if (m_kart->getPosition() > 1)
282                     m_kart->setEnergy(m_kart->getEnergy() + 2);
283                 else
284                     m_kart->setEnergy(m_kart->getEnergy() + 1);
285             }
286             else if (RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD ||
287                 RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_BEST)
288             {
289                 if (m_kart->getPosition() > 1)
290                     m_kart->setEnergy(m_kart->getEnergy() + 7);
291                 else
292                     m_kart->setEnergy(m_kart->getEnergy() + 4);
293             }
294         }
295     }
296 
297     // Having a non-moving AI can be useful for debugging, e.g. aiming
298     // or slipstreaming.
299 #undef AI_DOES_NOT_MOVE_FOR_DEBUGGING
300 #ifdef AI_DOES_NOT_MOVE_FOR_DEBUGGING
301     m_controls->setAccel(0);
302     m_controls->setSteer(0);
303     return;
304 #endif
305 
306     // If the kart needs to be rescued, do it now (and nothing else)
307     if(isStuck() && !m_kart->getKartAnimation())
308     {
309         // For network AI controller
310         if (m_enabled_network_ai)
311             m_controls->setRescue(true);
312         else
313             RescueAnimation::create(m_kart);
314         AIBaseLapController::update(ticks);
315         return;
316     }
317 
318     if( m_world->isStartPhase() )
319     {
320         handleRaceStart();
321         AIBaseLapController::update(ticks);
322         return;
323     }
324 
325     // Get information that is needed by more than 1 of the handling funcs
326     computeNearestKarts();
327 
328     if (!m_enabled_network_ai)
329     {
330         int num_ai = m_world->getNumKarts() - RaceManager::get()->getNumPlayers();
331         int position_among_ai = m_kart->getPosition() - m_num_players_ahead;
332         // Karts with boosted AI get a better speed cap value
333         if (m_kart->getBoostAI())
334             position_among_ai = 1;
335         float speed_cap = m_ai_properties->getSpeedCap(m_distance_to_player,
336             position_among_ai, num_ai);
337         m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI,
338             speed_cap, /*fade_in_time*/0);
339     }
340 
341     //Detect if we are going to crash with the track and/or kart
342     checkCrashes(m_kart->getXYZ());
343     determineTrackDirection();
344 
345     /*Response handling functions*/
346     handleAccelerationAndBraking(ticks);
347     handleSteering(dt);
348     handleRescue(dt);
349 
350     // Make sure that not all AI karts use the zipper at the same
351     // time in time trial at start up, so disable it during the 5 first seconds
352     if(RaceManager::get()->isTimeTrialMode() && (m_world->getTime()<5.0f) )
353         m_controls->setFire(false);
354 
355     /*And obviously general kart stuff*/
356     AIBaseLapController::update(ticks);
357 }   // update
358 
359 //-----------------------------------------------------------------------------
360 /** Decides in which direction to steer. If the kart is off track, it will
361  *  steer towards the center of the track. Otherwise it will call one of
362  *  the findNonCrashingPoint() functions to determine a point to aim for. Then
363  *  it will evaluate items to see if it should aim for any items or try to
364  *  avoid item, and potentially adjust the aim-at point, before computing the
365  *  steer direction to arrive at the currently aim-at point.
366  *  \param dt Time step size.
367  */
handleSteering(float dt)368 void SkiddingAI::handleSteering(float dt)
369 {
370     // Special behaviour if we have a bomb attached: try to hit the kart ahead
371     // of us.
372     if(m_ai_properties->m_handle_bomb &&
373         m_kart->getAttachment()->getType()==Attachment::ATTACH_BOMB)
374     {
375         //TODO : add logic to allow an AI kart to pass the bomb to a kart
376         //       close behind by slowing/steering slightly
377         // If we are close enough and can pass the bomb, try to hit this kart
378         if ( m_kart_ahead != m_kart->getAttachment()->getPreviousOwner() &&
379             m_distance_ahead<=10)
380         {
381             Vec3 target = m_kart_ahead->getXYZ();
382 
383             // If we are faster, try to predict the point where we will hit
384             // the other kart
385             if((m_kart_ahead->getSpeed() < m_kart->getSpeed()) &&
386                 !m_kart_ahead->isGhostKart())
387             {
388                 float time_till_hit = m_distance_ahead
389                                     / (m_kart->getSpeed()-m_kart_ahead->getSpeed());
390                 target += m_kart_ahead->getVelocity()*time_till_hit;
391             }
392             float steer_angle = steerToPoint(target);
393             setSteering(steer_angle, dt);
394             return;
395         }
396     }
397 
398     const int next = m_next_node_index[m_track_node];
399 
400     float steer_angle = 0.0f;
401 
402     /*The AI responds based on the information we just gathered, using a
403      *finite state machine.
404      */
405     //Reaction to being outside of the road
406     float side_dist =
407         m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() );
408 
409     int item_skill = computeSkill(ITEM_SKILL);
410 
411     if( fabsf(side_dist)  >
412        0.5f* DriveGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f )
413     {
414         steer_angle = steerToPoint(DriveGraph::get()->getNode(next)
415                                                    ->getCenter());
416 
417 #ifdef AI_DEBUG
418         m_debug_sphere[0]->setPosition(DriveGraph::get()->getNode(next)
419                          ->getCenter().toIrrVector());
420         Log::debug(getControllerName().c_str(),
421                    "Outside of road: steer to center point.");
422 #endif
423     }
424     //If we are going to crash against a kart, avoid it if it doesn't
425     //drives the kart out of the road
426     //TODO : adds item handling to use a cake if available to
427     //open the road
428     else if( m_crashes.m_kart != -1 && !m_crashes.m_road )
429     {
430         //-1 = left, 1 = right, 0 = no crash.
431         if( m_start_kart_crash_direction == 1 )
432         {
433             steer_angle = steerToAngle(next, M_PI*0.5f );
434             m_start_kart_crash_direction = 0;
435         }
436         else if(m_start_kart_crash_direction == -1)
437         {
438             steer_angle = steerToAngle(next, -M_PI*0.5f);
439             m_start_kart_crash_direction = 0;
440         }
441         else
442         {
443             if(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ) >
444                m_world->getDistanceToCenterForKart( m_crashes.m_kart ))
445             {
446                 steer_angle = steerToAngle(next, M_PI*0.5f );
447                 m_start_kart_crash_direction = 1;
448             }
449             else
450             {
451                 steer_angle = steerToAngle(next, -M_PI*0.5f );
452                 m_start_kart_crash_direction = -1;
453             }
454         }
455 
456 #ifdef AI_DEBUG
457         Log::debug(getControllerName().c_str(),
458                    "Velocity vector crashes with kart "
459                    "and doesn't crashes with road : steer 90 "
460                    "degrees away from kart.");
461 #endif
462 
463     }
464     else
465     {
466         m_start_kart_crash_direction = 0;
467         Vec3 aim_point;
468         int last_node = Graph::UNKNOWN_SECTOR;
469 
470         switch(m_point_selection_algorithm)
471         {
472         case PSA_NEW:    findNonCrashingPointNew(&aim_point, &last_node);
473                          break;
474         case PSA_DEFAULT:findNonCrashingPoint(&aim_point, &last_node);
475                          break;
476         }
477 #ifdef AI_DEBUG
478         m_debug_sphere[m_point_selection_algorithm]->setPosition(aim_point.toIrrVector());
479 #endif
480 #ifdef AI_DEBUG_KART_AIM
481         const Vec3 eps(0,0.5f,0);
482         m_curve[CURVE_AIM]->clear();
483         m_curve[CURVE_AIM]->addPoint(m_kart->getXYZ()+eps);
484         m_curve[CURVE_AIM]->addPoint(aim_point);
485 #endif
486 
487         //Manage item utilisation
488         handleItems(dt, &aim_point, last_node, item_skill);
489 
490         // Potentially adjust the point to aim for in order to either
491         // aim to collect item, or steer to avoid a bad item.
492         if(m_ai_properties->m_collect_avoid_items && m_kart->getBlockedByPlungerTicks()<=0)
493             handleItemCollectionAndAvoidance(&aim_point, last_node);
494 
495         steer_angle = steerToPoint(aim_point);
496     }  // if m_current_track_direction!=LEFT/RIGHT
497 
498     setSteering(steer_angle, dt);
499 }   // handleSteering
500 
501 //-----------------------------------------------------------------------------
502 /** Decides if the currently selected aim at point (as determined by
503  *  handleSteering) should be changed in order to collect/avoid an item.
504  *  There are 5 potential phases:
505  *  1. Collect all items close by and sort them by items-to-avoid and
506  *     items-to-collect. 'Close by' are all items between the current
507  *     graph node the kart is on and the graph node the aim_point is on.
508  *     The function evaluateItems() filters those items: atm all items-to-avoid
509  *     are collected, and all items-to-collect that are not too far away from
510  *     the intended driving direction (i.e. don't require a too sharp steering
511  *     change).
512  *  2. If a pre-selected item (see phase 5) exists, and items-to-avoid which
513  *     might get hit if the pre-selected item is collected, the pre-selected
514  *     item is unselected. This can happens if e.g. items-to-avoid are behind
515  *     the pre-selected items on a different graph node and were therefore not
516  *     evaluated then the now pre-selected item was selected initially.
517  *  3. If a pre-selected item exists, the kart will steer towards it. The AI
518  *     does a much better job of collecting items if after selecting an item
519  *     it tries to collect this item even if it doesn't fulfill the original
520  *     conditions to be selected in the first place anymore. Example: An item
521  *     was selected to be collected because the AI was hitting it anyway. Then
522  *     the aim_point changes, and the selected item is not that close anymore.
523  *     In many cases it is better to keep on aiming for the items (otherwise
524  *     the aiming will not have much benefit and mostly only results in
525  *     collecting items that are on long straights).
526  *     At this stage because of phase 2) it is certain that no item-to-avoid
527  *     will be hit. The function handleSelectedItem() evaluates if it is still
528  *     feasible to collect them item (if the kart has already passed the item
529  *     it won't reverse to collect it). If the item is still to be aimed for,
530  *     adjust the aim_point and return.
531  *  4. Make sure to avoid any items-to-avoid. The function steerToAvoid
532  *     selects a new aim point if otherwise an item-to-avoid would be hit.
533  *     If this is the case, aim_point is adjused and control returns to the
534  *     caller.
535  *  5. Try to collect an item-to-collect. Select the closest item to the
536  *     kart (which in case of a row of items will be the item the kart
537  *     is roughly driving towards to anyway). It is then tested if the kart
538  *     would hit any item-to-avoid when driving towards this item - if so
539  *     the driving direction is not changed and the function returns.
540  *     Otherwise, i.e. no items-to-avoid will be hit, it is evaluated if
541  *     the kart is (atm) going to hit it anyway. In this case, the item is
542  *     selected (see phase 2 and 3). If on the other hand the item is not
543  *     too far our of the way, aim_point is adjusted to drive towards
544  *     this item (but it is not selected, so the item will be discarded if
545  *     the kart is getting too far away from it). Ideally later as the
546  *     kart is steering towards this item the item will be selected.
547  *
548  *  \param aim_point Currently selected point to aim at. If the AI should
549  *         try to collect an item, this value will be changed.
550  *  \param last_node Index of the graph node on which the aim_point is.
551  */
handleItemCollectionAndAvoidance(Vec3 * aim_point,int last_node)552 void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point,
553                                                   int last_node)
554 {
555 #ifdef AI_DEBUG
556     m_item_sphere->setVisible(false);
557 #endif
558     // Angle to aim_point
559     Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ();
560 
561     // Make sure we have a valid last_node
562     if(last_node==Graph::UNKNOWN_SECTOR)
563         last_node = m_next_node_index[m_track_node];
564 
565     int node = m_track_node;
566     float distance = 0;
567     std::vector<const ItemState *> items_to_collect;
568     std::vector<const ItemState *> items_to_avoid;
569 
570     // 1) Filter and sort all items close by
571     // -------------------------------------
572     const float max_item_lookahead_distance = 30.f;
573     while(distance < max_item_lookahead_distance)
574     {
575         int n_index= DriveGraph::get()->getNode(node)->getIndex();
576         const std::vector<ItemState*> &items_ahead =
577                                   m_item_manager->getItemsInQuads(n_index);
578         for(unsigned int i=0; i<items_ahead.size(); i++)
579         {
580             evaluateItems(items_ahead[i],  kart_aim_direction,
581                           &items_to_avoid, &items_to_collect);
582         }   // for i<items_ahead;
583         distance += DriveGraph::get()->getDistanceToNext(node,
584                                                       m_successor_index[node]);
585         node = m_next_node_index[node];
586         // Stop when we have reached the last quad
587         if(node==last_node) break;
588     }   // while (distance < max_item_lookahead_distance)
589 
590     m_avoid_item_close = items_to_avoid.size()>0;
591 
592     core::line3df line_to_target_3d((*aim_point).toIrrVector(),
593                                      m_kart->getXYZ().toIrrVector());
594 
595     // 2) If the kart is aiming for an item, but (suddenly) detects
596     //    some close-by items to avoid (e.g. behind the item, which was too
597     //    far away to be considered earlier, or because the item was switched
598     //    to a bad item), the kart cancels collecting the item if this could
599     //    cause the item-to-avoid to be collected.
600     // --------------------------------------------------------------------
601     if(m_item_to_collect && items_to_avoid.size()>0)
602     {
603         for(unsigned int i=0; i< items_to_avoid.size(); i++)
604         {
605             Vec3 d = items_to_avoid[i]->getXYZ()-m_item_to_collect->getXYZ();
606             if( d.length2()>m_ai_properties->m_bad_item_closeness_2)
607                 continue;
608             // It could make sense to also test if the bad item would
609             // actually be hit, not only if it is close (which can result
610             // in false positives, i.e. stop collecting an item though
611             // it is not actually necessary). But in (at least) one case
612             // steering after collecting m_item_to_collect causes it to then
613             // collect the bad item (it's too close to avoid it at that
614             // time). So for now here is no additional test, if we see
615             // false positives we can handle it here.
616             m_item_to_collect = NULL;
617             break;
618         }   // for i<items_to_avoid.size()
619     }   // if m_item_to_collect && items_to_avoid.size()>0
620 
621 
622     // 3) Steer towards a pre-selected item
623     // -------------------------------------
624     if(m_item_to_collect)
625     {
626         if(handleSelectedItem(kart_aim_direction, aim_point))
627         {
628             // Still aim at the previsouly selected item.
629             *aim_point = m_item_to_collect->getXYZ();
630 #ifdef AI_DEBUG
631             m_item_sphere->setVisible(true);
632             m_item_sphere->setPosition(aim_point->toIrrVector());
633 #endif
634             return;
635         }
636 
637         if(m_ai_debug)
638             Log::debug(getControllerName().c_str(), "%s unselects item.",
639                        m_kart->getIdent().c_str());
640         // Otherwise remove the pre-selected item (and start
641         // looking for a new item).
642         m_item_to_collect = NULL;
643     }   // m_item_to_collect
644 
645     // 4) Avoid items-to-avoid
646     // -----------------------
647     if(items_to_avoid.size()>0)
648     {
649         // If we need to steer to avoid an item, this takes priority,
650         // ignore items to collect and return the new aim_point.
651         if(steerToAvoid(items_to_avoid, line_to_target_3d, aim_point))
652         {
653 #ifdef AI_DEBUG
654             m_item_sphere->setVisible(true);
655             m_item_sphere->setPosition(aim_point->toIrrVector());
656 #endif
657             return;
658         }
659     }
660 
661     // 5) We are aiming for a new item. If necessary, determine
662     // randomly if this item sshould actually be collected.
663     // --------------------------------------------------------
664     if(items_to_collect.size()>0)
665     {
666         if(items_to_collect[0] != m_last_item_random)
667         {
668             int p = (int)(100.0f*m_ai_properties->
669                           getItemCollectProbability(m_distance_to_player));
670             m_really_collect_item = m_random_collect_item.get(100)<p;
671             m_last_item_random = items_to_collect[0];
672         }
673         if(!m_really_collect_item)
674         {
675             // The same item was selected previously, but it was randomly
676             // decided not to collect it - so keep on ignoring this item.
677             return;
678         }
679     }
680 
681     // Reset the probability if a different (or no) item is selected.
682     if(items_to_collect.size()==0 || items_to_collect[0]!=m_last_item_random)
683         m_last_item_random = NULL;
684 
685     // 6) Try to aim for items-to-collect
686     // ----------------------------------
687     if(items_to_collect.size()>0)
688     {
689         const ItemState *item_to_collect = items_to_collect[0];
690         // Test if we would hit a bad item when aiming at this good item.
691         // If so, don't change the aim. In this case it has already been
692         // ensured that we won't hit the bad item (otherwise steerToAvoid
693         // would have detected this earlier).
694         if(!hitBadItemWhenAimAt(item_to_collect, items_to_avoid))
695         {
696             // If the item is hit (with the current steering), it means
697             // it's on a good enough driveline, so make this item a permanent
698             // target. Otherwise only try to get closer (till hopefully this
699             // item s on our driveline)
700             if(item_to_collect->hitLine(line_to_target_3d, m_kart))
701             {
702 #ifdef AI_DEBUG
703                 m_item_sphere->setVisible(true);
704                 m_item_sphere->setPosition(item_to_collect->getXYZ()
705                                                            .toIrrVector());
706 #endif
707                 if(m_ai_debug)
708                     Log::debug(getControllerName().c_str(),
709                                "%s selects item type '%d'.",
710                                m_kart->getIdent().c_str(),
711                                item_to_collect->getType());
712                 m_item_to_collect = item_to_collect;
713             }
714             else
715             {
716                 // Kart will not hit item, try to get closer to this item
717                 // so that it can potentially become a permanent target.
718                 const Vec3& xyz = item_to_collect->getXYZ();
719                 float angle_to_item = (xyz - m_kart->getXYZ())
720                     .angle(kart_aim_direction);
721                 float angle = normalizeAngle(angle_to_item);
722 
723                 if(fabsf(angle) < 0.3)
724                 {
725                     *aim_point = item_to_collect->getXYZ();
726 #ifdef AI_DEBUG
727                     m_item_sphere->setVisible(true);
728                     m_item_sphere->setPosition(item_to_collect->getXYZ()
729                                                                .toIrrVector());
730 #endif
731                     if(m_ai_debug)
732                         Log::debug(getControllerName().c_str(),
733                                    "%s adjusts to hit type %d angle %f.",
734                                    m_kart->getIdent().c_str(),
735                                    item_to_collect->getType(), angle);
736                 }
737                 else
738                 {
739                     if(m_ai_debug)
740                         Log::debug(getControllerName().c_str(),
741                                    "%s won't hit '%d', angle %f.",
742                                    m_kart->getIdent().c_str(),
743                                    item_to_collect->getType(), angle);
744                 }
745             }   // kart will not hit item
746         }   // does hit hit bad item
747     }   // if item to consider was found
748 
749 }   // handleItemCollectionAndAvoidance
750 
751 //-----------------------------------------------------------------------------
752 /** Returns true if the AI would hit any of the listed bad items when trying
753  *  to drive towards the specified item.
754  *  \param item The item the AI is evaluating for collection.
755  *  \param items_to_aovid A list of bad items close by. All of these needs
756  *        to be avoided.
757  *  \return True if it would hit any of the bad items.
758 */
hitBadItemWhenAimAt(const ItemState * item,const std::vector<const ItemState * > & items_to_avoid)759 bool SkiddingAI::hitBadItemWhenAimAt(const ItemState *item,
760                           const std::vector<const ItemState *> &items_to_avoid)
761 {
762     core::line3df to_item(m_kart->getXYZ().toIrrVector(),
763                           item->getXYZ().toIrrVector());
764     for(unsigned int i=0; i<items_to_avoid.size(); i++)
765     {
766         if(items_to_avoid[i]->hitLine(to_item, m_kart))
767             return true;
768     }
769     return false;
770 }   // hitBadItemWhenAimAt
771 
772 //-----------------------------------------------------------------------------
773 /** This function is called when the AI is trying to hit an item that is
774  *  pre-selected to be collected. The AI only evaluates if it's still
775  *  feasible/useful to try to collect this item, or abandon it (and then
776  *  look for a new item). At item is unselected if the kart has passed it
777  *  (so collecting it would require the kart to reverse).
778  *  \pre m_item_to_collect is defined
779  *  \param kart_aim_angle The angle towards the current aim_point.
780  *  \param aim_point The current aim_point.
781  *  \param last_node
782  *  \return True if th AI should still aim for the pre-selected item.
783  */
handleSelectedItem(Vec3 kart_aim_direction,Vec3 * aim_point)784 bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point)
785 {
786     // If the item is unavailable keep on testing. It is not necessary
787     // to test if an item has turned bad, this was tested before this
788     // function is called.
789     if(!m_item_to_collect->isAvailable())
790         return false;
791 
792     const Vec3 &xyz = m_item_to_collect->getXYZ();
793     float angle_to_item = (xyz - m_kart->getXYZ()).angle(kart_aim_direction);
794     float angle = normalizeAngle(angle_to_item);
795 
796     if(fabsf(angle)>1.5)
797     {
798         // We (likely) have passed the item we were aiming for
799         return false;
800     }
801     else
802     {
803         // Keep on aiming for last selected item
804         *aim_point = m_item_to_collect->getXYZ();
805     }
806     return true;
807 }   // handleSelectedItem
808 
809 //-----------------------------------------------------------------------------
810 /** Decides if steering is necessary to avoid bad items. If so, it modifies
811  *  the aim_point and returns true.
812  *  \param items_to_avoid List of items to avoid.
813  *  \param line_to_target The 2d line from the current kart position to
814  *         the current aim point.
815  *  \param aim_point The point the AI is steering towards (not taking items
816  *         into account).
817  *  \return True if steering is necessary to avoid an item.
818  */
steerToAvoid(const std::vector<const ItemState * > & items_to_avoid,const core::line3df & line_to_target,Vec3 * aim_point)819 bool SkiddingAI::steerToAvoid(const std::vector<const ItemState *> &items_to_avoid,
820                               const core::line3df &line_to_target,
821                               Vec3 *aim_point)
822 {
823     // First determine the left-most and right-most item.
824     float left_most        = items_to_avoid[0]->getDistanceFromCenter();
825     float right_most       = items_to_avoid[0]->getDistanceFromCenter();
826     int   index_left_most  = 0;
827     int   index_right_most = 0;
828 
829     for(unsigned int i=1; i<items_to_avoid.size(); i++)
830     {
831         float dist = items_to_avoid[i]->getDistanceFromCenter();
832         if (dist<left_most)
833         {
834             left_most       = dist;
835             index_left_most = i;
836         }
837         if(dist>right_most)
838         {
839             right_most       = dist;
840             index_right_most = i;
841         }
842     }
843 
844     // Check if we would drive left of the leftmost or right of the
845     // rightmost point - if so, nothing to do.
846     const Vec3& left = items_to_avoid[index_left_most]->getXYZ();
847     int node_index = items_to_avoid[index_left_most]->getGraphNode();
848     const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal();
849     Vec3 hit;
850     Vec3 hit_nor(0, 1, 0);
851     const Material* m;
852     m_track->getPtrTriangleMesh()->castRay(
853         Vec3(line_to_target.getMiddle()) + normal,
854         Vec3(line_to_target.getMiddle()) + normal * -10000, &hit, &m,
855         &hit_nor);
856     Vec3 p1 = line_to_target.start,
857          p2 = line_to_target.getMiddle() + hit_nor.toIrrVector(),
858          p3 = line_to_target.end;
859 
860     int item_index = -1;
861     bool is_left    = false;
862 
863     // >=0 means the point is to the right of the line, or the line is
864     // to the left of the point.
865     if(left.sideofPlane(p1,p2,p3) <= 0)
866     {
867         // Left of leftmost point
868         item_index = index_left_most;
869         is_left       = true;
870     }
871     else
872     {
873         const Vec3& right = items_to_avoid[index_right_most]->getXYZ();
874         if (right.sideofPlane(p1, p2, p3) >= 0)
875         {
876             // Right of rightmost point
877             item_index = index_right_most;
878             is_left    = false;
879         }
880     }
881     if(item_index>-1)
882     {
883         // Even though we are on the side, we must make sure
884         // that we don't hit that item
885         // If we don't hit the item on the side, no more tests are necessary
886         if(!items_to_avoid[item_index]->hitLine(line_to_target, m_kart))
887             return false;
888 
889         // See if we can avoid this item by driving further to the side
890         const Vec3 *avoid_point = items_to_avoid[item_index]
891                                 ->getAvoidancePoint(is_left);
892         // If avoid_point is NULL, it means steering more to the sides
893         // brings us off track. In this case just try to steer to the
894         // other side (e.g. when hitting a left-most item and the kart can't
895         // steer further left, steer a bit to the right of the left-most item
896         // (without further tests if we might hit anything else).
897         if(!avoid_point)
898             avoid_point = items_to_avoid[item_index]
899                         ->getAvoidancePoint(!is_left);
900         *aim_point = *avoid_point;
901         return true;
902     }
903 
904     // At this stage there must be at least two items - if there was
905     // only a single item, the 'left of left-most' or 'right of right-most'
906     // tests above are had been and an appropriate steering point was already
907     // determined.
908 
909     // Try to identify two items we are driving between (if the kart is not
910     // driving between two items, one of the 'left of left-most' etc.
911     // tests before applied and this point would not be reached).
912 
913     float min_distance[2] = {99999.9f, 99999.9f};
914     int   index[2] = {-1, -1};
915     core::vector3df closest3d[2];
916     for(unsigned int i=0; i<items_to_avoid.size(); i++)
917     {
918         const Vec3 &xyz         = items_to_avoid[i]->getXYZ();
919         core::vector3df point3d = line_to_target.getClosestPoint(xyz.toIrrVector());
920         float d = (xyz.toIrrVector() - point3d).getLengthSQ();
921         float direction = xyz.sideofPlane(p1,p2,p3);
922         int ind = direction<0 ? 1 : 0;
923         if(d<min_distance[ind])
924         {
925             min_distance[ind] = d;
926             index[ind]        = i;
927             closest3d[ind]    = point3d;
928         }
929     }
930 
931     assert(index[0]!= index[1]);
932     assert(index[0]!=-1       );
933     assert(index[1]!=-1       );
934 
935     // We are driving between item_to_avoid[index[0]] and ...[1].
936     // If we don't hit any of them, just keep on driving as normal
937     bool hit_left  = items_to_avoid[index[0]]->hitKart(closest3d[0], m_kart);
938     bool hit_right = items_to_avoid[index[1]]->hitKart(closest3d[1], m_kart);
939     if( !hit_left && !hit_right)
940         return false;
941 
942     // If we hit the left item, aim at the right avoidance point
943     // of the left item. We might still hit the right item ... this might
944     // still be better than going too far off track
945     if(hit_left)
946     {
947         *aim_point =
948             *(items_to_avoid[index[0]]->getAvoidancePoint(/*left*/false));
949         return true;
950     }
951 
952     // Now we must be hitting the right item, so try aiming at the left
953     // avoidance point of the right item.
954     *aim_point = *(items_to_avoid[index[1]]->getAvoidancePoint(/*left*/true));
955     return true;
956 }   // steerToAvoid
957 
958 //-----------------------------------------------------------------------------
959 /** This subroutine decides if the specified item should be collected,
960  *  avoided, or ignored. It can potentially use the state of the
961  *  kart to make this decision, e.g. depending on what item the kart currently
962  *  has, how much nitro it has etc. Though atm it picks the first good item,
963  *  and tries to avoid any bad items on the track.
964  *  \param item The item which is considered for picking/avoidance.
965  *  \param kart_aim_angle The angle of the line from the kart to the aim point.
966  *         If aim_angle==kart_heading then the kart is driving towards the
967  *         item.
968  *  \param item_to_avoid A pointer to a previously selected item to avoid
969  *         (NULL if no item was avoided so far).
970  *  \param item_to_collect A pointer to a previously selected item to collect.
971  */
evaluateItems(const ItemState * item,Vec3 kart_aim_direction,std::vector<const ItemState * > * items_to_avoid,std::vector<const ItemState * > * items_to_collect)972 void SkiddingAI::evaluateItems(const ItemState *item, Vec3 kart_aim_direction,
973                                std::vector<const ItemState *> *items_to_avoid,
974                                std::vector<const ItemState *> *items_to_collect)
975 {
976     const KartProperties *kp = m_kart->getKartProperties();
977 
978     // Ignore items that are currently disabled
979     if(!item->isAvailable()) return;
980 
981     // If the item type is not handled here, ignore it
982     Item::ItemType type = item->getType();
983     if( type!=Item::ITEM_BANANA    && type!=Item::ITEM_BUBBLEGUM &&
984         type!=Item::ITEM_BONUS_BOX &&
985         type!=Item::ITEM_NITRO_BIG && type!=Item::ITEM_NITRO_SMALL  )
986         return;
987 
988     bool avoid = false;
989     switch(type)
990     {
991         // Negative items: avoid them
992         case Item::ITEM_BUBBLEGUM: // fallthrough
993         case Item::ITEM_BANANA: avoid = true;  break;
994 
995         // Positive items: try to collect
996         case Item::ITEM_NITRO_BIG:
997             // Only collect nitro, if it can actually be stored.
998             if (m_kart->getEnergy() + kp->getNitroBigContainer()
999                 > kp->getNitroMax())
1000                   return;
1001             break;
1002         case Item::ITEM_NITRO_SMALL:
1003             // Only collect nitro, if it can actually be stored.
1004             if (m_kart->getEnergy() + kp->getNitroSmallContainer()
1005                 > kp->getNitroMax())
1006                   return;
1007             break;
1008         case Item::ITEM_BONUS_BOX:
1009             break;
1010         default: assert(false); break;
1011     }    // switch
1012 
1013 
1014     // Ignore items to be collected that are out of our way (though all items
1015     // to avoid are collected).
1016     if(!avoid)
1017     {
1018         const Vec3 &xyz = item->getXYZ();
1019         float angle_to_item =
1020             (xyz - m_kart->getXYZ()).angle(kart_aim_direction);
1021         float diff = normalizeAngle(angle_to_item);
1022 
1023         // The kart is driving at high speed, when the current max speed
1024         // is higher than the max speed of the kart (which is caused by
1025         // any powerups etc)
1026         // Otherwise check for skidding. If the kart is still collecting
1027         // skid bonus, currentMaxSpeed is not affected yet, but it might
1028         // be if the kart would need to turn sharper, therefore stops
1029         // skidding, and will get the bonus speed.
1030         bool high_speed = (m_kart->getCurrentMaxSpeed() >
1031                            kp->getEngineMaxSpeed() ) ||
1032                           m_kart->getSkidding()->getSkidBonusReady();
1033         float max_angle = high_speed
1034                         ? m_ai_properties->m_max_item_angle_high_speed
1035                         : m_ai_properties->m_max_item_angle;
1036 
1037         if(fabsf(diff) > max_angle)
1038             return;
1039 
1040     }   // if !avoid
1041 
1042     // Now insert the item into the sorted list of items to avoid
1043     // (or to collect). The lists are (for now) sorted by distance
1044     std::vector<const ItemState*> *list;
1045     if(avoid)
1046         list = items_to_avoid;
1047     else
1048         list = items_to_collect;
1049 
1050     float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2();
1051 
1052     // This list is usually very short, so use a simple bubble sort
1053     list->push_back(item);
1054     int i;
1055     for(i=(int)list->size()-2; i>=0; i--)
1056     {
1057         float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2();
1058         if(d<=new_distance)
1059         {
1060             break;
1061         }
1062         (*list)[i+1] = (*list)[i];
1063     }
1064     (*list)[i+1] = item;
1065 
1066 }   // evaluateItems
1067 
1068 //-----------------------------------------------------------------------------
1069 /** Handle all items depending on the chosen strategy.
1070  *  Level 0 "AI" : do nothing (not used by default)
1071  *  Level 1 "AI" : use items after a random time
1072  *  Level 2 to 5 AI : strategy detailed before each item
1073  *  Each successive level is overall stronger (5 the strongest, 2 the weakest of
1074  *  non-random strategies), but two levels may share a strategy for a given item.
1075  *  \param dt Time step size.
1076  *  STATE: shield on -> avoid usage of offensive items (with certain tolerance)
1077  *  STATE: swatter on -> avoid usage of shield
1078  */
handleItems(const float dt,const Vec3 * aim_point,int last_node,int item_skill)1079 void SkiddingAI::handleItems(const float dt, const Vec3 *aim_point, int last_node, int item_skill)
1080 {
1081     m_controls->setFire(false);
1082     if(m_kart->getKartAnimation() ||
1083         m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING )
1084         return;
1085 
1086     m_time_since_last_shot += dt;
1087 
1088     //time since last shot is meant to avoid using the same item
1089     //several times in rapid succession ; not to wait to use a useful
1090     //collected item
1091     if ( m_kart->getPowerup()->getType() != m_kart->getLastUsedPowerup() )
1092     {
1093        m_time_since_last_shot = 50.0f; //The AI may wait if the value is low, so set a high value
1094     }
1095 
1096     if (m_superpower == RaceManager::SUPERPOWER_NOLOK_BOSS)
1097     {
1098         m_controls->setLookBack(m_kart->getPowerup()->getType() ==
1099                                    PowerupManager::POWERUP_BOWLING   );
1100 
1101         if( m_time_since_last_shot > 3.0f )
1102         {
1103             m_controls->setFire(true);
1104             if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_SWATTER)
1105                 m_time_since_last_shot = 3.0f;
1106             else
1107             {
1108                 // to make things less predictable :)
1109                 m_time_since_last_shot = (rand() % 1000) / 1000.0f * 3.0f - 2.0f;
1110             }
1111         }
1112         else
1113         {
1114             m_controls->setFire(false);
1115         }
1116         return;
1117     }
1118 
1119     // Tactic 0: don't use item
1120     // -----------------------------------------
1121     if(item_skill == 0)
1122     {
1123         return;
1124     }
1125 
1126     // Tactic 1: wait between 5 and 10 seconds, then use item
1127     // ------------------------------------------------------
1128     if(item_skill == 1)
1129     {
1130         int random_t = 0;
1131         random_t = m_random_skid.get(6); //Reuse the random skid generator
1132         random_t = random_t + 5;
1133 
1134         if( m_time_since_last_shot > random_t )
1135         {
1136             m_controls->setFire(true);
1137             m_time_since_last_shot = 0.0f;
1138         }
1139         return;
1140     }
1141 
1142     // Tactics 2 to 5: calculate
1143     // -----------------------------------------
1144     float min_bubble_time = 2.0f;
1145 
1146     // Preparing item list for item aware actions
1147 
1148     // Angle to aim_point
1149     Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ();
1150 
1151     // Make sure we have a valid last_node
1152     if(last_node==Graph::UNKNOWN_SECTOR)
1153         last_node = m_next_node_index[m_track_node];
1154 
1155     int node = m_track_node;
1156     float distance = 0;
1157     std::vector<const ItemState *> items_to_collect;
1158     std::vector<const ItemState *> items_to_avoid;
1159 
1160     // 1) Filter and sort all items close by
1161     // -------------------------------------
1162     const float max_item_lookahead_distance = 20.0f;
1163     while(distance < max_item_lookahead_distance)
1164     {
1165         int n_index= DriveGraph::get()->getNode(node)->getIndex();
1166         const std::vector<ItemState *> &items_ahead =
1167             m_item_manager->getItemsInQuads(n_index);
1168         for(unsigned int i=0; i<items_ahead.size(); i++)
1169         {
1170             evaluateItems(items_ahead[i],  kart_aim_direction,
1171                           &items_to_avoid, &items_to_collect);
1172         }   // for i<items_ahead;
1173         distance += DriveGraph::get()->getDistanceToNext(node,
1174                                                       m_successor_index[node]);
1175         node = m_next_node_index[node];
1176         // Stop when we have reached the last quad
1177         if(node==last_node) break;
1178     }   // while (distance < max_item_lookahead_distance)
1179 
1180     //items_to_avoid and items_to_collect now contain the closest item information needed after
1181     //What matters is (a) if the lists are void ; (b) if they are not, what kind of item it is
1182 
1183     switch( m_kart->getPowerup()->getType() )
1184     {
1185     case PowerupManager::POWERUP_BUBBLEGUM:
1186         {
1187             handleBubblegum(item_skill, items_to_collect, items_to_avoid);
1188             break;
1189         } // POWERUP_BUBBLEGUM
1190 
1191     case PowerupManager::POWERUP_CAKE:
1192         {
1193             // if the kart has a shield, do not break it by using a cake.
1194             if((m_kart->getShieldTime() > min_bubble_time) && (stk_config->m_shield_restrict_weapons == true))
1195                 break;
1196 
1197             handleCake(item_skill);
1198             break;
1199         }   // POWERUP_CAKE
1200 
1201     case PowerupManager::POWERUP_BOWLING:
1202         {
1203             // if the kart has a shield, do not break it by using a bowling ball.
1204             if((m_kart->getShieldTime() > min_bubble_time) && (stk_config->m_shield_restrict_weapons == true))
1205                 break;
1206 
1207             handleBowling(item_skill);
1208             break;
1209         }   // POWERUP_BOWLING
1210 
1211     case PowerupManager::POWERUP_ZIPPER:
1212         // Do nothing. Further up a zipper is used if nitro should be selected,
1213         // saving the (potential more valuable nitro) for later
1214         break;   // POWERUP_ZIPPER
1215 
1216     case PowerupManager::POWERUP_PLUNGER:
1217         {
1218             // if the kart has a shield, do not break it by using a plunger.
1219             if((m_kart->getShieldTime() > min_bubble_time) && (stk_config->m_shield_restrict_weapons == true))
1220                 break;
1221 
1222             // Leave more time after a plunger, since it will take some
1223             // time before a plunger effect becomes obvious.
1224             if(m_time_since_last_shot < 5.0f) break;
1225 
1226             // Plungers can be fired backwards and are faster,
1227             // so allow more distance for shooting.
1228             bool fire_backwards = (m_kart_behind && m_kart_ahead &&
1229                                    m_distance_behind < m_distance_ahead) ||
1230                                   !m_kart_ahead;
1231             float distance      = fire_backwards ? m_distance_behind
1232                                                  : m_distance_ahead;
1233             m_controls->setFire(distance < 30.0f                 ||
1234                                  m_time_since_last_shot > 10.0f    );
1235             if(m_controls->getFire())
1236                 m_controls->setLookBack(fire_backwards);
1237             break;
1238         }   // POWERUP_PLUNGER
1239 
1240     case PowerupManager::POWERUP_SWITCH:
1241         {
1242             handleSwitch(item_skill, items_to_collect, items_to_avoid);
1243             break;
1244         } // POWERUP_SWITCH
1245 
1246     case PowerupManager::POWERUP_PARACHUTE:
1247         {
1248         // Wait one second more than a previous parachute
1249         if(m_time_since_last_shot > m_kart->getKartProperties()->getParachuteDurationOther() + 1.0f)
1250             m_controls->setFire(true);
1251         break;
1252         }// POWERUP_PARACHUTE
1253 
1254     case PowerupManager::POWERUP_SWATTER:
1255         {
1256              // if the kart has a shield, do not break it by using a swatter.
1257             if(m_kart->getShieldTime() > min_bubble_time)
1258                 break;
1259 
1260             handleSwatter(item_skill);
1261             break;
1262         } // POWERUP_SWATTER
1263     case PowerupManager::POWERUP_RUBBERBALL:
1264         // if the kart has a shield, do not break it by using a swatter.
1265         if((m_kart->getShieldTime() > min_bubble_time) && (stk_config->m_shield_restrict_weapons == true))
1266             break;
1267         // Perhaps some more sophisticated algorithm might be useful.
1268         // For now: fire if there is a kart ahead (which means that
1269         // this kart is certainly not the first kart)
1270         m_controls->setFire(m_kart_ahead != NULL);
1271         break;
1272     default:
1273         Log::error(getControllerName().c_str(),
1274                    "Invalid or unhandled powerup '%d' in default AI.",
1275                    m_kart->getPowerup()->getType());
1276         assert(false);
1277     }
1278     if(m_controls->getFire())  m_time_since_last_shot = 0.0f;
1279 }   // handleItems
1280 
1281 
1282 //-----------------------------------------------------------------------------
1283 /** Handle bubblegum depending on the chosen strategy
1284  * Level 2 : Use the shield immediately after a wait time
1285  * Level 3 : Use the shield against flyables except cakes. Use the shield against bad attachments
1286  *           and plunger. Use the bubble gum against an enemy close behind, except if holding a swatter.
1287  * Level 4 : Level 3, and protect against cakes too, and use before hitting gum/banana
1288  * Level 5 : Level 4, and use before hitting item box, and let plunger hit
1289  *                   (can use the shield after), and use against bomb only when the timer ends
1290  *  \param item_skill The skill with which to use the item
1291  *  \param items_to_collect The list of close good items
1292  *  \param items_to_avoid The list of close bad items
1293  */
handleBubblegum(int item_skill,const std::vector<const ItemState * > & items_to_collect,const std::vector<const ItemState * > & items_to_avoid)1294 void SkiddingAI::handleBubblegum(int item_skill,
1295                                  const std::vector<const ItemState *> &items_to_collect,
1296                                  const std::vector<const ItemState *> &items_to_avoid)
1297 {
1298     float shield_radius = m_ai_properties->m_shield_incoming_radius;
1299 
1300     int projectile_types[4]; //[3] basket, [2] cakes, [1] plunger, [0] bowling
1301     projectile_types[0] = ProjectileManager::get()->getNearbyProjectileCount(m_kart, shield_radius, PowerupManager::POWERUP_BOWLING);
1302     projectile_types[1] = ProjectileManager::get()->getNearbyProjectileCount(m_kart, shield_radius, PowerupManager::POWERUP_PLUNGER);
1303     projectile_types[2] = ProjectileManager::get()->getNearbyProjectileCount(m_kart, shield_radius, PowerupManager::POWERUP_CAKE);
1304     projectile_types[3] = ProjectileManager::get()->getNearbyProjectileCount(m_kart, shield_radius, PowerupManager::POWERUP_RUBBERBALL);
1305 
1306     bool projectile_is_close = false;
1307     projectile_is_close = ProjectileManager::get()->projectileIsClose(m_kart, shield_radius);
1308 
1309     Attachment::AttachmentType type = m_kart->getAttachment()->getType();
1310 
1311     if((item_skill == 2) && (m_time_since_last_shot > 2.0f))
1312     {
1313         m_controls->setFire(true);
1314         m_controls->setLookBack(false);
1315         return;
1316     }
1317 
1318     // Check if a flyable (cake, ...) is close. If so, use bubblegum
1319     // as shield
1320     if(item_skill == 3) //don't protect against cakes
1321     {
1322        if( !m_kart->isShielded() && projectile_is_close
1323           && projectile_types[2] == 0)
1324        {
1325           //don't discard swatter against plunger
1326           if( projectile_types[1] == 0
1327              || (projectile_types[1] >= 1 && type != Attachment::ATTACH_SWATTER))
1328           {
1329              m_controls->setFire(true);
1330              m_controls->setLookBack(false);
1331              return;
1332           }
1333        }
1334     }
1335     else if(item_skill == 4)
1336     {
1337        if( !m_kart->isShielded() && projectile_is_close)
1338        {
1339           //don't discard swatter against plunger
1340           if( projectile_types[1] == 0
1341              || (projectile_types[1] >= 1 && type != Attachment::ATTACH_SWATTER))
1342           {
1343              m_controls->setFire(true);
1344              m_controls->setLookBack(false);
1345              return;
1346           }
1347        }
1348     }
1349     else if (item_skill == 5) //don't protect against plungers alone
1350     {
1351        if( !m_kart->isShielded() && projectile_is_close)
1352        {
1353           if (projectile_types[0] >=1 || projectile_types[2] >=1 || projectile_types[3] >=1 )
1354           {
1355              m_controls->setFire(true);
1356              m_controls->setLookBack(false);
1357              return;
1358           }
1359        }
1360     }
1361 
1362     // Use shield to remove bad attachments
1363     if( (type == Attachment::ATTACH_BOMB && item_skill != 5)
1364       || type == Attachment::ATTACH_PARACHUTE
1365       || type == Attachment::ATTACH_ANVIL )
1366     {
1367         m_controls->setFire(true);
1368         m_controls->setLookBack(false);
1369         return;
1370     }
1371     //if it is a bomb, wait : we may pass it to another kart before the timer runs out
1372     if (item_skill == 5 && type == Attachment::ATTACH_BOMB)
1373     {
1374         if (m_kart->getAttachment()->getTicksLeft() < stk_config->time2Ticks(2))
1375         {
1376             m_controls->setFire(true);
1377             m_controls->setLookBack(false);
1378             return;
1379         }
1380     }
1381 
1382     //If the kart view is blocked by a plunger, use the shield
1383     if(m_kart->getBlockedByPlungerTicks()>0)
1384     {
1385         m_controls->setFire(true);
1386         m_controls->setLookBack(false);
1387         return;
1388     }
1389 
1390     // Use shield if kart is going to hit a bad item (banana or bubblegum)
1391     if((item_skill == 4) || (item_skill == 5))
1392     {
1393        if( !m_kart->isShielded() && items_to_avoid.size()>0)
1394        {
1395           float d = (items_to_avoid[0]->getXYZ() - m_kart->getXYZ()).length2();
1396 
1397           if ((item_skill == 4 && d < 1.5f) || (item_skill == 5 && d < 0.7f))
1398           {
1399              m_controls->setFire(true);
1400              m_controls->setLookBack(false);
1401              return;
1402           }
1403        }
1404     }
1405 
1406     // Use shield if kart is going to hit an item box
1407     if (item_skill == 5)
1408     {
1409        if( !m_kart->isShielded() && items_to_collect.size()>0)
1410        {
1411           float d = (items_to_collect[0]->getXYZ() - m_kart->getXYZ()).length2();
1412 
1413           if ((items_to_collect[0]->getType() == Item::ITEM_BONUS_BOX) && (d < 0.7f))
1414           {
1415              m_controls->setFire(true);
1416              m_controls->setLookBack(false);
1417              return;
1418           }
1419        }
1420     }
1421 
1422     // Avoid dropping all bubble gums one after another
1423     if( m_time_since_last_shot < 2.0f) return;
1424 
1425     // Use bubblegum if the next kart  behind is 'close'
1426     // and straight behind
1427 
1428     bool straight_behind = false;
1429     if (m_kart_behind)
1430     {
1431         Vec3 behind_lc = m_kart->getTrans().inverse()
1432             (m_kart_behind->getXYZ());
1433         const float abs_angle =
1434             atan2f(fabsf(behind_lc.x()), fabsf(behind_lc.z()));
1435         if (abs_angle < 0.2f) straight_behind = true;
1436     }
1437 
1438     if(m_distance_behind < 8.0f && straight_behind &&
1439        (!m_item_manager->areItemsSwitched() || item_skill < 4))
1440     {
1441         m_controls->setFire(true);
1442         m_controls->setLookBack(true);
1443         return;
1444     }
1445     return;
1446 } //handleBubblegum
1447 
1448 //-----------------------------------------------------------------------------
1449 /** Handle cake depending on the chosen strategy
1450  * Level 2 : Use the cake against any close vulnerable enemy, with priority to those ahead and close,
1451  *           check if the enemy is roughly ahead.
1452  * Level 3 : Level 2 and don't fire on slower karts
1453  * Level 4 : Level 3 and fire if the kart has a swatter which may hit us
1454  * Level 5 : Level 4 and don't fire on a shielded kart if we're just behind (gum)
1455  *  \param item_skill The skill with which to use the item
1456  */
handleCake(int item_skill)1457 void SkiddingAI::handleCake(int item_skill)
1458 {
1459     // Leave some time between shots
1460     if(m_time_since_last_shot<2.0f) return;
1461 
1462     // Do not fire if the target is too far
1463     bool kart_behind_is_close = m_distance_behind < 25.0f;
1464     bool kart_ahead_is_close = m_distance_ahead < 20.0f;
1465 
1466     bool kart_ahead_has_gum = false;
1467 
1468     bool straight_ahead = false;
1469     bool rather_ahead = false;//used to not fire in big curves
1470     bool rather_behind = false;
1471 
1472     if (m_kart_ahead)
1473     {
1474         kart_ahead_has_gum = (m_kart_ahead->getAttachment()->getType()
1475                                  == Attachment::ATTACH_BUBBLEGUM_SHIELD);
1476 
1477         Vec3 ahead_lc = m_kart->getTrans().inverse()
1478             (m_kart_ahead->getXYZ());
1479         const float abs_angle =
1480             atan2f(fabsf(ahead_lc.x()), fabsf(ahead_lc.z()));
1481         if (abs_angle < 0.2f) straight_ahead = true;
1482         if (abs_angle < 0.5f) rather_ahead = true;
1483     }
1484 
1485     if (m_kart_behind)
1486     {
1487         Vec3 behind_lc = m_kart->getTrans().inverse()
1488             (m_kart_behind->getXYZ());
1489         const float abs_angle =
1490             atan2f(fabsf(behind_lc.x()), fabsf(behind_lc.z()));
1491         if (abs_angle < 0.5f) rather_behind = true;
1492     }
1493 
1494     // Do not fire if the kart is driving too slow
1495     bool kart_behind_is_slow =
1496         (m_kart_behind && m_kart_behind->getSpeed() < m_kart->getSpeed());
1497     bool kart_ahead_is_slow =
1498         (m_kart_ahead && m_kart_ahead->getSpeed() < m_kart->getSpeed());
1499 
1500     //Establish which of the target is better if any
1501     float fire_ahead = 0.0f;
1502     float fire_behind = 0.0f;
1503 
1504     if (kart_behind_is_close && rather_behind) fire_behind += 25.0f - m_distance_behind;
1505     else                      fire_behind -= 100.0f;
1506 
1507     if (kart_ahead_is_close && rather_ahead)  fire_ahead += 30.0f - m_distance_ahead; //prefer targetting ahead
1508     else                      fire_ahead -= 100.0f;
1509 
1510     // Don't fire on an invulnerable kart.
1511     if (m_kart_behind && m_kart_behind->isInvulnerable())
1512         fire_behind -= 100.0f;
1513 
1514     if (m_kart_ahead && m_kart_ahead->isInvulnerable())
1515         fire_ahead -= 100.0f;
1516 
1517     // Don't fire at a kart that is slower than us. Reason is that
1518     // we can either save the cake for later since we will overtake
1519     // the kart anyway, or that this might force the kart ahead to
1520     // use its nitro/zipper (and then we will shoot since then the
1521     // kart is faster).
1522     if(item_skill >= 3)
1523     {
1524         if (kart_behind_is_slow) fire_behind -= 50.0f;
1525         else                     fire_behind += 25.0f;
1526 
1527         if (kart_ahead_is_slow) fire_ahead -= 50.0f;
1528         else                    fire_ahead += 25.0f;
1529     }
1530 
1531     //Try to take out a kart which has a swatter in priority
1532     if (item_skill>=4)
1533     {
1534         bool kart_behind_has_swatter = false;
1535         if (m_kart_behind)
1536         {
1537             kart_behind_has_swatter = (m_kart_behind->getAttachment()->getType()
1538                                        == Attachment::ATTACH_SWATTER);
1539         }
1540 
1541         bool kart_ahead_has_swatter = false;
1542         if (m_kart_ahead)
1543         {
1544             kart_ahead_has_swatter = (m_kart_ahead->getAttachment()->getType()
1545                                       == Attachment::ATTACH_SWATTER);
1546         }
1547 
1548         //If it is slower, the swatter is more dangerous
1549         if (kart_ahead_has_swatter)
1550         {
1551             if (kart_ahead_is_slow) fire_ahead += 75.0f;
1552             else                    fire_ahead += 15.0f;
1553         }
1554         //If it is slower, we can wait for it to get faster and closer before firing
1555         if (kart_behind_has_swatter)
1556         {
1557             if (!kart_behind_is_slow) fire_behind += 25.0f;
1558         }
1559     }
1560 
1561     //Don't fire on a kart straight ahead with a bubblegum shield
1562     if (item_skill == 5 && kart_ahead_has_gum && straight_ahead)
1563     {
1564         fire_ahead -= 100.0f;
1565     }
1566 
1567     // Compare how interesting each target is to determine if firing backwards or not
1568 
1569     bool fire_backwards = false;
1570 
1571     if (fire_behind > fire_ahead)
1572     {
1573         fire_backwards = true;
1574     }
1575 
1576     bool fire = (fire_backwards && fire_behind > 0) || (!fire_backwards && fire_ahead > 0);
1577 
1578     m_controls->setFire( fire );
1579     if(m_controls->getFire())
1580         m_controls->setLookBack(fire_backwards);
1581     return;
1582 
1583 } //handleCake
1584 
1585 
1586 //-----------------------------------------------------------------------------
1587 /** Handle the bowling ball depending on the chosen strategy
1588  * Level 2 : Use the bowling ball against enemies straight ahead
1589              or straight behind, and not invulnerable, with a 5 second delay
1590  * Level 3 : Only 3 seconds of delay
1591  * Level 4 : Same as level 3
1592  * Level 5 : Level 4 and don't fire on a shielded kart if we're just behind (gum)
1593  *  \param item_skill The skill with which to use the item
1594  */
handleBowling(int item_skill)1595 void SkiddingAI::handleBowling(int item_skill)
1596 {
1597     // Leave more time between bowling balls, since they are
1598     // slower, so it should take longer to hit something which
1599     // can result in changing our target.
1600     if(item_skill == 2 && m_time_since_last_shot < 5.0f) return;
1601     if(item_skill >= 3 && m_time_since_last_shot < 3.0f) return;
1602 
1603     // Consider angle towards karts
1604     bool straight_behind = false;
1605     bool straight_ahead = false;
1606     if (m_kart_behind)
1607     {
1608         Vec3 behind_lc = m_kart->getTrans().inverse()
1609             (m_kart_behind->getXYZ());
1610         const float abs_angle =
1611             atan2f(fabsf(behind_lc.x()), fabsf(behind_lc.z()));
1612         if (abs_angle < 0.2f) straight_behind = true;
1613     }
1614     if (m_kart_ahead)
1615     {
1616         Vec3 ahead_lc = m_kart->getTrans().inverse()
1617             (m_kart_ahead->getXYZ());
1618         const float abs_angle =
1619             atan2f(fabsf(ahead_lc.x()), fabsf(ahead_lc.z()));
1620         if (abs_angle < 0.2f) straight_ahead = true;
1621     }
1622 
1623     // Don't fire if the kart we are aiming at is invulnerable.
1624     if (m_kart_behind && m_kart_behind->isInvulnerable())
1625         straight_behind = false;
1626 
1627     if (m_kart_ahead && m_kart_ahead->isInvulnerable())
1628         straight_ahead = false;
1629 
1630     //Don't fire on a kart straight ahead with a bubblegum shield
1631     if (item_skill == 5)
1632     {
1633         bool kart_ahead_has_gum = false;
1634 
1635         if (m_kart_ahead)
1636         {
1637             kart_ahead_has_gum = (m_kart_ahead->getAttachment()->getType()
1638                                  == Attachment::ATTACH_BUBBLEGUM_SHIELD);
1639         }
1640 
1641         if (kart_ahead_has_gum && straight_ahead)
1642         {
1643             straight_ahead = false;//disable firing ahead
1644         }
1645     }
1646 
1647     //don't fire if there is no vulnerable kart in the line of fire
1648     if (!straight_behind && !straight_ahead)
1649     {
1650         return;
1651     }
1652 
1653     // Bowling balls are slower, so only fire on closer karts - but when
1654     // firing backwards, the kart can be further away, since the ball
1655     // acts a bit like a mine (and the kart is racing towards it, too)
1656     bool fire_backwards = (straight_behind && !straight_ahead) ||
1657                           (straight_behind && m_distance_behind < m_distance_ahead);
1658 
1659     float distance = fire_backwards ? m_distance_behind
1660                                     : m_distance_ahead;
1661     m_controls->setFire( ( (fire_backwards && distance < 30.0f)  ||
1662                            (!fire_backwards && distance <10.0f)    ));
1663     if(m_controls->getFire())
1664         m_controls->setLookBack(fire_backwards);
1665     return;
1666 } //handleBowling
1667 
1668 //-----------------------------------------------------------------------------
1669 /** Handle the swatter depending on the chosen strategy
1670  * Level 2 : Use the swatter immediately after a wait time
1671  * Level 3 : Use the swatter when enemies are close
1672  * Level 4 : Level 3 and use the swatter to remove bad attachments
1673  * Level 5 : Level 4 and use against bomb only when the timer ends
1674  *  \param item_skill The skill with which to use the item
1675  */
handleSwatter(int item_skill)1676 void SkiddingAI::handleSwatter(int item_skill)
1677 {
1678     Attachment::AttachmentType type = m_kart->getAttachment()->getType();
1679 
1680     if((item_skill == 2) && (m_time_since_last_shot > 2.0f))
1681     {
1682         m_controls->setFire(true);
1683         return;
1684     }
1685 
1686     // Use swatter to remove bad attachments
1687     if((item_skill == 4) || (item_skill == 5))
1688     {
1689         if( (type == Attachment::ATTACH_BOMB
1690              && item_skill == 4)
1691              || type == Attachment::ATTACH_PARACHUTE
1692              || type == Attachment::ATTACH_ANVIL )
1693         {
1694             m_controls->setFire(true);
1695             m_controls->setLookBack(false);
1696             return;
1697         }
1698         //if it is a bomb, wait : we may pass it to another kart before the timer runs out
1699         if (item_skill == 5 && type == Attachment::ATTACH_BOMB)
1700         {
1701             if (m_kart->getAttachment()->getTicksLeft() > stk_config->time2Ticks(3))
1702             {
1703                 m_controls->setFire(true);
1704                 m_controls->setLookBack(false);
1705                 return;
1706             }
1707         }
1708     }
1709      // Squared distance for which the swatter works
1710      float d2 = m_kart->getKartProperties()->getSwatterDistance();
1711 
1712      // Fire if the closest kart ahead or to the back is not already
1713      // squashed and close enough.
1714      // FIXME: this can be improved on, since more than one kart might
1715      //        be hit, and a kart ahead might not be at an angle at
1716      //        which the glove can be used.
1717      if(  ( m_kart_ahead && !m_kart_ahead->isSquashed()      &&
1718              (m_kart_ahead->getXYZ()-m_kart->getXYZ()).length2()<d2 &&
1719              m_kart_ahead->getSpeed() < m_kart->getSpeed()     ) ||
1720           ( m_kart_behind && !m_kart_behind->isSquashed() &&
1721              (m_kart_behind->getXYZ()-m_kart->getXYZ()).length2()<d2) )
1722              m_controls->setFire(true);
1723      return;
1724 } //handleSwatter
1725 
1726 //-----------------------------------------------------------------------------
1727 /** Handle switch depending on the chosen strategy
1728  * Level 2 : Use the switch after a wait time
1729  * Level 3 : Same as level 2 but don't fire if close to a good item
1730  * Level 4 : Same as level 3 and fire if very close to a bad item
1731  * Level 5 : Use if it makes a better item available, or if very close
1732  *           to a bad item. Don't use it if too close of a good item.
1733  *  \param item_skill The skill with which to use the item
1734  *  \param items_to_collect The list of close good items
1735  *  \param items_to_avoid The list of close bad items
1736  */
handleSwitch(int item_skill,const std::vector<const ItemState * > & items_to_collect,const std::vector<const ItemState * > & items_to_avoid)1737 void SkiddingAI::handleSwitch(int item_skill,
1738                               const std::vector<const ItemState *> &items_to_collect,
1739                               const std::vector<const ItemState *> &items_to_avoid)
1740 {
1741     // It's extremely unlikely two switches are used close one after another
1742     if(item_skill == 2)
1743     {
1744         if (m_time_since_last_shot > 2.0f)
1745         {
1746             m_controls->setFire(true);
1747            return;
1748         }
1749     }
1750 
1751     else if((item_skill == 3) || (item_skill == 4))
1752     {
1753        if( (item_skill == 4) && items_to_avoid.size() > 0)
1754        {
1755            float d = (items_to_avoid[0]->getXYZ() - m_kart->getXYZ()).length2();
1756 
1757            if (d < 2.0f)
1758            {
1759                m_controls->setFire(true);
1760                return;
1761            }
1762         }
1763         else if (items_to_collect.size() > 0)
1764         {
1765             float d = (items_to_collect[0]->getXYZ() - m_kart->getXYZ()).length2();
1766 
1767             if (d > 10.0f)
1768             {
1769                m_controls->setFire(true);
1770                return;
1771             }
1772         }
1773         else if (m_time_since_last_shot > 2.0f)
1774         {
1775              m_controls->setFire(true);
1776              return;
1777         }
1778     }
1779 
1780     //TODO : retrieve ranking powerup class and use it to evaluate the best item
1781     //       available depending of if the switch is used or not
1782     //       In the mean time big nitro > item box > small nitro
1783     //TODO : make steering switch-aware so that the kart goes towards a bad item
1784     //       and use the switch at the last moment
1785     //It would also be possible but complicated to check if using the switch will
1786     //cause another kart not far from taking a bad item instead of a good one
1787     //It should also be possible but complicated to discard items when a good
1788     //and a bad one are two close from one another
1789     else if(item_skill == 5)
1790     {
1791        //First step : identify the best available item
1792        int bad = 0;
1793        int good = 0;
1794 
1795        //Good will store 1 for nitro, big or small, 2 for item box
1796        //Big nitro are usually hard to take for the AI
1797        for(int i=(int)items_to_collect.size()-1; i>=0; i--)
1798        {
1799            if (items_to_collect[i]->getType() == Item::ITEM_BONUS_BOX)
1800            {
1801               good = 2;
1802              i = -1;
1803            }
1804           else if ( (items_to_collect[i]->getType() == Item::ITEM_NITRO_BIG) ||
1805                (items_to_collect[i]->getType() == Item::ITEM_NITRO_SMALL) )
1806           {
1807               good = 1;
1808           }
1809        }
1810 
1811        //Bad will store 2 for bananas, 3 for bubble gum
1812        for(int i=(int)items_to_avoid.size()-1; i>=0; i--)
1813        {
1814            if (items_to_avoid[i]->getType() == Item::ITEM_BUBBLEGUM)
1815            {
1816               bad = 3;
1817               i = -1;
1818            }
1819            else if ( items_to_avoid[i]->getType() == Item::ITEM_BANANA )
1820            {
1821               bad = 2;
1822            }
1823        }
1824 
1825        //Second step : make sure a close item don't make the choice pointless
1826        if( items_to_avoid.size()>0)
1827        {
1828            float d = (items_to_avoid[0]->getXYZ() - m_kart->getXYZ()).length2();
1829 
1830            //fire if very close to a bad item
1831            if (d < 2.0f)
1832            {
1833               m_controls->setFire(true);
1834               return;
1835            }
1836        }
1837        if( items_to_collect.size()>0)
1838        {
1839           float d = (items_to_collect[0]->getXYZ() - m_kart->getXYZ()).length2();
1840 
1841           //don't fire if close to a good item
1842           if (d < 5.0f)
1843           {
1844              return;
1845           }
1846        }
1847 
1848        //Third step : Use or don't use to get the best available item
1849        if( bad > good)
1850        {
1851           m_controls->setFire(true);
1852           return;
1853        }
1854     } //item_skill == 5
1855 
1856     return;
1857 } //handleSwitch
1858 
1859 //-----------------------------------------------------------------------------
1860 /** Determines the closest karts just behind and in front of this kart. The
1861  *  'closeness' is for now simply based on the position, i.e. if a kart is
1862  *  more than one lap behind or ahead, it is not considered to be closest.
1863  */
computeNearestKarts()1864 void SkiddingAI::computeNearestKarts()
1865 {
1866     int my_position    = m_kart->getPosition();
1867 
1868     // If we are not the first, there must be another kart ahead of this kart
1869     if( my_position>1 )
1870     {
1871         m_kart_ahead = m_world->getKartAtPosition(my_position-1);
1872         if(m_kart_ahead &&
1873               ( m_kart_ahead->isEliminated() || m_kart_ahead->hasFinishedRace()))
1874               m_kart_ahead = NULL;
1875     }
1876     else
1877         m_kart_ahead = NULL;
1878 
1879     if( my_position<(int)m_world->getCurrentNumKarts())
1880     {
1881         m_kart_behind = m_world->getKartAtPosition(my_position+1);
1882         if(m_kart_behind &&
1883             (m_kart_behind->isEliminated() || m_kart_behind->hasFinishedRace()))
1884             m_kart_behind = NULL;
1885     }
1886     else
1887         m_kart_behind = NULL;
1888 
1889     m_distance_leader = m_distance_ahead = m_distance_behind = 9999999.9f;
1890     float my_dist = m_world->getOverallDistance(m_kart->getWorldKartId());
1891 
1892     if(m_kart_ahead)
1893         m_distance_ahead = m_world->getOverallDistance(m_kart_ahead->getWorldKartId()) - my_dist;
1894     if(m_kart_behind)
1895         m_distance_behind = my_dist - m_world->getOverallDistance(m_kart_behind->getWorldKartId());
1896 
1897     if(   RaceManager::get()->isFollowMode() && m_kart->getWorldKartId() != 0)
1898         m_distance_leader = m_world->getOverallDistance(0 /*leader kart ID*/) - my_dist;
1899 
1900     // Compute distance to target player kart
1901 
1902     float target_overall_distance = 0.0f;
1903     float own_overall_distance = m_world->getOverallDistance(m_kart->getWorldKartId());
1904     m_num_players_ahead = 0;
1905 
1906     if (m_enabled_network_ai)
1907     {
1908         // Use maximum distance to player for network ai
1909         m_distance_to_player = 9999999.9f;
1910         return;
1911     }
1912     unsigned int n = ProfileWorld::isProfileMode() ? 0 : RaceManager::get()->getNumPlayers();
1913 
1914     std::vector<float> overall_distance;
1915     // Get the players distances
1916     for(unsigned int i=0; i<n; i++)
1917     {
1918         unsigned int kart_id = m_world->getPlayerKart(i)->getWorldKartId();
1919         overall_distance.push_back(m_world->getOverallDistance(kart_id));
1920     }
1921 
1922     // Sort the list in descending order
1923     std::sort(overall_distance.begin(), overall_distance.end(), std::greater<float>());
1924 
1925     // Get the AI's position (the position update may not be done, leading to crashes)
1926     int curr_position = 1;
1927     for(unsigned int i=0; i<m_world->getNumKarts(); i++)
1928     {
1929         if(   m_world->getOverallDistance(i) > own_overall_distance
1930            && !m_world->getKart(i)->isEliminated())
1931             curr_position++;
1932     }
1933 
1934     for(unsigned int i=0; i<n; i++)
1935     {
1936         if(overall_distance[i]>own_overall_distance)
1937             m_num_players_ahead++;
1938     }
1939 
1940     // Force best driving when profiling and for FTL leaders
1941     if(   ProfileWorld::isProfileMode()
1942        || ( RaceManager::get()->isFollowMode() && m_kart->getWorldKartId() == 0))
1943         target_overall_distance = 999999.9f;
1944 
1945     // In higher difficulties and in follow the leader, rubber band towards the first player,
1946     // if at all (SuperTux has no rubber banding at all). Boosted AIs also target the 1st player.
1947     else if (   RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_HARD
1948              || RaceManager::get()->getDifficulty() == RaceManager::DIFFICULTY_BEST
1949              || RaceManager::get()->isFollowMode()
1950              || m_kart->getBoostAI())
1951     {
1952         target_overall_distance = overall_distance[n-1]; // Highest player distance
1953     }
1954     // Distribute the AIs to players
1955     else
1956     {
1957         int num_ai = m_world->getNumKarts() - RaceManager::get()->getNumPlayers();
1958         int position_among_ai = curr_position - m_num_players_ahead;
1959 
1960         // Converts a position among AI to a position among players
1961         // The 1st player get an index of 0, the 2nd an index of 2, etc.
1962         int target_index = 0;
1963 
1964         // Avoid a division by 0. If there is only one AI, it will target the first player
1965         if (num_ai > 1)
1966         {
1967             target_index  = (position_among_ai-1) * (RaceManager::get()->getNumPlayers()-1);
1968             target_index += (num_ai/2) - 1;
1969             target_index  = target_index / (num_ai - 1);
1970         }
1971 
1972         assert(target_index >= 0 && (unsigned)target_index <= n-1);
1973         target_overall_distance = overall_distance[target_index];
1974     }
1975     // Now convert 'maximum overall distance' to distance to player.
1976     m_distance_to_player = own_overall_distance - target_overall_distance;
1977 }   // computeNearestKarts
1978 
1979 //-----------------------------------------------------------------------------
1980 /** Determines if the AI should accelerate or not, and if not if it should brake.
1981  *  \param ticks Time step size.
1982  * //TODO : make acceleration steering aware
1983  */
handleAccelerationAndBraking(int ticks)1984 void SkiddingAI::handleAccelerationAndBraking(int ticks)
1985 {
1986     // Step 0 (start only) : do not accelerate until we have delayed the start enough
1987     if( m_start_delay > 0 )
1988     {
1989         m_start_delay -= ticks;
1990         m_controls->setAccel(0.0f);
1991         return;
1992     }
1993 
1994     // Step 1 : determine the appropriate max speed for the curve we are in
1995     //         (this is also calculated in straights, as there is always a
1996     //          curve lurking at its end)
1997 
1998     // FIXME - requires fixing of the turn radius bugs
1999 
2000     float max_turn_speed =
2001         m_kart->getSpeedForTurnRadius(m_current_curve_radius)*1.5f;
2002 
2003     // A kart will not brake when the speed is already slower than this
2004     // value. This prevents a kart from going too slow (or even backwards)
2005     // in tight curves.
2006     const float MIN_SPEED = 5.0f;
2007 
2008     // Step 2 : handle braking (there are some cases who need braking besides
2009     //          a too great speed, like overtaking the leader in FTL)
2010     handleBraking(max_turn_speed, MIN_SPEED);
2011 
2012     if( m_controls->getBrake())
2013     {
2014         m_controls->setAccel(0.0f);
2015         return;
2016     }
2017 
2018     // Step 3 : handle nitro and zipper
2019 
2020     // If a bomb is attached, nitro might already be set.
2021     // FIXME : the bomb situation should be merged here
2022     if(!m_controls->getNitro())
2023         handleNitroAndZipper(max_turn_speed);
2024 
2025     // Step 4 : handle plunger effect
2026 
2027     if(m_kart->getBlockedByPlungerTicks()>0)
2028     {
2029         int item_skill = computeSkill(ITEM_SKILL);
2030         float accel_threshold = 0.5f;
2031 
2032         if (item_skill == 0)
2033             accel_threshold = 0.3f;
2034         else if (item_skill == 1)
2035             accel_threshold = 0.5f;
2036         else if (item_skill == 2)
2037             accel_threshold = 0.6f;
2038         else if (item_skill == 3)
2039             accel_threshold = 0.7f;
2040         else if (item_skill == 4)
2041             accel_threshold = 0.8f;
2042         // The best players, knowing the track, don't slow down with a plunger
2043         else if (item_skill == 5)
2044             accel_threshold = 1.0f;
2045 
2046         if(m_kart->getSpeed() > m_kart->getCurrentMaxSpeed() * accel_threshold)
2047             m_controls->setAccel(0.0f);
2048         return;
2049     }
2050 
2051     m_controls->setAccel(stk_config->m_ai_acceleration);
2052 
2053 }   // handleAccelerationAndBraking
2054 
2055 
2056 //-----------------------------------------------------------------------------
2057 /** This function decides if the AI should brake.
2058  *  The decision can be based on race mode (e.g. in follow the leader the AI
2059  *  will brake if it is ahead of the leader). Otherwise it will depend on
2060  *  the direction the AI is facing (if it's not facing in the track direction
2061  *  it will brake in order to make it easier to re-align itself), and
2062  *  estimated curve radius (brake to avoid being pushed out of a curve).
2063  */
handleBraking(float max_turn_speed,float min_speed)2064 void SkiddingAI::handleBraking(float max_turn_speed, float min_speed)
2065 {
2066     m_controls->setBrake(false);
2067     // In follow the leader mode, the kart should brake if they are ahead of
2068     // the leader (and not the leader, i.e. don't have initial position 1)
2069     // TODO : if there is still time in the countdown and the leader is faster,
2070     //        the AI kart should not slow down too much, to stay closer to the
2071     //        leader once overtaken.
2072     if(   RaceManager::get()->isFollowMode() && m_distance_leader < 2
2073        && m_kart->getInitialPosition()>1
2074        && m_world->getOverallDistance(m_kart->getWorldKartId()) > 0 )
2075     {
2076 #ifdef DEBUG
2077     if(m_ai_debug)
2078         Log::debug(getControllerName().c_str(), "braking: %s too close of leader.",
2079                    m_kart->getIdent().c_str());
2080 #endif
2081 
2082         m_controls->setBrake(true);
2083         return;
2084     }
2085 
2086     // If the kart is not facing roughly in the direction of the track, brake
2087     // so that it is easier for the kart to turn in the right direction.
2088     if(m_current_track_direction==DriveNode::DIR_UNDEFINED &&
2089         m_kart->getSpeed() > min_speed)
2090     {
2091 #ifdef DEBUG
2092         if(m_ai_debug)
2093             Log::debug(getControllerName().c_str(),
2094                        "%s not aligned with track.",
2095                        m_kart->getIdent().c_str());
2096 #endif
2097         m_controls->setBrake(true);
2098         return;
2099     }
2100     if(m_current_track_direction==DriveNode::DIR_LEFT ||
2101        m_current_track_direction==DriveNode::DIR_RIGHT   )
2102     {
2103         if(m_kart->getSpeed() > max_turn_speed  &&
2104             m_kart->getSpeed()>min_speed        &&
2105             fabsf(m_controls->getSteer()) > 0.95f )
2106         {
2107             m_controls->setBrake(true);
2108 #ifdef DEBUG
2109             if(m_ai_debug)
2110                 Log::debug(getControllerName().c_str(),
2111                            "speed %f too tight curve: radius %f ",
2112                            m_kart->getSpeed(),
2113                            m_kart->getIdent().c_str(),
2114                            m_current_curve_radius);
2115 #endif
2116         }
2117         return;
2118     }
2119 
2120     return;
2121 
2122 }   // handleBraking
2123 
2124 //-----------------------------------------------------------------------------
handleRaceStart()2125 void SkiddingAI::handleRaceStart()
2126 {
2127     if( m_start_delay <  0 )
2128     {
2129         if (m_enabled_network_ai)
2130         {
2131             m_start_delay = 0;
2132             return;
2133         }
2134         // Each kart starts at a different, random time, and the time is
2135         // smaller depending on the difficulty.
2136         m_start_delay = stk_config->time2Ticks(
2137                         m_ai_properties->m_min_start_delay
2138                       + (float) rand() / RAND_MAX
2139                       * (m_ai_properties->m_max_start_delay -
2140                          m_ai_properties->m_min_start_delay)   );
2141 
2142         float false_start_probability =
2143                m_superpower == RaceManager::SUPERPOWER_NOLOK_BOSS
2144                ? 0.0f  : m_ai_properties->m_false_start_probability;
2145 
2146         // Now check for a false start. If so, add 1 second penalty time.
2147         if (rand() < RAND_MAX * false_start_probability)
2148         {
2149             m_start_delay+=stk_config->m_penalty_ticks;
2150             return;
2151         }
2152         m_kart->setStartupBoost(m_kart->getStartupBoostFromStartTicks(
2153             m_start_delay + stk_config->time2Ticks(1.0f)));
2154         m_start_delay = 0;
2155     }
2156 }   // handleRaceStart
2157 
2158 //-----------------------------------------------------------------------------
2159 //TODO: if the AI is crashing constantly, make it move backwards in a straight
2160 //line, then move forward while turning.
2161 
handleRescue(const float dt)2162 void SkiddingAI::handleRescue(const float dt)
2163 {
2164     // check if kart is stuck
2165     if(m_kart->getSpeed()<2.0f && !m_kart->getKartAnimation() &&
2166         !m_world->isStartPhase() && m_start_delay == 0)
2167     {
2168         m_time_since_stuck += dt;
2169         if(m_time_since_stuck > 2.0f)
2170         {
2171             // For network AI controller
2172             if (m_enabled_network_ai)
2173                 m_controls->setRescue(true);
2174             else
2175                 RescueAnimation::create(m_kart);
2176             m_time_since_stuck=0.0f;
2177         }   // m_time_since_stuck > 2.0f
2178     }
2179     else
2180     {
2181         m_time_since_stuck = 0.0f;
2182     }
2183 }   // handleRescue
2184 
2185 //-----------------------------------------------------------------------------
2186 /** Decides wether to use nitro and zipper or not.
2187  */
handleNitroAndZipper(float max_safe_speed)2188 void SkiddingAI::handleNitroAndZipper(float max_safe_speed)
2189 {
2190     int nitro_skill = computeSkill(NITRO_SKILL);
2191     int item_skill = computeSkill(ITEM_SKILL);
2192 
2193     //Nitro continue to be advantageous during the fadeout (nitro ticks continue to tick in the negatives)
2194     int nitro_ticks = m_kart->getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_NITRO);
2195     float time_until_fadeout = ( stk_config->ticks2Time(nitro_ticks)
2196                        + m_kart->getKartProperties()->getNitroFadeOutTime() );
2197 
2198     // Because it takes some time after nitro activation to accelerate to the increased max speed,
2199     // the best moment for the next burst of nitro is not when the fade-out has
2200     // finished, but it is not either before the fade-out starts.
2201     float nitro_max_active = m_kart->getKartProperties()->getNitroDuration();
2202     float nitro_max_fadeout = m_kart->getKartProperties()->getNitroFadeOutTime();
2203 
2204     //Nitro skill 0 : don't use
2205     //Nitro skill 1 : don't use if the kart is braking, is not on the ground, has finished the race, has no nitro,
2206     //                has a parachute or an anvil attached, or has a plunger in the face.
2207     //                Otherwise, use it immediately
2208     //Nitro skill 2 : Don't use nitro if there is more than 35% of main effect left..
2209     //                Use it when at max speed or under 5 of speed (after rescue, etc.). Use it to pass bombs.
2210     //                Tries to builds a reserve of 4 energy to use towards the end
2211     //Nitro skill 3 : Same as level 2, but don't use until 10% of main effect left, and don't use close
2212     //                to bad items, and has a target reserve of 8 energy
2213     //Nitro skill 4 : Same as level 3, but don't use until 50% of fadeout left and ignore the plunger
2214     //                and has a target reserve of 12 energy
2215 
2216     m_controls->setNitro(false);
2217 
2218     float energy_reserve = 0;
2219 
2220     if (nitro_skill == 2)
2221         energy_reserve = 4;
2222     else if (nitro_skill == 3)
2223         energy_reserve = 8;
2224     else if (nitro_skill == 4)
2225         energy_reserve = 12;
2226 
2227     // No point in building a big nitro reserve in nitro for FTL AIs,
2228     // just keep enough to help accelerating after an accident
2229     if(RaceManager::get()->isFollowMode())
2230         energy_reserve = std::min(2.0f, energy_reserve);
2231 
2232     // Don't use nitro or zipper if we are braking
2233     if(m_controls->getBrake()) return;
2234 
2235     // Don't use nitro or zipper if the kart is not on ground or has finished the race
2236     if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return;
2237 
2238     // Don't use nitro or zipper when the AI has a plunger in the face!
2239     if(m_kart->getBlockedByPlungerTicks()>0)
2240     {
2241         if ((nitro_skill < 4) && (item_skill < 5))
2242             return;
2243         // No else-if because the nitro_skill and item_skill conditions can happen together
2244         if (nitro_skill < 4)
2245             nitro_skill = 0;
2246         if (item_skill < 5)
2247             item_skill = 0;
2248     }
2249 
2250     // Don't use nitro or zipper if it would make the kart go too fast
2251 
2252     if(m_kart->getSpeed() + m_kart->getKartProperties()->getNitroMaxSpeedIncrease() > max_safe_speed)
2253         nitro_skill = 0;
2254 
2255     // FIXME : as the zipper can give +15, but only gives +5 instant, this may be too conservative
2256     if(m_kart->getSpeed() + m_kart->getKartProperties()->getZipperMaxSpeedIncrease() > max_safe_speed)
2257         item_skill = 0;
2258 
2259     // If a parachute or anvil is attached, the nitro and zipper don't give much
2260     // benefit. Better wait till later.
2261     const bool has_slowdown_attachment =
2262         m_kart->getAttachment()->getType()==Attachment::ATTACH_PARACHUTE ||
2263         m_kart->getAttachment()->getType()==Attachment::ATTACH_ANVIL;
2264     if(has_slowdown_attachment) return;
2265 
2266     // Don't compute nitro usage if we don't have nitro
2267     if( m_kart->getEnergy()==0 )
2268     {
2269        nitro_skill = 0;
2270     }
2271 
2272     // If the AI has a lot of nitro, it should consume it without waiting for some fadeout
2273     bool big_reserve = false;
2274 
2275 
2276     // If this kart is the last kart, set nitro reserve to be at most 2
2277     const unsigned int num_karts = m_world->getCurrentNumKarts();
2278     if(nitro_skill >= 2 && m_kart->getPosition()== (int)num_karts &&
2279         num_karts > 1 )
2280     {
2281         energy_reserve = 2;
2282     }
2283 
2284     // Estimate time towards the end of the race.
2285     // Decreases the reserve size when there is an estimate of time remaining
2286     // to the end of less than 2,5 times the maximum nitro effect duration.
2287     // This vary depending on kart characteristic.
2288     // There is a margin because the kart will go a bit faster than predicted
2289     // by the estimate, because the kart may collect more nitro and because
2290     // there may be moments when it's not useful to use nitro (parachutes, etc).
2291     if(nitro_skill >= 2 && energy_reserve > 0.0f)
2292     {
2293         float finish = m_world->getEstimatedFinishTime(m_kart->getWorldKartId()) - m_world->getTime();
2294         float max_time_effect = (nitro_max_active + nitro_max_fadeout) / m_kart->getKartProperties()->getNitroConsumption()
2295                                 * m_kart->getEnergy()*2; //the minimum burst consumes around 0.5 energy
2296 
2297         // The burster forces the AI to consume its reserve by series of 2 bursts
2298         // Otherwise the bursting differences of the various nitro skill wouldn't matter here
2299         // In short races, most AI nitro usage may be at the end with the reserve
2300 
2301         if(m_burster && time_until_fadeout >= 0)
2302             energy_reserve = 0;
2303         else if (m_burster)
2304             m_burster = false;
2305 
2306         if( (2.5f*max_time_effect) >= finish )
2307         {
2308             // Absolute reduction to avoid small amount of unburned nitro at the end
2309             energy_reserve = std::min(energy_reserve, finish/(2.5f*max_time_effect/m_kart->getEnergy()) - 0.5f);
2310         }
2311         if( (1.8f*max_time_effect) >= finish || m_kart->getEnergy() >= 16)
2312             big_reserve = true;
2313     }
2314 
2315     // Don't use nitro if there is already a nitro boost active
2316     // Nitro effect and fadeout may vary between karts type
2317     // So vary time according to kart properties
2318     if (   ((nitro_skill == 4) && time_until_fadeout >= (nitro_max_fadeout*0.50f) && !big_reserve)
2319         || ((nitro_skill == 4) && time_until_fadeout >= (nitro_max_fadeout))
2320         || ((nitro_skill == 3) && time_until_fadeout >= (nitro_max_fadeout + nitro_max_active*0.10f))
2321         || ((nitro_skill == 2) && time_until_fadeout >= (nitro_max_fadeout + nitro_max_active*0.35f)))
2322     {
2323        nitro_skill = 0;
2324     }
2325 
2326     // If trying to pass a bomb to a kart faster or far ahead, use nitro reserve
2327     if(m_kart->getAttachment()->getType() == Attachment::ATTACH_BOMB
2328        && nitro_skill >= 2 && energy_reserve > 0.0f)
2329     {
2330         if (m_distance_ahead>10.0f || m_kart_ahead->getSpeed() > m_kart->getSpeed() )
2331         {
2332             energy_reserve = 0;
2333         }
2334     }
2335 
2336     // TODO : if a kart behind and reasonably close goes faster
2337     //        and it has a swatter, use nitro to try and dodge the swatter.
2338 
2339     // Don't use nitro if building an energy reserve
2340     if (m_kart->getEnergy() <= energy_reserve)
2341     {
2342         nitro_skill = 0;
2343     }
2344 
2345     // If basic AI, or if the kart is very slow (e.g. after rescue) but not too much (accident)
2346     // or if kart is near the base max speed, use nitro.
2347     // This last condition is to profit from the max speed increase
2348     // And because it means there should be no slowing down from, e.g. plungers
2349     // If the kart has a huge reserve and may not have enough time until the end to ue it,
2350     // relax the speed condition.
2351     if (    nitro_skill == 1
2352         || (nitro_skill > 1 && (   (m_kart->getSpeed()<5 && m_kart->getSpeed()>2)
2353                                 || (m_kart->getSpeed()> 0.96f*m_kart->getCurrentMaxSpeed())
2354                                 || (m_kart->getSpeed()> 0.3f *m_kart->getCurrentMaxSpeed() && big_reserve))) )
2355     {
2356         m_controls->setNitro(true);
2357         m_burster = !m_burster;
2358     }
2359 
2360     //TODO : for now we don't disable nitro use when close to bananas and gums,
2361     //       because it hurts more often than not (when the bad item is avoided)
2362     if(m_avoid_item_close)
2363         return;
2364 
2365     // Use zipper
2366     if(m_kart->getPowerup()->getType() == PowerupManager::POWERUP_ZIPPER
2367         && item_skill >= 2 && m_kart->getSpeed()>1.0f &&
2368         m_kart->getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0)
2369     {
2370         DriveNode::DirectionType dir;
2371         unsigned int last;
2372         const DriveNode* dn = DriveGraph::get()->getNode(m_track_node);
2373         dn->getDirectionData(m_successor_index[m_track_node], &dir, &last);
2374         if(dir==DriveNode::DIR_STRAIGHT)
2375         {
2376             float diff = DriveGraph::get()->getDistanceFromStart(last)
2377                        - DriveGraph::get()->getDistanceFromStart(m_track_node);
2378             if(diff<0) diff += Track::getCurrentTrack()->getTrackLength();
2379             if(diff>m_ai_properties->m_straight_length_for_zipper)
2380                 m_controls->setFire(true);
2381         }
2382 
2383     }
2384 }   // handleNitroAndZipper
2385 
2386 //-----------------------------------------------------------------------------
2387 /** Returns the AI skill value used by the kart
2388  */
computeSkill(SkillType type)2389 int SkiddingAI::computeSkill(SkillType type)
2390 {
2391     if (type == ITEM_SKILL)
2392     {
2393         int item_skill = 0;
2394 
2395         if (m_ai_properties->m_item_usage_skill > 0)
2396         {
2397             if (m_ai_properties->m_item_usage_skill > 5)
2398             {
2399                 item_skill = 5;
2400             }
2401             else
2402             {
2403                 item_skill = m_ai_properties->m_item_usage_skill;
2404             }
2405         }
2406 
2407 
2408         if (m_kart->getBoostAI() == true)
2409         {
2410             if (item_skill < 5)
2411             {
2412                 item_skill = item_skill + 1; //possible improvement : make the boost amplitude pulled from config
2413             }
2414         }
2415         return item_skill;
2416     }
2417 
2418     else if (type == NITRO_SKILL)
2419     {
2420         int nitro_skill = 0;
2421 
2422         if (m_ai_properties->m_nitro_usage > 0)
2423         {
2424             if (m_ai_properties->m_nitro_usage > 4)
2425             {
2426                 nitro_skill = 4;
2427             }
2428             else
2429             {
2430                 nitro_skill = m_ai_properties->m_nitro_usage;
2431             }
2432         }
2433 
2434         if (m_kart->getBoostAI() == true)
2435         {
2436             if (nitro_skill < 4)
2437             {
2438                 nitro_skill = nitro_skill + 1; //possible improvement : make the boost amplitude pulled from config
2439             }
2440         }
2441         return nitro_skill;
2442     }
2443 
2444     return 0;
2445 } //computeSkill
2446 
2447 //-----------------------------------------------------------------------------
checkCrashes(const Vec3 & pos)2448 void SkiddingAI::checkCrashes(const Vec3& pos )
2449 {
2450     int steps = int( m_kart->getVelocityLC().getZ() / m_kart_length );
2451     if( steps < 2 ) steps = 2;
2452 
2453     // The AI drives significantly better with more steps, so for now
2454     // add 5 additional steps.
2455     steps+=5;
2456 
2457     //Right now there are 2 kind of 'crashes': with other karts and another
2458     //with the track. The sight line is used to find if the karts crash with
2459     //each other, but the first step is twice as big as other steps to avoid
2460     //having karts too close in any direction. The crash with the track can
2461     //tell when a kart is going to get out of the track so it steers.
2462     m_crashes.clear();
2463 
2464     // If slipstream should be handled actively, trigger overtaking the
2465     // kart which gives us slipstream if slipstream is ready
2466     const SlipStream *slip=m_kart->getSlipstream();
2467     // Atm network ai always use slipstream because it's a player controller
2468     // underlying
2469     bool use_slipstream =
2470         m_enabled_network_ai || m_ai_properties->m_make_use_of_slipstream;
2471     if(use_slipstream &&
2472         slip->isSlipstreamReady() &&
2473         slip->getSlipstreamTarget())
2474     {
2475         //Log::debug(getControllerName().c_str(), "%s overtaking %s",
2476         //           m_kart->getIdent().c_str(),
2477         //           m_kart->getSlipstreamKart()->getIdent().c_str());
2478         // FIXME: we might define a minimum distance, and if the target kart
2479         // is too close break first - otherwise the AI hits the kart when
2480         // trying to overtake it, actually speeding the other kart up.
2481         m_crashes.m_kart = slip->getSlipstreamTarget()->getWorldKartId();
2482     }
2483 
2484     const size_t NUM_KARTS = m_world->getNumKarts();
2485 
2486     float speed = m_kart->getVelocity().length();
2487     // If the velocity is zero, no sense in checking for crashes in time
2488     if(speed==0) return;
2489 
2490     Vec3 vel_normal = m_kart->getVelocity().normalized();
2491 
2492     // Time it takes to drive for m_kart_length units.
2493     float dt = m_kart_length / speed;
2494 
2495     int current_node = m_track_node;
2496     if(steps<1 || steps>1000)
2497     {
2498         Log::warn(getControllerName().c_str(),
2499                   "Incorrect STEPS=%d. kart_length %f velocity %f",
2500                   steps, m_kart_length, m_kart->getVelocityLC().getZ());
2501         steps=1000;
2502     }
2503     for(int i = 1; steps > i; ++i)
2504     {
2505         Vec3 step_coord = pos + vel_normal* m_kart_length * float(i);
2506 
2507         /* Find if we crash with any kart, as long as we haven't found one
2508          * yet
2509          */
2510         if( m_crashes.m_kart == -1 )
2511         {
2512             for( unsigned int j = 0; j < NUM_KARTS; ++j )
2513             {
2514                 const AbstractKart* kart = m_world->getKart(j);
2515                 // Ignore eliminated karts
2516                 if(kart==m_kart||kart->isEliminated()||kart->isGhostKart()) continue;
2517                 const AbstractKart *other_kart = m_world->getKart(j);
2518                 // Ignore karts ahead that are faster than this kart.
2519                 if(m_kart->getVelocityLC().getZ() < other_kart->getVelocityLC().getZ())
2520                     continue;
2521                 Vec3 other_kart_xyz = other_kart->getXYZ()
2522                                     + other_kart->getVelocity()*(i*dt);
2523                 float kart_distance = (step_coord - other_kart_xyz).length();
2524 
2525                 if( kart_distance < m_kart_length)
2526                     m_crashes.m_kart = j;
2527             }
2528         }
2529 
2530         /*Find if we crash with the drivelines*/
2531         if(current_node!=Graph::UNKNOWN_SECTOR &&
2532             m_next_node_index[current_node]!=-1)
2533             DriveGraph::get()->findRoadSector(step_coord, &current_node,
2534                         /* sectors to test*/ &m_all_look_aheads[current_node]);
2535 
2536         if( current_node == Graph::UNKNOWN_SECTOR)
2537         {
2538             m_crashes.m_road = true;
2539             return;
2540         }
2541     }
2542 }   // checkCrashes
2543 
2544 //-----------------------------------------------------------------------------
2545 /** This is a new version of findNonCrashingPoint, which at this stage is
2546  *  slightly inferior (though faster and more correct) than the original
2547  *  version - the original code cuts corner more aggressively than this
2548  *  version (and in most cases cuting the corner does not end in a
2549  *  collision, so it's actually faster).
2550  *  This version find the point furthest ahead which can be reached by
2551  *  travelling in a straight direction from the current location of the
2552  *  kart. This is done by using two lines: one from the kart to the
2553  *  lower left side of the next quad, and one from the kart to the
2554  *  lower right side of the next quad. The area between those two lines
2555  *  can be reached by the kart in a straight line, and will not go off
2556  *  track (assuming that the kart is on track). Then the next quads are
2557  *  tested: New left/right lines are computed. If the new left line is to
2558  *  the right of the old left line, the new left line becomes the current
2559  *  left line:
2560  *
2561  *            X      The new left line connecting kart to X will be to the right
2562  *        \        / of the old left line, so the available area for the kart
2563  *         \      /  will be dictated by the new left line.
2564  *          \    /
2565  *           kart
2566  *
2567  *  Similarly for the right side. This will narrow down the available area
2568  *  the kart can aim at, till finally the left and right line overlap.
2569  *  All points between the connection of the two end points of the left and
2570  *  right line can be reached without getting off track. Which point the
2571  *  kart aims at then depends on the direction of the track: if there is
2572  *  a left turn, the kart will aim to the left point (and vice versa for
2573  *  right turn) - slightly offset by the width of the kart to avoid that
2574  *  the kart is getting off track.
2575  *  \param aim_position The point to aim for, i.e. the point that can be
2576  *         driven to in a straight line.
2577  *  \param last_node The graph node index in which the aim_position is.
2578 */
findNonCrashingPointNew(Vec3 * result,int * last_node)2579 void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node)
2580 {
2581     *last_node = m_next_node_index[m_track_node];
2582     const core::vector2df xz = m_kart->getXYZ().toIrrVector2d();
2583 
2584     const DriveNode* dn = DriveGraph::get()->getNode(*last_node);
2585 
2586     // Index of the left and right end of a quad.
2587     const unsigned int LEFT_END_POINT  = 0;
2588     const unsigned int RIGHT_END_POINT = 1;
2589     core::line2df left (xz, (*dn)[LEFT_END_POINT ].toIrrVector2d());
2590     core::line2df right(xz, (*dn)[RIGHT_END_POINT].toIrrVector2d());
2591 
2592 #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
2593     const Vec3 eps1(0,0.5f,0);
2594     m_curve[CURVE_LEFT]->clear();
2595     m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1);
2596     m_curve[CURVE_LEFT]->addPoint((*dn)[LEFT_END_POINT]+eps1);
2597     m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1);
2598     m_curve[CURVE_RIGHT]->clear();
2599     m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1);
2600     m_curve[CURVE_RIGHT]->addPoint((*dn)[RIGHT_END_POINT]+eps1);
2601     m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1);
2602 #endif
2603 #if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
2604     const Vec3 eps(0,0.5f,0);
2605     m_curve[CURVE_KART]->clear();
2606     m_curve[CURVE_KART]->addPoint(m_kart->getXYZ()+eps);
2607     Vec3 forw(0, 0, 50);
2608     m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
2609 #endif
2610     while(1)
2611     {
2612         unsigned int next_sector = m_next_node_index[*last_node];
2613         const DriveNode* dn_next = DriveGraph::get()->getNode(next_sector);
2614         // Test if the next left point is to the right of the left
2615         // line. If so, a new left line is defined.
2616         if(left.getPointOrientation((*dn_next)[LEFT_END_POINT].toIrrVector2d())
2617             < 0 )
2618         {
2619             core::vector2df p = (*dn_next)[LEFT_END_POINT].toIrrVector2d();
2620             // Stop if the new point is to the right of the right line
2621             if(right.getPointOrientation(p)<0)
2622                 break;
2623             left.end = p;
2624 #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
2625             Vec3 ppp(p.X, m_kart->getXYZ().getY(), p.Y);
2626             m_curve[CURVE_LEFT]->addPoint(ppp+eps);
2627             m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps);
2628 #endif
2629         }
2630         else
2631             break;
2632 
2633         // Test if new right point is to the left of the right line. If
2634         // so, a new right line is defined.
2635         if(right.getPointOrientation((*dn_next)[RIGHT_END_POINT].toIrrVector2d())
2636             > 0 )
2637         {
2638             core::vector2df p = (*dn_next)[RIGHT_END_POINT].toIrrVector2d();
2639             // Break if new point is to the left of left line
2640             if(left.getPointOrientation(p)>0)
2641                 break;
2642 #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING)
2643 
2644             Vec3 ppp(p.X, m_kart->getXYZ().getY(), p.Y);
2645             m_curve[CURVE_RIGHT]->addPoint(ppp+eps);
2646             m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps);
2647 #endif
2648             right.end = p;
2649         }
2650         else
2651             break;
2652         *last_node = next_sector;
2653     }   // while
2654 
2655     //Vec3 ppp(0.5f*(left.end.X+right.end.X),
2656     //         m_kart->getXYZ().getY(),
2657     //         0.5f*(left.end.Y+right.end.Y));
2658     //*result = ppp;
2659 
2660     *result = DriveGraph::get()->getNode(*last_node)->getCenter();
2661 }   // findNonCrashingPointNew
2662 
2663 //-----------------------------------------------------------------------------
2664 /** This is basically the original AI algorithm. It is clearly buggy:
2665  *  1. the test:
2666  *
2667  *         distance + m_kart_width * 0.5f
2668  *                  > DriveGraph::get()->getNode(*last_node)->getPathWidth() )
2669  *
2670  *     is incorrect, it should compare with getPathWith*0.5f (since distance
2671  *     is the distance from the center, i.e. it is half the path width if
2672  *     the point is at the edge).
2673  *  2. the test:
2674  *
2675  *         DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
2676  *                                          *last_node );
2677  *     in the for loop tests always against distance from the same
2678  *     graph node (*last_node), while de-fact the loop will test points
2679  *     on various graph nodes.
2680  *
2681  *  This results in this algorithm often picking points to aim at that
2682  *  would actually force the kart off track. But in reality the kart has
2683  *  to turn (and does not immediate in one frame change its direction)
2684  *  which takes some time - so it is actually mostly on track.
2685  *  Since this algoritm (so far) ends up with by far the best AI behaviour,
2686  *  it is for now the default).
2687  *  \param aim_position On exit contains the point the AI should aim at.
2688  *  \param last_node On exit contais the graph node the AI is aiming at.
2689 */
findNonCrashingPoint(Vec3 * aim_position,int * last_node)2690  void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node)
2691 {
2692 #ifdef AI_DEBUG_KART_HEADING
2693     const Vec3 eps(0,0.5f,0);
2694     m_curve[CURVE_KART]->clear();
2695     m_curve[CURVE_KART]->addPoint(m_kart->getXYZ()+eps);
2696     Vec3 forw(0, 0, 50);
2697     m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
2698 #endif
2699     *last_node = m_next_node_index[m_track_node];
2700     float angle = DriveGraph::get()->getAngleToNext(m_track_node,
2701                                               m_successor_index[m_track_node]);
2702 
2703     Vec3 direction;
2704     Vec3 step_track_coord;
2705 
2706     // The original while(1) loop is replaced with a for loop to avoid
2707     // infinite loops (which we had once or twice). Usually the number
2708     // of iterations in the while loop is less than 7.
2709     for(unsigned int j=0; j<100; j++)
2710     {
2711         // target_sector is the sector at the longest distance that we can
2712         // drive to without crashing with the track.
2713         int target_sector = m_next_node_index[*last_node];
2714         float angle1 = DriveGraph::get()->getAngleToNext(target_sector,
2715                                                 m_successor_index[target_sector]);
2716         // In very sharp turns this algorithm tends to aim at off track points,
2717         // resulting in hitting a corner. So test for this special case and
2718         // prevent a too-far look-ahead in this case
2719         float diff = normalizeAngle(angle1-angle);
2720         if(fabsf(diff)>1.5f)
2721         {
2722             *aim_position = DriveGraph::get()->getNode(target_sector)
2723                                             ->getCenter();
2724             return;
2725         }
2726 
2727         //direction is a vector from our kart to the sectors we are testing
2728         direction = DriveGraph::get()->getNode(target_sector)->getCenter()
2729                   - m_kart->getXYZ();
2730 
2731         float len=direction.length();
2732         unsigned int steps = (unsigned int)( len / m_kart_length );
2733         if( steps < 3 ) steps = 3;
2734 
2735         // That shouldn't happen, but since we had one instance of
2736         // STK hanging, add an upper limit here (usually it's at most
2737         // 20 steps)
2738         if( steps>1000) steps = 1000;
2739 
2740         // Protection against having vel_normal with nan values
2741         if(len>0.0f) {
2742             direction*= 1.0f/len;
2743         }
2744 
2745         Vec3 step_coord;
2746         //Test if we crash if we drive towards the target sector
2747         for(unsigned int i = 2; i < steps; ++i )
2748         {
2749             step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i);
2750 
2751             DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord,
2752                                              *last_node );
2753 
2754             float distance = fabsf(step_track_coord[0]);
2755 
2756             //If we are outside, the previous node is what we are looking for
2757             if ( distance + m_kart_width * 0.5f
2758                  > DriveGraph::get()->getNode(*last_node)->getPathWidth() )
2759             {
2760                 *aim_position = DriveGraph::get()->getNode(*last_node)
2761                                                 ->getCenter();
2762                 return;
2763             }
2764         }
2765         angle = angle1;
2766         *last_node = target_sector;
2767     }   // for i<100
2768     *aim_position = DriveGraph::get()->getNode(*last_node)->getCenter();
2769 }   // findNonCrashingPoint
2770 
2771 //-----------------------------------------------------------------------------
2772 /** Determines the direction of the track ahead of the kart: 0 indicates
2773  *  straight, +1 right turn, -1 left turn.
2774  */
determineTrackDirection()2775 void SkiddingAI::determineTrackDirection()
2776 {
2777     const DriveGraph *dg = DriveGraph::get();
2778     unsigned int succ    = m_successor_index[m_track_node];
2779     unsigned int next    = dg->getNode(m_track_node)->getSuccessor(succ);
2780     float angle_to_track = 0.0f;
2781     if (m_kart->getVelocity().length() > 0.0f)
2782     {
2783         Vec3 track_direction = -dg->getNode(m_track_node)->getCenter()
2784             + dg->getNode(next)->getCenter();
2785         angle_to_track =
2786             track_direction.angle(m_kart->getVelocity().normalized());
2787     }
2788     angle_to_track = normalizeAngle(angle_to_track);
2789 
2790     // In certain circumstances (esp. S curves) it is possible that the
2791     // kart is not facing in the direction of the track. In this case
2792     // determining the curve radius based on the direction the kart is
2793     // facing results in very incorrect results (example: if the kart is
2794     // in a tight curve, but already facing towards the last point of the
2795     // curve - in this case a huge curve radius will be computes (since
2796     // the kart is nearly going straight), while in fact the kart would
2797     // go across the circle and not along, bumping into the track).
2798     // To avoid this we set the direction to undefined in this case,
2799     // which causes the kart to brake (which will allow the kart to
2800     // quicker be aligned with the track again).
2801     if(fabsf(angle_to_track) > 0.22222f * M_PI)
2802     {
2803         m_current_track_direction = DriveNode::DIR_UNDEFINED;
2804         return;
2805     }
2806 
2807     dg->getNode(next)->getDirectionData(m_successor_index[next],
2808                                         &m_current_track_direction,
2809                                         &m_last_direction_node);
2810 
2811 #ifdef AI_DEBUG
2812     m_curve[CURVE_QG]->clear();
2813     for(unsigned int i=m_track_node; i<=m_last_direction_node; i++)
2814     {
2815         m_curve[CURVE_QG]->addPoint(dg->getNode(i)->getCenter());
2816     }
2817 #endif
2818 
2819     if(m_current_track_direction==DriveNode::DIR_LEFT  ||
2820        m_current_track_direction==DriveNode::DIR_RIGHT   )
2821     {
2822         handleCurve();
2823     }   // if(m_current_track_direction == DIR_LEFT || DIR_RIGHT   )
2824 
2825 
2826     return;
2827 }   // determineTrackDirection
2828 
2829 // ----------------------------------------------------------------------------
2830 /** If the kart is at/in a curve, determine the turn radius.
2831  */
handleCurve()2832 void SkiddingAI::handleCurve()
2833 {
2834     // Ideally we would like to have a circle that:
2835     // 1) goes through the kart position
2836     // 2) has the current heading of the kart as tangent in that point
2837     // 3) goes through the last point
2838     // 4) has a tangent at the last point that faces towards the next node
2839     // Unfortunately conditions 1 to 3 already fully determine the circle,
2840     // i.e. it is not always possible to find an appropriate circle.
2841     // Using the first three conditions is mostly a good choice (since the
2842     // kart will already point towards the direction of the circle), and
2843     // the case that the kart is facing wrong was already tested for before
2844 
2845     const DriveGraph *dg = DriveGraph::get();
2846     const Vec3& last_xyz = dg->getNode(m_last_direction_node)->getCenter();
2847 
2848     determineTurnRadius(last_xyz, &m_curve_center, &m_current_curve_radius);
2849     assert(!std::isnan(m_curve_center.getX()));
2850     assert(!std::isnan(m_curve_center.getY()));
2851     assert(!std::isnan(m_curve_center.getZ()));
2852 
2853 #undef ADJUST_TURN_RADIUS_TO_AVOID_CRASH_INTO_TRACK
2854 #ifdef ADJUST_TURN_RADIUS_TO_AVOID_CRASH_INTO_TRACK
2855     // NOTE: this can deadlock if the AI is going on a shortcut, since
2856     // m_last_direction_node is based on going on the main driveline :(
2857     unsigned int i= m_track_node;
2858     while(1)
2859     {
2860         i = m_next_node_index[i];
2861         // Pick either the lower left or right point:
2862         int index = m_current_track_direction==DriveNode::DIR_LEFT
2863                   ? 0 : 1;
2864         Vec3 curve_center_wc = m_kart->getTrans()(m_curve_center);
2865         float r = (curve_center_wc - *(dg->getNode(i))[index]).length();
2866         if(m_current_curve_radius < r)
2867         {
2868             last_xyz = *(dg->getNode(i)[index]);
2869             determineTurnRadius(last_xyz,
2870                                 &m_curve_center, &m_current_curve_radius);
2871             m_last_direction_node = i;
2872             break;
2873         }
2874         if(i==m_last_direction_node)
2875             break;
2876     }
2877 #endif
2878 #if defined(AI_DEBUG) && defined(AI_DEBUG_CIRCLES)
2879     m_curve[CURVE_PREDICT1]->makeCircle(m_kart->getTrans()(m_curve_center),
2880                                         m_current_curve_radius);
2881     m_curve[CURVE_PREDICT1]->addPoint(last_xyz);
2882     m_curve[CURVE_PREDICT1]->addPoint(m_kart->getTrans()(m_curve_center));
2883     m_curve[CURVE_PREDICT1]->addPoint(m_kart->getXYZ());
2884 #endif
2885 
2886 }   // handleCurve
2887 // ----------------------------------------------------------------------------
2888 /** Determines if the kart should skid. The base implementation enables
2889  *  skidding
2890  *  \param steer_fraction The steering fraction as computed by the
2891  *          AIBaseLapController.
2892  *  \return True if the kart should skid.
2893  */
canSkid(float steer_fraction)2894 bool SkiddingAI::canSkid(float steer_fraction)
2895 {
2896     if(fabsf(steer_fraction)>1.5f)
2897     {
2898         // If the kart has to do a sharp turn, but is already skidding, find
2899         // a good time to release the skid button, since this will turn the
2900         // kart more sharply:
2901         if(m_controls->getSkidControl())
2902         {
2903 #ifdef DEBUG
2904             if(m_ai_debug)
2905             {
2906                 if(fabsf(steer_fraction)>=2.5f)
2907                     Log::debug(getControllerName().c_str(),
2908                                "%s stops skidding (%f).",
2909                                m_kart->getIdent().c_str(), steer_fraction);
2910             }
2911 #endif
2912             // If the current turn is not sharp enough, delay releasing
2913             // the skid button.
2914             return fabsf(steer_fraction)<2.5f;
2915         }
2916 
2917         // If the kart is not skidding, now is not a good time to start
2918         return false;
2919     }
2920 
2921     // No skidding on straights
2922     if(m_current_track_direction==DriveNode::DIR_STRAIGHT ||
2923        m_current_track_direction==DriveNode::DIR_UNDEFINED  )
2924     {
2925 #ifdef DEBUG
2926         if(m_controls->getSkidControl() && m_ai_debug)
2927         {
2928             Log::debug(getControllerName().c_str(),
2929                        "%s stops skidding on straight.",
2930                        m_kart->getIdent().c_str());
2931         }
2932 #endif
2933         return false;
2934     }
2935 
2936     const float MIN_SKID_SPEED = 5.0f;
2937     const DriveGraph *dg = DriveGraph::get();
2938     Vec3 last_xyz        = m_kart->getTrans().inverse()
2939                            (dg->getNode(m_last_direction_node)->getCenter());
2940 
2941     // Only try skidding when a certain minimum speed is reached.
2942     if(m_kart->getSpeed()<MIN_SKID_SPEED) return false;
2943 
2944     // Estimate how long it takes to finish the curve
2945     Vec3 diff_kart = -m_curve_center;
2946     Vec3 diff_last = last_xyz - m_curve_center;
2947     float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ());
2948     float angle_last = atan2(diff_last.getX(), diff_last.getZ());
2949     float angle = m_current_track_direction == DriveNode::DIR_RIGHT
2950                 ? angle_last - angle_kart
2951                 : angle_kart - angle_last;
2952     angle = normalizeAngle(angle);
2953     float length = m_current_curve_radius*fabsf(angle);
2954     float duration = length / m_kart->getSpeed();
2955     // The estimated skdding time is usually too short - partly because
2956     // he speed of the kart decreases during the turn, partly because
2957     // the actual path is adjusted during the turn. So apply an
2958     // experimentally found factor in to get better estimates.
2959     duration *= 1.5f;
2960 
2961     // If the remaining estimated time for skidding is too short, stop
2962     // it. This code will mostly trigger the bonus at the end of a skid.
2963     if(m_controls->getSkidControl() && duration < 1.0f)
2964     {
2965         if(m_ai_debug)
2966             Log::debug(getControllerName().c_str(),
2967                        "'%s' too short, stop skid.",
2968                        m_kart->getIdent().c_str());
2969         return false;
2970     }
2971     // Test if the AI is trying to skid against track direction. This
2972     // can happen if the AI is adjusting steering somewhat (e.g. in a
2973     // left turn steer right to avoid getting too close to the left
2974     // vorder). In this case skidding will be useless.
2975     else if( (steer_fraction > 0 &&
2976               m_current_track_direction==DriveNode::DIR_LEFT) ||
2977              (steer_fraction < 0 &&
2978               m_current_track_direction==DriveNode::DIR_RIGHT)  )
2979         {
2980 #ifdef DEBUG
2981             if(m_controls->getSkidControl() && m_ai_debug)
2982                 Log::debug(getControllerName().c_str(),
2983                            "%s skidding against track direction.",
2984                            m_kart->getIdent().c_str());
2985 #endif
2986             return false;
2987         }
2988     // If there is a skidding bonus, try to get it.
2989     else if (m_kart->getKartProperties()->getSkidBonusSpeed().size() > 0 &&
2990              m_kart->getKartProperties()->getSkidTimeTillBonus()[0] < duration)
2991     {
2992 #ifdef DEBUG
2993         if(!m_controls->getSkidControl() && m_ai_debug)
2994             Log::debug(getControllerName().c_str(),
2995                        "%s start skid, duration %f.",
2996                        m_kart->getIdent().c_str(), duration);
2997 #endif
2998         return true;
2999 
3000     }  // if curve long enough for skidding
3001 
3002 #ifdef DEBUG
3003         if(m_controls->getSkidControl() && m_ai_debug)
3004             Log::debug(getControllerName().c_str(),
3005                        "%s has no reasons to skid anymore.",
3006                        m_kart->getIdent().c_str());
3007 #endif
3008     return false;
3009 }   // canSkid
3010 
3011 //-----------------------------------------------------------------------------
3012 /** Converts the steering angle to a lr steering in the range of -1 to 1.
3013  *  If the steering angle is too great, it will also trigger skidding. This
3014  *  function uses a 'time till full steer' value specifying the time it takes
3015  *  for the wheel to reach full left/right steering similar to player karts
3016  *  when using a digital input device. The parameter is defined in the kart
3017  *  properties and helps somewhat to make AI karts more 'pushable' (since
3018  *  otherwise the karts counter-steer to fast).
3019  *  It also takes the effect of a plunger into account by restricting the
3020  *  actual steer angle to 50% of the maximum.
3021  *  \param angle Steering angle.
3022  *  \param dt Time step.
3023  */
setSteering(float angle,float dt)3024 void SkiddingAI::setSteering(float angle, float dt)
3025 {
3026     float steer_fraction = angle / m_kart->getMaxSteerAngle();
3027 
3028     // Use a simple finite state machine to make sure to randomly decide
3029     // whether to skid or not only once per skid section. See docs for
3030     // m_skid_probability_state for more details.
3031     if(!canSkid(steer_fraction))
3032     {
3033         m_skid_probability_state = SKID_PROBAB_NOT_YET;
3034         m_controls->setSkidControl(KartControl::SC_NONE);
3035     }
3036     else
3037     {
3038         KartControl::SkidControl sc = steer_fraction > 0
3039                                     ? KartControl::SC_RIGHT
3040                                     : KartControl::SC_LEFT;
3041         if(m_skid_probability_state==SKID_PROBAB_NOT_YET)
3042         {
3043             int prob = (int)(100.0f*m_ai_properties
3044                                ->getSkiddingProbability(m_distance_to_player));
3045             int r = m_random_skid.get(100);
3046             m_skid_probability_state = (r<prob)
3047                                      ? SKID_PROBAB_SKID
3048                                      : SKID_PROBAB_NO_SKID;
3049 #undef PRINT_SKID_STATS
3050 #ifdef PRINT_SKID_STATS
3051             Log::info(getControllerName().c_str(),
3052                       "%s distance %f prob %d skidding %s",
3053                       m_kart->getIdent().c_str(), distance, prob,
3054                       sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right");
3055 #endif
3056         }
3057         m_controls->setSkidControl(m_skid_probability_state == SKID_PROBAB_SKID
3058                                    ? sc : KartControl::SC_NONE );
3059     }
3060 
3061     // Adjust steer fraction in case to be in [-1,1]
3062     if     (steer_fraction >  1.0f) steer_fraction =  1.0f;
3063     else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
3064 
3065     // Restrict steering when a plunger is in the face
3066     // The degree of restriction depends on item_skill
3067 
3068     //FIXME : the AI speed estimate in curves don't account for this restriction
3069     if(m_kart->getBlockedByPlungerTicks()>0)
3070     {
3071         int item_skill = computeSkill(ITEM_SKILL);
3072         float steering_limit = 0.5f;
3073         if (item_skill == 0)
3074             steering_limit = 0.35f;
3075         else if (item_skill == 1)
3076             steering_limit = 0.45f;
3077         else if (item_skill == 2)
3078             steering_limit = 0.55f;
3079         else if (item_skill == 3)
3080             steering_limit = 0.65f;
3081         else if (item_skill == 4)
3082             steering_limit = 0.75f;
3083         else if (item_skill == 5)
3084             steering_limit = 0.9f;
3085 
3086         if     (steer_fraction >  steering_limit) steer_fraction =  steering_limit;
3087         else if(steer_fraction < -steering_limit) steer_fraction = -steering_limit;
3088     }
3089 
3090     const Skidding *skidding = m_kart->getSkidding();
3091 
3092     // If we are supposed to skid, but the current steering is still
3093     // in the wrong direction, don't start to skid just now, since then
3094     // we can't turn into the direction we want to anymore (see
3095     // Skidding class)
3096     Skidding::SkidState ss = skidding->getSkidState();
3097     if((ss==Skidding::SKID_ACCUMULATE_LEFT  && steer_fraction>0.2f ) ||
3098        (ss==Skidding::SKID_ACCUMULATE_RIGHT && steer_fraction<-0.2f)    )
3099     {
3100         m_controls->setSkidControl(KartControl::SC_NONE);
3101 #ifdef DEBUG
3102         if(m_ai_debug)
3103             Log::info(getControllerName().c_str(),
3104                       "'%s' wrong steering, stop skid.",
3105                       m_kart->getIdent().c_str());
3106 #endif
3107     }
3108 
3109     if(m_controls->getSkidControl()!=KartControl::SC_NONE &&
3110             ( ss==Skidding::SKID_ACCUMULATE_LEFT ||
3111               ss==Skidding::SKID_ACCUMULATE_RIGHT  )  )
3112     {
3113         steer_fraction =
3114             skidding->getSteeringWhenSkidding(steer_fraction);
3115         if(fabsf(steer_fraction)>1.8)
3116         {
3117 #ifdef DEBUG
3118             if(m_ai_debug)
3119                 Log::info(getControllerName().c_str(),
3120                           "%s steering too much (%f).",
3121                           m_kart->getIdent().c_str(), steer_fraction);
3122 #endif
3123             m_controls->setSkidControl(KartControl::SC_NONE);
3124         }
3125         if(steer_fraction<-1.0f)
3126             steer_fraction = -1.0f;
3127         else if(steer_fraction>1.0f)
3128             steer_fraction = 1.0f;
3129     }
3130 
3131     float old_steer      = m_controls->getSteer();
3132 
3133     // The AI has its own 'time full steer' value (which is the time
3134     float max_steer_change = dt/m_ai_properties->m_time_full_steer;
3135     if(old_steer < steer_fraction)
3136     {
3137         m_controls->setSteer( (old_steer+max_steer_change > steer_fraction)
3138                               ? steer_fraction : old_steer+max_steer_change );
3139     }
3140     else
3141     {
3142         m_controls->setSteer( (old_steer-max_steer_change < steer_fraction)
3143                                ? steer_fraction : old_steer-max_steer_change );
3144     }
3145 
3146 
3147 }   // setSteering
3148