mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2025-06-07 19:32:09 +03:00
5558 lines
151 KiB
C++
5558 lines
151 KiB
C++
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
|
|
//
|
|
// Purpose: This file contains brand new VScript singletons and singletons replicated from API
|
|
// documentation in other games.
|
|
//
|
|
// See vscript_funcs_shared.cpp for more information.
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include <vgui_controls/Controls.h>
|
|
#include <vgui/ILocalize.h>
|
|
#include "ammodef.h"
|
|
#include "tier1/utlcommon.h"
|
|
|
|
#include "soundenvelope.h"
|
|
#include "saverestore_utlvector.h"
|
|
#include "stdstring.h"
|
|
|
|
#ifndef CLIENT_DLL
|
|
#include "ai_speech.h"
|
|
#include "ai_memory.h"
|
|
#include "ai_squad.h"
|
|
#endif // !CLIENT_DLL
|
|
|
|
#include "usermessages.h"
|
|
#include "filesystem.h"
|
|
#include "igameevents.h"
|
|
#include "engine/ivdebugoverlay.h"
|
|
|
|
#ifdef CLIENT_DLL
|
|
#include "IEffects.h"
|
|
#include "fx.h"
|
|
#include "itempents.h"
|
|
#include "c_te_legacytempents.h"
|
|
#include "iefx.h"
|
|
#include "dlight.h"
|
|
|
|
#if !defined(NO_STEAM)
|
|
#include "steam/steam_api.h"
|
|
#endif
|
|
#endif
|
|
|
|
#include "vscript_singletons.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
extern IScriptManager *scriptmanager;
|
|
|
|
|
|
#ifdef GAME_DLL
|
|
extern void SendProxy_StringT_To_String(const SendProp*, const void*, const void*, DVariant*, int, int);
|
|
extern void SendProxy_UtlVectorLength(const SendProp*, const void*, const void*, DVariant*, int, int);
|
|
class CSendProxyRecipients;
|
|
extern void* SendProxy_LengthTable(const SendProp*, const void*, const void* pData, CSendProxyRecipients*, int);
|
|
#define DataTableProxy_EHandle SendProxy_EHandleToInt
|
|
#define DataTableProxy_String SendProxy_StringToString
|
|
#define DataTableProxy_TableLength SendProxy_LengthTable
|
|
#define DataTableProxy_UtlVectorLength SendProxy_UtlVectorLength
|
|
#else
|
|
extern void RecvProxy_UtlVectorLength(const CRecvProxyData*, void*, void*);
|
|
extern void DataTableRecvProxy_LengthProxy(const RecvProp*, void**, void*, int);
|
|
#define DataTableProxy_EHandle RecvProxy_IntToEHandle
|
|
#define DataTableProxy_String RecvProxy_StringToString
|
|
#define DataTableProxy_TableLength DataTableRecvProxy_LengthProxy
|
|
#define DataTableProxy_UtlVectorLength RecvProxy_UtlVectorLength
|
|
#endif
|
|
extern ISaveRestoreOps* GetPhysObjSaveRestoreOps( PhysInterfaceId_t );
|
|
extern ISaveRestoreOps* ActivityDataOps();
|
|
extern ISaveRestoreOps* GetSoundSaveRestoreOps();
|
|
extern ISaveRestoreOps* GetStdStringDataOps();
|
|
#ifdef GAME_DLL
|
|
#define UTLVECTOR_DATAOPS( fieldType, dataType )\
|
|
CUtlVectorDataopsInstantiator< fieldType >::GetDataOps( (CUtlVector< dataType >*)0 )
|
|
#define IS_EHANDLE_UTLVECTOR( td )\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseEntity > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseFlex > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseAnimating > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseCombatWeapon > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBasePlayer > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CAI_BaseNPC > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneEntity > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneListManager > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CRagdollBoogie > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CFish > ) ||\
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CVGuiScreen > )
|
|
|
|
class CSceneListManager;
|
|
class CRagdollBoogie;
|
|
class CFish;
|
|
#ifdef _DEBUG
|
|
class CStringTableSaveRestoreOps;
|
|
extern CStringTableSaveRestoreOps g_VguiScreenStringOps;
|
|
extern INetworkStringTable *g_pStringTableVguiScreen;
|
|
extern ISaveRestoreOps *thinkcontextFuncs;
|
|
class CAI_EnemiesListSaveRestoreOps;
|
|
extern CAI_EnemiesListSaveRestoreOps g_AI_MemoryListSaveRestoreOps;
|
|
class CConceptHistoriesDataOps;
|
|
extern CConceptHistoriesDataOps g_ConceptHistoriesSaveDataOps;
|
|
#endif
|
|
#endif
|
|
|
|
//=============================================================================
|
|
// Net Prop Manager
|
|
// Based on L4D2 API
|
|
//=============================================================================
|
|
class CScriptNetPropManager
|
|
{
|
|
private:
|
|
#if GAME_DLL
|
|
typedef SendProp NetProp;
|
|
typedef SendTable NetTable;
|
|
typedef ServerClass NetworkClass;
|
|
|
|
NetworkClass *GetNetworkClass( CBaseEntity* p ) { return p->GetServerClass(); }
|
|
NetTable *GetNetTable( NetworkClass* p ) { return p->m_pTable; }
|
|
|
|
void NetworkStateChanged( CBaseEntity* p, int o ) { p->NetworkProp()->NetworkStateChanged( o ); }
|
|
#else
|
|
typedef RecvProp NetProp;
|
|
typedef RecvTable NetTable;
|
|
typedef ClientClass NetworkClass;
|
|
|
|
NetworkClass *GetNetworkClass( CBaseEntity* p ) { return p->GetClientClass(); }
|
|
NetTable *GetNetTable( NetworkClass* p ) { return p->m_pRecvTable; }
|
|
|
|
void NetworkStateChanged( CBaseEntity*, int ) {}
|
|
#endif
|
|
|
|
int GetClassID( CBaseEntity *p )
|
|
{
|
|
return GetNetworkClass( p )->m_ClassID;
|
|
}
|
|
|
|
int GetIntPropSize( NetProp *pProp )
|
|
{
|
|
Assert( pProp->GetType() == DPT_Int );
|
|
|
|
#ifdef GAME_DLL
|
|
extern void SendProxy_UInt8ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int );
|
|
extern void SendProxy_UInt16ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int );
|
|
extern void SendProxy_UInt32ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int );
|
|
|
|
SendVarProxyFn proxy = pProp->GetProxyFn();
|
|
|
|
if ( proxy == SendProxy_Int8ToInt32 || proxy == SendProxy_UInt8ToInt32 )
|
|
return 8;
|
|
if ( proxy == SendProxy_Int16ToInt32 || proxy == SendProxy_UInt16ToInt32 )
|
|
return 16;
|
|
if ( proxy == SendProxy_Int32ToInt32 || proxy == SendProxy_UInt32ToInt32 )
|
|
return 32;
|
|
|
|
return pProp->m_nBits;
|
|
#else
|
|
RecvVarProxyFn proxy = pProp->GetProxyFn();
|
|
|
|
if ( proxy == RecvProxy_Int32ToInt8 )
|
|
return 8;
|
|
if ( proxy == RecvProxy_Int32ToInt16 )
|
|
return 16;
|
|
if ( proxy == RecvProxy_Int32ToInt32 )
|
|
return 32;
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
bool IsEHandle( NetProp *pProp )
|
|
{
|
|
return ( pProp->GetProxyFn() == DataTableProxy_EHandle );
|
|
}
|
|
|
|
bool IsUtlVector( NetProp *pProp )
|
|
{
|
|
#ifdef GAME_DLL
|
|
SendVarProxyFn proxy = pProp->GetProxyFn();
|
|
#else
|
|
RecvVarProxyFn proxy = pProp->GetProxyFn();
|
|
#endif
|
|
|
|
return ( proxy == DataTableProxy_UtlVectorLength );
|
|
}
|
|
|
|
private:
|
|
enum types
|
|
{
|
|
_INT1 = ( 1 << 0 ),
|
|
_INT8 = ( 1 << 1 ),
|
|
_INT16 = ( 1 << 2 ),
|
|
_INT32 = ( 1 << 3 ),
|
|
_FLOAT = ( 1 << 4 ),
|
|
_VEC3 = ( 1 << 5 ),
|
|
_VEC2 = ( 1 << 6 ),
|
|
_EHANDLE = ( 1 << 7 ),
|
|
_CLASSPTR = ( 1 << 8 ),
|
|
_EDICT = ( 1 << 9 ),
|
|
_CSTRING = ( 1 << 10 ),
|
|
_STRING_T = ( 1 << 11 ),
|
|
_ARRAY = ( 1 << 12 ),
|
|
_DATATABLE = ( 1 << 13 ),
|
|
|
|
_PHYS = ( 1 << 14 ),
|
|
_STDSTRING = _CSTRING | _STRING_T,
|
|
|
|
_DAR_EHANDLE = _EHANDLE | _ARRAY,
|
|
_DAR_CLASSPTR = _CLASSPTR | _ARRAY,
|
|
_DAR_INT = _INT32 | _ARRAY,
|
|
_DAR_FLOAT = _FLOAT | _ARRAY,
|
|
|
|
//_MAX = ( 1 << 15 )
|
|
};
|
|
|
|
// UNDONE: Special case for GetPropType() to be able to return the table/array itself
|
|
#define INDEX_GET_TYPE 0
|
|
|
|
#define MASK_INT_SIZE( _size ) ( ( 1 << (_size - 1) ) | ( (1 << (_size - 1)) - 1 ) )
|
|
#define MASK_NEAREST_BYTE( _bits ) ( ( (1 << ALIGN_TO_NEAREST_BYTE(_bits)) - 1 ) & ~((1 << _bits) - 1) )
|
|
#define ALIGN_TO_NEAREST_BYTE( _bits ) ( (_bits + 7) & ~7 )
|
|
#define VARINFO_ARRAYSIZE_BITS 12
|
|
|
|
struct varinfo_t
|
|
{
|
|
int offset : 32; // actually a short
|
|
|
|
union
|
|
{
|
|
int mask : 32;
|
|
int stringsize : 32;
|
|
};
|
|
|
|
enum types datatype : 16;
|
|
|
|
// element size in bytes
|
|
unsigned int elemsize : 8;
|
|
unsigned int arraysize : VARINFO_ARRAYSIZE_BITS;
|
|
|
|
// Following are only used in integer netprops to handle unsigned and size casting
|
|
bool isUnsigned : 1;
|
|
bool isNotNetworked : 1;
|
|
|
|
int GetOffset( int index )
|
|
{
|
|
return offset + index * elemsize;
|
|
}
|
|
};
|
|
|
|
// Wrapper to be able to set case sensitive comparator in node insertion
|
|
class vardict_t : public CUtlDict< varinfo_t >
|
|
{
|
|
public:
|
|
vardict_t() : CUtlDict< varinfo_t >( k_eDictCompareTypeCaseSensitive ) {}
|
|
};
|
|
|
|
// NOTE: This is lazy and inefficient.
|
|
// Simply map highest level class id to unique caches.
|
|
CUtlVector< int > m_EntMap;
|
|
CUtlVector< vardict_t > m_VarDicts;
|
|
|
|
varinfo_t* CacheNew( CBaseEntity *pEnt, const char *szProp )
|
|
{
|
|
int idx = m_EntMap.Find( GetClassID( pEnt ) );
|
|
if ( idx == m_EntMap.InvalidIndex() )
|
|
{
|
|
// Vector indices are kept in parallel as a workaround for encapsulating maps
|
|
idx = m_EntMap.AddToTail( GetClassID( pEnt ) );
|
|
m_VarDicts.AddToTail();
|
|
}
|
|
|
|
vardict_t &dict = m_VarDicts.Element( idx );
|
|
|
|
idx = dict.Find( szProp );
|
|
if ( idx == dict.InvalidIndex() )
|
|
idx = dict.Insert( szProp );
|
|
|
|
varinfo_t *pInfo = &dict.Element( idx );
|
|
V_memset( pInfo, 0, sizeof( varinfo_t ) );
|
|
return pInfo;
|
|
}
|
|
|
|
varinfo_t* CacheFetch( CBaseEntity *pEnt, const char *szProp )
|
|
{
|
|
int idx = m_EntMap.Find( GetClassID( pEnt ) );
|
|
if ( idx == m_EntMap.InvalidIndex() )
|
|
return NULL;
|
|
|
|
vardict_t &dict = m_VarDicts.Element( idx );
|
|
idx = dict.Find( szProp );
|
|
if ( idx == dict.InvalidIndex() )
|
|
return NULL;
|
|
|
|
varinfo_t *pInfo = &dict.Element( idx );
|
|
return pInfo;
|
|
}
|
|
|
|
public:
|
|
~CScriptNetPropManager()
|
|
{
|
|
PurgeCache();
|
|
}
|
|
|
|
void PurgeCache()
|
|
{
|
|
m_EntMap.Purge();
|
|
m_VarDicts.Purge();
|
|
}
|
|
|
|
private:
|
|
typedescription_t *FindField( char *pBase, datamap_t *map, const char *szName, int *offset )
|
|
{
|
|
if ( map->baseMap )
|
|
{
|
|
typedescription_t* p = FindField( pBase, map->baseMap, szName, offset );
|
|
if ( p )
|
|
return p;
|
|
}
|
|
|
|
typedescription_t *pFields = map->dataDesc;
|
|
int numFields = map->dataNumFields;
|
|
|
|
for ( int i = 0; i < numFields; i++ )
|
|
{
|
|
typedescription_t* td = &pFields[i];
|
|
int fieldType = td->fieldType;
|
|
int fieldOffset = td->fieldOffset[ TD_OFFSET_NORMAL ];
|
|
|
|
if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) )
|
|
continue;
|
|
|
|
if ( fieldType == FIELD_VOID || fieldType == FIELD_FUNCTION )
|
|
continue;
|
|
|
|
if ( !V_strcmp( td->fieldName, szName ) )
|
|
{
|
|
*offset += fieldOffset;
|
|
|
|
if ( td->flags & FTYPEDESC_PTR )
|
|
{
|
|
// Follow the pointer
|
|
char * const pRef = *(char**)( pBase + *offset );
|
|
Assert( pRef );
|
|
*offset = pRef - pBase;
|
|
}
|
|
|
|
return td;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NetProp *FindProp( char *pBase, NetTable *pTable, const char *szName, int *offset )
|
|
{
|
|
int numProps = pTable->GetNumProps();
|
|
|
|
for ( int i = 0; i < numProps; i++ )
|
|
{
|
|
NetProp* pProp = pTable->GetProp(i);
|
|
|
|
if ( pProp->IsInsideArray() )
|
|
continue;
|
|
|
|
if ( !V_strcmp( pProp->GetName(), szName ) )
|
|
{
|
|
*offset += pProp->GetOffset();
|
|
return pProp;
|
|
}
|
|
|
|
// Go into inherited fields but not member tables, they are looked up explicitly
|
|
// This is only a problem with m_AnimOverlay
|
|
if ( ( pProp->GetFlags() & SPROP_COLLAPSIBLE ) ||
|
|
( pProp->GetType() == DPT_DataTable && pProp->GetOffset() == 0 ) )
|
|
{
|
|
// Don't go into lengthproxy
|
|
if ( pProp->GetDataTableProxyFn() == DataTableProxy_TableLength )
|
|
continue;
|
|
|
|
NetProp *p = FindProp( pBase + pProp->GetOffset(), pProp->GetDataTable(), szName, offset );
|
|
if ( p )
|
|
{
|
|
*offset += pProp->GetOffset();
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedescription_t *FindInDataMap( char * const pBase, datamap_t *map, const char *szFullProp, int *offset )
|
|
{
|
|
*offset = 0;
|
|
|
|
// Look for exact match
|
|
typedescription_t *pField = FindField( pBase, map, szFullProp, offset );
|
|
if ( pField )
|
|
return pField;
|
|
|
|
// Look for members
|
|
const char *pszProp = szFullProp;
|
|
const char *pszPropEnd = V_strnchr( pszProp, '.', 512 );
|
|
if ( !pszPropEnd )
|
|
return NULL;
|
|
do
|
|
{
|
|
// this string comes from squirrel stringtable, it can be modified
|
|
*((char*)pszPropEnd) = 0;
|
|
pField = FindField( pBase, map, pszProp, offset );
|
|
*((char*)pszPropEnd) = '.';
|
|
pszProp = pszPropEnd + 1;
|
|
|
|
if ( !pField || ( map = pField->td ) == NULL )
|
|
return NULL;
|
|
|
|
// Look for exact match again, just in case
|
|
pField = FindField( pBase, map, pszProp, offset );
|
|
if ( pField )
|
|
return pField;
|
|
} while ( ( pszPropEnd = V_strnchr( pszProp, '.', 512 ) ) != NULL );
|
|
|
|
return FindField( pBase, map, pszProp, offset );
|
|
}
|
|
|
|
NetProp *FindInNetTable( char * const pBase, NetTable *pTable, const char *szFullProp, int *offset )
|
|
{
|
|
*offset = 0;
|
|
|
|
// Look for exact match
|
|
NetProp *pProp = FindProp( pBase, pTable, szFullProp, offset );
|
|
if ( pProp )
|
|
return pProp;
|
|
|
|
// Look for members
|
|
const char *pszProp = szFullProp;
|
|
const char *pszPropEnd = V_strnchr( pszProp, '.', 512 );
|
|
if ( !pszPropEnd )
|
|
return NULL;
|
|
do
|
|
{
|
|
// this string comes from squirrel stringtable, it can be modified
|
|
*((char*)pszPropEnd) = 0;
|
|
pProp = FindProp( pBase, pTable, pszProp, offset );
|
|
*((char*)pszPropEnd) = '.';
|
|
pszProp = pszPropEnd + 1;
|
|
|
|
if ( !pProp || ( pTable = pProp->GetDataTable() ) == NULL )
|
|
return NULL;
|
|
|
|
// Look for exact match again for fields such as m_Local{m_skybox3d.scale}
|
|
pProp = FindProp( pBase, pTable, pszProp, offset );
|
|
if ( pProp )
|
|
return pProp;
|
|
} while ( ( pszPropEnd = V_strnchr( pszProp, '.', 512 ) ) != NULL );
|
|
|
|
return FindProp( pBase, pTable, pszProp, offset );
|
|
}
|
|
|
|
// Searches NetTable first to handle overwritten member network variables - see
|
|
// CPlayerResource::m_iHealth and CBaseEntity::m_iHealth
|
|
varinfo_t *GetVarInfo( CBaseEntity *pEnt, const char *szProp, int index )
|
|
{
|
|
int offset = 0;
|
|
NetTable *pTable = GetNetTable( GetNetworkClass( pEnt ) );
|
|
NetProp *pProp = FindInNetTable( (char*)pEnt, pTable, szProp, &offset );
|
|
if ( pProp )
|
|
{
|
|
|
|
#define SetVarInfo()\
|
|
varinfo_t *pInfo = CacheNew( pEnt, szProp );\
|
|
pInfo->isNotNetworked = 0;\
|
|
pInfo->elemsize = pProp->GetElementStride();\
|
|
pInfo->arraysize = pProp->GetNumElements();\
|
|
pInfo->offset = offset;
|
|
|
|
switch ( pProp->GetType() )
|
|
{
|
|
case DPT_Int:
|
|
{
|
|
if ( IsUtlVector( pProp ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if ( index < 0 || index >= pProp->GetNumElements() )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
Assert( index == 0 || pProp->GetElementStride() > 0 );
|
|
|
|
if ( IsEHandle( pProp ) )
|
|
{
|
|
Assert( pProp->GetElementStride() == sizeof(int) || pProp->GetElementStride() < 0 );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_EHANDLE;
|
|
return pInfo;
|
|
}
|
|
else
|
|
{
|
|
const int size = GetIntPropSize( pProp );
|
|
#ifdef CLIENT_DLL
|
|
// Client might be reading any amount of bits in a custom RecvProxy
|
|
// Break and check the datamaps
|
|
if ( size == 0 )
|
|
break;
|
|
#endif
|
|
Assert( size <= pProp->GetElementStride() || pProp->GetElementStride() < 0 );
|
|
|
|
SetVarInfo();
|
|
pInfo->mask = MASK_INT_SIZE( size );
|
|
pInfo->datatype = types::_INT32;
|
|
return pInfo;
|
|
}
|
|
}
|
|
case DPT_Float:
|
|
{
|
|
if ( index < 0 || index >= pProp->GetNumElements() )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
Assert( index == 0 || pProp->GetElementStride() > 0 );
|
|
Assert( pProp->GetElementStride() == sizeof(float) || pProp->GetElementStride() < 0 );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_FLOAT;
|
|
return pInfo;
|
|
}
|
|
case DPT_Vector:
|
|
{
|
|
if ( index < 0 || index >= pProp->GetNumElements() )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
Assert( index == 0 || pProp->GetElementStride() > 0 );
|
|
Assert( pProp->GetElementStride() == sizeof(float)*3 || pProp->GetElementStride() < 0 );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_VEC3;
|
|
return pInfo;
|
|
}
|
|
case DPT_VectorXY:
|
|
{
|
|
if ( index < 0 || index >= pProp->GetNumElements() )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
Assert( index == 0 || pProp->GetElementStride() > 0 );
|
|
Assert( pProp->GetElementStride() == sizeof(float)*2 || pProp->GetElementStride() < 0 );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_VEC2;
|
|
return pInfo;
|
|
}
|
|
case DPT_String:
|
|
{
|
|
if ( index < 0 || index >= pProp->GetNumElements() )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
Assert( index == 0 || pProp->GetElementStride() > 0 );
|
|
|
|
SetVarInfo();
|
|
#ifdef GAME_DLL
|
|
pInfo->stringsize = 0;
|
|
#else
|
|
pInfo->stringsize = pProp->m_StringBufferSize;
|
|
#endif
|
|
#ifdef GAME_DLL
|
|
if ( pProp->GetProxyFn() == SendProxy_StringT_To_String )
|
|
{
|
|
pInfo->datatype = types::_STRING_T;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
Assert( pProp->GetProxyFn() == DataTableProxy_String );
|
|
pInfo->datatype = types::_CSTRING;
|
|
}
|
|
return pInfo;
|
|
}
|
|
case DPT_DataTable:
|
|
{
|
|
NetTable* pArray = pProp->GetDataTable();
|
|
|
|
if ( V_strcmp( pProp->GetName(), pArray->GetName() ) != 0 )
|
|
{
|
|
Warning( "DT is not an array! %s(%s)\n", pProp->GetName(), pArray->GetName() );
|
|
return NULL;
|
|
}
|
|
|
|
if ( index < 0 || index >= pArray->GetNumProps() )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
pProp = pArray->GetProp( index );
|
|
|
|
switch ( pProp->GetType() )
|
|
{
|
|
case DPT_Int:
|
|
{
|
|
if ( IsEHandle( pProp ) )
|
|
{
|
|
varinfo_t *pInfo = CacheNew( pEnt, szProp );
|
|
pInfo->elemsize = sizeof(int);
|
|
pInfo->arraysize = pArray->GetNumProps();
|
|
pInfo->offset = offset;
|
|
pInfo->datatype = types::_EHANDLE;
|
|
return pInfo;
|
|
}
|
|
else
|
|
{
|
|
const int size = GetIntPropSize( pProp );
|
|
#ifdef CLIENT_DLL
|
|
// Client might be reading any amount of bits in a custom RecvProxy
|
|
// Break and check the datamaps
|
|
if ( size == 0 )
|
|
break;
|
|
#endif
|
|
varinfo_t *pInfo = CacheNew( pEnt, szProp );
|
|
|
|
if ( pArray->GetNumProps() > 1 )
|
|
{
|
|
pInfo->elemsize = pArray->GetProp(1)->GetOffset() - pArray->GetProp(0)->GetOffset();
|
|
}
|
|
else
|
|
{
|
|
// Doesn't matter for an array of a single element
|
|
pInfo->elemsize = 0;
|
|
}
|
|
|
|
pInfo->arraysize = pArray->GetNumProps();
|
|
pInfo->offset = offset;
|
|
pInfo->mask = MASK_INT_SIZE( size );
|
|
pInfo->datatype = types::_INT32;
|
|
return pInfo;
|
|
}
|
|
}
|
|
case DPT_Float:
|
|
{
|
|
varinfo_t *pInfo = CacheNew( pEnt, szProp );
|
|
pInfo->elemsize = sizeof(float);
|
|
pInfo->arraysize = pArray->GetNumProps();
|
|
pInfo->offset = offset;
|
|
pInfo->datatype = types::_FLOAT;
|
|
return pInfo;
|
|
}
|
|
case DPT_Vector:
|
|
{
|
|
varinfo_t *pInfo = CacheNew( pEnt, szProp );
|
|
pInfo->elemsize = sizeof(float)*3;
|
|
pInfo->arraysize = pArray->GetNumProps();
|
|
pInfo->offset = offset;
|
|
pInfo->datatype = types::_VEC3;
|
|
return pInfo;
|
|
}
|
|
case DPT_VectorXY:
|
|
{
|
|
varinfo_t *pInfo = CacheNew( pEnt, szProp );
|
|
pInfo->elemsize = sizeof(float)*2;
|
|
pInfo->arraysize = pArray->GetNumProps();
|
|
pInfo->offset = offset;
|
|
pInfo->datatype = types::_VEC2;
|
|
return pInfo;
|
|
}
|
|
case DPT_DataTable:
|
|
{
|
|
AssertMsg( 0, "DT in DT" );
|
|
return NULL;
|
|
}
|
|
case DPT_Array:
|
|
{
|
|
AssertMsg( 0, "Array in DT" );
|
|
return NULL;
|
|
}
|
|
case DPT_String:
|
|
{
|
|
AssertMsg( 0, "String in DT" );
|
|
return NULL;
|
|
}
|
|
default: UNREACHABLE();
|
|
}
|
|
#ifdef CLIENT_DLL
|
|
// DPT_Int can break into here for datamap fallback
|
|
break;
|
|
#else
|
|
UNREACHABLE();
|
|
#endif
|
|
} // DPT_DataTable
|
|
case DPT_Array:
|
|
{
|
|
Assert( pProp->GetArrayProp() );
|
|
|
|
NetProp *pArray = pProp->GetArrayProp();
|
|
offset += pArray->GetOffset();
|
|
|
|
if ( index < 0 || index >= pProp->GetNumElements() )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
switch ( pArray->GetType() )
|
|
{
|
|
case DPT_Int:
|
|
{
|
|
Assert( index == 0 || pProp->GetElementStride() > 0 );
|
|
|
|
if ( IsEHandle( pArray ) )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_EHANDLE;
|
|
return pInfo;
|
|
}
|
|
else
|
|
{
|
|
const int size = GetIntPropSize( pArray );
|
|
#ifdef CLIENT_DLL
|
|
// Client might be reading any amount of bits in a custom RecvProxy
|
|
// Break and check the datamaps
|
|
if ( size == 0 )
|
|
break;
|
|
#endif
|
|
SetVarInfo();
|
|
pInfo->mask = MASK_INT_SIZE( size );
|
|
pInfo->datatype = types::_INT32;
|
|
return pInfo;
|
|
}
|
|
}
|
|
case DPT_Float:
|
|
{
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_FLOAT;
|
|
return pInfo;
|
|
}
|
|
case DPT_Vector:
|
|
{
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_VEC3;
|
|
return pInfo;
|
|
}
|
|
case DPT_VectorXY:
|
|
{
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_VEC2;
|
|
return pInfo;
|
|
}
|
|
case DPT_String:
|
|
{
|
|
AssertMsg( 0, "String array not implemented" );
|
|
return NULL;
|
|
}
|
|
case DPT_Array:
|
|
case DPT_DataTable: AssertMsg( 0, "DT in array" );
|
|
default: UNREACHABLE();
|
|
}
|
|
#ifdef CLIENT_DLL
|
|
// DPT_Int can break into here for datamap fallback
|
|
break;
|
|
#else
|
|
UNREACHABLE();
|
|
#endif
|
|
} // DPT_Array
|
|
default: UNREACHABLE();
|
|
}
|
|
// ambigious int size on client, check the datamaps
|
|
#undef SetVarInfo
|
|
}
|
|
|
|
datamap_t *map = pEnt->GetDataDescMap();
|
|
typedescription_t *pField = FindInDataMap( (char*)pEnt, map, szProp, &offset );
|
|
if ( pField )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
find_field:
|
|
#endif
|
|
if ( index < 0 || index >= pField->fieldSize )
|
|
{
|
|
Warning( "NetProp element index out of range! %s[%d]\n", szProp, index );
|
|
return NULL;
|
|
}
|
|
|
|
#define SetVarInfo()\
|
|
varinfo_t *pInfo = CacheNew( pEnt, szProp );\
|
|
pInfo->isNotNetworked = 1;\
|
|
pInfo->elemsize = pField->fieldSizeInBytes / pField->fieldSize;\
|
|
pInfo->arraysize = pField->fieldSize;\
|
|
pInfo->offset = offset;
|
|
|
|
switch ( pField->fieldType )
|
|
{
|
|
case FIELD_INTEGER:
|
|
case FIELD_MATERIALINDEX:
|
|
case FIELD_MODELINDEX:
|
|
case FIELD_COLOR32:
|
|
case FIELD_TICK:
|
|
case FIELD_BOOLEAN:
|
|
case FIELD_CHARACTER:
|
|
case FIELD_SHORT:
|
|
{
|
|
SetVarInfo();
|
|
pInfo->isUnsigned = ( pField->flags & SPROP_UNSIGNED ) != 0;
|
|
pInfo->isNotNetworked = 1;
|
|
switch ( pField->fieldType )
|
|
{
|
|
case FIELD_INTEGER:
|
|
case FIELD_MATERIALINDEX:
|
|
case FIELD_MODELINDEX:
|
|
case FIELD_COLOR32:
|
|
case FIELD_TICK:
|
|
pInfo->datatype = types::_INT32; break;
|
|
case FIELD_BOOLEAN:
|
|
pInfo->datatype = types::_INT1; break;
|
|
case FIELD_CHARACTER:
|
|
Assert( pField->fieldSizeInBytes == pField->fieldSize );
|
|
pInfo->stringsize = pField->fieldSizeInBytes;
|
|
pInfo->datatype = types::_INT8; break;
|
|
case FIELD_SHORT:
|
|
pInfo->datatype = types::_INT16; break;
|
|
default: UNREACHABLE();
|
|
}
|
|
return pInfo;
|
|
}
|
|
case FIELD_FLOAT:
|
|
case FIELD_TIME:
|
|
{
|
|
Assert( sizeof(float) == pField->fieldSizeInBytes / pField->fieldSize );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_FLOAT;
|
|
return pInfo;
|
|
}
|
|
case FIELD_EHANDLE:
|
|
{
|
|
Assert( sizeof(int) == pField->fieldSizeInBytes / pField->fieldSize );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_EHANDLE;
|
|
return pInfo;
|
|
}
|
|
#ifdef GAME_DLL
|
|
case FIELD_CLASSPTR:
|
|
{
|
|
Assert( sizeof(int*) == pField->fieldSizeInBytes / pField->fieldSize );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_CLASSPTR;
|
|
return pInfo;
|
|
}
|
|
case FIELD_EDICT:
|
|
{
|
|
Assert( sizeof(int*) == pField->fieldSizeInBytes / pField->fieldSize );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_EDICT;
|
|
return pInfo;
|
|
}
|
|
#endif
|
|
case FIELD_VECTOR:
|
|
case FIELD_POSITION_VECTOR:
|
|
{
|
|
Assert( sizeof(float)*3 == pField->fieldSizeInBytes / pField->fieldSize );
|
|
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_VEC3;
|
|
return pInfo;
|
|
}
|
|
case FIELD_STRING:
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
{
|
|
SetVarInfo();
|
|
pInfo->stringsize = 0;
|
|
pInfo->datatype = types::_STRING_T;
|
|
return pInfo;
|
|
}
|
|
case FIELD_CUSTOM:
|
|
{
|
|
if ( pField->pSaveRestoreOps == GetPhysObjSaveRestoreOps( PIID_IPHYSICSOBJECT ) )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_PHYS;
|
|
return pInfo;
|
|
}
|
|
else if ( pField->pSaveRestoreOps == ActivityDataOps() )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_INT32;
|
|
return pInfo;
|
|
}
|
|
#ifdef GAME_DLL
|
|
else if ( IS_EHANDLE_UTLVECTOR( pField ) )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get
|
|
pInfo->datatype = types::_DAR_EHANDLE;
|
|
}
|
|
else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get
|
|
pInfo->datatype = types::_DAR_CLASSPTR;
|
|
}
|
|
else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get
|
|
pInfo->datatype = types::_DAR_INT;
|
|
}
|
|
else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) ||
|
|
pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get
|
|
pInfo->datatype = types::_DAR_FLOAT;
|
|
}
|
|
// Only used by CAI_PlayerAlly::m_PendingConcept
|
|
else if ( pField->pSaveRestoreOps == GetStdStringDataOps() )
|
|
{
|
|
SetVarInfo();
|
|
pInfo->datatype = types::_STDSTRING;
|
|
return pInfo;
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
case FIELD_EMBEDDED:
|
|
return NULL;
|
|
default:
|
|
AssertMsg( 0, "Unknown type %d\n", pField->fieldType );
|
|
return NULL;
|
|
}
|
|
UNREACHABLE();
|
|
#undef SetVarInfo
|
|
}
|
|
#ifdef CLIENT_DLL
|
|
else
|
|
{
|
|
map = pEnt->GetPredDescMap();
|
|
pField = FindInDataMap( (char*)pEnt, map, szProp, &offset );
|
|
if ( pField )
|
|
{
|
|
goto find_field;
|
|
}
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
public:
|
|
// FIXME: Cannot get datatable/arrays at the moment
|
|
bool HasProp( HSCRIPT hEnt, const char *szProp )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return false;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE );
|
|
|
|
if ( !pInfo )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// FIXME: Cannot get datatable/arrays at the moment
|
|
const char *GetPropType( HSCRIPT hEnt, const char *szProp )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return NULL;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE );
|
|
|
|
if ( !pInfo )
|
|
return NULL;
|
|
}
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_INT1:
|
|
case types::_INT8:
|
|
case types::_INT16:
|
|
case types::_INT32:
|
|
return "integer";
|
|
case types::_FLOAT:
|
|
return "float";
|
|
case types::_VEC3:
|
|
return "vector";
|
|
case types::_VEC2:
|
|
return "vector2d";
|
|
case types::_CSTRING:
|
|
case types::_STRING_T:
|
|
case types::_STDSTRING:
|
|
return "string";
|
|
case types::_EHANDLE:
|
|
case types::_CLASSPTR:
|
|
case types::_EDICT:
|
|
return "entity";
|
|
case types::_PHYS:
|
|
return "phys";
|
|
case types::_ARRAY:
|
|
return "array";
|
|
case types::_DATATABLE:
|
|
return "datatable";
|
|
}
|
|
|
|
if ( pInfo->arraysize > 1 )
|
|
return "array";
|
|
|
|
return "<unknown>";
|
|
}
|
|
|
|
int GetPropArraySize( HSCRIPT hEnt, const char *szProp )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return -1;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE );
|
|
|
|
if ( !pInfo )
|
|
return -1;
|
|
}
|
|
#ifdef GAME_DLL
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_DAR_EHANDLE:
|
|
{
|
|
CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return -1;
|
|
return vec.Count();
|
|
}
|
|
case types::_DAR_CLASSPTR:
|
|
{
|
|
CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return -1;
|
|
return vec.Count();
|
|
}
|
|
case types::_DAR_INT:
|
|
{
|
|
CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return -1;
|
|
return vec.Count();
|
|
}
|
|
case types::_DAR_FLOAT:
|
|
{
|
|
CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return -1;
|
|
return vec.Count();
|
|
}
|
|
}
|
|
#endif
|
|
return pInfo->arraysize;
|
|
}
|
|
|
|
public:
|
|
int GetPropIntArray( HSCRIPT hEnt, const char *szProp, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return -1;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return -1;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return -1;
|
|
|
|
if ( pInfo->isNotNetworked )
|
|
{
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_INT32:
|
|
if ( pInfo->isUnsigned )
|
|
return *(unsigned int*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
return *(int*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
case types::_INT1:
|
|
return *(bool*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
case types::_INT8:
|
|
if ( pInfo->isUnsigned )
|
|
return *(unsigned char*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
return *(char*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
case types::_INT16:
|
|
if ( pInfo->isUnsigned )
|
|
return *(unsigned short*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
return *(short*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
#ifdef GAME_DLL
|
|
case types::_DAR_INT:
|
|
{
|
|
CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return -1;
|
|
if ( index >= vec.Count() )
|
|
return -1;
|
|
return vec[ index ];
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_INT32:
|
|
return (*(int*)((char*)pEnt + pInfo->GetOffset( index ))) & pInfo->mask;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void SetPropIntArray( HSCRIPT hEnt, const char *szProp, int value, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return;
|
|
|
|
if ( pInfo->isNotNetworked )
|
|
{
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_INT32:
|
|
if ( pInfo->isUnsigned )
|
|
{
|
|
*(unsigned int*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
*(int*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
case types::_INT1:
|
|
*(bool*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
case types::_INT8:
|
|
if ( pInfo->isUnsigned )
|
|
{
|
|
*(unsigned char*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
*(char*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
case types::_INT16:
|
|
if ( pInfo->isUnsigned )
|
|
{
|
|
*(unsigned short*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
*(short*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
#ifdef GAME_DLL
|
|
case types::_DAR_INT:
|
|
{
|
|
CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return;
|
|
if ( index >= vec.Count() )
|
|
return;
|
|
vec[ index ] = value;
|
|
NetworkStateChanged( pEnt, pInfo->offset );
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_INT32:
|
|
{
|
|
int *dest = (int*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
*dest = (*dest & ~pInfo->mask) | (value & pInfo->mask);
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
float GetPropFloatArray( HSCRIPT hEnt, const char *szProp, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return -1;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return -1;
|
|
}
|
|
|
|
unsigned int arraysize = pInfo->arraysize;
|
|
|
|
if ( pInfo->datatype == types::_VEC3 )
|
|
arraysize *= 3;
|
|
|
|
if ( index < 0 || (unsigned int)index >= arraysize )
|
|
return -1;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_FLOAT:
|
|
return *(float*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
case types::_VEC3:
|
|
return ((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ];
|
|
#ifdef GAME_DLL
|
|
case types::_DAR_FLOAT:
|
|
{
|
|
CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return -1;
|
|
if ( index >= vec.Count() )
|
|
return -1;
|
|
return vec[ index ];
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void SetPropFloatArray( HSCRIPT hEnt, const char *szProp, float value, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return;
|
|
}
|
|
|
|
unsigned int arraysize = pInfo->arraysize;
|
|
|
|
if ( pInfo->datatype == types::_VEC3 )
|
|
arraysize *= 3;
|
|
|
|
if ( index < 0 || (unsigned int)index >= arraysize )
|
|
return;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_FLOAT:
|
|
*(float*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
case types::_VEC3:
|
|
((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ] = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index / 3 ) );
|
|
break;
|
|
#ifdef GAME_DLL
|
|
case types::_DAR_FLOAT:
|
|
{
|
|
CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return;
|
|
if ( index >= vec.Count() )
|
|
return;
|
|
vec[ index ] = value;
|
|
NetworkStateChanged( pEnt, pInfo->offset );
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
HSCRIPT GetPropEntityArray( HSCRIPT hEnt, const char *szProp, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return NULL;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return NULL;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return NULL;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_EHANDLE:
|
|
{
|
|
EHANDLE &iEHandle = *(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
return ToHScript( iEHandle );
|
|
}
|
|
#ifdef GAME_DLL
|
|
case types::_CLASSPTR:
|
|
{
|
|
CBaseEntity* ptr = *(CBaseEntity**)((char*)pEnt + pInfo->GetOffset( index ));
|
|
return ToHScript( ptr );
|
|
}
|
|
case types::_EDICT:
|
|
{
|
|
edict_t* ptr = *(edict_t**)((char*)pEnt + pInfo->GetOffset( index ));
|
|
return ToHScript( GetContainingEntity( ptr ) );
|
|
}
|
|
case types::_DAR_EHANDLE:
|
|
{
|
|
CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return NULL;
|
|
if ( index >= vec.Count() )
|
|
return NULL;
|
|
return ToHScript( vec[ index ] );
|
|
}
|
|
case types::_DAR_CLASSPTR:
|
|
{
|
|
CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return NULL;
|
|
if ( index >= vec.Count() )
|
|
return NULL;
|
|
return ToHScript( vec[ index ] );
|
|
}
|
|
#endif
|
|
case types::_PHYS:
|
|
{
|
|
IPhysicsObject* ptr = *(IPhysicsObject**)((char*)pEnt + pInfo->GetOffset( index ));
|
|
return ptr ? g_pScriptVM->RegisterInstance( ptr ) : NULL;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void SetPropEntityArray( HSCRIPT hEnt, const char *szProp, HSCRIPT value, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_EHANDLE:
|
|
*(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index )) = ToEnt( value );
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
#ifdef GAME_DLL
|
|
case types::_CLASSPTR:
|
|
*(CBaseEntity**)((char*)pEnt + pInfo->GetOffset( index )) = ToEnt( value );
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
case types::_EDICT:
|
|
{
|
|
CBaseEntity* ptr = ToEnt( value );
|
|
*(edict_t**)((char*)pEnt + pInfo->GetOffset( index )) = ptr ? ptr->edict() : NULL;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
case types::_DAR_EHANDLE:
|
|
{
|
|
CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return;
|
|
if ( index >= vec.Count() )
|
|
return;
|
|
vec[ index ] = ToEnt( value );
|
|
NetworkStateChanged( pEnt, pInfo->offset );
|
|
break;
|
|
}
|
|
case types::_DAR_CLASSPTR:
|
|
{
|
|
CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset);
|
|
if ( !vec.Base() )
|
|
return;
|
|
if ( index >= vec.Count() )
|
|
return;
|
|
vec[ index ] = ToEnt( value );
|
|
NetworkStateChanged( pEnt, pInfo->offset );
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
const Vector &GetPropVectorArray( HSCRIPT hEnt, const char *szProp, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return vec3_invalid;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return vec3_invalid;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return vec3_invalid;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_VEC3:
|
|
return *(Vector*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
}
|
|
|
|
return vec3_invalid;
|
|
}
|
|
|
|
void SetPropVectorArray( HSCRIPT hEnt, const char *szProp, const Vector &value, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_VEC3:
|
|
*(Vector*)((char*)pEnt + pInfo->GetOffset( index )) = value;
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
const char *GetPropStringArray( HSCRIPT hEnt, const char *szProp, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return NULL;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return NULL;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return NULL;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_CSTRING:
|
|
return (const char*)((char*)pEnt + pInfo->GetOffset( index ));
|
|
case types::_STRING_T: // Identical to _CSTRING on client
|
|
return STRING( *(string_t*)((char*)pEnt + pInfo->GetOffset( index )) );
|
|
case types::_INT8:
|
|
{
|
|
if ( !pInfo->stringsize )
|
|
return NULL;
|
|
|
|
char * const pVar = ((char*)pEnt + pInfo->GetOffset( index ));
|
|
|
|
// Is this null terminated?
|
|
int i = 0;
|
|
char *c = pVar;
|
|
while ( *(c++) && i++ < pInfo->stringsize );
|
|
|
|
if ( i >= pInfo->stringsize )
|
|
{
|
|
// Not a null terminated string, don't talk to me ever again
|
|
pInfo->stringsize = 0;
|
|
return NULL;
|
|
}
|
|
|
|
return pVar;
|
|
}
|
|
#ifdef GAME_DLL
|
|
case types::_STDSTRING:
|
|
return ( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->c_str();
|
|
#endif
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void SetPropStringArray( HSCRIPT hEnt, const char *szProp, const char *value, int index )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
varinfo_t *pInfo = CacheFetch( pEnt, szProp );
|
|
if ( !pInfo )
|
|
{
|
|
pInfo = GetVarInfo( pEnt, szProp, index );
|
|
|
|
if ( !pInfo )
|
|
return;
|
|
}
|
|
|
|
if ( index < 0 || (unsigned int)index >= pInfo->arraysize )
|
|
return;
|
|
|
|
switch ( pInfo->datatype )
|
|
{
|
|
case types::_CSTRING:
|
|
case types::_INT8:
|
|
{
|
|
if ( pInfo->stringsize )
|
|
{
|
|
V_strncpy( (char*)pEnt + pInfo->GetOffset( index ), value, pInfo->stringsize );
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
}
|
|
case types::_STRING_T:
|
|
{
|
|
extern string_t FindPooledString( const char* );
|
|
extern string_t AllocPooledString( const char* );
|
|
|
|
string_t src = FindPooledString( value );
|
|
if ( src == NULL_STRING )
|
|
src = AllocPooledString( value );
|
|
#ifdef GAME_DLL
|
|
*(string_t*)((char*)pEnt + pInfo->GetOffset( index )) = src;
|
|
#else
|
|
V_strcpy( (char*)pEnt + pInfo->GetOffset( index ), src );
|
|
#endif
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
#ifdef GAME_DLL
|
|
case types::_STDSTRING:
|
|
{
|
|
( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->assign( value, V_strlen(value) );
|
|
NetworkStateChanged( pEnt, pInfo->GetOffset( index ) );
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#define GetProp( type, name )\
|
|
type GetProp##name( HSCRIPT hEnt, const char* szProp )\
|
|
{\
|
|
return GetProp##name##Array( hEnt, szProp, 0 );\
|
|
}
|
|
|
|
#define SetProp( type, name )\
|
|
void SetProp##name( HSCRIPT hEnt, const char* szProp, type value )\
|
|
{\
|
|
SetProp##name##Array( hEnt, szProp, value, 0 );\
|
|
}
|
|
|
|
GetProp( int, Int );
|
|
SetProp( int, Int );
|
|
GetProp( float, Float );
|
|
SetProp( float, Float );
|
|
GetProp( HSCRIPT, Entity );
|
|
SetProp( HSCRIPT, Entity );
|
|
GetProp( const Vector&, Vector );
|
|
SetProp( const Vector&, Vector );
|
|
GetProp( const char*, String );
|
|
SetProp( const char*, String );
|
|
|
|
#undef GetProp
|
|
#undef SetProp
|
|
|
|
#ifdef _DEBUG
|
|
private:
|
|
CUtlBuffer m_output;
|
|
CUtlString m_indent;
|
|
int m_indent_level;
|
|
|
|
void IndentStart()
|
|
{
|
|
m_indent = "";
|
|
m_indent_level = 0;
|
|
}
|
|
|
|
void Indent1()
|
|
{
|
|
m_indent_level++;
|
|
m_indent.Append("\t");
|
|
}
|
|
|
|
void Indent0()
|
|
{
|
|
m_indent_level--;
|
|
m_indent = m_indent.Slice( 0, m_indent_level );
|
|
}
|
|
|
|
void PrintVec3( float *pVar )
|
|
{
|
|
if ( *(Vector*)pVar != vec3_invalid )
|
|
{
|
|
Print( "[%f %f %f]", pVar[0], pVar[1], pVar[2] );
|
|
}
|
|
else
|
|
{
|
|
Print("vec3_invalid");
|
|
}
|
|
}
|
|
|
|
void PrintVec2( float *pVar )
|
|
{
|
|
Print( "[%f %f]", pVar[0], pVar[1] );
|
|
}
|
|
|
|
void PrintEntity( EHANDLE* pVar )
|
|
{
|
|
CBaseEntity* ent = *pVar;
|
|
if ( ent )
|
|
{
|
|
Print("[%d]%s", ent->entindex(), ent->GetDebugName());
|
|
}
|
|
else
|
|
{
|
|
Print("null");
|
|
}
|
|
}
|
|
#ifdef GAME_DLL
|
|
void PrintEntity( CBaseEntity* pVar )
|
|
{
|
|
CBaseEntity* ent = pVar;
|
|
if ( ent )
|
|
{
|
|
Print("[%d]%s", ent->entindex(), ent->GetDebugName());
|
|
}
|
|
else
|
|
{
|
|
Print("null");
|
|
}
|
|
}
|
|
|
|
void PrintEntity( edict_t* pVar )
|
|
{
|
|
CBaseEntity* ent = GetContainingEntity( pVar );
|
|
if ( ent )
|
|
{
|
|
Print("[%d]%s", ent->entindex(), ent->GetDebugName());
|
|
}
|
|
else
|
|
{
|
|
Print("null");
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef GAME_DLL
|
|
void PrintString( string_t pVar )
|
|
{
|
|
if ( STRING(pVar) )
|
|
{
|
|
Print("\"%s\"", STRING(pVar));
|
|
}
|
|
else
|
|
{
|
|
Print("null");
|
|
}
|
|
}
|
|
#endif
|
|
void PrintString( const char *pVar )
|
|
{
|
|
if ( pVar )
|
|
{
|
|
Print("\"%s\"", pVar);
|
|
}
|
|
else
|
|
{
|
|
Print("null");
|
|
}
|
|
}
|
|
|
|
void PrintPropType( NetProp *pProp )
|
|
{
|
|
switch ( pProp->GetType() )
|
|
{
|
|
case DPT_Int:
|
|
if ( IsUtlVector( pProp ) )
|
|
{
|
|
Print("UtlVector");
|
|
}
|
|
else if ( IsEHandle( pProp ) )
|
|
{
|
|
Print( "entity" );
|
|
}
|
|
else
|
|
{
|
|
Print( "int" );
|
|
}
|
|
break;
|
|
#ifdef SUPPORTS_INT64
|
|
case DPT_Int64:
|
|
AssertMsg( 0, "not implemented" );
|
|
Print( "int64" );
|
|
break;
|
|
#endif
|
|
case DPT_Float:
|
|
Print( "float" );
|
|
break;
|
|
case DPT_Vector:
|
|
Print( "vec3" );
|
|
break;
|
|
case DPT_VectorXY:
|
|
Print( "vec2" );
|
|
break;
|
|
case DPT_String:
|
|
{
|
|
#ifdef GAME_DLL
|
|
if ( pProp->GetProxyFn() == SendProxy_StringT_To_String )
|
|
{
|
|
Print("string_t");
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
Print("string[%d]", pProp->m_StringBufferSize);
|
|
#else
|
|
Print("string");
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case DPT_Array:
|
|
case DPT_DataTable:
|
|
break;
|
|
default: UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void PrintProp_r( char *pVar, NetProp *pProp )
|
|
{
|
|
switch ( pProp->GetType() )
|
|
{
|
|
case DPT_Int:
|
|
{
|
|
if ( IsUtlVector( pProp ) )
|
|
{
|
|
}
|
|
else if ( IsEHandle( pProp ) )
|
|
{
|
|
PrintEntity( (EHANDLE*)pVar );
|
|
}
|
|
else
|
|
{
|
|
#ifdef GAME_DLL
|
|
// Is this value larger than networked size?
|
|
AssertMsg( (*(int*)pVar & MASK_NEAREST_BYTE( pProp->m_nBits )) == 0,
|
|
"%s(%i) %d bits doesn't fit networked %d bits",
|
|
pProp->GetName(), *(int*)pVar & MASK_NEAREST_BYTE( pProp->m_nBits ), ALIGN_TO_NEAREST_BYTE(pProp->m_nBits), pProp->m_nBits );
|
|
#endif
|
|
int size = GetIntPropSize( pProp );
|
|
if ( size )
|
|
{
|
|
Print( "%i", *(int*)pVar & MASK_INT_SIZE( size ) );
|
|
}
|
|
else
|
|
{
|
|
Print( "<unknown size> 0x%08x", *(int*)pVar );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#ifdef SUPPORTS_INT64
|
|
case DPT_Int64:
|
|
{
|
|
Print( "%lli", *(int64*)pVar );
|
|
break;
|
|
}
|
|
#endif
|
|
case DPT_Float:
|
|
{
|
|
Assert( pProp->GetElementStride() == sizeof(float) || pProp->GetElementStride() < 0 );
|
|
if ( *(float*)pVar == FLT_MAX )
|
|
{
|
|
Print("FLT_MAX");
|
|
}
|
|
else
|
|
{
|
|
Print("%f", *(float*)pVar);
|
|
}
|
|
break;
|
|
}
|
|
case DPT_Vector:
|
|
{
|
|
PrintVec3( (float*)pVar );
|
|
break;
|
|
}
|
|
case DPT_VectorXY:
|
|
{
|
|
PrintVec2( (float*)pVar );
|
|
break;
|
|
}
|
|
case DPT_String:
|
|
{
|
|
#ifdef GAME_DLL
|
|
if ( pProp->GetProxyFn() == SendProxy_StringT_To_String )
|
|
{
|
|
PrintString( *(string_t*)pVar );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
Assert( pProp->GetProxyFn() == DataTableProxy_String );
|
|
PrintString( (char*)pVar );
|
|
}
|
|
break;
|
|
}
|
|
case DPT_DataTable:
|
|
{
|
|
NetTable* pArray = pProp->GetDataTable();
|
|
Assert( pArray->GetNumProps() );
|
|
|
|
if ( V_strcmp( pProp->GetName(), pArray->GetName() ) != 0 )
|
|
{
|
|
Print( " -> (%s)\n", pArray->GetName() );
|
|
DumpNetTable_r( pVar, pArray );
|
|
break;
|
|
}
|
|
|
|
// Double check that each element is the same size
|
|
// Array indexing ints gets element size from this
|
|
int diff1 = pArray->GetProp(1)->GetOffset() - pArray->GetProp(0)->GetOffset();
|
|
for ( int k = 0; k < pArray->GetNumProps()-1; k++ )
|
|
{
|
|
int diff2 = pArray->GetProp(k+1)->GetOffset() - pArray->GetProp(k)->GetOffset();
|
|
Assert( diff1 == diff2 );
|
|
}
|
|
|
|
Print(" <");
|
|
PrintPropType( pArray->GetProp(0) );
|
|
Print(" array> #%d", pArray->GetNumProps());
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
|
|
for ( int j = 0; j < pArray->GetNumProps(); j++ )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
PrintProp_r( pVar + pArray->GetProp(j)->GetOffset(), pArray->GetProp(j) );
|
|
}
|
|
|
|
Indent0();
|
|
Print( "\n%s]", m_indent.Get() );
|
|
|
|
break;
|
|
}
|
|
case DPT_Array:
|
|
{
|
|
Assert( pProp->GetArrayProp() );
|
|
NetProp *pArray = pProp->GetArrayProp();
|
|
pVar += pArray->GetOffset();
|
|
|
|
int numElements = pProp->GetNumElements();
|
|
int elementStride = pProp->GetElementStride();
|
|
|
|
Print(" <");
|
|
PrintPropType( pArray );
|
|
Print(" array> #%d", numElements);
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
|
|
for ( int j = 0; j < numElements; j++ )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
PrintProp_r( pVar + j * elementStride, pArray );
|
|
}
|
|
|
|
Indent0();
|
|
Print( "\n%s]", m_indent.Get() );
|
|
|
|
break;
|
|
}
|
|
default: UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void DumpNetTable_r( void *pEnt, NetTable *pTable )
|
|
{
|
|
Print("%s{\n", m_indent.Get());
|
|
Indent1();
|
|
|
|
int numProps = pTable->GetNumProps();
|
|
|
|
for ( int i = 0; i < numProps; i++ )
|
|
{
|
|
NetProp* pProp = pTable->GetProp(i);
|
|
char* pVar = (char*)pEnt + pProp->GetOffset();
|
|
|
|
if ( pProp->IsInsideArray() )
|
|
continue;
|
|
|
|
Print( "%s%s", m_indent.Get(), pProp->GetName() );
|
|
|
|
if ( pProp->GetOffset() == 0 )
|
|
Print("<0>");
|
|
|
|
if ( pProp->GetType() != DPT_DataTable )
|
|
Print(" <");
|
|
PrintPropType( pProp );
|
|
if ( pProp->GetType() != DPT_DataTable )
|
|
Print("> ");
|
|
PrintProp_r( pVar, pProp );
|
|
Print("\n");
|
|
}
|
|
|
|
Indent0();
|
|
Print("%s}", m_indent.Get());
|
|
}
|
|
|
|
void PrintFieldType( char *pVar, typedescription_t *td )
|
|
{
|
|
switch ( td->fieldType )
|
|
{
|
|
case FIELD_INTEGER:
|
|
case FIELD_MATERIALINDEX:
|
|
case FIELD_MODELINDEX:
|
|
case FIELD_TICK:
|
|
Print( "int" );
|
|
break;
|
|
case FIELD_SHORT:
|
|
Print( "short" );
|
|
break;
|
|
case FIELD_CHARACTER:
|
|
Print( "char" );
|
|
break;
|
|
case FIELD_BOOLEAN:
|
|
Print( "bool" );
|
|
break;
|
|
case FIELD_COLOR32:
|
|
Print( "clr32" );
|
|
break;
|
|
case FIELD_FLOAT:
|
|
case FIELD_TIME:
|
|
Print( "float" );
|
|
break;
|
|
case FIELD_VECTOR:
|
|
case FIELD_POSITION_VECTOR:
|
|
Print( "vec3" );
|
|
break;
|
|
case FIELD_VECTOR2D:
|
|
Print( "vec2" );
|
|
break;
|
|
case FIELD_STRING:
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
Print( "string" );
|
|
break;
|
|
case FIELD_EHANDLE:
|
|
#ifdef GAME_DLL
|
|
case FIELD_CLASSPTR:
|
|
case FIELD_EDICT:
|
|
#endif
|
|
Print( "entity" );
|
|
break;
|
|
case FIELD_VMATRIX:
|
|
Print( "VMatrix" );
|
|
break;
|
|
case FIELD_VMATRIX_WORLDSPACE:
|
|
Print( "VMatrix WORLDSPACE" );
|
|
break;
|
|
case FIELD_MATRIX3X4_WORLDSPACE:
|
|
Print( "matrix3x4 WORLDSPACE" );
|
|
break;
|
|
case FIELD_INTERVAL:
|
|
Print( "interval_t" );
|
|
break;
|
|
case FIELD_CUSTOM:
|
|
PrintCustomFieldType( pVar, td );
|
|
break;
|
|
case FIELD_EMBEDDED:
|
|
if ( td->fieldSize > 1 )
|
|
Print( "DT" );
|
|
break;
|
|
default:
|
|
Print( "unknown %d", td->fieldType );
|
|
}
|
|
}
|
|
|
|
void PrintCustomFieldType( char *pVar, typedescription_t *td )
|
|
{
|
|
Assert( td->fieldType == FIELD_CUSTOM );
|
|
|
|
const char *g_ppszPhysTypeNames[PIID_NUM_TYPES] =
|
|
{
|
|
"Unknown Phys",
|
|
"IPhysicsObject",
|
|
"IPhysicsFluidController",
|
|
"IPhysicsSpring",
|
|
"IPhysicsConstraintGroup",
|
|
"IPhysicsConstraint",
|
|
"IPhysicsShadowController",
|
|
"IPhysicsPlayerController",
|
|
"IPhysicsMotionController",
|
|
"IPhysicsVehicleController",
|
|
};
|
|
|
|
for ( int i = 0; i < PIID_NUM_TYPES; i++ )
|
|
{
|
|
if ( td->pSaveRestoreOps == GetPhysObjSaveRestoreOps( (PhysInterfaceId_t)i ) )
|
|
{
|
|
Print("%s", g_ppszPhysTypeNames[i]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( td->pSaveRestoreOps == ActivityDataOps() )
|
|
{
|
|
Print("int");
|
|
}
|
|
else if ( td->pSaveRestoreOps == GetSoundSaveRestoreOps() )
|
|
{
|
|
Print("CSoundPatch");
|
|
}
|
|
else if ( td->pSaveRestoreOps == GetStdStringDataOps() )
|
|
{
|
|
Print("stdstring");
|
|
}
|
|
#ifdef GAME_DLL
|
|
else if ( IS_EHANDLE_UTLVECTOR( td ) )
|
|
{
|
|
CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)pVar;
|
|
if ( vec.Base() )
|
|
Print("entity utlvector #%d", vec.Count());
|
|
else
|
|
Print("entity utlvector");
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) )
|
|
{
|
|
CUtlVector< int > &vec = *(CUtlVector< int >*)pVar;
|
|
if ( vec.Base() )
|
|
Print("int utlvector #%d", vec.Count());
|
|
else
|
|
Print("int utlvector");
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) ||
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) )
|
|
{
|
|
CUtlVector< float > &vec = *(CUtlVector< float >*)pVar;
|
|
if ( vec.Base() )
|
|
Print("float utlvector #%d", vec.Count());
|
|
else
|
|
Print("float utlvector");
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_STRING, string_t ) )
|
|
{
|
|
CUtlVector< string_t > &vec = *(CUtlVector< string_t >*)pVar;
|
|
if ( vec.Base() )
|
|
Print("string utlvector #%d", vec.Count());
|
|
else
|
|
Print("string utlvector");
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) )
|
|
{
|
|
CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)pVar;
|
|
if ( vec.Base() )
|
|
Print("entity utlvector #%d", vec.Count());
|
|
else
|
|
Print("entity utlvector");
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_VECTOR, Vector ) )
|
|
{
|
|
AssertMsg( 0, "Implement me" );
|
|
CUtlVector< Vector > &vec = *(CUtlVector< Vector >*)pVar;
|
|
if ( vec.Base() )
|
|
Print("Vector utlvector #%d", vec.Count());
|
|
else
|
|
Print("Vector utlvector");
|
|
}
|
|
else if ( !V_strcmp( td->fieldName, "m_pIk" ) )
|
|
{
|
|
Print("IK");
|
|
}
|
|
else if ( td->pSaveRestoreOps == thinkcontextFuncs )
|
|
{
|
|
Print("thinkfunc");
|
|
}
|
|
else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_AI_MemoryListSaveRestoreOps) )
|
|
{
|
|
Print("AI memory map");
|
|
}
|
|
else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_VguiScreenStringOps))
|
|
{
|
|
Print("string (vgui screen)");
|
|
}
|
|
else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_ConceptHistoriesSaveDataOps) )
|
|
{
|
|
Print("concept histories");
|
|
}
|
|
#endif // GAME_DLL
|
|
else
|
|
{
|
|
Print("custom");
|
|
}
|
|
}
|
|
|
|
void PrintCustomField( char *pVar, typedescription_t *td )
|
|
{
|
|
Assert( td->fieldType == FIELD_CUSTOM );
|
|
|
|
for ( int i = 0; i < PIID_NUM_TYPES; i++ )
|
|
{
|
|
if ( td->pSaveRestoreOps == GetPhysObjSaveRestoreOps( (PhysInterfaceId_t)i ) )
|
|
{
|
|
Print("0x%x", pVar);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( td->pSaveRestoreOps == ActivityDataOps() )
|
|
{
|
|
Print("%i", *(int*)pVar);
|
|
}
|
|
else if ( td->pSaveRestoreOps == GetSoundSaveRestoreOps() )
|
|
{
|
|
if ( *pVar )
|
|
{
|
|
CSoundPatch *pSound = *(CSoundPatch**)pVar;
|
|
PrintString( CSoundEnvelopeController::GetController().SoundGetName( pSound ) );
|
|
}
|
|
else
|
|
{
|
|
Print( "null" );
|
|
}
|
|
}
|
|
else if ( td->pSaveRestoreOps == GetStdStringDataOps() )
|
|
{
|
|
Print("%s", ((std::string*)pVar)->c_str());
|
|
}
|
|
#ifdef GAME_DLL
|
|
else if ( IS_EHANDLE_UTLVECTOR( td ) )
|
|
{
|
|
CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)pVar;
|
|
if ( !vec.Base() )
|
|
{
|
|
Print("null");
|
|
return;
|
|
}
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
FOR_EACH_VEC( vec, i )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
PrintEntity( vec[i] );
|
|
}
|
|
Indent0();
|
|
Print("\n%s]", m_indent.Get());
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) )
|
|
{
|
|
CUtlVector< int > &vec = *(CUtlVector< int >*)pVar;
|
|
if ( !vec.Base() )
|
|
{
|
|
Print("null");
|
|
return;
|
|
}
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
FOR_EACH_VEC( vec, i )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
Print( "%i", vec[i] );
|
|
}
|
|
Indent0();
|
|
Print("\n%s]", m_indent.Get());
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) ||
|
|
td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) )
|
|
{
|
|
CUtlVector< float > &vec = *(CUtlVector< float >*)pVar;
|
|
if ( !vec.Base() )
|
|
{
|
|
Print("null");
|
|
return;
|
|
}
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
FOR_EACH_VEC( vec, i )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
Print( "%f", vec[i] );
|
|
}
|
|
Indent0();
|
|
Print("\n%s]", m_indent.Get());
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_STRING, string_t ) )
|
|
{
|
|
CUtlVector< string_t > &vec = *(CUtlVector< string_t >*)pVar;
|
|
if ( !vec.Base() )
|
|
{
|
|
Print("null");
|
|
return;
|
|
}
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
FOR_EACH_VEC( vec, i )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
PrintString( vec[i] );
|
|
}
|
|
Indent0();
|
|
Print("\n%s]", m_indent.Get());
|
|
}
|
|
else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) )
|
|
{
|
|
CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)pVar;
|
|
if ( !vec.Base() )
|
|
{
|
|
Print("null");
|
|
return;
|
|
}
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
FOR_EACH_VEC( vec, i )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
PrintEntity( vec[i] );
|
|
}
|
|
Indent0();
|
|
Print("\n%s]", m_indent.Get());
|
|
}
|
|
else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_VguiScreenStringOps) )
|
|
{
|
|
const char *pString = g_pStringTableVguiScreen->GetString( *(int*)pVar );
|
|
PrintString( (char*)pString );
|
|
}
|
|
#endif // GAME_DLL
|
|
else
|
|
{
|
|
Print("0x%x", pVar);
|
|
}
|
|
}
|
|
|
|
void PrintField_r( char *pVar, typedescription_t *td )
|
|
{
|
|
switch ( td->fieldType )
|
|
{
|
|
case FIELD_INTEGER:
|
|
case FIELD_MATERIALINDEX:
|
|
case FIELD_MODELINDEX:
|
|
case FIELD_TICK:
|
|
if ( td->flags & SPROP_UNSIGNED )
|
|
{
|
|
Print("%u", *(unsigned int*)pVar);
|
|
}
|
|
else
|
|
{
|
|
Print("%i", *(int*)pVar);
|
|
}
|
|
break;
|
|
case FIELD_COLOR32:
|
|
Print("0x%08x", *(int*)pVar);
|
|
break;
|
|
case FIELD_BOOLEAN:
|
|
Print("%i", *(bool*)pVar & 1);
|
|
break;
|
|
case FIELD_CHARACTER:
|
|
if ( *pVar < 0x20 )
|
|
{
|
|
Print("%i (0x%x)", *pVar, *pVar);
|
|
}
|
|
else
|
|
{
|
|
Print("%i '%c'", *pVar, *pVar);
|
|
}
|
|
break;
|
|
case FIELD_SHORT:
|
|
if ( td->flags & SPROP_UNSIGNED )
|
|
{
|
|
Print("%u", *(unsigned short*)pVar);
|
|
}
|
|
else
|
|
{
|
|
Print("%i", *(short*)pVar);
|
|
}
|
|
break;
|
|
case FIELD_FLOAT:
|
|
case FIELD_TIME:
|
|
if ( *(float*)pVar == FLT_MAX )
|
|
{
|
|
Print("FLT_MAX");
|
|
}
|
|
else
|
|
{
|
|
Print("%f", *(float*)pVar);
|
|
}
|
|
break;
|
|
case FIELD_VECTOR:
|
|
case FIELD_POSITION_VECTOR:
|
|
PrintVec3( (float*)pVar );
|
|
break;
|
|
case FIELD_VECTOR2D:
|
|
PrintVec2( (float*)pVar );
|
|
break;
|
|
case FIELD_STRING:
|
|
case FIELD_MODELNAME:
|
|
case FIELD_SOUNDNAME:
|
|
#ifdef GAME_DLL
|
|
PrintString( *(string_t*)pVar );
|
|
#else
|
|
PrintString( *(char**)pVar );
|
|
#endif
|
|
break;
|
|
case FIELD_EHANDLE:
|
|
PrintEntity( (EHANDLE*)pVar );
|
|
break;
|
|
#ifdef GAME_DLL
|
|
case FIELD_CLASSPTR:
|
|
PrintEntity( *(CBaseEntity**)pVar );
|
|
break;
|
|
case FIELD_EDICT:
|
|
PrintEntity( *(edict_t**)pVar );
|
|
break;
|
|
#endif
|
|
case FIELD_EMBEDDED:
|
|
Print(" -> (%s)\n", td->td->dataClassName);
|
|
DumpDataFields_r( pVar, td->td );
|
|
break;
|
|
case FIELD_CUSTOM:
|
|
PrintCustomField( pVar, td );
|
|
break;
|
|
default:
|
|
Print( "<unknown field %d>", td->fieldType );
|
|
}
|
|
}
|
|
|
|
void DumpDataFields_r( void *pEnt, datamap_t *map )
|
|
{
|
|
Print("%s{\n", m_indent.Get());
|
|
Indent1();
|
|
|
|
if ( map->baseMap )
|
|
{
|
|
Print("%sbaseclass -> (%s)\n", m_indent.Get(), map->baseMap->dataClassName);
|
|
DumpDataFields_r( pEnt, map->baseMap );
|
|
Print("\n");
|
|
}
|
|
|
|
typedescription_t *pFields = map->dataDesc;
|
|
int numFields = map->dataNumFields;
|
|
|
|
for ( int i = 0; i < numFields; i++ )
|
|
{
|
|
typedescription_t* td = &pFields[i];
|
|
|
|
if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) )
|
|
continue;
|
|
|
|
if ( td->fieldType == FIELD_VOID || td->fieldType == FIELD_FUNCTION )
|
|
continue;
|
|
|
|
char *pVar = (char*)pEnt + td->fieldOffset[ TD_OFFSET_NORMAL ];
|
|
|
|
if ( td->flags & FTYPEDESC_PTR )
|
|
{
|
|
AssertMsg( *(char**)pVar, "NULL ptr ref" );
|
|
pVar = *(char**)pVar;
|
|
}
|
|
|
|
Print( "%s%s", m_indent.Get(), td->fieldName );
|
|
|
|
if ( td->fieldSize == 1 )
|
|
{
|
|
if ( td->fieldType != FIELD_EMBEDDED )
|
|
Print(" <");
|
|
PrintFieldType( pVar, td );
|
|
if ( td->fieldType != FIELD_EMBEDDED )
|
|
Print("> ");
|
|
PrintField_r( pVar, td );
|
|
}
|
|
else
|
|
{
|
|
Print(" <");
|
|
PrintFieldType( pVar, td );
|
|
Print(" array> #%d", td->fieldSize);
|
|
|
|
Print("\n%s[", m_indent.Get());
|
|
Indent1();
|
|
|
|
for ( int j = 0; j < td->fieldSize; j++ )
|
|
{
|
|
Print("\n%s", m_indent.Get());
|
|
PrintField_r( pVar + j * td->fieldSizeInBytes / td->fieldSize, td );
|
|
}
|
|
|
|
Indent0();
|
|
Print("\n%s]", m_indent.Get());
|
|
}
|
|
|
|
Print("\n");
|
|
}
|
|
|
|
Indent0();
|
|
Print("%s}", m_indent.Get());
|
|
}
|
|
|
|
void Print( const char *fmt, ... )
|
|
{
|
|
char buf[2048];
|
|
va_list va;
|
|
va_start( va, fmt );
|
|
V_vsnprintf( buf, sizeof(buf) - 1, fmt, va );
|
|
va_end( va );
|
|
|
|
m_output.PutString( buf );
|
|
}
|
|
|
|
public:
|
|
void Dump( HSCRIPT hEnt, const char* filename )
|
|
{
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
if ( !pEnt )
|
|
return;
|
|
|
|
if ( !filename || !*filename )
|
|
return;
|
|
|
|
m_output.SetBufferType( true, false );
|
|
IndentStart();
|
|
|
|
Print( "<NetTable>\n" );
|
|
Print( "(%s)\n", GetNetTable( GetNetworkClass(pEnt) )->GetName() );
|
|
DumpNetTable_r( pEnt, GetNetTable( GetNetworkClass(pEnt) ) );
|
|
Print( "\n</NetTable>\n" );
|
|
|
|
Print( "<DataDesc>\n" );
|
|
Print( "(%s)\n", pEnt->GetDataDescMap()->dataClassName );
|
|
DumpDataFields_r( pEnt, pEnt->GetDataDescMap() );
|
|
Print( "\n</DataDesc>\n" );
|
|
#ifdef CLIENT_DLL
|
|
Print( "<PredDesc>\n" );
|
|
Print( "(%s)\n", pEnt->GetPredDescMap()->dataClassName );
|
|
DumpDataFields_r( pEnt, pEnt->GetPredDescMap() );
|
|
Print( "\n</PredDesc>\n" );
|
|
#endif
|
|
const char *pszFile = V_GetFileName( filename );
|
|
filesystem->WriteFile( pszFile, "MOD", m_output );
|
|
|
|
m_indent.Purge();
|
|
m_output.Purge();
|
|
}
|
|
#endif // _DEBUG
|
|
} g_ScriptNetPropManager;
|
|
|
|
BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SINGLETON "Allows reading and updating the network properties and data fields of an entity." )
|
|
DEFINE_SCRIPTFUNC( GetPropArraySize, "Returns the size of an array." )
|
|
DEFINE_SCRIPTFUNC( GetPropEntity, "Reads an entity." )
|
|
DEFINE_SCRIPTFUNC( GetPropEntityArray, "Reads an entity from an array." )
|
|
DEFINE_SCRIPTFUNC( GetPropFloat, "Reads a float." )
|
|
DEFINE_SCRIPTFUNC( GetPropFloatArray, "Reads a float from an array." )
|
|
DEFINE_SCRIPTFUNC( GetPropInt, "Reads an integer." )
|
|
DEFINE_SCRIPTFUNC( GetPropIntArray, "Reads an integer from an array." )
|
|
DEFINE_SCRIPTFUNC( GetPropString, "Reads a string." )
|
|
DEFINE_SCRIPTFUNC( GetPropStringArray, "Reads a string from an array." )
|
|
DEFINE_SCRIPTFUNC( GetPropVector, "Reads a 3D vector." )
|
|
DEFINE_SCRIPTFUNC( GetPropVectorArray, "Reads a 3D vector from an array." )
|
|
DEFINE_SCRIPTFUNC( GetPropType, "Returns the netprop type as a string." )
|
|
DEFINE_SCRIPTFUNC( HasProp, "Checks if netprop/datafield exists." )
|
|
DEFINE_SCRIPTFUNC( SetPropEntity, "Sets an entity." )
|
|
DEFINE_SCRIPTFUNC( SetPropEntityArray, "Sets an entity in an array." )
|
|
DEFINE_SCRIPTFUNC( SetPropFloat, "Sets to the specified float." )
|
|
DEFINE_SCRIPTFUNC( SetPropFloatArray, "Sets a float in an array." )
|
|
DEFINE_SCRIPTFUNC( SetPropInt, "Sets to the specified integer." )
|
|
DEFINE_SCRIPTFUNC( SetPropIntArray, "Sets an integer in an array." )
|
|
DEFINE_SCRIPTFUNC( SetPropString, "Sets to the specified string." )
|
|
DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a string in an array." )
|
|
DEFINE_SCRIPTFUNC( SetPropVector, "Sets to the specified vector." )
|
|
DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a 3D vector in an array." )
|
|
#ifdef _DEBUG
|
|
DEFINE_SCRIPTFUNC( Dump, "Dump all readable netprop and datafield values of this entity. Pass in file name to write into." );
|
|
#endif
|
|
END_SCRIPTDESC();
|
|
|
|
//=============================================================================
|
|
// Localization Interface
|
|
// Unique to Mapbase
|
|
//=============================================================================
|
|
class CScriptLocalize
|
|
{
|
|
public:
|
|
|
|
const char *GetTokenAsUTF8( const char *pszToken )
|
|
{
|
|
const char *pText = g_pVGuiLocalize->FindAsUTF8( pszToken );
|
|
if ( pText )
|
|
{
|
|
return pText;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void AddStringAsUTF8( const char *pszToken, const char *pszString )
|
|
{
|
|
wchar_t wpszString[256];
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( pszString, wpszString, sizeof(wpszString) );
|
|
|
|
// TODO: This is a fake file name! Should "fileName" mean anything?
|
|
g_pVGuiLocalize->AddString( pszToken, wpszString, "resource/vscript_localization.txt" );
|
|
}
|
|
|
|
private:
|
|
} g_ScriptLocalize;
|
|
|
|
BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptLocalize, "CLocalize", SCRIPT_SINGLETON "Accesses functions related to localization strings." )
|
|
|
|
DEFINE_SCRIPTFUNC( GetTokenAsUTF8, "Gets the current language's token as a UTF-8 string (not Unicode)." )
|
|
|
|
DEFINE_SCRIPTFUNC( AddStringAsUTF8, "Adds a new localized token as a UTF-8 string (not Unicode)." )
|
|
|
|
END_SCRIPTDESC();
|
|
|
|
//=============================================================================
|
|
// Game Event Listener
|
|
// Based on Source 2 API
|
|
//
|
|
// NOTE: In Source 2 vscript (Lua) event listener contexts are tables that are
|
|
// passed to the callback function as the call environment.
|
|
// In mapbase implementation these are string identifiers because unlike Lua,
|
|
// Squirrel has closure methods such as 'bindenv' which can bind functions to specified environments.
|
|
//=============================================================================
|
|
|
|
// Define to use the older code that loads all events manually independent from the game event manager.
|
|
// Otherwise access event descriptors directly from engine.
|
|
//#define USE_OLD_EVENT_DESCRIPTORS 1
|
|
|
|
class CScriptGameEventListener : public IGameEventListener2, public CAutoGameSystem
|
|
{
|
|
public:
|
|
CScriptGameEventListener() : m_bActive(false)
|
|
{
|
|
#ifdef _DEBUG
|
|
m_nEventTick = 0;
|
|
#endif
|
|
}
|
|
|
|
~CScriptGameEventListener()
|
|
{
|
|
StopListeningForEvent();
|
|
}
|
|
|
|
int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext );
|
|
void StopListeningForEvent();
|
|
|
|
public:
|
|
static bool StopListeningToGameEvent( int listener );
|
|
static void StopListeningToAllGameEvents( const char* szContext );
|
|
|
|
public:
|
|
void FireGameEvent( IGameEvent *event );
|
|
void LevelShutdownPreEntity();
|
|
|
|
private:
|
|
//int m_index;
|
|
HSCRIPT m_hCallback;
|
|
unsigned int m_iContextHash;
|
|
bool m_bActive;
|
|
#ifdef _DEBUG
|
|
int m_nEventTick;
|
|
#endif
|
|
|
|
static StringHashFunctor Hash;
|
|
static inline unsigned int HashContext( const char* c ) { return c ? Hash(c) : 0; }
|
|
|
|
inline int GetIndex()
|
|
{
|
|
Assert( sizeof(CScriptGameEventListener*) == sizeof(int) );
|
|
return reinterpret_cast<intptr_t>(this);
|
|
}
|
|
|
|
public:
|
|
enum // event data types, dependant on engine definitions
|
|
{
|
|
TYPE_LOCAL = 0,
|
|
TYPE_STRING = 1,
|
|
TYPE_FLOAT = 2,
|
|
TYPE_LONG = 3,
|
|
TYPE_SHORT = 4,
|
|
TYPE_BYTE = 5,
|
|
TYPE_BOOL = 6
|
|
};
|
|
static void WriteEventData( IGameEvent *event, HSCRIPT hTable );
|
|
|
|
#ifdef USE_OLD_EVENT_DESCRIPTORS
|
|
static void LoadAllEvents();
|
|
static void LoadEventsFromFile( const char *filename, const char *pathID = NULL );
|
|
static CUtlMap< unsigned int, KeyValues* > s_GameEvents;
|
|
static CUtlVector< KeyValues* > s_LoadedFiles;
|
|
#endif
|
|
|
|
public:
|
|
//static int g_nIndexCounter;
|
|
static CUtlVectorAutoPurge< CScriptGameEventListener* > s_Listeners;
|
|
#if _DEBUG
|
|
static void DumpEventListeners();
|
|
#endif
|
|
|
|
};
|
|
|
|
CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_Listeners;
|
|
StringHashFunctor CScriptGameEventListener::Hash;
|
|
|
|
#ifdef USE_OLD_EVENT_DESCRIPTORS
|
|
CUtlMap< unsigned int, KeyValues* > CScriptGameEventListener::s_GameEvents( DefLessFunc(unsigned int) );
|
|
CUtlVector< KeyValues* > CScriptGameEventListener::s_LoadedFiles;
|
|
#endif
|
|
|
|
|
|
#if _DEBUG
|
|
#ifdef CLIENT_DLL
|
|
CON_COMMAND_F( cl_dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT )
|
|
{
|
|
CScriptGameEventListener::DumpEventListeners();
|
|
}
|
|
#else
|
|
CON_COMMAND_F( dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT )
|
|
{
|
|
CScriptGameEventListener::DumpEventListeners();
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#ifdef USE_OLD_EVENT_DESCRIPTORS
|
|
//-----------------------------------------------------------------------------
|
|
// Executed in LevelInitPreEntity
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptGameEventListener::LoadAllEvents()
|
|
{
|
|
// Listed in the same order they are loaded in GameEventManager
|
|
const char *filenames[] =
|
|
{
|
|
"resource/serverevents.res",
|
|
"resource/gameevents.res",
|
|
"resource/mapbaseevents.res",
|
|
"resource/modevents.res"
|
|
};
|
|
|
|
const char *pathlist[] =
|
|
{
|
|
"GAME",
|
|
"MOD"
|
|
};
|
|
|
|
// Destroy old KeyValues
|
|
if ( s_LoadedFiles.Count() )
|
|
{
|
|
for ( int i = s_LoadedFiles.Count(); i--; )
|
|
s_LoadedFiles[i]->deleteThis();
|
|
s_LoadedFiles.Purge();
|
|
s_GameEvents.Purge();
|
|
}
|
|
|
|
for ( int j = 0; j < ARRAYSIZE(pathlist); ++j )
|
|
for ( int i = 0; i < ARRAYSIZE(filenames); ++i )
|
|
{
|
|
LoadEventsFromFile( filenames[i], pathlist[j] );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Load event files into a lookup array to be able to return the event data to the VM.
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const char *pathID )
|
|
{
|
|
KeyValues *pKV = new KeyValues("GameEvents");
|
|
|
|
if ( !pKV->LoadFromFile( filesystem, filename, pathID ) )
|
|
{
|
|
// CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Failed to load file [%s]%s\n", pathID, filename );
|
|
pKV->deleteThis();
|
|
return;
|
|
}
|
|
|
|
int count = 0;
|
|
|
|
for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() )
|
|
{
|
|
for ( KeyValues *sub = key->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
|
|
{
|
|
if ( sub->GetDataType() == KeyValues::TYPE_STRING )
|
|
{
|
|
const char *szVal = sub->GetString();
|
|
if ( !V_stricmp( szVal, "string" ) )
|
|
{
|
|
sub->SetInt( NULL, TYPE_STRING );
|
|
}
|
|
else if ( !V_stricmp( szVal, "bool" ) )
|
|
{
|
|
sub->SetInt( NULL, TYPE_BOOL );
|
|
}
|
|
else if ( !V_stricmp( szVal, "byte" ) )
|
|
{
|
|
sub->SetInt( NULL, TYPE_BYTE );
|
|
}
|
|
else if ( !V_stricmp( szVal, "short" ) )
|
|
{
|
|
sub->SetInt( NULL, TYPE_SHORT );
|
|
}
|
|
else if ( !V_stricmp( szVal, "long" ) )
|
|
{
|
|
sub->SetInt( NULL, TYPE_LONG );
|
|
}
|
|
else if ( !V_stricmp( szVal, "float" ) )
|
|
{
|
|
sub->SetInt( NULL, TYPE_FLOAT );
|
|
}
|
|
}
|
|
// none : value is not networked
|
|
// string : a zero terminated string
|
|
// bool : unsigned int, 1 bit
|
|
// byte : unsigned int, 8 bit
|
|
// short : signed int, 16 bit
|
|
// long : signed int, 32 bit
|
|
// float : float, 32 bit
|
|
}
|
|
|
|
// Store event subkeys
|
|
// Replace key so modevents can overwrite gameevents.
|
|
// It does not check for hash collisions, however.
|
|
s_GameEvents.InsertOrReplace( Hash( key->GetName() ), key );
|
|
++count;
|
|
}
|
|
|
|
// Store files (allocated KV)
|
|
s_LoadedFiles.AddToTail( pKV );
|
|
|
|
CGMsg( 2, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Loaded [%s]%s (%i)\n", pathID, filename, count );
|
|
}
|
|
#endif
|
|
|
|
#if _DEBUG
|
|
void CScriptGameEventListener::DumpEventListeners()
|
|
{
|
|
CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump start\n" );
|
|
CGMsg( 0, CON_GROUP_VSCRIPT, "# ID CONTEXT\n" );
|
|
FOR_EACH_VEC( s_Listeners, i )
|
|
{
|
|
CGMsg( 0, CON_GROUP_VSCRIPT, " %d : %d : %u\n", i,
|
|
s_Listeners[i]->GetIndex(),
|
|
s_Listeners[i]->m_iContextHash );
|
|
}
|
|
CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump end\n" );
|
|
}
|
|
#endif
|
|
|
|
void CScriptGameEventListener::LevelShutdownPreEntity()
|
|
{
|
|
s_Listeners.FindAndFastRemove(this);
|
|
delete this;
|
|
}
|
|
|
|
void CScriptGameEventListener::FireGameEvent( IGameEvent *event )
|
|
{
|
|
#ifdef _DEBUG
|
|
m_nEventTick = gpGlobals->tickcount;
|
|
#endif
|
|
ScriptVariant_t hTable;
|
|
g_pScriptVM->CreateTable( hTable );
|
|
WriteEventData( event, hTable );
|
|
g_pScriptVM->SetValue( hTable, "game_event_listener", GetIndex() );
|
|
// g_pScriptVM->SetValue( hTable, "game_event_name", event->GetName() );
|
|
g_pScriptVM->ExecuteFunction( m_hCallback, &hTable, 1, NULL, NULL, true );
|
|
g_pScriptVM->ReleaseScript( hTable );
|
|
}
|
|
|
|
struct CGameEventDescriptor
|
|
{
|
|
byte _0[36];
|
|
KeyValues *m_pEventKeys;
|
|
//byte _1[22];
|
|
};
|
|
|
|
class CGameEvent__// : public IGameEvent
|
|
{
|
|
public:
|
|
virtual ~CGameEvent__(); // [0]
|
|
CGameEventDescriptor *m_pDescriptor; // 0x04
|
|
//KeyValues *m_pEventData; // 0x08
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptGameEventListener::WriteEventData( IGameEvent *event, HSCRIPT hTable )
|
|
{
|
|
#ifdef USE_OLD_EVENT_DESCRIPTORS
|
|
int i = s_GameEvents.Find( Hash( event->GetName() ) );
|
|
if ( i == s_GameEvents.InvalidIndex() )
|
|
return;
|
|
KeyValues *pKV = s_GameEvents[i];
|
|
#endif
|
|
|
|
#if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS)
|
|
try
|
|
{
|
|
#endif
|
|
|
|
#if !defined(USE_OLD_EVENT_DESCRIPTORS)
|
|
KeyValues *pKV = reinterpret_cast< CGameEvent__* >(event)->m_pDescriptor->m_pEventKeys;
|
|
#endif
|
|
|
|
for ( KeyValues *sub = pKV->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
|
|
{
|
|
const char *szKey = sub->GetName();
|
|
switch ( sub->GetInt() )
|
|
{
|
|
case TYPE_LOCAL:
|
|
case TYPE_STRING: g_pScriptVM->SetValue( hTable, szKey, event->GetString( szKey ) ); break;
|
|
case TYPE_FLOAT: g_pScriptVM->SetValue( hTable, szKey, event->GetFloat ( szKey ) ); break;
|
|
case TYPE_BOOL: g_pScriptVM->SetValue( hTable, szKey, event->GetBool ( szKey ) ); break;
|
|
default: g_pScriptVM->SetValue( hTable, szKey, event->GetInt ( szKey ) );
|
|
}
|
|
}
|
|
|
|
#if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS)
|
|
// Access a bunch of KeyValues functions to validate it is the correct address.
|
|
// This may not always throw an exception when it is incorrect, but eventually it will.
|
|
}
|
|
catch (...)
|
|
{
|
|
// CGameEvent or CGameEventDescriptor offsets did not match!
|
|
// This should mean these were modified in engine.dll.
|
|
//
|
|
// Implement this utility yourself by adding a function to get event descriptor keys
|
|
// either on CGameEventManager or on CGameEvent interfaces.
|
|
// On CGameEventManager downcast IGameEvent input to CGameEvent, then return event->descriptor->keys
|
|
// On CGameEvent return (member) descriptor->keys
|
|
//
|
|
// Finally assign it to pKV above.
|
|
|
|
Warning("CScriptGameEventListener::WriteEventData internal error\n");
|
|
Assert(0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
int CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext )
|
|
{
|
|
bool bValid;
|
|
|
|
if ( gameeventmanager && hFunc )
|
|
#ifdef CLIENT_DLL
|
|
bValid = gameeventmanager->AddListener( this, szEvent, false );
|
|
#else
|
|
bValid = gameeventmanager->AddListener( this, szEvent, true );
|
|
#endif
|
|
else bValid = false;
|
|
|
|
if ( bValid )
|
|
{
|
|
m_iContextHash = HashContext( szContext );
|
|
m_hCallback = hFunc;
|
|
m_bActive = true;
|
|
|
|
s_Listeners.AddToTail( this );
|
|
|
|
return GetIndex();
|
|
}
|
|
else
|
|
{
|
|
delete this;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Free stuff. Called from the destructor, does not remove itself from the listener list.
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptGameEventListener::StopListeningForEvent()
|
|
{
|
|
if ( !m_bActive )
|
|
return;
|
|
|
|
if ( g_pScriptVM )
|
|
g_pScriptVM->ReleaseScript( m_hCallback );
|
|
|
|
m_hCallback = NULL;
|
|
m_bActive = false;
|
|
|
|
if ( gameeventmanager )
|
|
gameeventmanager->RemoveListener( this );
|
|
|
|
#ifdef _DEBUG
|
|
// Event listeners are iterated forwards in the game event manager,
|
|
// removing while iterating will cause it to skip one listener.
|
|
//
|
|
// Fix this in engine without altering any behaviour by
|
|
// changing event exeuction order to tail->head,
|
|
// changing listener removal to tail->head,
|
|
// changing listener addition to head
|
|
if ( m_nEventTick == gpGlobals->tickcount )
|
|
{
|
|
Warning("CScriptGameEventListener stopped in the same frame it was fired. This will break other event listeners!\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stop the specified event listener.
|
|
//-----------------------------------------------------------------------------
|
|
bool CScriptGameEventListener::StopListeningToGameEvent( int listener )
|
|
{
|
|
CScriptGameEventListener *p = reinterpret_cast<CScriptGameEventListener*>(listener); // INT_TO_POINTER
|
|
|
|
bool bRemoved = s_Listeners.FindAndFastRemove(p);
|
|
if ( bRemoved )
|
|
{
|
|
delete p;
|
|
}
|
|
|
|
return bRemoved;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stops listening to all events within a context.
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptGameEventListener::StopListeningToAllGameEvents( const char* szContext )
|
|
{
|
|
unsigned int hash = HashContext( szContext );
|
|
for ( int i = s_Listeners.Count(); i--; )
|
|
{
|
|
CScriptGameEventListener *pCur = s_Listeners[i];
|
|
if ( pCur->m_iContextHash == hash )
|
|
{
|
|
s_Listeners.FastRemove(i);
|
|
delete pCur;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//=============================================================================
|
|
|
|
static int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext )
|
|
{
|
|
CScriptGameEventListener *p = new CScriptGameEventListener();
|
|
return p->ListenToGameEvent( szEvent, hFunc, szContext );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
static void FireGameEvent( const char* szEvent, HSCRIPT hTable )
|
|
{
|
|
IGameEvent *event = gameeventmanager->CreateEvent( szEvent );
|
|
if ( event )
|
|
{
|
|
ScriptVariant_t key, val;
|
|
int nIterator = -1;
|
|
while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 )
|
|
{
|
|
switch ( val.m_type )
|
|
{
|
|
case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break;
|
|
case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break;
|
|
case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break;
|
|
case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break;
|
|
}
|
|
|
|
g_pScriptVM->ReleaseValue(key);
|
|
g_pScriptVM->ReleaseValue(val);
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
gameeventmanager->FireEventClientSide(event);
|
|
#else
|
|
gameeventmanager->FireEvent(event);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifndef CLIENT_DLL
|
|
//-----------------------------------------------------------------------------
|
|
// Copy of FireGameEvent, server only with no broadcast to clients.
|
|
//-----------------------------------------------------------------------------
|
|
static void FireGameEventLocal( const char* szEvent, HSCRIPT hTable )
|
|
{
|
|
IGameEvent *event = gameeventmanager->CreateEvent( szEvent );
|
|
if ( event )
|
|
{
|
|
ScriptVariant_t key, val;
|
|
int nIterator = -1;
|
|
while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 )
|
|
{
|
|
switch ( val.m_type )
|
|
{
|
|
case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break;
|
|
case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break;
|
|
case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break;
|
|
case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break;
|
|
}
|
|
|
|
g_pScriptVM->ReleaseValue(key);
|
|
g_pScriptVM->ReleaseValue(val);
|
|
}
|
|
|
|
gameeventmanager->FireEvent(event,true);
|
|
}
|
|
}
|
|
#endif // !CLIENT_DLL
|
|
|
|
static ScriptHook_t g_Hook_OnSave;
|
|
static ScriptHook_t g_Hook_OnRestore;
|
|
|
|
//=============================================================================
|
|
// Save/Restore Utility
|
|
// Based on L4D2 API
|
|
//=============================================================================
|
|
class CScriptSaveRestoreUtil : public CAutoGameSystem
|
|
{
|
|
public:
|
|
static void SaveTable( const char *szId, HSCRIPT hTable );
|
|
static void RestoreTable( const char *szId, HSCRIPT hTable );
|
|
static void ClearSavedTable( const char *szId );
|
|
|
|
public: // IGameSystem
|
|
|
|
void OnSave()
|
|
{
|
|
if ( g_pScriptVM )
|
|
{
|
|
if ( GetScriptHookManager().IsEventHooked( "OnSave" ) )
|
|
g_Hook_OnSave.Call( NULL, NULL, NULL );
|
|
|
|
// Legacy hook
|
|
HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnSave" );
|
|
if ( hFunc )
|
|
{
|
|
g_pScriptVM->Call( hFunc );
|
|
g_pScriptVM->ReleaseScript( hFunc );
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
// On the client, OnRestore() is called before VScript is actually restored, so this has to be called manually from VScript save/restore instead
|
|
void OnVMRestore()
|
|
#else
|
|
void OnRestore()
|
|
#endif
|
|
{
|
|
if ( g_pScriptVM )
|
|
{
|
|
if ( GetScriptHookManager().IsEventHooked( "OnRestore" ) )
|
|
g_Hook_OnRestore.Call( NULL, NULL, NULL );
|
|
|
|
// Legacy hook
|
|
HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnRestore" );
|
|
if ( hFunc )
|
|
{
|
|
g_pScriptVM->Call( hFunc );
|
|
g_pScriptVM->ReleaseScript( hFunc );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
FOR_EACH_MAP_FAST( m_Lookup, i )
|
|
m_Lookup[i]->deleteThis();
|
|
m_Lookup.Purge();
|
|
}
|
|
|
|
private:
|
|
static StringHashFunctor Hash;
|
|
static CUtlMap< unsigned int, KeyValues* > m_Lookup;
|
|
|
|
} g_ScriptSaveRestoreUtil;
|
|
|
|
#ifdef CLIENT_DLL
|
|
void VScriptSaveRestoreUtil_OnVMRestore()
|
|
{
|
|
g_ScriptSaveRestoreUtil.OnVMRestore();
|
|
}
|
|
#endif
|
|
|
|
CUtlMap< unsigned int, KeyValues* > CScriptSaveRestoreUtil::m_Lookup( DefLessFunc(unsigned int) );
|
|
StringHashFunctor CScriptSaveRestoreUtil::Hash;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Store a table with primitive values that will persist across level transitions and save loads.
|
|
// Case sensitive
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptSaveRestoreUtil::SaveTable( const char *szId, HSCRIPT hTable )
|
|
{
|
|
KeyValues *pKV;
|
|
unsigned int hash = Hash(szId);
|
|
|
|
int idx = m_Lookup.Find( hash );
|
|
if ( idx == m_Lookup.InvalidIndex() )
|
|
{
|
|
pKV = new KeyValues("ScriptSavedTable");
|
|
m_Lookup.Insert( hash, pKV );
|
|
}
|
|
else
|
|
{
|
|
pKV = m_Lookup[idx];
|
|
pKV->Clear();
|
|
}
|
|
|
|
ScriptVariant_t key, val;
|
|
int nIterator = -1;
|
|
while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 )
|
|
{
|
|
switch ( val.m_type )
|
|
{
|
|
case FIELD_FLOAT: pKV->SetFloat ( key.m_pszString, val.m_float ); break;
|
|
case FIELD_INTEGER: pKV->SetInt ( key.m_pszString, val.m_int ); break;
|
|
case FIELD_BOOLEAN: pKV->SetBool ( key.m_pszString, val.m_bool ); break;
|
|
case FIELD_CSTRING: pKV->SetString( key.m_pszString, val.m_pszString ); break;
|
|
}
|
|
|
|
g_pScriptVM->ReleaseValue(key);
|
|
g_pScriptVM->ReleaseValue(val);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Retrieves a table from storage. Write into input table.
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptSaveRestoreUtil::RestoreTable( const char *szId, HSCRIPT hTable )
|
|
{
|
|
int idx = m_Lookup.Find( Hash(szId) );
|
|
if ( idx == m_Lookup.InvalidIndex() )
|
|
{
|
|
// DevWarning( 2, "RestoreTable could not find saved table with context '%s'\n", szId );
|
|
return;
|
|
}
|
|
|
|
KeyValues *pKV = m_Lookup[idx];
|
|
FOR_EACH_SUBKEY( pKV, key )
|
|
{
|
|
switch ( key->GetDataType() )
|
|
{
|
|
case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetString() ); break;
|
|
case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetInt() ); break;
|
|
case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetFloat() ); break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Remove a saved table.
|
|
//-----------------------------------------------------------------------------
|
|
void CScriptSaveRestoreUtil::ClearSavedTable( const char *szId )
|
|
{
|
|
int idx = m_Lookup.Find( Hash(szId) );
|
|
if ( idx != m_Lookup.InvalidIndex() )
|
|
{
|
|
m_Lookup[idx]->deleteThis();
|
|
m_Lookup.RemoveAt( idx );
|
|
}
|
|
else
|
|
{
|
|
// DevWarning( 2, "ClearSavedTable could not find saved table with context '%s'\n", szId );
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Read/Write to File
|
|
// Based on L4D2/Source 2 API
|
|
//=============================================================================
|
|
#define SCRIPT_MAX_FILE_READ_SIZE (64 * 1024 * 1024) // 64MB
|
|
#define SCRIPT_MAX_FILE_WRITE_SIZE (64 * 1024 * 1024) // 64MB
|
|
#define SCRIPT_RW_PATH_ID "MOD"
|
|
#define SCRIPT_RW_FULL_PATH_FMT "vscript_io/%s"
|
|
|
|
class CScriptReadWriteFile : public CAutoGameSystem
|
|
{
|
|
// A singleton class with all static members is used to be able to free the read string on level shutdown,
|
|
// and register script funcs directly. Same reason applies to CScriptSaveRestoreUtil
|
|
public:
|
|
static bool FileWrite( const char *szFile, const char *szInput );
|
|
static const char *FileRead( const char *szFile );
|
|
static bool FileExists( const char *szFile );
|
|
|
|
// NOTE: These two functions are new with Mapbase and have no Valve equivalent
|
|
static bool KeyValuesWrite( const char *szFile, HSCRIPT hInput );
|
|
static HSCRIPT KeyValuesRead( const char *szFile );
|
|
|
|
void LevelShutdownPostEntity()
|
|
{
|
|
if ( m_pszReturnReadFile )
|
|
{
|
|
delete[] m_pszReturnReadFile;
|
|
m_pszReturnReadFile = NULL;
|
|
}
|
|
}
|
|
|
|
private:
|
|
static char *m_pszReturnReadFile;
|
|
|
|
} g_ScriptReadWrite;
|
|
|
|
char *CScriptReadWriteFile::m_pszReturnReadFile = NULL;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool CScriptReadWriteFile::FileWrite( const char *szFile, const char *szInput )
|
|
{
|
|
size_t len = strlen(szInput);
|
|
if ( len > SCRIPT_MAX_FILE_WRITE_SIZE )
|
|
{
|
|
DevWarning( 2, "Input is too large for a ScriptFileWrite ( %s / %d MB )\n", V_pretifymem(len,2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) );
|
|
return false;
|
|
}
|
|
|
|
char pszFullName[MAX_PATH];
|
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
|
|
|
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
|
{
|
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
|
return false;
|
|
}
|
|
|
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
|
|
buf.PutString(szInput);
|
|
|
|
int nSize = V_strlen(pszFullName) + 1;
|
|
char *pszDir = (char*)stackalloc(nSize);
|
|
V_memcpy( pszDir, pszFullName, nSize );
|
|
V_StripFilename( pszDir );
|
|
|
|
g_pFullFileSystem->CreateDirHierarchy( pszDir, SCRIPT_RW_PATH_ID );
|
|
bool res = g_pFullFileSystem->WriteFile( pszFullName, SCRIPT_RW_PATH_ID, buf );
|
|
buf.Purge();
|
|
return res;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
const char *CScriptReadWriteFile::FileRead( const char *szFile )
|
|
{
|
|
char pszFullName[MAX_PATH];
|
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
|
|
|
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
|
{
|
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int size = g_pFullFileSystem->Size( pszFullName, SCRIPT_RW_PATH_ID );
|
|
if ( size >= SCRIPT_MAX_FILE_READ_SIZE )
|
|
{
|
|
DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptFileRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE );
|
|
return NULL;
|
|
}
|
|
|
|
FileHandle_t file = g_pFullFileSystem->Open( pszFullName, "rb", SCRIPT_RW_PATH_ID );
|
|
if ( !file )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Close the previous buffer
|
|
if (m_pszReturnReadFile)
|
|
g_pFullFileSystem->FreeOptimalReadBuffer( m_pszReturnReadFile );
|
|
|
|
unsigned bufSize = g_pFullFileSystem->GetOptimalReadSize( file, size + 2 );
|
|
m_pszReturnReadFile = (char*)g_pFullFileSystem->AllocOptimalReadBuffer( file, bufSize );
|
|
|
|
bool bRetOK = ( g_pFullFileSystem->ReadEx( m_pszReturnReadFile, bufSize, size, file ) != 0 );
|
|
g_pFullFileSystem->Close( file ); // close file after reading
|
|
|
|
if ( bRetOK )
|
|
{
|
|
m_pszReturnReadFile[size] = 0; // null terminate file as EOF
|
|
//buffer[size+1] = 0; // double NULL terminating in case this is a unicode file
|
|
return m_pszReturnReadFile;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool CScriptReadWriteFile::FileExists( const char *szFile )
|
|
{
|
|
char pszFullName[MAX_PATH];
|
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
|
|
|
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
|
{
|
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
|
return NULL;
|
|
}
|
|
|
|
return g_pFullFileSystem->FileExists( pszFullName, SCRIPT_RW_PATH_ID );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Get the checksum of any file. Can be used to check the existence or validity of a file.
|
|
// Returns unsigned int as hex string.
|
|
//-----------------------------------------------------------------------------
|
|
/*
|
|
const char *CScriptReadWriteFile::CRC32_Checksum( const char *szFilename )
|
|
{
|
|
CUtlBuffer buf( 0, 0, CUtlBuffer::READ_ONLY );
|
|
if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, buf ) )
|
|
return NULL;
|
|
|
|
// first time calling, allocate
|
|
if ( !m_pszReturnCRC32 )
|
|
m_pszReturnCRC32 = new char[9]; // 'FFFFFFFF\0'
|
|
|
|
V_snprintf( const_cast<char*>(m_pszReturnCRC32), 9, "%X", CRC32_ProcessSingleBuffer( buf.Base(), buf.Size()-1 ) );
|
|
buf.Purge();
|
|
|
|
return m_pszReturnCRC32;
|
|
}
|
|
*/
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool CScriptReadWriteFile::KeyValuesWrite( const char *szFile, HSCRIPT hInput )
|
|
{
|
|
KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hInput );
|
|
if (!pKV)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
|
|
pKV->RecursiveSaveToFile( buf, 0 );
|
|
|
|
if ( buf.Size() > SCRIPT_MAX_FILE_WRITE_SIZE )
|
|
{
|
|
DevWarning( 2, "Input is too large for a ScriptKeyValuesWrite ( %s / %d MB )\n", V_pretifymem(buf.Size(),2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) );
|
|
buf.Purge();
|
|
return false;
|
|
}
|
|
|
|
char pszFullName[MAX_PATH];
|
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
|
|
|
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
|
{
|
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
|
buf.Purge();
|
|
return false;
|
|
}
|
|
|
|
int nSize = V_strlen(pszFullName) + 1;
|
|
char *pszDir = (char*)stackalloc(nSize);
|
|
V_memcpy( pszDir, pszFullName, nSize );
|
|
V_StripFilename( pszDir );
|
|
|
|
g_pFullFileSystem->CreateDirHierarchy( pszDir, SCRIPT_RW_PATH_ID );
|
|
bool res = g_pFullFileSystem->WriteFile( pszFullName, SCRIPT_RW_PATH_ID, buf );
|
|
buf.Purge();
|
|
return res;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile )
|
|
{
|
|
char pszFullName[MAX_PATH];
|
|
V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile );
|
|
|
|
if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) )
|
|
{
|
|
DevWarning( 2, "Invalid file location : %s\n", szFile );
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int size = g_pFullFileSystem->Size( pszFullName, SCRIPT_RW_PATH_ID );
|
|
if ( size >= SCRIPT_MAX_FILE_READ_SIZE )
|
|
{
|
|
DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptKeyValuesRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE );
|
|
return NULL;
|
|
}
|
|
|
|
KeyValues *pKV = new KeyValues( szFile );
|
|
if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFullName, SCRIPT_RW_PATH_ID ) )
|
|
{
|
|
pKV->deleteThis();
|
|
return NULL;
|
|
}
|
|
|
|
HSCRIPT hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); // bAllowDestruct is supposed to automatically remove the involved KV
|
|
|
|
return hScript;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Network message helper
|
|
// (Unique to mapbase)
|
|
//
|
|
// Uses usermessages for server to client, UserCmd for client to server communication.
|
|
// The custom message name is hashed and sent as word with the message.
|
|
//=============================================================================
|
|
|
|
static CNetMsgScriptHelper scriptnetmsg;
|
|
CNetMsgScriptHelper *g_ScriptNetMsg = &scriptnetmsg;
|
|
|
|
#ifdef GAME_DLL
|
|
#define m_MsgIn_() m_MsgIn->
|
|
#define DLL_LOC_STR "[Server]"
|
|
#else
|
|
#define m_MsgIn_() m_MsgIn.
|
|
#define DLL_LOC_STR "[Client]"
|
|
#endif
|
|
|
|
#ifdef GAME_DLL
|
|
#define SCRIPT_NETMSG_WRITE_FUNC
|
|
#else
|
|
#define SCRIPT_NETMSG_WRITE_FUNC if ( m_bWriteIgnore ) { return; }
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
#ifdef GAME_DLL
|
|
ConVar script_net_debug("script_net_debug", "0");
|
|
#define DebugNetMsg( l, ... ) do { if (script_net_debug.GetInt() >= l) ConColorMsg( Color(100, 225, 255, 255), __VA_ARGS__ ); } while (0);
|
|
#else
|
|
ConVar script_net_debug("script_net_debug_client", "0");
|
|
#define DebugNetMsg( l, ... ) do { if (script_net_debug.GetInt() >= l) ConColorMsg( Color(100, 225, 175, 255), __VA_ARGS__ ); } while (0);
|
|
#endif
|
|
#define DebugWarning(...) Warning( __VA_ARGS__ )
|
|
#else
|
|
#define DebugNetMsg(...) (void)(0)
|
|
#define DebugWarning(...) (void)(0)
|
|
#endif
|
|
|
|
|
|
// Keep track of message names to print on failure
|
|
#ifdef _DEBUG
|
|
struct NetMsgHook_t
|
|
{
|
|
void Set( const char *s )
|
|
{
|
|
hash = CNetMsgScriptHelper::Hash( s );
|
|
name = strdup(s);
|
|
}
|
|
|
|
~NetMsgHook_t()
|
|
{
|
|
free( name );
|
|
}
|
|
|
|
int hash;
|
|
char *name;
|
|
};
|
|
|
|
CUtlVector< NetMsgHook_t > g_NetMsgHooks;
|
|
|
|
static const char *GetNetMsgName( int hash )
|
|
{
|
|
FOR_EACH_VEC( g_NetMsgHooks, i )
|
|
{
|
|
if ( g_NetMsgHooks[i].hash == hash )
|
|
return g_NetMsgHooks[i].name;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *HasNetMsgCollision( int hash, const char *ignore )
|
|
{
|
|
FOR_EACH_VEC( g_NetMsgHooks, i )
|
|
{
|
|
if ( g_NetMsgHooks[i].hash == hash && V_strcmp( g_NetMsgHooks[i].name, ignore ) != 0 )
|
|
{
|
|
return g_NetMsgHooks[i].name;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
|
|
|
|
inline int CNetMsgScriptHelper::Hash( const char *key )
|
|
{
|
|
int hash = HashStringCaseless( key );
|
|
Assert( hash < (1 << SCRIPT_NETMSG_HEADER_BITS) );
|
|
return hash;
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteToBuffer( bf_write *bf )
|
|
{
|
|
#ifdef CLIENT_DLL
|
|
Assert( m_nQueueCount < ( 1 << SCRIPT_NETMSG_QUEUE_BITS ) );
|
|
bf->WriteUBitLong( m_nQueueCount, SCRIPT_NETMSG_QUEUE_BITS );
|
|
|
|
DebugNetMsg( 2, DLL_LOC_STR " CNetMsgScriptHelper::WriteToBuffer() count(%d) size(%d)\n",
|
|
m_nQueueCount, m_MsgOut.GetNumBitsWritten() + SCRIPT_NETMSG_QUEUE_BITS );
|
|
#endif
|
|
|
|
bf->WriteBits( m_MsgOut.GetData(), m_MsgOut.GetNumBitsWritten() );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Reset the current network message buffer
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::Reset()
|
|
{
|
|
m_MsgOut.StartWriting( m_MsgData, sizeof(m_MsgData), 0 );
|
|
#ifdef GAME_DLL
|
|
m_filter.Reset();
|
|
#else
|
|
m_iLastBit = 0;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create the storage for the receiver callback functions.
|
|
// Functions are handled in the VM, the storage table is here.
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::InitPostVM()
|
|
{
|
|
ScriptVariant_t hHooks;
|
|
g_pScriptVM->CreateTable( hHooks );
|
|
m_Hooks = (HSCRIPT)hHooks;
|
|
}
|
|
|
|
void CNetMsgScriptHelper::LevelShutdownPreVM()
|
|
{
|
|
Reset();
|
|
if ( m_Hooks )
|
|
g_pScriptVM->ReleaseScript( m_Hooks );
|
|
m_Hooks = NULL;
|
|
|
|
#ifdef CLIENT_DLL
|
|
m_bWriteReady = m_bWriteIgnore = false;
|
|
m_MsgIn.Reset();
|
|
#else
|
|
m_MsgIn = NULL;
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
g_NetMsgHooks.Purge();
|
|
#endif
|
|
}
|
|
|
|
#ifdef CLIENT_DLL
|
|
|
|
bool CNetMsgScriptHelper::Init() // IGameSystem
|
|
{
|
|
usermessages->HookMessage( "ScriptMsg", __MsgFunc_ScriptMsg );
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::__MsgFunc_ScriptMsg( bf_read &msg )
|
|
{
|
|
g_ScriptNetMsg->ReceiveMessage( msg );
|
|
}
|
|
|
|
#endif // CLIENT_DLL
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
#ifdef GAME_DLL
|
|
void CNetMsgScriptHelper::ReceiveMessage( bf_read *msg, CBaseEntity *pPlayer )
|
|
{
|
|
m_MsgIn = msg;
|
|
#else
|
|
void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg )
|
|
{
|
|
m_MsgIn.StartReading( msg.m_pData, msg.m_nDataBytes );
|
|
#endif
|
|
|
|
DebugNetMsg( 2, DLL_LOC_STR " %s()\n", __FUNCTION__ );
|
|
|
|
// Don't do anything if there's no VM here. This can happen if a message from the server goes to a VM-less client, or vice versa.
|
|
if ( !g_pScriptVM )
|
|
{
|
|
CGWarning( 0, CON_GROUP_VSCRIPT, DLL_LOC_STR " CNetMsgScriptHelper: No VM on receiving side\n" );
|
|
return;
|
|
}
|
|
|
|
#ifdef GAME_DLL
|
|
int count = m_MsgIn_()ReadUBitLong( SCRIPT_NETMSG_QUEUE_BITS );
|
|
DebugNetMsg( 2, " msg count %d\n", count );
|
|
while ( count-- )
|
|
#endif
|
|
{
|
|
int hash = m_MsgIn_()ReadWord();
|
|
|
|
#ifdef _DEBUG
|
|
const char *msgName = GetNetMsgName( hash );
|
|
DebugNetMsg( 2, " -- begin msg [%d]%s\n", hash, msgName );
|
|
#endif
|
|
|
|
ScriptVariant_t hfn;
|
|
if ( g_pScriptVM->GetValue( m_Hooks, hash, &hfn ) )
|
|
{
|
|
#ifdef GAME_DLL
|
|
if ( g_pScriptVM->Call( hfn, NULL, true, NULL, pPlayer->m_hScriptInstance ) == SCRIPT_ERROR )
|
|
#else
|
|
if ( g_pScriptVM->ExecuteFunction( hfn, NULL, 0, NULL, NULL, true ) == SCRIPT_ERROR )
|
|
#endif
|
|
{
|
|
#ifdef _DEBUG
|
|
DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback '%s'\n", GetNetMsgName( hash ) );
|
|
#else
|
|
DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback [%d]\n", hash );
|
|
#endif
|
|
}
|
|
g_pScriptVM->ReleaseValue( hfn );
|
|
}
|
|
else
|
|
{
|
|
DevWarning( 1, DLL_LOC_STR " NetMsg hook not found [%d]\n", hash );
|
|
}
|
|
|
|
DebugNetMsg( 2, " -- end msg\n" );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Start writing new custom network message
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::Start( const char *msg )
|
|
{
|
|
if ( !msg || !msg[0] )
|
|
{
|
|
g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" );
|
|
return;
|
|
}
|
|
|
|
DebugNetMsg( 1, DLL_LOC_STR " %s() [%d]%s\n", __FUNCTION__, Hash( msg ), msg );
|
|
|
|
#ifdef CLIENT_DLL
|
|
// Client can write multiple messages in a frame before the usercmd is sent,
|
|
// this queue system ensures client messages are written to the cmd all at once.
|
|
// NOTE: All messages share the same buffer.
|
|
if ( !m_bWriteReady )
|
|
{
|
|
Reset();
|
|
m_nQueueCount = 0;
|
|
m_bWriteIgnore = false;
|
|
}
|
|
else if ( m_nQueueCount == ((1<<SCRIPT_NETMSG_QUEUE_BITS)-1) )
|
|
{
|
|
Warning( DLL_LOC_STR " NetMsg queue is full, cannot write '%s'!\n", msg );
|
|
|
|
m_bWriteIgnore = true;
|
|
return;
|
|
}
|
|
|
|
++m_nQueueCount;
|
|
#else
|
|
Reset();
|
|
#endif
|
|
|
|
m_MsgOut.WriteWord( Hash( msg ) );
|
|
}
|
|
|
|
#ifdef GAME_DLL
|
|
//-----------------------------------------------------------------------------
|
|
// server -> client
|
|
//
|
|
// Sends an exclusive usermessage.
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::Send( HSCRIPT player, bool bReliable )
|
|
{
|
|
DebugNetMsg( 1, DLL_LOC_STR " %s() size(%d)\n", __FUNCTION__, GetNumBitsWritten() );
|
|
|
|
CBaseEntity *pPlayer = ToEnt(player);
|
|
if ( pPlayer )
|
|
{
|
|
m_filter.AddRecipient( (CBasePlayer*)pPlayer );
|
|
}
|
|
|
|
if ( bReliable )
|
|
{
|
|
m_filter.MakeReliable();
|
|
}
|
|
|
|
Assert( usermessages->LookupUserMessage( "ScriptMsg" ) != -1 );
|
|
|
|
DoSendUserMsg( &m_filter, usermessages->LookupUserMessage( "ScriptMsg" ) );
|
|
}
|
|
#else // CLIENT_DLL
|
|
//-----------------------------------------------------------------------------
|
|
// client -> server
|
|
//
|
|
// Mark UserCmd delta ready.
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::Send()
|
|
{
|
|
DebugNetMsg( 1, DLL_LOC_STR " %s() size(%d)\n", __FUNCTION__, m_bWriteIgnore ? 0 : GetNumBitsWritten() );
|
|
|
|
m_bWriteReady = true;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::Receive( const char *msg, HSCRIPT func )
|
|
{
|
|
if ( !msg || !msg[0] )
|
|
{
|
|
g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" );
|
|
return;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
int hash = Hash( msg );
|
|
|
|
const char *psz = HasNetMsgCollision( hash, msg );
|
|
AssertMsg3( !psz, DLL_LOC_STR " NetMsg hash collision! [%d] '%s', '%s'\n", hash, msg, psz );
|
|
|
|
NetMsgHook_t &hook = g_NetMsgHooks[ g_NetMsgHooks.AddToTail() ];
|
|
hook.Set( msg );
|
|
#endif
|
|
|
|
if ( func )
|
|
{
|
|
g_pScriptVM->SetValue( m_Hooks, Hash( msg ), func );
|
|
}
|
|
else
|
|
{
|
|
g_pScriptVM->ClearValue( m_Hooks, Hash( msg ) );
|
|
}
|
|
}
|
|
|
|
#ifdef GAME_DLL
|
|
void CNetMsgScriptHelper::DoSendUserMsg( CRecipientFilter *filter, int type )
|
|
{
|
|
WriteToBuffer( engine->UserMessageBegin( filter, type ) );
|
|
engine->MessageEnd();
|
|
}
|
|
|
|
void CNetMsgScriptHelper::DoSendEntityMsg( CBaseEntity *entity, bool reliable )
|
|
{
|
|
WriteToBuffer( engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable ) );
|
|
engine->MessageEnd();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Send a usermessage from the server to the client
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::SendUserMessage( HSCRIPT hPlayer, const char *msg, bool bReliable )
|
|
{
|
|
int msg_type = usermessages->LookupUserMessage(msg);
|
|
if ( msg_type == -1 )
|
|
{
|
|
g_pScriptVM->RaiseException( UTIL_VarArgs("SendUserMessage: Unregistered message '%s'", msg) );
|
|
return;
|
|
}
|
|
|
|
CBaseEntity *pPlayer = ToEnt(hPlayer);
|
|
if ( pPlayer )
|
|
{
|
|
m_filter.AddRecipient( (CBasePlayer*)pPlayer );
|
|
}
|
|
|
|
if ( bReliable )
|
|
{
|
|
m_filter.MakeReliable();
|
|
}
|
|
|
|
DoSendUserMsg( &m_filter, msg_type );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Send a message from a server side entity to its client side counterpart
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::SendEntityMessage( HSCRIPT hEnt, bool bReliable )
|
|
{
|
|
CBaseEntity *entity = ToEnt(hEnt);
|
|
if ( !entity )
|
|
{
|
|
g_pScriptVM->RaiseException("SendEntityMessage: invalid entity");
|
|
return;
|
|
}
|
|
|
|
DoSendEntityMsg( entity, bReliable );
|
|
}
|
|
#else
|
|
//-----------------------------------------------------------------------------
|
|
// Dispatch a usermessage on client
|
|
//-----------------------------------------------------------------------------
|
|
void CNetMsgScriptHelper::DispatchUserMessage( const char *msg )
|
|
{
|
|
bf_read buffer( m_MsgOut.GetData(), m_MsgOut.GetNumBytesWritten() );
|
|
usermessages->DispatchUserMessage( usermessages->LookupUserMessage(msg), buffer );
|
|
}
|
|
#endif // GAME_DLL
|
|
|
|
void CNetMsgScriptHelper::WriteInt( int iValue, int bits )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteSBitLong( iValue, bits );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteUInt( int iValue, int bits )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteUBitLong( iValue, bits );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteByte( int iValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteByte( iValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteChar( int iValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteChar( iValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteShort( int iValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteShort( iValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteWord( int iValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteWord( iValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteLong( int iValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteLong( iValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteFloat( float flValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteFloat( flValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteNormal( float flValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteBitNormal( flValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteAngle( float flValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteBitAngle( flValue, 8 );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteCoord( float flValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteBitCoord( flValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteVec3Coord( const Vector& rgflValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteBitVec3Coord( rgflValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteVec3Normal( const Vector& rgflValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteBitVec3Normal( rgflValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteAngles( const QAngle& rgflValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteBitAngles( rgflValue );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteString( const char *sz )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
|
|
// Larger strings can be written but cannot be read
|
|
Assert( V_strlen(sz) < SCRIPT_NETMSG_STRING_SIZE );
|
|
|
|
m_MsgOut.WriteString( sz );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteBool( bool bValue )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
m_MsgOut.WriteOneBit( bValue ? 1 : 0 );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteEntity( HSCRIPT hEnt )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
CBaseEntity *p = ToEnt(hEnt);
|
|
int i = p ? p->entindex() : 0;
|
|
m_MsgOut.WriteUBitLong( i, MAX_EDICT_BITS );
|
|
}
|
|
|
|
void CNetMsgScriptHelper::WriteEHandle( HSCRIPT hEnt )
|
|
{
|
|
SCRIPT_NETMSG_WRITE_FUNC
|
|
CBaseEntity *pEnt = ToEnt( hEnt );
|
|
long iEncodedEHandle;
|
|
if ( pEnt )
|
|
{
|
|
EHANDLE hEnt = pEnt;
|
|
int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1;
|
|
iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS);
|
|
}
|
|
else
|
|
{
|
|
iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE;
|
|
}
|
|
m_MsgOut.WriteLong( iEncodedEHandle );
|
|
}
|
|
|
|
int CNetMsgScriptHelper::ReadInt( int bits )
|
|
{
|
|
return m_MsgIn_()ReadSBitLong(bits);
|
|
}
|
|
|
|
int CNetMsgScriptHelper::ReadUInt( int bits )
|
|
{
|
|
return m_MsgIn_()ReadUBitLong(bits);
|
|
}
|
|
|
|
int CNetMsgScriptHelper::ReadByte()
|
|
{
|
|
return m_MsgIn_()ReadByte();
|
|
}
|
|
|
|
int CNetMsgScriptHelper::ReadChar()
|
|
{
|
|
return m_MsgIn_()ReadChar();
|
|
}
|
|
|
|
int CNetMsgScriptHelper::ReadShort()
|
|
{
|
|
return m_MsgIn_()ReadShort();
|
|
}
|
|
|
|
int CNetMsgScriptHelper::ReadWord()
|
|
{
|
|
return m_MsgIn_()ReadWord();
|
|
}
|
|
|
|
int CNetMsgScriptHelper::ReadLong()
|
|
{
|
|
return m_MsgIn_()ReadLong();
|
|
}
|
|
|
|
float CNetMsgScriptHelper::ReadFloat()
|
|
{
|
|
return m_MsgIn_()ReadFloat();
|
|
}
|
|
|
|
float CNetMsgScriptHelper::ReadNormal()
|
|
{
|
|
return m_MsgIn_()ReadBitNormal();
|
|
}
|
|
|
|
float CNetMsgScriptHelper::ReadAngle()
|
|
{
|
|
return m_MsgIn_()ReadBitAngle( 8 );
|
|
}
|
|
|
|
float CNetMsgScriptHelper::ReadCoord()
|
|
{
|
|
return m_MsgIn_()ReadBitCoord();
|
|
}
|
|
|
|
const Vector& CNetMsgScriptHelper::ReadVec3Coord()
|
|
{
|
|
static Vector vec3;
|
|
m_MsgIn_()ReadBitVec3Coord(vec3);
|
|
return vec3;
|
|
}
|
|
|
|
const Vector& CNetMsgScriptHelper::ReadVec3Normal()
|
|
{
|
|
static Vector vec3;
|
|
m_MsgIn_()ReadBitVec3Normal(vec3);
|
|
return vec3;
|
|
}
|
|
|
|
const QAngle& CNetMsgScriptHelper::ReadAngles()
|
|
{
|
|
static QAngle vec3;
|
|
m_MsgIn_()ReadBitAngles(vec3);
|
|
return vec3;
|
|
}
|
|
|
|
const char* CNetMsgScriptHelper::ReadString()
|
|
{
|
|
static char buf[ SCRIPT_NETMSG_STRING_SIZE ];
|
|
m_MsgIn_()ReadString( buf, sizeof(buf) );
|
|
return buf;
|
|
}
|
|
|
|
bool CNetMsgScriptHelper::ReadBool()
|
|
{
|
|
return m_MsgIn_()ReadOneBit();
|
|
}
|
|
|
|
HSCRIPT CNetMsgScriptHelper::ReadEntity()
|
|
{
|
|
int index = m_MsgIn_()ReadUBitLong( MAX_EDICT_BITS );
|
|
|
|
if ( !index )
|
|
return NULL;
|
|
|
|
#ifdef GAME_DLL
|
|
edict_t *e = INDEXENT(index);
|
|
if ( e && !e->IsFree() )
|
|
{
|
|
return ToHScript( GetContainingEntity(e) );
|
|
}
|
|
#else // CLIENT_DLL
|
|
if ( index < NUM_ENT_ENTRIES )
|
|
{
|
|
return ToHScript( CBaseEntity::Instance(index) );
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
HSCRIPT CNetMsgScriptHelper::ReadEHandle()
|
|
{
|
|
int iEncodedEHandle = m_MsgIn_()ReadLong();
|
|
if ( iEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE )
|
|
return NULL;
|
|
int iEntry = iEncodedEHandle & ( (1 << MAX_EDICT_BITS) - 1 );
|
|
int iSerialNum = iEncodedEHandle >> MAX_EDICT_BITS;
|
|
return ToHScript( EHANDLE( iEntry, iSerialNum ) );
|
|
}
|
|
|
|
inline int CNetMsgScriptHelper::GetNumBitsWritten()
|
|
{
|
|
#ifdef GAME_DLL
|
|
return m_MsgOut.GetNumBitsWritten() - SCRIPT_NETMSG_HEADER_BITS;
|
|
#else
|
|
return m_MsgOut.m_iCurBit - m_iLastBit - SCRIPT_NETMSG_HEADER_BITS;
|
|
#endif
|
|
}
|
|
|
|
|
|
BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "Network messages" )
|
|
|
|
#ifdef GAME_DLL
|
|
DEFINE_SCRIPTFUNC( SendUserMessage, "Send a usermessage from the server to the client" )
|
|
DEFINE_SCRIPTFUNC( SendEntityMessage, "Send a message from a server side entity to its client side counterpart" )
|
|
|
|
// TODO: multiplayer
|
|
#else
|
|
DEFINE_SCRIPTFUNC( DispatchUserMessage, "Dispatch a usermessage on client" )
|
|
#endif
|
|
|
|
DEFINE_SCRIPTFUNC( Reset, "Reset the current network message buffer" )
|
|
DEFINE_SCRIPTFUNC( Start, "Start writing new custom network message" )
|
|
DEFINE_SCRIPTFUNC( Receive, "Set custom network message callback" )
|
|
DEFINE_SCRIPTFUNC_NAMED( Receive, "Recieve", SCRIPT_HIDE ) // This was a typo until v6.3
|
|
#ifdef GAME_DLL
|
|
DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 252 bytes)" )
|
|
#else
|
|
DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2044 bytes)" )
|
|
#endif
|
|
|
|
DEFINE_SCRIPTFUNC( WriteInt, "variable bit signed int" )
|
|
DEFINE_SCRIPTFUNC( WriteUInt, "variable bit unsigned int" )
|
|
DEFINE_SCRIPTFUNC( WriteByte, "8 bit unsigned char" )
|
|
DEFINE_SCRIPTFUNC( WriteChar, "8 bit char" )
|
|
DEFINE_SCRIPTFUNC( WriteShort, "16 bit short" )
|
|
DEFINE_SCRIPTFUNC( WriteWord, "16 bit unsigned short" )
|
|
DEFINE_SCRIPTFUNC( WriteLong, "32 bit long" )
|
|
DEFINE_SCRIPTFUNC( WriteFloat, "32 bit float" )
|
|
DEFINE_SCRIPTFUNC( WriteNormal, "12 bit" )
|
|
DEFINE_SCRIPTFUNC( WriteAngle, "8 bit unsigned char" )
|
|
DEFINE_SCRIPTFUNC( WriteCoord, "" )
|
|
DEFINE_SCRIPTFUNC( WriteVec3Coord, "" )
|
|
DEFINE_SCRIPTFUNC( WriteVec3Normal, "27 bit" )
|
|
DEFINE_SCRIPTFUNC( WriteAngles, "" )
|
|
DEFINE_SCRIPTFUNC( WriteString, "max 512 bytes at once" )
|
|
DEFINE_SCRIPTFUNC( WriteBool, "1 bit" )
|
|
DEFINE_SCRIPTFUNC( WriteEntity, "11 bit (entindex)" )
|
|
DEFINE_SCRIPTFUNC( WriteEHandle, "32 bit long" )
|
|
|
|
DEFINE_SCRIPTFUNC( ReadInt, "" )
|
|
DEFINE_SCRIPTFUNC( ReadUInt, "" )
|
|
DEFINE_SCRIPTFUNC( ReadByte, "" )
|
|
DEFINE_SCRIPTFUNC( ReadChar, "" )
|
|
DEFINE_SCRIPTFUNC( ReadShort, "" )
|
|
DEFINE_SCRIPTFUNC( ReadWord, "" )
|
|
DEFINE_SCRIPTFUNC( ReadLong, "" )
|
|
DEFINE_SCRIPTFUNC( ReadFloat, "" )
|
|
DEFINE_SCRIPTFUNC( ReadNormal, "" )
|
|
DEFINE_SCRIPTFUNC( ReadAngle, "" )
|
|
DEFINE_SCRIPTFUNC( ReadCoord, "" )
|
|
DEFINE_SCRIPTFUNC( ReadVec3Coord, "" )
|
|
DEFINE_SCRIPTFUNC( ReadVec3Normal, "" )
|
|
DEFINE_SCRIPTFUNC( ReadAngles, "" )
|
|
DEFINE_SCRIPTFUNC( ReadString, "" )
|
|
DEFINE_SCRIPTFUNC( ReadBool, "" )
|
|
DEFINE_SCRIPTFUNC( ReadEntity, "" )
|
|
DEFINE_SCRIPTFUNC( ReadEHandle, "" )
|
|
|
|
DEFINE_SCRIPTFUNC( GetNumBitsWritten, "" )
|
|
|
|
END_SCRIPTDESC();
|
|
|
|
|
|
|
|
#define RETURN_IF_CANNOT_DRAW_OVERLAY\
|
|
if (engine->IsPaused())\
|
|
return;
|
|
class CDebugOverlayScriptHelper
|
|
{
|
|
public:
|
|
|
|
void Box( const Vector &origin, const Vector &mins, const Vector &maxs, int r, int g, int b, int a, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddBoxOverlay(origin, mins, maxs, vec3_angle, r, g, b, a, flDuration);
|
|
}
|
|
void BoxDirection( const Vector &origin, const Vector &mins, const Vector &maxs, const Vector &forward, int r, int g, int b, int a, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
QAngle f_angles = vec3_angle;
|
|
f_angles.y = UTIL_VecToYaw(forward);
|
|
|
|
debugoverlay->AddBoxOverlay(origin, mins, maxs, f_angles, r, g, b, a, flDuration);
|
|
}
|
|
void BoxAngles( const Vector &origin, const Vector &mins, const Vector &maxs, const QAngle &angles, int r, int g, int b, int a, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddBoxOverlay(origin, mins, maxs, angles, r, g, b, a, flDuration);
|
|
}
|
|
void SweptBox( const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, const QAngle & angles, int r, int g, int b, int a, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddSweptBoxOverlay(start, end, mins, maxs, angles, r, g, b, a, flDuration);
|
|
}
|
|
void EntityBounds( HSCRIPT pEntity, int r, int g, int b, int a, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
CBaseEntity *pEnt = ToEnt(pEntity);
|
|
if (!pEnt)
|
|
return;
|
|
|
|
const CCollisionProperty *pCollide = pEnt->CollisionProp();
|
|
debugoverlay->AddBoxOverlay(pCollide->GetCollisionOrigin(), pCollide->OBBMins(), pCollide->OBBMaxs(), pCollide->GetCollisionAngles(), r, g, b, a, flDuration);
|
|
}
|
|
void Line( const Vector &origin, const Vector &target, int r, int g, int b, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddLineOverlay(origin, target, r, g, b, noDepthTest, flDuration);
|
|
}
|
|
void Triangle( const Vector &p1, const Vector &p2, const Vector &p3, int r, int g, int b, int a, bool noDepthTest, float duration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddTriangleOverlay(p1, p2, p3, r, g, b, a, noDepthTest, duration);
|
|
}
|
|
void EntityText( int entityID, int text_offset, const char *text, float flDuration, int r, int g, int b, int a )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddEntityTextOverlay(entityID, text_offset, flDuration,
|
|
(int)clamp(r * 255.f, 0.f, 255.f), (int)clamp(g * 255.f, 0.f, 255.f), (int)clamp(b * 255.f, 0.f, 255.f),
|
|
(int)clamp(a * 255.f, 0.f, 255.f), text);
|
|
}
|
|
void EntityTextAtPosition( const Vector &origin, int text_offset, const char *text, float flDuration, int r, int g, int b, int a )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddTextOverlayRGB(origin, text_offset, flDuration, r, g, b, a, "%s", text);
|
|
}
|
|
void Grid( const Vector &vPosition )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddGridOverlay(vPosition);
|
|
}
|
|
void Text( const Vector &origin, const char *text, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddTextOverlay(origin, flDuration, "%s", text);
|
|
}
|
|
void ScreenText( float fXpos, float fYpos, const char *text, int r, int g, int b, int a, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
debugoverlay->AddScreenTextOverlay(fXpos, fYpos, flDuration, r, g, b, a, text);
|
|
}
|
|
void Cross3D( const Vector &position, float size, int r, int g, int b, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Line( position + Vector(size,0,0), position - Vector(size,0,0), r, g, b, noDepthTest, flDuration );
|
|
Line( position + Vector(0,size,0), position - Vector(0,size,0), r, g, b, noDepthTest, flDuration );
|
|
Line( position + Vector(0,0,size), position - Vector(0,0,size), r, g, b, noDepthTest, flDuration );
|
|
}
|
|
void Cross3DOriented( const Vector &position, const QAngle &angles, float size, int r, int g, int b, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Vector forward, right, up;
|
|
AngleVectors( angles, &forward, &right, &up );
|
|
|
|
forward *= size;
|
|
right *= size;
|
|
up *= size;
|
|
|
|
Line( position + right, position - right, r, g, b, noDepthTest, flDuration );
|
|
Line( position + forward, position - forward, r, g, b, noDepthTest, flDuration );
|
|
Line( position + up, position - up, r, g, b, noDepthTest, flDuration );
|
|
}
|
|
void DrawTickMarkedLine( const Vector &startPos, const Vector &endPos, float tickDist, int tickTextDist, int r, int g, int b, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Vector lineDir = (endPos - startPos);
|
|
float lineDist = VectorNormalize(lineDir);
|
|
int numTicks = lineDist / tickDist;
|
|
|
|
Vector upVec = Vector(0,0,4);
|
|
Vector sideDir;
|
|
Vector tickPos = startPos;
|
|
int tickTextCnt = 0;
|
|
|
|
CrossProduct(lineDir, upVec, sideDir);
|
|
|
|
Line(startPos, endPos, r, g, b, noDepthTest, flDuration);
|
|
|
|
for (int i = 0; i<numTicks + 1; i++)
|
|
{
|
|
Vector tickLeft = tickPos - sideDir;
|
|
Vector tickRight = tickPos + sideDir;
|
|
|
|
if (tickTextCnt == tickTextDist)
|
|
{
|
|
char text[25];
|
|
Q_snprintf(text, sizeof(text), "%i", i);
|
|
Vector textPos = tickLeft + Vector(0, 0, 8);
|
|
Line(tickLeft, tickRight, 255, 255, 255, noDepthTest, flDuration);
|
|
Text(textPos, text, flDuration);
|
|
tickTextCnt = 0;
|
|
}
|
|
else
|
|
{
|
|
Line(tickLeft, tickRight, r, g, b, noDepthTest, flDuration);
|
|
}
|
|
|
|
tickTextCnt++;
|
|
|
|
tickPos = tickPos + (tickDist * lineDir);
|
|
}
|
|
}
|
|
void HorzArrow( const Vector &startPos, const Vector &endPos, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Vector lineDir = (endPos - startPos);
|
|
VectorNormalize( lineDir );
|
|
Vector upVec = Vector( 0, 0, 1 );
|
|
Vector sideDir;
|
|
float radius = width / 2.0;
|
|
|
|
CrossProduct(lineDir, upVec, sideDir);
|
|
|
|
Vector p1 = startPos - sideDir * radius;
|
|
Vector p2 = endPos - lineDir * width - sideDir * radius;
|
|
Vector p3 = endPos - lineDir * width - sideDir * width;
|
|
Vector p4 = endPos;
|
|
Vector p5 = endPos - lineDir * width + sideDir * width;
|
|
Vector p6 = endPos - lineDir * width + sideDir * radius;
|
|
Vector p7 = startPos + sideDir * radius;
|
|
|
|
Line(p1, p2, r,g,b,noDepthTest,flDuration);
|
|
Line(p2, p3, r,g,b,noDepthTest,flDuration);
|
|
Line(p3, p4, r,g,b,noDepthTest,flDuration);
|
|
Line(p4, p5, r,g,b,noDepthTest,flDuration);
|
|
Line(p5, p6, r,g,b,noDepthTest,flDuration);
|
|
Line(p6, p7, r,g,b,noDepthTest,flDuration);
|
|
|
|
if ( a > 0 )
|
|
{
|
|
Triangle( p5, p4, p3, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p1, p7, p6, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p6, p2, p1, r, g, b, a, noDepthTest, flDuration );
|
|
|
|
Triangle( p3, p4, p5, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p6, p7, p1, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p1, p2, p6, r, g, b, a, noDepthTest, flDuration );
|
|
}
|
|
}
|
|
void YawArrow( const Vector &startPos, float yaw, float length, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Vector forward = UTIL_YawToVector( yaw );
|
|
HorzArrow( startPos, startPos + forward * length, width, r, g, b, a, noDepthTest, flDuration );
|
|
}
|
|
void VertArrow( const Vector &startPos, const Vector &endPos, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Vector lineDir = (endPos - startPos);
|
|
VectorNormalize( lineDir );
|
|
Vector upVec;
|
|
Vector sideDir;
|
|
float radius = width / 2.0;
|
|
|
|
VectorVectors( lineDir, sideDir, upVec );
|
|
|
|
Vector p1 = startPos - upVec * radius;
|
|
Vector p2 = endPos - lineDir * width - upVec * radius;
|
|
Vector p3 = endPos - lineDir * width - upVec * width;
|
|
Vector p4 = endPos;
|
|
Vector p5 = endPos - lineDir * width + upVec * width;
|
|
Vector p6 = endPos - lineDir * width + upVec * radius;
|
|
Vector p7 = startPos + upVec * radius;
|
|
|
|
Line(p1, p2, r,g,b,noDepthTest,flDuration);
|
|
Line(p2, p3, r,g,b,noDepthTest,flDuration);
|
|
Line(p3, p4, r,g,b,noDepthTest,flDuration);
|
|
Line(p4, p5, r,g,b,noDepthTest,flDuration);
|
|
Line(p5, p6, r,g,b,noDepthTest,flDuration);
|
|
Line(p6, p7, r,g,b,noDepthTest,flDuration);
|
|
|
|
if ( a > 0 )
|
|
{
|
|
Triangle( p5, p4, p3, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p1, p7, p6, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p6, p2, p1, r, g, b, a, noDepthTest, flDuration );
|
|
|
|
Triangle( p3, p4, p5, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p6, p7, p1, r, g, b, a, noDepthTest, flDuration );
|
|
Triangle( p1, p2, p6, r, g, b, a, noDepthTest, flDuration );
|
|
}
|
|
}
|
|
void Axis( const Vector &position, const QAngle &angles, float size, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Vector xvec, yvec, zvec;
|
|
AngleVectors( angles, &xvec, &yvec, &zvec );
|
|
|
|
xvec = position + (size * xvec);
|
|
yvec = position - (size * yvec);
|
|
zvec = position + (size * zvec);
|
|
|
|
Line( position, xvec, 255, 0, 0, noDepthTest, flDuration );
|
|
Line( position, yvec, 0, 255, 0, noDepthTest, flDuration );
|
|
Line( position, zvec, 0, 0, 255, noDepthTest, flDuration );
|
|
}
|
|
void Sphere( const Vector ¢er, float radius, int r, int g, int b, bool noDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
Vector edge, lastEdge;
|
|
|
|
float axisSize = radius;
|
|
Line( center + Vector( 0, 0, -axisSize ), center + Vector( 0, 0, axisSize ), r, g, b, noDepthTest, flDuration );
|
|
Line( center + Vector( 0, -axisSize, 0 ), center + Vector( 0, axisSize, 0 ), r, g, b, noDepthTest, flDuration );
|
|
Line( center + Vector( -axisSize, 0, 0 ), center + Vector( axisSize, 0, 0 ), r, g, b, noDepthTest, flDuration );
|
|
|
|
lastEdge = Vector( radius + center.x, center.y, center.z );
|
|
float angle;
|
|
for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
|
|
{
|
|
edge.x = radius * cosf( angle / 180.0f * M_PI ) + center.x;
|
|
edge.y = center.y;
|
|
edge.z = radius * sinf( angle / 180.0f * M_PI ) + center.z;
|
|
|
|
Line( edge, lastEdge, r, g, b, noDepthTest, flDuration );
|
|
|
|
lastEdge = edge;
|
|
}
|
|
|
|
lastEdge = Vector( center.x, radius + center.y, center.z );
|
|
for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
|
|
{
|
|
edge.x = center.x;
|
|
edge.y = radius * cosf( angle / 180.0f * M_PI ) + center.y;
|
|
edge.z = radius * sinf( angle / 180.0f * M_PI ) + center.z;
|
|
|
|
Line( edge, lastEdge, r, g, b, noDepthTest, flDuration );
|
|
|
|
lastEdge = edge;
|
|
}
|
|
|
|
lastEdge = Vector( center.x, radius + center.y, center.z );
|
|
for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
|
|
{
|
|
edge.x = radius * cosf( angle / 180.0f * M_PI ) + center.x;
|
|
edge.y = radius * sinf( angle / 180.0f * M_PI ) + center.y;
|
|
edge.z = center.z;
|
|
|
|
Line( edge, lastEdge, r, g, b, noDepthTest, flDuration );
|
|
|
|
lastEdge = edge;
|
|
}
|
|
}
|
|
void CircleOriented( const Vector &position, const QAngle &angles, float radius, int r, int g, int b, int a, bool bNoDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
matrix3x4_t xform;
|
|
AngleMatrix(angles, position, xform);
|
|
Vector xAxis, yAxis;
|
|
MatrixGetColumn(xform, 2, xAxis);
|
|
MatrixGetColumn(xform, 1, yAxis);
|
|
Circle(position, xAxis, yAxis, radius, r, g, b, a, bNoDepthTest, flDuration);
|
|
}
|
|
void Circle( const Vector &position, const Vector &xAxis, const Vector &yAxis, float radius, int r, int g, int b, int a, bool bNoDepthTest, float flDuration )
|
|
{
|
|
RETURN_IF_CANNOT_DRAW_OVERLAY
|
|
|
|
const unsigned int nSegments = 16;
|
|
const float flRadStep = (M_PI*2.0f) / (float) nSegments;
|
|
|
|
Vector vecLastPosition;
|
|
Vector vecStart = position + xAxis * radius;
|
|
Vector vecPosition = vecStart;
|
|
|
|
for ( int i = 1; i <= nSegments; i++ )
|
|
{
|
|
vecLastPosition = vecPosition;
|
|
|
|
float flSin, flCos;
|
|
SinCos( flRadStep*i, &flSin, &flCos );
|
|
vecPosition = position + (xAxis * flCos * radius) + (yAxis * flSin * radius);
|
|
|
|
Line( vecLastPosition, vecPosition, r, g, b, bNoDepthTest, flDuration );
|
|
|
|
if ( a && i > 1 )
|
|
{
|
|
debugoverlay->AddTriangleOverlay( vecStart, vecLastPosition, vecPosition, r, g, b, a, bNoDepthTest, flDuration );
|
|
}
|
|
}
|
|
}
|
|
#ifndef CLIENT_DLL
|
|
void SetDebugBits( HSCRIPT hEntity, int bit ) // DebugOverlayBits_t
|
|
{
|
|
CBaseEntity *pEnt = ToEnt(hEntity);
|
|
if (!pEnt)
|
|
return;
|
|
|
|
if (pEnt->m_debugOverlays & bit)
|
|
{
|
|
pEnt->m_debugOverlays &= ~bit;
|
|
}
|
|
else
|
|
{
|
|
pEnt->m_debugOverlays |= bit;
|
|
|
|
#ifdef AI_MONITOR_FOR_OSCILLATION
|
|
if (pEnt->IsNPC())
|
|
{
|
|
pEnt->MyNPCPointer()->m_ScheduleHistory.RemoveAll();
|
|
}
|
|
#endif//AI_MONITOR_FOR_OSCILLATION
|
|
}
|
|
}
|
|
#endif
|
|
void ClearAllOverlays()
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
// Clear all entities of their debug overlays
|
|
for (CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity; pEntity = gEntList.NextEnt(pEntity))
|
|
{
|
|
pEntity->m_debugOverlays = 0;
|
|
}
|
|
#endif
|
|
|
|
debugoverlay->ClearAllOverlays();
|
|
}
|
|
|
|
private:
|
|
} g_ScriptDebugOverlay;
|
|
|
|
BEGIN_SCRIPTDESC_ROOT( CDebugOverlayScriptHelper, SCRIPT_SINGLETON "CDebugOverlayScriptHelper" )
|
|
DEFINE_SCRIPTFUNC( Box, "Draws a world-space axis-aligned box. Specify bounds in world space." )
|
|
DEFINE_SCRIPTFUNC( BoxDirection, "Draw box oriented to a Vector direction" )
|
|
DEFINE_SCRIPTFUNC( BoxAngles, "Draws an oriented box at the origin. Specify bounds in local space." )
|
|
DEFINE_SCRIPTFUNC( SweptBox, "Draws a swept box. Specify endpoints in world space and the bounds in local space." )
|
|
DEFINE_SCRIPTFUNC( EntityBounds, "Draws bounds of an entity" )
|
|
DEFINE_SCRIPTFUNC( Line, "Draws a line between two points" )
|
|
DEFINE_SCRIPTFUNC( Triangle, "Draws a filled triangle. Specify vertices in world space." )
|
|
DEFINE_SCRIPTFUNC( EntityText, "Draws text on an entity" )
|
|
DEFINE_SCRIPTFUNC( EntityTextAtPosition, "Draw entity text overlay at a specific position" )
|
|
DEFINE_SCRIPTFUNC( Grid, "Add grid overlay" )
|
|
DEFINE_SCRIPTFUNC( Text, "Draws 2D text. Specify origin in world space." )
|
|
DEFINE_SCRIPTFUNC( ScreenText, "Draws 2D text. Specify coordinates in screen space." )
|
|
DEFINE_SCRIPTFUNC( Cross3D, "Draws a world-aligned cross. Specify origin in world space." )
|
|
DEFINE_SCRIPTFUNC( Cross3DOriented, "Draws an oriented cross. Specify origin in world space." )
|
|
DEFINE_SCRIPTFUNC( DrawTickMarkedLine, "Draws a dashed line. Specify endpoints in world space." )
|
|
DEFINE_SCRIPTFUNC( HorzArrow, "Draws a horizontal arrow. Specify endpoints in world space." )
|
|
DEFINE_SCRIPTFUNC( YawArrow, "Draws a arrow associated with a specific yaw. Specify endpoints in world space." )
|
|
DEFINE_SCRIPTFUNC( VertArrow, "Draws a vertical arrow. Specify endpoints in world space." )
|
|
DEFINE_SCRIPTFUNC( Axis, "Draws an axis. Specify origin + orientation in world space." )
|
|
DEFINE_SCRIPTFUNC( Sphere, "Draws a wireframe sphere. Specify center in world space." )
|
|
DEFINE_SCRIPTFUNC( CircleOriented, "Draws a circle oriented. Specify center in world space." )
|
|
DEFINE_SCRIPTFUNC( Circle, "Draws a circle. Specify center in world space." )
|
|
#ifndef CLIENT_DLL
|
|
DEFINE_SCRIPTFUNC( SetDebugBits, "Set debug bits on entity" )
|
|
#endif
|
|
DEFINE_SCRIPTFUNC( ClearAllOverlays, "Clear all debug overlays at once" )
|
|
END_SCRIPTDESC();
|
|
|
|
|
|
|
|
//=============================================================================
|
|
// ConVars
|
|
//=============================================================================
|
|
class CScriptConCommand : public ConCommand, public ICommandCallback, public ICommandCompletionCallback
|
|
{
|
|
typedef ConCommand BaseClass;
|
|
|
|
public:
|
|
~CScriptConCommand()
|
|
{
|
|
Unregister();
|
|
}
|
|
|
|
CScriptConCommand( const char *name, HSCRIPT fn, const char *helpString, int flags, ConCommand *pLinked = NULL )
|
|
: BaseClass( name, this, helpString, flags, 0 ),
|
|
m_pLinked(pLinked),
|
|
m_hCallback(fn),
|
|
m_hCompletionCallback(NULL)
|
|
{
|
|
m_nCmdNameLen = V_strlen(name) + 1;
|
|
Assert( m_nCmdNameLen - 1 <= 128 );
|
|
}
|
|
|
|
void CommandCallback( const CCommand &command )
|
|
{
|
|
int count = command.ArgC();
|
|
ScriptVariant_t *vArgv = (ScriptVariant_t*)stackalloc( sizeof(ScriptVariant_t) * count );
|
|
for ( int i = 0; i < count; ++i )
|
|
{
|
|
vArgv[i] = command[i];
|
|
}
|
|
ScriptVariant_t ret;
|
|
if ( g_pScriptVM->ExecuteFunction( m_hCallback, vArgv, count, &ret, NULL, true ) == SCRIPT_ERROR )
|
|
{
|
|
DevWarning( 1, "CScriptConCommand: invalid callback for '%s'\n", command[0] );
|
|
}
|
|
if ( m_pLinked && (ret.m_type == FIELD_BOOLEAN) && ret.m_bool )
|
|
{
|
|
m_pLinked->Dispatch( command );
|
|
}
|
|
}
|
|
|
|
int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands )
|
|
{
|
|
Assert( g_pScriptVM );
|
|
Assert( m_hCompletionCallback );
|
|
|
|
ScriptVariant_t hArray;
|
|
g_pScriptVM->CreateArray( hArray );
|
|
|
|
// split command name from partial, pass both separately to the script function
|
|
char *cmdname = (char*)stackalloc( m_nCmdNameLen );
|
|
V_memcpy( cmdname, partial, m_nCmdNameLen - 1 );
|
|
cmdname[ m_nCmdNameLen - 1 ] = 0;
|
|
|
|
char argPartial[256];
|
|
V_StrRight( partial, V_strlen(partial) - m_nCmdNameLen, argPartial, sizeof(argPartial) );
|
|
|
|
ScriptVariant_t args[3] = { cmdname, argPartial, hArray };
|
|
if ( g_pScriptVM->ExecuteFunction( m_hCompletionCallback, args, 3, NULL, NULL, true ) == SCRIPT_ERROR )
|
|
{
|
|
DevWarning( 1, "CScriptConCommand: invalid command completion callback for '%s'\n", cmdname );
|
|
g_pScriptVM->ReleaseScript( hArray );
|
|
return 0;
|
|
}
|
|
|
|
int count = 0;
|
|
ScriptVariant_t val;
|
|
int it = -1;
|
|
while ( ( it = g_pScriptVM->GetKeyValue( hArray, it, NULL, &val ) ) != -1 )
|
|
{
|
|
if ( val.m_type == FIELD_CSTRING )
|
|
{
|
|
CUtlString &s = commands.Element( commands.AddToTail() );
|
|
int len = V_strlen( val.m_pszString );
|
|
|
|
if ( len <= COMMAND_COMPLETION_ITEM_LENGTH - 1 )
|
|
{
|
|
s.Set( val.m_pszString );
|
|
}
|
|
else
|
|
{
|
|
s.SetDirect( val.m_pszString, COMMAND_COMPLETION_ITEM_LENGTH - 1 );
|
|
}
|
|
|
|
++count;
|
|
}
|
|
g_pScriptVM->ReleaseValue(val);
|
|
|
|
if ( count == COMMAND_COMPLETION_MAXITEMS )
|
|
break;
|
|
}
|
|
g_pScriptVM->ReleaseScript( hArray );
|
|
return count;
|
|
}
|
|
|
|
void SetCompletionCallback( HSCRIPT fn )
|
|
{
|
|
if ( m_hCompletionCallback )
|
|
g_pScriptVM->ReleaseScript( m_hCompletionCallback );
|
|
|
|
if (fn)
|
|
{
|
|
if ( !BaseClass::IsRegistered() )
|
|
return;
|
|
|
|
BaseClass::m_pCommandCompletionCallback = this;
|
|
BaseClass::m_bHasCompletionCallback = true;
|
|
m_hCompletionCallback = fn;
|
|
}
|
|
else
|
|
{
|
|
BaseClass::m_pCommandCompletionCallback = NULL;
|
|
BaseClass::m_bHasCompletionCallback = false;
|
|
m_hCompletionCallback = NULL;
|
|
}
|
|
}
|
|
|
|
void SetCallback( HSCRIPT fn )
|
|
{
|
|
if (fn)
|
|
{
|
|
if ( !BaseClass::IsRegistered() )
|
|
Register();
|
|
|
|
if ( m_hCallback )
|
|
g_pScriptVM->ReleaseScript( m_hCallback );
|
|
m_hCallback = fn;
|
|
}
|
|
else
|
|
{
|
|
Unregister();
|
|
}
|
|
}
|
|
|
|
inline void Unregister()
|
|
{
|
|
if ( g_pCVar && BaseClass::IsRegistered() )
|
|
g_pCVar->UnregisterConCommand( this );
|
|
|
|
if ( g_pScriptVM )
|
|
{
|
|
if ( m_hCallback )
|
|
{
|
|
g_pScriptVM->ReleaseScript( m_hCallback );
|
|
m_hCallback = NULL;
|
|
}
|
|
|
|
SetCompletionCallback( NULL );
|
|
}
|
|
}
|
|
|
|
inline void Register()
|
|
{
|
|
if ( g_pCVar )
|
|
g_pCVar->RegisterConCommand( this );
|
|
}
|
|
|
|
HSCRIPT m_hCallback;
|
|
ConCommand *m_pLinked;
|
|
HSCRIPT m_hCompletionCallback;
|
|
int m_nCmdNameLen;
|
|
};
|
|
|
|
class CScriptConVar : public ConVar
|
|
{
|
|
typedef ConVar BaseClass;
|
|
|
|
public:
|
|
~CScriptConVar()
|
|
{
|
|
Unregister();
|
|
}
|
|
|
|
CScriptConVar( const char *pName, const char *pDefaultValue, const char *pHelpString, int flags/*, float fMin, float fMax*/ )
|
|
: BaseClass( pName, pDefaultValue, flags, pHelpString ),
|
|
m_hCallback(NULL)
|
|
{}
|
|
|
|
void SetChangeCallback( HSCRIPT fn )
|
|
{
|
|
void ScriptConVarCallback( IConVar*, const char*, float );
|
|
|
|
if ( m_hCallback )
|
|
g_pScriptVM->ReleaseScript( m_hCallback );
|
|
|
|
if (fn)
|
|
{
|
|
m_hCallback = fn;
|
|
BaseClass::InstallChangeCallback( (FnChangeCallback_t)ScriptConVarCallback );
|
|
}
|
|
else
|
|
{
|
|
m_hCallback = NULL;
|
|
BaseClass::InstallChangeCallback( NULL );
|
|
}
|
|
}
|
|
|
|
inline void Unregister()
|
|
{
|
|
if ( g_pCVar && BaseClass::IsRegistered() )
|
|
g_pCVar->UnregisterConCommand( this );
|
|
|
|
if ( g_pScriptVM )
|
|
{
|
|
SetChangeCallback( NULL );
|
|
}
|
|
}
|
|
|
|
HSCRIPT m_hCallback;
|
|
};
|
|
|
|
static CUtlMap< unsigned int, bool > g_ConVarsBlocked( DefLessFunc(unsigned int) );
|
|
static CUtlMap< unsigned int, bool > g_ConCommandsOverridable( DefLessFunc(unsigned int) );
|
|
static CUtlMap< unsigned int, CScriptConCommand* > g_ScriptConCommands( DefLessFunc(unsigned int) );
|
|
static CUtlMap< unsigned int, CScriptConVar* > g_ScriptConVars( DefLessFunc(unsigned int) );
|
|
|
|
|
|
class CScriptConvarAccessor : public CAutoGameSystem
|
|
{
|
|
public:
|
|
static inline unsigned int Hash( const char*sz ){ return HashStringCaseless(sz); }
|
|
|
|
public:
|
|
inline void AddOverridable( const char *name )
|
|
{
|
|
g_ConCommandsOverridable.InsertOrReplace( Hash(name), true );
|
|
}
|
|
|
|
inline bool IsOverridable( unsigned int hash )
|
|
{
|
|
int idx = g_ConCommandsOverridable.Find( hash );
|
|
return ( idx != g_ConCommandsOverridable.InvalidIndex() );
|
|
}
|
|
|
|
inline void AddBlockedConVar( const char *name )
|
|
{
|
|
g_ConVarsBlocked.InsertOrReplace( Hash(name), true );
|
|
}
|
|
|
|
inline bool IsBlockedConvar( const char *name )
|
|
{
|
|
int idx = g_ConVarsBlocked.Find( Hash(name) );
|
|
return ( idx != g_ConVarsBlocked.InvalidIndex() );
|
|
}
|
|
|
|
public:
|
|
void RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags );
|
|
void SetCompletionCallback( const char *name, HSCRIPT fn );
|
|
void UnregisterCommand( const char *name );
|
|
void RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags );
|
|
void SetChangeCallback( const char *name, HSCRIPT fn );
|
|
|
|
HSCRIPT GetCommandClient()
|
|
{
|
|
#ifdef GAME_DLL
|
|
return ToHScript( UTIL_GetCommandClient() );
|
|
#else
|
|
return ToHScript( C_BasePlayer::GetLocalPlayer() );
|
|
#endif
|
|
}
|
|
#ifdef GAME_DLL
|
|
const char *GetClientConvarValue( int index, const char* cvar )
|
|
{
|
|
return engine->GetClientConVarValue( index, cvar );
|
|
}
|
|
#endif
|
|
public:
|
|
bool Init();
|
|
|
|
void LevelShutdownPostEntity()
|
|
{
|
|
g_ScriptConCommands.PurgeAndDeleteElements();
|
|
g_ScriptConVars.PurgeAndDeleteElements();
|
|
}
|
|
|
|
public:
|
|
float GetFloat( const char *pszConVar )
|
|
{
|
|
ConVarRef cvar( pszConVar );
|
|
if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) )
|
|
return NULL;
|
|
return cvar.GetFloat();
|
|
}
|
|
|
|
int GetInt( const char *pszConVar )
|
|
{
|
|
ConVarRef cvar( pszConVar );
|
|
if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) )
|
|
return NULL;
|
|
return cvar.GetInt();
|
|
}
|
|
|
|
bool GetBool( const char *pszConVar )
|
|
{
|
|
ConVarRef cvar( pszConVar );
|
|
if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) )
|
|
return NULL;
|
|
return cvar.GetBool();
|
|
}
|
|
|
|
const char *GetStr( const char *pszConVar )
|
|
{
|
|
ConVarRef cvar( pszConVar );
|
|
if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) )
|
|
return NULL;
|
|
return cvar.GetString();
|
|
}
|
|
|
|
const char *GetDefaultValue( const char *pszConVar )
|
|
{
|
|
ConVarRef cvar( pszConVar );
|
|
return cvar.GetDefault();
|
|
}
|
|
|
|
bool IsFlagSet( const char *pszConVar, int nFlags )
|
|
{
|
|
ConVarRef cvar( pszConVar );
|
|
return cvar.IsFlagSet( nFlags );
|
|
}
|
|
|
|
void SetFloat( const char *pszConVar, float value )
|
|
{
|
|
SetValue( pszConVar, value );
|
|
}
|
|
|
|
void SetInt( const char *pszConVar, int value )
|
|
{
|
|
SetValue( pszConVar, value );
|
|
}
|
|
|
|
void SetBool( const char *pszConVar, bool value )
|
|
{
|
|
SetValue( pszConVar, value );
|
|
}
|
|
|
|
void SetStr( const char *pszConVar, const char *value )
|
|
{
|
|
SetValue( pszConVar, value );
|
|
}
|
|
|
|
template <typename T>
|
|
void SetValue( const char *pszConVar, T value )
|
|
{
|
|
ConVarRef cvar( pszConVar );
|
|
if ( !cvar.IsValid() )
|
|
return;
|
|
|
|
if ( cvar.IsFlagSet( FCVAR_NOT_CONNECTED | FCVAR_SERVER_CANNOT_QUERY ) )
|
|
return;
|
|
|
|
if ( IsBlockedConvar( pszConVar ) )
|
|
return;
|
|
|
|
cvar.SetValue( value );
|
|
}
|
|
|
|
} g_ScriptConvarAccessor;
|
|
|
|
|
|
void CScriptConvarAccessor::RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags )
|
|
{
|
|
unsigned int hash = Hash(name);
|
|
int idx = g_ScriptConCommands.Find(hash);
|
|
if ( idx == g_ScriptConCommands.InvalidIndex() )
|
|
{
|
|
ConCommandBase *pBase = g_pCVar->FindCommandBase(name);
|
|
if ( pBase && ( !pBase->IsCommand() || !IsOverridable(hash) ) )
|
|
{
|
|
DevWarning( 1, "CScriptConvarAccessor::RegisterCommand unable to register blocked ConCommand: %s\n", name );
|
|
return;
|
|
}
|
|
|
|
if ( !fn )
|
|
return;
|
|
|
|
CScriptConCommand *p = new CScriptConCommand( name, fn, helpString, flags, static_cast< ConCommand* >(pBase) );
|
|
g_ScriptConCommands.Insert( hash, p );
|
|
}
|
|
else
|
|
{
|
|
CScriptConCommand *pCmd = g_ScriptConCommands[idx];
|
|
pCmd->SetCallback( fn );
|
|
//CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterCommand replacing command already registered: %s\n", name );
|
|
}
|
|
}
|
|
|
|
void CScriptConvarAccessor::SetCompletionCallback( const char *name, HSCRIPT fn )
|
|
{
|
|
unsigned int hash = Hash(name);
|
|
int idx = g_ScriptConCommands.Find(hash);
|
|
if ( idx != g_ScriptConCommands.InvalidIndex() )
|
|
{
|
|
g_ScriptConCommands[idx]->SetCompletionCallback( fn );
|
|
}
|
|
}
|
|
|
|
void CScriptConvarAccessor::UnregisterCommand( const char *name )
|
|
{
|
|
unsigned int hash = Hash(name);
|
|
int idx = g_ScriptConCommands.Find(hash);
|
|
if ( idx != g_ScriptConCommands.InvalidIndex() )
|
|
{
|
|
g_ScriptConCommands[idx]->Unregister();
|
|
}
|
|
}
|
|
|
|
void CScriptConvarAccessor::RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags )
|
|
{
|
|
Assert( g_pCVar );
|
|
unsigned int hash = Hash(name);
|
|
int idx = g_ScriptConVars.Find(hash);
|
|
if ( idx == g_ScriptConVars.InvalidIndex() )
|
|
{
|
|
if ( g_pCVar->FindCommandBase(name) )
|
|
{
|
|
DevWarning( 1, "CScriptConvarAccessor::RegisterConvar unable to register blocked ConCommand: %s\n", name );
|
|
return;
|
|
}
|
|
|
|
CScriptConVar *p = new CScriptConVar( name, pDefaultValue, helpString, flags );
|
|
g_ScriptConVars.Insert( hash, p );
|
|
}
|
|
else
|
|
{
|
|
//CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterConvar convar %s already registered\n", name );
|
|
}
|
|
}
|
|
|
|
void CScriptConvarAccessor::SetChangeCallback( const char *name, HSCRIPT fn )
|
|
{
|
|
unsigned int hash = Hash(name);
|
|
int idx = g_ScriptConVars.Find(hash);
|
|
if ( idx != g_ScriptConVars.InvalidIndex() )
|
|
{
|
|
g_ScriptConVars[idx]->SetChangeCallback( fn );
|
|
}
|
|
}
|
|
|
|
void ScriptConVarCallback( IConVar *var, const char* pszOldValue, float flOldValue )
|
|
{
|
|
ConVar *cvar = (ConVar*)var;
|
|
const char *name = cvar->GetName();
|
|
unsigned int hash = CScriptConvarAccessor::Hash( name );
|
|
int idx = g_ScriptConVars.Find(hash);
|
|
if ( idx != g_ScriptConVars.InvalidIndex() )
|
|
{
|
|
Assert( g_ScriptConVars[idx]->m_hCallback );
|
|
|
|
ScriptVariant_t args[5] = { name, pszOldValue, flOldValue, cvar->GetString(), cvar->GetFloat() };
|
|
if ( g_pScriptVM->ExecuteFunction( g_ScriptConVars[idx]->m_hCallback, args, 5, NULL, NULL, true ) == SCRIPT_ERROR )
|
|
{
|
|
DevWarning( 1, "CScriptConVar: invalid change callback for '%s'\n", name );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool CScriptConvarAccessor::Init()
|
|
{
|
|
static bool bExecOnce = false;
|
|
if ( bExecOnce )
|
|
return true;
|
|
bExecOnce = true;
|
|
|
|
AddOverridable( "+attack" );
|
|
AddOverridable( "+attack2" );
|
|
AddOverridable( "+attack3" );
|
|
AddOverridable( "+forward" );
|
|
AddOverridable( "+back" );
|
|
AddOverridable( "+moveleft" );
|
|
AddOverridable( "+moveright" );
|
|
AddOverridable( "+use" );
|
|
AddOverridable( "+jump" );
|
|
AddOverridable( "+zoom" );
|
|
AddOverridable( "+reload" );
|
|
AddOverridable( "+speed" );
|
|
AddOverridable( "+walk" );
|
|
AddOverridable( "+duck" );
|
|
AddOverridable( "+strafe" );
|
|
AddOverridable( "+alt1" );
|
|
AddOverridable( "+alt2" );
|
|
AddOverridable( "+grenade1" );
|
|
AddOverridable( "+grenade2" );
|
|
AddOverridable( "+showscores" );
|
|
AddOverridable( "+voicerecord" );
|
|
|
|
AddOverridable( "-attack" );
|
|
AddOverridable( "-attack2" );
|
|
AddOverridable( "-attack3" );
|
|
AddOverridable( "-forward" );
|
|
AddOverridable( "-back" );
|
|
AddOverridable( "-moveleft" );
|
|
AddOverridable( "-moveright" );
|
|
AddOverridable( "-use" );
|
|
AddOverridable( "-jump" );
|
|
AddOverridable( "-zoom" );
|
|
AddOverridable( "-reload" );
|
|
AddOverridable( "-speed" );
|
|
AddOverridable( "-walk" );
|
|
AddOverridable( "-duck" );
|
|
AddOverridable( "-strafe" );
|
|
AddOverridable( "-alt1" );
|
|
AddOverridable( "-alt2" );
|
|
AddOverridable( "-grenade1" );
|
|
AddOverridable( "-grenade2" );
|
|
AddOverridable( "-showscores" );
|
|
AddOverridable( "-voicerecord" );
|
|
|
|
AddOverridable( "toggle_duck" );
|
|
AddOverridable( "impulse" );
|
|
AddOverridable( "use" );
|
|
AddOverridable( "lastinv" );
|
|
AddOverridable( "invnext" );
|
|
AddOverridable( "invprev" );
|
|
AddOverridable( "phys_swap" );
|
|
AddOverridable( "slot0" );
|
|
AddOverridable( "slot1" );
|
|
AddOverridable( "slot2" );
|
|
AddOverridable( "slot3" );
|
|
AddOverridable( "slot4" );
|
|
AddOverridable( "slot5" );
|
|
AddOverridable( "slot6" );
|
|
AddOverridable( "slot7" );
|
|
AddOverridable( "slot8" );
|
|
AddOverridable( "slot9" );
|
|
AddOverridable( "slot10" );
|
|
|
|
AddOverridable( "save" );
|
|
AddOverridable( "load" );
|
|
|
|
AddOverridable( "say" );
|
|
AddOverridable( "say_team" );
|
|
|
|
|
|
AddBlockedConVar( "con_enable" );
|
|
AddBlockedConVar( "cl_allowdownload" );
|
|
AddBlockedConVar( "cl_allowupload" );
|
|
AddBlockedConVar( "cl_downloadfilter" );
|
|
|
|
return true;
|
|
}
|
|
|
|
BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarAccessor, "CConvars", SCRIPT_SINGLETON "Provides an interface to convars." )
|
|
DEFINE_SCRIPTFUNC( RegisterConvar, "register a new console variable." )
|
|
DEFINE_SCRIPTFUNC( RegisterCommand, "register a console command." )
|
|
DEFINE_SCRIPTFUNC( SetCompletionCallback, "callback is called with 3 parameters (cmd, partial, commands), user strings must be appended to 'commands' array" )
|
|
DEFINE_SCRIPTFUNC( SetChangeCallback, "callback is called with 5 parameters (var, szOldValue, flOldValue, szNewValue, flNewValue)" )
|
|
DEFINE_SCRIPTFUNC( UnregisterCommand, "unregister a console command." )
|
|
DEFINE_SCRIPTFUNC( GetCommandClient, "returns the player who issued this console command." )
|
|
#ifdef GAME_DLL
|
|
DEFINE_SCRIPTFUNC( GetClientConvarValue, "Get a convar keyvalue for a specified client" )
|
|
#endif
|
|
DEFINE_SCRIPTFUNC( GetFloat, "Returns the convar as a float. May return null if no such convar." )
|
|
DEFINE_SCRIPTFUNC( GetInt, "Returns the convar as an int. May return null if no such convar." )
|
|
DEFINE_SCRIPTFUNC( GetBool, "Returns the convar as a bool. May return null if no such convar." )
|
|
DEFINE_SCRIPTFUNC( GetStr, "Returns the convar as a string. May return null if no such convar." )
|
|
DEFINE_SCRIPTFUNC( GetDefaultValue, "Returns the convar's default value as a string. May return null if no such convar." )
|
|
DEFINE_SCRIPTFUNC( IsFlagSet, "Returns the convar's flags. May return null if no such convar." )
|
|
DEFINE_SCRIPTFUNC( SetFloat, "Sets the value of the convar as a float." )
|
|
DEFINE_SCRIPTFUNC( SetInt, "Sets the value of the convar as an int." )
|
|
DEFINE_SCRIPTFUNC( SetBool, "Sets the value of the convar as a bool." )
|
|
DEFINE_SCRIPTFUNC( SetStr, "Sets the value of the convar as a string." )
|
|
END_SCRIPTDESC();
|
|
|
|
|
|
//=============================================================================
|
|
// Effects
|
|
// (Unique to mapbase)
|
|
//
|
|
// At the moment only clientside until a filtering method on server is finalised.
|
|
//
|
|
// TEs most of the time call IEffects (g_pEffects) or ITempEnts (tempents) on client,
|
|
// but they also record for tools recording mode.
|
|
//
|
|
// On client no TE is suppressed.
|
|
// TE flags are found at tempent.h
|
|
//
|
|
// TODO:
|
|
//=============================================================================
|
|
#ifdef CLIENT_DLL
|
|
|
|
class CEffectsScriptHelper
|
|
{
|
|
public:
|
|
void DynamicLight( int index, const Vector& origin, int r, int g, int b, int exponent,
|
|
float radius, float die, float decay, int style = 0, int flags = 0 )
|
|
{
|
|
//te->DynamicLight( filter, delay, &origin, r, g, b, exponent, radius, die, decay );
|
|
dlight_t *dl = effects->CL_AllocDlight( index );
|
|
dl->origin = origin;
|
|
dl->color.r = r;
|
|
dl->color.g = g;
|
|
dl->color.b = b;
|
|
dl->color.exponent = exponent;
|
|
dl->radius = radius;
|
|
dl->die = gpGlobals->curtime + die;
|
|
dl->decay = decay;
|
|
dl->style = style;
|
|
dl->flags = flags;
|
|
}
|
|
|
|
void Explosion( const Vector& pos, float scale, int radius, int magnitude, int flags )
|
|
{
|
|
C_RecipientFilter filter;
|
|
filter.AddAllPlayers();
|
|
// framerate, modelindex, normal and materialtype are unused
|
|
// radius for ragdolls
|
|
extern short g_sModelIndexFireball;
|
|
te->Explosion( filter, 0.0f, &pos, g_sModelIndexFireball, scale, 15, flags, radius, magnitude, &vec3_origin );
|
|
}
|
|
|
|
// void FXExplosion( const Vector& pos, const Vector& normal, int materialType = 'C' )
|
|
// {
|
|
// // just the particles
|
|
// // materialtype only for debris. can be 'C','W' or anything else.
|
|
// FX_Explosion( const_cast<Vector&>(pos), const_cast<Vector&>(normal), materialType );
|
|
// }
|
|
|
|
// void ConcussiveExplosion( const Vector& pos, const Vector& normal )
|
|
// {
|
|
// FX_ConcussiveExplosion( const_cast<Vector&>(pos), const_cast<Vector&>(normal) );
|
|
// }
|
|
|
|
// void MicroExplosion( const Vector& pos, const Vector& normal )
|
|
// {
|
|
// FX_MicroExplosion( const_cast<Vector&>(pos), const_cast<Vector&>(normal) );
|
|
// }
|
|
|
|
// void MuzzleFlash( int type, HSCRIPT hEntity, int attachment, bool firstPerson )
|
|
// {
|
|
// C_BaseEntity *p = ToEnt(hEntity);
|
|
// ClientEntityHandle_t ent = p ? (ClientEntityList().EntIndexToHandle)( p->entindex() ) : NULL;;
|
|
// tempents->MuzzleFlash( type, ent, attachment, firstPerson );
|
|
// }
|
|
|
|
void Sparks( const Vector& pos, int nMagnitude, int nTrailLength, const Vector& pDir )
|
|
{
|
|
//te->Sparks( filter, delay, &pos, nMagnitude, nTrailLength, &pDir );
|
|
//g_pEffects->Sparks( pos, nMagnitude, nTrailLength, &pDir );
|
|
FX_ElectricSpark( pos, nMagnitude, nTrailLength, &pDir );
|
|
}
|
|
|
|
void MetalSparks( const Vector& pos, const Vector& dir )
|
|
{
|
|
//g_pEffects->MetalSparks( pos, dir );
|
|
FX_MetalSpark( pos, dir, dir );
|
|
}
|
|
|
|
// void Smoke( const Vector& pos, float scale, int framerate)
|
|
// {
|
|
// extern short g_sModelIndexSmoke;
|
|
// //te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, scale * 10.0f, framerate );
|
|
// g_pEffects->Smoke( pos, g_sModelIndexSmoke, scale, framerate );
|
|
// }
|
|
|
|
void Dust( const Vector &pos, const Vector &dir, float size, float speed )
|
|
{
|
|
//te->Dust( filter, delay, pos, dir, size, speed );
|
|
//g_pEffects->Dust( pos, dir, size, speed );
|
|
FX_Dust( pos, dir, size, speed );
|
|
}
|
|
|
|
void Bubbles( const Vector &mins, const Vector &maxs, float height, int modelindex, int count, float speed )
|
|
{
|
|
//int bubbles = modelinfo->GetModelIndex( "sprites/bubble.vmt" );
|
|
//te->Bubbles( filter, delay, &mins, &maxs, height, modelindex, count, speed );
|
|
tempents->Bubbles( mins, maxs, height, modelindex, count, speed );
|
|
}
|
|
|
|
// void Fizz( const Vector& mins, const Vector& maxs, int modelIndex, int density, int current/*, int flags*/ )
|
|
// {
|
|
// //te->Fizz( filter, delay, ent, modelindex, density, current );
|
|
// //tempents->FizzEffect( ToEnt(ent), modelindex, density, current );
|
|
// }
|
|
|
|
void Sprite( const Vector &pos, const Vector &dir, float scale, int modelIndex, int rendermode,
|
|
int renderfx, int brightness, float life, int flags )
|
|
{
|
|
//te->Sprite( filter, delay, &pos, modelindex, size, brightness );
|
|
float a = (1.0 / 255.0) * brightness;
|
|
tempents->TempSprite( pos, dir, scale, modelIndex, rendermode, renderfx, a, life, flags );
|
|
}
|
|
|
|
// void PhysicsProp( float delay, int modelindex, int skin, const Vector& pos, const QAngle &angles,
|
|
// const Vector& vel, int flags, int effects )
|
|
// {
|
|
// //te->PhysicsProp( filter, delay, modelindex, skin, pos, angles, vel, flags, effects );
|
|
// tempents->PhysicsProp( modelindex, skin, pos, angles, vel, flags, effects );
|
|
// }
|
|
|
|
void ClientProjectile( const Vector& vecOrigin, const Vector& vecVelocity, const Vector& vecAccel, int modelindex,
|
|
int lifetime, HSCRIPT pOwner, const char *pszImpactEffect = NULL, const char *pszParticleEffect = NULL )
|
|
{
|
|
//te->ClientProjectile( filter, delay, &vecOrigin, &vecVelocity, modelindex, lifetime, ToEnt(pOwner) );
|
|
if ( pszImpactEffect && !(*pszImpactEffect) )
|
|
pszImpactEffect = NULL;
|
|
if ( pszParticleEffect && !(*pszParticleEffect) )
|
|
pszParticleEffect = NULL;
|
|
tempents->ClientProjectile( vecOrigin, vecVelocity, vecAccel, modelindex, lifetime, ToEnt(pOwner), pszImpactEffect, pszParticleEffect );
|
|
}
|
|
|
|
} g_ScriptEffectsHelper;
|
|
|
|
BEGIN_SCRIPTDESC_ROOT_NAMED( CEffectsScriptHelper, "CEffects", SCRIPT_SINGLETON "" )
|
|
DEFINE_SCRIPTFUNC( DynamicLight, "" )
|
|
DEFINE_SCRIPTFUNC( Explosion, "" )
|
|
DEFINE_SCRIPTFUNC( Sparks, "" )
|
|
DEFINE_SCRIPTFUNC( MetalSparks, "" )
|
|
DEFINE_SCRIPTFUNC( Dust, "" )
|
|
DEFINE_SCRIPTFUNC( Bubbles, "" )
|
|
DEFINE_SCRIPTFUNC( Sprite, "" )
|
|
DEFINE_SCRIPTFUNC( ClientProjectile, "" )
|
|
END_SCRIPTDESC();
|
|
|
|
|
|
|
|
//=============================================================================
|
|
//=============================================================================
|
|
|
|
extern CGlowObjectManager g_GlowObjectManager;
|
|
|
|
class CScriptGlowObjectManager : public CAutoGameSystem
|
|
{
|
|
public:
|
|
CUtlVector<int> m_RegisteredObjects;
|
|
|
|
void LevelShutdownPostEntity()
|
|
{
|
|
FOR_EACH_VEC( m_RegisteredObjects, i )
|
|
g_GlowObjectManager.UnregisterGlowObject( m_RegisteredObjects[i] );
|
|
m_RegisteredObjects.Purge();
|
|
}
|
|
|
|
public:
|
|
int Register( HSCRIPT hEntity, int r, int g, int b, int a, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded )
|
|
{
|
|
Vector vGlowColor;
|
|
vGlowColor.x = r * ( 1.0f / 255.0f );
|
|
vGlowColor.y = g * ( 1.0f / 255.0f );
|
|
vGlowColor.z = b * ( 1.0f / 255.0f );
|
|
float flGlowAlpha = a * ( 1.0f / 255.0f );
|
|
int idx = g_GlowObjectManager.RegisterGlowObject( ToEnt(hEntity), vGlowColor, flGlowAlpha, bRenderWhenOccluded, bRenderWhenUnoccluded, -1 );
|
|
m_RegisteredObjects.AddToTail( idx );
|
|
return idx;
|
|
}
|
|
|
|
void Unregister( int nGlowObjectHandle )
|
|
{
|
|
if ( (nGlowObjectHandle < 0) || (nGlowObjectHandle >= g_GlowObjectManager.m_GlowObjectDefinitions.Count()) )
|
|
return;
|
|
g_GlowObjectManager.UnregisterGlowObject( nGlowObjectHandle );
|
|
m_RegisteredObjects.FindAndFastRemove( nGlowObjectHandle );
|
|
}
|
|
|
|
void SetEntity( int nGlowObjectHandle, HSCRIPT hEntity )
|
|
{
|
|
g_GlowObjectManager.SetEntity( nGlowObjectHandle, ToEnt(hEntity) );
|
|
}
|
|
|
|
void SetColor( int nGlowObjectHandle, int r, int g, int b )
|
|
{
|
|
Vector vGlowColor;
|
|
vGlowColor.x = r * ( 1.0f / 255.0f );
|
|
vGlowColor.y = g * ( 1.0f / 255.0f );
|
|
vGlowColor.z = b * ( 1.0f / 255.0f );
|
|
g_GlowObjectManager.SetColor( nGlowObjectHandle, vGlowColor );
|
|
}
|
|
|
|
void SetAlpha( int nGlowObjectHandle, int a )
|
|
{
|
|
float flGlowAlpha = a * ( 1.0f / 255.0f );
|
|
g_GlowObjectManager.SetAlpha( nGlowObjectHandle, flGlowAlpha );
|
|
}
|
|
|
|
void SetRenderFlags( int nGlowObjectHandle, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded )
|
|
{
|
|
g_GlowObjectManager.SetRenderFlags( nGlowObjectHandle, bRenderWhenOccluded, bRenderWhenUnoccluded );
|
|
}
|
|
|
|
} g_ScriptGlowObjectManager;
|
|
|
|
BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptGlowObjectManager, "CGlowObjectManager", SCRIPT_SINGLETON "" )
|
|
DEFINE_SCRIPTFUNC( Register, "( HSCRIPT hEntity, int r, int g, int b, int a, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded )" )
|
|
DEFINE_SCRIPTFUNC( Unregister, "" )
|
|
DEFINE_SCRIPTFUNC( SetEntity, "" )
|
|
DEFINE_SCRIPTFUNC( SetColor, "" )
|
|
DEFINE_SCRIPTFUNC( SetAlpha, "" )
|
|
DEFINE_SCRIPTFUNC( SetRenderFlags, "" )
|
|
END_SCRIPTDESC();
|
|
|
|
|
|
//=============================================================================
|
|
//=============================================================================
|
|
|
|
|
|
#if !defined(NO_STEAM)
|
|
class CScriptSteamAPI
|
|
{
|
|
public:
|
|
const char *GetSteam2ID()
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamUser() )
|
|
return NULL;
|
|
|
|
CSteamID id = steamapicontext->SteamUser()->GetSteamID();
|
|
|
|
uint32 accountID = id.GetAccountID();
|
|
uint32 steamInstanceID = 0;
|
|
uint32 high32bits = accountID % 2;
|
|
uint32 low32bits = accountID / 2;
|
|
|
|
static char ret[48];
|
|
V_snprintf( ret, sizeof(ret), "STEAM_%u:%u:%u", steamInstanceID, high32bits, low32bits );
|
|
return ret;
|
|
}
|
|
|
|
int GetSecondsSinceComputerActive()
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamUtils() )
|
|
return 0;
|
|
|
|
return steamapicontext->SteamUtils()->GetSecondsSinceComputerActive();
|
|
}
|
|
|
|
int GetCurrentBatteryPower()
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamUtils() )
|
|
return 0;
|
|
|
|
return steamapicontext->SteamUtils()->GetCurrentBatteryPower();
|
|
}
|
|
#if 0
|
|
const char *GetIPCountry()
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamUtils() )
|
|
return NULL;
|
|
|
|
const char *get = steamapicontext->SteamUtils()->GetIPCountry();
|
|
if ( !get )
|
|
return NULL;
|
|
|
|
static char ret[3];
|
|
V_strncpy( ret, get, 3 );
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
const char *GetCurrentGameLanguage()
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamApps() )
|
|
return NULL;
|
|
|
|
const char *lang = steamapicontext->SteamApps()->GetCurrentGameLanguage();
|
|
if ( !lang )
|
|
return NULL;
|
|
|
|
static char ret[16];
|
|
V_strncpy( ret, lang, sizeof(ret) );
|
|
|
|
return ret;
|
|
}
|
|
const char *GetCurrentBetaName()
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamApps() )
|
|
return NULL;
|
|
|
|
static char ret[16];
|
|
steamapicontext->SteamApps()->GetCurrentBetaName( ret, sizeof( ret ) );
|
|
return ret;
|
|
}
|
|
#if 0
|
|
bool IsSubscribedApp( int nAppID )
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamApps() )
|
|
return false;
|
|
|
|
return steamapicontext->SteamApps()->BIsSubscribedApp( nAppID );
|
|
}
|
|
#endif
|
|
bool IsAppInstalled( int nAppID )
|
|
{
|
|
if ( !steamapicontext || !steamapicontext->SteamApps() )
|
|
return false;
|
|
|
|
return steamapicontext->SteamApps()->BIsAppInstalled( nAppID );
|
|
}
|
|
|
|
} g_ScriptSteamAPI;
|
|
|
|
BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptSteamAPI, "CSteamAPI", SCRIPT_SINGLETON "" )
|
|
DEFINE_SCRIPTFUNC( GetSteam2ID, "" )
|
|
//DEFINE_SCRIPTFUNC( IsVACBanned, "" )
|
|
DEFINE_SCRIPTFUNC( GetSecondsSinceComputerActive, "Returns the number of seconds since the user last moved the mouse." )
|
|
DEFINE_SCRIPTFUNC( GetCurrentBatteryPower, "Return the amount of battery power left in the current system in % [0..100], 255 for being on AC power" )
|
|
//DEFINE_SCRIPTFUNC( GetIPCountry, "Returns the 2 digit ISO 3166-1-alpha-2 format country code this client is running in (as looked up via an IP-to-location database)" )
|
|
DEFINE_SCRIPTFUNC( GetCurrentGameLanguage, "Gets the current language that the user has set as API language code. This falls back to the Steam UI language if the user hasn't explicitly picked a language for the title." )
|
|
DEFINE_SCRIPTFUNC( GetCurrentBetaName, "Gets the name of the user's current beta branch. In Source SDK Base 2013 Singleplayer, this will usually return 'upcoming'." )
|
|
//DEFINE_SCRIPTFUNC( IsSubscribedApp, "Returns true if the user is subscribed to the specified app ID." )
|
|
DEFINE_SCRIPTFUNC( IsAppInstalled, "Returns true if the user has the specified app ID installed on their computer." )
|
|
END_SCRIPTDESC();
|
|
#endif // !NO_STEAM
|
|
|
|
#endif // CLIENT_DLL
|
|
|
|
|
|
void RegisterScriptSingletons()
|
|
{
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::SaveTable, "SaveTable", "Store a table with primitive values that will persist across level transitions and save loads." );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::RestoreTable, "RestoreTable", "Retrieves a table from storage. Write into input table." );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::ClearSavedTable, "ClearSavedTable", "Removes the table with the given context." );
|
|
ScriptRegisterSimpleHook( g_pScriptVM, g_Hook_OnSave, "OnSave", FIELD_VOID, "Called when the game is saved." );
|
|
ScriptRegisterSimpleHook( g_pScriptVM, g_Hook_OnRestore, "OnRestore", FIELD_VOID, "Called when the game is restored." );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileWrite, "StringToFile", "Stores the string into the file" );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileRead, "FileToString", "Returns the string from the file, null if no file or file is too big." );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileExists, "FileExists", "Returns true if the file exists." );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesWrite, "KeyValuesToFile", "Stores the CScriptKeyValues into the file" );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesRead, "FileToKeyValues", "Returns the CScriptKeyValues from the file, null if no file or file is too big." );
|
|
|
|
ScriptRegisterFunction( g_pScriptVM, ListenToGameEvent, "Register as a listener for a game event from script." );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToGameEvent, "StopListeningToGameEvent", "Stop the specified event listener." );
|
|
ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToAllGameEvents, "StopListeningToAllGameEvents", "Stop listening to all game events within a specific context." );
|
|
ScriptRegisterFunction( g_pScriptVM, FireGameEvent, "Fire a game event." );
|
|
#ifndef CLIENT_DLL
|
|
ScriptRegisterFunction( g_pScriptVM, FireGameEventLocal, "Fire a game event without broadcasting to the client." );
|
|
#endif
|
|
|
|
g_pScriptVM->RegisterInstance( &g_ScriptNetPropManager, "NetProps" );
|
|
g_pScriptVM->RegisterInstance( &g_ScriptLocalize, "Localize" );
|
|
g_pScriptVM->RegisterInstance( g_ScriptNetMsg, "NetMsg" );
|
|
g_pScriptVM->RegisterInstance( &g_ScriptDebugOverlay, "debugoverlay" );
|
|
g_pScriptVM->RegisterInstance( &g_ScriptConvarAccessor, "Convars" );
|
|
#ifdef CLIENT_DLL
|
|
g_pScriptVM->RegisterInstance( &g_ScriptEffectsHelper, "effects" );
|
|
g_pScriptVM->RegisterInstance( &g_ScriptGlowObjectManager, "GlowObjectManager" );
|
|
|
|
#if !defined(NO_STEAM)
|
|
g_pScriptVM->RegisterInstance( &g_ScriptSteamAPI, "steam" );
|
|
#endif
|
|
#endif
|
|
|
|
// Singletons not unique to VScript (not declared or defined here)
|
|
g_pScriptVM->RegisterInstance( GameRules(), "GameRules" );
|
|
g_pScriptVM->RegisterInstance( GetAmmoDef(), "AmmoDef" );
|
|
#ifndef CLIENT_DLL
|
|
g_pScriptVM->RegisterInstance( &g_AI_SquadManager, "Squads" );
|
|
#endif
|
|
|
|
#ifdef USE_OLD_EVENT_DESCRIPTORS
|
|
CScriptGameEventListener::LoadAllEvents();
|
|
#endif
|
|
|
|
g_ScriptNetMsg->InitPostVM();
|
|
}
|