/* $Id: scusibot.cpp,v 1.9 2005/10/16 17:53:05 pohlt Exp $ */ #include "scusibot.hpp" #include "avatar.hpp" #include "stationarygun.hpp" #include "global.hpp" #include ScusiBot::ScusiBot() : m_event( Event::EMPTY ), m_count( 0 ), m_state( IDLE ) { m_ID = Bot::SCUSI_BOT; } void ScusiBot::update() { Vector position = m_avatar->getPos(); Vector positionDiff = position - m_lastPosition; std::vector visibleObjects = m_avatar->getVisibleObjects(); StationaryGun* gun = 0; // aim to the nearest adversary avatar visible Avatar* nearestAvatar = 0; Vector targetVector; real targetDistance = 0.0; m_visibleAvatars.clear(); for ( unsigned int i = 0; i < visibleObjects.size(); i++ ) { Object* object = visibleObjects[i]; if ( object->isAvatar() ) { Avatar* avatar = dynamic_cast( object ); m_visibleAvatars.push_back( avatar ); if ( avatar && avatar->getPlayer()->getPlayerOrTeamID() != m_avatar->getPlayer()->getPlayerOrTeamID() ) { Vector vector = avatar->getPos() - m_avatar->getPos(); real distance = targetVector.abs2(); if ( !nearestAvatar || distance < targetDistance ) { nearestAvatar = avatar; targetVector = vector; targetDistance = distance; } } } else if ( object->getID() == STATIC_GUN_I ) { gun = dynamic_cast( object ); } } if ( m_state == ATTACK_AVATAR ) { if ( std::find( visibleObjects.begin(), visibleObjects.end(), m_targetAvatar ) == visibleObjects.end() || !m_targetAvatar->isLiving() ) { m_state = IDLE; m_targetAvatar = 0; } else { targetVector = m_targetAvatar->getPos() - m_avatar->getPos(); targetVector += ( m_targetAvatar->getVel() - m_avatar->getVel() ) * ( targetVector.abs() / 27.0 ); // move towards target avatar moveTo( targetVector, 20.0, 50.0 ); // adjust aiming angle aimAt( m_targetAvatar ); shootAt( m_targetAvatar ); } } else if ( gun ) { m_event.removeAction( Event::SHOOT ); // If already in gun, blast 'em all! if ( m_avatar->getAttachedObjectIndex( gun ) != -1 ) { m_state = IN_GUN; // check if there is an adversary behind the gun which is aiming at us std::vector::const_iterator it; for ( it = m_visibleAvatars.begin(); it != m_visibleAvatars.end(); it++ ) { Avatar* avatar = *it; if ( avatar->getPlayer()->getPlayerOrTeamID() == m_avatar->getPlayer()->getPlayerOrTeamID() ) continue; if ( avatar->getPosX() + 10.0 < gun->getPosX() ) continue; Vector avatarVector = m_avatar->getPos() - avatar->getPos(); real avatarAngle = avatar->getAimingAngle(); real angle = ATAN2_REAL( FABS_REAL( avatarVector.x ), -avatarVector.y ) / M_PI * 180.0; if ( FABS_REAL( angle - avatarAngle ) < 10.0 ) { // INFO( "ScusiBot: attacking avatar\n" ); m_targetAvatar = avatar; m_state = ATTACK_AVATAR; m_event.addAction( Event::JUMP ); break; } } if ( m_state == IN_GUN ) { // adjust aiming if ( nearestAvatar ) { targetVector = nearestAvatar->getPos() - gun->getPos(); targetVector += nearestAvatar->getVel() * ( targetVector.abs() * 27.0 ); real enemyAngle = ATAN2_REAL( FABS_REAL( targetVector.x ), -targetVector.y ) / M_PI * 180.0; Vector aimingVector = gun->getAimingVector(); real aimingAngle = ATAN2_REAL( FABS_REAL( aimingVector.x ), -aimingVector.y ) / M_PI * 180.0; if ( enemyAngle < aimingAngle ) { // INFO( "ScusiBot: Aiming up\n" ); if ( aimingAngle - enemyAngle < 5.0 ) { m_event.removeAction( Event::DOWN | Event::UP ); } else { m_event.removeAction( Event::DOWN ); m_event.addAction( Event::UP ); } } else if ( enemyAngle > aimingAngle ) { // INFO( "ScusiBot: Aiming down\n" ); if ( enemyAngle - aimingAngle < 5.0 ) { m_event.removeAction( Event::DOWN | Event::UP ); } else { m_event.removeAction( Event::UP ); m_event.addAction( Event::DOWN ); } } } m_event.addAction( Event::SHOOT ); } } else { Vector preLandingPoint( gun->getPos() + Vector( 55.0, -40.0 )); Vector landingPoint( gun->getPos() + Vector( 12.0, -30.0 )); // attack enemy players which are in the gun Avatar* gunPilot = 0; if ( ( gunPilot = gun->getPilot() ) && gunPilot->getPlayer()->getPlayerOrTeamID() != m_avatar->getPlayer()->getPlayerOrTeamID() ) { m_state = ATTACK_AVATAR_IN_GUN; keepPosition( preLandingPoint ); aimAt( gunPilot ); shootAt( gunPilot ); } else { if ( m_state == ATTACK_AVATAR_IN_GUN ) { m_state = MOVE_TO_PRELANDING_POINT; } } if ( m_state == MOVE_TO_LANDING_POINT || m_state == ADJUST_GUN_SEAT || m_state == IN_GUN ) { // check if there is an adversary behind the gun which is aiming at us std::vector::const_iterator it; for ( it = m_visibleAvatars.begin(); it != m_visibleAvatars.end(); it++ ) { Avatar* avatar = *it; if ( avatar->getPlayer()->getPlayerOrTeamID() == m_avatar->getPlayer()->getPlayerOrTeamID() ) continue; if ( avatar->getPosX() + 10.0 < gun->getPosX() ) continue; Vector avatarVector = m_avatar->getPos() - avatar->getPos(); real avatarAngle = avatar->getAimingAngle(); real angle = ATAN2_REAL( FABS_REAL( avatarVector.x ), -avatarVector.y ) / M_PI * 180.0; if ( FABS_REAL( angle - avatarAngle ) < 10.0 ) { // INFO( "ScusiBot: attacking avatar\n" ); m_targetAvatar = avatar; m_state = ATTACK_AVATAR; m_event.addAction( Event::JUMP ); break; } } } if ( m_state == IDLE ) { m_state = MOVE_TO_PRELANDING_POINT; } if ( m_state == MOVE_TO_PRELANDING_POINT ) { // INFO( "ScusiBot: (%f,%f) Moving to pre landing point (%f,%f)\n", // m_avatar->getPosition().x, m_avatar->getPosition().y, preLandingPoint.x, preLandingPoint.y ); keepPosition( preLandingPoint ); if ( ( preLandingPoint - m_avatar->getPos() ).abs() < 5.0 ) { m_state = MOVE_TO_LANDING_POINT; } } if ( m_state == MOVE_TO_LANDING_POINT ) { // INFO( "ScusiBot: (%f,%f) At pre landing point, moving to landing point (%f,%f)\n", // m_avatar->getPosition().x, m_avatar->getPosition().y, landingPoint.x, landingPoint.y ); keepPosition( landingPoint ); if ( ( landingPoint - m_avatar->getPos() ).abs() < 5.0 ) { // INFO( "ScusiBot: (%f,%f) At landing point, shutting down\n", // m_avatar->getPosition().x, m_avatar->getPosition().y ); m_state = ADJUST_GUN_SEAT; } if ( landingPoint.x - m_avatar->getPosX() > 10.0 || landingPoint.y - m_avatar->getPosY() < -20.0 ) { m_state = MOVE_TO_PRELANDING_POINT; } } if ( m_state == ADJUST_GUN_SEAT ) { m_event.removeAction( Event::JUMP ); if ( m_avatar->getPosX() < landingPoint.x ) { m_event.removeAction( Event::LEFT ); m_event.addAction( Event::RIGHT ); } else if ( m_avatar->getPosX() > landingPoint.x ) { m_event.removeAction( Event::RIGHT ); m_event.addAction( Event::LEFT ); } } if ( m_state == IN_GUN ) { m_state = IDLE; } } } else if ( nearestAvatar ) { targetVector += ( nearestAvatar->getVel() - m_avatar->getVel() ) * ( targetVector.abs() / 27.0 ); moveTo( targetVector, 20.0, 50.0 ); aimAt( nearestAvatar ); shootAt( nearestAvatar ); } else { // always keep moving if ( positionDiff.abs() < 1.0 ) { if ( m_count > 5 ) { m_count = 0; // try digging if ( !m_event.testAnyActions( Event::DIG ) ) { // INFO( "ScusiBot: start digging\n" ); m_event.addAction( Event::DIG ); } // try jumping else if ( !m_event.testAnyActions( Event::JUMP ) ) { // INFO( "ScusiBot: start jumping\n" ); m_event.addAction( Event::JUMP ); } else { // turn around and stop digging if ( m_event.testAnyActions( Event::LEFT ) ) { // INFO( "ScusiBot: turn right\n" ); m_event.removeAction( Event::LEFT ); m_event.addAction( Event::RIGHT ); } else { // INFO( "ScusiBot: turn left\n" ); m_event.removeAction( Event::RIGHT ); m_event.addAction( Event::LEFT ); } // INFO( "ScusiBot: stop digging\n" ); m_event.removeAction( Event::DIG ); m_event.removeAction( Event::JUMP ); } } else m_count++; } m_lastPosition = position; m_event.removeAction( Event::SHOOT ); } // respawn if dead if ( !m_avatar->isLiving() ) { // INFO( "ScusiBot: respawn\n" ); m_event.addAction( Event::SPAWN ); } else m_event.removeAction( Event::SPAWN ); } void ScusiBot::moveTo( const Vector& targetVector, real tolX, real tolY ) { // turn to the side where the enemy is located if ( targetVector.x > tolX ) { m_event.removeAction( Event::LEFT ); m_event.addAction( Event::RIGHT ); } else if ( targetVector.x < -tolX ) { m_event.removeAction( Event::RIGHT ); m_event.addAction( Event::LEFT ); } else { m_event.removeAction( Event::LEFT ); m_event.removeAction( Event::RIGHT ); } // start or stop jumping/flying if necessary if ( targetVector.y < -tolY ) { m_event.addAction( Event::JUMP ); } else if ( targetVector.y > tolY ) { m_event.removeAction( Event::JUMP ); } } void ScusiBot::keepPosition( const Vector& pos ) { const Vector currentPosition = m_avatar->getPos(); const Vector vector = pos - currentPosition; // adjust left/right steering if ( vector.x > 1.0 ) { real timeToArrive = SQRT_REAL( 2.0 * vector.x / 0.1 ); real neededSpeed = timeToArrive * 0.1; if ( m_avatar->getVelX() < neededSpeed ) { m_event.removeAction( Event::LEFT ); m_event.addAction( Event::RIGHT ); } else { m_event.removeAction( Event::RIGHT ); m_event.addAction( Event::LEFT ); } } else if ( vector.x < -1.0 ) { real timeToArrive = SQRT_REAL( - 2.0 * vector.x / 0.1 ); real neededSpeed = - timeToArrive * 0.1; if ( m_avatar->getVelX() < neededSpeed ) { m_event.removeAction( Event::LEFT ); m_event.addAction( Event::RIGHT ); } else { m_event.removeAction( Event::RIGHT ); m_event.addAction( Event::LEFT ); } } else { m_event.removeAction( Event::LEFT | Event::RIGHT ); } // adjust jetpack if ( vector.y < 0.0 ) { real timeToArrive = SQRT_REAL( - 2.0 * vector.y / GRAVITATION ); real neededSpeed = - timeToArrive * GRAVITATION; if ( m_avatar->getVelY() > neededSpeed ) m_event.addAction( Event::JUMP ); else m_event.removeAction( Event::JUMP ); } else if ( vector.y > 0.0 ) { real timeToArrive = SQRT_REAL( 2.0 * vector.y / GRAVITATION ); real neededSpeed = timeToArrive * GRAVITATION; if ( m_avatar->getVelY() > neededSpeed ) m_event.addAction( Event::JUMP ); else m_event.removeAction( Event::JUMP ); } } void ScusiBot::aimAt( Object* object ) { // adjust aiming angle Vector objectVector = object->getPos() - m_avatar->getPos(); objectVector += ( object->getVel() - m_avatar->getVel() ) * ( objectVector.abs() / 27.0 ); real objectAngle = ATAN2_REAL( FABS_REAL( objectVector.x ), -objectVector.y ) / M_PI * 180.0; LOG( 5 ) INFO( "ScusiBot: enemy angle is %f, aiming angle is %f\n", objectAngle, m_avatar->getAimingAngle() ); if ( objectAngle < m_avatar->getAimingAngle() ) { LOG( 5 ) INFO( "ScusiBot: Aiming up\n" ); if ( m_avatar->getAimingAngle() - objectAngle < 5.0 || ( m_avatar->getAimingAngle() - objectAngle < 10.0 && m_avatar->getAimingSpeed() > 1.0 ) ) { m_event.removeAction( Event::DOWN | Event::UP ); } else { m_event.removeAction( Event::DOWN ); m_event.addAction( Event::UP ); } } else if ( objectAngle > m_avatar->getAimingAngle() ) { LOG( 5 ) INFO( "ScusiBot: Aiming down\n" ); if ( objectAngle - m_avatar->getAimingAngle() < 5.0 || ( objectAngle - m_avatar->getAimingAngle() < 10.0 && m_avatar->getAimingSpeed() > 1.0 ) ) { m_event.removeAction( Event::DOWN | Event::UP ); } else { m_event.removeAction( Event::UP ); m_event.addAction( Event::DOWN ); } } // correct direction if ( objectVector.x > 0.0 ) { m_event.removeAction( Event::LEFT ); m_event.addAction( Event::RIGHT ); } else if ( objectVector.x < 0.0 ) { m_event.removeAction( Event::RIGHT ); m_event.addAction( Event::LEFT ); } } void ScusiBot::shootAt( Object* object ) { // shoot whenever possible if ( getActiveWeapon()->getCurrentAmmo() == 0 ) { m_event.removeAction( Event::SHOOT ); if ( getWeapon( 5 )->getCurrentAmmo() > 0 ) { m_event.setWeapon( 6 ); } else if ( getWeapon( 0 )->getCurrentAmmo() > 0 ) { m_event.setWeapon( 1 ); } else if ( getWeapon( 1 )->getCurrentAmmo() > 0 ) { m_event.setWeapon( 2 ); } else if ( getWeapon( 2 )->getCurrentAmmo() > 0 ) { m_event.setWeapon( 3 ); } } else { int dummy; if ( m_avatar->m_worldPointer->getMap()->testLinePassable( m_avatar->getIntPosX(), m_avatar->getIntPosY(), object ->getIntPosX(), object ->getIntPosY(), dummy, dummy, dummy, dummy, Flags::OBSTACLE_EARTH ) ) { m_event.addAction( Event::SHOOT ); } } }