mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-01-27 22:27:57 +03:00
Merge pull request #271 from Blixibon/mapbase/feature/new-companion-npc-concepts
New and ported response concepts for companion NPCs
This commit is contained in:
commit
4c2823e814
@ -671,13 +671,27 @@ void CAI_BaseNPC::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bo
|
|||||||
{
|
{
|
||||||
BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
|
BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Alyx's enemy ignited code from below can now be run on any NPC as long as
|
||||||
|
// it's our current enemy.
|
||||||
|
if ( GetEnemy() && GetEnemy()->IsNPC() )
|
||||||
|
{
|
||||||
|
GetEnemy()->MyNPCPointer()->EnemyIgnited( this );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HL2_EPISODIC
|
#ifdef HL2_EPISODIC
|
||||||
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
||||||
if ( pPlayer && pPlayer->IRelationType( this ) != D_LI )
|
if ( pPlayer && pPlayer->IRelationType( this ) != D_LI )
|
||||||
{
|
{
|
||||||
CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx();
|
CNPC_Alyx *alyx = CNPC_Alyx::GetAlyx();
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Alyx's code continues to run if Alyx was not this NPC's enemy.
|
||||||
|
if ( alyx && alyx != GetEnemy() )
|
||||||
|
#else
|
||||||
if ( alyx )
|
if ( alyx )
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
alyx->EnemyIgnited( this );
|
alyx->EnemyIgnited( this );
|
||||||
}
|
}
|
||||||
@ -16476,6 +16490,21 @@ void CAI_BaseNPC::ModifyOrAppendEnemyCriteria( AI_CriteriaSet& set, CBaseEntity
|
|||||||
set.AppendCriteria( "enemyclass", g_pGameRules->AIClassText( pEnemy->Classify() ) ); // UTIL_VarArgs("%i", pEnemy->Classify())
|
set.AppendCriteria( "enemyclass", g_pGameRules->AIClassText( pEnemy->Classify() ) ); // UTIL_VarArgs("%i", pEnemy->Classify())
|
||||||
set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(pEnemy) ) );
|
set.AppendCriteria( "distancetoenemy", UTIL_VarArgs( "%f", EnemyDistance(pEnemy) ) );
|
||||||
set.AppendCriteria( "timesincecombat", "-1" );
|
set.AppendCriteria( "timesincecombat", "-1" );
|
||||||
|
|
||||||
|
CAI_BaseNPC *pNPC = pEnemy->MyNPCPointer();
|
||||||
|
if (pNPC)
|
||||||
|
{
|
||||||
|
set.AppendCriteria("enemy_is_npc", "1");
|
||||||
|
|
||||||
|
set.AppendCriteria( "enemy_activity", CAI_BaseNPC::GetActivityName( pNPC->GetActivity() ) );
|
||||||
|
set.AppendCriteria( "enemy_weapon", pNPC->GetActiveWeapon() ? pNPC->GetActiveWeapon()->GetClassname() : "0" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set.AppendCriteria("enemy_is_npc", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
pEnemy->AppendContextToCriteria( set, "enemy_" );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1995,6 +1995,9 @@ public:
|
|||||||
//---------------------------------
|
//---------------------------------
|
||||||
|
|
||||||
virtual void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false );
|
virtual void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false );
|
||||||
|
#ifdef MAPBASE
|
||||||
|
virtual void EnemyIgnited( CAI_BaseNPC *pVictim ) {}
|
||||||
|
#endif
|
||||||
virtual bool PassesDamageFilter( const CTakeDamageInfo &info );
|
virtual bool PassesDamageFilter( const CTakeDamageInfo &info );
|
||||||
|
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
|
@ -128,6 +128,12 @@ ConceptInfo_t g_ConceptInfos[] =
|
|||||||
|
|
||||||
// Passenger behaviour
|
// Passenger behaviour
|
||||||
{ TLK_PASSENGER_NEW_RADAR_CONTACT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
{ TLK_PASSENGER_NEW_RADAR_CONTACT, SPEECH_IMPORTANT, -1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
{ TLK_TAKING_FIRE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
{ TLK_NEW_ENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
{ TLK_COMBAT_IDLE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -1259,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 )
|
||||||
{
|
{
|
||||||
|
@ -132,6 +132,13 @@
|
|||||||
#define TLK_TGCATCHUP "TLK_TGCATCHUP"
|
#define TLK_TGCATCHUP "TLK_TGCATCHUP"
|
||||||
#define TLK_TGENDTOUR "TLK_TGENDTOUR"
|
#define TLK_TGENDTOUR "TLK_TGENDTOUR"
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Additional concepts for companions in mods
|
||||||
|
#define TLK_TAKING_FIRE "TLK_TAKING_FIRE" // Someone fired at me (regardless of whether I was hit)
|
||||||
|
#define TLK_NEW_ENEMY "TLK_NEW_ENEMY" // A new enemy appeared while combat was already in progress
|
||||||
|
#define TLK_COMBAT_IDLE "TLK_COMBAT_IDLE" // Similar to TLK_ATTACKING, but specifically for when *not* currently attacking (e.g. when in cover or reloading)
|
||||||
|
#endif
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this
|
#define TALKRANGE_MIN 500.0 // don't talk to anyone farther away than this
|
||||||
@ -315,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
|
||||||
//---------------------------------
|
//---------------------------------
|
||||||
|
@ -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
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
@ -190,6 +190,24 @@ int CAI_FuncTankBehavior::SelectSchedule()
|
|||||||
return SCHED_IDLE_STAND;
|
return SCHED_IDLE_STAND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose:
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CAI_FuncTankBehavior::ModifyOrAppendCriteria( AI_CriteriaSet &set )
|
||||||
|
{
|
||||||
|
BaseClass::ModifyOrAppendCriteria( set );
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
set.AppendCriteria( "ft_mounted", m_bMounted ? "1" : "0" );
|
||||||
|
|
||||||
|
if (m_hFuncTank)
|
||||||
|
{
|
||||||
|
set.AppendCriteria( "ft_classname", m_hFuncTank->GetClassname() );
|
||||||
|
m_hFuncTank->AppendContextToCriteria( set, "ft_" );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose:
|
// Purpose:
|
||||||
// Input : activity -
|
// Input : activity -
|
||||||
|
@ -55,6 +55,8 @@ public:
|
|||||||
bool CanManTank( CFuncTank *pTank, bool bForced );
|
bool CanManTank( CFuncTank *pTank, bool bForced );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void ModifyOrAppendCriteria( AI_CriteriaSet &set );
|
||||||
|
|
||||||
Activity NPC_TranslateActivity( Activity activity );
|
Activity NPC_TranslateActivity( Activity activity );
|
||||||
|
|
||||||
// Conditions:
|
// Conditions:
|
||||||
|
@ -2608,6 +2608,9 @@ void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &ve
|
|||||||
// Inherit some misc. properties
|
// Inherit some misc. properties
|
||||||
pCrab->m_bForceServerRagdoll = m_bForceServerRagdoll;
|
pCrab->m_bForceServerRagdoll = m_bForceServerRagdoll;
|
||||||
pCrab->m_iViewHideFlags = m_iViewHideFlags;
|
pCrab->m_iViewHideFlags = m_iViewHideFlags;
|
||||||
|
|
||||||
|
// Add response context for companion response (more reliable than checking for post-death zombie entity)
|
||||||
|
pCrab->AddContext( "from_zombie", "1", 2.0f );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// make me the crab's owner to avoid collision issues
|
// make me the crab's owner to avoid collision issues
|
||||||
|
@ -1092,10 +1092,14 @@ void CNPC_Alyx::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void CNPC_Alyx::EnemyIgnited( CAI_BaseNPC *pVictim )
|
void CNPC_Alyx::EnemyIgnited( CAI_BaseNPC *pVictim )
|
||||||
{
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
BaseClass::EnemyIgnited( pVictim );
|
||||||
|
#else
|
||||||
if ( FVisible( pVictim ) )
|
if ( FVisible( pVictim ) )
|
||||||
{
|
{
|
||||||
SpeakIfAllowed( TLK_ENEMY_BURNING );
|
SpeakIfAllowed( TLK_ENEMY_BURNING );
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -1252,6 +1256,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void )
|
|||||||
|
|
||||||
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
CBasePlayer *pPlayer = AI_GetSinglePlayer();
|
||||||
|
|
||||||
|
#ifndef MAPBASE // Ported to CNPC_PlayerCompanion
|
||||||
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
|
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
|
||||||
{
|
{
|
||||||
if ( GetEnemy()->Classify() == CLASS_HEADCRAB )
|
if ( GetEnemy()->Classify() == CLASS_HEADCRAB )
|
||||||
@ -1278,6 +1283,7 @@ void CNPC_Alyx::DoCustomSpeechAI( void )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Darkness mode speech
|
// Darkness mode speech
|
||||||
ClearCondition( COND_ALYX_IN_DARK );
|
ClearCondition( COND_ALYX_IN_DARK );
|
||||||
@ -1917,6 +1923,7 @@ int CNPC_Alyx::SelectSchedule( void )
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
int CNPC_Alyx::SelectScheduleDanger( void )
|
int CNPC_Alyx::SelectScheduleDanger( void )
|
||||||
{
|
{
|
||||||
|
#ifndef MAPBASE
|
||||||
if( HasCondition( COND_HEAR_DANGER ) )
|
if( HasCondition( COND_HEAR_DANGER ) )
|
||||||
{
|
{
|
||||||
CSound *pSound;
|
CSound *pSound;
|
||||||
@ -1929,6 +1936,7 @@ int CNPC_Alyx::SelectScheduleDanger( void )
|
|||||||
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
|
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return BaseClass::SelectScheduleDanger();
|
return BaseClass::SelectScheduleDanger();
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#include "mapbase/GlobalStrings.h"
|
#include "mapbase/GlobalStrings.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "vehicle_base.h"
|
#include "vehicle_base.h"
|
||||||
|
#include "npc_headcrab.h"
|
||||||
|
#include "npc_BaseZombie.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ConVar ai_debug_readiness("ai_debug_readiness", "0" );
|
ConVar ai_debug_readiness("ai_debug_readiness", "0" );
|
||||||
@ -640,6 +642,55 @@ void CNPC_PlayerCompanion::DoCustomSpeechAI( void )
|
|||||||
{
|
{
|
||||||
SpeakIfAllowed( TLK_PLDEAD );
|
SpeakIfAllowed( TLK_PLDEAD );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MAPBASE
|
||||||
|
// Unique new enemy concepts ported from Alyx
|
||||||
|
// The casts have been changed to dynamic_cast due to the risk of non-CBaseHeadcrab/CNPC_BaseZombie enemies using those classes
|
||||||
|
if ( HasCondition(COND_NEW_ENEMY) && GetEnemy() )
|
||||||
|
{
|
||||||
|
int nClass = GetEnemy()->Classify();
|
||||||
|
if ( nClass == CLASS_HEADCRAB )
|
||||||
|
{
|
||||||
|
CBaseHeadcrab *pHC = dynamic_cast<CBaseHeadcrab*>(GetEnemy());
|
||||||
|
if ( pHC )
|
||||||
|
{
|
||||||
|
// If we see a headcrab for the first time as he's jumping at me, freak out!
|
||||||
|
if ( ( GetEnemy()->GetEnemy() == this ) && pHC->IsJumping() && gpGlobals->curtime - GetEnemies()->FirstTimeSeen(GetEnemy()) < 0.5 )
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( "TLK_SPOTTED_INCOMING_HEADCRAB" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we see a headcrab leaving a zombie that just died, mention it
|
||||||
|
// (Note that this is now a response context since some death types remove the zombie instantly)
|
||||||
|
int nContext = pHC->FindContextByName( "from_zombie" );
|
||||||
|
if ( nContext > -1 && !ContextExpired( nContext ) ) // pHC->GetOwnerEntity() && ( pHC->GetOwnerEntity()->Classify() == CLASS_ZOMBIE ) && !pHC->GetOwnerEntity()->IsAlive()
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( "TLK_SPOTTED_HEADCRAB_LEAVING_ZOMBIE" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( nClass == CLASS_ZOMBIE )
|
||||||
|
{
|
||||||
|
CNPC_BaseZombie *pZombie = dynamic_cast<CNPC_BaseZombie*>(GetEnemy());
|
||||||
|
// If we see a zombie getting up, mention it
|
||||||
|
if ( pZombie && pZombie->IsGettingUp() )
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( "TLK_SPOTTED_ZOMBIE_WAKEUP" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( gpGlobals->curtime - GetEnemies()->TimeAtFirstHand( GetEnemy() ) <= 1.0f && nClass != CLASS_BULLSEYE )
|
||||||
|
{
|
||||||
|
// New concept which did not originate from Alyx, but is in the same category as the above concepts.
|
||||||
|
// This is meant to be used when a new enemy enters the arena while combat is already in progress.
|
||||||
|
// (Note that this can still trigger when combat begins, but unlike TLK_STARTCOMBAT, it has no delay
|
||||||
|
// between combat engagements.)
|
||||||
|
SpeakIfAllowed( TLK_NEW_ENEMY );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -910,8 +961,21 @@ int CNPC_PlayerCompanion::SelectScheduleDanger()
|
|||||||
|
|
||||||
if ( pSound && (pSound->m_iType & SOUND_DANGER) )
|
if ( pSound && (pSound->m_iType & SOUND_DANGER) )
|
||||||
{
|
{
|
||||||
|
#ifdef MAPBASE
|
||||||
|
if ( pSound->SoundChannel() == SOUNDENT_CHANNEL_ZOMBINE_GRENADE )
|
||||||
|
{
|
||||||
|
SetSpeechTarget( pSound->m_hOwner );
|
||||||
|
SpeakIfAllowed( TLK_DANGER_ZOMBINE_GRENADE );
|
||||||
|
}
|
||||||
|
else if (!(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR | SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak())
|
||||||
|
{
|
||||||
|
SetSpeechTarget( pSound->m_hOwner );
|
||||||
|
SpeakIfAllowed( TLK_DANGER );
|
||||||
|
}
|
||||||
|
#else
|
||||||
if ( !(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR|SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak() )
|
if ( !(pSound->SoundContext() & (SOUND_CONTEXT_MORTAR|SOUND_CONTEXT_FROM_SNIPER)) || IsOkToCombatSpeak() )
|
||||||
SpeakIfAllowed( TLK_DANGER );
|
SpeakIfAllowed( TLK_DANGER );
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( HasCondition( COND_PC_SAFE_FROM_MORTAR ) )
|
if ( HasCondition( COND_PC_SAFE_FROM_MORTAR ) )
|
||||||
{
|
{
|
||||||
@ -4309,6 +4373,20 @@ void CNPC_PlayerCompanion::Event_KilledOther( CBaseEntity *pVictim, const CTakeD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Purpose: Called by enemy NPC's when they are ignited
|
||||||
|
// Input : pVictim - entity that was ignited
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CNPC_PlayerCompanion::EnemyIgnited( CAI_BaseNPC *pVictim )
|
||||||
|
{
|
||||||
|
BaseClass::EnemyIgnited( pVictim );
|
||||||
|
|
||||||
|
if ( FVisible( pVictim ) )
|
||||||
|
{
|
||||||
|
SpeakIfAllowed( TLK_ENEMY_BURNING );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose: Handles custom combat speech stuff ported from Alyx.
|
// Purpose: Handles custom combat speech stuff ported from Alyx.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -4376,6 +4454,21 @@ void CNPC_PlayerCompanion::DoCustomCombatAI( void )
|
|||||||
{
|
{
|
||||||
SpeakIfAllowed( TLK_MANY_ENEMIES );
|
SpeakIfAllowed( TLK_MANY_ENEMIES );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're not currently attacking or vulnerable, try speaking
|
||||||
|
else if ( gpGlobals->curtime - GetLastAttackTime() > 1.0f && (!HasCondition( COND_SEE_ENEMY ) || IsCurSchedule( SCHED_RELOAD ) || IsCurSchedule( SCHED_HIDE_AND_RELOAD )) )
|
||||||
|
{
|
||||||
|
int chance = ( IsMoving() ) ? 20 : 3;
|
||||||
|
if ( ShouldSpeakRandom( TLK_COMBAT_IDLE, chance ) )
|
||||||
|
{
|
||||||
|
AI_CriteriaSet modifiers;
|
||||||
|
|
||||||
|
modifiers.AppendCriteria( "in_cover", HasMemory( bits_MEMORY_INCOVER ) ? "1" : "0" );
|
||||||
|
modifiers.AppendCriteria( "lastseenenemy", UTIL_VarArgs( "%f", gpGlobals->curtime - GetEnemyLastTimeSeen() ) );
|
||||||
|
|
||||||
|
SpeakIfAllowed( TLK_COMBAT_IDLE, modifiers );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -242,6 +242,7 @@ public:
|
|||||||
|
|
||||||
virtual void Event_Killed( const CTakeDamageInfo &info );
|
virtual void Event_Killed( const CTakeDamageInfo &info );
|
||||||
virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
|
virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info );
|
||||||
|
virtual void EnemyIgnited( CAI_BaseNPC *pVictim );
|
||||||
virtual void DoCustomCombatAI( void );
|
virtual void DoCustomCombatAI( void );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -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
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
@ -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; }
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user