aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/hl2/npc_playercompanion.h
blob: f1c3b1f39a812c16abe0ad6867390064e7f3e80f (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
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Base class for humanoid NPCs intended to fight along side player in close
// environments
//
//=============================================================================//

#ifndef NPC_PLAYERCOMPANION_H
#define NPC_PLAYERCOMPANION_H

#include "ai_playerally.h"

#include "ai_behavior_follow.h"
#include "ai_behavior_standoff.h"
#include "ai_behavior_assault.h"
#include "ai_behavior_lead.h"
#include "ai_behavior_actbusy.h"
#include "ai_behavior_fear.h"

#ifdef HL2_EPISODIC
#include "ai_behavior_operator.h"
#include "ai_behavior_passenger_companion.h"
#endif

#if defined( _WIN32 )
#pragma once
#endif

enum AIReadiness_t
{
	AIRL_PANIC = -2,
	AIRL_STEALTH = -1,
	AIRL_RELAXED = 0,
	AIRL_STIMULATED,
	AIRL_AGITATED,
};

enum AIReadinessUse_t
{
	AIRU_NEVER,
	AIRU_ALWAYS,
	AIRU_ONLY_PLAYER_SQUADMATES,
};


class CCompanionActivityRemap : public CActivityRemap
{
public:
	CCompanionActivityRemap( void ) : 
	  m_fUsageBits( 0 ),
	  m_readiness( AIRL_RELAXED ),
	  m_bAiming( false ),
	  m_bWeaponRequired( false ),
	  m_bInVehicle( false ) {} 

	// This bitfield maps which bits of data are being utilized by this data structure, since not all criteria
	// in the parsed file are essential.  You must add corresponding bits to the definitions below and maintain
	// their state in the parsing of the file, as well as check the bitfield before accessing the data.  This
	// could be encapsulated into this class, but we'll probably move away from this model and closer to something
	// more akin to the response rules -- jdw

	int				m_fUsageBits;

	AIReadiness_t	m_readiness;
	bool			m_bAiming;
	bool			m_bWeaponRequired;
	bool			m_bInVehicle;		// For future expansion, this needs to speak more to the exact seat, role, and vehicle
};

// Usage bits for remap "extra" parsing - if these bits are set, the associated data has changed
#define bits_REMAP_READINESS		(1<<0)
#define bits_REMAP_AIMING			(1<<1)
#define bits_REMAP_WEAPON_REQUIRED	(1<<2)
#define bits_REMAP_IN_VEHICLE		(1<<3)

// Readiness modes that only change due to mapmaker scripts
#define READINESS_MIN_VALUE			-2
#define READINESS_MODE_PANIC		-2
#define READINESS_MODE_STEALTH		-1

// Readiness modes that change normally
#define READINESS_VALUE_RELAXED		0.1f
#define READINESS_VALUE_STIMULATED	0.95f
#define READINESS_VALUE_AGITATED	1.0f

class CPhysicsProp;

//-----------------------------------------------------------------------------
//
// CLASS: CNPC_PlayerCompanion
//
//-----------------------------------------------------------------------------

class CNPC_PlayerCompanion : public CAI_PlayerAlly
{
	DECLARE_CLASS( CNPC_PlayerCompanion, CAI_PlayerAlly );

public:
	//---------------------------------
	bool			CreateBehaviors();
	void			Precache();
	void			Spawn();
	virtual void	SelectModel() {};

	virtual int		Restore( IRestore &restore );
	virtual void	DoCustomSpeechAI( void );

	//---------------------------------
	int 			ObjectCaps();
	bool 			ShouldAlwaysThink();

	Disposition_t	IRelationType( CBaseEntity *pTarget );
	
	bool			IsSilentSquadMember() const;

	//---------------------------------
	// Behavior
	//---------------------------------
	void 			GatherConditions();
	virtual void	PredictPlayerPush();
	void			BuildScheduleTestBits();

	CSound			*GetBestSound( int validTypes = ALL_SOUNDS );
	bool			QueryHearSound( CSound *pSound );
	bool			QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC = false );
	bool			ShouldIgnoreSound( CSound * );
	
	int 			SelectSchedule();

	virtual int 	SelectScheduleDanger();
	virtual int 	SelectSchedulePriorityAction();
	virtual int 	SelectScheduleNonCombat()			{ return SCHED_NONE; }
	virtual int 	SelectScheduleCombat();
	int 			SelectSchedulePlayerPush();

	virtual bool	CanReload( void );

	virtual bool	ShouldDeferToFollowBehavior();
	bool			ShouldDeferToPassengerBehavior( void );

	bool			IsValidReasonableFacing( const Vector &vecSightDir, float sightDist );
	
	int 			TranslateSchedule( int scheduleType );
	
	void 			StartTask( const Task_t *pTask );
	void 			RunTask( const Task_t *pTask );
	
	Activity		TranslateActivityReadiness( Activity activity );
	Activity		NPC_TranslateActivity( Activity eNewActivity );
	void 			HandleAnimEvent( animevent_t *pEvent );
	bool			HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);

	int				GetSoundInterests();
	
	void 			Touch( CBaseEntity *pOther );

	virtual bool	IgnorePlayerPushing( void );

	void			ModifyOrAppendCriteria( AI_CriteriaSet& set );
	void			Activate( void );

	void			PrepareReadinessRemap( void );
	
	virtual bool	IsNavigationUrgent( void );

	//---------------------------------
	// Readiness
	//---------------------------------

protected:
	virtual bool	IsReadinessCapable();
	bool			IsReadinessLocked() { return gpGlobals->curtime < m_flReadinessLockedUntil; }
	void			AddReadiness( float flAdd, bool bOverrideLock = false );
	void			SubtractReadiness( float flAdd, bool bOverrideLock = false );
	void			SetReadinessValue( float flSet );
	void			SetReadinessSensitivity( float flSensitivity ) { m_flReadinessSensitivity = flSensitivity; }
	virtual void	UpdateReadiness();
	virtual float	GetReadinessDecay();
	bool			IsInScriptedReadinessState( void ) { return (m_flReadiness < 0 ); }

	CUtlVector< CCompanionActivityRemap > m_activityMappings;

public:
	float			GetReadinessValue()	{ return m_flReadiness; }
	int				GetReadinessLevel();
	void			SetReadinessLevel( int iLevel, bool bOverrideLock, bool bSlam );
	void			LockReadiness( float duration = -1.0f ); // Defaults to indefinitely locked
	void			UnlockReadiness( void );

	virtual			void ReadinessLevelChanged( int iPriorLevel ) { 	}

	void			InputGiveWeapon( inputdata_t &inputdata );

#ifdef HL2_EPISODIC
	//---------------------------------
	// Vehicle passenger
	//---------------------------------
	void			InputEnterVehicle( inputdata_t &inputdata );
	void			InputEnterVehicleImmediately( inputdata_t &inputdata );
	void			InputCancelEnterVehicle( inputdata_t &inputdata );
	void			InputExitVehicle( inputdata_t &inputdata );
	bool			CanEnterVehicle( void );
	bool			CanExitVehicle( void );
	void			EnterVehicle( CBaseEntity *pEntityVehicle, bool bImmediately );
	virtual bool	ExitVehicle( void );

	virtual void	UpdateEfficiency( bool bInPVS );
	virtual bool	IsInAVehicle( void ) const;
	virtual	IServerVehicle *GetVehicle( void );
	virtual CBaseEntity *GetVehicleEntity( void );

	virtual bool CanRunAScriptedNPCInteraction( bool bForced = false );
	virtual bool IsAllowedToDodge( void );

#endif // HL2_EPISODIC

public:

	virtual void	OnPlayerKilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );

	//---------------------------------
	//---------------------------------
	bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs );

	//---------------------------------
	// Aiming
	//---------------------------------
	CBaseEntity		*GetAimTarget() { return m_hAimTarget; }
	void			SetAimTarget( CBaseEntity *pTarget );
	void			StopAiming( char *pszReason = NULL );
	bool			FindNewAimTarget();
	void			OnNewLookTarget();
	bool			ShouldBeAiming();
	virtual bool	IsAllowedToAim();
	bool			HasAimLOS( CBaseEntity *pAimTarget );
	void			AimGun();
	CBaseEntity		*GetAlternateMoveShootTarget();

	//---------------------------------
	// Combat
	//---------------------------------
	virtual void 	LocateEnemySound() {};

	bool			IsValidEnemy( CBaseEntity *pEnemy );

	bool 			IsSafeFromFloorTurret( const Vector &vecLocation, CBaseEntity *pTurret );

	bool			ShouldMoveAndShoot( void );
	void			OnUpdateShotRegulator();

	void			DecalTrace( trace_t *pTrace, char const *decalName );
	bool 			FCanCheckAttacks();
	Vector 			GetActualShootPosition( const Vector &shootOrigin );
	WeaponProficiency_t CalcWeaponProficiency( CBaseCombatWeapon *pWeapon );
	bool			ShouldLookForBetterWeapon();
	bool			Weapon_CanUse( CBaseCombatWeapon *pWeapon );
	void			Weapon_Equip( CBaseCombatWeapon *pWeapon );
	void			PickupWeapon( CBaseCombatWeapon *pWeapon );
	
	bool 			FindCoverPos( CBaseEntity *pEntity, Vector *pResult);
	bool			FindCoverPosInRadius( CBaseEntity *pEntity, const Vector &goalPos, float coverRadius, Vector *pResult );
	bool			FindCoverPos( CSound *pSound, Vector *pResult );
	bool			FindMortarCoverPos( CSound *pSound, Vector *pResult );
	bool 			IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition );

	bool			IsEnemyTurret() { return ( GetEnemy() && IsTurret(GetEnemy()) ); }
	
	static bool		IsMortar( CBaseEntity *pEntity );
	static bool		IsSniper( CBaseEntity *pEntity );
	static bool		IsTurret(  CBaseEntity *pEntity );
	static bool		IsGunship( CBaseEntity *pEntity );
	
	//---------------------------------
	// Damage handling
	//---------------------------------
	int 			OnTakeDamage_Alive( const CTakeDamageInfo &info );
	void 			OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity *pAttacker );

	//---------------------------------
	// Hints
	//---------------------------------
	bool			FValidateHintType ( CAI_Hint *pHint );

	//---------------------------------
	// Navigation
	//---------------------------------
	bool			IsValidMoveAwayDest( const Vector &vecDest );
	bool 			ValidateNavGoal();
	bool 			OverrideMove( float flInterval );				// Override to take total control of movement (return true if done so)
	bool			MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost );
	float			GetIdealSpeed() const;
	float			GetIdealAccel() const;
	bool			OnObstructionPreSteer( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult );

	//---------------------------------
	// Inputs
	//---------------------------------
	void 			InputOutsideTransition( inputdata_t &inputdata );
	void			InputSetReadinessPanic( inputdata_t &inputdata );
	void			InputSetReadinessStealth( inputdata_t &inputdata );
	void			InputSetReadinessLow( inputdata_t &inputdata );
	void			InputSetReadinessMedium( inputdata_t &inputdata );
	void			InputSetReadinessHigh( inputdata_t &inputdata );
	void			InputLockReadiness( inputdata_t &inputdata );
#if HL2_EPISODIC
	void			InputClearAllOuputs( inputdata_t &inputdata ); ///< annihilate every output on this npc
#endif

	bool			AllowReadinessValueChange( void );

protected:
	//-----------------------------------------------------
	// Conditions, Schedules, Tasks
	//-----------------------------------------------------
	enum
	{
		COND_PC_HURTBYFIRE = BaseClass::NEXT_CONDITION,
		COND_PC_SAFE_FROM_MORTAR,
		COND_PC_BECOMING_PASSENGER,
		NEXT_CONDITION,

		SCHED_PC_COWER = BaseClass::NEXT_SCHEDULE,
		SCHED_PC_MOVE_TOWARDS_COVER_FROM_BEST_SOUND,
		SCHED_PC_TAKE_COVER_FROM_BEST_SOUND,
		SCHED_PC_FLEE_FROM_BEST_SOUND,
		SCHED_PC_FAIL_TAKE_COVER_TURRET,
		SCHED_PC_FAKEOUT_MORTAR,
		SCHED_PC_GET_OFF_COMPANION,
		NEXT_SCHEDULE,

		TASK_PC_WAITOUT_MORTAR = BaseClass::NEXT_TASK,
		TASK_PC_GET_PATH_OFF_COMPANION,
		NEXT_TASK,
	};

private:
	void SetupCoverSearch( CBaseEntity *pEntity );
	void CleanupCoverSearch();

	//-----------------------------------------------------
	
	bool			m_bMovingAwayFromPlayer;
	bool			m_bWeightPathsInCover;

	enum eCoverType
	{
		CT_NORMAL,
		CT_TURRET,
		CT_MORTAR
	};

	static eCoverType	gm_fCoverSearchType;
	static bool 		gm_bFindingCoverFromAllEnemies;

	CSimpleSimTimer		m_FakeOutMortarTimer;

	// Derived classes should not use the expresser directly
	virtual CAI_Expresser *GetExpresser()	{ return BaseClass::GetExpresser(); }

protected:
	//-----------------------------------------------------

	virtual CAI_FollowBehavior &GetFollowBehavior( void ) { return m_FollowBehavior; }

	CAI_AssaultBehavior				m_AssaultBehavior;
	CAI_FollowBehavior				m_FollowBehavior;
	CAI_StandoffBehavior			m_StandoffBehavior;
	CAI_LeadBehavior				m_LeadBehavior;
	CAI_ActBusyBehavior				m_ActBusyBehavior;
#ifdef HL2_EPISODIC
	CAI_OperatorBehavior			m_OperatorBehavior;
	CAI_PassengerBehaviorCompanion	m_PassengerBehavior;
	CAI_FearBehavior				m_FearBehavior;
#endif
	//-----------------------------------------------------

	bool	ShouldAlwaysTransition( void );

	// Readiness is a value that's fed by various events in the NPC's AI. It is used
	// to make decisions about what type of posture the NPC should be in (relaxed, agitated).
	// It is not used to make decisions about what to do in the AI. 
	float m_flReadiness;
	float m_flReadinessSensitivity;
	bool m_bReadinessCapable;
	float m_flReadinessLockedUntil;
	float	m_fLastBarrelExploded;
	float	m_fLastPlayerKill;
	int		m_iNumConsecutiveBarrelsExploded;  // Companions keep track of the # of consecutive barrels exploded by the player and speaks a response as it increases
	int		m_iNumConsecutivePlayerKills;  // Alyx keeps track of the # of consecutive kills by the player and speaks a response as it increases

	//-----------------------------------------------------

	float m_flBoostSpeed;

	//-----------------------------------------------------
	
	CSimpleSimTimer m_AnnounceAttackTimer;

	//-----------------------------------------------------

	EHANDLE m_hAimTarget;

#ifdef HL2_EPISODIC
	CHandle<CPhysicsProp>	m_hFlare;
#endif // HL2_EPISODIC

	//-----------------------------------------------------

	static string_t gm_iszMortarClassname;
	static string_t gm_iszFloorTurretClassname;
	static string_t gm_iszGroundTurretClassname;
	static string_t gm_iszShotgunClassname;
	static string_t	gm_iszRollerMineClassname;

	//-----------------------------------------------------

	void	InputEnableAlwaysTransition( inputdata_t &inputdata );
	void	InputDisableAlwaysTransition( inputdata_t &inputdata );
	bool	m_bAlwaysTransition;
	bool	m_bDontPickupWeapons;

	void	InputEnableWeaponPickup( inputdata_t &inputdata );
	void	InputDisableWeaponPickup( inputdata_t &inputdata );

	COutputEvent	m_OnWeaponPickup;

	CStopwatch		m_SpeechWatch_PlayerLooking;

	DECLARE_DATADESC();
	DEFINE_CUSTOM_AI;
};

// Used for quick override move searches against certain types of entities
void OverrideMoveCache_ForceRepopulateList( void );
CBaseEntity *OverrideMoveCache_FindTargetsInRadius( CBaseEntity *pFirstEntity, const Vector &vecOrigin, float flRadius );
void OverrideMoveCache_LevelInitPreEntity( void );
void OverrideMoveCache_LevelShutdownPostEntity( void );

#endif // NPC_PLAYERCOMPANION_H