855 lines
22 KiB
C++
855 lines
22 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"
|
|
#include "ai_speech.h"
|
|
#include "tier0/icommandline.h"
|
|
#include <ctype.h>
|
|
#include "sceneentity.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/response_types_internal.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
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 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;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
/// 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:
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
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_F( rr_reloadresponsesystems, "Reload all response system scripts.", FCVAR_CHEAT )
|
|
{
|
|
defaultresponsesytem.ReloadAllResponseSystems();
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
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();
|
|
}
|