summaryrefslogtreecommitdiff
path: root/game/server/cstrike/bot/cs_bot_radio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/cstrike/bot/cs_bot_radio.cpp')
-rw-r--r--game/server/cstrike/bot/cs_bot_radio.cpp346
1 files changed, 346 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/cs_bot_radio.cpp b/game/server/cstrike/bot/cs_bot_radio.cpp
new file mode 100644
index 0000000..08acb4e
--- /dev/null
+++ b/game/server/cstrike/bot/cs_bot_radio.cpp
@@ -0,0 +1,346 @@
+//========= 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;
+}
+