summaryrefslogtreecommitdiff
path: root/game/server/cstrike/bot/cs_bot_chatter.h
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/cstrike/bot/cs_bot_chatter.h')
-rw-r--r--game/server/cstrike/bot/cs_bot_chatter.h656
1 files changed, 656 insertions, 0 deletions
diff --git a/game/server/cstrike/bot/cs_bot_chatter.h b/game/server/cstrike/bot/cs_bot_chatter.h
new file mode 100644
index 0000000..47ba802
--- /dev/null
+++ b/game/server/cstrike/bot/cs_bot_chatter.h
@@ -0,0 +1,656 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Bot radio chatter system
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#ifndef CS_BOT_CHATTER_H
+#define CS_BOT_CHATTER_H
+
+#pragma warning( disable : 4786 ) // long STL names get truncated in browse info.
+
+#include "nav_mesh.h"
+#include "cs_gamestate.h"
+
+class CCSBot;
+class BotChatterInterface;
+
+#define MAX_PLACES_PER_MAP 64
+
+typedef unsigned int PlaceCriteria;
+
+typedef unsigned int CountCriteria;
+#define UNDEFINED_COUNT 0xFFFF
+#define COUNT_CURRENT_ENEMIES 0xFF // use the number of enemies we see right when we speak
+#define COUNT_MANY 4 // equal to or greater than this is "many"
+
+#define UNDEFINED_SUBJECT (-1)
+
+/// @todo Make Place a class with member fuctions for this
+const Vector *GetRandomSpotAtPlace( Place place );
+
+//----------------------------------------------------------------------------------------------------
+/**
+ * A meme is a unit information that bots use to
+ * transmit information to each other via the radio
+ */
+class BotMeme
+{
+public:
+ void Transmit( CCSBot *sender ) const; ///< transmit meme to other bots
+ // It is a best practice to always have a virtual destructor in an interface
+ // class. Otherwise if the derived classes have destructors they will not be
+ // called.
+ virtual ~BotMeme() {}
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const = 0; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotHelpMeme : public BotMeme
+{
+public:
+ BotHelpMeme( Place place = UNDEFINED_PLACE )
+ {
+ m_place = place;
+ }
+
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+
+private:
+ Place m_place; ///< where the help is needed
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotBombsiteStatusMeme : public BotMeme
+{
+public:
+ enum StatusType { CLEAR, PLANTED };
+
+ BotBombsiteStatusMeme( int zoneIndex, StatusType status )
+ {
+ m_zoneIndex = zoneIndex;
+ m_status = status;
+ }
+
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+
+private:
+ int m_zoneIndex; ///< the bombsite
+ StatusType m_status; ///< whether it is cleared or the bomb is there (planted)
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotBombStatusMeme : public BotMeme
+{
+public:
+ BotBombStatusMeme( CSGameState::BombState state, const Vector &pos )
+ {
+ m_state = state;
+ m_pos = pos;
+ }
+
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+
+private:
+ CSGameState::BombState m_state;
+ Vector m_pos;
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotFollowMeme : public BotMeme
+{
+public:
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotDefendHereMeme : public BotMeme
+{
+public:
+ BotDefendHereMeme( const Vector &pos )
+ {
+ m_pos = pos;
+ }
+
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+
+private:
+ Vector m_pos;
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotWhereBombMeme : public BotMeme
+{
+public:
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotRequestReportMeme : public BotMeme
+{
+public:
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotAllHostagesGoneMeme : public BotMeme
+{
+public:
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotHostageBeingTakenMeme : public BotMeme
+{
+public:
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotHeardNoiseMeme : public BotMeme
+{
+public:
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+class BotWarnSniperMeme : public BotMeme
+{
+public:
+ virtual void Interpret( CCSBot *sender, CCSBot *receiver ) const; ///< cause the given bot to act on this meme
+};
+
+//----------------------------------------------------------------------------------------------------
+enum BotStatementType
+{
+ REPORT_VISIBLE_ENEMIES,
+ REPORT_ENEMY_ACTION,
+ REPORT_MY_CURRENT_TASK,
+ REPORT_MY_INTENTION,
+ REPORT_CRITICAL_EVENT,
+ REPORT_REQUEST_HELP,
+ REPORT_REQUEST_INFORMATION,
+ REPORT_ROUND_END,
+ REPORT_MY_PLAN,
+ REPORT_INFORMATION,
+ REPORT_EMOTE,
+ REPORT_ACKNOWLEDGE, ///< affirmative or negative
+ REPORT_ENEMIES_REMAINING,
+ REPORT_FRIENDLY_FIRE,
+ REPORT_KILLED_FRIEND,
+ REPORT_ENEMY_LOST,
+
+ NUM_BOT_STATEMENT_TYPES
+};
+
+//----------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------
+/**
+ * BotSpeakables are the smallest unit of bot chatter.
+ * They represent a specific wav file of a phrase, and the criteria for which it is useful
+ */
+class BotSpeakable
+{
+public:
+ BotSpeakable();
+ ~BotSpeakable();
+ char *m_phrase;
+ float m_duration;
+ PlaceCriteria m_place;
+ CountCriteria m_count;
+};
+typedef CUtlVector< BotSpeakable * > BotSpeakableVector;
+typedef CUtlVector< BotSpeakableVector * > BotVoiceBankVector;
+
+
+//----------------------------------------------------------------------------------------------------
+/**
+ * The BotPhrase class is a collection of Speakables associated with a name, ID, and criteria
+ */
+class BotPhrase
+{
+public:
+ char *GetSpeakable( int bankIndex, float *duration = NULL ) const; ///< return a random speakable and its duration in seconds that meets the current criteria
+
+ // NOTE: Criteria must be set just before the GetSpeakable() call, since they are shared among all bots
+ void ClearCriteria( void ) const;
+ void SetPlaceCriteria( PlaceCriteria place ) const; ///< all returned phrases must have this place criteria
+ void SetCountCriteria( CountCriteria count ) const; ///< all returned phrases must have this count criteria
+
+ const char *GetName( void ) const { return m_name; }
+ const unsigned int GetPlace( void ) const { return m_place; }
+ RadioType GetRadioEquivalent( void ) const { return m_radioEvent; } ///< return equivalent "standard radio" event
+ bool IsImportant( void ) const { return m_isImportant; } ///< return true if this phrase is part of an important statement
+
+ bool IsPlace( void ) const { return m_isPlace; }
+
+ void Randomize( void ); ///< randomly shuffle the speakable order
+
+private:
+ friend class BotPhraseManager;
+ BotPhrase( bool isPlace );
+ ~BotPhrase();
+
+ char *m_name;
+ Place m_place;
+ bool m_isPlace; ///< true if this is a Place phrase
+ RadioType m_radioEvent; ///< equivalent radio event
+ bool m_isImportant; ///< mission-critical statement
+
+ mutable BotVoiceBankVector m_voiceBank; ///< array of voice banks (arrays of speakables)
+ CUtlVector< int > m_count; ///< number of speakables
+ mutable CUtlVector< int > m_index; ///< index of next speakable to return
+ int m_numVoiceBanks; ///< number of voice banks that have been initialized
+ void InitVoiceBank( int bankIndex ); ///< sets up the vector of voice banks for the first bankIndex voice banks
+
+ mutable PlaceCriteria m_placeCriteria;
+ mutable CountCriteria m_countCriteria;
+};
+typedef CUtlVector<BotPhrase *> BotPhraseList;
+
+inline void BotPhrase::ClearCriteria( void ) const
+{
+ m_placeCriteria = ANY_PLACE;
+ m_countCriteria = UNDEFINED_COUNT;
+}
+
+inline void BotPhrase::SetPlaceCriteria( PlaceCriteria place ) const
+{
+ m_placeCriteria = place;
+}
+
+inline void BotPhrase::SetCountCriteria( CountCriteria count ) const
+{
+ m_countCriteria = count;
+}
+
+enum BotChatterOutputType
+{
+ BOT_CHATTER_RADIO,
+ BOT_CHATTER_VOICE
+};
+typedef CUtlVector<BotChatterOutputType> BotOutputList;
+
+//----------------------------------------------------------------------------------------------------
+/**
+ * The BotPhraseManager is a singleton that provides an interface to all BotPhrase collections
+ */
+class BotPhraseManager
+{
+public:
+ BotPhraseManager( void );
+ ~BotPhraseManager();
+
+ bool Initialize( const char *filename, int bankIndex ); ///< initialize phrase system from database file for a specific voice bank (0 is the default voice bank)
+
+ void OnRoundRestart( void ); ///< invoked when round resets
+ void OnMapChange( void ); ///< invoked when map changes
+ void Reset( void );
+
+ const BotPhrase *GetPhrase( const char *name ) const; ///< given a name, return the associated phrase collection
+ const BotPhrase *GetPainPhrase( void ) const { return m_painPhrase; } ///< optimization, replaces a static pointer to the phrase
+ const BotPhrase *GetAgreeWithPlanPhrase( void ) const { return m_agreeWithPlanPhrase; } ///< optimization, replaces a static pointer to the phrase
+
+ const BotPhrase *GetPlace( const char *name ) const; ///< given a name, return the associated Place phrase collection
+ const BotPhrase *GetPlace( unsigned int id ) const; ///< given an id, return the associated Place phrase collection
+
+ const BotPhraseList *GetPlaceList( void ) const { return &m_placeList; }
+
+ float GetPlaceStatementInterval( Place where ) const; ///< return time last statement of given type was emitted by a teammate for the given place
+ void ResetPlaceStatementInterval( Place where ); ///< set time of last statement of given type was emitted by a teammate for the given place
+
+ BotChatterOutputType GetOutputType( int voiceBank ) const;
+
+private:
+ BotPhraseList m_list; ///< master list of all phrase collections
+ BotPhraseList m_placeList; ///< master list of all Place phrases
+
+ BotOutputList m_output;
+
+ const BotPhrase *m_painPhrase;
+ const BotPhrase *m_agreeWithPlanPhrase;
+
+ struct PlaceTimeInfo
+ {
+ Place placeID;
+ IntervalTimer timer;
+ };
+ mutable PlaceTimeInfo m_placeStatementHistory[ MAX_PLACES_PER_MAP ];
+ mutable int m_placeCount;
+ int FindPlaceIndex( Place where ) const;
+};
+
+inline int BotPhraseManager::FindPlaceIndex( Place where ) const
+{
+ for( int i=0; i<m_placeCount; ++i )
+ if (m_placeStatementHistory[i].placeID == where)
+ return i;
+
+ // no such place - allocate it
+ if (m_placeCount < MAX_PLACES_PER_MAP)
+ {
+ m_placeStatementHistory[ ++m_placeCount ].placeID = where;
+ m_placeStatementHistory[ ++m_placeCount ].timer.Invalidate();
+ return m_placeCount-1;
+ }
+
+ // place directory is full
+ return -1;
+}
+
+/**
+ * Return time last statement of given type was emitted by a teammate for the given place
+ */
+inline float BotPhraseManager::GetPlaceStatementInterval( Place place ) const
+{
+ int index = FindPlaceIndex( place );
+
+ if (index < 0)
+ return 999999.9f;
+
+ if (index >= m_placeCount)
+ return 999999.9f;
+
+ return m_placeStatementHistory[ index ].timer.GetElapsedTime();
+}
+
+/**
+ * Set time of last statement of given type was emitted by a teammate for the given place
+ */
+inline void BotPhraseManager::ResetPlaceStatementInterval( Place place )
+{
+ int index = FindPlaceIndex( place );
+
+ if (index < 0)
+ return;
+
+ if (index >= m_placeCount)
+ return;
+
+ // update entry
+ m_placeStatementHistory[ index ].timer.Reset();
+}
+
+extern BotPhraseManager *TheBotPhrases;
+
+
+
+//----------------------------------------------------------------------------------------------------
+/**
+ * Statements are meaningful collections of phrases
+ */
+class BotStatement
+{
+public:
+ BotStatement( BotChatterInterface *chatter, BotStatementType type, float expireDuration );
+ ~BotStatement();
+
+ BotChatterInterface *GetChatter( void ) const { return m_chatter; }
+ CCSBot *GetOwner( void ) const;
+
+ BotStatementType GetType( void ) const { return m_type; } ///< return the type of statement this is
+ bool IsImportant( void ) const; ///< return true if this statement is "important" and not personality chatter
+
+ bool HasSubject( void ) const { return (m_subject == UNDEFINED_SUBJECT) ? false : true; }
+ void SetSubject( int playerID ) { m_subject = playerID; } ///< who this statement is about
+ int GetSubject( void ) const { return m_subject; } ///< who this statement is about
+
+ bool HasPlace( void ) const { return (GetPlace()) ? true : false; }
+ Place GetPlace( void ) const; ///< if this statement refers to a specific place, return that place
+ void SetPlace( Place where ) { m_place = where; } ///< explicitly set place
+
+ bool HasCount( void ) const; ///< return true if this statement has an associated count
+
+ bool IsRedundant( const BotStatement *say ) const; ///< return true if this statement is the same as the given one
+ bool IsObsolete( void ) const; ///< return true if this statement is no longer appropriate to say
+ void Convert( const BotStatement *say ); ///< possibly change what were going to say base on what teammate is saying
+
+ void AppendPhrase( const BotPhrase *phrase );
+
+ void SetStartTime( float timestamp ) { m_startTime = timestamp; } ///< define the earliest time this statement can be spoken
+ float GetStartTime( void ) const { return m_startTime; }
+
+ enum ConditionType
+ {
+ IS_IN_COMBAT,
+ RADIO_SILENCE,
+ ENEMIES_REMAINING,
+
+ NUM_CONDITIONS
+ };
+
+ void AddCondition( ConditionType condition ); ///< conditions must be true for the statement to be spoken
+ bool IsValid( void ) const; ///< verify all attached conditions
+
+ enum ContextType
+ {
+ CURRENT_ENEMY_COUNT,
+ REMAINING_ENEMY_COUNT,
+ SHORT_DELAY,
+ LONG_DELAY,
+ ACCUMULATE_ENEMIES_DELAY
+ };
+ void AppendPhrase( ContextType contextPhrase ); ///< special phrases that depend on the context
+
+ bool Update( void ); ///< emit statement over time, return false if statement is done
+ bool IsSpeaking( void ) const { return m_isSpeaking; } ///< return true if this statement is currently being spoken
+ float GetTimestamp( void ) const { return m_timestamp; } ///< get time statement was created (but not necessarily started talking)
+
+ void AttachMeme( BotMeme *meme ); ///< attach a meme to this statement, to be transmitted to other friendly bots when spoken
+
+private:
+ friend class BotChatterInterface;
+
+ BotChatterInterface *m_chatter; ///< the chatter system this statement is part of
+
+ BotStatement *m_next, *m_prev; ///< linked list hooks
+
+ BotStatementType m_type; ///< what kind of statement this is
+ int m_subject; ///< who this subject is about
+ Place m_place; ///< explicit place - note some phrases have implicit places as well
+ BotMeme *m_meme; ///< a statement can only have a single meme for now
+
+ float m_timestamp; ///< time when message was created
+ float m_startTime; ///< the earliest time this statement can be spoken
+ float m_expireTime; ///< time when this statement is no longer valid
+ float m_speakTimestamp; ///< time when message began being spoken
+ bool m_isSpeaking; ///< true if this statement is current being spoken
+
+ float m_nextTime; ///< time for next phrase to begin
+
+ enum { MAX_BOT_PHRASES = 4 };
+ struct
+ {
+ bool isPhrase;
+ union
+ {
+ const BotPhrase *phrase;
+ ContextType context;
+ };
+ }
+ m_statement[ MAX_BOT_PHRASES ];
+
+ enum { MAX_BOT_CONDITIONS = 4 };
+ ConditionType m_condition[ MAX_BOT_CONDITIONS ]; ///< conditions that must be true for the statement to be said
+ int m_conditionCount;
+
+ int m_index; ///< m_index refers to the phrase currently being spoken, or -1 if we havent started yet
+ int m_count;
+};
+
+//----------------------------------------------------------------------------------------------------
+/**
+ * This class defines the interface to the bot radio chatter system
+ */
+class BotChatterInterface
+{
+public:
+ BotChatterInterface( CCSBot *me );
+ ~BotChatterInterface( );
+
+ void Reset( void ); ///< reset to initial state
+ void Update( void ); ///< process ongoing chatter
+
+ /// invoked when event occurs in the game (some events have NULL entities)
+ void OnDeath( void ); ///< invoked when we die
+
+ enum VerbosityType
+ {
+ NORMAL, ///< full chatter
+ MINIMAL, ///< only scenario-critical events
+ RADIO, ///< use the standard radio instead
+ OFF ///< no chatter at all
+ };
+ VerbosityType GetVerbosity( void ) const; ///< return our current level of verbosity
+
+ CCSBot *GetOwner( void ) const { return m_me; }
+
+ bool IsTalking( void ) const; ///< return true if we are currently talking
+ float GetRadioSilenceDuration( void ); ///< return time since any teammate said anything
+ void ResetRadioSilenceDuration( void );
+
+ enum { MUST_ADD = 1 };
+ void AddStatement( BotStatement *statement, bool mustAdd = false ); ///< register a statement for speaking
+ void RemoveStatement( BotStatement *statement ); ///< remove a statement
+
+ BotStatement *GetActiveStatement( void ); ///< returns the statement that is being spoken, or is next to be spoken if no-one is speaking now
+ BotStatement *GetStatement( void ) const; ///< returns our current statement, or NULL if we aren't speaking
+
+ int GetPitch( void ) const { return m_pitch; }
+
+
+ //-- things the bots can say ---------------------------------------------------------------------
+ void Say( const char *phraseName, float lifetime = 3.0f, float delay = 0.0f );
+
+ void AnnouncePlan( const char *phraseName, Place where );
+ void Affirmative( void );
+ void Negative( void );
+
+ void EnemySpotted( void ); ///< report enemy sightings
+ void KilledMyEnemy( int victimID );
+ void EnemiesRemaining( void );
+
+ void SpottedSniper( void );
+ void FriendSpottedSniper( void );
+
+ void Clear( Place where );
+
+ void ReportIn( void ); ///< ask for current situation
+ void ReportingIn( void ); ///< report current situation
+
+ bool NeedBackup( void );
+ void PinnedDown( void );
+ void Scared( void );
+ void HeardNoise( const Vector &pos );
+ void FriendHeardNoise( void );
+
+ void TheyPickedUpTheBomb( void );
+ void GoingToPlantTheBomb( Place where );
+ void BombsiteClear( int zoneIndex );
+ void FoundPlantedBomb( int zoneIndex );
+ void PlantingTheBomb( Place where );
+ void SpottedBomber( CBasePlayer *bomber );
+ void SpottedLooseBomb( CBaseEntity *bomb );
+ void GuardingLooseBomb( CBaseEntity *bomb );
+ void RequestBombLocation( void );
+
+ #define IS_PLAN true
+ void GuardingHostages( Place where, bool isPlan = false );
+ void GuardingHostageEscapeZone( bool isPlan = false );
+ void HostagesBeingTaken( void );
+ void HostagesTaken( void );
+ void TalkingToHostages( void );
+ void EscortingHostages( void );
+ void HostageDown( void );
+ void GuardingBombsite( Place where );
+
+ void CelebrateWin( void );
+
+ void Encourage( const char *phraseName, float repeatInterval = 10.0f, float lifetime = 3.0f ); ///< "encourage" the player to do the scenario
+
+ void KilledFriend( void );
+ void FriendlyFire( void );
+
+ bool SeesAtLeastOneEnemy( void ) const { return m_seeAtLeastOneEnemy; }
+
+private:
+ BotStatement *m_statementList; ///< list of all active/pending messages for this bot
+
+ void ReportEnemies( void ); ///< track nearby enemy count and generate enemy activity statements
+ bool ShouldSpeak( void ) const; ///< return true if we speaking makes sense now
+
+ CCSBot *m_me; ///< the bot this chatter is for
+
+ bool m_seeAtLeastOneEnemy;
+ float m_timeWhenSawFirstEnemy;
+ bool m_reportedEnemies;
+ bool m_requestedBombLocation; ///< true if we already asked where the bomb has been planted
+
+ int m_pitch;
+
+ static IntervalTimer m_radioSilenceInterval[ 2 ]; ///< one timer for each team
+
+ IntervalTimer m_needBackupInterval;
+ IntervalTimer m_spottedBomberInterval;
+ IntervalTimer m_scaredInterval;
+ IntervalTimer m_planInterval;
+ CountdownTimer m_spottedLooseBombTimer;
+ CountdownTimer m_heardNoiseTimer;
+ CountdownTimer m_escortingHostageTimer;
+ CountdownTimer m_warnSniperTimer;
+ static CountdownTimer m_encourageTimer; ///< timer to know when we can "encourage" the human player again - shared by all bots
+};
+
+inline BotChatterInterface::VerbosityType BotChatterInterface::GetVerbosity( void ) const
+{
+ const char *string = cv_bot_chatter.GetString();
+
+ if (string == NULL)
+ return NORMAL;
+
+ if (string[0] == 'm' || string[0] == 'M')
+ return MINIMAL;
+
+ if (string[0] == 'r' || string[0] == 'R')
+ return RADIO;
+
+ if (string[0] == 'o' || string[0] == 'O')
+ return OFF;
+
+ return NORMAL;
+}
+
+
+inline bool BotChatterInterface::IsTalking( void ) const
+{
+ if (m_statementList)
+ return m_statementList->IsSpeaking();
+
+ return false;
+}
+
+inline BotStatement *BotChatterInterface::GetStatement( void ) const
+{
+ return m_statementList;
+}
+
+
+inline void BotChatterInterface::Say( const char *phraseName, float lifetime, float delay )
+{
+ BotStatement *say = new BotStatement( this, REPORT_MY_INTENTION, lifetime );
+
+ say->AppendPhrase( TheBotPhrases->GetPhrase( phraseName ) );
+
+ if (delay > 0.0f)
+ say->SetStartTime( gpGlobals->curtime + delay );
+
+ AddStatement( say );
+}
+
+
+
+
+#endif // CS_BOT_CHATTER_H