mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-01-06 04:05:32 +03:00
1370 lines
35 KiB
C++
1370 lines
35 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
|
|
|
|
#include "cbase.h"
|
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
|
#include "AI_ResponseSystem.h"
|
|
#include "igamesystem.h"
|
|
#include "AI_Criteria.h"
|
|
#include <KeyValues.h>
|
|
#include "filesystem.h"
|
|
#include "utldict.h"
|
|
#ifdef GAME_DLL
|
|
#include "ai_speech.h"
|
|
#endif
|
|
#include "tier0/icommandline.h"
|
|
#include <ctype.h>
|
|
#include "isaverestore.h"
|
|
#include "utlbuffer.h"
|
|
#include "stringpool.h"
|
|
#include "fmtstr.h"
|
|
#include "multiplay_gamerules.h"
|
|
#include "characterset.h"
|
|
#include "responserules/response_host_interface.h"
|
|
#include "../../responserules/runtime/response_types_internal.h"
|
|
|
|
#include "scenefilecache/ISceneFileCache.h"
|
|
|
|
#ifdef GAME_DLL
|
|
#include "sceneentity.h"
|
|
#endif
|
|
|
|
#include "networkstringtabledefs.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#undef IResponseSystem
|
|
using namespace ResponseRules;
|
|
|
|
extern ConVar rr_debugresponses; // ( "rr_debugresponses", "0", FCVAR_NONE, "Show verbose matching output (1 for simple, 2 for rule scoring, 3 for noisy). If set to 4, it will only show response success/failure for npc_selected NPCs." );
|
|
extern ConVar rr_debugrule; // ( "rr_debugrule", "", FCVAR_NONE, "If set to the name of the rule, that rule's score will be shown whenever a concept is passed into the response rules system.");
|
|
extern ConVar rr_dumpresponses; // ( "rr_dumpresponses", "0", FCVAR_NONE, "Dump all response_rules.txt and rules (requires restart)" );
|
|
extern ConVar rr_debugresponseconcept; // ( "rr_debugresponseconcept", "", FCVAR_NONE, "If set, rr_debugresponses will print only responses testing for the specified concept" );
|
|
|
|
static void CC_RR_DumpHashInfo( const CCommand &args );
|
|
|
|
#ifdef MAPBASE
|
|
ConVar rr_enhanced_saverestore( "rr_enhanced_saverestore", "0", FCVAR_NONE, "Enables enhanced save/restore capabilities for the Response System." );
|
|
#endif
|
|
|
|
extern ISceneFileCache *scenefilecache;
|
|
extern INetworkStringTable *g_pStringTableClientSideChoreoScenes;
|
|
|
|
static characterset_t g_BreakSetIncludingColons;
|
|
|
|
// Simple class to initialize breakset
|
|
class CBreakInit
|
|
{
|
|
public:
|
|
CBreakInit()
|
|
{
|
|
CharacterSetBuild( &g_BreakSetIncludingColons, "{}()':" );
|
|
}
|
|
} g_BreakInit;
|
|
|
|
inline char rr_tolower( char c )
|
|
{
|
|
if ( c >= 'A' && c <= 'Z' )
|
|
return c - 'A' + 'a';
|
|
return c;
|
|
}
|
|
// BUG BUG: Note that this function doesn't check for data overruns!!!
|
|
// Also, this function lowercases the token as it parses!!!
|
|
inline const char *RR_Parse(const char *data, char *token )
|
|
{
|
|
unsigned char c;
|
|
int len;
|
|
characterset_t *breaks = &g_BreakSetIncludingColons;
|
|
len = 0;
|
|
token[0] = 0;
|
|
|
|
if (!data)
|
|
return NULL;
|
|
|
|
// skip whitespace
|
|
skipwhite:
|
|
while ( (c = *data) <= ' ')
|
|
{
|
|
if (c == 0)
|
|
return NULL; // end of file;
|
|
data++;
|
|
}
|
|
|
|
// skip // comments
|
|
if (c=='/' && data[1] == '/')
|
|
{
|
|
while (*data && *data != '\n')
|
|
data++;
|
|
goto skipwhite;
|
|
}
|
|
|
|
|
|
// handle quoted strings specially
|
|
if (c == '\"')
|
|
{
|
|
data++;
|
|
while (1)
|
|
{
|
|
c = rr_tolower( *data++ );
|
|
if (c=='\"' || !c)
|
|
{
|
|
token[len] = 0;
|
|
return data;
|
|
}
|
|
token[len] = c;
|
|
len++;
|
|
}
|
|
}
|
|
|
|
// parse single characters
|
|
if ( IN_CHARACTERSET( *breaks, c ) )
|
|
{
|
|
token[len] = c;
|
|
len++;
|
|
token[len] = 0;
|
|
return data+1;
|
|
}
|
|
|
|
// parse a regular word
|
|
do
|
|
{
|
|
token[len] = rr_tolower( c );
|
|
data++;
|
|
len++;
|
|
c = rr_tolower( *data );
|
|
if ( IN_CHARACTERSET( *breaks, c ) )
|
|
break;
|
|
} while (c>32);
|
|
|
|
token[len] = 0;
|
|
return data;
|
|
}
|
|
|
|
#ifdef MAPBASE
|
|
// A version of the above which preserves casing and supports escaped quotes
|
|
inline const char *RR_Parse_Preserve(const char *data, char *token )
|
|
{
|
|
unsigned char c;
|
|
int len;
|
|
characterset_t *breaks = &g_BreakSetIncludingColons;
|
|
len = 0;
|
|
token[0] = 0;
|
|
|
|
if (!data)
|
|
return NULL;
|
|
|
|
// skip whitespace
|
|
skipwhite:
|
|
while ( (c = *data) <= ' ')
|
|
{
|
|
if (c == 0)
|
|
return NULL; // end of file;
|
|
data++;
|
|
}
|
|
|
|
// skip // comments
|
|
if (c=='/' && data[1] == '/')
|
|
{
|
|
while (*data && *data != '\n')
|
|
data++;
|
|
goto skipwhite;
|
|
}
|
|
|
|
// handle quoted strings specially
|
|
if (c == '\"')
|
|
{
|
|
bool escaped = false;
|
|
data++;
|
|
while (1)
|
|
{
|
|
c = *data++;
|
|
if ((c=='\"' && !escaped) || !c)
|
|
{
|
|
token[len] = 0;
|
|
return data;
|
|
}
|
|
|
|
escaped = (c == '\\');
|
|
if (!escaped)
|
|
{
|
|
token[len] = c;
|
|
len++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// parse single characters
|
|
if ( IN_CHARACTERSET( *breaks, c ) )
|
|
{
|
|
token[len] = c;
|
|
len++;
|
|
token[len] = 0;
|
|
return data+1;
|
|
}
|
|
|
|
// parse a regular word
|
|
do
|
|
{
|
|
token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if ( IN_CHARACTERSET( *breaks, c ) )
|
|
break;
|
|
} while (c>32);
|
|
|
|
token[len] = 0;
|
|
return data;
|
|
}
|
|
#endif
|
|
|
|
namespace ResponseRules
|
|
{
|
|
extern const char *ResponseCopyString( const char *in );
|
|
}
|
|
|
|
// Host functions required by the ResponseRules::IEngineEmulator interface
|
|
class CResponseRulesToEngineInterface : public ResponseRules::IEngineEmulator
|
|
{
|
|
/// Given an input text buffer data pointer, parses a single token into the variable token and returns the new
|
|
/// reading position
|
|
virtual const char *ParseFile( const char *data, char *token, int maxlen )
|
|
{
|
|
NOTE_UNUSED( maxlen );
|
|
return RR_Parse( data, token );
|
|
}
|
|
|
|
#ifdef MAPBASE
|
|
/// (Optional) Same as ParseFile, but with casing preserved and escaped quotes supported
|
|
virtual const char *ParseFilePreserve( const char *data, char *token, int maxlen )
|
|
{
|
|
NOTE_UNUSED( maxlen );
|
|
return RR_Parse_Preserve( data, token );
|
|
}
|
|
#endif
|
|
|
|
/// Return a pointer to an IFileSystem we can use to read and process scripts.
|
|
virtual IFileSystem *GetFilesystem()
|
|
{
|
|
return filesystem;
|
|
}
|
|
|
|
/// Return a pointer to an instance of an IUniformRandomStream
|
|
virtual IUniformRandomStream *GetRandomStream()
|
|
{
|
|
return random;
|
|
}
|
|
|
|
/// Return a pointer to a tier0 ICommandLine
|
|
virtual ICommandLine *GetCommandLine()
|
|
{
|
|
return CommandLine();
|
|
}
|
|
|
|
/// Emulates the server's UTIL_LoadFileForMe
|
|
virtual byte *LoadFileForMe( const char *filename, int *pLength )
|
|
{
|
|
return UTIL_LoadFileForMe( filename, pLength );
|
|
}
|
|
|
|
/// Emulates the server's UTIL_FreeFile
|
|
virtual void FreeFile( byte *buffer )
|
|
{
|
|
return UTIL_FreeFile( buffer );
|
|
}
|
|
|
|
};
|
|
|
|
CResponseRulesToEngineInterface g_ResponseRulesEngineWrapper;
|
|
IEngineEmulator *IEngineEmulator::s_pSingleton = &g_ResponseRulesEngineWrapper;
|
|
|
|
|
|
BEGIN_SIMPLE_DATADESC( ParserResponse )
|
|
// DEFINE_FIELD( type, FIELD_INTEGER ),
|
|
// DEFINE_ARRAY( value, FIELD_CHARACTER ),
|
|
// DEFINE_FIELD( weight, FIELD_FLOAT ),
|
|
DEFINE_FIELD( depletioncount, FIELD_CHARACTER ),
|
|
// DEFINE_FIELD( first, FIELD_BOOLEAN ),
|
|
// DEFINE_FIELD( last, FIELD_BOOLEAN ),
|
|
END_DATADESC()
|
|
|
|
|
|
BEGIN_SIMPLE_DATADESC( ResponseGroup )
|
|
// DEFINE_FIELD( group, FIELD_UTLVECTOR ),
|
|
// DEFINE_FIELD( rp, FIELD_EMBEDDED ),
|
|
// DEFINE_FIELD( m_bDepleteBeforeRepeat, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_nDepletionCount, FIELD_CHARACTER ),
|
|
// DEFINE_FIELD( m_bHasFirst, FIELD_BOOLEAN ),
|
|
// DEFINE_FIELD( m_bHasLast, FIELD_BOOLEAN ),
|
|
// DEFINE_FIELD( m_bSequential, FIELD_BOOLEAN ),
|
|
// DEFINE_FIELD( m_bNoRepeat, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
|
|
DEFINE_FIELD( m_nCurrentIndex, FIELD_CHARACTER ),
|
|
END_DATADESC()
|
|
|
|
|
|
/// Add some game-specific code to the basic response system
|
|
/// (eg, the scene precacher, which requires the client and server
|
|
/// to work)
|
|
|
|
class CGameResponseSystem : public CResponseSystem
|
|
{
|
|
public:
|
|
CGameResponseSystem();
|
|
|
|
virtual void Precache();
|
|
virtual void PrecacheResponses( bool bEnable )
|
|
{
|
|
m_bPrecache = bEnable;
|
|
}
|
|
bool ShouldPrecache() { return m_bPrecache; }
|
|
|
|
protected:
|
|
bool m_bPrecache;
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CGameResponseSystem::CGameResponseSystem() : m_bPrecache(true)
|
|
{};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if 0
|
|
class CScenePrecacheSystem : public CAutoGameSystem
|
|
{
|
|
public:
|
|
CScenePrecacheSystem() : CAutoGameSystem( "CScenePrecacheSystem" ), m_RepeatCounts( 0, 0, DefLessFunc( int ) )
|
|
{
|
|
}
|
|
|
|
// Level init, shutdown
|
|
virtual void LevelShutdownPreEntity()
|
|
{
|
|
m_RepeatCounts.Purge();
|
|
}
|
|
|
|
bool ShouldPrecache( char const *pszScene )
|
|
{
|
|
int hash = HashStringCaselessConventional( pszScene );
|
|
|
|
int slot = m_RepeatCounts.Find( hash );
|
|
if ( slot != m_RepeatCounts.InvalidIndex() )
|
|
{
|
|
m_RepeatCounts[ slot ]++;
|
|
return false;
|
|
}
|
|
|
|
m_RepeatCounts.Insert( hash, 0 );
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
|
|
CUtlMap< int, int > m_RepeatCounts;
|
|
};
|
|
|
|
static CScenePrecacheSystem g_ScenePrecacheSystem;
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Used for precaching instanced scenes
|
|
// Input : *pszScene -
|
|
//-----------------------------------------------------------------------------
|
|
void PrecacheInstancedScene( char const *pszScene )
|
|
{
|
|
static int nMakingReslists = -1;
|
|
|
|
if ( !g_ScenePrecacheSystem.ShouldPrecache( pszScene ) )
|
|
return;
|
|
|
|
if ( nMakingReslists == -1 )
|
|
{
|
|
nMakingReslists = CommandLine()->FindParm( "-makereslists" ) > 0 ? 1 : 0;
|
|
}
|
|
|
|
if ( nMakingReslists == 1 )
|
|
{
|
|
// Just stat the file to add to reslist
|
|
g_pFullFileSystem->Size( pszScene );
|
|
}
|
|
|
|
// verify existence, cache is pre-populated, should be there
|
|
SceneCachedData_t sceneData;
|
|
if ( !scenefilecache->GetSceneCachedData( pszScene, &sceneData ) )
|
|
{
|
|
// Scenes are sloppy and don't always exist.
|
|
// A scene that is not in the pre-built cache image, but on disk, is a true error.
|
|
if ( IsX360() && ( g_pFullFileSystem->GetDVDMode() != DVDMODE_STRICT ) && g_pFullFileSystem->FileExists( pszScene, "GAME" ) )
|
|
{
|
|
Warning( "PrecacheInstancedScene: Missing scene '%s' from scene image cache.\nRebuild scene image cache!\n", pszScene );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( int i = 0; i < sceneData.numSounds; ++i )
|
|
{
|
|
short stringId = scenefilecache->GetSceneCachedSound( sceneData.sceneId, i );
|
|
CBaseEntity::PrecacheScriptSound( scenefilecache->GetSceneString( stringId ) );
|
|
}
|
|
}
|
|
|
|
g_pStringTableClientSideChoreoScenes->AddString( CBaseEntity::IsServer(), pszScene );
|
|
}
|
|
#endif
|
|
|
|
static void TouchFile( char const *pchFileName )
|
|
{
|
|
IEngineEmulator::Get()->GetFilesystem()->Size( pchFileName );
|
|
}
|
|
|
|
void CGameResponseSystem::Precache()
|
|
{
|
|
bool bTouchFiles = CommandLine()->FindParm( "-makereslists" ) != 0;
|
|
|
|
// enumerate and mark all the scripts so we know they're referenced
|
|
for ( int i = 0; i < (int)m_Responses.Count(); i++ )
|
|
{
|
|
ResponseGroup &group = m_Responses[i];
|
|
|
|
for ( int j = 0; j < group.group.Count(); j++)
|
|
{
|
|
ParserResponse &response = group.group[j];
|
|
|
|
switch ( response.type )
|
|
{
|
|
default:
|
|
break;
|
|
case RESPONSE_SCENE:
|
|
{
|
|
// fixup $gender references
|
|
char file[_MAX_PATH];
|
|
Q_strncpy( file, response.value, sizeof(file) );
|
|
char *gender = strstr( file, "$gender" );
|
|
if ( gender )
|
|
{
|
|
// replace with male & female
|
|
const char *postGender = gender + strlen("$gender");
|
|
*gender = 0;
|
|
char genderFile[_MAX_PATH];
|
|
// male
|
|
Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender);
|
|
|
|
PrecacheInstancedScene( genderFile );
|
|
if ( bTouchFiles )
|
|
{
|
|
TouchFile( genderFile );
|
|
}
|
|
|
|
Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender);
|
|
|
|
PrecacheInstancedScene( genderFile );
|
|
if ( bTouchFiles )
|
|
{
|
|
TouchFile( genderFile );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrecacheInstancedScene( file );
|
|
if ( bTouchFiles )
|
|
{
|
|
TouchFile( file );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case RESPONSE_SPEAK:
|
|
{
|
|
CBaseEntity::PrecacheScriptSound( response.value );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: A special purpose response system associated with a custom entity
|
|
//-----------------------------------------------------------------------------
|
|
class CInstancedResponseSystem : public CGameResponseSystem
|
|
{
|
|
typedef CGameResponseSystem BaseClass;
|
|
|
|
public:
|
|
CInstancedResponseSystem( const char *scriptfile ) :
|
|
m_pszScriptFile( 0 )
|
|
{
|
|
Assert( scriptfile );
|
|
|
|
int len = Q_strlen( scriptfile ) + 1;
|
|
m_pszScriptFile = new char[ len ];
|
|
Assert( m_pszScriptFile );
|
|
Q_strncpy( m_pszScriptFile, scriptfile, len );
|
|
}
|
|
|
|
~CInstancedResponseSystem()
|
|
{
|
|
delete[] m_pszScriptFile;
|
|
}
|
|
virtual const char *GetScriptFile( void )
|
|
{
|
|
Assert( m_pszScriptFile );
|
|
return m_pszScriptFile;
|
|
}
|
|
|
|
// CAutoGameSystem
|
|
virtual bool Init()
|
|
{
|
|
const char *basescript = GetScriptFile();
|
|
LoadRuleSet( basescript );
|
|
return true;
|
|
}
|
|
|
|
virtual void LevelInitPostEntity()
|
|
{
|
|
ResetResponseGroups();
|
|
}
|
|
|
|
virtual void Release()
|
|
{
|
|
Clear();
|
|
delete this;
|
|
}
|
|
private:
|
|
|
|
char *m_pszScriptFile;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: The default response system for expressive AIs
|
|
//-----------------------------------------------------------------------------
|
|
class CDefaultResponseSystem : public CGameResponseSystem, public CAutoGameSystem
|
|
{
|
|
typedef CAutoGameSystem BaseClass;
|
|
|
|
public:
|
|
CDefaultResponseSystem() : CAutoGameSystem( "CDefaultResponseSystem" )
|
|
{
|
|
}
|
|
|
|
virtual const char *GetScriptFile( void )
|
|
{
|
|
return "scripts/talker/response_rules.txt";
|
|
}
|
|
|
|
// CAutoServerSystem
|
|
virtual bool Init();
|
|
virtual void Shutdown();
|
|
|
|
virtual void LevelInitPostEntity()
|
|
{
|
|
}
|
|
|
|
virtual void Release()
|
|
{
|
|
Assert( 0 );
|
|
}
|
|
|
|
void AddInstancedResponseSystem( const char *scriptfile, CInstancedResponseSystem *sys )
|
|
{
|
|
m_InstancedSystems.Insert( scriptfile, sys );
|
|
}
|
|
|
|
CInstancedResponseSystem *FindResponseSystem( const char *scriptfile )
|
|
{
|
|
int idx = m_InstancedSystems.Find( scriptfile );
|
|
if ( idx == m_InstancedSystems.InvalidIndex() )
|
|
return NULL;
|
|
return m_InstancedSystems[ idx ];
|
|
}
|
|
|
|
IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile )
|
|
{
|
|
COM_TimestampedLog( "PrecacheCustomResponseSystem %s - Start", scriptfile );
|
|
CInstancedResponseSystem *sys = ( CInstancedResponseSystem * )FindResponseSystem( scriptfile );
|
|
if ( !sys )
|
|
{
|
|
sys = new CInstancedResponseSystem( scriptfile );
|
|
if ( !sys )
|
|
{
|
|
Error( "Failed to load response system data from %s", scriptfile );
|
|
}
|
|
|
|
if ( !sys->Init() )
|
|
{
|
|
Error( "CInstancedResponseSystem: Failed to init response system from %s!", scriptfile );
|
|
}
|
|
|
|
AddInstancedResponseSystem( scriptfile, sys );
|
|
}
|
|
|
|
sys->Precache();
|
|
|
|
COM_TimestampedLog( "PrecacheCustomResponseSystem %s - Finish", scriptfile );
|
|
|
|
return ( IResponseSystem * )sys;
|
|
}
|
|
|
|
IResponseSystem *BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore );
|
|
void DestroyCustomResponseSystems();
|
|
|
|
virtual void LevelInitPreEntity()
|
|
{
|
|
// This will precache the default system
|
|
// All user installed systems are init'd by PrecacheCustomResponseSystem which will call sys->Precache() on the ones being used
|
|
|
|
// FIXME: This is SLOW the first time you run the engine (can take 3 - 10 seconds!!!)
|
|
if ( ShouldPrecache() )
|
|
{
|
|
Precache();
|
|
}
|
|
|
|
#ifdef MAPBASE
|
|
if (!rr_enhanced_saverestore.GetBool() || gpGlobals->eLoadType != MapLoad_Transition)
|
|
#endif
|
|
ResetResponseGroups();
|
|
}
|
|
|
|
void ReloadAllResponseSystems()
|
|
{
|
|
Clear();
|
|
Init();
|
|
|
|
int c = m_InstancedSystems.Count();
|
|
for ( int i = c - 1 ; i >= 0; i-- )
|
|
{
|
|
CInstancedResponseSystem *sys = m_InstancedSystems[ i ];
|
|
if ( !IsCustomManagable() )
|
|
{
|
|
sys->Clear();
|
|
sys->Init();
|
|
}
|
|
else
|
|
{
|
|
// Custom reponse rules will manage/reload themselves - remove them.
|
|
m_InstancedSystems.RemoveAt( i );
|
|
}
|
|
}
|
|
|
|
// precache sounds in case we added new ones
|
|
Precache();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
void ClearInstanced()
|
|
{
|
|
int c = m_InstancedSystems.Count();
|
|
for ( int i = c - 1 ; i >= 0; i-- )
|
|
{
|
|
CInstancedResponseSystem *sys = m_InstancedSystems[ i ];
|
|
sys->Release();
|
|
}
|
|
m_InstancedSystems.RemoveAll();
|
|
}
|
|
|
|
CUtlDict< CInstancedResponseSystem *, int > m_InstancedSystems;
|
|
friend void CC_RR_DumpHashInfo( const CCommand &args );
|
|
};
|
|
|
|
IResponseSystem *CDefaultResponseSystem::BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore )
|
|
{
|
|
// Create a instanced response system.
|
|
CInstancedResponseSystem *pCustomSystem = new CInstancedResponseSystem( pszCustomName );
|
|
if ( !pCustomSystem )
|
|
{
|
|
Error( "BuildCustomResponseSystemGivenCriterea: Failed to create custom response system %s!", pszCustomName );
|
|
}
|
|
|
|
pCustomSystem->Clear();
|
|
|
|
// Copy the relevant rules and data.
|
|
/*
|
|
int nRuleCount = m_Rules.Count();
|
|
for ( int iRule = 0; iRule < nRuleCount; ++iRule )
|
|
*/
|
|
for ( ResponseRulePartition::tIndex iIdx = m_RulePartitions.First() ;
|
|
m_RulePartitions.IsValid(iIdx) ;
|
|
iIdx = m_RulePartitions.Next( iIdx ) )
|
|
{
|
|
Rule *pRule = &m_RulePartitions[iIdx];
|
|
if ( pRule )
|
|
{
|
|
float flScore = 0.0f;
|
|
|
|
int nCriteriaCount = pRule->m_Criteria.Count();
|
|
for ( int iCriteria = 0; iCriteria < nCriteriaCount; ++iCriteria )
|
|
{
|
|
int iRuleCriteria = pRule->m_Criteria[iCriteria];
|
|
|
|
flScore += LookForCriteria( criteriaSet, iRuleCriteria );
|
|
if ( flScore >= flCriteriaScore )
|
|
{
|
|
CopyRuleFrom( pRule, iIdx, pCustomSystem );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set as a custom response system.
|
|
m_bCustomManagable = true;
|
|
AddInstancedResponseSystem( pszCustomName, pCustomSystem );
|
|
|
|
// pCustomSystem->DumpDictionary( pszCustomName );
|
|
|
|
return pCustomSystem;
|
|
}
|
|
|
|
void CDefaultResponseSystem::DestroyCustomResponseSystems()
|
|
{
|
|
ClearInstanced();
|
|
}
|
|
|
|
|
|
static CDefaultResponseSystem defaultresponsesytem;
|
|
IResponseSystem *g_pResponseSystem = &defaultresponsesytem;
|
|
|
|
CON_COMMAND( rr_reloadresponsesystems, "Reload all response system scripts." )
|
|
{
|
|
#ifdef GAME_DLL
|
|
if ( !UTIL_IsCommandIssuedByServerAdmin() )
|
|
return;
|
|
#endif
|
|
|
|
defaultresponsesytem.ReloadAllResponseSystems();
|
|
}
|
|
|
|
#if RR_DUMPHASHINFO_ENABLED
|
|
static void CC_RR_DumpHashInfo( const CCommand &args )
|
|
{
|
|
defaultresponsesytem.m_RulePartitions.PrintBucketInfo( &defaultresponsesytem );
|
|
}
|
|
static ConCommand rr_dumphashinfo( "rr_dumphashinfo", CC_RR_DumpHashInfo, "Statistics on primary hash bucketing of response rule partitions");
|
|
#endif
|
|
|
|
#ifdef MAPBASE
|
|
// Designed for extern magic, this gives the <, >, etc. of response system criteria to the outside world.
|
|
// Mostly just used for Matcher_Match in matchers.h.
|
|
bool ResponseSystemCompare( const char *criterion, const char *value )
|
|
{
|
|
Criteria criteria;
|
|
criteria.value = criterion;
|
|
defaultresponsesytem.ComputeMatcher( &criteria, criteria.matcher );
|
|
return defaultresponsesytem.CompareUsingMatcher( value, criteria.matcher, true );
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CResponseFilePrecacher
|
|
//
|
|
// Purpose: Precaches a single talker file. That's it.
|
|
//
|
|
// It copies from a bunch of the original Response System class and therefore it's really messy.
|
|
// Despite the horrors a normal programmer might find in here, I think it performs better than anything else I could've come up with.
|
|
//-----------------------------------------------------------------------------
|
|
/*
|
|
class CResponseFilePrecacher
|
|
{
|
|
public:
|
|
|
|
// Stuff copied from the Response System.
|
|
// Direct copy-pastes are very compact, to say the least.
|
|
inline bool ParseToken( void )
|
|
{
|
|
if ( m_bUnget )
|
|
{ m_bUnget = false; return true; }
|
|
if ( m_ScriptStack.Count() <= 0 )
|
|
{ return false; }
|
|
|
|
m_ScriptStack[ 0 ].currenttoken = engine->ParseFile( m_ScriptStack[ 0 ].currenttoken, token, sizeof( token ) );
|
|
m_ScriptStack[ 0 ].tokencount++;
|
|
return m_ScriptStack[ 0 ].currenttoken != NULL ? true : false;
|
|
}
|
|
|
|
CUtlVector< CResponseSystem::ScriptEntry > m_ScriptStack;
|
|
bool m_bUnget;
|
|
char token[ 1204 ];
|
|
|
|
|
|
void PrecacheResponse( const char *response, byte type )
|
|
{
|
|
switch ( type )
|
|
{
|
|
default:
|
|
break;
|
|
case RESPONSE_SCENE:
|
|
{
|
|
DevMsg("Precaching scene %s...\n", response);
|
|
|
|
// fixup $gender references
|
|
char file[_MAX_PATH];
|
|
Q_strncpy( file, response, sizeof(file) );
|
|
char *gender = strstr( file, "$gender" );
|
|
if ( gender )
|
|
{
|
|
// replace with male & female
|
|
const char *postGender = gender + strlen("$gender");
|
|
*gender = 0;
|
|
char genderFile[_MAX_PATH];
|
|
|
|
Q_snprintf( genderFile, sizeof(genderFile), "%smale%s", file, postGender);
|
|
PrecacheInstancedScene( genderFile );
|
|
|
|
Q_snprintf( genderFile, sizeof(genderFile), "%sfemale%s", file, postGender);
|
|
PrecacheInstancedScene( genderFile );
|
|
}
|
|
else
|
|
{
|
|
PrecacheInstancedScene( file );
|
|
}
|
|
}
|
|
break;
|
|
case RESPONSE_SPEAK:
|
|
{
|
|
DevMsg("Precaching sound %s...\n", response);
|
|
CBaseEntity::PrecacheScriptSound( response );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool IsRootCommand()
|
|
{
|
|
if (!Q_stricmp( token, "#include" ) || !Q_stricmp( token, "response" )
|
|
|| !Q_stricmp( token, "enumeration" ) || !Q_stricmp( token, "criteria" )
|
|
|| !Q_stricmp( token, "criterion" ) || !Q_stricmp( token, "rule" ))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void ParseResponse( void )
|
|
{
|
|
// Must go to response group name
|
|
ParseToken();
|
|
|
|
while ( 1 )
|
|
{
|
|
ParseToken();
|
|
|
|
if ( !Q_stricmp( token, "{" ) )
|
|
{
|
|
while ( 1 )
|
|
{
|
|
ParseToken();
|
|
if ( !Q_stricmp( token, "}" ) )
|
|
break;
|
|
|
|
byte type = ComputeResponseType( token );
|
|
if (type == RESPONSE_NONE)
|
|
continue;
|
|
|
|
ParseToken();
|
|
char *value = CopyString( token );
|
|
|
|
PrecacheResponse(value, type);
|
|
}
|
|
break;
|
|
}
|
|
|
|
byte type = ComputeResponseType( token );
|
|
if (type == RESPONSE_NONE)
|
|
break;
|
|
|
|
ParseToken();
|
|
char *value = CopyString( token );
|
|
|
|
PrecacheResponse(value, type);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool LoadFromBuffer(const char *scriptfile, unsigned char *buffer, CStringPool &includedFiles)
|
|
{
|
|
includedFiles.Allocate( scriptfile );
|
|
|
|
CResponseSystem::ScriptEntry e;
|
|
e.name = filesystem->FindOrAddFileName( scriptfile );
|
|
e.buffer = buffer;
|
|
e.currenttoken = (char *)e.buffer;
|
|
e.tokencount = 0;
|
|
m_ScriptStack.AddToHead( e );
|
|
|
|
while ( 1 )
|
|
{
|
|
ParseToken();
|
|
if ( !token[0] )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( !Q_stricmp( token, "response" ) )
|
|
{
|
|
ParseResponse();
|
|
}
|
|
else if ( !Q_stricmp( token, "#include" ) || !Q_stricmp( token, "#base" ) )
|
|
{
|
|
// Compacted version of ParseInclude(), including new changes.
|
|
// Look at that if you want to read.
|
|
char includefile[ 256 ];
|
|
ParseToken();
|
|
if (scriptfile) { size_t len = strlen(scriptfile)-1;
|
|
for (size_t i = 0; i < len; i++)
|
|
{ if (scriptfile[i] == CORRECT_PATH_SEPARATOR || scriptfile[i] == INCORRECT_PATH_SEPARATOR)
|
|
{ len = i; }
|
|
} Q_strncpy(includefile, scriptfile, len+1);
|
|
if (len+1 != strlen(scriptfile))
|
|
{ Q_snprintf(includefile, sizeof(includefile), "%s/%s", includefile, token); }
|
|
else includefile[0] = '\0';
|
|
} if (!includefile[0]) Q_snprintf( includefile, sizeof( includefile ), "scripts/%s", token );
|
|
|
|
if ( includedFiles.Find( includefile ) == NULL )
|
|
{
|
|
MEM_ALLOC_CREDIT();
|
|
|
|
// Try and load it
|
|
CUtlBuffer buf;
|
|
if ( filesystem->ReadFile( includefile, "GAME", buf ) )
|
|
{
|
|
LoadFromBuffer( includefile, (unsigned char *)buf.PeekGet(), includedFiles );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m_ScriptStack.Count() > 0 )
|
|
m_ScriptStack.Remove( 0 );
|
|
|
|
return true;
|
|
}
|
|
};
|
|
*/
|
|
|
|
// Loads a file directly to the main response system
|
|
bool LoadResponseSystemFile(const char *scriptfile)
|
|
{
|
|
CUtlBuffer buf;
|
|
if ( !filesystem->ReadFile( scriptfile, "GAME", buf ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// This is a really messy and specialized system that precaches the responses and only the responses of a talker file.
|
|
/*
|
|
CStringPool includedFiles;
|
|
CResponseFilePrecacher *rs = new CResponseFilePrecacher();
|
|
if (!rs || !rs->LoadFromBuffer(scriptfile, (unsigned char *)buf.PeekGet(), includedFiles))
|
|
{
|
|
Warning( "Failed to load response system data from %s", scriptfile );
|
|
delete rs;
|
|
return false;
|
|
}
|
|
delete rs;
|
|
*/
|
|
|
|
// HACKHACK: This is not very efficient
|
|
/*
|
|
CInstancedResponseSystem *tempSys = new CInstancedResponseSystem( scriptfile );
|
|
if ( tempSys && tempSys->Init() )
|
|
{
|
|
tempSys->Precache();
|
|
|
|
for ( ResponseRulePartition::tIndex idx = tempSys->m_RulePartitions.First() ;
|
|
tempSys->m_RulePartitions.IsValid(idx) ;
|
|
idx = tempSys->m_RulePartitions.Next(idx) )
|
|
{
|
|
Rule &rule = tempSys->m_RulePartitions[idx];
|
|
tempSys->CopyRuleFrom( &rule, idx, &defaultresponsesytem );
|
|
}
|
|
|
|
tempSys->Release();
|
|
}
|
|
*/
|
|
|
|
// HACKHACK: This is even less efficient
|
|
defaultresponsesytem.LoadFromBuffer( scriptfile, (const char *)buf.PeekGet() );
|
|
defaultresponsesytem.Precache();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Called from Mapbase manifests to flush
|
|
void ReloadResponseSystem()
|
|
{
|
|
defaultresponsesytem.ReloadAllResponseSystems();
|
|
}
|
|
#endif
|
|
|
|
static short RESPONSESYSTEM_SAVE_RESTORE_VERSION = 1;
|
|
|
|
// note: this won't save/restore settings from instanced response systems. Could add that with a CDefSaveRestoreOps implementation if needed
|
|
//
|
|
class CDefaultResponseSystemSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
|
|
{
|
|
public:
|
|
const char *GetBlockName()
|
|
{
|
|
return "ResponseSystem";
|
|
}
|
|
|
|
void WriteSaveHeaders( ISave *pSave )
|
|
{
|
|
pSave->WriteShort( &RESPONSESYSTEM_SAVE_RESTORE_VERSION );
|
|
}
|
|
|
|
void ReadRestoreHeaders( IRestore *pRestore )
|
|
{
|
|
// No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
|
|
short version;
|
|
pRestore->ReadShort( &version );
|
|
m_fDoLoad = ( version == RESPONSESYSTEM_SAVE_RESTORE_VERSION );
|
|
}
|
|
|
|
void Save( ISave *pSave )
|
|
{
|
|
CDefaultResponseSystem& rs = defaultresponsesytem;
|
|
|
|
int count = rs.m_Responses.Count();
|
|
pSave->WriteInt( &count );
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
pSave->StartBlock( "ResponseGroup" );
|
|
|
|
pSave->WriteString( rs.m_Responses.GetElementName( i ) );
|
|
const ResponseGroup *group = &rs.m_Responses[ i ];
|
|
pSave->WriteAll( group );
|
|
|
|
short groupCount = group->group.Count();
|
|
pSave->WriteShort( &groupCount );
|
|
for ( int j = 0; j < groupCount; ++j )
|
|
{
|
|
const ParserResponse *response = &group->group[ j ];
|
|
pSave->StartBlock( "Response" );
|
|
pSave->WriteString( response->value );
|
|
pSave->WriteAll( response );
|
|
pSave->EndBlock();
|
|
}
|
|
|
|
pSave->EndBlock();
|
|
}
|
|
|
|
#ifdef MAPBASE
|
|
// Enhanced Response System save/restore
|
|
int count2 = 0;
|
|
if (rr_enhanced_saverestore.GetBool())
|
|
{
|
|
// Rule state save/load
|
|
count2 = rs.m_RulePartitions.Count();
|
|
pSave->WriteInt( &count2 );
|
|
for ( ResponseRulePartition::tIndex idx = rs.m_RulePartitions.First() ;
|
|
rs.m_RulePartitions.IsValid(idx) ;
|
|
idx = rs.m_RulePartitions.Next(idx) )
|
|
{
|
|
pSave->StartBlock( "Rule" );
|
|
|
|
pSave->WriteString( rs.m_RulePartitions.GetElementName( idx ) );
|
|
const Rule &rule = rs.m_RulePartitions[ idx ];
|
|
|
|
bool bEnabled = rule.m_bEnabled;
|
|
pSave->WriteBool( &bEnabled );
|
|
|
|
pSave->EndBlock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Indicate this isn't using enhanced save/restore
|
|
pSave->WriteInt( &count2 );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Restore( IRestore *pRestore, bool createPlayers )
|
|
{
|
|
if ( !m_fDoLoad )
|
|
return;
|
|
|
|
CDefaultResponseSystem& rs = defaultresponsesytem;
|
|
|
|
int count = pRestore->ReadInt();
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF];
|
|
pRestore->StartBlock( szResponseGroupBlockName );
|
|
if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) )
|
|
{
|
|
|
|
char groupname[ 256 ];
|
|
pRestore->ReadString( groupname, sizeof( groupname ), 0 );
|
|
|
|
// Try and find it
|
|
int idx = rs.m_Responses.Find( groupname );
|
|
if ( idx != rs.m_Responses.InvalidIndex() )
|
|
{
|
|
ResponseGroup *group = &rs.m_Responses[ idx ];
|
|
pRestore->ReadAll( group );
|
|
|
|
short groupCount = pRestore->ReadShort();
|
|
for ( int j = 0; j < groupCount; ++j )
|
|
{
|
|
char szResponseBlockName[SIZE_BLOCK_NAME_BUF];
|
|
|
|
char responsename[ 256 ];
|
|
pRestore->StartBlock( szResponseBlockName );
|
|
if ( !Q_stricmp( szResponseBlockName, "Response" ) )
|
|
{
|
|
pRestore->ReadString( responsename, sizeof( responsename ), 0 );
|
|
|
|
// Find it by name
|
|
int ri;
|
|
for ( ri = 0; ri < group->group.Count(); ++ri )
|
|
{
|
|
ParserResponse *response = &group->group[ ri ];
|
|
if ( !Q_stricmp( response->value, responsename ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ri < group->group.Count() )
|
|
{
|
|
ParserResponse *response = &group->group[ ri ];
|
|
pRestore->ReadAll( response );
|
|
}
|
|
}
|
|
|
|
pRestore->EndBlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
pRestore->EndBlock();
|
|
}
|
|
|
|
#ifdef MAPBASE
|
|
// Enhanced Response System save/restore
|
|
count = pRestore->ReadInt();
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
char szRuleBlockName[SIZE_BLOCK_NAME_BUF];
|
|
pRestore->StartBlock( szRuleBlockName );
|
|
if ( !Q_stricmp( szRuleBlockName, "Rule" ) )
|
|
{
|
|
char groupname[ 256 ];
|
|
pRestore->ReadString( groupname, sizeof( groupname ), 0 );
|
|
|
|
// Try and find it
|
|
Rule *rule = rs.m_RulePartitions.FindByName( groupname );
|
|
if ( rule )
|
|
{
|
|
bool bEnabled;
|
|
pRestore->ReadBool( &bEnabled );
|
|
rule->m_bEnabled = bEnabled;
|
|
}
|
|
else
|
|
{
|
|
Warning("Warning: Can't find rule %s\n", groupname);
|
|
}
|
|
}
|
|
|
|
pRestore->EndBlock();
|
|
}
|
|
#endif
|
|
}
|
|
private:
|
|
|
|
bool m_fDoLoad;
|
|
|
|
} g_DefaultResponseSystemSaveRestoreBlockHandler;
|
|
|
|
ISaveRestoreBlockHandler *GetDefaultResponseSystemSaveRestoreBlockHandler()
|
|
{
|
|
return &g_DefaultResponseSystemSaveRestoreBlockHandler;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CResponseSystemSaveRestoreOps
|
|
//
|
|
// Purpose: Handles save and load for instanced response systems...
|
|
//
|
|
// BUGBUG: This will save the same response system to file multiple times for "shared" response systems and
|
|
// therefore it'll restore the same data onto the same pointer N times on reload (probably benign for now, but we could
|
|
// write code to save/restore the instanced ones by filename in the block handler above maybe?
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class CResponseSystemSaveRestoreOps : public CDefSaveRestoreOps
|
|
{
|
|
public:
|
|
|
|
virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
|
|
{
|
|
CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField;
|
|
if ( !pRS || pRS == &defaultresponsesytem )
|
|
return;
|
|
|
|
int count = pRS->m_Responses.Count();
|
|
pSave->WriteInt( &count );
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
pSave->StartBlock( "ResponseGroup" );
|
|
|
|
pSave->WriteString( pRS->m_Responses.GetElementName( i ) );
|
|
const ResponseGroup *group = &pRS->m_Responses[ i ];
|
|
pSave->WriteAll( group );
|
|
|
|
short groupCount = group->group.Count();
|
|
pSave->WriteShort( &groupCount );
|
|
for ( int j = 0; j < groupCount; ++j )
|
|
{
|
|
const ParserResponse *response = &group->group[ j ];
|
|
pSave->StartBlock( "Response" );
|
|
pSave->WriteString( response->value );
|
|
pSave->WriteAll( response );
|
|
pSave->EndBlock();
|
|
}
|
|
|
|
pSave->EndBlock();
|
|
}
|
|
}
|
|
|
|
virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
|
|
{
|
|
CResponseSystem *pRS = *(CResponseSystem **)fieldInfo.pField;
|
|
if ( !pRS || pRS == &defaultresponsesytem )
|
|
return;
|
|
|
|
int count = pRestore->ReadInt();
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
char szResponseGroupBlockName[SIZE_BLOCK_NAME_BUF];
|
|
pRestore->StartBlock( szResponseGroupBlockName );
|
|
if ( !Q_stricmp( szResponseGroupBlockName, "ResponseGroup" ) )
|
|
{
|
|
|
|
char groupname[ 256 ];
|
|
pRestore->ReadString( groupname, sizeof( groupname ), 0 );
|
|
|
|
// Try and find it
|
|
int idx = pRS->m_Responses.Find( groupname );
|
|
if ( idx != pRS->m_Responses.InvalidIndex() )
|
|
{
|
|
ResponseGroup *group = &pRS->m_Responses[ idx ];
|
|
pRestore->ReadAll( group );
|
|
|
|
short groupCount = pRestore->ReadShort();
|
|
for ( int j = 0; j < groupCount; ++j )
|
|
{
|
|
char szResponseBlockName[SIZE_BLOCK_NAME_BUF];
|
|
|
|
char responsename[ 256 ];
|
|
pRestore->StartBlock( szResponseBlockName );
|
|
if ( !Q_stricmp( szResponseBlockName, "Response" ) )
|
|
{
|
|
pRestore->ReadString( responsename, sizeof( responsename ), 0 );
|
|
|
|
// Find it by name
|
|
int ri;
|
|
for ( ri = 0; ri < group->group.Count(); ++ri )
|
|
{
|
|
ParserResponse *response = &group->group[ ri ];
|
|
if ( !Q_stricmp( response->value, responsename ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ri < group->group.Count() )
|
|
{
|
|
ParserResponse *response = &group->group[ ri ];
|
|
pRestore->ReadAll( response );
|
|
}
|
|
}
|
|
|
|
pRestore->EndBlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
pRestore->EndBlock();
|
|
}
|
|
}
|
|
|
|
} g_ResponseSystemSaveRestoreOps;
|
|
|
|
ISaveRestoreOps *responseSystemSaveRestoreOps = &g_ResponseSystemSaveRestoreOps;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CDefaultResponseSystem::Init()
|
|
{
|
|
/*
|
|
Warning( "sizeof( Response ) == %d\n", sizeof( Response ) );
|
|
Warning( "sizeof( ResponseGroup ) == %d\n", sizeof( ResponseGroup ) );
|
|
Warning( "sizeof( Criteria ) == %d\n", sizeof( Criteria ) );
|
|
Warning( "sizeof( AI_ResponseParams ) == %d\n", sizeof( AI_ResponseParams ) );
|
|
*/
|
|
const char *basescript = GetScriptFile();
|
|
|
|
LoadRuleSet( basescript );
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CDefaultResponseSystem::Shutdown()
|
|
{
|
|
// Wipe instanced versions
|
|
ClearInstanced();
|
|
|
|
// Clear outselves
|
|
Clear();
|
|
// IServerSystem chain
|
|
BaseClass::Shutdown();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Instance a custom response system
|
|
// Input : *scriptfile -
|
|
// Output : IResponseSystem
|
|
//-----------------------------------------------------------------------------
|
|
IResponseSystem *PrecacheCustomResponseSystem( const char *scriptfile )
|
|
{
|
|
return defaultresponsesytem.PrecacheCustomResponseSystem( scriptfile );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Instance a custom response system
|
|
// Input : *scriptfile -
|
|
// set -
|
|
// Output : IResponseSystem
|
|
//-----------------------------------------------------------------------------
|
|
IResponseSystem *BuildCustomResponseSystemGivenCriteria( const char *pszBaseFile, const char *pszCustomName, AI_CriteriaSet &criteriaSet, float flCriteriaScore )
|
|
{
|
|
return defaultresponsesytem.BuildCustomResponseSystemGivenCriteria( pszBaseFile, pszCustomName, criteriaSet, flCriteriaScore );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void DestroyCustomResponseSystems()
|
|
{
|
|
defaultresponsesytem.DestroyCustomResponseSystems();
|
|
}
|