You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
9.5 KiB
290 lines
9.5 KiB
5 years ago
|
//========= 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;
|
||
|
};
|