/** This is the main entry point for the AI. * It is called once per frame for each AI and determines the behaviour of * the AI, e.g. steering, accelerating/braking, firing. */ void ArenaAI::update(float dt) { // This is used to enable firing an item backwards. m_controls->m_look_back = false; m_controls->m_nitro = false; // Don't do anything if there is currently a kart animations shown. if (m_kart->getKartAnimation()) return; if (isWaiting()) { AIBaseController::update(dt); return; } checkIfStuck(dt); if (handleArenaUnstuck(dt)) return; findClosestKart(true); findTarget(); handleArenaItems(dt); handleArenaBanana(); if (m_kart->getSpeed() > 15.0f && m_cur_kart_pos_data.angle < 0.2f) { // Only use nitro when target is straight m_controls->m_nitro = true; } if (m_is_uturn) { handleArenaUTurn(dt); } else { handleArenaAcceleration(dt); handleArenaSteering(dt); handleArenaBraking(); } AIBaseController::update(dt); } // update
/** Find a suitable target to follow, it will first call * \ref SoccerWorld::getBallChaser to check if this AI should go chasing the * ball and try to score, otherwise it will call \ref tryCollectItem if * needed. After that it will call \ref SoccerWorld::getAttacker to see if * this AI should attack the kart in opposite team which is chasing the ball, * if not go for the closest kart found by \ref findClosestKart. */ void SoccerAI::findTarget() { findClosestKart(true/*consider_difficulty*/, false/*find_sta*/); // Check if this AI kart is the one who will chase the ball if (m_world->getBallChaser(m_cur_team) == (signed)m_kart->getWorldKartId()) { m_target_point = determineBallAimingPosition(); m_target_node = m_world->getBallNode(); return; } // Always reset this flag, // in case the ball chaser lost the ball somehow m_overtake_ball = false; if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER) { tryCollectItem(&m_target_point , &m_target_node); } else if (m_world->getAttacker(m_cur_team) == (signed)m_kart ->getWorldKartId()) { // This AI will attack the other team ball chaser int id = m_world->getBallChaser(m_opp_team); const AbstractKart* kart = m_world->getKart(id); m_target_point = kart->getXYZ(); m_target_node = m_world->getSectorForKart(kart); } else { m_target_point = m_closest_kart_point; m_target_node = m_closest_kart_node; } } // findTarget
/** Determine how AI should use its item, different \ref m_cur_difficulty will * have a corresponding strategy. * \param dt Time step size. */ void ArenaAI::useItems(const float dt) { m_controls->setFire(false); if (m_kart->getKartAnimation() || m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING) return; // Find a closest kart again, this time we ignore difficulty findClosestKart(false/*consider_difficulty*/, false/*find_sta*/); if (!m_closest_kart) return; Vec3 closest_kart_point_lc = m_kart->getTrans().inverse()(m_closest_kart_point); m_time_since_last_shot += dt; float min_bubble_time = 2.0f; const bool difficulty = m_cur_difficulty == RaceManager::DIFFICULTY_EASY || m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM; const bool fire_behind = closest_kart_point_lc.z() < 0 && !difficulty; const float abs_angle = atan2f(fabsf(closest_kart_point_lc.x()), fabsf(closest_kart_point_lc.z())); const bool perfect_aim = abs_angle < 0.2f; // Compensate the distance because this distance is straight to straight // in graph node, so if kart to kart are not facing like so as, their real // distance maybe smaller const float dist_to_kart = getKartDistance(m_closest_kart) * 0.8f; switch(m_kart->getPowerup()->getType()) { case PowerupManager::POWERUP_BUBBLEGUM: { Attachment::AttachmentType type = m_kart->getAttachment()->getType(); // Don't use shield when we have a swatter. if (type == Attachment::ATTACH_SWATTER) break; // Check if a flyable (cake, ...) is close or a kart nearby // has a swatter attachment. If so, use bubblegum // as shield if ( (!m_kart->isShielded() && projectile_manager->projectileIsClose(m_kart, m_ai_properties->m_shield_incoming_radius) ) || (dist_to_kart < 15.0f && (m_closest_kart->getAttachment()-> getType() == Attachment::ATTACH_SWATTER) ) ) { m_controls->setFire(true); m_controls->setLookBack(false); break; } // Avoid dropping all bubble gums one after another if (m_time_since_last_shot < 3.0f) break; // Use bubblegum if the kart around is close, // or can't find a close kart for too long time if (dist_to_kart < 15.0f || m_time_since_last_shot > 15.0f) { m_controls->setFire(true); m_controls->setLookBack(true); break; } break; // POWERUP_BUBBLEGUM } case PowerupManager::POWERUP_CAKE: { // if the kart has a shield, do not break it by using a cake. if (m_kart->getShieldTime() > min_bubble_time) break; // Leave some time between shots if (m_time_since_last_shot < 1.0f) break; if (dist_to_kart < 25.0f && !m_closest_kart->isInvulnerable()) { m_controls->setFire(true); m_controls->setLookBack(fire_behind); break; } break; } // POWERUP_CAKE case PowerupManager::POWERUP_BOWLING: { // if the kart has a shield, do not break it by using a bowling ball. if (m_kart->getShieldTime() > min_bubble_time) break; // Leave some time between shots if (m_time_since_last_shot < 1.0f) break; if (dist_to_kart < 6.0f && (difficulty || perfect_aim) && !m_closest_kart->isInvulnerable()) { m_controls->setFire(true); m_controls->setLookBack(fire_behind); break; } break; } // POWERUP_BOWLING case PowerupManager::POWERUP_SWATTER: { // Squared distance for which the swatter works float d2 = m_kart->getKartProperties()->getSwatterDistance(); // if the kart has a shield, do not break it by using a swatter. if (m_kart->getShieldTime() > min_bubble_time) break; if (!m_closest_kart->isSquashed() && dist_to_kart * dist_to_kart < d2 && m_closest_kart->getSpeed() < m_kart->getSpeed()) { m_controls->setFire(true); m_controls->setLookBack(false); break; } break; } // Below powerups won't appear in arena, so skip them case PowerupManager::POWERUP_ZIPPER: break; // POWERUP_ZIPPER case PowerupManager::POWERUP_PLUNGER: break; // POWERUP_PLUNGER case PowerupManager::POWERUP_SWITCH: // Don't handle switch m_controls->setFire(true); // (use it no matter what) for now break; // POWERUP_SWITCH case PowerupManager::POWERUP_PARACHUTE: break; // POWERUP_PARACHUTE case PowerupManager::POWERUP_ANVIL: break; // POWERUP_ANVIL case PowerupManager::POWERUP_RUBBERBALL: break; default: Log::error("ArenaAI", "Invalid or unhandled powerup '%d' in default AI.", m_kart->getPowerup()->getType()); assert(false); } if (m_controls->getFire()) m_time_since_last_shot = 0.0f; } // useItems
//----------------------------------------------------------------------------- void ArenaAI::handleArenaItems(const float dt) { m_controls->m_fire = false; if (m_kart->getKartAnimation() || m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING) return; // Find a closest kart again, this time we ignore difficulty findClosestKart(false); if (!m_closest_kart) return; m_time_since_last_shot += dt; float min_bubble_time = 2.0f; const bool difficulty = m_cur_difficulty == RaceManager::DIFFICULTY_EASY || m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM; const bool fire_behind = m_closest_kart_pos_data.behind && !difficulty; const bool perfect_aim = m_closest_kart_pos_data.angle < 0.2f; switch(m_kart->getPowerup()->getType()) { case PowerupManager::POWERUP_BUBBLEGUM: { Attachment::AttachmentType type = m_kart->getAttachment()->getType(); // Don't use shield when we have a swatter. if (type == Attachment::ATTACH_SWATTER || type == Attachment::ATTACH_NOLOKS_SWATTER) break; // Check if a flyable (cake, ...) is close or a kart nearby // has a swatter attachment. If so, use bubblegum // as shield if ((!m_kart->isShielded() && projectile_manager->projectileIsClose(m_kart, m_ai_properties->m_shield_incoming_radius)) || (m_closest_kart_pos_data.distance < 15.0f && ((m_closest_kart->getAttachment()-> getType() == Attachment::ATTACH_SWATTER) || (m_closest_kart->getAttachment()-> getType() == Attachment::ATTACH_NOLOKS_SWATTER)))) { m_controls->m_fire = true; m_controls->m_look_back = false; break; } // Avoid dropping all bubble gums one after another if (m_time_since_last_shot < 3.0f) break; // Use bubblegum if the next kart behind is 'close' but not too close, // or can't find a close kart for too long time if ((m_closest_kart_pos_data.distance < 15.0f && m_closest_kart_pos_data.distance > 3.0f) || m_time_since_last_shot > 15.0f) { m_controls->m_fire = true; m_controls->m_look_back = true; break; } break; // POWERUP_BUBBLEGUM } case PowerupManager::POWERUP_CAKE: { // if the kart has a shield, do not break it by using a cake. if (m_kart->getShieldTime() > min_bubble_time) break; // Leave some time between shots if (m_time_since_last_shot < 1.0f) break; if (m_closest_kart_pos_data.distance < 25.0f && !m_closest_kart->isInvulnerable()) { m_controls->m_fire = true; m_controls->m_look_back = fire_behind; break; } break; } // POWERUP_CAKE case PowerupManager::POWERUP_BOWLING: { // if the kart has a shield, do not break it by using a bowling ball. if (m_kart->getShieldTime() > min_bubble_time) break; // Leave some time between shots if (m_time_since_last_shot < 1.0f) break; if (m_closest_kart_pos_data.distance < 6.0f && (difficulty || perfect_aim)) { m_controls->m_fire = true; m_controls->m_look_back = fire_behind; break; } break; } // POWERUP_BOWLING case PowerupManager::POWERUP_SWATTER: { // Squared distance for which the swatter works float d2 = m_kart->getKartProperties()->getSwatterDistance(); // if the kart has a shield, do not break it by using a swatter. if (m_kart->getShieldTime() > min_bubble_time) break; if (!m_closest_kart->isSquashed() && m_closest_kart_pos_data.distance < d2 && m_closest_kart->getSpeed() < m_kart->getSpeed()) { m_controls->m_fire = true; m_controls->m_look_back = false; break; } break; } // Below powerups won't appear in arena, so skip them case PowerupManager::POWERUP_ZIPPER: break; // POWERUP_ZIPPER case PowerupManager::POWERUP_PLUNGER: break; // POWERUP_PLUNGER case PowerupManager::POWERUP_SWITCH: // Don't handle switch m_controls->m_fire = true; // (use it no matter what) for now break; // POWERUP_SWITCH case PowerupManager::POWERUP_PARACHUTE: break; // POWERUP_PARACHUTE case PowerupManager::POWERUP_ANVIL: break; // POWERUP_ANVIL case PowerupManager::POWERUP_RUBBERBALL: break; default: Log::error("ArenaAI", "Invalid or unhandled powerup '%d' in default AI.", m_kart->getPowerup()->getType()); assert(false); } if (m_controls->m_fire) m_time_since_last_shot = 0.0f; } // handleArenaItems