source-sdk-2013-mapbase/sp/src/game/server/ai_speech.cpp

1409 lines
36 KiB
C++
Raw Normal View History

2013-12-03 07:31:46 +04:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "ai_speech.h"
#include "game.h"
#include "engine/IEngineSound.h"
#include "KeyValues.h"
#include "ai_basenpc.h"
#include "AI_Criteria.h"
#include "isaverestore.h"
#include "sceneentity.h"
#ifdef MAPBASE
#include "ai_squad.h"
#endif
2013-12-03 07:31:46 +04:00
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#define DEBUG_AISPEECH 1
#ifdef DEBUG_AISPEECH
ConVar ai_debug_speech( "ai_debug_speech", "0" );
#define DebuggingSpeech() ai_debug_speech.GetBool()
#else
inline void SpeechMsg( ... ) {}
#define DebuggingSpeech() (false)
#endif
extern ConVar rr_debugresponses;
//-----------------------------------------------------------------------------
CAI_TimedSemaphore g_AIFriendliesTalkSemaphore;
CAI_TimedSemaphore g_AIFoesTalkSemaphore;
ConceptHistory_t::~ConceptHistory_t()
{
if ( response )
{
delete response;
}
response = NULL;
}
ConceptHistory_t::ConceptHistory_t( const ConceptHistory_t& src )
{
timeSpoken = src.timeSpoken;
response = NULL;
if ( src.response )
{
response = new AI_Response( *src.response );
}
}
ConceptHistory_t& ConceptHistory_t::operator =( const ConceptHistory_t& src )
{
if ( this == &src )
return *this;
timeSpoken = src.timeSpoken;
response = NULL;
if ( src.response )
{
response = new AI_Response( *src.response );
}
return *this;
}
BEGIN_SIMPLE_DATADESC( ConceptHistory_t )
DEFINE_FIELD( timeSpoken, FIELD_TIME ), // Relative to server time
// DEFINE_EMBEDDED( response, FIELD_??? ), // This is manually saved/restored by the ConceptHistory saverestore ops below
END_DATADESC()
class CConceptHistoriesDataOps : public CDefSaveRestoreOps
{
public:
virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
{
CUtlDict< ConceptHistory_t, int > *ch = ((CUtlDict< ConceptHistory_t, int > *)fieldInfo.pField);
int count = ch->Count();
pSave->WriteInt( &count );
for ( int i = 0 ; i < count; i++ )
{
ConceptHistory_t *pHistory = &(*ch)[ i ];
pSave->StartBlock();
{
// Write element name
pSave->WriteString( ch->GetElementName( i ) );
// Write data
pSave->WriteAll( pHistory );
// Write response blob
bool hasresponse = pHistory->response != NULL ? true : false;
pSave->WriteBool( &hasresponse );
if ( hasresponse )
{
pSave->WriteAll( pHistory->response );
}
// TODO: Could blat out pHistory->criteria pointer here, if it's needed
}
pSave->EndBlock();
}
}
virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
{
CUtlDict< ConceptHistory_t, int > *ch = ((CUtlDict< ConceptHistory_t, int > *)fieldInfo.pField);
int count = pRestore->ReadInt();
Assert( count >= 0 );
for ( int i = 0 ; i < count; i++ )
{
char conceptname[ 512 ];
conceptname[ 0 ] = 0;
ConceptHistory_t history;
pRestore->StartBlock();
{
pRestore->ReadString( conceptname, sizeof( conceptname ), 0 );
pRestore->ReadAll( &history );
bool hasresponse = false;
pRestore->ReadBool( &hasresponse );
if ( hasresponse )
{
history.response = new AI_Response();
pRestore->ReadAll( history.response );
}
}
pRestore->EndBlock();
// TODO: Could restore pHistory->criteria pointer here, if it's needed
// Add to utldict
if ( conceptname[0] != 0 )
{
ch->Insert( conceptname, history );
}
else
{
Assert( !"Error restoring ConceptHistory_t, discarding!" );
}
}
}
virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
{
}
virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
{
CUtlDict< ConceptHistory_t, int > *ch = ((CUtlDict< ConceptHistory_t, int > *)fieldInfo.pField);
return ch->Count() == 0 ? true : false;
}
};
CConceptHistoriesDataOps g_ConceptHistoriesSaveDataOps;
//-----------------------------------------------------------------------------
//
// CLASS: CAI_Expresser
//
BEGIN_SIMPLE_DATADESC( CAI_Expresser )
// m_pSink (reconnected on load)
// DEFINE_FIELD( m_pOuter, CHandle < CBaseFlex > ),
DEFINE_CUSTOM_FIELD( m_ConceptHistories, &g_ConceptHistoriesSaveDataOps ),
DEFINE_FIELD( m_flStopTalkTime, FIELD_TIME ),
DEFINE_FIELD( m_flStopTalkTimeWithoutDelay, FIELD_TIME ),
DEFINE_FIELD( m_flBlockedTalkTime, FIELD_TIME ),
DEFINE_FIELD( m_voicePitch, FIELD_INTEGER ),
DEFINE_FIELD( m_flLastTimeAcceptedSpeak, FIELD_TIME ),
END_DATADESC()
//-------------------------------------
bool CAI_Expresser::SemaphoreIsAvailable( CBaseEntity *pTalker )
{
if ( !GetSink()->UseSemaphore() )
return true;
CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( pTalker->MyNPCPointer() );
return (pSemaphore ? pSemaphore->IsAvailable( pTalker ) : true);
}
//-------------------------------------
float CAI_Expresser::GetSemaphoreAvailableTime( CBaseEntity *pTalker )
{
CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( pTalker->MyNPCPointer() );
return (pSemaphore ? pSemaphore->GetReleaseTime() : 0);
}
//-------------------------------------
int CAI_Expresser::GetVoicePitch() const
{
return m_voicePitch + random->RandomInt(0,3);
}
#ifdef DEBUG
static int g_nExpressers;
#endif
CAI_Expresser::CAI_Expresser( CBaseFlex *pOuter )
: m_pOuter( pOuter ),
m_pSink( NULL ),
m_flStopTalkTime( 0 ),
m_flLastTimeAcceptedSpeak( 0 ),
m_flBlockedTalkTime( 0 ),
m_flStopTalkTimeWithoutDelay( 0 ),
m_voicePitch( 100 )
{
#ifdef DEBUG
g_nExpressers++;
#endif
}
CAI_Expresser::~CAI_Expresser()
{
m_ConceptHistories.Purge();
CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( GetOuter() );
if ( pSemaphore )
{
if ( pSemaphore->GetOwner() == GetOuter() )
pSemaphore->Release();
#ifdef DEBUG
g_nExpressers--;
if ( g_nExpressers == 0 && pSemaphore->GetOwner() )
DevMsg( 2, "Speech semaphore being held by non-talker entity\n" );
#endif
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_Expresser::TestAllResponses()
{
IResponseSystem *pResponseSystem = GetOuter()->GetResponseSystem();
if ( pResponseSystem )
{
CUtlVector<AI_Response *> responses;
pResponseSystem->GetAllResponses( &responses );
for ( int i = 0; i < responses.Count(); i++ )
{
char response[ 256 ];
responses[i]->GetResponse( response, sizeof( response ) );
Msg( "Response: %s\n", response );
SpeakDispatchResponse( "", responses[i] );
}
}
}
//-----------------------------------------------------------------------------
static const int LEN_SPECIFIC_SCENE_MODIFIER = strlen( AI_SPECIFIC_SCENE_MODIFIER );
//-----------------------------------------------------------------------------
// Purpose: Searches for a possible response
// Input : concept -
// NULL -
// Output : AI_Response
//-----------------------------------------------------------------------------
AI_Response *CAI_Expresser::SpeakFindResponse( AIConcept_t concept, const char *modifiers /*= NULL*/ )
{
#ifdef MAPBASE
AI_CriteriaSet set;
if (modifiers)
{
MergeModifiers(set, modifiers);
}
// Now return the code in the new function.
return SpeakFindResponse(concept, set);
#else
2013-12-03 07:31:46 +04:00
IResponseSystem *rs = GetOuter()->GetResponseSystem();
if ( !rs )
{
Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" );
return NULL;
}
AI_CriteriaSet set;
// Always include the concept name
set.AppendCriteria( "concept", concept, CONCEPT_WEIGHT );
// Always include any optional modifiers
if ( modifiers != NULL )
{
char copy_modifiers[ 255 ];
const char *pCopy;
char key[ 128 ] = { 0 };
char value[ 128 ] = { 0 };
Q_strncpy( copy_modifiers, modifiers, sizeof( copy_modifiers ) );
pCopy = copy_modifiers;
while( pCopy )
{
pCopy = SplitContext( pCopy, key, sizeof( key ), value, sizeof( value ), NULL );
if( *key && *value )
{
set.AppendCriteria( key, value, CONCEPT_WEIGHT );
}
}
}
// Let our outer fill in most match criteria
GetOuter()->ModifyOrAppendCriteria( set );
// Append local player criteria to set, but not if this is a player doing the talking
if ( !GetOuter()->IsPlayer() )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
if( pPlayer )
pPlayer->ModifyOrAppendPlayerCriteria( set );
}
#ifdef MAPBASE
GetOuter()->ReAppendContextCriteria( set );
#endif
2013-12-03 07:31:46 +04:00
// Now that we have a criteria set, ask for a suitable response
AI_Response *result = new AI_Response;
Assert( result && "new AI_Response: Returned a NULL AI_Response!" );
bool found = rs->FindBestResponse( set, *result, this );
if ( rr_debugresponses.GetInt() == 3 )
{
if ( ( GetOuter()->MyNPCPointer() && GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) || GetOuter()->IsPlayer() )
{
const char *pszName;
if ( GetOuter()->IsPlayer() )
{
pszName = ((CBasePlayer*)GetOuter())->GetPlayerName();
}
else
{
pszName = GetOuter()->GetDebugName();
}
if ( found )
{
char response[ 256 ];
result->GetResponse( response, sizeof( response ) );
Warning( "RESPONSERULES: %s spoke '%s'. Found response '%s'.\n", pszName, concept, response );
}
else
{
Warning( "RESPONSERULES: %s spoke '%s'. Found no matching response.\n", pszName, concept );
}
}
}
if ( !found )
{
//Assert( !"rs->FindBestResponse: Returned a NULL AI_Response!" );
delete result;
return NULL;
}
char response[ 256 ];
result->GetResponse( response, sizeof( response ) );
if ( !response[0] )
{
delete result;
return NULL;
}
if ( result->GetOdds() < 100 && random->RandomInt( 1, 100 ) <= result->GetOdds() )
{
delete result;
return NULL;
}
return result;
#endif
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Merges modifiers with set.
//-----------------------------------------------------------------------------
void CAI_Expresser::MergeModifiers( AI_CriteriaSet& set, const char *modifiers )
{
char copy_modifiers[ 255 ];
const char *pCopy;
char key[ 128 ] = { 0 };
char value[ 128 ] = { 0 };
Q_strncpy( copy_modifiers, modifiers, sizeof( copy_modifiers ) );
pCopy = copy_modifiers;
while( pCopy )
{
pCopy = SplitContext( pCopy, key, sizeof( key ), value, sizeof( value ), NULL );
if( *key && *value )
{
set.AppendCriteria( key, value, CONCEPT_WEIGHT );
}
}
2013-12-03 07:31:46 +04:00
}
//-----------------------------------------------------------------------------
// Purpose: Searches for a possible response, takes an AI_CriteriaSet instead.
// Input : concept -
// NULL -
// Output : AI_Response
//-----------------------------------------------------------------------------
AI_Response *CAI_Expresser::SpeakFindResponse( AIConcept_t concept, AI_CriteriaSet &modifiers )
{
IResponseSystem *rs = GetOuter()->GetResponseSystem();
if ( !rs )
{
Assert( !"No response system installed for CAI_Expresser::GetOuter()!!!" );
return NULL;
}
AI_CriteriaSet set;
// Always include the concept name
set.AppendCriteria( "concept", concept, CONCEPT_WEIGHT );
// Tier 1: Criteria
// Let our outer fill in most match criteria
GetOuter()->ModifyOrAppendCriteria( set );
// Append local player criteria to set, but not if this is a player doing the talking
if ( !GetOuter()->IsPlayer() )
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
if( pPlayer )
pPlayer->ModifyOrAppendPlayerCriteria( set );
}
// Tier 2: Modifiers
set.MergeSet(modifiers);
// Tier 3: Contexts
GetOuter()->ReAppendContextCriteria( set );
// Now that we have a criteria set, ask for a suitable response
AI_Response *result = new AI_Response;
Assert( result && "new AI_Response: Returned a NULL AI_Response!" );
bool found = rs->FindBestResponse( set, *result, this );
if ( rr_debugresponses.GetInt() == 3 )
{
if ( ( GetOuter()->MyNPCPointer() && GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) || GetOuter()->IsPlayer() )
{
const char *pszName;
if ( GetOuter()->IsPlayer() )
{
pszName = ((CBasePlayer*)GetOuter())->GetPlayerName();
}
else
{
pszName = GetOuter()->GetDebugName();
}
if ( found )
{
char response[ 256 ];
result->GetResponse( response, sizeof( response ) );
Warning( "RESPONSERULES: %s spoke '%s'. Found response '%s'.\n", pszName, concept, response );
}
else
{
Warning( "RESPONSERULES: %s spoke '%s'. Found no matching response.\n", pszName, concept );
}
}
}
if ( !found )
{
//Assert( !"rs->FindBestResponse: Returned a NULL AI_Response!" );
delete result;
return NULL;
}
char response[ 256 ];
result->GetResponse( response, sizeof( response ) );
if ( !response[0] )
{
delete result;
return NULL;
}
if ( result->GetOdds() < 100 && random->RandomInt( 1, 100 ) <= result->GetOdds() )
{
delete result;
return NULL;
}
return result;
}
#endif
2013-12-03 07:31:46 +04:00
//-----------------------------------------------------------------------------
// Purpose: Dispatches the result
// Input : *response -
//-----------------------------------------------------------------------------
#ifdef MAPBASE
Merged dev changes 9/28/2019 - Experimental RPC stuff for the future - Fixed players running over allies with vehicles (kind of) - Modified redirect filter infrastructure to support when there's no target filter (meaning it will just make sure the entity exists) - Fixed SDK_EyeRefract - Fixed env_beam SetStart/EndEntity - New "OnStateChange" output on NPCs - scripted_face removed (use generic facing VCDs instead) - Fixed RPC - View ID nodraw keyvalue + reflective glass view ID fix - CopyAnimationDataFrom expansion (more variables copied) - Fixed pre-Mapbase env_projectedtextures not updating after loading a save - Fixed(?) player companion grenade throwing being interrupted - Added convars for secondary and NPC shotgun pellet amounts - NPC fade distance/scale transfers to its ragdoll - Made node graph rebuild occur sooner after map load - Added option to disable "node graph out of date" message - Fixed ent_fire delay (decimals discarded before) - "SetFilter" on func_clip_vphysics - Fixed func_tank zero barrel (untested) - Fixed npc_turret_ground parenting fix - Added toggle-able weapon_crossbow experimental hit location code - Fixed ally grenades being considered Combine grenades - Added SDK_MonitorScreen and SDK_UnlitTwoTexture - Updated README - Added !activator/!caller support to logic_collision_pair - Fixed ortho not working in script_intro - Added Nbc66's closed captioning language fix - Applied fade fix to server ragdolls - Added combine_mine friend/foe filters - Fixed env_starfield pausing - Reworked PickupWeapon/Item inputs - Fixed context response system $ usage - Fixed env_break_shooter velocity/speed - Made func_breakable "Spawn on break" support other entities - Fixed OnThrowGrenade > point_entity_replace blip - Added mapname to logic_externaldata - Added "Random Template" to point_template - Added "Use LOS" to trigger_look - Added flags based on L4D(2) to trigger_playermovement - Added npc_combine_s "Alternate Capable" keyvalue that enables both grenades and alt-fire at the same time regardless of elite status - Fixed npc_combine_s DropGrenade input - Miscellaneous code and comment changes
2019-09-29 01:56:52 +03:00
bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response *result, IRecipientFilter *filter, AI_CriteriaSet *modifiers )
#else
2013-12-03 07:31:46 +04:00
bool CAI_Expresser::SpeakDispatchResponse( AIConcept_t concept, AI_Response *result, IRecipientFilter *filter /* = NULL */ )
#endif
2013-12-03 07:31:46 +04:00
{
char response[ 256 ];
result->GetResponse( response, sizeof( response ) );
#ifdef MAPBASE
if (response[0] == '$')
{
Merged dev changes 9/28/2019 - Experimental RPC stuff for the future - Fixed players running over allies with vehicles (kind of) - Modified redirect filter infrastructure to support when there's no target filter (meaning it will just make sure the entity exists) - Fixed SDK_EyeRefract - Fixed env_beam SetStart/EndEntity - New "OnStateChange" output on NPCs - scripted_face removed (use generic facing VCDs instead) - Fixed RPC - View ID nodraw keyvalue + reflective glass view ID fix - CopyAnimationDataFrom expansion (more variables copied) - Fixed pre-Mapbase env_projectedtextures not updating after loading a save - Fixed(?) player companion grenade throwing being interrupted - Added convars for secondary and NPC shotgun pellet amounts - NPC fade distance/scale transfers to its ragdoll - Made node graph rebuild occur sooner after map load - Added option to disable "node graph out of date" message - Fixed ent_fire delay (decimals discarded before) - "SetFilter" on func_clip_vphysics - Fixed func_tank zero barrel (untested) - Fixed npc_turret_ground parenting fix - Added toggle-able weapon_crossbow experimental hit location code - Fixed ally grenades being considered Combine grenades - Added SDK_MonitorScreen and SDK_UnlitTwoTexture - Updated README - Added !activator/!caller support to logic_collision_pair - Fixed ortho not working in script_intro - Added Nbc66's closed captioning language fix - Applied fade fix to server ragdolls - Added combine_mine friend/foe filters - Fixed env_starfield pausing - Reworked PickupWeapon/Item inputs - Fixed context response system $ usage - Fixed env_break_shooter velocity/speed - Made func_breakable "Spawn on break" support other entities - Fixed OnThrowGrenade > point_entity_replace blip - Added mapname to logic_externaldata - Added "Random Template" to point_template - Added "Use LOS" to trigger_look - Added flags based on L4D(2) to trigger_playermovement - Added npc_combine_s "Alternate Capable" keyvalue that enables both grenades and alt-fire at the same time regardless of elite status - Fixed npc_combine_s DropGrenade input - Miscellaneous code and comment changes
2019-09-29 01:56:52 +03:00
const char *context = response + 1;
const char *replace = GetOuter()->GetContextValue(context);
Merged dev changes 9/28/2019 - Experimental RPC stuff for the future - Fixed players running over allies with vehicles (kind of) - Modified redirect filter infrastructure to support when there's no target filter (meaning it will just make sure the entity exists) - Fixed SDK_EyeRefract - Fixed env_beam SetStart/EndEntity - New "OnStateChange" output on NPCs - scripted_face removed (use generic facing VCDs instead) - Fixed RPC - View ID nodraw keyvalue + reflective glass view ID fix - CopyAnimationDataFrom expansion (more variables copied) - Fixed pre-Mapbase env_projectedtextures not updating after loading a save - Fixed(?) player companion grenade throwing being interrupted - Added convars for secondary and NPC shotgun pellet amounts - NPC fade distance/scale transfers to its ragdoll - Made node graph rebuild occur sooner after map load - Added option to disable "node graph out of date" message - Fixed ent_fire delay (decimals discarded before) - "SetFilter" on func_clip_vphysics - Fixed func_tank zero barrel (untested) - Fixed npc_turret_ground parenting fix - Added toggle-able weapon_crossbow experimental hit location code - Fixed ally grenades being considered Combine grenades - Added SDK_MonitorScreen and SDK_UnlitTwoTexture - Updated README - Added !activator/!caller support to logic_collision_pair - Fixed ortho not working in script_intro - Added Nbc66's closed captioning language fix - Applied fade fix to server ragdolls - Added combine_mine friend/foe filters - Fixed env_starfield pausing - Reworked PickupWeapon/Item inputs - Fixed context response system $ usage - Fixed env_break_shooter velocity/speed - Made func_breakable "Spawn on break" support other entities - Fixed OnThrowGrenade > point_entity_replace blip - Added mapname to logic_externaldata - Added "Random Template" to point_template - Added "Use LOS" to trigger_look - Added flags based on L4D(2) to trigger_playermovement - Added npc_combine_s "Alternate Capable" keyvalue that enables both grenades and alt-fire at the same time regardless of elite status - Fixed npc_combine_s DropGrenade input - Miscellaneous code and comment changes
2019-09-29 01:56:52 +03:00
// If we can't find the context, check modifiers
if (!replace && modifiers)
{
for (int i = 0; i < modifiers->GetCount(); i++)
{
if (FStrEq(context, modifiers->GetName(i)))
{
replace = modifiers->GetValue(i);
break;
}
}
}
if (replace)
{
Merged dev changes 9/28/2019 - Experimental RPC stuff for the future - Fixed players running over allies with vehicles (kind of) - Modified redirect filter infrastructure to support when there's no target filter (meaning it will just make sure the entity exists) - Fixed SDK_EyeRefract - Fixed env_beam SetStart/EndEntity - New "OnStateChange" output on NPCs - scripted_face removed (use generic facing VCDs instead) - Fixed RPC - View ID nodraw keyvalue + reflective glass view ID fix - CopyAnimationDataFrom expansion (more variables copied) - Fixed pre-Mapbase env_projectedtextures not updating after loading a save - Fixed(?) player companion grenade throwing being interrupted - Added convars for secondary and NPC shotgun pellet amounts - NPC fade distance/scale transfers to its ragdoll - Made node graph rebuild occur sooner after map load - Added option to disable "node graph out of date" message - Fixed ent_fire delay (decimals discarded before) - "SetFilter" on func_clip_vphysics - Fixed func_tank zero barrel (untested) - Fixed npc_turret_ground parenting fix - Added toggle-able weapon_crossbow experimental hit location code - Fixed ally grenades being considered Combine grenades - Added SDK_MonitorScreen and SDK_UnlitTwoTexture - Updated README - Added !activator/!caller support to logic_collision_pair - Fixed ortho not working in script_intro - Added Nbc66's closed captioning language fix - Applied fade fix to server ragdolls - Added combine_mine friend/foe filters - Fixed env_starfield pausing - Reworked PickupWeapon/Item inputs - Fixed context response system $ usage - Fixed env_break_shooter velocity/speed - Made func_breakable "Spawn on break" support other entities - Fixed OnThrowGrenade > point_entity_replace blip - Added mapname to logic_externaldata - Added "Random Template" to point_template - Added "Use LOS" to trigger_look - Added flags based on L4D(2) to trigger_playermovement - Added npc_combine_s "Alternate Capable" keyvalue that enables both grenades and alt-fire at the same time regardless of elite status - Fixed npc_combine_s DropGrenade input - Miscellaneous code and comment changes
2019-09-29 01:56:52 +03:00
DevMsg("Replacing %s with %s...\n", response, replace);
Q_strncpy(response, replace, sizeof(response));
Merged dev changes 9/28/2019 - Experimental RPC stuff for the future - Fixed players running over allies with vehicles (kind of) - Modified redirect filter infrastructure to support when there's no target filter (meaning it will just make sure the entity exists) - Fixed SDK_EyeRefract - Fixed env_beam SetStart/EndEntity - New "OnStateChange" output on NPCs - scripted_face removed (use generic facing VCDs instead) - Fixed RPC - View ID nodraw keyvalue + reflective glass view ID fix - CopyAnimationDataFrom expansion (more variables copied) - Fixed pre-Mapbase env_projectedtextures not updating after loading a save - Fixed(?) player companion grenade throwing being interrupted - Added convars for secondary and NPC shotgun pellet amounts - NPC fade distance/scale transfers to its ragdoll - Made node graph rebuild occur sooner after map load - Added option to disable "node graph out of date" message - Fixed ent_fire delay (decimals discarded before) - "SetFilter" on func_clip_vphysics - Fixed func_tank zero barrel (untested) - Fixed npc_turret_ground parenting fix - Added toggle-able weapon_crossbow experimental hit location code - Fixed ally grenades being considered Combine grenades - Added SDK_MonitorScreen and SDK_UnlitTwoTexture - Updated README - Added !activator/!caller support to logic_collision_pair - Fixed ortho not working in script_intro - Added Nbc66's closed captioning language fix - Applied fade fix to server ragdolls - Added combine_mine friend/foe filters - Fixed env_starfield pausing - Reworked PickupWeapon/Item inputs - Fixed context response system $ usage - Fixed env_break_shooter velocity/speed - Made func_breakable "Spawn on break" support other entities - Fixed OnThrowGrenade > point_entity_replace blip - Added mapname to logic_externaldata - Added "Random Template" to point_template - Added "Use LOS" to trigger_look - Added flags based on L4D(2) to trigger_playermovement - Added npc_combine_s "Alternate Capable" keyvalue that enables both grenades and alt-fire at the same time regardless of elite status - Fixed npc_combine_s DropGrenade input - Miscellaneous code and comment changes
2019-09-29 01:56:52 +03:00
// Precache it now because it may not have been precached before
switch ( result->GetType() )
{
case RESPONSE_SPEAK:
{
GetOuter()->PrecacheScriptSound( response );
}
break;
case RESPONSE_SCENE:
{
// TODO: Gender handling?
PrecacheInstancedScene( response );
}
break;
}
}
}
#endif
2013-12-03 07:31:46 +04:00
float delay = result->GetDelay();
bool spoke = false;
soundlevel_t soundlevel = result->GetSoundLevel();
if ( IsSpeaking() && concept[0] != 0 )
{
DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) already speaking, forcing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept );
// Tracker 15911: Can break the game if we stop an imported map placed lcs here, so only
// cancel actor out of instanced scripted scenes. ywb
RemoveActorFromScriptedScenes( GetOuter(), true /*instanced scenes only*/ );
GetOuter()->SentenceStop();
if ( IsRunningScriptedScene( GetOuter() ) )
{
DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) refusing to speak due to scene entity, tossing '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), concept );
delete result;
return false;
}
}
switch ( result->GetType() )
{
default:
case RESPONSE_NONE:
break;
case RESPONSE_SPEAK:
{
if ( !result->ShouldntUseScene() )
{
// This generates a fake CChoreoScene wrapping the sound.txt name
Mapbase v2.0; bulk commit - Added custom map compile tools (vbsp, vvis, vrad) - Changed blink fix (shouldn't change anything in-game) - Added auto-completion to ent_create, npc_create, and the main set of "npc_" debug commands - Added ent_create_aimed, an ent_create equivalent of npc_create_aimed - Made hunters start using the "vs. player" melee animation against smaller NPCs that look weird with the "stab" attack - Added "explosion_sparks" convar, which fixes broken code for giving explosions sparks (disabled by default because of how different it looks) - Made interaction code capable of being dispatched on any entity, not just combat characters - Added npc_barnacle_ignite convar, which lets barnacles be ignited by flares - Fixed certain NPCs getting out of the way for the player when they hate them - Fixed auto-generated "speak" scene responses not using parameters that work on real VCDs - Made "stop_on_nonidle" capable of being used in any mod, not just HL2 episodic mods - Selectable color for ragdoll boogie/point_ragdollboogie - Fixed PickupWeaponInstant not firing weapon pickup outputs - Introduced inputs and keyvalues for "lerping" to math_counter_advanced - Fixed ClearConsole on logic_console - logic_convar should now detect client convars correctly - New NormalizeAngles input on math_vector - logic_modelinfo LookupActivity input - math_generate fixed and expanded to be more like math_counter - Added a WIP game logging system for playtesting maps - Introduced logic_playerinfo, an entity that can read a player's name or ID - Fixed some new filters not working with filter_multi - Added radius pickup spawnflag to func_physbox - Added "Preserve name" spawnflag to weapons - Added cc_achievement_debug message for when an achievement doesn't exist - Made npc_combine_s not speak while in logic_choreographed_scenes - Fixed zombie torsos/legs/headcrabs not being serverside when zombie is forced to server ragdoll - Expanded and cleaned up npc_zombie_custom - Fixed func_commandredirects not cleaning up correctly and sometimes crashing the game - Allowed player squad commands to go through +USE-held objects - Added a bunch of I/O/KV to trigger_waterydeath for better configuration - Changed save comment system to use the chapter title from world properties, and the ability to suppress the title popup that normally results from it - Adjusted game_convar_mod for MP planning - Removed the func_precipitation custom particle/splash code for now, as it was causing problems - Fixed env_global_light not accepting lightcolor - Added "Additional Buttons" to player_speedmod - Added save comment to RPC - Added env_projectedtexture attenuation - Added scripted_sequence OnPreIdleSequence - Added OnCrab to zombies - Added skill_changed game event (may need further testing) - Added a fix for viewmodels flipping under extreme FOV values - Added code that allows mappers to change the skin on shotgunners without it usually flipping back randomly - Fixed a very, very, very major shader performance issue - New SetAbsOrigin/Angles inputs on all entities, analogous to SetLocalOrigin/Angles - Code improvements for I/O involving angles - logic_entity_position improvements/fixes, including a new OutAngles output that outputs the angles on position calls - Alternate collision/player avoidance spawnflag obsoletion enforcement disabled - Enable/DisableHazardLights inputs on the EP2 jalopy, equivalent to the keyvalue - Miscellaneous shader formatting adjustments and fixes - Fixed AlwaysDrawOff on env_projectedtexture not being a valid input
2019-12-14 07:20:02 +03:00
#ifdef MAPBASE
spoke = SpeakAutoGeneratedScene( response, delay, result, filter );
#else
2013-12-03 07:31:46 +04:00
spoke = SpeakAutoGeneratedScene( response, delay );
Mapbase v2.0; bulk commit - Added custom map compile tools (vbsp, vvis, vrad) - Changed blink fix (shouldn't change anything in-game) - Added auto-completion to ent_create, npc_create, and the main set of "npc_" debug commands - Added ent_create_aimed, an ent_create equivalent of npc_create_aimed - Made hunters start using the "vs. player" melee animation against smaller NPCs that look weird with the "stab" attack - Added "explosion_sparks" convar, which fixes broken code for giving explosions sparks (disabled by default because of how different it looks) - Made interaction code capable of being dispatched on any entity, not just combat characters - Added npc_barnacle_ignite convar, which lets barnacles be ignited by flares - Fixed certain NPCs getting out of the way for the player when they hate them - Fixed auto-generated "speak" scene responses not using parameters that work on real VCDs - Made "stop_on_nonidle" capable of being used in any mod, not just HL2 episodic mods - Selectable color for ragdoll boogie/point_ragdollboogie - Fixed PickupWeaponInstant not firing weapon pickup outputs - Introduced inputs and keyvalues for "lerping" to math_counter_advanced - Fixed ClearConsole on logic_console - logic_convar should now detect client convars correctly - New NormalizeAngles input on math_vector - logic_modelinfo LookupActivity input - math_generate fixed and expanded to be more like math_counter - Added a WIP game logging system for playtesting maps - Introduced logic_playerinfo, an entity that can read a player's name or ID - Fixed some new filters not working with filter_multi - Added radius pickup spawnflag to func_physbox - Added "Preserve name" spawnflag to weapons - Added cc_achievement_debug message for when an achievement doesn't exist - Made npc_combine_s not speak while in logic_choreographed_scenes - Fixed zombie torsos/legs/headcrabs not being serverside when zombie is forced to server ragdoll - Expanded and cleaned up npc_zombie_custom - Fixed func_commandredirects not cleaning up correctly and sometimes crashing the game - Allowed player squad commands to go through +USE-held objects - Added a bunch of I/O/KV to trigger_waterydeath for better configuration - Changed save comment system to use the chapter title from world properties, and the ability to suppress the title popup that normally results from it - Adjusted game_convar_mod for MP planning - Removed the func_precipitation custom particle/splash code for now, as it was causing problems - Fixed env_global_light not accepting lightcolor - Added "Additional Buttons" to player_speedmod - Added save comment to RPC - Added env_projectedtexture attenuation - Added scripted_sequence OnPreIdleSequence - Added OnCrab to zombies - Added skill_changed game event (may need further testing) - Added a fix for viewmodels flipping under extreme FOV values - Added code that allows mappers to change the skin on shotgunners without it usually flipping back randomly - Fixed a very, very, very major shader performance issue - New SetAbsOrigin/Angles inputs on all entities, analogous to SetLocalOrigin/Angles - Code improvements for I/O involving angles - logic_entity_position improvements/fixes, including a new OutAngles output that outputs the angles on position calls - Alternate collision/player avoidance spawnflag obsoletion enforcement disabled - Enable/DisableHazardLights inputs on the EP2 jalopy, equivalent to the keyvalue - Miscellaneous shader formatting adjustments and fixes - Fixed AlwaysDrawOff on env_projectedtexture not being a valid input
2019-12-14 07:20:02 +03:00
#endif
2013-12-03 07:31:46 +04:00
}
else
{
float speakTime = GetResponseDuration( result );
GetOuter()->EmitSound( response );
DevMsg( "SpeakDispatchResponse: Entity ( %i/%s ) playing sound '%s'\n", GetOuter()->entindex(), STRING( GetOuter()->GetEntityName() ), response );
NoteSpeaking( speakTime, delay );
spoke = true;
}
}
break;
case RESPONSE_SENTENCE:
{
spoke = ( -1 != SpeakRawSentence( response, delay, VOL_NORM, soundlevel ) ) ? true : false;
}
break;
case RESPONSE_SCENE:
{
spoke = SpeakRawScene( response, delay, result, filter );
}
break;
case RESPONSE_RESPONSE:
{
// This should have been recursively resolved already
Assert( 0 );
}
break;
case RESPONSE_PRINT:
{
if ( g_pDeveloper->GetInt() > 0 )
{
Vector vPrintPos;
GetOuter()->CollisionProp()->NormalizedToWorldSpace( Vector(0.5,0.5,1.0f), &vPrintPos );
NDebugOverlay::Text( vPrintPos, response, true, 1.5 );
spoke = true;
}
}
break;
}
if ( spoke )
{
m_flLastTimeAcceptedSpeak = gpGlobals->curtime;
if ( DebuggingSpeech() && g_pDeveloper->GetInt() > 0 && response && result->GetType() != RESPONSE_PRINT )
{
Vector vPrintPos;
GetOuter()->CollisionProp()->NormalizedToWorldSpace( Vector(0.5,0.5,1.0f), &vPrintPos );
NDebugOverlay::Text( vPrintPos, CFmtStr( "%s: %s", concept, response ), true, 1.5 );
}
#ifdef MAPBASE
if (result->GetContext())
{
const char *pszContext = result->GetContext();
// Check for operators
char *pOperator = Q_strstr(pszContext, ":")+1;
if (pOperator && (pOperator[0] == '+' || pOperator[0] == '-' ||
pOperator[0] == '*' || pOperator[0] == '/'))
{
pszContext = ParseApplyContext(pszContext);
}
int iContextFlags = result->GetContextFlags();
if ( iContextFlags & APPLYCONTEXT_SQUAD )
{
CAI_BaseNPC *pNPC = GetOuter()->MyNPCPointer();
if (pNPC && pNPC->GetSquad())
{
AISquadIter_t iter;
CAI_BaseNPC *pSquadmate = pNPC->GetSquad()->GetFirstMember( &iter );
while ( pSquadmate )
{
pSquadmate->AddContext( pszContext );
pSquadmate = pNPC->GetSquad()->GetNextMember( &iter );
}
}
}
if ( iContextFlags & APPLYCONTEXT_ENEMY )
{
CBaseEntity *pEnemy = GetOuter()->GetEnemy();
if ( pEnemy )
{
pEnemy->AddContext( pszContext );
}
}
if ( iContextFlags & APPLYCONTEXT_WORLD )
{
CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) );
if ( pEntity )
{
pEntity->AddContext( pszContext );
}
}
if ( iContextFlags == 0 || iContextFlags & APPLYCONTEXT_SELF )
{
GetOuter()->AddContext( pszContext );
}
}
#else
2013-12-03 07:31:46 +04:00
if ( result->IsApplyContextToWorld() )
{
CBaseEntity *pEntity = CBaseEntity::Instance( engine->PEntityOfEntIndex( 0 ) );
if ( pEntity )
{
pEntity->AddContext( result->GetContext() );
}
}
else
{
GetOuter()->AddContext( result->GetContext() );
}
#endif
2013-12-03 07:31:46 +04:00
SetSpokeConcept( concept, result );
}
else
{
delete result;
}
return spoke;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *response -
// Output : float
//-----------------------------------------------------------------------------
float CAI_Expresser::GetResponseDuration( AI_Response *result )
{
Assert( result );
char response[ 256 ];
result->GetResponse( response, sizeof( response ) );
switch ( result->GetType() )
{
default:
case RESPONSE_NONE:
break;
case RESPONSE_SPEAK:
{
return GetOuter()->GetSoundDuration( response, STRING( GetOuter()->GetModelName() ) );
}
break;
case RESPONSE_SENTENCE:
{
Assert( 0 );
return 999.0f;
}
break;
case RESPONSE_SCENE:
{
return GetSceneDuration( response );
}
break;
case RESPONSE_RESPONSE:
{
// This should have been recursively resolved already
Assert( 0 );
}
break;
case RESPONSE_PRINT:
{
return 1.0;
}
break;
}
return 0.0f;
}
//-----------------------------------------------------------------------------
// Purpose: Placeholder for rules based response system
// Input : concept -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_Expresser::Speak( AIConcept_t concept, const char *modifiers /*= NULL*/, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ )
{
AI_Response *result = SpeakFindResponse( concept, modifiers );
if ( !result )
{
return false;
}
SpeechMsg( GetOuter(), "%s (%p) spoke %s (%f)\n", STRING(GetOuter()->GetEntityName()), GetOuter(), concept, gpGlobals->curtime );
bool spoke = SpeakDispatchResponse( concept, result, filter );
if ( pszOutResponseChosen )
{
result->GetResponse( pszOutResponseChosen, bufsize );
}
return spoke;
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Uses an AI_CriteriaSet directly instead of using context-format modifier text.
// Input : concept -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CAI_Expresser::Speak( AIConcept_t concept, AI_CriteriaSet& modifiers, char *pszOutResponseChosen /* = NULL*/, size_t bufsize /* = 0 */, IRecipientFilter *filter /* = NULL */ )
{
AI_Response *result = SpeakFindResponse( concept, modifiers );
if ( !result )
{
return false;
}
SpeechMsg( GetOuter(), "%s (%p) spoke %s (%f)\n", STRING(GetOuter()->GetEntityName()), GetOuter(), concept, gpGlobals->curtime );
Merged dev changes 9/28/2019 - Experimental RPC stuff for the future - Fixed players running over allies with vehicles (kind of) - Modified redirect filter infrastructure to support when there's no target filter (meaning it will just make sure the entity exists) - Fixed SDK_EyeRefract - Fixed env_beam SetStart/EndEntity - New "OnStateChange" output on NPCs - scripted_face removed (use generic facing VCDs instead) - Fixed RPC - View ID nodraw keyvalue + reflective glass view ID fix - CopyAnimationDataFrom expansion (more variables copied) - Fixed pre-Mapbase env_projectedtextures not updating after loading a save - Fixed(?) player companion grenade throwing being interrupted - Added convars for secondary and NPC shotgun pellet amounts - NPC fade distance/scale transfers to its ragdoll - Made node graph rebuild occur sooner after map load - Added option to disable "node graph out of date" message - Fixed ent_fire delay (decimals discarded before) - "SetFilter" on func_clip_vphysics - Fixed func_tank zero barrel (untested) - Fixed npc_turret_ground parenting fix - Added toggle-able weapon_crossbow experimental hit location code - Fixed ally grenades being considered Combine grenades - Added SDK_MonitorScreen and SDK_UnlitTwoTexture - Updated README - Added !activator/!caller support to logic_collision_pair - Fixed ortho not working in script_intro - Added Nbc66's closed captioning language fix - Applied fade fix to server ragdolls - Added combine_mine friend/foe filters - Fixed env_starfield pausing - Reworked PickupWeapon/Item inputs - Fixed context response system $ usage - Fixed env_break_shooter velocity/speed - Made func_breakable "Spawn on break" support other entities - Fixed OnThrowGrenade > point_entity_replace blip - Added mapname to logic_externaldata - Added "Random Template" to point_template - Added "Use LOS" to trigger_look - Added flags based on L4D(2) to trigger_playermovement - Added npc_combine_s "Alternate Capable" keyvalue that enables both grenades and alt-fire at the same time regardless of elite status - Fixed npc_combine_s DropGrenade input - Miscellaneous code and comment changes
2019-09-29 01:56:52 +03:00
bool spoke = SpeakDispatchResponse( concept, result, filter, &modifiers );
if ( pszOutResponseChosen )
{
result->GetResponse( pszOutResponseChosen, bufsize );
}
return spoke;
}
#endif
2013-12-03 07:31:46 +04:00
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CAI_Expresser::SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter /* = NULL */ )
{
float sceneLength = GetOuter()->PlayScene( pszScene, delay, response, filter );
if ( sceneLength > 0 )
{
SpeechMsg( GetOuter(), "SpeakRawScene( %s, %f) %f\n", pszScene, delay, sceneLength );
#if defined( HL2_EPISODIC ) || defined( TF_DLL )
char szInstanceFilename[256];
GetOuter()->GenderExpandString( pszScene, szInstanceFilename, sizeof( szInstanceFilename ) );
// Only mark ourselves as speaking if the scene has speech
if ( GetSceneSpeechCount(szInstanceFilename) > 0 )
{
NoteSpeaking( sceneLength, delay );
}
#else
NoteSpeaking( sceneLength, delay );
#endif
return true;
}
return false;
}
// This will create a fake .vcd/CChoreoScene to wrap the sound to be played
Mapbase v2.0; bulk commit - Added custom map compile tools (vbsp, vvis, vrad) - Changed blink fix (shouldn't change anything in-game) - Added auto-completion to ent_create, npc_create, and the main set of "npc_" debug commands - Added ent_create_aimed, an ent_create equivalent of npc_create_aimed - Made hunters start using the "vs. player" melee animation against smaller NPCs that look weird with the "stab" attack - Added "explosion_sparks" convar, which fixes broken code for giving explosions sparks (disabled by default because of how different it looks) - Made interaction code capable of being dispatched on any entity, not just combat characters - Added npc_barnacle_ignite convar, which lets barnacles be ignited by flares - Fixed certain NPCs getting out of the way for the player when they hate them - Fixed auto-generated "speak" scene responses not using parameters that work on real VCDs - Made "stop_on_nonidle" capable of being used in any mod, not just HL2 episodic mods - Selectable color for ragdoll boogie/point_ragdollboogie - Fixed PickupWeaponInstant not firing weapon pickup outputs - Introduced inputs and keyvalues for "lerping" to math_counter_advanced - Fixed ClearConsole on logic_console - logic_convar should now detect client convars correctly - New NormalizeAngles input on math_vector - logic_modelinfo LookupActivity input - math_generate fixed and expanded to be more like math_counter - Added a WIP game logging system for playtesting maps - Introduced logic_playerinfo, an entity that can read a player's name or ID - Fixed some new filters not working with filter_multi - Added radius pickup spawnflag to func_physbox - Added "Preserve name" spawnflag to weapons - Added cc_achievement_debug message for when an achievement doesn't exist - Made npc_combine_s not speak while in logic_choreographed_scenes - Fixed zombie torsos/legs/headcrabs not being serverside when zombie is forced to server ragdoll - Expanded and cleaned up npc_zombie_custom - Fixed func_commandredirects not cleaning up correctly and sometimes crashing the game - Allowed player squad commands to go through +USE-held objects - Added a bunch of I/O/KV to trigger_waterydeath for better configuration - Changed save comment system to use the chapter title from world properties, and the ability to suppress the title popup that normally results from it - Adjusted game_convar_mod for MP planning - Removed the func_precipitation custom particle/splash code for now, as it was causing problems - Fixed env_global_light not accepting lightcolor - Added "Additional Buttons" to player_speedmod - Added save comment to RPC - Added env_projectedtexture attenuation - Added scripted_sequence OnPreIdleSequence - Added OnCrab to zombies - Added skill_changed game event (may need further testing) - Added a fix for viewmodels flipping under extreme FOV values - Added code that allows mappers to change the skin on shotgunners without it usually flipping back randomly - Fixed a very, very, very major shader performance issue - New SetAbsOrigin/Angles inputs on all entities, analogous to SetLocalOrigin/Angles - Code improvements for I/O involving angles - logic_entity_position improvements/fixes, including a new OutAngles output that outputs the angles on position calls - Alternate collision/player avoidance spawnflag obsoletion enforcement disabled - Enable/DisableHazardLights inputs on the EP2 jalopy, equivalent to the keyvalue - Miscellaneous shader formatting adjustments and fixes - Fixed AlwaysDrawOff on env_projectedtexture not being a valid input
2019-12-14 07:20:02 +03:00
#ifdef MAPBASE
bool CAI_Expresser::SpeakAutoGeneratedScene( char const *soundname, float delay, AI_Response *response, IRecipientFilter *filter )
#else
2013-12-03 07:31:46 +04:00
bool CAI_Expresser::SpeakAutoGeneratedScene( char const *soundname, float delay )
Mapbase v2.0; bulk commit - Added custom map compile tools (vbsp, vvis, vrad) - Changed blink fix (shouldn't change anything in-game) - Added auto-completion to ent_create, npc_create, and the main set of "npc_" debug commands - Added ent_create_aimed, an ent_create equivalent of npc_create_aimed - Made hunters start using the "vs. player" melee animation against smaller NPCs that look weird with the "stab" attack - Added "explosion_sparks" convar, which fixes broken code for giving explosions sparks (disabled by default because of how different it looks) - Made interaction code capable of being dispatched on any entity, not just combat characters - Added npc_barnacle_ignite convar, which lets barnacles be ignited by flares - Fixed certain NPCs getting out of the way for the player when they hate them - Fixed auto-generated "speak" scene responses not using parameters that work on real VCDs - Made "stop_on_nonidle" capable of being used in any mod, not just HL2 episodic mods - Selectable color for ragdoll boogie/point_ragdollboogie - Fixed PickupWeaponInstant not firing weapon pickup outputs - Introduced inputs and keyvalues for "lerping" to math_counter_advanced - Fixed ClearConsole on logic_console - logic_convar should now detect client convars correctly - New NormalizeAngles input on math_vector - logic_modelinfo LookupActivity input - math_generate fixed and expanded to be more like math_counter - Added a WIP game logging system for playtesting maps - Introduced logic_playerinfo, an entity that can read a player's name or ID - Fixed some new filters not working with filter_multi - Added radius pickup spawnflag to func_physbox - Added "Preserve name" spawnflag to weapons - Added cc_achievement_debug message for when an achievement doesn't exist - Made npc_combine_s not speak while in logic_choreographed_scenes - Fixed zombie torsos/legs/headcrabs not being serverside when zombie is forced to server ragdoll - Expanded and cleaned up npc_zombie_custom - Fixed func_commandredirects not cleaning up correctly and sometimes crashing the game - Allowed player squad commands to go through +USE-held objects - Added a bunch of I/O/KV to trigger_waterydeath for better configuration - Changed save comment system to use the chapter title from world properties, and the ability to suppress the title popup that normally results from it - Adjusted game_convar_mod for MP planning - Removed the func_precipitation custom particle/splash code for now, as it was causing problems - Fixed env_global_light not accepting lightcolor - Added "Additional Buttons" to player_speedmod - Added save comment to RPC - Added env_projectedtexture attenuation - Added scripted_sequence OnPreIdleSequence - Added OnCrab to zombies - Added skill_changed game event (may need further testing) - Added a fix for viewmodels flipping under extreme FOV values - Added code that allows mappers to change the skin on shotgunners without it usually flipping back randomly - Fixed a very, very, very major shader performance issue - New SetAbsOrigin/Angles inputs on all entities, analogous to SetLocalOrigin/Angles - Code improvements for I/O involving angles - logic_entity_position improvements/fixes, including a new OutAngles output that outputs the angles on position calls - Alternate collision/player avoidance spawnflag obsoletion enforcement disabled - Enable/DisableHazardLights inputs on the EP2 jalopy, equivalent to the keyvalue - Miscellaneous shader formatting adjustments and fixes - Fixed AlwaysDrawOff on env_projectedtexture not being a valid input
2019-12-14 07:20:02 +03:00
#endif
2013-12-03 07:31:46 +04:00
{
Mapbase v2.0; bulk commit - Added custom map compile tools (vbsp, vvis, vrad) - Changed blink fix (shouldn't change anything in-game) - Added auto-completion to ent_create, npc_create, and the main set of "npc_" debug commands - Added ent_create_aimed, an ent_create equivalent of npc_create_aimed - Made hunters start using the "vs. player" melee animation against smaller NPCs that look weird with the "stab" attack - Added "explosion_sparks" convar, which fixes broken code for giving explosions sparks (disabled by default because of how different it looks) - Made interaction code capable of being dispatched on any entity, not just combat characters - Added npc_barnacle_ignite convar, which lets barnacles be ignited by flares - Fixed certain NPCs getting out of the way for the player when they hate them - Fixed auto-generated "speak" scene responses not using parameters that work on real VCDs - Made "stop_on_nonidle" capable of being used in any mod, not just HL2 episodic mods - Selectable color for ragdoll boogie/point_ragdollboogie - Fixed PickupWeaponInstant not firing weapon pickup outputs - Introduced inputs and keyvalues for "lerping" to math_counter_advanced - Fixed ClearConsole on logic_console - logic_convar should now detect client convars correctly - New NormalizeAngles input on math_vector - logic_modelinfo LookupActivity input - math_generate fixed and expanded to be more like math_counter - Added a WIP game logging system for playtesting maps - Introduced logic_playerinfo, an entity that can read a player's name or ID - Fixed some new filters not working with filter_multi - Added radius pickup spawnflag to func_physbox - Added "Preserve name" spawnflag to weapons - Added cc_achievement_debug message for when an achievement doesn't exist - Made npc_combine_s not speak while in logic_choreographed_scenes - Fixed zombie torsos/legs/headcrabs not being serverside when zombie is forced to server ragdoll - Expanded and cleaned up npc_zombie_custom - Fixed func_commandredirects not cleaning up correctly and sometimes crashing the game - Allowed player squad commands to go through +USE-held objects - Added a bunch of I/O/KV to trigger_waterydeath for better configuration - Changed save comment system to use the chapter title from world properties, and the ability to suppress the title popup that normally results from it - Adjusted game_convar_mod for MP planning - Removed the func_precipitation custom particle/splash code for now, as it was causing problems - Fixed env_global_light not accepting lightcolor - Added "Additional Buttons" to player_speedmod - Added save comment to RPC - Added env_projectedtexture attenuation - Added scripted_sequence OnPreIdleSequence - Added OnCrab to zombies - Added skill_changed game event (may need further testing) - Added a fix for viewmodels flipping under extreme FOV values - Added code that allows mappers to change the skin on shotgunners without it usually flipping back randomly - Fixed a very, very, very major shader performance issue - New SetAbsOrigin/Angles inputs on all entities, analogous to SetLocalOrigin/Angles - Code improvements for I/O involving angles - logic_entity_position improvements/fixes, including a new OutAngles output that outputs the angles on position calls - Alternate collision/player avoidance spawnflag obsoletion enforcement disabled - Enable/DisableHazardLights inputs on the EP2 jalopy, equivalent to the keyvalue - Miscellaneous shader formatting adjustments and fixes - Fixed AlwaysDrawOff on env_projectedtexture not being a valid input
2019-12-14 07:20:02 +03:00
#ifdef MAPBASE
float speakTime = GetOuter()->PlayAutoGeneratedSoundScene( soundname, delay, response, filter );
#else
2013-12-03 07:31:46 +04:00
float speakTime = GetOuter()->PlayAutoGeneratedSoundScene( soundname );
Mapbase v2.0; bulk commit - Added custom map compile tools (vbsp, vvis, vrad) - Changed blink fix (shouldn't change anything in-game) - Added auto-completion to ent_create, npc_create, and the main set of "npc_" debug commands - Added ent_create_aimed, an ent_create equivalent of npc_create_aimed - Made hunters start using the "vs. player" melee animation against smaller NPCs that look weird with the "stab" attack - Added "explosion_sparks" convar, which fixes broken code for giving explosions sparks (disabled by default because of how different it looks) - Made interaction code capable of being dispatched on any entity, not just combat characters - Added npc_barnacle_ignite convar, which lets barnacles be ignited by flares - Fixed certain NPCs getting out of the way for the player when they hate them - Fixed auto-generated "speak" scene responses not using parameters that work on real VCDs - Made "stop_on_nonidle" capable of being used in any mod, not just HL2 episodic mods - Selectable color for ragdoll boogie/point_ragdollboogie - Fixed PickupWeaponInstant not firing weapon pickup outputs - Introduced inputs and keyvalues for "lerping" to math_counter_advanced - Fixed ClearConsole on logic_console - logic_convar should now detect client convars correctly - New NormalizeAngles input on math_vector - logic_modelinfo LookupActivity input - math_generate fixed and expanded to be more like math_counter - Added a WIP game logging system for playtesting maps - Introduced logic_playerinfo, an entity that can read a player's name or ID - Fixed some new filters not working with filter_multi - Added radius pickup spawnflag to func_physbox - Added "Preserve name" spawnflag to weapons - Added cc_achievement_debug message for when an achievement doesn't exist - Made npc_combine_s not speak while in logic_choreographed_scenes - Fixed zombie torsos/legs/headcrabs not being serverside when zombie is forced to server ragdoll - Expanded and cleaned up npc_zombie_custom - Fixed func_commandredirects not cleaning up correctly and sometimes crashing the game - Allowed player squad commands to go through +USE-held objects - Added a bunch of I/O/KV to trigger_waterydeath for better configuration - Changed save comment system to use the chapter title from world properties, and the ability to suppress the title popup that normally results from it - Adjusted game_convar_mod for MP planning - Removed the func_precipitation custom particle/splash code for now, as it was causing problems - Fixed env_global_light not accepting lightcolor - Added "Additional Buttons" to player_speedmod - Added save comment to RPC - Added env_projectedtexture attenuation - Added scripted_sequence OnPreIdleSequence - Added OnCrab to zombies - Added skill_changed game event (may need further testing) - Added a fix for viewmodels flipping under extreme FOV values - Added code that allows mappers to change the skin on shotgunners without it usually flipping back randomly - Fixed a very, very, very major shader performance issue - New SetAbsOrigin/Angles inputs on all entities, analogous to SetLocalOrigin/Angles - Code improvements for I/O involving angles - logic_entity_position improvements/fixes, including a new OutAngles output that outputs the angles on position calls - Alternate collision/player avoidance spawnflag obsoletion enforcement disabled - Enable/DisableHazardLights inputs on the EP2 jalopy, equivalent to the keyvalue - Miscellaneous shader formatting adjustments and fixes - Fixed AlwaysDrawOff on env_projectedtexture not being a valid input
2019-12-14 07:20:02 +03:00
#endif
2013-12-03 07:31:46 +04:00
if ( speakTime > 0 )
{
SpeechMsg( GetOuter(), "SpeakAutoGeneratedScene( %s, %f) %f\n", soundname, delay, speakTime );
NoteSpeaking( speakTime, delay );
return true;
}
return false;
}
//-------------------------------------
int CAI_Expresser::SpeakRawSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, CBaseEntity *pListener )
{
int sentenceIndex = -1;
if ( !pszSentence )
return sentenceIndex;
if ( pszSentence[0] == AI_SP_SPECIFIC_SENTENCE )
{
sentenceIndex = SENTENCEG_Lookup( pszSentence );
if( sentenceIndex == -1 )
{
// sentence not found
return -1;
}
CPASAttenuationFilter filter( GetOuter(), soundlevel );
CBaseEntity::EmitSentenceByIndex( filter, GetOuter()->entindex(), CHAN_VOICE, sentenceIndex, volume, soundlevel, 0, GetVoicePitch());
}
else
{
sentenceIndex = SENTENCEG_PlayRndSz( GetOuter()->NetworkProp()->edict(), pszSentence, volume, soundlevel, 0, GetVoicePitch() );
}
SpeechMsg( GetOuter(), "SpeakRawSentence( %s, %f) %f\n", pszSentence, delay, engine->SentenceLength( sentenceIndex ) );
NoteSpeaking( engine->SentenceLength( sentenceIndex ), delay );
return sentenceIndex;
}
//-------------------------------------
void CAI_Expresser::BlockSpeechUntil( float time )
{
SpeechMsg( GetOuter(), "BlockSpeechUntil(%f) %f\n", time, time - gpGlobals->curtime );
m_flBlockedTalkTime = time;
}
//-------------------------------------
void CAI_Expresser::NoteSpeaking( float duration, float delay )
{
duration += delay;
GetSink()->OnStartSpeaking();
if ( duration <= 0 )
{
// no duration :(
m_flStopTalkTime = gpGlobals->curtime + 3;
duration = 0;
}
else
{
m_flStopTalkTime = gpGlobals->curtime + duration;
}
m_flStopTalkTimeWithoutDelay = m_flStopTalkTime - delay;
SpeechMsg( GetOuter(), "NoteSpeaking( %f, %f ) (stop at %f)\n", duration, delay, m_flStopTalkTime );
if ( GetSink()->UseSemaphore() )
{
CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( GetOuter() );
if ( pSemaphore )
{
pSemaphore->Acquire( duration, GetOuter() );
}
}
}
//-------------------------------------
void CAI_Expresser::ForceNotSpeaking( void )
{
if ( IsSpeaking() )
{
m_flStopTalkTime = gpGlobals->curtime;
m_flStopTalkTimeWithoutDelay = gpGlobals->curtime;
CAI_TimedSemaphore *pSemaphore = GetMySpeechSemaphore( GetOuter() );
if ( pSemaphore )
{
if ( pSemaphore->GetOwner() == GetOuter() )
{
pSemaphore->Release();
}
}
}
}
//-------------------------------------
bool CAI_Expresser::IsSpeaking( void )
{
if ( m_flStopTalkTime > gpGlobals->curtime )
SpeechMsg( GetOuter(), "IsSpeaking() %f\n", m_flStopTalkTime - gpGlobals->curtime );
if ( m_flLastTimeAcceptedSpeak == gpGlobals->curtime ) // only one speak accepted per think
return true;
return ( m_flStopTalkTime > gpGlobals->curtime );
}
//-------------------------------------
bool CAI_Expresser::CanSpeak()
{
if ( m_flLastTimeAcceptedSpeak == gpGlobals->curtime ) // only one speak accepted per think
return false;
float timeOk = MAX( m_flStopTalkTime, m_flBlockedTalkTime );
return ( timeOk <= gpGlobals->curtime );
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if it's ok for this entity to speak after himself.
// The base CanSpeak() includes the default speech delay, and won't
// return true until that delay time has passed after finishing the
// speech. This returns true as soon as the speech finishes.
//-----------------------------------------------------------------------------
bool CAI_Expresser::CanSpeakAfterMyself()
{
if ( m_flLastTimeAcceptedSpeak == gpGlobals->curtime ) // only one speak accepted per think
return false;
float timeOk = MAX( m_flStopTalkTimeWithoutDelay, m_flBlockedTalkTime );
return ( timeOk <= gpGlobals->curtime );
}
//-------------------------------------
bool CAI_Expresser::CanSpeakConcept( AIConcept_t concept )
{
// Not in history?
int iter = m_ConceptHistories.Find( concept );
if ( iter == m_ConceptHistories.InvalidIndex() )
{
return true;
}
ConceptHistory_t *history = &m_ConceptHistories[iter];
Assert( history );
AI_Response *response = history->response;
if ( !response )
return true;
if ( response->GetSpeakOnce() )
return false;
float respeakDelay = response->GetRespeakDelay();
if ( respeakDelay != 0.0f )
{
if ( history->timeSpoken != -1 && ( gpGlobals->curtime < history->timeSpoken + respeakDelay ) )
return false;
}
return true;
}
//-------------------------------------
bool CAI_Expresser::SpokeConcept( AIConcept_t concept )
{
return GetTimeSpokeConcept( concept ) != -1.f;
}
//-------------------------------------
float CAI_Expresser::GetTimeSpokeConcept( AIConcept_t concept )
{
int iter = m_ConceptHistories.Find( concept );
if ( iter == m_ConceptHistories.InvalidIndex() )
return -1;
ConceptHistory_t *h = &m_ConceptHistories[iter];
return h->timeSpoken;
}
//-------------------------------------
void CAI_Expresser::SetSpokeConcept( AIConcept_t concept, AI_Response *response, bool bCallback )
{
int idx = m_ConceptHistories.Find( concept );
if ( idx == m_ConceptHistories.InvalidIndex() )
{
ConceptHistory_t h;
h.timeSpoken = gpGlobals->curtime;
idx = m_ConceptHistories.Insert( concept, h );
}
ConceptHistory_t *slot = &m_ConceptHistories[ idx ];
slot->timeSpoken = gpGlobals->curtime;
// Update response info
if ( response )
{
AI_Response *r = slot->response;
if ( r )
{
delete r;
}
// FIXME: Are we leaking AI_Responses?
slot->response = response;
}
#ifdef MAPBASE
// This "weapondelay" was just in player allies before.
// Putting it here eliminates the need to implement OnSpokeConcept on all NPCs for weapondelay.
if ( bCallback )
{
if( response != NULL && (response->GetParams()->flags & AI_ResponseParams::RG_WEAPONDELAY) )
{
if ( GetOuter()->IsNPC() )
{
// Stop shooting, as instructed, so that my speech can be heard.
GetOuter()->MyNPCPointer()->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + response->GetWeaponDelay() );
}
else
{
char szResponse[64];
response->GetName(szResponse, sizeof(szResponse));
Warning("%s response %s wants to use weapondelay, but %s is not a NPC!\n", GetOuter()->GetDebugName(), szResponse, GetOuter()->GetDebugName());
}
}
GetSink()->OnSpokeConcept( concept, response );
}
#else
2013-12-03 07:31:46 +04:00
if ( bCallback )
GetSink()->OnSpokeConcept( concept, response );
#endif
2013-12-03 07:31:46 +04:00
}
//-------------------------------------
void CAI_Expresser::ClearSpokeConcept( AIConcept_t concept )
{
m_ConceptHistories.Remove( concept );
}
//-------------------------------------
void CAI_Expresser::DumpHistories()
{
int c = 1;
for ( int i = m_ConceptHistories.First(); i != m_ConceptHistories.InvalidIndex(); i = m_ConceptHistories.Next(i ) )
{
ConceptHistory_t *h = &m_ConceptHistories[ i ];
DevMsg( "%i: %s at %f\n", c++, m_ConceptHistories.GetElementName( i ), h->timeSpoken );
}
}
//-------------------------------------
bool CAI_Expresser::IsValidResponse( ResponseType_t type, const char *pszValue )
{
if ( type == RESPONSE_SCENE )
{
char szInstanceFilename[256];
GetOuter()->GenderExpandString( pszValue, szInstanceFilename, sizeof( szInstanceFilename ) );
return ( GetSceneDuration( szInstanceFilename ) > 0 );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CAI_TimedSemaphore *CAI_Expresser::GetMySpeechSemaphore( CBaseEntity *pNpc )
{
if ( !pNpc->MyNPCPointer() )
return NULL;
return (pNpc->MyNPCPointer()->IsPlayerAlly() ? &g_AIFriendliesTalkSemaphore : &g_AIFoesTalkSemaphore );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CAI_Expresser::SpeechMsg( CBaseEntity *pFlex, const char *pszFormat, ... )
{
if ( !DebuggingSpeech() )
return;
char string[ 2048 ];
va_list argptr;
va_start( argptr, pszFormat );
Q_vsnprintf( string, sizeof(string), pszFormat, argptr );
va_end( argptr );
if ( pFlex->MyNPCPointer() )
{
DevMsg( pFlex->MyNPCPointer(), string );
}
else
{
DevMsg( "%s", string );
}
UTIL_LogPrintf( string );
}
#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
char *CAI_Expresser::ParseApplyContext( const char *szContext )
{
char szKey[128];
char szValue[128];
float flDuration = 0.0;
SplitContext(szContext, szKey, sizeof(szKey), szValue, sizeof(szValue), &flDuration);
// This is the value without the operator
const char *pszValue = szValue + 1;
// This is the operator itself
char cOperator = szValue[0];
// This is the value to operate with (e.g. the "7" in "+7")
float flNewValue = atof( pszValue );
if (flNewValue == 0.0f)
{
// If it's really 0, then this is a waste of time
Warning("\"%s\" was detected by applyContext operators as an operable number, but it's not.\n", szValue);
return strdup(szContext);
}
// This is the existing value; will be operated upon and become the final assignment
float flValue = 0.0f;
const char *szExistingValue = GetOuter()->GetContextValue(szKey);
if (szExistingValue)
flValue = atof(szExistingValue);
// Do the operation based on what the character was
switch (cOperator)
{
case '+': flValue += flNewValue; break;
case '-': flValue -= flNewValue; break;
case '*': flValue *= flNewValue; break;
case '/': flValue /= flNewValue; break;
}
Q_snprintf(szValue, sizeof(szValue), "%f", flValue);
// Remove all trailing 0's from the float to maintain whole integers
int i;
for (i = strlen(szValue)-1; szValue[i] == '0'; i--)
{
szValue[i] = '\0';
}
// If there were only zeroes past the period, this is a whole number. Remove the period
if (szValue[i] == '.')
szValue[i] = '\0';
return UTIL_VarArgs("%s:%s:%f", szKey, szValue, flDuration);
}
#endif
2013-12-03 07:31:46 +04:00
//-----------------------------------------------------------------------------
void CAI_ExpresserHost_NPC_DoModifyOrAppendCriteria( CAI_BaseNPC *pSpeaker, AI_CriteriaSet& set )
{
// Append current activity name
const char *pActivityName = pSpeaker->GetActivityName( pSpeaker->GetActivity() );
if ( pActivityName )
{
set.AppendCriteria( "activity", pActivityName );
}
static const char *pStateNames[] = { "None", "Idle", "Alert", "Combat", "Scripted", "PlayDead", "Dead" };
if ( (int)pSpeaker->m_NPCState < ARRAYSIZE(pStateNames) )
{
set.AppendCriteria( "npcstate", UTIL_VarArgs( "[NPCState::%s]", pStateNames[pSpeaker->m_NPCState] ) );
}
#ifndef MAPBASE
2013-12-03 07:31:46 +04:00
if ( pSpeaker->GetEnemy() )
{
set.AppendCriteria( "enemy", pSpeaker->GetEnemy()->GetClassname() );
set.AppendCriteria( "timesincecombat", "-1" );
}
else
{
if ( pSpeaker->GetLastEnemyTime() == 0.0 )
set.AppendCriteria( "timesincecombat", "999999.0" );
else
set.AppendCriteria( "timesincecombat", UTIL_VarArgs( "%f", gpGlobals->curtime - pSpeaker->GetLastEnemyTime() ) );
}
#endif
2013-12-03 07:31:46 +04:00
set.AppendCriteria( "speed", UTIL_VarArgs( "%.3f", pSpeaker->GetSmoothedVelocity().Length() ) );
CBaseCombatWeapon *weapon = pSpeaker->GetActiveWeapon();
if ( weapon )
{
set.AppendCriteria( "weapon", weapon->GetClassname() );
}
else
{
set.AppendCriteria( "weapon", "none" );
}
CBasePlayer *pPlayer = AI_GetSinglePlayer();
if ( pPlayer )
{
Vector distance = pPlayer->GetAbsOrigin() - pSpeaker->GetAbsOrigin();
set.AppendCriteria( "distancetoplayer", UTIL_VarArgs( "%f", distance.Length() ) );
}
else
{
set.AppendCriteria( "distancetoplayer", UTIL_VarArgs( "%i", MAX_COORD_RANGE ) );
}
if ( pSpeaker->HasCondition( COND_SEE_PLAYER ) )
{
set.AppendCriteria( "seeplayer", "1" );
}
else
{
set.AppendCriteria( "seeplayer", "0" );
}
if ( pPlayer && pPlayer->FInViewCone( pSpeaker ) && pPlayer->FVisible( pSpeaker ) )
{
set.AppendCriteria( "seenbyplayer", "1" );
}
else
{
set.AppendCriteria( "seenbyplayer", "0" );
}
}
//-----------------------------------------------------------------------------
//=============================================================================
// HPE_BEGIN:
// [Forrest] Remove npc_speakall from Counter-Strike.
//=============================================================================
#ifndef CSTRIKE_DLL
extern CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
CON_COMMAND( npc_speakall, "Force the npc to try and speak all their responses" )
{
if ( !UTIL_IsCommandIssuedByServerAdmin() )
return;
CBaseEntity *pEntity;
if ( args[1] && *args[1] )
{
pEntity = gEntList.FindEntityByName( NULL, args[1], NULL );
if ( !pEntity )
{
pEntity = gEntList.FindEntityByClassname( NULL, args[1] );
}
}
else
{
pEntity = FindPickerEntity( UTIL_GetCommandClient() );
}
if ( pEntity )
{
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
if (pNPC)
{
if ( pNPC->GetExpresser() )
{
bool save = engine->LockNetworkStringTables( false );
pNPC->GetExpresser()->TestAllResponses();
engine->LockNetworkStringTables( save );
}
}
}
}
#endif
//=============================================================================
// HPE_END
//=============================================================================
//-----------------------------------------------------------------------------
CMultiplayer_Expresser::CMultiplayer_Expresser( CBaseFlex *pOuter ) : CAI_Expresser( pOuter )
{
m_bAllowMultipleScenes = false;
}
bool CMultiplayer_Expresser::IsSpeaking( void )
{
if ( m_bAllowMultipleScenes )
{
return false;
}
return CAI_Expresser::IsSpeaking();
}
void CMultiplayer_Expresser::AllowMultipleScenes()
{
m_bAllowMultipleScenes = true;
}
void CMultiplayer_Expresser::DisallowMultipleScenes()
{
m_bAllowMultipleScenes = false;
2013-06-27 02:22:04 +04:00
}