Optional support for fired bullets notifying targets (currently only used for NPC companion speech)

This commit is contained in:
ALLEN-PC\acj30 2024-01-03 16:00:10 -06:00
parent 85c1f7d113
commit 9070d9a844
6 changed files with 155 additions and 0 deletions

View File

@ -1265,6 +1265,38 @@ void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled )
} }
} }
#ifdef MAPBASE
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_PlayerAlly::OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd )
{
BaseClass::OnEnemyRangeAttackedMe( pEnemy, vecDir, vecEnd );
if ( IRelationType( pEnemy ) <= D_FR )
{
AI_CriteriaSet modifiers;
ModifyOrAppendEnemyCriteria( modifiers, pEnemy );
Vector vecEntDir = (pEnemy->EyePosition() - EyePosition());
float flDot = DotProduct( vecEntDir.Normalized(), vecDir );
modifiers.AppendCriteria( "shot_dot", CNumStr( flDot ) );
if (GetLastDamageTime() == gpGlobals->curtime)
modifiers.AppendCriteria( "missed", "0" );
else
modifiers.AppendCriteria( "missed", "1" );
// Check if they're out of ammo
if ( pEnemy->IsCombatCharacter() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon() && pEnemy->MyCombatCharacterPointer()->GetActiveWeapon()->Clip1() <= 0 )
modifiers.AppendCriteria( "last_attack", "1" );
else
modifiers.AppendCriteria( "last_attack", "0" );
SpeakIfAllowed( TLK_TAKING_FIRE, modifiers );
}
}
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void CAI_PlayerAlly::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) void CAI_PlayerAlly::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
{ {

View File

@ -322,6 +322,10 @@ public:
//--------------------------------- //---------------------------------
void OnKilledNPC( CBaseCombatCharacter *pKilled ); void OnKilledNPC( CBaseCombatCharacter *pKilled );
#ifdef MAPBASE
void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd );
#endif
//--------------------------------- //---------------------------------
// Damage handling // Damage handling
//--------------------------------- //---------------------------------

View File

@ -263,6 +263,10 @@ public:
virtual bool CanBecomeServerRagdoll( void ) { return true; } virtual bool CanBecomeServerRagdoll( void ) { return true; }
#ifdef MAPBASE
virtual void OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector &vecDir, const Vector &vecEnd ) {}
#endif
// ----------------------- // -----------------------
// Damage // Damage
// ----------------------- // -----------------------

View File

@ -7734,6 +7734,93 @@ void CBasePlayer::ResetAutoaim( void )
m_fOnTarget = false; m_fOnTarget = false;
} }
#ifdef MAPBASE
ConVar player_debug_probable_aim_target( "player_debug_probable_aim_target", "0", FCVAR_CHEAT, "" );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseEntity *CBasePlayer::GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir )
{
trace_t tr;
CBaseEntity *pIgnore = NULL;
if (IsInAVehicle())
pIgnore = GetVehicleEntity();
CTraceFilterSkipTwoEntities traceFilter( this, pIgnore, COLLISION_GROUP_NONE );
// Based on dot product and distance
// If we aim directly at something, only return it if there's not a larger entity slightly off-center
// Should be weighted based on whether an entity is a NPC, etc.
CBaseEntity *pBestEnt = NULL;
float flBestWeight = 0.0f;
for (CBaseEntity *pEntity = UTIL_EntitiesInPVS( this, NULL ); pEntity; pEntity = UTIL_EntitiesInPVS( this, pEntity ))
{
// Combat characters can be unviewable if they just died
if (!pEntity->IsViewable() && !pEntity->IsCombatCharacter())
continue;
if (pEntity == this || pEntity->GetMoveParent() == this || pEntity == GetVehicleEntity())
continue;
Vector vecEntDir = (pEntity->EyePosition() - vecSrc);
float flDot = DotProduct( vecEntDir.Normalized(), vecDir);
if (flDot < m_flFieldOfView)
continue;
// Make sure we can see it
UTIL_TraceLine( vecSrc, pEntity->EyePosition(), MASK_SHOT, &traceFilter, &tr );
if (tr.m_pEnt != pEntity)
{
if (pEntity->IsCombatCharacter())
{
// Trace between centers as well just in case our eyes are blocked
UTIL_TraceLine( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), MASK_SHOT, &traceFilter, &tr );
if (tr.m_pEnt != pEntity)
continue;
}
else
continue;
}
float flWeight = flDot - (vecEntDir.LengthSqr() / Square( 2048.0f ));
if (pEntity->IsCombatCharacter())
{
// Hostile NPCs are more likely targets
if (IRelationType( pEntity ) <= D_FR)
flWeight += 0.5f;
}
else if (pEntity->GetFlags() & FL_AIMTARGET)
{
// FL_AIMTARGET is often used for props like explosive barrels
flWeight += 0.25f;
}
if (player_debug_probable_aim_target.GetBool())
{
float flWeightClamped = 1.0f - RemapValClamped( flWeight, -2.0f, 2.0f, 0.0f, 1.0f );
pEntity->EntityText( 0, UTIL_VarArgs( "%f", flWeight ), 2.0f, flWeightClamped * 255.0f, 255.0f, flWeightClamped * 255.0f, 255 );
}
if (flWeight > flBestWeight)
{
pBestEnt = pEntity;
flBestWeight = flWeight;
}
}
if (player_debug_probable_aim_target.GetBool())
{
Msg( "Best probable aim target is %s\n", pBestEnt->GetDebugName() );
NDebugOverlay::EntityBounds( pBestEnt, 255, 100, 0, 0, 2.0f );
}
return pBestEnt;
}
#endif
// ========================================================================== // ==========================================================================
// > Weapon stuff // > Weapon stuff
// ========================================================================== // ==========================================================================

View File

@ -608,6 +608,11 @@ public:
virtual bool ShouldAutoaim( void ); virtual bool ShouldAutoaim( void );
void SetTargetInfo( Vector &vecSrc, float flDist ); void SetTargetInfo( Vector &vecSrc, float flDist );
#ifdef MAPBASE
// Tries to figure out what the player is trying to aim at
CBaseEntity *GetProbableAimTarget( const Vector &vecSrc, const Vector &vecDir );
#endif
void SetViewEntity( CBaseEntity *pEntity ); void SetViewEntity( CBaseEntity *pEntity );
CBaseEntity *GetViewEntity( void ) { return m_hViewEntity; } CBaseEntity *GetViewEntity( void ) { return m_hViewEntity; }

View File

@ -83,6 +83,10 @@ ConVar ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED );
ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED ); ConVar ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );
ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT ); ConVar ai_debug_shoot_positions( "ai_debug_shoot_positions", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
#if defined(MAPBASE) && defined(GAME_DLL)
ConVar ai_shot_notify_targets( "ai_shot_notify_targets", "0", FCVAR_NONE, "Allows fired bullets to notify the NPCs and players they are targeting, regardless of whether they hit them or not. Can be used for custom AI and speech." );
#endif
// Utility func to throttle rate at which the "reasonable position" spew goes out // Utility func to throttle rate at which the "reasonable position" spew goes out
static double s_LastEntityReasonableEmitTime; static double s_LastEntityReasonableEmitTime;
bool CheckEmitReasonablePhysicsSpew() bool CheckEmitReasonablePhysicsSpew()
@ -2081,6 +2085,25 @@ void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType ); CTakeDamageInfo dmgInfo( this, pAttacker, flCumulativeDamage, nDamageType );
gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo ); gamestats->Event_WeaponHit( pPlayer, info.m_bPrimaryAttack, pPlayer->GetActiveWeapon()->GetClassname(), dmgInfo );
} }
#ifdef MAPBASE
if ( ai_shot_notify_targets.GetBool() )
{
if ( IsPlayer() )
{
// Look for probable target to notify of attack
CBaseEntity *pAimTarget = static_cast<CBasePlayer*>(this)->GetProbableAimTarget( info.m_vecSrc, info.m_vecDirShooting );
if ( pAimTarget && pAimTarget->IsCombatCharacter() )
{
pAimTarget->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd );
}
}
else if ( GetEnemy() && GetEnemy()->IsCombatCharacter() )
{
GetEnemy()->MyCombatCharacterPointer()->OnEnemyRangeAttackedMe( this, vecDir, vecEnd );
}
}
#endif
#endif #endif
} }