aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/ai_playerally.h
blob: bd1e5ab4662be0915335e6c5d8e40ce759f3d57d (plain) (blame)
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