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
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#ifndef AI_PLAYERALLY_H
#define AI_PLAYERALLY_H
#include "utlmap.h"
#include "simtimer.h"
#include "AI_Criteria.h"
#include "ai_baseactor.h"
#include "ai_speechfilter.h"
#ifndef _WIN32
#undef min
#endif
#include "stdstring.h"
#ifndef _WIN32
#undef MINMAX_H
#include "minmax.h"
#endif
#if defined( _WIN32 )
#pragma once
#endif
//-----------------------------------------------------------------------------
#define TLK_ANSWER "TLK_ANSWER"
#define TLK_ANSWER_HELLO "TLK_ANSWER_HELLO"
#define TLK_QUESTION "TLK_QUESTION"
#define TLK_IDLE "TLK_IDLE"
#define TLK_STARE "TLK_STARE"
#define TLK_LOOK "TLK_LOOK" // player looking at player for a second
#define TLK_USE "TLK_USE"
#define TLK_STARTFOLLOW "TLK_STARTFOLLOW"
#define TLK_STOPFOLLOW "TLK_STOPFOLLOW"
#define TLK_JOINPLAYER "TLK_JOINPLAYER"
#define TLK_STOP "TLK_STOP"
#define TLK_NOSHOOT "TLK_NOSHOOT"
#define TLK_HELLO "TLK_HELLO"
#define TLK_PHELLO "TLK_PHELLO"
#define TLK_HELLO_NPC "TLK_HELLO_NPC"
#define TLK_PIDLE "TLK_PIDLE"
#define TLK_PQUESTION "TLK_PQUESTION"
#define TLK_PLHURT1 "TLK_PLHURT1"
#define TLK_PLHURT2 "TLK_PLHURT2"
#define TLK_PLHURT3 "TLK_PLHURT3"
#define TLK_PLHURT "TLK_PLHURT"
#define TLK_PLPUSH "TLK_PLPUSH"
#define TLK_PLRELOAD "TLK_PLRELOAD"
#define TLK_SMELL "TLK_SMELL"
#define TLK_SHOT "TLK_SHOT"
#define TLK_WOUND "TLK_WOUND"
#define TLK_MORTAL "TLK_MORTAL"
#define TLK_DANGER "TLK_DANGER"
#define TLK_SEE_COMBINE "TLK_SEE_COMBINE"
#define TLK_ENEMY_DEAD "TLK_ENEMY_DEAD"
#define TLK_ALYX_ENEMY_DEAD "TLK_ALYX_ENEMY_DEAD"
#define TLK_SELECTED "TLK_SELECTED" // selected by player in command mode.
#define TLK_COMMANDED "TLK_COMMANDED" // received orders from player in command mode
#define TLK_COMMAND_FAILED "TLK_COMMAND_FAILED"
#define TLK_DENY_COMMAND "TLK_DENY_COMMAND" // designer has asked this NPC to politely deny player commands to move the squad
#define TLK_BETRAYED "TLK_BETRAYED" // player killed an ally in front of me.
#define TLK_ALLY_KILLED "TLK_ALLY_KILLED" // witnessed an ally die some other way.
#define TLK_ATTACKING "TLK_ATTACKING" // about to fire my weapon at a target
#define TLK_HEAL "TLK_HEAL" // healing someone
#define TLK_GIVEAMMO "TLK_GIVEAMMO" // giving ammo to someone
#define TLK_DEATH "TLK_DEATH" // Death rattle
#define TLK_HELP_ME "TLK_HELP_ME" // call out to the player for help
#define TLK_PLYR_PHYSATK "TLK_PLYR_PHYSATK" // Player's attacked me with a thrown physics object
#define TLK_NEWWEAPON "TLK_NEWWEAPON"
#define TLK_PLDEAD "TLK_PLDEAD"
#define TLK_HIDEANDRELOAD "TLK_HIDEANDRELOAD"
#define TLK_STARTCOMBAT "TLK_STARTCOMBAT"
#define TLK_WATCHOUT "TLK_WATCHOUT"
#define TLK_MOBBED "TLK_MOBBED"
#define TLK_MANY_ENEMIES "TLK_MANY_ENEMIES"
#define TLK_FLASHLIGHT_ILLUM "TLK_FLASHLIGHT_ILLUM"
#define TLK_FLASHLIGHT_ON "TLK_FLASHLIGHT_ON" // player turned on flashlight
#define TLK_FLASHLIGHT_OFF "TLK_FLASHLIGHT_OFF" // player turned off flashlight
#define TLK_DARKNESS_LOSTPLAYER "TLK_DARKNESS_LOSTPLAYER"
#define TLK_DARKNESS_FOUNDPLAYER "TLK_DARKNESS_FOUNDPLAYER"
#define TLK_DARKNESS_UNKNOWN_WOUND "TLK_DARKNESS_UNKNOWN_WOUND"
#define TLK_DARKNESS_HEARDSOUND "TLK_DARKNESS_HEARDSOUND"
#define TLK_DARKNESS_LOSTENEMY_BY_FLASHLIGHT "TLK_DARKNESS_LOSTENEMY_BY_FLASHLIGHT"
#define TLK_DARKNESS_LOSTENEMY_BY_FLASHLIGHT_EXPIRED "TLK_DARKNESS_LOSTENEMY_BY_FLASHLIGHT_EXPIRED"
#define TLK_DARKNESS_FOUNDENEMY_BY_FLASHLIGHT "TLK_DARKNESS_FOUNDENEMY_BY_FLASHLIGHT"
#define TLK_DARKNESS_FLASHLIGHT_EXPIRED "TLK_DARKNESS_FLASHLIGHT_EXPIRED" // flashlight expired while not in combat
#define TLK_DARKNESS_ENEMY_IN_DARKNESS "TLK_DARKNESS_ENEMY_IN_DARKNESS" // have an enemy, but it's in the darkness
#define TLK_SPOTTED_INCOMING_HEADCRAB "TLK_SPOTTED_INCOMING_HEADCRAB"
#define TLK_CANT_INTERACT_NOW "TLK_CANT_INTERACT_NOW" // to busy to interact with an object the player is holding up to me
#define TLK_ALLY_IN_BARNACLE "TLK_ALLY_IN_BARNACLE" // Barnacle is lifting my buddy!
#define TLK_SELF_IN_BARNACLE "TLK_SELF_IN_BARNACLE" // I was grabbed by a barnacle!
#define TLK_FOUNDPLAYER "TLK_FOUNDPLAYER"
#define TLK_PLAYER_KILLED_NPC "TLK_PLAYER_KILLED_NPC"
#define TLK_ENEMY_BURNING "TLK_ENEMY_BURNING"
#define TLK_SPOTTED_ZOMBIE_WAKEUP "TLK_SPOTTED_ZOMBIE_WAKEUP"
#define TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE "TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE"
#define TLK_DANGER_ZOMBINE_GRENADE "TLK_DANGER_ZOMBINE_GRENADE"
#define TLK_BALLSOCKETED "TLK_BALLSOCKETED"
// Vehicle passenger
#define TLK_PASSENGER_WARN_COLLISION "TLK_PASSENGER_WARN_COLLISION" // About to collide with something
#define TLK_PASSENGER_IMPACT "TLK_PASSENGER_IMPACT" // Just hit something
#define TLK_PASSENGER_OVERTURNED "TLK_PASSENGER_OVERTURNED" // Vehicle has just overturned
#define TLK_PASSENGER_REQUEST_UPRIGHT "TLK_PASSENGER_REQUEST_UPRIGHT" // Vehicle needs to be put upright
#define TLK_PASSENGER_ERRATIC_DRIVING "TLK_PASSENGER_ERRATIC_DRIVING" // Vehicle is moving erratically
#define TLK_PASSENGER_VEHICLE_STARTED "TLK_PASSENGER_VEHICLE_STARTED" // Vehicle has started moving
#define TLK_PASSENGER_VEHICLE_STOPPED "TLK_PASSENGER_VEHICLE_STOPPED" // Vehicle has stopped moving
#define TLK_PASSENGER_BEGIN_ENTRANCE "TLK_PASSENGER_BEGIN_ENTRANCE" // Passenger started entering
#define TLK_PASSENGER_FINISH_ENTRANCE "TLK_PASSENGER_FINISH_ENTRANCE" // Passenger finished entering (is in seat)
#define TLK_PASSENGER_BEGIN_EXIT "TLK_PASSENGER_BEGIN_EXIT" // Passenger started exiting
#define TLK_PASSENGER_FINISH_EXIT "TLK_PASSENGER_FINISH_EXIT" // Passenger finished exiting (seat is vacated)
#define TLK_PASSENGER_PLAYER_ENTERED "TLK_PASSENGER_PLAYER_ENTERED" // Player entered the vehicle
#define TLK_PASSENGER_PLAYER_EXITED "TLK_PASSENGER_PLAYER_EXITED" // Player exited the vehicle
#define TLK_PASSENGER_NEW_RADAR_CONTACT "TLK_PASSENGER_NEW_RADAR_CONTACT" // Noticed a brand new contact on the radar
#define TLK_PASSENGER_PUNTED "TLK_PASSENGER_PUNTED" // The player has punted us while we're sitting in the vehicle
// Vortigaunt
#define TLK_VORTIGAUNT_DISPEL "TLK_VORTIGAUNT_DISPEL" // Dispel attack starting
// resume is "as I was saying..." or "anyhow..."
#define TLK_RESUME "TLK_RESUME"
// tourguide stuff below
#define TLK_TGSTAYPUT "TLK_TGSTAYPUT"
#define TLK_TGFIND "TLK_TGFIND"
#define TLK_TGSEEK "TLK_TGSEEK"
#define TLK_TGLOSTYOU "TLK_TGLOSTYOU"
#define TLK_TGCATCHUP "TLK_TGCATCHUP"
#define TLK_TGENDTOUR "TLK_TGENDTOUR"
//-----------------------------------------------------------------------------
#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this
//-----------------------------------------------------------------------------
#define TALKER_STARE_DIST 128 // anyone closer than this and looking at me is probably staring at me.
#define TALKER_DEFER_IDLE_SPEAK_MIN 10
#define TALKER_DEFER_IDLE_SPEAK_MAX 20
//-----------------------------------------------------------------------------
class CAI_PlayerAlly;
//-----------------------------------------------------------------------------
//
// CLASS: CAI_AllySpeechManager
//
//-----------------------------------------------------------------------------
enum ConceptCategory_t
{
SPEECH_IDLE,
SPEECH_IMPORTANT,
SPEECH_PRIORITY,
SPEECH_NUM_CATEGORIES
};
struct ConceptCategoryInfo_t
{
float minGlobalDelay;
float maxGlobalDelay;
float minPersonalDelay;
float maxPersonalDelay;
};
enum AIConceptFlags_t
{
AICF_DEFAULT = 0,
AICF_SPEAK_ONCE = 0x01,
AICF_PROPAGATE_SPOKEN = 0x02,
AICF_TARGET_PLAYER = 0x04,
AICF_QUESTION = 0x08,
AICF_ANSWER = 0x10,
};
struct ConceptInfo_t
{
AIConcept_t concept;
ConceptCategory_t category;
float minGlobalCategoryDelay;
float maxGlobalCategoryDelay;
float minPersonalCategoryDelay;
float maxPersonalCategoryDelay;
float minConceptDelay;
float maxConceptDelay;
int flags;
};
//-------------------------------------
class CAI_AllySpeechManager : public CLogicalEntity
{
DECLARE_CLASS( CAI_AllySpeechManager, CLogicalEntity );
public:
CAI_AllySpeechManager();
~CAI_AllySpeechManager();
void Spawn();
void AddCustomConcept( const ConceptInfo_t &conceptInfo );
ConceptCategoryInfo_t *GetConceptCategoryInfo( ConceptCategory_t category );
ConceptInfo_t *GetConceptInfo( AIConcept_t concept );
void OnSpokeConcept( CAI_PlayerAlly *pPlayerAlly, AIConcept_t concept, AI_Response *response );
void SetCategoryDelay( ConceptCategory_t category, float minDelay, float maxDelay = 0.0 );
bool CategoryDelayExpired( ConceptCategory_t category );
bool ConceptDelayExpired( AIConcept_t concept );
private:
CSimpleSimTimer m_ConceptCategoryTimers[SPEECH_NUM_CATEGORIES];
CUtlMap<string_t, CSimpleSimTimer, char> m_ConceptTimers;
friend CAI_AllySpeechManager *GetAllySpeechManager();
static CAI_AllySpeechManager *gm_pSpeechManager;
DECLARE_DATADESC();
};
//-------------------------------------
CAI_AllySpeechManager *GetAllySpeechManager();
//-----------------------------------------------------------------------------
//
// CLASS: CAI_PlayerAlly
//
//-----------------------------------------------------------------------------
class CAI_AllySpeechManager;
enum AISpeechTargetSearchFlags_t
{
AIST_PLAYERS = (1<<0),
AIST_NPCS = (1<<1),
AIST_IGNORE_RELATIONSHIP = (1<<2),
AIST_ANY_QUALIFIED = (1<<3),
AIST_FACING_TARGET = (1<<4),
};
struct AISpeechSelection_t
{
std::string concept;
AI_Response Response;
EHANDLE hSpeechTarget;
};
//-------------------------------------
class CAI_PlayerAlly : public CAI_BaseActor
{
DECLARE_CLASS( CAI_PlayerAlly, CAI_BaseActor );
public:
//---------------------------------
int ObjectCaps( void ) { return UsableNPCObjectCaps(BaseClass::ObjectCaps()); }
void TalkInit( void );
//---------------------------------
// Behavior
//---------------------------------
void GatherConditions( void );
void GatherEnemyConditions( CBaseEntity *pEnemy );
void OnStateChange( NPC_STATE OldState, NPC_STATE NewState );
void PrescheduleThink( void );
int SelectSchedule( void );
int SelectNonCombatSpeech( AISpeechSelection_t *pSelection );
virtual int SelectNonCombatSpeechSchedule();
int TranslateSchedule( int scheduleType );
void OnStartSchedule( int scheduleType );
void StartTask( const Task_t *pTask );
void RunTask( const Task_t *pTask );
void TaskFail( AI_TaskFailureCode_t );
void TaskFail( const char *pszGeneralFailText ) { BaseClass::TaskFail( pszGeneralFailText ); }
void ClearTransientConditions();
void Touch( CBaseEntity *pOther );
//---------------------------------
// Combat
//---------------------------------
void OnKilledNPC( CBaseCombatCharacter *pKilled );
//---------------------------------
// Damage handling
//---------------------------------
void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
int OnTakeDamage_Alive( const CTakeDamageInfo &info );
int TakeHealth( float flHealth, int bitsDamageType );
void Event_Killed( const CTakeDamageInfo &info );
bool CreateVPhysics();
//---------------------------------
virtual void PainSound( const CTakeDamageInfo &info );
//---------------------------------
// Speech & Acting
//---------------------------------
CBaseEntity *EyeLookTarget( void ); // Override to look at talk target
CBaseEntity *FindNamedEntity( const char *pszName, IEntityFindFilter *pFilter = NULL );
CBaseEntity *FindSpeechTarget( int flags );
virtual bool IsValidSpeechTarget( int flags, CBaseEntity *pEntity );
CBaseEntity *GetSpeechTarget() { return m_hTalkTarget.Get(); }
void SetSpeechTarget( CBaseEntity *pSpeechTarget ) { m_hTalkTarget = pSpeechTarget; }
void SetSpeechFilter( CAI_SpeechFilter *pFilter ) { m_hSpeechFilter = pFilter; }
CAI_SpeechFilter *GetSpeechFilter( void ) { return m_hSpeechFilter; }
//---------------------------------
virtual bool SelectIdleSpeech( AISpeechSelection_t *pSelection );
virtual bool SelectAlertSpeech( AISpeechSelection_t *pSelection );
virtual bool SelectInterjection();
virtual bool SelectPlayerUseSpeech();
//---------------------------------
virtual bool SelectQuestionAndAnswerSpeech( AISpeechSelection_t *pSelection );
virtual void PostSpeakDispatchResponse( AIConcept_t concept, AI_Response *response );
bool SelectQuestionFriend( CBaseEntity *pFriend, AISpeechSelection_t *pSelection );
bool SelectAnswerFriend( CBaseEntity *pFriend, AISpeechSelection_t *pSelection, bool bRespondingToHello );
//---------------------------------
bool SelectSpeechResponse( AIConcept_t concept, const char *pszModifiers, CBaseEntity *pTarget, AISpeechSelection_t *pSelection );
void SetPendingSpeech( AIConcept_t concept, AI_Response &Response );
void ClearPendingSpeech();
bool HasPendingSpeech() { return !m_PendingConcept.empty(); }
//---------------------------------
bool CanPlaySentence( bool fDisregardState );
int PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener );
//---------------------------------
void DeferAllIdleSpeech( float flDelay = -1, CAI_BaseNPC *pIgnore = NULL );
//---------------------------------
bool IsOkToSpeak( ConceptCategory_t category, bool fRespondingToPlayer = false );
//---------------------------------
bool IsOkToSpeak( void );
bool IsOkToCombatSpeak( void );
bool IsOkToSpeakInResponseToPlayer( void );
bool ShouldSpeakRandom( AIConcept_t concept, int iChance );
bool IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer = false );
virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
void ModifyOrAppendCriteria( AI_CriteriaSet& set );
//---------------------------------
float GetTimePlayerStaring() { return ( m_flTimePlayerStartStare != 0 ) ? gpGlobals->curtime - m_flTimePlayerStartStare : 0; }
//---------------------------------
// NPC Event Response System
virtual bool CanRespondToEvent( const char *ResponseConcept );
virtual bool RespondedTo( const char *ResponseConcept, bool bForce, bool bCancelScene );
//---------------------------------
void OnSpokeConcept( AIConcept_t concept, AI_Response *response );
void OnStartSpeaking();
// Inputs
virtual void InputIdleRespond( inputdata_t &inputdata ) {};
void InputSpeakResponseConcept( inputdata_t &inputdata );
virtual bool SpeakMapmakerInterruptConcept( string_t iszConcept );
void DisplayDeathMessage( void );
virtual const char *GetDeathMessageText( void ) { return "GAMEOVER_ALLY"; }
void InputMakeGameEndAlly( inputdata_t &inputdata );
void InputMakeRegularAlly( inputdata_t &inputdata );
void InputAnswerQuestion( inputdata_t &inputdata );
void InputAnswerQuestionHello( inputdata_t &inputdata );
void InputEnableSpeakWhileScripting( inputdata_t &inputdata );
void InputDisableSpeakWhileScripting( inputdata_t &inputdata );
void AnswerQuestion( CAI_PlayerAlly *pQuestioner, int iQARandomNum, bool bAnsweringHello );
protected:
#ifdef HL2_DLL
// Health regeneration for friendly allies
virtual bool ShouldRegenerateHealth( void ) { return ( Classify() == CLASS_PLAYER_ALLY_VITAL ); }
#endif
inline bool CanSpeakWhileScripting();
// Whether we are a vital ally (useful for wrting Classify() for classes that are only sometimes vital,
// such as the Lone Vort in Ep2.) The usual means by which any other function should determine if a character
// is vital is to determine Classify() == CLASS_PLAYER_ALLY_VITAL. Do not use this function outside that
// context.
inline bool IsGameEndAlly( void ) { return m_bGameEndAlly; }
//-----------------------------------------------------
// Conditions, Schedules, Tasks
//-----------------------------------------------------
enum
{
SCHED_TALKER_SPEAK_PENDING_IDLE = BaseClass::NEXT_SCHEDULE,
SCHED_TALKER_SPEAK_PENDING_ALERT,
SCHED_TALKER_SPEAK_PENDING_COMBAT,
NEXT_SCHEDULE,
TASK_TALKER_SPEAK_PENDING = BaseClass::NEXT_TASK,
NEXT_TASK,
COND_TALKER_CLIENTUNSEEN = BaseClass::NEXT_CONDITION,
COND_TALKER_PLAYER_DEAD,
COND_TALKER_PLAYER_STARING,
NEXT_CONDITION
};
private:
void SetCategoryDelay( ConceptCategory_t category, float minDelay, float maxDelay = 0.0 ) { m_ConceptCategoryTimers[category].Set( minDelay, maxDelay ); }
bool CategoryDelayExpired( ConceptCategory_t category ) { return m_ConceptCategoryTimers[category].Expired(); }
friend class CAI_AllySpeechManager;
//---------------------------------
AI_Response m_PendingResponse;
std::string m_PendingConcept;
float m_TimePendingSet;
//---------------------------------
EHANDLE m_hTalkTarget; // who to look at while talking
float m_flNextRegenTime;
float m_flTimePlayerStartStare;
EHANDLE m_hPotentialSpeechTarget; // NPC to tell the response rules about when trying to find a response to talk to them with
float m_flNextIdleSpeechTime;
int m_iQARandomNumber;
//---------------------------------
CSimpleSimTimer m_ConceptCategoryTimers[3];
//---------------------------------
CHandle<CAI_SpeechFilter> m_hSpeechFilter;
bool m_bGameEndAlly;
bool m_bCanSpeakWhileScripting; // Allows mapmakers to override NPC_STATE_SCRIPT or IsScripting() for responses.
float m_flTimeLastRegen; // Last time I regenerated a bit of health.
float m_flHealthAccumulator; // Counterpart to the damage accumulator in CBaseCombatCharacter. So ally health regeneration is accurate over time.
#ifdef _XBOX
protected:
#endif
DECLARE_DATADESC();
protected:
DEFINE_CUSTOM_AI;
};
bool CAI_PlayerAlly::CanSpeakWhileScripting()
{
return m_bCanSpeakWhileScripting;
}
//-----------------------------------------------------------------------------
#endif // AI_PLAYERALLY_H
|