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
|