Optional npc_helicopter damage and stealth changes

This commit is contained in:
ALLEN-PC\acj30 2025-01-24 10:59:39 -06:00 committed by Blixibon
parent fbb7769acd
commit 3f64380755
2 changed files with 232 additions and 4 deletions

View File

@ -42,6 +42,10 @@ virtual void NullThink( void );
ConVar g_debug_basehelicopter( "g_debug_basehelicopter", "0", FCVAR_CHEAT );
#ifdef MAPBASE
ConVar g_helicopter_use_sight_condition( "g_helicopter_use_sight_condition", "0", 0, "If enabled, helicopters will use the AI sight condition instead of just checking if there's a clear path to the player. This prevents them from cheating." );
#endif
//---------------------------------------------------------
//---------------------------------------------------------
// TODOs
@ -702,7 +706,11 @@ void CBaseHelicopter::UpdateEnemy()
{
CBaseEntity *pEnemy = GetEnemy();
GatherEnemyConditions( pEnemy );
#ifdef MAPBASE
if ( (g_helicopter_use_sight_condition.GetBool() && pEnemy->Classify() != CLASS_BULLSEYE) ? HasCondition( COND_SEE_ENEMY ) : FVisible( pEnemy ) )
#else
if ( FVisible( pEnemy ) )
#endif
{
if (m_flLastSeen < gpGlobals->curtime - 2)
{

View File

@ -44,6 +44,7 @@
#ifdef MAPBASE
#include "filters.h"
#include "ai_hint.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
@ -561,6 +562,13 @@ private:
// Death, etc.
void InputSelfDestruct( inputdata_t &inputdata );
#ifdef MAPBASE
// This is identical to SelfDestruct, except the helicopter won't throw out chunks while flying to a crash point.
// This input is meant to be used when the pilot is killed and there's nothing wrong with the helicopter itself.
// If there are no crash points, the helicopter will explode in place as normal.
void InputSelfDestructNoFX( inputdata_t &inputdata );
#endif
// Enemy visibility check
CBaseEntity *FindTrackBlocker( const Vector &vecViewPoint, const Vector &vecTargetPos );
@ -584,6 +592,7 @@ private:
// Various states of the helicopter firing...
bool PoseGunTowardTargetDirection( const Vector &vTargetDir );
float GetGunPoseSpeed() const;
// Compute the position to fire at (vehicle + non-vehicle case)
void ComputeFireAtPosition( Vector *pVecActualTargetPosition );
@ -595,6 +604,17 @@ private:
bool DoGunFiring( const Vector &vBasePos, const Vector &vGunDir, const Vector &vecFireAtPosition );
void FireElectricityGun( );
#ifdef MAPBASE
// Idle aiming
bool FindIdleAimTarget();
bool ValidIdleAimTarget( CBaseEntity *pAimTarget );
bool FValidateHintType( CAI_Hint *pHint );
inline CBaseEntity *GetIdleAimTarget() const { return m_hIdleAimTarget; }
inline void SetIdleAimTarget( CBaseEntity *pNewAimTarget ) { m_hIdleAimTarget = pNewAimTarget; }
#endif
// Chooses a point within the circle of death to fire in
void PickDirectionToCircleOfDeath( const Vector &vBasePos, const Vector &vecFireAtPosition, Vector *pResult );
@ -784,6 +804,17 @@ private:
bool m_bBombsExplodeOnContact;
bool m_bNonCombat;
#ifdef MAPBASE
bool m_bIdleAimAround;
EHANDLE m_hIdleAimTarget;
Vector m_vecIdleAimDir;
float m_flNextIdleAimTime;
bool m_bDisableSmokeTrails;
bool m_bDisableCorpses;
bool m_bDisableExplosions;
#endif
int m_nNearShots;
int m_nMaxNearShots;
@ -923,6 +954,15 @@ BEGIN_DATADESC( CNPC_AttackHelicopter )
#ifdef MAPBASE
DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ),
DEFINE_KEYFIELD( m_bIdleAimAround, FIELD_BOOLEAN, "IdleAimAround" ),
DEFINE_FIELD( m_hIdleAimTarget, FIELD_EHANDLE ),
DEFINE_FIELD( m_vecIdleAimDir, FIELD_VECTOR ),
DEFINE_FIELD( m_flNextIdleAimTime, FIELD_TIME ),
DEFINE_KEYFIELD( m_bDisableSmokeTrails, FIELD_BOOLEAN, "DisableSmokeTrails" ),
DEFINE_KEYFIELD( m_bDisableCorpses, FIELD_BOOLEAN, "DisableCorpses" ),
DEFINE_KEYFIELD( m_bDisableExplosions, FIELD_BOOLEAN, "DisableExplosions" ),
#endif
DEFINE_FIELD( m_hCrashPoint, FIELD_EHANDLE ),
@ -959,6 +999,9 @@ BEGIN_DATADESC( CNPC_AttackHelicopter )
DEFINE_INPUTFUNC( FIELD_VOID, "EnablePathVisibilityTests", InputEnablePathVisibilityTests ),
DEFINE_INPUTFUNC( FIELD_VOID, "SelfDestruct", InputSelfDestruct ),
#ifdef MAPBASE
DEFINE_INPUTFUNC( FIELD_VOID, "SelfDestructNoFX", InputSelfDestructNoFX ),
#endif
DEFINE_THINKFUNC( BlinkLightsThink ),
DEFINE_THINKFUNC( SpotlightThink ),
@ -1709,6 +1752,21 @@ void CNPC_AttackHelicopter::InputSelfDestruct( inputdata_t &inputdata )
TakeDamage( info );
}
#ifdef MAPBASE
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CNPC_AttackHelicopter::InputSelfDestructNoFX( inputdata_t &inputdata )
{
m_bDisableSmokeTrails = true;
m_bDisableCorpses = true;
m_bDisableExplosions = true;
m_lifeState = LIFE_ALIVE; // Force to die properly.
CTakeDamageInfo info( this, this, Vector(0, 0, 1), WorldSpaceCenter(), GetMaxHealth(), CLASS_MISSILE );
TakeDamage( info );
}
#endif
//-----------------------------------------------------------------------------
// For scripted times where it *has* to shoot
//-----------------------------------------------------------------------------
@ -2308,21 +2366,23 @@ bool CNPC_AttackHelicopter::PoseGunTowardTargetDirection( const Vector &vTargetD
}
}
float flPoseSpeed = GetGunPoseSpeed();
if (angles.x > m_angGun.x)
{
m_angGun.x = MIN( angles.x, m_angGun.x + 12 );
m_angGun.x = MIN( angles.x, m_angGun.x + flPoseSpeed );
}
if (angles.x < m_angGun.x)
{
m_angGun.x = MAX( angles.x, m_angGun.x - 12 );
m_angGun.x = MAX( angles.x, m_angGun.x - flPoseSpeed );
}
if (angles.y > m_angGun.y)
{
m_angGun.y = MIN( angles.y, m_angGun.y + 12 );
m_angGun.y = MIN( angles.y, m_angGun.y + flPoseSpeed );
}
if (angles.y < m_angGun.y)
{
m_angGun.y = MAX( angles.y, m_angGun.y - 12 );
m_angGun.y = MAX( angles.y, m_angGun.y - flPoseSpeed );
}
SetPoseParameter( m_poseWeapon_Pitch, -m_angGun.x );
@ -2332,6 +2392,20 @@ bool CNPC_AttackHelicopter::PoseGunTowardTargetDirection( const Vector &vTargetD
}
//------------------------------------------------------------------------------
// Various states of the helicopter firing...
//------------------------------------------------------------------------------
float CNPC_AttackHelicopter::GetGunPoseSpeed() const
{
#ifdef MAPBASE
if (m_bIdleAimAround && !GetEnemy())
return 4.0f;
#endif
return 12.0f;
}
//------------------------------------------------------------------------------
// Compute the enemy position (non-vehicle case)
//------------------------------------------------------------------------------
@ -2426,6 +2500,87 @@ bool CNPC_AttackHelicopter::DoGunIdle( const Vector &vGunDir, const Vector &vTar
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Finds an idle aim target. Based on CNPC_PlayerCompanion code
//-----------------------------------------------------------------------------
bool CNPC_AttackHelicopter::FindIdleAimTarget()
{
CAI_Hint *pHint;
CHintCriteria hintCriteria;
CBaseEntity *pPriorAimTarget = GetIdleAimTarget();
hintCriteria.SetHintType( HINT_WORLD_VISUALLY_INTERESTING );
hintCriteria.SetFlag( bits_HINT_NODE_VISIBLE | bits_HINT_NODE_IN_VIEWCONE | bits_HINT_NPC_IN_NODE_FOV | bits_HINT_NODE_USE_GROUP | bits_HINT_NODE_NEAREST );
hintCriteria.AddIncludePosition( GetAbsOrigin(), CHOPPER_MAX_GUN_DIST );
pHint = CAI_HintManager::FindHint( this, hintCriteria );
if( pHint )
{
if ( !ValidIdleAimTarget( pHint ) )
{
return false;
}
if ( pHint != pPriorAimTarget )
{
// Notify of the change.
SetIdleAimTarget( pHint );
return true;
}
}
// Didn't find an aim target, or found the same one.
return false;
}
//-----------------------------------------------------------------------------
// Finds an idle aim target. Based on CNPC_PlayerCompanion code
//-----------------------------------------------------------------------------
bool CNPC_AttackHelicopter::ValidIdleAimTarget( CBaseEntity *pAimTarget )
{
Vector vecTargetOrigin = pAimTarget->GetAbsOrigin();
Vector vBasePos;
GetAttachment( m_nGunBaseAttachment, vBasePos );
if ( vecTargetOrigin.z > vBasePos.z || (vecTargetOrigin - vBasePos).Length() < 128.0f )
{
// Too close!
return false;
}
trace_t tr;
UTIL_TraceLine( vBasePos, vecTargetOrigin, MASK_BLOCKLOS, this, COLLISION_GROUP_NONE, &tr );
if ( tr.fraction < 1.0f && tr.m_pEnt != pAimTarget )
{
// No LOS
return false;
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CNPC_AttackHelicopter::FValidateHintType( CAI_Hint *pHint )
{
switch( pHint->HintType() )
{
case HINT_WORLD_VISUALLY_INTERESTING:
return true;
break;
default:
break;
}
return BaseClass::FValidateHintType( pHint );
}
#endif
//------------------------------------------------------------------------------
// How easy is the target to hit?
//------------------------------------------------------------------------------
@ -3716,12 +3871,20 @@ int CNPC_AttackHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info )
}
}
#ifdef MAPBASE
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) && !m_bDisableSmokeTrails )
#else
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) )
#endif
{
AddSmokeTrail( info.GetDamagePosition() );
}
#ifdef MAPBASE
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_CORPSES ) && !m_bDisableCorpses )
#else
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_CORPSES ) )
#endif
{
if ( nPrevHealth != GetMaxHealth() )
{
@ -3729,7 +3892,11 @@ int CNPC_AttackHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info )
}
}
#ifdef MAPBASE
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) && !m_bDisableExplosions )
#else
if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) )
#endif
{
ExplodeAndThrowChunk( info.GetDamagePosition() );
}
@ -3978,7 +4145,11 @@ void CNPC_AttackHelicopter::PrescheduleThink( void )
SetDesiredPosition( GetCrashPoint()->WorldSpaceCenter() );
}
#ifdef MAPBASE
if ( !m_bDisableExplosions && random->RandomInt( 0, 4 ) == 0 )
#else
if ( random->RandomInt( 0, 4 ) == 0 )
#endif
{
Vector explodePoint;
CollisionProp()->RandomPointInBounds( Vector(0.25,0.25,0.25), Vector(0.75,0.75,0.75), &explodePoint );
@ -5003,6 +5174,55 @@ void CNPC_AttackHelicopter::Hunt( void )
#endif
}
#ifdef MAPBASE
if (m_bIdleAimAround)
{
if (!GetEnemy())
{
if (GetIdleAimTarget())
{
if (!ValidIdleAimTarget( GetIdleAimTarget() ))
{
SetIdleAimTarget( NULL );
m_flNextIdleAimTime = gpGlobals->curtime;
}
}
if (m_flNextIdleAimTime < gpGlobals->curtime)
{
if (!FindIdleAimTarget())
{
SetIdleAimTarget( NULL );
// Find a random direction in front of us instead
Vector vBasePos, vecForward, vecRight, vecUp;
GetAttachment( m_nGunBaseAttachment, vBasePos, &vecForward, &vecRight, &vecUp );
m_vecIdleAimDir = vecForward + (vecRight * RandomFloat( -0.25f, 0.25f )) + (vecUp * RandomFloat( -0.8f, -0.25f ));
}
m_flNextIdleAimTime = gpGlobals->curtime + RandomFloat( 4.0f, 6.0f );
}
if (GetIdleAimTarget())
{
// Get gun attachment points
Vector vBasePos;
GetAttachment( m_nGunBaseAttachment, vBasePos );
m_vecIdleAimDir = GetIdleAimTarget()->GetAbsOrigin() - vBasePos;
VectorNormalize( m_vecIdleAimDir );
}
PoseGunTowardTargetDirection( m_vecIdleAimDir );
}
else if (GetIdleAimTarget())
{
SetIdleAimTarget( NULL );
}
}
#endif
#ifdef HL2_EPISODIC
// Update our bone followers
m_BoneFollowerManager.UpdateBoneFollowers(this);