1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
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
|