|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
// Author: Michael S. Booth ([email protected]), 2003
#include "cbase.h"
#include "cs_bot.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern int gmsgBotVoice;
//--------------------------------------------------------------------------------------------------------------
/**
* Returns true if the radio message is an order to do something * NOTE: "Report in" is not considered a "command" because it doesnt ask the bot to go somewhere, or change its mind */ bool CCSBot::IsRadioCommand( RadioType event ) const { if (event == RADIO_AFFIRMATIVE || event == RADIO_NEGATIVE || event == RADIO_ENEMY_SPOTTED || event == RADIO_SECTOR_CLEAR || event == RADIO_REPORTING_IN || event == RADIO_REPORT_IN_TEAM || event == RADIO_ENEMY_DOWN || event == RADIO_INVALID ) return false;
return true; }
//--------------------------------------------------------------------------------------------------------------
/**
* Respond to radio commands from HUMAN players */ void CCSBot::RespondToRadioCommands( void ) { // bots use the chatter system to respond to each other
if (m_radioSubject != NULL && m_radioSubject->IsPlayer()) { CCSPlayer *player = m_radioSubject; if (player->IsBot()) { m_lastRadioCommand = RADIO_INVALID; return; } } if (m_lastRadioCommand == RADIO_INVALID) return;
// a human player has issued a radio command
GetChatter()->ResetRadioSilenceDuration();
// if we are doing something important, ignore the radio
// unless it is a "report in" request - we can do that while we continue to do other things
/// @todo Create "uninterruptable" flag
if (m_lastRadioCommand != RADIO_REPORT_IN_TEAM) { if (IsBusy()) { // consume command
m_lastRadioCommand = RADIO_INVALID; return; } }
// wait for reaction time before responding
// delay needs to be long enough for the radio message we're responding to to finish
float respondTime = 1.0f + 2.0f * GetProfile()->GetReactionTime(); if (IsRogue()) respondTime += 2.0f;
if (gpGlobals->curtime - m_lastRadioRecievedTimestamp < respondTime) return;
// rogues won't follow commands, unless already following the player
if (!IsFollowing() && IsRogue()) { if (IsRadioCommand( m_lastRadioCommand )) { GetChatter()->Negative(); }
// consume command
m_lastRadioCommand = RADIO_INVALID; return; }
CCSPlayer *player = m_radioSubject; if (player == NULL) return;
// respond to command
bool canDo = false; const float inhibitAutoFollowDuration = 60.0f; switch( m_lastRadioCommand ) { case RADIO_REPORT_IN_TEAM: { GetChatter()->ReportingIn(); break; }
case RADIO_FOLLOW_ME: case RADIO_COVER_ME: case RADIO_STICK_TOGETHER_TEAM: case RADIO_REGROUP_TEAM: { if (!IsFollowing()) { Follow( player ); player->AllowAutoFollow(); canDo = true; } break; }
case RADIO_ENEMY_SPOTTED: case RADIO_NEED_BACKUP: case RADIO_TAKING_FIRE: if (!IsFollowing()) { Follow( player ); GetChatter()->Say( "OnMyWay" ); player->AllowAutoFollow(); canDo = false; } break;
case RADIO_TEAM_FALL_BACK: { if (TryToRetreat()) canDo = true; break; }
case RADIO_HOLD_THIS_POSITION: { // find the leader's area
SetTask( HOLD_POSITION ); StopFollowing(); player->InhibitAutoFollow( inhibitAutoFollowDuration ); Hide( TheNavMesh->GetNearestNavArea( m_radioPosition ) ); canDo = true; break; }
case RADIO_GO_GO_GO: case RADIO_STORM_THE_FRONT: StopFollowing(); Hunt(); canDo = true; player->InhibitAutoFollow( inhibitAutoFollowDuration ); break;
case RADIO_GET_OUT_OF_THERE: if (TheCSBots()->IsBombPlanted()) { EscapeFromBomb(); player->InhibitAutoFollow( inhibitAutoFollowDuration ); canDo = true; } break;
case RADIO_SECTOR_CLEAR: { // if this is a defusal scenario, and the bomb is planted,
// and a human player cleared a bombsite, check it off our list too
if (TheCSBots()->GetScenario() == CCSBotManager::SCENARIO_DEFUSE_BOMB) { if (GetTeamNumber() == TEAM_CT && TheCSBots()->IsBombPlanted()) { const CCSBotManager::Zone *zone = TheCSBots()->GetClosestZone( player );
if (zone) { GetGameState()->ClearBombsite( zone->m_index );
// if we are huting for the planted bomb, re-select bombsite
if (GetTask() == FIND_TICKING_BOMB) Idle();
canDo = true; } } } break; }
default: // ignore all other radio commands for now
return; }
if (canDo) { // affirmative
GetChatter()->Affirmative();
// if we agreed to follow a new command, put away our grenade
if (IsRadioCommand( m_lastRadioCommand ) && IsUsingGrenade()) { EquipBestWeapon(); } }
// consume command
m_lastRadioCommand = RADIO_INVALID; }
//--------------------------------------------------------------------------------------------------------------
/**
* Decide if we should move to help the player, return true if we will */ bool CCSBot::RespondToHelpRequest( CCSPlayer *them, Place place, float maxRange ) { if (IsRogue()) return false;
// if we're busy, ignore
if (IsBusy()) return false;
Vector themOrigin = GetCentroid( them );
// if we are too far away, ignore
if (maxRange > 0.0f) { // compute actual travel distance
PathCost cost(this); float travelDistance = NavAreaTravelDistance( m_lastKnownArea, TheNavMesh->GetNearestNavArea( themOrigin ), cost ); if (travelDistance < 0.0f) return false;
if (travelDistance > maxRange) return false; }
if (place == UNDEFINED_PLACE) { // if we have no "place" identifier, go directly to them
// if we are already there, ignore
float rangeSq = (them->GetAbsOrigin() - GetAbsOrigin()).LengthSqr(); const float close = 750.0f * 750.0f; if (rangeSq < close) return true;
MoveTo( themOrigin, FASTEST_ROUTE ); } else { // if we are already there, ignore
if (GetPlace() == place) return true;
// go to where help is needed
const Vector *pos = GetRandomSpotAtPlace( place ); if (pos) { MoveTo( *pos, FASTEST_ROUTE ); } else { MoveTo( themOrigin, FASTEST_ROUTE ); } }
// acknowledge
GetChatter()->Say( "OnMyWay" );
return true; }
//--------------------------------------------------------------------------------------------------------------
/**
* Send a radio message */ void CCSBot::SendRadioMessage( RadioType event ) { // make sure this is a radio event
if (event <= RADIO_START_1 || event >= RADIO_END) return;
PrintIfWatched( "%3.1f: SendRadioMessage( %s )\n", gpGlobals->curtime, RadioEventName[ event ] );
// note the time the message was sent
TheCSBots()->SetRadioMessageTimestamp( event, GetTeamNumber() );
m_lastRadioSentTimestamp = gpGlobals->curtime;
char slot[2]; slot[1] = '\000';
if (event > RADIO_START_1 && event < RADIO_START_2) { HandleMenu_Radio1( event - RADIO_START_1 ); } else if (event > RADIO_START_2 && event < RADIO_START_3) { HandleMenu_Radio2( event - RADIO_START_2 ); } else { HandleMenu_Radio3( event - RADIO_START_3 ); } }
//--------------------------------------------------------------------------------------------------------------
/**
* Send voice chatter. Also sends the entindex and duration for voice feedback. */ void CCSBot::SpeakAudio( const char *voiceFilename, float duration, int pitch ) { if( !IsAlive() ) return;
if ( IsObserver() ) return;
CRecipientFilter filter; ConstructRadioFilter( filter );
UserMessageBegin ( filter, "RawAudio" ); WRITE_BYTE( pitch ); WRITE_BYTE( entindex() ); WRITE_FLOAT( duration ); WRITE_STRING( voiceFilename ); MessageEnd();
GetChatter()->ResetRadioSilenceDuration();
m_voiceEndTimestamp = gpGlobals->curtime + duration; }
|