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, ¤t_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