Add new response concepts + port several Alyx concepts for companion NPCs

This commit is contained in:
ALLEN-PC\acj30 2024-01-03 15:57:18 -06:00
parent e34e0d3b10
commit 85c1f7d113
7 changed files with 126 additions and 0 deletions

View File

@ -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 );
} }

View File

@ -1974,6 +1974,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 );
//--------------------------------- //---------------------------------

View File

@ -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
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -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

View File

@ -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();
} }

View File

@ -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,49 @@ 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" );
}
// If we see a headcrab leaving a zombie that just died, mention it
else if ( 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" );
}
}
else 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 +955,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 +4367,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 +4448,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

View File

@ -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