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.
289 lines
9.5 KiB
289 lines
9.5 KiB
//========= 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; |
|
};
|
|
|