aboutsummaryrefslogtreecommitdiff
path: root/sp/src/game/server/hl2/npc_alyx_episodic.h
blob: 5657dae7b6377f5476e414e2989701ee0465504d (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
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:		Base combat character with no AI
//
//=====================================================================================//

#include "ai_baseactor.h"
#include "npc_playercompanion.h"
#include "ai_behavior_holster.h"
#include "ai_behavior_functank.h"
#include "soundenvelope.h"

extern ConVar npc_alyx_readiness;

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

	// fast class list
	CNPC_Alyx *m_pNext;

	virtual void	ModifyOrAppendCriteria( AI_CriteriaSet &set );

	bool ForceVehicleInteraction( const char *lpszInteractionName, CBaseCombatCharacter *pOther );

	CNPC_Alyx( );
	~CNPC_Alyx( );

	static CNPC_Alyx *GetAlyx( void );

	bool	CreateBehaviors();
	void	Spawn( void );
	void	Activate( void );
	void	StopLoopingSounds( void );
	void	SelectModel();
	void	Precache( void );
	void	SetupAlyxWithoutParent( void );
	void	CreateEmpTool( void );
	void	PrescheduleThink( void );
	void	GatherConditions();
	bool	ShouldPlayerAvoid( void );
	void	AnalyzeGunfireSound( CSound *pSound );
	bool	IsValidEnemy( CBaseEntity *pEnemy );
	void	Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
	void	Event_Killed( const CTakeDamageInfo &info );
	void	EnemyIgnited( CAI_BaseNPC *pVictim );
	void	CombineBallSocketed( int iNumBounces );
	void	AimGun( void );
	Vector  GetActualShootPosition( const Vector &shootOrigin );
	float	MaxYawSpeed( void );
	void	OnUpdateShotRegulator();
	bool	IsCrouchedActivity( Activity activity );
	bool	OnBeginMoveAndShoot();
	void	SpeakAttacking( void );

	virtual float	GetJumpGravity() const		{ return 1.8f; }

	// Crouching
	Vector  GetCrouchEyeOffset( void ) { return Vector(0,0,50); }
	Vector  GetCrouchGunOffset( void ) { return Vector(0,0,40); }
	bool	EnemyIsValidCrouchTarget( CBaseEntity *pEnemy );
	bool	Stand( void );
	bool	Crouch( void );
	void	DesireCrouch( void );

	// Custom AI
	void	DoCustomCombatAI( void );
	void	DoMobbedCombatAI( void );
	void	DoCustomSpeechAI( void );

	Disposition_t	IRelationType( CBaseEntity *pTarget );
	int				IRelationPriority( CBaseEntity *pTarget );

	CAI_FollowBehavior &GetFollowBehavior( void );

	Class_T Classify ( void );
	bool	FValidateHintType( CAI_Hint *pHint );
	int		ObjectCaps();
	void	HandleAnimEvent( animevent_t *pEvent );
	bool	FInViewCone( CBaseEntity *pEntity );
	bool	QuerySeeEntity( CBaseEntity *pEntity, bool bOnlyHateOrFearIfNPC = false );
	bool	CanSeeEntityInDarkness( CBaseEntity *pEntity );
	bool	IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition );
	Activity NPC_TranslateActivity ( Activity activity );
	bool	ShouldDeferToFollowBehavior();
	void	BuildScheduleTestBits();
	bool	ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior );
	int		SelectSchedule( void );
	int		SelectScheduleDanger( void );
	int		TranslateSchedule( int scheduleType );
	void	StartTask( const Task_t *pTask );
	void	RunTask( const Task_t *pTask );
	void	OnStateChange( NPC_STATE OldState, NPC_STATE NewState );
	float	LengthOfLastCombat( void ) const;
	// bool	IsNavigationUrgent();

	void	TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
	bool	CanBeHitByMeleeAttack( CBaseEntity *pAttacker );
	int		OnTakeDamage_Alive( const CTakeDamageInfo &info );
	bool	FCanCheckAttacks();
	float	GetAttackDamageScale( CBaseEntity *pVictim );


	bool	HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt);
	virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
	
	void	HolsterPistol();
	void	DrawPistol();
	void	Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL );

	void	SetEMPHolstered( bool bHolstered ) { m_bIsEMPHolstered = bHolstered; }
	bool	IsEMPHolstered() { return (!m_hEmpTool || m_hEmpTool->GetParent() != this || m_bIsEMPHolstered); }

	float	GetReadinessDecay() { return 60.0f; }
	virtual bool	IsAllowedToAim();

	virtual void	PainSound( const CTakeDamageInfo &info );
    virtual void	DeathSound( const CTakeDamageInfo &info );

	// Hacking and object interaction
	void	SearchForInteractTargets();
	bool	IsValidInteractTarget( CBaseEntity *pTarget );
	bool	CanInteractWithTarget( CBaseEntity *pTarget );
	void	SetInteractTarget( CBaseEntity *pTarget );
	bool	HasInteractTarget() { return m_hHackTarget != NULL; }
	CBaseEntity *GetInteractTarget() { return m_hHackTarget; }
	void	EmpZapTarget( CBaseEntity *pTarget );

	virtual void OnSeeEntity( CBaseEntity *pEntity );

	void InputAllowInteraction( inputdata_t &inputdata )
	{
		m_bInteractionAllowed = true;
	}
	void InputDisallowInteraction( inputdata_t &inputdata )
	{
		m_bInteractionAllowed = false;
	}
	void InputAllowDarknessSpeech( inputdata_t &inputdata )
	{
		m_bDarknessSpeechAllowed = inputdata.value.Bool();
	}
	void InputGiveEMP( inputdata_t &inputdata );
	void InputVehiclePunted( inputdata_t &inputdata );
	void InputOutsideTransition( inputdata_t &inputdata );

	virtual void	OnGivenWeapon( CBaseCombatWeapon *pNewWeapon );
	virtual void	OnChangeActiveWeapon( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon );
	virtual void	Weapon_Equip( CBaseCombatWeapon *pWeapon );
	virtual bool	Weapon_CanUse( CBaseCombatWeapon *pWeapon );

	// Blinding
	virtual void PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot );
	void		 CheckBlindedByFlare( void );
	bool		 CanBeBlindedByFlashlight( bool bCheckLightSources );
	bool		 PlayerFlashlightOnMyEyes( CBasePlayer *pPlayer );
	bool		 BlindedByFlare( void );
	bool		 CanReload( void );

	virtual bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs );
	virtual void OnSelectedLookTarget( AILookTargetArgs_t *pArgs );
	virtual bool IsReadinessCapable( void );

	virtual	void ReadinessLevelChanged( int iPriorLevel );

	bool IsAllowedToInteract();
	virtual void	BarnacleDeathSound( void );

	virtual const char *GetDeathMessageText( void ) { return "GAMEOVER_ALYXDEAD"; }

	PassengerState_e	GetPassengerState( void );

	void				Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
	bool				PlayerInSpread( const Vector &sourcePos, const Vector &targetPos, float flSpread, float maxDistOffCenter, bool ignoreHatedPlayers );

private:
	EHANDLE	m_hEmpTool;
	EHANDLE	m_hHackTarget;
	CHandle<CAI_Hint>	m_hStealthLookTarget;
	bool	m_bInteractionAllowed;
	float	m_fTimeNextSearchForInteractTargets;
	bool	m_bDarknessSpeechAllowed;
	bool	m_bIsEMPHolstered;
	bool	m_bIsFlashlightBlind;
	float	m_fStayBlindUntil;
	float	m_flDontBlindUntil;
	bool	m_bSpokeLostPlayerInDarkness;
	bool	m_bPlayerFlashlightState;
	bool	m_bHadCondSeeEnemy;
	string_t m_iszCurrentBlindScene;
	float	m_fTimeUntilNextDarknessFoundPlayer;
	float   m_fCombatStartTime;
	float	m_fCombatEndTime;
	float	m_flNextCrouchTime;

	CSoundPatch		*m_sndDarknessBreathing;

	// Speech timers
	// Theoretically, these shouldn't be needed. Instead, each response
	// should prevent the concept being spoken for the desired time. But
	// until the responses exists, Alyx will spam the response rules forever,
	// so these timers stop that.
	CRandStopwatch  m_SpeechWatch_LostPlayer;
	CSimpleSimTimer m_SpeechTimer_HeardSound;
	CRandStopwatch	m_SpeechWatch_SoundDelay;
	CRandStopwatch	m_SpeechWatch_BreathingRamp;
	CRandStopwatch	m_SpeechWatch_FoundPlayer;

	CAI_MoveMonitor m_MoveMonitor;

	enum WeaponType_t
	{
		WT_NONE,
		WT_ALYXGUN,
		WT_SMG1,
		WT_SHOTGUN,
		WT_AR2,
		WT_OTHER,
	};

	int m_WeaponType;

	bool m_bShouldHaveEMP;

	CAI_FuncTankBehavior	m_FuncTankBehavior;

	COutputEvent			m_OnFinishInteractWithObject;
	COutputEvent			m_OnPlayerUse;

	bool	RunningPassengerBehavior( void );

	WeaponType_t ComputeWeaponType( CBaseEntity *pWeapon = NULL );
	WeaponType_t GetWeaponType() { return (WeaponType_t)m_WeaponType; }
	bool HasShotgun()	{ Assert( m_WeaponType == ComputeWeaponType() ); return ( m_WeaponType == WT_SHOTGUN ); }
	bool HasAlyxgun()	{ Assert( m_WeaponType == ComputeWeaponType() ); return ( m_WeaponType == WT_ALYXGUN ); }
	bool HasAR2()		{ Assert( m_WeaponType == ComputeWeaponType() ); return ( m_WeaponType == WT_AR2 ); }

private:
	enum
	{
		COND_ALYX_HAS_INTERACT_TARGET = BaseClass::NEXT_CONDITION,
		COND_ALYX_NO_INTERACT_TARGET,
		COND_ALYX_CAN_INTERACT_WITH_TARGET, // Hack target is in a suitable state and location to hack
		COND_ALYX_CAN_NOT_INTERACT_WITH_TARGET,
		COND_ALYX_PLAYER_TURNED_ON_FLASHLIGHT,
		COND_ALYX_PLAYER_TURNED_OFF_FLASHLIGHT,
		COND_ALYX_PLAYER_FLASHLIGHT_EXPIRED,
		COND_ALYX_IN_DARK,					// lights are out and she can't see
	};

	enum
	{
		SCHED_ALYX_PREPARE_TO_INTERACT_WITH_TARGET = BaseClass::NEXT_SCHEDULE,
		SCHED_ALYX_WAIT_TO_INTERACT_WITH_TARGET,
		SCHED_ALYX_INTERACT_WITH_TARGET,
		SCHED_ALYX_INTERACTION_INTERRUPTED,
		SCHED_ALYX_FINISH_INTERACTING_WITH_TARGET,
		SCHED_ALYX_HOLSTER_EMP,
		SCHED_ALYX_ALERT_FACE_AWAYFROM_BESTSOUND,
		SCHED_ALYX_RANGE_ATTACK1,
		SCHED_ALYX_ALERT_REACT_TO_COMBAT_SOUND,
		SCHED_ALYX_COMBAT_FACE,
		SCHED_ALYX_WAKE_ANGRY,
		SCHED_ALYX_NEW_WEAPON,
		SCHED_ALYX_IDLE_STAND,
		SCHED_ALYX_ALERT_FACE_BESTSOUND,
		SCHED_ALYX_FALL_TO_GROUND,
	};

	enum 
	{
		
		TASK_ALYX_BEGIN_INTERACTION = BaseClass::NEXT_TASK,
		TASK_ALYX_COMPLETE_INTERACTION,
		TASK_ALYX_ANNOUNCE_HACK,
		TASK_ALYX_GET_PATH_TO_INTERACT_TARGET,
		TASK_ALYX_WAIT_HACKING,
		TASK_ALYX_HOLSTER_PISTOL,
		TASK_ALYX_DRAW_PISTOL,
		TASK_ALYX_HOLSTER_AND_DESTROY_PISTOL,
		TASK_ALYX_BUILD_COMBAT_FACE_PATH,
		TASK_ALYX_SET_IDLE_ACTIVITY,
		TASK_ALYX_FALL_TO_GROUND,
	};

	DECLARE_DATADESC();
	DEFINE_CUSTOM_AI;
};