From c86bf826e0940cb74bf3384823059f20fcf6e8de Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 5 Dec 2023 19:53:46 +0300 Subject: [PATCH 001/103] Rewrite CScriptNetPropManager --- .../shared/mapbase/vscript_singletons.cpp | 2625 +++++++++++++++-- 1 file changed, 2419 insertions(+), 206 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index c5cca45b35a..6cdf66df043 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -14,7 +14,13 @@ #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 @@ -43,285 +49,2492 @@ 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 { -public: +private: +#if GAME_DLL + typedef SendProp NetProp; + typedef SendTable NetTable; + typedef ServerClass NetworkClass; -#ifdef CLIENT_DLL - RecvProp *RecurseTable( RecvTable *pTable, const char *pszPropName ) + 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 - SendProp *RecurseTable( SendTable *pTable, const char *pszPropName ) + 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 ) { -#ifdef CLIENT_DLL - RecvProp *pProp = NULL; + 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 - SendProp *pProp = NULL; + 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 - for (int i = 0; i < pTable->GetNumProps(); i++) - { - pProp = pTable->GetProp( i ); - if (pProp->GetType() == DPT_DataTable) - { - pProp = RecurseTable(pProp->GetDataTable(), pszPropName); - if (pProp) - return pProp; - } - else - { - if (FStrEq( pProp->GetName(), pszPropName )) - return pProp; - } - } + } - return NULL; + bool IsEHandle( NetProp *pProp ) + { + return ( pProp->GetProxyFn() == DataTableProxy_EHandle ); } -#ifdef CLIENT_DLL - RecvProp *RecurseNetworkClass( ClientClass *pClass, const char *pszPropName ) -#else - SendProp *RecurseNetworkClass( ServerClass *pClass, const char *pszPropName ) -#endif + bool IsUtlVector( NetProp *pProp ) { -#ifdef CLIENT_DLL - RecvProp *pProp = RecurseTable( pClass->m_pRecvTable, pszPropName ); +#ifdef GAME_DLL + SendVarProxyFn proxy = pProp->GetProxyFn(); #else - SendProp *pProp = RecurseTable( pClass->m_pTable, pszPropName ); + RecvVarProxyFn proxy = pProp->GetProxyFn(); #endif - if (pProp) - return pProp; - if (pClass->m_pNext) - return RecurseNetworkClass( pClass->m_pNext, pszPropName ); - else - return NULL; + return ( proxy == DataTableProxy_UtlVectorLength ); } -#ifdef CLIENT_DLL - RecvProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) +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 { - if (pEnt) + int offset : 32; // actually a short + + union { - return RecurseNetworkClass( pEnt->GetClientClass(), pszPropName ); - } + int mask : 32; + int stringsize : 32; + }; - return NULL; - } -#else - SendProp *GetPropByName( CBaseEntity *pEnt, const char *pszPropName ) - { - if (pEnt) + 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 RecurseNetworkClass( pEnt->GetServerClass(), pszPropName ); + return offset + index * elemsize; } + }; - return NULL; - } -#endif + // 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 ) {} + }; - int GetPropArraySize( HSCRIPT hEnt, const char *pszPropName ) + // 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 ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp) + int idx = m_EntMap.Find( GetClassID( pEnt ) ); + if ( idx == m_EntMap.InvalidIndex() ) { - // TODO: Is this what this function wants? - return pProp->GetNumElements(); + // Vector indices are kept in parallel as a workaround for encapsulating maps + idx = m_EntMap.AddToTail( GetClassID( pEnt ) ); + m_VarDicts.AddToTail(); } - return -1; + 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; } - #define GetPropFunc( name, varType, propType, defaultval ) \ - varType name( HSCRIPT hEnt, const char *pszPropName ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - return *(varType*)((char *)pEnt + pProp->GetOffset()); \ - } \ - return defaultval; \ - } \ - - #define GetPropFuncArray( name, varType, propType, defaultval ) \ - varType name( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - return ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; \ - } \ - return defaultval; \ - } \ - - GetPropFunc( GetPropFloat, float, DPT_Float, -1 ); - GetPropFuncArray( GetPropFloatArray, float, DPT_Float, -1 ); - GetPropFunc( GetPropInt, int, DPT_Int, -1 ); - GetPropFuncArray( GetPropIntArray, int, DPT_Int, -1 ); - GetPropFunc( GetPropVector, Vector, DPT_Vector, vec3_invalid ); - GetPropFuncArray( GetPropVectorArray, Vector, DPT_Vector, vec3_invalid ); - - HSCRIPT GetPropEntity( HSCRIPT hEnt, const char *pszPropName ) + varinfo_t* CacheFetch( CBaseEntity *pEnt, const char *szProp ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) - { - return ToHScript( *(CHandle*)((char *)pEnt + pProp->GetOffset()) ); - } + int idx = m_EntMap.Find( GetClassID( pEnt ) ); + if ( idx == m_EntMap.InvalidIndex() ) + return NULL; - 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; } - HSCRIPT GetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) +public: + ~CScriptNetPropManager() { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) - { - return ToHScript( ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] ); - } + PurgeCache(); + } - return NULL; + void PurgeCache() + { + m_EntMap.Purge(); + m_VarDicts.Purge(); } - const char *GetPropString( HSCRIPT hEnt, const char *pszPropName ) +private: + typedescription_t *FindField( char *pBase, datamap_t *map, const char *szName, int *offset ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + if ( map->baseMap ) { - return (const char*)((char *)pEnt + pProp->GetOffset()); + typedescription_t* p = FindField( pBase, map->baseMap, szName, offset ); + if ( p ) + return p; } - return NULL; - } + typedescription_t *pFields = map->dataDesc; + int numFields = map->dataNumFields; - const char *GetPropStringArray( HSCRIPT hEnt, const char *pszPropName, int iArrayElement ) - { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + for ( int i = 0; i < numFields; i++ ) { - return ((const char**)((char *)pEnt + pProp->GetOffset()))[iArrayElement]; + 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; } - const char *GetPropType( HSCRIPT hEnt, const char *pszPropName ) + NetProp *FindProp( char *pBase, NetTable *pTable, const char *szName, int *offset ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp) + int numProps = pTable->GetNumProps(); + + for ( int i = 0; i < numProps; i++ ) { - switch (pProp->GetType()) + NetProp* pProp = pTable->GetProp(i); + + if ( pProp->IsInsideArray() ) + continue; + + if ( !V_strcmp( pProp->GetName(), szName ) ) { - case DPT_Int: return "integer"; - case DPT_Float: return "float"; - case DPT_Vector: return "vector"; - case DPT_VectorXY: return "vector2d"; - case DPT_String: return "string"; - case DPT_Array: return "array"; - case DPT_DataTable: return "datatable"; + *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; } - bool HasProp( HSCRIPT hEnt, const char *pszPropName ) + typedescription_t *FindInDataMap( char * const pBase, datamap_t *map, const char *szFullProp, int *offset ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - return GetPropByName( pEnt, pszPropName ) != NULL; - } - - #define SetPropFunc( name, varType, propType ) \ - void name( HSCRIPT hEnt, const char *pszPropName, varType value ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - *(varType*)((char *)pEnt + pProp->GetOffset()) = value; \ - } \ - } \ - - #define SetPropFuncArray( name, varType, propType ) \ - void name( HSCRIPT hEnt, const char *pszPropName, varType value, int iArrayElement ) \ - { \ - CBaseEntity *pEnt = ToEnt( hEnt ); \ - auto *pProp = GetPropByName( pEnt, pszPropName ); \ - if (pProp && pProp->GetType() == propType) \ - { \ - ((varType*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = value; \ - } \ - } \ - - SetPropFunc( SetPropFloat, float, DPT_Float ); - SetPropFuncArray( SetPropFloatArray, float, DPT_Float ); - SetPropFunc( SetPropInt, int, DPT_Int ); - SetPropFuncArray( SetPropIntArray, int, DPT_Int ); - SetPropFunc( SetPropVector, Vector, DPT_Vector ); - SetPropFuncArray( SetPropVectorArray, Vector, DPT_Vector ); - SetPropFunc( SetPropString, const char*, DPT_String ); - SetPropFuncArray( SetPropStringArray, const char*, DPT_String ); - - void SetPropEntity( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value ) + *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 ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + *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 { - *((CHandle*)((char *)pEnt + pProp->GetOffset())) = ToEnt(value); - } + // 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 ); } - HSCRIPT SetPropEntityArray( HSCRIPT hEnt, const char *pszPropName, HSCRIPT value, int iArrayElement ) + // 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 ) { - CBaseEntity *pEnt = ToEnt( hEnt ); - auto *pProp = GetPropByName( pEnt, pszPropName ); - if (pProp && pProp->GetType() == DPT_Int) + int offset = 0; + NetTable *pTable = GetNetTable( GetNetworkClass( pEnt ) ); + NetProp *pProp = FindInNetTable( (char*)pEnt, pTable, szProp, &offset ); + if ( pProp ) { - ((CHandle*)((char *)pEnt + pProp->GetOffset()))[iArrayElement] = ToEnt(value); + +#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 } - return NULL; - } + 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; + } -private: -} g_ScriptNetPropManager; +#define SetVarInfo()\ + varinfo_t *pInfo = CacheNew( pEnt, szProp );\ + pInfo->isNotNetworked = 1;\ + pInfo->elemsize = pField->fieldSizeInBytes / pField->fieldSize;\ + pInfo->arraysize = pField->fieldSize;\ + pInfo->offset = offset; -BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SINGLETON "Allows reading and updating the network properties of an entity." ) - DEFINE_SCRIPTFUNC( GetPropArraySize, "Returns the size of an netprop array, or -1." ) - DEFINE_SCRIPTFUNC( GetPropEntity, "Reads an EHANDLE valued netprop (21 bit integer). Returns the script handle of the entity." ) - DEFINE_SCRIPTFUNC( GetPropEntityArray, "Reads an EHANDLE valued netprop (21 bit integer) from an array. Returns the script handle of the entity." ) - DEFINE_SCRIPTFUNC( GetPropFloat, "Reads a float valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropFloatArray, "Reads a float valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropInt, "Reads an integer valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropIntArray, "Reads an integer valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropString, "Reads a string valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropStringArray, "Reads a string valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropVector, "Reads a 3D vector valued netprop." ) - DEFINE_SCRIPTFUNC( GetPropVectorArray, "Reads a 3D vector valued netprop from an array." ) - DEFINE_SCRIPTFUNC( GetPropType, "Returns the name of the netprop type as a string." ) - DEFINE_SCRIPTFUNC( HasProp, "Checks if a netprop exists." ) - DEFINE_SCRIPTFUNC( SetPropEntity, "Sets an EHANDLE valued netprop (21 bit integer) to reference the specified entity." ) - DEFINE_SCRIPTFUNC( SetPropEntityArray, "Sets an EHANDLE valued netprop (21 bit integer) from an array to reference the specified entity." ) - DEFINE_SCRIPTFUNC( SetPropFloat, "Sets a netprop to the specified float." ) - DEFINE_SCRIPTFUNC( SetPropFloatArray, "Sets a netprop from an array to the specified float." ) - DEFINE_SCRIPTFUNC( SetPropInt, "Sets a netprop to the specified integer." ) - DEFINE_SCRIPTFUNC( SetPropIntArray, "Sets a netprop from an array to the specified integer." ) - DEFINE_SCRIPTFUNC( SetPropString, "Sets a netprop to the specified string." ) - DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a netprop from an array to the specified string." ) - DEFINE_SCRIPTFUNC( SetPropVector, "Sets a netprop to the specified vector." ) - DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a netprop from an array to the specified vector." ) + 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 ""; + } + + 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; + } + + if ( pInfo->datatype == types::_VEC3 ) + index /= 3; + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return -1; + + switch ( pInfo->datatype ) + { + case types::_VEC3: + case types::_FLOAT: + return *(float*)((char*)pEnt + pInfo->GetOffset( index )); +#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; + } + + if ( pInfo->datatype == types::_VEC3 ) + index /= 3; + + if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + return; + + switch ( pInfo->datatype ) + { + case types::_VEC3: + case types::_FLOAT: + *(float*)((char*)pEnt + pInfo->GetOffset( index )) = value; + NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); + 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 )\ + {\ + return 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( Vector, Vector ); + SetProp( 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( " 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( "", 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( "\n" ); + Print( "(%s)\n", GetNetTable( GetNetworkClass(pEnt) )->GetName() ); + DumpNetTable_r( pEnt, GetNetTable( GetNetworkClass(pEnt) ) ); + Print( "\n\n" ); + + Print( "\n" ); + Print( "(%s)\n", pEnt->GetDataDescMap()->dataClassName ); + DumpDataFields_r( pEnt, pEnt->GetDataDescMap() ); + Print( "\n\n" ); +#ifdef CLIENT_DLL + Print( "\n" ); + Print( "(%s)\n", pEnt->GetPredDescMap()->dataClassName ); + DumpDataFields_r( pEnt, pEnt->GetPredDescMap() ); + Print( "\n\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(); //============================================================================= From db0e0fc29e57e4384cd122e0c2215f7e49219a89 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Wed, 5 Oct 2022 00:06:38 -0400 Subject: [PATCH 002/103] SiN: Episodes compatible VGUI Screens --- sp/src/game/client/c_baseplayer.cpp | 9 ++ sp/src/game/client/c_vguiscreen.cpp | 129 ++++++++++++++++++++------ sp/src/game/client/c_vguiscreen.h | 4 + sp/src/game/server/baseentity.h | 8 ++ sp/src/game/server/hl2/hl2_player.cpp | 14 +++ sp/src/game/server/vguiscreen.cpp | 110 +++++++++++++++++++--- sp/src/game/server/vguiscreen.h | 13 +++ sp/src/game/shared/gamerules.cpp | 25 +++++ sp/src/game/shared/gamerules.h | 7 +- sp/src/game/shared/in_buttons.h | 4 + 10 files changed, 282 insertions(+), 41 deletions(-) diff --git a/sp/src/game/client/c_baseplayer.cpp b/sp/src/game/client/c_baseplayer.cpp index 6e06834155d..5a21e16e479 100644 --- a/sp/src/game/client/c_baseplayer.cpp +++ b/sp/src/game/client/c_baseplayer.cpp @@ -1133,6 +1133,9 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2 | IN_VALIDVGUIINPUT); +#ifdef MAPBASE + pCmd->buttons |= IN_VGUIMODE; +#endif // MAPBASE return; } #else @@ -1142,6 +1145,9 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); +#ifdef MAPBASE + pCmd->buttons = (pCmd->buttons & ~IN_USE) | IN_VGUIMODE; +#endif // MAPBASE return; } #endif @@ -1206,6 +1212,9 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); +#ifdef MAPBASE + pCmd->buttons = (pCmd->buttons & ~IN_USE) | IN_VGUIMODE; +#endif // MAPBASE } } diff --git a/sp/src/game/client/c_vguiscreen.cpp b/sp/src/game/client/c_vguiscreen.cpp index adf8830c22c..3dc9cfd945f 100644 --- a/sp/src/game/client/c_vguiscreen.cpp +++ b/sp/src/game/client/c_vguiscreen.cpp @@ -38,10 +38,14 @@ extern vgui::IInputInternal *g_InputInternal; #define VGUI_SCREEN_MODE_RADIUS 80 //Precache the materials -CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectVGuiScreen ) -CLIENTEFFECT_MATERIAL( "engine/writez" ) +CLIENTEFFECT_REGISTER_BEGIN(PrecacheEffectVGuiScreen) +CLIENTEFFECT_MATERIAL("engine/writez") CLIENTEFFECT_REGISTER_END() +#ifdef MAPBASE +C_EntityClassList g_VGUIScreenList; +template <> C_VGuiScreen* C_EntityClassList::m_pClassList = NULL; +#endif // MAPBASE // ----------------------------------------------------------------------------- // // This is a cache of preloaded keyvalues. @@ -102,11 +106,19 @@ C_VGuiScreen::C_VGuiScreen() m_WriteZMaterial.Init( "engine/writez", TEXTURE_GROUP_VGUI ); m_OverlayMaterial.Init( m_WriteZMaterial ); + +#ifdef MAPBASE + g_VGUIScreenList.Insert(this); +#endif // MAPBASE } C_VGuiScreen::~C_VGuiScreen() { DestroyVguiScreen(); + +#ifdef MAPBASE + g_VGUIScreenList.Remove(this); +#endif // MAPBASE } //----------------------------------------------------------------------------- @@ -416,35 +428,70 @@ void C_VGuiScreen::ClientThink( void ) int px = (int)(u * m_nPixelWidth + 0.5f); int py = (int)(v * m_nPixelHeight + 0.5f); +#ifndef MAPBASE // Generate mouse input commands if ((px != m_nOldPx) || (py != m_nOldPy)) { - g_InputInternal->InternalCursorMoved( px, py ); + g_InputInternal->InternalCursorMoved(px, py); + m_nOldPx = px; m_nOldPy = py; } if (m_nButtonPressed & IN_ATTACK) { - g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_PRESSED ); + g_InputInternal->SetMouseCodeState(MOUSE_LEFT, vgui::BUTTON_PRESSED); g_InputInternal->InternalMousePressed(MOUSE_LEFT); } if (m_nButtonPressed & IN_ATTACK2) { - g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_PRESSED ); - g_InputInternal->InternalMousePressed( MOUSE_RIGHT ); + g_InputInternal->SetMouseCodeState(MOUSE_RIGHT, vgui::BUTTON_PRESSED); + g_InputInternal->InternalMousePressed(MOUSE_RIGHT); } - if ( (m_nButtonReleased & IN_ATTACK) || m_bLoseThinkNextFrame) // for a button release on loosing focus + if ((m_nButtonReleased & IN_ATTACK) || m_bLoseThinkNextFrame) // for a button release on loosing focus { - g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_RELEASED ); - g_InputInternal->InternalMouseReleased( MOUSE_LEFT ); + g_InputInternal->SetMouseCodeState(MOUSE_LEFT, vgui::BUTTON_RELEASED); + g_InputInternal->InternalMouseReleased(MOUSE_LEFT); } if (m_nButtonReleased & IN_ATTACK2) { - g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_RELEASED ); - g_InputInternal->InternalMouseReleased( MOUSE_RIGHT ); + g_InputInternal->SetMouseCodeState(MOUSE_RIGHT, vgui::BUTTON_RELEASED); + g_InputInternal->InternalMouseReleased(MOUSE_RIGHT); + } +#else + vgui::VPANEL focus = g_InputInternal->GetMouseOver(); + // Generate mouse input commands + if ((px != m_nOldPx) || (py != m_nOldPy)) + { + g_InputInternal->UpdateCursorPosInternal(px, py); + + m_nOldPx = px; + m_nOldPy = py; + + focus = pPanel->IsWithinTraverse(px, py, true); + g_InputInternal->SetMouseFocus(focus); + vgui::ivgui()->PostMessage(focus, new KeyValues("CursorMoved", "xpos", px, "ypos", py), NULL); } + for (int i = 0; i < 2; i++) + { + const int nBit = i ? IN_ATTACK2 : (IN_ATTACK | IN_USE); + const vgui::MouseCode nButton = i ? MOUSE_RIGHT : MOUSE_LEFT; + + if ((m_nButtonReleased & nBit) || m_bLoseThinkNextFrame) // for a button release on loosing focus + { + g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_PRESSED); + vgui::ivgui()->PostMessage(focus, new KeyValues("MousePressed", "code", nButton), NULL); + } + else if (m_nButtonPressed & nBit) + { + g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_RELEASED); + vgui::ivgui()->PostMessage(focus, new KeyValues("MouseReleased", "code", nButton), NULL); + } + } +#endif // !MAPBASE + + if ( m_bLoseThinkNextFrame == true ) { m_bLoseThinkNextFrame = false; @@ -627,6 +674,7 @@ bool C_VGuiScreen::IsInputOnlyToOwner( void ) return (m_fScreenFlags & VGUI_SCREEN_ONLY_USABLE_BY_OWNER) != 0; } +#ifndef MAPBASE //----------------------------------------------------------------------------- // // Enumator class for finding vgui screens close to the local player @@ -634,29 +682,29 @@ bool C_VGuiScreen::IsInputOnlyToOwner( void ) //----------------------------------------------------------------------------- class CVGuiScreenEnumerator : public IPartitionEnumerator { - DECLARE_CLASS_GAMEROOT( CVGuiScreenEnumerator, IPartitionEnumerator ); + DECLARE_CLASS_GAMEROOT(CVGuiScreenEnumerator, IPartitionEnumerator); public: - virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); + virtual IterationRetval_t EnumElement(IHandleEntity* pHandleEntity); int GetScreenCount(); - C_VGuiScreen *GetVGuiScreen( int index ); + C_VGuiScreen* GetVGuiScreen(int index); private: CUtlVector< CHandle< C_VGuiScreen > > m_VguiScreens; }; -IterationRetval_t CVGuiScreenEnumerator::EnumElement( IHandleEntity *pHandleEntity ) +IterationRetval_t CVGuiScreenEnumerator::EnumElement(IHandleEntity* pHandleEntity) { - C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() ); - if ( pEnt == NULL ) + C_BaseEntity* pEnt = ClientEntityList().GetBaseEntityFromHandle(pHandleEntity->GetRefEHandle()); + if (pEnt == NULL) return ITERATION_CONTINUE; // FIXME.. pretty expensive... - C_VGuiScreen *pScreen = dynamic_cast(pEnt); - if ( pScreen ) + C_VGuiScreen* pScreen = dynamic_cast(pEnt); + if (pScreen) { - int i = m_VguiScreens.AddToTail( ); - m_VguiScreens[i].Set( pScreen ); + int i = m_VguiScreens.AddToTail(); + m_VguiScreens[i].Set(pScreen); } return ITERATION_CONTINUE; @@ -667,10 +715,12 @@ int CVGuiScreenEnumerator::GetScreenCount() return m_VguiScreens.Count(); } -C_VGuiScreen *CVGuiScreenEnumerator::GetVGuiScreen( int index ) +C_VGuiScreen* CVGuiScreenEnumerator::GetVGuiScreen(int index) { return m_VguiScreens[index].Get(); -} +} +#endif // !MAPBASE + //----------------------------------------------------------------------------- @@ -704,18 +754,29 @@ C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &vi Ray_t lookRay; lookRay.Init( viewPosition, lookEnd ); +#ifndef MAPBASE // Look for vgui screens that are close to the player CVGuiScreenEnumerator localScreens; - partition->EnumerateElementsInSphere( PARTITION_CLIENT_NON_STATIC_EDICTS, viewPosition, VGUI_SCREEN_MODE_RADIUS, false, &localScreens ); + partition->EnumerateElementsInSphere(PARTITION_CLIENT_NON_STATIC_EDICTS, viewPosition, VGUI_SCREEN_MODE_RADIUS, false, &localScreens); +#endif // !MAPBASE Vector vecOut, vecViewDelta; float flBestDist = 2.0f; C_VGuiScreen *pBestScreen = NULL; +#ifndef MAPBASE for (int i = localScreens.GetScreenCount(); --i >= 0; ) +#else + for (C_VGuiScreen* pScreen = g_VGUIScreenList.m_pClassList; pScreen != NULL; pScreen = pScreen->m_pNext) +#endif // !MAPBASE { - C_VGuiScreen *pScreen = localScreens.GetVGuiScreen(i); - +#ifndef MAPBASE + C_VGuiScreen* pScreen = localScreens.GetVGuiScreen(i); +#else + // Skip if out of PVS + if (pScreen->IsDormant()) + continue; +#endif if ( pScreen->IsAttachedToViewModel() ) continue; @@ -865,11 +926,21 @@ vgui::Panel *CVGuiScreenPanel::CreateControlByName(const char *controlName) //----------------------------------------------------------------------------- // Purpose: Called when the user presses a button //----------------------------------------------------------------------------- -void CVGuiScreenPanel::OnCommand( const char *command) +void CVGuiScreenPanel::OnCommand(const char* command) { - if ( Q_stricmp( command, "vguicancel" ) ) + if (Q_stricmp(command, "vguicancel")) { - engine->ClientCmd( const_cast( command ) ); +#ifdef MAPBASE + if (m_hEntity && m_hEntity->IsServerEntity()) + { + KeyValues* pCommand = new KeyValues("EntityCommand"); + pCommand->SetInt("entindex", m_hEntity->index); + pCommand->SetString("command_data", command); + engine->ServerCmdKeyValues(pCommand); + } + else +#endif + engine->ClientCmd(const_cast(command)); } BaseClass::OnCommand(command); diff --git a/sp/src/game/client/c_vguiscreen.h b/sp/src/game/client/c_vguiscreen.h index 71780093c5b..331fbc78627 100644 --- a/sp/src/game/client/c_vguiscreen.h +++ b/sp/src/game/client/c_vguiscreen.h @@ -66,6 +66,10 @@ class C_VGuiScreen : public C_BaseEntity public: DECLARE_CLIENTCLASS(); +#ifdef MAPBASE + C_VGuiScreen* m_pNext; +#endif // MAPBASE + C_VGuiScreen(); ~C_VGuiScreen(); diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index acbffdb2cb3..3da44bc7fda 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -606,6 +606,9 @@ class CBaseEntity : public IServerEntity void ValidateEntityConnections(); void FireNamedOutput( const char *pszOutput, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller, float flDelay = 0.0f ); +#ifdef MAPBASE + virtual +#endif CBaseEntityOutput *FindNamedOutput( const char *pszOutput ); #ifdef MAPBASE_VSCRIPT void ScriptFireOutput( const char *pszOutput, HSCRIPT hActivator, HSCRIPT hCaller, const char *szValue, float flDelay ); @@ -864,6 +867,11 @@ class CBaseEntity : public IServerEntity void SetAIWalkable( bool bBlocksLOS ); bool IsAIWalkable( void ); + +#ifdef MAPBASE + virtual void HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) {} +#endif // MAPBASE + private: int SaveDataDescBlock( ISave &save, datamap_t *dmap ); int RestoreDataDescBlock( IRestore &restore, datamap_t *dmap ); diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index cefdeeb41ae..b99f05b25e3 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -3617,6 +3617,20 @@ void CHL2_Player::UpdateWeaponPosture( void ) { m_LowerWeaponTimer.Set( .3 ); VPROF( "CHL2_Player::UpdateWeaponPosture-CheckLower" ); + +#ifdef MAPBASE + if (m_nButtons & IN_VGUIMODE) + { + //We're over a friendly, drop our weapon + if (Weapon_Lower() == false) + { + //FIXME: We couldn't lower our weapon! + } + + return; + } +#endif // MAPBASE + Vector vecAim = BaseClass::GetAutoaimVector( AUTOAIM_SCALE_DIRECT_ONLY ); const float CHECK_FRIENDLY_RANGE = 50 * 12; diff --git a/sp/src/game/server/vguiscreen.cpp b/sp/src/game/server/vguiscreen.cpp index e049da22b53..8163131ca95 100644 --- a/sp/src/game/server/vguiscreen.cpp +++ b/sp/src/game/server/vguiscreen.cpp @@ -35,21 +35,21 @@ PRECACHE_REGISTER( vgui_screen ); //----------------------------------------------------------------------------- // Save/load //----------------------------------------------------------------------------- -BEGIN_DATADESC( CVGuiScreen ) +BEGIN_DATADESC(CVGuiScreen) - DEFINE_CUSTOM_FIELD( m_nPanelName, &g_VguiScreenStringOps ), - DEFINE_FIELD( m_nAttachmentIndex, FIELD_INTEGER ), +DEFINE_CUSTOM_FIELD(m_nPanelName, &g_VguiScreenStringOps), +DEFINE_FIELD(m_nAttachmentIndex, FIELD_INTEGER), // DEFINE_FIELD( m_nOverlayMaterial, FIELD_INTEGER ), - DEFINE_FIELD( m_fScreenFlags, FIELD_INTEGER ), - DEFINE_KEYFIELD( m_flWidth, FIELD_FLOAT, "width" ), - DEFINE_KEYFIELD( m_flHeight, FIELD_FLOAT, "height" ), - DEFINE_KEYFIELD( m_strOverlayMaterial, FIELD_STRING, "overlaymaterial" ), - DEFINE_FIELD( m_hPlayerOwner, FIELD_EHANDLE ), +DEFINE_FIELD(m_fScreenFlags, FIELD_INTEGER), +DEFINE_KEYFIELD(m_flWidth, FIELD_FLOAT, "width"), +DEFINE_KEYFIELD(m_flHeight, FIELD_FLOAT, "height"), +DEFINE_KEYFIELD(m_strOverlayMaterial, FIELD_STRING, "overlaymaterial"), +DEFINE_FIELD(m_hPlayerOwner, FIELD_EHANDLE), - DEFINE_INPUTFUNC( FIELD_VOID, "SetActive", InputSetActive ), - DEFINE_INPUTFUNC( FIELD_VOID, "SetInactive", InputSetInactive ), +DEFINE_INPUTFUNC(FIELD_VOID, "SetActive", InputSetActive), +DEFINE_INPUTFUNC(FIELD_VOID, "SetInactive", InputSetInactive), -END_DATADESC() +END_DATADESC(); //----------------------------------------------------------------------------- @@ -75,6 +75,20 @@ bool CVGuiScreen::KeyValue( const char *szKeyName, const char *szValue ) *s = '\0'; } +#ifdef MAPBASE + // Named command outputs + if (szKeyName[0] == '~' && szKeyName[1]) + { + const char* pszOutputName = szKeyName + 1; + int i = m_PanelOutputs.Find(pszOutputName); + if (!m_PanelOutputs.IsValidIndex(i)) + i = m_PanelOutputs.Insert(pszOutputName, new COutputEvent); + + m_PanelOutputs[i]->ParseEventAction(szValue); + return true; + } +#endif // MAPBASE + if ( FStrEq( szKeyName, "panelname" )) { SetPanelName( szValue ); @@ -158,6 +172,80 @@ void CVGuiScreen::OnRestore() BaseClass::OnRestore(); } +#ifdef MAPBASE +CVGuiScreen::~CVGuiScreen() +{ + m_PanelOutputs.PurgeAndDeleteElements(); +} + +int CVGuiScreen::Save(ISave& save) +{ + int status = BaseClass::Save(save); + if (!status) + return 0; + + const int iCount = m_PanelOutputs.Count(); + save.WriteInt(&iCount); + for (int i = 0; i < iCount; i++) + { + CBaseEntityOutput* output = m_PanelOutputs[i]; + const int nElems = output->NumberOfElements(); + save.WriteString(m_PanelOutputs.GetElementName(i)); + save.WriteInt(&nElems); + if (!output->Save(save)) + return 0; + } + + return status; +} + +int CVGuiScreen::Restore(IRestore& restore) +{ + int status = BaseClass::Restore(restore); + if (!status) + return 0; + + const int iCount = restore.ReadInt(); + m_PanelOutputs.EnsureCapacity(iCount); + for (int i = 0; i < iCount; i++) + { + char cName[MAX_KEY]; + restore.ReadString(cName, MAX_KEY, 0); + const int iIndex = m_PanelOutputs.Insert(cName, new COutputEvent); + const int nElems = restore.ReadInt(); + if (!m_PanelOutputs[iIndex]->Restore(restore, nElems)) + return 0; + } + + return status; +} + +void CVGuiScreen::HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) +{ + const int i = m_PanelOutputs.Find(pKeyValues->GetString()); + if (m_PanelOutputs.IsValidIndex(i)) + { + variant_t Val; + Val.Set(FIELD_VOID, NULL); + m_PanelOutputs[i]->FireOutput(Val, pClient, this); + } +} + +CBaseEntityOutput* CVGuiScreen::FindNamedOutput(const char* pszOutput) +{ + if (pszOutput && pszOutput[0] == '~' && pszOutput[1]) + { + const int i = m_PanelOutputs.Find(pszOutput + 1); + if (m_PanelOutputs.IsValidIndex(i)) + return m_PanelOutputs[i]; + + return NULL; + } + + return BaseClass::FindNamedOutput(pszOutput); +} +#endif // MAPBASE + void CVGuiScreen::SetAttachmentIndex( int nIndex ) { m_nAttachmentIndex = nIndex; diff --git a/sp/src/game/server/vguiscreen.h b/sp/src/game/server/vguiscreen.h index cf72091653d..1cb7dc7e6f0 100644 --- a/sp/src/game/server/vguiscreen.h +++ b/sp/src/game/server/vguiscreen.h @@ -32,6 +32,15 @@ class CVGuiScreen : public CBaseEntity virtual void Activate(); virtual void OnRestore(); +#ifdef MAPBASE + ~CVGuiScreen(); + virtual int Save(ISave& save); + virtual int Restore(IRestore& restore); + + virtual void HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues); + virtual CBaseEntityOutput* FindNamedOutput(const char* pszOutput); +#endif // MAPBASE + const char *GetPanelName() const; // Sets the screen size + resolution @@ -75,6 +84,10 @@ class CVGuiScreen : public CBaseEntity CNetworkVar( int, m_fScreenFlags ); CNetworkVar( EHANDLE, m_hPlayerOwner ); +#ifdef MAPBASE + CUtlDict m_PanelOutputs; +#endif // MAPBASE + friend CVGuiScreen *CreateVGuiScreen( const char *pScreenClassname, const char *pScreenType, CBaseEntity *pAttachedTo, CBaseEntity *pOwner, int nAttachmentIndex ); }; diff --git a/sp/src/game/shared/gamerules.cpp b/sp/src/game/shared/gamerules.cpp index cb199617179..face9d54c8d 100644 --- a/sp/src/game/shared/gamerules.cpp +++ b/sp/src/game/shared/gamerules.cpp @@ -963,3 +963,28 @@ CTacticalMissionManager *CGameRules::TacticalMissionManagerFactory( void ) } #endif + +#ifdef MAPBASE +void CGameRules::ClientCommandKeyValues(edict_t* pEntity, KeyValues* pKeyValues) +{ +#ifndef CLIENT_DLL + static int s_nEntityCommandSymbol = KeyValues::CallGetSymbolForString("EntityCommand"); + static int s_nEntIndexSymbol = KeyValues::CallGetSymbolForString("entindex"); + static int s_nCommandDataSymbol = KeyValues::CallGetSymbolForString("command_data"); + + CBasePlayer* pPlayer = (CBasePlayer*)GetContainingEntity(pEntity); + if (!pPlayer) + return; + + if (pKeyValues->GetNameSymbol() == s_nEntityCommandSymbol) + { + CBaseEntity* pEntity = CBaseEntity::Instance(pKeyValues->GetInt(s_nEntIndexSymbol)); + KeyValues* pkvCommand = pKeyValues->FindKey(s_nCommandDataSymbol); + if (pEntity && pkvCommand) + { + pEntity->HandleEntityCommand(pPlayer, pkvCommand); + } + } +#endif // GAME_DLL +} +#endif // MAPBASE diff --git a/sp/src/game/shared/gamerules.h b/sp/src/game/shared/gamerules.h index 9ae672b6ba2..a60f2771904 100644 --- a/sp/src/game/shared/gamerules.h +++ b/sp/src/game/shared/gamerules.h @@ -178,7 +178,12 @@ abstract_class CGameRules : public CAutoGameSystemPerFrame //Allow thirdperson camera. virtual bool AllowThirdPersonCamera( void ) { return false; } - virtual void ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues ) {} +#ifdef MAPBASE + virtual void ClientCommandKeyValues(edict_t* pEntity, KeyValues* pKeyValues); +#else + virtual void ClientCommandKeyValues(edict_t* pEntity, KeyValues* pKeyValues) {} +#endif // MAPBASE + // IsConnectedUserInfoChangeAllowed allows the clients to change // cvars with the FCVAR_NOT_CONNECTED rule if it returns true diff --git a/sp/src/game/shared/in_buttons.h b/sp/src/game/shared/in_buttons.h index 870961f8fab..74e2ca5faa5 100644 --- a/sp/src/game/shared/in_buttons.h +++ b/sp/src/game/shared/in_buttons.h @@ -43,6 +43,10 @@ #define IN_GRENADE2 (1 << 24) // grenade 2 #define IN_ATTACK3 (1 << 25) +#ifdef MAPBASE +#define IN_VGUIMODE (1 << 26) +#endif // MAPBASE + #ifdef VGUI_SCREEN_FIX #define IN_VALIDVGUIINPUT (1 << 23) //bitflag for vgui fix #endif From e304b1a90eb29ec47a40aa6a144acb2759b97ded Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Tue, 1 Aug 2023 17:46:29 -0400 Subject: [PATCH 003/103] Fixed C_VGuiScreen sending reversed pressed/unpressed events to the panel --- sp/src/game/client/c_vguiscreen.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sp/src/game/client/c_vguiscreen.cpp b/sp/src/game/client/c_vguiscreen.cpp index 3dc9cfd945f..c2c177236e8 100644 --- a/sp/src/game/client/c_vguiscreen.cpp +++ b/sp/src/game/client/c_vguiscreen.cpp @@ -480,13 +480,13 @@ void C_VGuiScreen::ClientThink( void ) if ((m_nButtonReleased & nBit) || m_bLoseThinkNextFrame) // for a button release on loosing focus { - g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_PRESSED); - vgui::ivgui()->PostMessage(focus, new KeyValues("MousePressed", "code", nButton), NULL); + g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_RELEASED); + vgui::ivgui()->PostMessage(focus, new KeyValues("MouseReleased", "code", nButton), NULL); } else if (m_nButtonPressed & nBit) { - g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_RELEASED); - vgui::ivgui()->PostMessage(focus, new KeyValues("MouseReleased", "code", nButton), NULL); + g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_PRESSED); + vgui::ivgui()->PostMessage(focus, new KeyValues("MousePressed", "code", nButton), NULL); } } #endif // !MAPBASE From 146df3c961b0cc7c36857045792cea756a630ed7 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Wed, 2 Aug 2023 16:48:00 -0400 Subject: [PATCH 004/103] Also suppress attack3 in vgui screen mode --- sp/src/game/client/c_baseplayer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sp/src/game/client/c_baseplayer.cpp b/sp/src/game/client/c_baseplayer.cpp index 5a21e16e479..73d593c5882 100644 --- a/sp/src/game/client/c_baseplayer.cpp +++ b/sp/src/game/client/c_baseplayer.cpp @@ -1146,7 +1146,8 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); #ifdef MAPBASE - pCmd->buttons = (pCmd->buttons & ~IN_USE) | IN_VGUIMODE; + pCmd->buttons &= ~(IN_USE | IN_ATTACK3); + pCmd->buttons |= IN_VGUIMODE; #endif // MAPBASE return; } @@ -1213,7 +1214,8 @@ void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd ) // Kill all attack inputs if we're in vgui screen mode pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2); #ifdef MAPBASE - pCmd->buttons = (pCmd->buttons & ~IN_USE) | IN_VGUIMODE; + pCmd->buttons &= ~(IN_USE | IN_ATTACK3); + pCmd->buttons |= IN_VGUIMODE; #endif // MAPBASE } } From eb46739ab57027e52674f640b4a4c6acf4480edf Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Sat, 5 Aug 2023 18:38:04 -0400 Subject: [PATCH 005/103] Fixed vgui screens potentially crashing on save --- sp/src/game/server/vguiscreen.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/vguiscreen.cpp b/sp/src/game/server/vguiscreen.cpp index 8163131ca95..e26e08cc810 100644 --- a/sp/src/game/server/vguiscreen.cpp +++ b/sp/src/game/server/vguiscreen.cpp @@ -82,7 +82,11 @@ bool CVGuiScreen::KeyValue( const char *szKeyName, const char *szValue ) const char* pszOutputName = szKeyName + 1; int i = m_PanelOutputs.Find(pszOutputName); if (!m_PanelOutputs.IsValidIndex(i)) - i = m_PanelOutputs.Insert(pszOutputName, new COutputEvent); + { + auto pMem = new COutputEvent; + V_memset(pMem, 0, sizeof(COutputEvent)); + i = m_PanelOutputs.Insert(pszOutputName, pMem); + } m_PanelOutputs[i]->ParseEventAction(szValue); return true; From 9f34a64e636e7d32b8b25ab06cf2df5de1ab5d21 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Fri, 29 Dec 2023 14:08:13 -0500 Subject: [PATCH 006/103] Vgui screens now try to pass panel commands to the entity that created them --- sp/src/game/server/baseentity.h | 4 +++- sp/src/game/server/vguiscreen.cpp | 17 ++++++++++++++++- sp/src/game/server/vguiscreen.h | 2 +- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 3da44bc7fda..7cd4670c281 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -869,7 +869,9 @@ class CBaseEntity : public IServerEntity bool IsAIWalkable( void ); #ifdef MAPBASE - virtual void HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) {} + // Handle a potentially complex command from a client. + // Returns true if the command was handled successfully. + virtual bool HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) { return false; } #endif // MAPBASE private: diff --git a/sp/src/game/server/vguiscreen.cpp b/sp/src/game/server/vguiscreen.cpp index e26e08cc810..867953429c3 100644 --- a/sp/src/game/server/vguiscreen.cpp +++ b/sp/src/game/server/vguiscreen.cpp @@ -224,15 +224,30 @@ int CVGuiScreen::Restore(IRestore& restore) return status; } -void CVGuiScreen::HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) +// Handle a command from the client-side vgui panel. +bool CVGuiScreen::HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues) { +#if defined(HL2MP) // Enable this in multiplayer. + // Restrict to commands from our owning player. + if ((m_fScreenFlags & VGUI_SCREEN_ONLY_USABLE_BY_OWNER) && pClient != m_hPlayerOwner.Get()) + return false; +#endif + + // Give the owning entity a chance to handle the command. + if (GetOwnerEntity() && GetOwnerEntity()->HandleEntityCommand(pClient, pKeyValues)) + return true; + + // See if we have an output for this command. const int i = m_PanelOutputs.Find(pKeyValues->GetString()); if (m_PanelOutputs.IsValidIndex(i)) { variant_t Val; Val.Set(FIELD_VOID, NULL); m_PanelOutputs[i]->FireOutput(Val, pClient, this); + return true; } + + return false; } CBaseEntityOutput* CVGuiScreen::FindNamedOutput(const char* pszOutput) diff --git a/sp/src/game/server/vguiscreen.h b/sp/src/game/server/vguiscreen.h index 1cb7dc7e6f0..557cacd7977 100644 --- a/sp/src/game/server/vguiscreen.h +++ b/sp/src/game/server/vguiscreen.h @@ -37,7 +37,7 @@ class CVGuiScreen : public CBaseEntity virtual int Save(ISave& save); virtual int Restore(IRestore& restore); - virtual void HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues); + virtual bool HandleEntityCommand(CBasePlayer* pClient, KeyValues* pKeyValues); virtual CBaseEntityOutput* FindNamedOutput(const char* pszOutput); #endif // MAPBASE From 9e2bdaab588a1ec8e7a9c96ac4deaccd45c5fc11 Mon Sep 17 00:00:00 2001 From: Peter Covington Date: Fri, 29 Dec 2023 21:15:23 -0500 Subject: [PATCH 007/103] Fixed vgui_screens always sending a mouse released event to the panel when the vgui_screen loses focus --- sp/src/game/client/c_vguiscreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/client/c_vguiscreen.cpp b/sp/src/game/client/c_vguiscreen.cpp index c2c177236e8..7695afd848e 100644 --- a/sp/src/game/client/c_vguiscreen.cpp +++ b/sp/src/game/client/c_vguiscreen.cpp @@ -478,7 +478,7 @@ void C_VGuiScreen::ClientThink( void ) const int nBit = i ? IN_ATTACK2 : (IN_ATTACK | IN_USE); const vgui::MouseCode nButton = i ? MOUSE_RIGHT : MOUSE_LEFT; - if ((m_nButtonReleased & nBit) || m_bLoseThinkNextFrame) // for a button release on loosing focus + if ((m_nButtonReleased & nBit) || ((m_nButtonState & nBit) && m_bLoseThinkNextFrame)) // for a button release on loosing focus { g_InputInternal->SetMouseCodeState(nButton, vgui::BUTTON_RELEASED); vgui::ivgui()->PostMessage(focus, new KeyValues("MouseReleased", "code", nButton), NULL); From 02e232babf80b12ba7db102b39a659c584658b66 Mon Sep 17 00:00:00 2001 From: rlen Date: Tue, 2 Jan 2024 01:39:12 +0900 Subject: [PATCH 008/103] add "OnPhysGunPull" output to CPhysicsProp --- sp/src/game/server/hl2/weapon_physcannon.cpp | 5 +++++ sp/src/game/server/props.cpp | 8 ++++++++ sp/src/game/server/props.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/sp/src/game/server/hl2/weapon_physcannon.cpp b/sp/src/game/server/hl2/weapon_physcannon.cpp index 66991e45e6f..19ebb8c3bb1 100644 --- a/sp/src/game/server/hl2/weapon_physcannon.cpp +++ b/sp/src/game/server/hl2/weapon_physcannon.cpp @@ -2723,6 +2723,11 @@ CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void ) pullDir *= (mass + 0.5) * (1/50.0f); } + CPhysicsProp* pProp = dynamic_cast(pObj); + if (pProp) { + pProp->OnPhysGunPull( pOwner, pullDir ); + } + // Nudge it towards us pObj->ApplyForceCenter( pullDir ); return OBJECT_NOT_FOUND; diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index 5ad4c11a3c9..d7fcf599026 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -3021,6 +3021,7 @@ BEGIN_DATADESC( CPhysicsProp ) DEFINE_OUTPUT( m_MotionEnabled, "OnMotionEnabled" ), DEFINE_OUTPUT( m_OnPhysGunPickup, "OnPhysGunPickup" ), DEFINE_OUTPUT( m_OnPhysGunOnlyPickup, "OnPhysGunOnlyPickup" ), + DEFINE_OUTPUT( m_OnPhysGunPull, "OnPhysGunPull" ), DEFINE_OUTPUT( m_OnPhysGunPunt, "OnPhysGunPunt" ), DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ), DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ), @@ -3391,6 +3392,13 @@ void CPhysicsProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r CheckRemoveRagdolls(); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPhysicsProp::OnPhysGunPull( CBasePlayer* pPhysGunUser, Vector pullDir ) { + m_OnPhysGunPull.FireOutput(pPhysGunUser, this); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/props.h b/sp/src/game/server/props.h index ebd87c4da11..c36f11bbd20 100644 --- a/sp/src/game/server/props.h +++ b/sp/src/game/server/props.h @@ -413,6 +413,7 @@ class CPhysicsProp : public CBreakableProp void EnableMotion( void ); bool CanBePickedUpByPhyscannon( void ); void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); + void OnPhysGunPull( CBasePlayer *pPhysGunUser, Vector pullDir ); void OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason ); bool GetPropDataAngles( const char *pKeyName, QAngle &vecAngles ); @@ -446,6 +447,7 @@ class CPhysicsProp : public CBreakableProp COutputEvent m_OnPhysGunPickup; COutputEvent m_OnPhysGunPunt; COutputEvent m_OnPhysGunOnlyPickup; + COutputEvent m_OnPhysGunPull; COutputEvent m_OnPhysGunDrop; COutputEvent m_OnPlayerUse; COutputEvent m_OnPlayerPickup; From f484aa7ebc8b6cdf5da41040456e7622fca0778d Mon Sep 17 00:00:00 2001 From: rlen Date: Mon, 19 Feb 2024 21:46:04 +0900 Subject: [PATCH 009/103] removed `pullDir` parameter from `OnPhysGunPull` --- sp/src/game/server/hl2/weapon_physcannon.cpp | 2 +- sp/src/game/server/props.cpp | 2 +- sp/src/game/server/props.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/weapon_physcannon.cpp b/sp/src/game/server/hl2/weapon_physcannon.cpp index 19ebb8c3bb1..8a246a2d566 100644 --- a/sp/src/game/server/hl2/weapon_physcannon.cpp +++ b/sp/src/game/server/hl2/weapon_physcannon.cpp @@ -2725,7 +2725,7 @@ CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void ) CPhysicsProp* pProp = dynamic_cast(pObj); if (pProp) { - pProp->OnPhysGunPull( pOwner, pullDir ); + pProp->OnPhysGunPull( pOwner ); } // Nudge it towards us diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index d7fcf599026..33aba2638f9 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -3395,7 +3395,7 @@ void CPhysicsProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CPhysicsProp::OnPhysGunPull( CBasePlayer* pPhysGunUser, Vector pullDir ) { +void CPhysicsProp::OnPhysGunPull( CBasePlayer* pPhysGunUser ) { m_OnPhysGunPull.FireOutput(pPhysGunUser, this); } diff --git a/sp/src/game/server/props.h b/sp/src/game/server/props.h index c36f11bbd20..5c57824daae 100644 --- a/sp/src/game/server/props.h +++ b/sp/src/game/server/props.h @@ -413,7 +413,7 @@ class CPhysicsProp : public CBreakableProp void EnableMotion( void ); bool CanBePickedUpByPhyscannon( void ); void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); - void OnPhysGunPull( CBasePlayer *pPhysGunUser, Vector pullDir ); + void OnPhysGunPull( CBasePlayer *pPhysGunUser ); void OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t reason ); bool GetPropDataAngles( const char *pKeyName, QAngle &vecAngles ); From 471a840ed98c7206237cb579671a6d6fda9fd4f9 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 9 Mar 2024 01:33:36 -0600 Subject: [PATCH 010/103] Fix soundlevel_t compile error on VS2013 --- sp/src/public/soundflags.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sp/src/public/soundflags.h b/sp/src/public/soundflags.h index 9fab2308543..2c978c2bc66 100644 --- a/sp/src/public/soundflags.h +++ b/sp/src/public/soundflags.h @@ -102,6 +102,10 @@ enum soundlevel_t #define MAX_SNDLVL_VALUE ((1< 50) ? (20.0f / (float)(a - 50)) : 4.0 ) +#else inline soundlevel_t ATTN_TO_SNDLVL(float a) { soundlevel_t sndlvl = soundlevel_t::SNDLVL_NONE; @@ -118,6 +122,7 @@ inline float SNDLVL_TO_ATTN(soundlevel_t s) { return (s > soundlevel_t::SNDLVL_50dB)? (20.0f / float(s - soundlevel_t::SNDLVL_50dB)) : 4.0f; } +#endif // This is a limit due to network encoding. // It encodes attenuation * 64 in 8 bits, so the maximum is (255 / 64) From 11e347e5595d4f69d27601e48b32ee126d18b9e3 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 8 Mar 2024 17:32:51 -0600 Subject: [PATCH 011/103] Fix stukabats spawning fully grown stukabats --- sp/src/game/server/ez2/npc_flyingpredator.cpp | 25 ++++++++++++++++--- sp/src/game/server/ez2/npc_flyingpredator.h | 3 +++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/sp/src/game/server/ez2/npc_flyingpredator.cpp b/sp/src/game/server/ez2/npc_flyingpredator.cpp index e7d4fd96dcc..6f77aa45a31 100644 --- a/sp/src/game/server/ez2/npc_flyingpredator.cpp +++ b/sp/src/game/server/ez2/npc_flyingpredator.cpp @@ -176,6 +176,9 @@ BEGIN_DATADESC( CNPC_FlyingPredator ) DEFINE_FIELD( m_vDesiredTarget, FIELD_VECTOR ), DEFINE_FIELD( m_vCurrentTarget, FIELD_VECTOR ), + DEFINE_KEYFIELD( m_AdultModelName, FIELD_MODELNAME, "adultmodel" ), + DEFINE_KEYFIELD( m_BabyModelName, FIELD_MODELNAME, "babymodel" ), + DEFINE_INPUTFUNC( FIELD_STRING, "ForceFlying", InputForceFlying ), DEFINE_INPUTFUNC( FIELD_STRING, "Fly", InputFly ), @@ -188,14 +191,14 @@ void CNPC_FlyingPredator::Spawn() { Precache( ); - SetModel( STRING( GetModelName() ) ); - if (m_bIsBaby) { + SetModel( STRING( m_BabyModelName ) ); SetHullType( HULL_TINY ); } else { + SetModel( STRING( m_AdultModelName ) ); SetHullType( HULL_WIDE_SHORT ); } @@ -259,10 +262,23 @@ void CNPC_FlyingPredator::Precache() if ( GetModelName() == NULL_STRING ) { - SetModelName( AllocPooledString( m_bIsBaby ? "models/stukapup.mdl" : "models/stukabat.mdl" ) ); + SetModelName( AllocPooledString( "models/stukabat.mdl" ) ); + } + + if ( m_AdultModelName == NULL_STRING ) + { + m_AdultModelName = GetModelName(); + } + else + PrecacheModel( STRING( m_AdultModelName ) ); + + if ( m_BabyModelName == NULL_STRING ) + { + m_BabyModelName = AllocPooledString( "models/stukapup.mdl" ); } PrecacheModel( STRING( GetModelName() ) ); + PrecacheModel( STRING( m_BabyModelName ) ); if (m_tEzVariant == EZ_VARIANT_RAD) { @@ -965,7 +981,8 @@ bool CNPC_FlyingPredator::SpawnNPC( const Vector position, bool isBaby ) pChild->m_tWanderState = this->m_tWanderState; pChild->m_bSpawningEnabled = this->m_bSpawningEnabled; pChild->m_bCanUseFlyNav = this->m_bCanUseFlyNav; - pChild->SetModelName( this->GetModelName() ); + pChild->m_BabyModelName = this->m_BabyModelName; + pChild->m_AdultModelName = this->m_AdultModelName; pChild->m_nSkin = this->m_nSkin; pChild->Precache(); diff --git a/sp/src/game/server/ez2/npc_flyingpredator.h b/sp/src/game/server/ez2/npc_flyingpredator.h index 749a6fed789..e5ae16cf2cc 100644 --- a/sp/src/game/server/ez2/npc_flyingpredator.h +++ b/sp/src/game/server/ez2/npc_flyingpredator.h @@ -126,6 +126,9 @@ class CNPC_FlyingPredator : public CNPC_BasePredator Vector m_vDesiredTarget; Vector m_vCurrentTarget; + string_t m_BabyModelName; + string_t m_AdultModelName; + DEFINE_CUSTOM_AI; }; #endif // NPC_FLYINGPREDATOR_H \ No newline at end of file From 4d3a951e05dd650b9cdfb09d5e9ffdd836fc7b7a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 8 Mar 2024 18:28:22 -0600 Subject: [PATCH 012/103] Bullsquids precache adult, baby, and egg models --- sp/src/game/server/ez2/npc_bullsquid.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sp/src/game/server/ez2/npc_bullsquid.cpp b/sp/src/game/server/ez2/npc_bullsquid.cpp index d9abc186a9c..fa52f27a02d 100644 --- a/sp/src/game/server/ez2/npc_bullsquid.cpp +++ b/sp/src/game/server/ez2/npc_bullsquid.cpp @@ -157,6 +157,9 @@ void CNPC_Bullsquid::Precache() { m_AdultModelName = GetModelName(); } + else + PrecacheModel( STRING( m_AdultModelName ) ); + // If there is no baby model name, use the same model as regular if ( m_BabyModelName == NULL_STRING ) { @@ -192,6 +195,8 @@ void CNPC_Bullsquid::Precache() } PrecacheModel( STRING( GetModelName() ) ); + PrecacheModel( STRING( m_BabyModelName ) ); + PrecacheModel( STRING( m_EggModelName ) ); m_nSquidSpitSprite = PrecacheModel("sprites/greenspit1.vmt");// client side spittle. From 015af6f307a5e23cdefd87d9a49909c95e3f9bfc Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Tue, 26 Mar 2024 02:32:22 -0500 Subject: [PATCH 013/103] Wilson look cameras and omnipresence --- sp/src/game/server/ai_playerally.cpp | 4 + sp/src/game/server/ai_playerally.h | 5 + sp/src/game/server/ez2/npc_wilson.cpp | 298 +++++++++++++++++++++++++- sp/src/game/server/ez2/npc_wilson.h | 61 ++++++ 4 files changed, 366 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index 317a5ac8393..c7111d7a302 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -1619,7 +1619,11 @@ bool CAI_PlayerAlly::IsValidSpeechTarget( int flags, CBaseEntity *pEntity ) //----------------------------------------------------------------------------- CBaseEntity *CAI_PlayerAlly::FindSpeechTarget( int flags ) { +#ifdef EZ2 + const Vector & vAbsOrigin = GetSpeechTargetSearchOrigin(); +#else const Vector & vAbsOrigin = GetAbsOrigin(); +#endif float closestDistSq = FLT_MAX; CBaseEntity * pNearest = NULL; float distSq; diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index d1c4bd896d7..358575ef79c 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -379,6 +379,11 @@ class CAI_PlayerAlly : public CAI_BaseActor CBaseEntity *FindSpeechTarget( int flags ); virtual bool IsValidSpeechTarget( int flags, CBaseEntity *pEntity ); + +#ifdef EZ2 + // Used by Wilson camera targets + virtual const Vector &GetSpeechTargetSearchOrigin() { return GetAbsOrigin(); } +#endif CBaseEntity *GetSpeechTarget() { return m_hTalkTarget.Get(); } void SetSpeechTarget( CBaseEntity *pSpeechTarget ) { m_hTalkTarget = pSpeechTarget; } diff --git a/sp/src/game/server/ez2/npc_wilson.cpp b/sp/src/game/server/ez2/npc_wilson.cpp index 91b0809c012..7a963fb564d 100644 --- a/sp/src/game/server/ez2/npc_wilson.cpp +++ b/sp/src/game/server/ez2/npc_wilson.cpp @@ -128,7 +128,10 @@ BEGIN_DATADESC(CNPC_Wilson) DEFINE_KEYFIELD( m_bDead, FIELD_BOOLEAN, "dead" ), - DEFINE_INPUT( m_bOmniscient, FIELD_BOOLEAN, "SetOmniscient" ), + DEFINE_INPUT( m_bOmniscient, FIELD_BOOLEAN, "SetOmniscient" ), // TODO: Replace with "Omnipresent" when saves can be changed + DEFINE_KEYFIELD( m_iszCameraTargets, FIELD_STRING, "CameraTargets" ), + + DEFINE_KEYFIELD( m_bSeeThroughPlayer, FIELD_BOOLEAN, "SeeThroughPlayer" ), DEFINE_INPUT( m_bCanBeEnemy, FIELD_BOOLEAN, "SetCanBeEnemy" ), @@ -153,6 +156,15 @@ BEGIN_DATADESC(CNPC_Wilson) DEFINE_INPUTFUNC( FIELD_VOID, "TurnOnDeadMode", InputTurnOnDeadMode ), DEFINE_INPUTFUNC( FIELD_VOID, "TurnOffDeadMode", InputTurnOffDeadMode ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetCameraTargets", InputSetCameraTargets ), + DEFINE_INPUTFUNC( FIELD_VOID, "ClearCameraTargets", InputClearCameraTargets ), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableSeeThroughPlayer", InputEnableSeeThroughPlayer ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableSeeThroughPlayer", InputDisableSeeThroughPlayer ), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableOmnipresence", InputEnableOmnipresence ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableOmnipresence", InputDisableOmnipresence ), + DEFINE_THINKFUNC( TeslaThink ), DEFINE_OUTPUT( m_OnTipped, "OnTipped" ), @@ -368,6 +380,42 @@ void CNPC_Wilson::Activate( void ) m_pMotionController->Enable(); } } + + RefreshCameraTargets(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CNPC_Wilson::KeyValue( const char *szKeyName, const char *szValue ) +{ + bool bResult = BaseClass::KeyValue( szKeyName, szValue ); + + if( !bResult ) + { + // Temporary until we can change saves and rename m_bOmniscient + if (FStrEq( szKeyName, "Omnipresent" )) + { + m_bOmniscient = atoi(szValue) != 0 ? true : false; + return true; + } + } + + return bResult; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Wilson::NPCInit( void ) +{ + BaseClass::NPCInit(); + + if (m_bOmniscient) + { + // Infinite look distance needed to see through distant cameras + SetDistLook( 9999999.0f ); + m_flDistTooFar = 9999999.0f; + } } //----------------------------------------------------------------------------- @@ -718,7 +766,7 @@ void CNPC_Wilson::NPCThink() { BaseClass::NPCThink(); - if (HasCondition(COND_IN_PVS)) + if (HasCondition(COND_IN_PVS) && (!m_bOmniscient || HasCondition(COND_SEE_PLAYER))) { // Needed for choreography reasons AimGun(); @@ -1176,6 +1224,123 @@ void CNPC_Wilson::AimGun() } } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::FInViewCone( const Vector &vecSpot ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special eye + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer && pPlayer->FInViewCone( vecSpot ) ) + return true; + } + + if ( m_hCameraTargets.Count() > 0 ) + { + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FInViewCone( vecSpot ) ) + return true; + } + + // None of the cameras saw it + // If we're omniscient, then our actual POV is irrelevant + if (m_bOmniscient) + return false; + } + + return BaseClass::FInViewCone( vecSpot ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special eye + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pEntity == pPlayer || (pPlayer && pPlayer->FVisible( pEntity )) ) + return true; + } + + if ( m_hCameraTargets.Count() > 0 ) + { + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + // Note that this will not use the visibility cache normally used by CBaseCombatCharacters (which is acceptable) + if ( m_hCameraTargets[i]->FVisible( pEntity, traceMask, ppBlocker ) ) + return true; + } + + // None of the cameras saw it + // If we're omniscient, then our actual POV is irrelevant + if (m_bOmniscient) + return false; + } + + return BaseClass::FVisible( pEntity, traceMask, ppBlocker ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::FVisible( const Vector &vecTarget, int traceMask, CBaseEntity **ppBlocker ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special eye + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if ( pPlayer && pPlayer->FVisible( vecTarget ) ) + return true; + } + + if ( m_hCameraTargets.Count() > 0 ) + { + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FVisible( vecTarget, traceMask, ppBlocker ) ) + return true; + } + + // None of the cameras saw it + // If we're omniscient, then our actual POV is irrelevant + if (m_bOmniscient) + return false; + } + + return BaseClass::FVisible( vecTarget, traceMask, ppBlocker ); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the first camera which can see the target +//----------------------------------------------------------------------------- +CWilsonCamera *CNPC_Wilson::GetCameraForTarget( CBaseEntity *pTarget ) +{ + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FInViewCone( pTarget->WorldSpaceCenter() ) && m_hCameraTargets[i]->FVisible( pTarget ) ) + return m_hCameraTargets[i]; + } + + return NULL; +} + //----------------------------------------------------------------------------- // Purpose: Return true if this NPC can hear the specified sound //----------------------------------------------------------------------------- @@ -1747,6 +1912,19 @@ void CNPC_Wilson::ModifyOrAppendCriteria(AI_CriteriaSet& set) set.AppendCriteria( "wilson_distance", "99999999999" ); set.AppendCriteria( "player_in_vehicle", "0" ); } + + if (m_hCameraTargets.Count() > 0 && GetSpeechTarget()) + { + // Check which camera we're looking at our speech target through + CWilsonCamera *pCamera = GetCameraForTarget( GetSpeechTarget() ); + if (pCamera) + { + set.AppendCriteria( "camera", STRING(pCamera->GetEntityName()) ); + pCamera->AppendContextToCriteria( set, "camera_" ); + } + else + set.AppendCriteria( "camera", "0" ); + } } //----------------------------------------------------------------------------- @@ -1890,6 +2068,47 @@ bool CNPC_Wilson::GetGameTextSpeechParams( hudtextparms_t ¶ms ) return true; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Wilson::RefreshCameraTargets() +{ + m_hCameraTargets.RemoveAll(); + + if (m_iszCameraTargets == NULL_STRING) + return; + + CBaseEntity *pCamera = gEntList.FindEntityGeneric( NULL, STRING( m_iszCameraTargets ), this, this, this ); + while (pCamera) + { + if (!FClassnameIs( pCamera, "point_wilson_camera" )) + { + DevWarning( "%s: \"%s\" (%s) is not a point_wilson_camera\n", GetDebugName(), pCamera->GetDebugName(), pCamera->GetClassname() ); + continue; + } + + m_hCameraTargets.AddToTail( static_cast(pCamera) ); + + pCamera = gEntList.FindEntityGeneric( pCamera, STRING( m_iszCameraTargets ), this, this, this ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector &CNPC_Wilson::GetSpeechTargetSearchOrigin() +{ + if ( m_bOmniscient ) + { + // It doesn't matter where we are, so search from the player's origin + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (pPlayer) + return pPlayer->GetAbsOrigin(); + } + + return BaseClass::GetSpeechTargetSearchOrigin(); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -2484,3 +2703,78 @@ void CArbeitScanner::CleanupScan(bool dispatchInteraction) m_pSprite = NULL; } } + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +BEGIN_DATADESC( CWilsonCamera ) + + DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ), + DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ), + DEFINE_KEYFIELD( m_flLookDistSqr, FIELD_FLOAT, "LookDist" ), + DEFINE_KEYFIELD( m_b3DViewCone, FIELD_BOOLEAN, "Use3DViewCone" ), + + DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), + DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetFOV", InputSetFOV ), + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetLookDist", InputSetLookDist ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( point_wilson_camera, CWilsonCamera ); + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CWilsonCamera::CWilsonCamera( void ) +{ + m_bDisabled = false; + m_flFieldOfView = 90.0f; + m_flLookDistSqr = 1024.0f; + m_b3DViewCone = false; +} + +//------------------------------------------------------------------------------ +// Purpose : +// Input : +// Output : +//------------------------------------------------------------------------------ +void CWilsonCamera::Spawn( void ) +{ + BaseClass::Spawn(); + + // Make FOV and look dist ready for visibility calculations + m_flFieldOfView = cos( DEG2RAD(m_flFieldOfView/2) ); + m_flLookDistSqr *= m_flLookDistSqr; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWilsonCamera::FInViewCone( const Vector &vecSpot ) +{ + Vector los = ( vecSpot - EyePosition() ); + + if (los.LengthSqr() > m_flLookDistSqr) + return false; + + Vector facingDir; + GetVectors( &facingDir, NULL, NULL ); + + if (m_b3DViewCone) + { + // do this in 2D + los.z = 0; + VectorNormalize( los ); + + facingDir.z = 0; + facingDir.AsVector2D().NormalizeInPlace(); + } + + float flDot = DotProduct( los, facingDir ); + + if ( flDot > m_flFieldOfView ) + return true; + + return false; +} diff --git a/sp/src/game/server/ez2/npc_wilson.h b/sp/src/game/server/ez2/npc_wilson.h index cd876f33f07..0d3f3e0bf4e 100644 --- a/sp/src/game/server/ez2/npc_wilson.h +++ b/sp/src/game/server/ez2/npc_wilson.h @@ -28,6 +28,8 @@ // So now it's CAI_SensingDummy. typedef CAI_SensingDummy CAI_WilsonBase; +class CWilsonCamera; + //----------------------------------------------------------------------------- // Purpose: Wilson, Willie, Will-E // @@ -53,6 +55,8 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, void Precache(); void Spawn(); void Activate( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void NPCInit( void ); bool CreateVPhysics( void ); void UpdateOnRemove( void ); float MaxYawSpeed( void ){ return 0; } @@ -97,6 +101,12 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, void AimGun(); + bool FInViewCone( CBaseEntity *pEntity ) { return BaseClass::FInViewCone( pEntity ); } + bool FInViewCone( const Vector &vecSpot ); + bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); + bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); + CWilsonCamera* GetCameraForTarget( CBaseEntity *pTarget ); + bool QueryHearSound( CSound *pSound ); // Wilson hardly cares about his NPC state because he's just a vessel for choreography and player attachment, not a useful combat ally. @@ -151,6 +161,9 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, bool IsOmniscient() { return m_bOmniscient; } + void RefreshCameraTargets(); + const Vector& GetSpeechTargetSearchOrigin(); + void InputEnableMotion( inputdata_t &inputdata ); void InputDisableMotion( inputdata_t &inputdata ); @@ -179,6 +192,15 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, void InputTurnOnDeadMode( inputdata_t &inputdata ) { SetPlayingDead( true ); } void InputTurnOffDeadMode( inputdata_t &inputdata ) { SetPlayingDead( false ); } + void InputSetCameraTargets( inputdata_t &inputdata ) { m_iszCameraTargets = inputdata.value.StringID(); RefreshCameraTargets(); } + void InputClearCameraTargets( inputdata_t &inputdata ) { m_iszCameraTargets = NULL_STRING; RefreshCameraTargets(); } + + void InputEnableSeeThroughPlayer( inputdata_t &inputdata ) { m_bSeeThroughPlayer = true; } + void InputDisableSeeThroughPlayer( inputdata_t &inputdata ) { m_bSeeThroughPlayer = false; } + + void InputEnableOmnipresence( inputdata_t &inputdata ) { m_bOmniscient = true; } + void InputDisableOmnipresence( inputdata_t &inputdata ) { m_bOmniscient = false; } + protected: //----------------------------------------------------- @@ -244,8 +266,15 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, // Makes Will-E always available as a speech target, even when out of regular range. // (e.g. Will-E on monitor in ez2_c3_3) + // + // MISNOMER: Actually means "omnipresent". This should be corrected next time we can break saves. bool m_bOmniscient; + string_t m_iszCameraTargets; + CUtlVector> m_hCameraTargets; // Refreshed every restore + + bool m_bSeeThroughPlayer; + // See CNPC_Wilson::CanBeAnEnemyOf(). bool m_bCanBeEnemy; @@ -356,3 +385,35 @@ class CArbeitScanner : public CBaseAnimating int m_iScanAttachment; }; + +//----------------------------------------------------------------------------- +// Purpose: Camera entity that Wilson can use to see outside of his body. +//----------------------------------------------------------------------------- +class CWilsonCamera : public CBaseEntity +{ + DECLARE_CLASS( CWilsonCamera, CBaseEntity ); + DECLARE_DATADESC(); +public: + CWilsonCamera(); + + void Spawn(); + bool FInViewCone( const Vector &vecSpot ); + + inline bool IsEnabled() const { return !m_bDisabled; } + inline float GetFOV() const { return m_flFieldOfView; } + inline float GetLookDistSqr() const { return m_flLookDistSqr; } + inline bool Using3DViewCone() const { return m_b3DViewCone; } + + void InputEnable( inputdata_t &inputdata ) { m_bDisabled = false; } + void InputDisable( inputdata_t &inputdata ) { m_bDisabled = true; } + + void InputSetFOV( inputdata_t &inputdata ) { m_flFieldOfView = cos( DEG2RAD( inputdata.value.Float() / 2 ) ); } + void InputSetLookDist( inputdata_t &inputdata ) { m_flLookDistSqr = Square( inputdata.value.Float() ); } + +private: + + bool m_bDisabled; + float m_flFieldOfView; + float m_flLookDistSqr; + bool m_b3DViewCone; +}; From 6895e6ea8b85220463ade16d2f3de97007ef447d Mon Sep 17 00:00:00 2001 From: Derek <1upderek@gmail.com> Date: Sat, 30 Mar 2024 22:37:27 -0400 Subject: [PATCH 014/103] Stasis grenade: NPCs should not teleport when unfrozen --- sp/src/game/server/ai_playerally.cpp | 32 ++++++++++ sp/src/game/server/ai_playerally.h | 4 ++ .../game/server/episodic/grenade_hopwire.cpp | 60 ++++++++++--------- sp/src/game/server/hl2/npc_metropolice.cpp | 19 ++++++ sp/src/game/server/physics_prop_ragdoll.cpp | 17 ++++++ sp/src/game/server/physics_prop_ragdoll.h | 3 + 6 files changed, 106 insertions(+), 29 deletions(-) diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index c7111d7a302..1f68747dcb0 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -18,6 +18,7 @@ #ifdef EZ2 #include "ez2/ai_concept_response.h" #include "ez2/ez2_player.h" +#include "ai_interactions.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -1342,6 +1343,37 @@ bool CAI_PlayerAlly::CanFlinch( void ) } #endif +#ifdef EZ2 +extern int g_interactionStasisGrenadeFreeze; +extern int g_interactionStasisGrenadeUnfreeze; + +//----------------------------------------------------------------------------- +// Purpose: This is a generic function (to be implemented by sub-classes) to +// handle specific interactions between different types of characters +// (For example the barnacle grabbing an NPC) +// Input : Constant for the type of interaction +// Output : true - if sub-class has a response for the interaction +// false - if sub-class has no response +//----------------------------------------------------------------------------- +bool CAI_PlayerAlly::HandleInteraction(int interactionType, void* data, CBaseCombatCharacter* sourceEnt) +{ + if (interactionType == g_interactionStasisGrenadeFreeze) + { + CapabilitiesRemove(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } + else if (interactionType == g_interactionStasisGrenadeUnfreeze) + { + CapabilitiesAdd(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } + + return BaseClass::HandleInteraction(interactionType, data, sourceEnt); +} +#endif + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_PlayerAlly::OnKilledNPC( CBaseCombatCharacter *pKilled ) diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index 358575ef79c..fa43e3b5f02 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -349,6 +349,10 @@ class CAI_PlayerAlly : public CAI_BaseActor virtual bool CanFlinch( void ); #endif +#ifdef EZ2 + bool HandleInteraction(int interactionType, void* data, CBaseCombatCharacter* sourceEnt); +#endif + //--------------------------------- // Combat //--------------------------------- diff --git a/sp/src/game/server/episodic/grenade_hopwire.cpp b/sp/src/game/server/episodic/grenade_hopwire.cpp index 2f8bd27c2d7..8e54b55c2db 100644 --- a/sp/src/game/server/episodic/grenade_hopwire.cpp +++ b/sp/src/game/server/episodic/grenade_hopwire.cpp @@ -106,6 +106,7 @@ int g_interactionXenGrenadeHop = 0; int g_interactionXenGrenadeRagdoll = 0; int g_interactionStasisGrenadeFreeze = 0; +int g_interactionStasisGrenadeUnfreeze = 0; #endif #ifdef EZ2 @@ -2366,6 +2367,7 @@ void CGrenadeHopwire::Precache( void ) g_interactionXenGrenadeRagdoll = CBaseCombatCharacter::GetInteractionID(); g_interactionStasisGrenadeFreeze = CBaseCombatCharacter::GetInteractionID(); + g_interactionStasisGrenadeUnfreeze = CBaseCombatCharacter::GetInteractionID(); } if (GetHopwireStyle() == HOPWIRE_XEN) @@ -3010,42 +3012,36 @@ void CStasisVortexController::PullThink( void ) // Reset ragdoll pRagdoll = NULL; - // Freeze NPCs by stopping their think function - if (pEnts[i]->MyCombatCharacterPointer()) + // Freeze NPCs + if (pEnts[i]->MyNPCPointer()) { - // If this NPC has already been frozen, don't refreeze - if (pEnts[i]->GetNextThink() == TICK_NEVER_THINK) + // If this NPC is in the freeze schedule, set it's think to the end of the vortex + if (pEnts[i]->MyNPCPointer()->IsCurSchedule(SCHED_NPC_FREEZE, false)) { + pEnts[i]->SetNextThink(MAX(pEnts[i]->GetNextThink(), m_flEndTime - TICK_INTERVAL)); + pEnts[i]->SetAbsVelocity(vec3_origin); continue; } - if (pEnts[i]->MyNPCPointer()) - { - pEnts[i]->MyNPCPointer()->SetCondition( COND_NPC_FREEZE ); - pEnts[i]->MyNPCPointer()->TaskInterrupt(); - } - - pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezeNPCThink, m_flEndTime, "StasisGrenadeUnfreeze" ); + pEnts[i]->MyNPCPointer()->SetCondition(COND_NPC_FREEZE); + pEnts[i]->MyNPCPointer()->TaskInterrupt(); - // Don't cancel the think function until the NPC picks up SCHED_NPC_FREEZE - if (pEnts[i]->MyNPCPointer() && !(pEnts[i]->MyNPCPointer()->IsCurSchedule(SCHED_NPC_FREEZE, false))) - continue; - - pEnts[i]->SetNextThink( TICK_NEVER_THINK ); - DevMsg( "NPC '%s' caught in stasis field! Thinking stopped\n", pEnts[i]->GetDebugName() ); + // Add tick interval to the unfreeze time to make sure that the unfreeze is after the end time + pEnts[i]->SetContextThink(&CStasisVortexController::UnfreezeNPCThink, m_flEndTime + TICK_INTERVAL, "StasisGrenadeUnfreeze"); continue; } if (FClassnameIs( pEnts[i], "prop_ragdoll" )) { - pRagdoll = dynamic_cast< CRagdollProp* >(this); + pRagdoll = dynamic_cast< CRagdollProp* >(pEnts[i]); } - if (pRagdoll) + if (pRagdoll && pRagdoll->IsMotionEnabled()) { pRagdoll->DisableMotion(); - pRagdoll->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime, "StasisGrenadeUnfreeze" ); + // Add tick interval to the unfreeze time to make sure that the unfreeze is after the end time + pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime + TICK_INTERVAL, "StasisGrenadeUnfreeze" ); continue; } @@ -3065,7 +3061,8 @@ void CStasisVortexController::PullThink( void ) pPhysObject->EnableMotion(false); pPhysObject->EnableGravity( false ); - pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime, "StasisGrenadeUnfreeze" ); + // Add tick interval to the unfreeze time to make sure that the unfreeze is after the end time + pEnts[i]->SetContextThink( &CStasisVortexController::UnfreezePhysicsObjectThink, m_flEndTime + TICK_INTERVAL, "StasisGrenadeUnfreeze" ); } // Keep going if need-be @@ -3109,11 +3106,14 @@ void CStasisVortexController::UnfreezeNPCThink( void ) if (MyNPCPointer() == NULL) return; - MyNPCPointer()->TaskInterrupt(); + // Dispatch interaction. Skip unfreezing if it returns true + if (MyNPCPointer()->DispatchInteraction(g_interactionStasisGrenadeUnfreeze, NULL, GetThrower())) + { + return; + } + MyNPCPointer()->ClearCondition( COND_NPC_FREEZE ); MyNPCPointer()->SetCondition( COND_NPC_UNFREEZE ); - MyNPCPointer()->Think(); - SetNextThink( gpGlobals->curtime ); } //----------------------------------------------------------------------------- @@ -3125,15 +3125,17 @@ void CStasisVortexController::UnfreezePhysicsObjectThink( void ) SetContextThink( NULL, TICK_NEVER_THINK, "StasisGrenadeUnfreeze" ); - if (FClassnameIs( this, "prop_ragdoll" )) + if (FClassnameIs( GetBaseEntity(), "prop_ragdoll")) { - pRagdoll = dynamic_cast< CRagdollProp* >(this); + pRagdoll = dynamic_cast< CRagdollProp* >(GetBaseEntity()); } if (pRagdoll) { - inputdata_t dummy; - pRagdoll->InputEnableMotion( dummy ); + inputdata_t ragdollData; + ragdollData.value.SetFloat(0.1f); + pRagdoll->InputEnableMotion(ragdollData); + pRagdoll->InputStartRadgollBoogie(ragdollData); return; } @@ -3159,7 +3161,7 @@ void CStasisVortexController::StartPull( const Vector &origin, float radius, flo m_flStrength= strength; // Play a danger sound throughout the duration of the vortex so that NPCs run away - CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), radius, duration, this ); + CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin(), radius * 1.25f, duration * 1.25f, this ); SetDefLessFunc( m_SpawnList ); m_SpawnList.EnsureCapacity( 16 ); diff --git a/sp/src/game/server/hl2/npc_metropolice.cpp b/sp/src/game/server/hl2/npc_metropolice.cpp index eb53ddcade0..6ad357c8f67 100644 --- a/sp/src/game/server/hl2/npc_metropolice.cpp +++ b/sp/src/game/server/hl2/npc_metropolice.cpp @@ -107,6 +107,10 @@ int g_interactionMetrocopIdleChatter = 0; int g_interactionMetrocopClearSentenceQueues = 0; extern int g_interactionHitByPlayerThrownPhysObj; +#ifdef EZ2 +extern int g_interactionStasisGrenadeFreeze; +extern int g_interactionStasisGrenadeUnfreeze; +#endif ConVar sk_metropolice_stitch_reaction( "sk_metropolice_stitch_reaction","1.0"); ConVar sk_metropolice_stitch_tight_hitcount( "sk_metropolice_stitch_tight_hitcount","2"); @@ -3729,6 +3733,21 @@ bool CNPC_MetroPolice::HandleInteraction(int interactionType, void *data, CBaseC return true; } +#ifdef EZ2 + if (interactionType == g_interactionStasisGrenadeFreeze) + { + CapabilitiesRemove(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } + else if (interactionType == g_interactionStasisGrenadeUnfreeze) + { + CapabilitiesAdd(bits_CAP_TURN_HEAD); + // Handle unfreeze normally + return false; + } +#endif + return BaseClass::HandleInteraction( interactionType, data, sourceEnt ); } diff --git a/sp/src/game/server/physics_prop_ragdoll.cpp b/sp/src/game/server/physics_prop_ragdoll.cpp index 9d73e4afcf9..f03319afc42 100644 --- a/sp/src/game/server/physics_prop_ragdoll.cpp +++ b/sp/src/game/server/physics_prop_ragdoll.cpp @@ -2002,6 +2002,23 @@ void CRagdollProp::GetAngleOverrideFromCurrentState( char *pOut, int size ) } } +#ifdef EZ2 +bool CRagdollProp::IsMotionEnabled(void) +{ + for (int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll) + { + IPhysicsObject* pPhysicsObject = m_ragdoll.list[iRagdoll].pObject; + if (pPhysicsObject != NULL && !pPhysicsObject->IsMotionEnabled()) + { + return false; + } + } + + return true; +} +#endif + + void CRagdollProp::DisableMotion( void ) { for ( int iRagdoll = 0; iRagdoll < m_ragdoll.listCount; ++iRagdoll ) diff --git a/sp/src/game/server/physics_prop_ragdoll.h b/sp/src/game/server/physics_prop_ragdoll.h index 3e8cabf9d07..a557bae9e5c 100644 --- a/sp/src/game/server/physics_prop_ragdoll.h +++ b/sp/src/game/server/physics_prop_ragdoll.h @@ -123,6 +123,9 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics void SetKiller( CBaseEntity *pKiller ) { m_hKiller = pKiller; } void GetAngleOverrideFromCurrentState( char *pOut, int size ); +#ifdef EZ2 + bool IsMotionEnabled(void); +#endif void DisableMotion( void ); // Input/Output From 2113410f800c8c102947f27d597cd489e60c3824 Mon Sep 17 00:00:00 2001 From: arbabf Date: Sat, 13 Apr 2024 00:43:14 +1000 Subject: [PATCH 015/103] Add random spawn type for env_headcrabcanister --- .../game/server/hl2/env_headcrabcanister.cpp | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sp/src/game/server/hl2/env_headcrabcanister.cpp b/sp/src/game/server/hl2/env_headcrabcanister.cpp index e94fe3f15f2..139b7b08cfe 100644 --- a/sp/src/game/server/hl2/env_headcrabcanister.cpp +++ b/sp/src/game/server/hl2/env_headcrabcanister.cpp @@ -33,6 +33,9 @@ ConVar sk_env_headcrabcanister_shake_radius( "sk_env_headcrabcanister_shake_radi ConVar sk_env_headcrabcanister_shake_radius_vehicle( "sk_env_headcrabcanister_shake_radius_vehicle", "2500" ); #define ENV_HEADCRABCANISTER_TRAIL_TIME 3.0f +#ifdef MAPBASE +#define RANDOM_CRAB_TYPE 3 +#endif //----------------------------------------------------------------------------- // Spawn flags @@ -258,7 +261,22 @@ void CEnvHeadcrabCanister::Precache( void ) PrecacheScriptSound( "HeadcrabCanister.SkyboxExplosion" ); PrecacheScriptSound( "HeadcrabCanister.Open" ); +#ifdef MAPBASE + if (m_nHeadcrabType != RANDOM_CRAB_TYPE) + { + UTIL_PrecacheOther( s_pHeadcrabClass[m_nHeadcrabType] ); + } + else + { + // precache all the headcrabs if we're spawning random species + for (int i = 0; i < ARRAYSIZE(s_pHeadcrabClass); i++) + { + UTIL_PrecacheOther( s_pHeadcrabClass[i] ); + } + } +#else UTIL_PrecacheOther( s_pHeadcrabClass[m_nHeadcrabType] ); +#endif } @@ -733,7 +751,17 @@ void CEnvHeadcrabCanister::HeadcrabCanisterSpawnHeadcrabThink() int nHeadCrabAttachment = LookupAttachment( "headcrab" ); if ( GetAttachment( nHeadCrabAttachment, vecSpawnPosition, vecSpawnAngles ) ) { +#ifdef MAPBASE + int iHeadcrabType = m_nHeadcrabType; + if (m_nHeadcrabType == RANDOM_CRAB_TYPE) + { + iHeadcrabType = RandomInt( 0, ARRAYSIZE(s_pHeadcrabClass) - 1 ); + } + + CBaseEntity *pEnt = CreateEntityByName( s_pHeadcrabClass[iHeadcrabType] ); +#else CBaseEntity *pEnt = CreateEntityByName( s_pHeadcrabClass[m_nHeadcrabType] ); +#endif CBaseHeadcrab *pHeadCrab = assert_cast(pEnt); // Necessary to get it to eject properly (don't allow the NPC From f370074d10ccdeaaad0ee311cc13cc5261529efa Mon Sep 17 00:00:00 2001 From: arbabf Date: Sat, 13 Apr 2024 00:46:36 +1000 Subject: [PATCH 016/103] Fix spacing to be more consistent --- sp/src/game/server/hl2/env_headcrabcanister.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sp/src/game/server/hl2/env_headcrabcanister.cpp b/sp/src/game/server/hl2/env_headcrabcanister.cpp index 139b7b08cfe..15a39f0db01 100644 --- a/sp/src/game/server/hl2/env_headcrabcanister.cpp +++ b/sp/src/game/server/hl2/env_headcrabcanister.cpp @@ -262,14 +262,14 @@ void CEnvHeadcrabCanister::Precache( void ) PrecacheScriptSound( "HeadcrabCanister.Open" ); #ifdef MAPBASE - if (m_nHeadcrabType != RANDOM_CRAB_TYPE) + if ( m_nHeadcrabType != RANDOM_CRAB_TYPE ) { UTIL_PrecacheOther( s_pHeadcrabClass[m_nHeadcrabType] ); } else { // precache all the headcrabs if we're spawning random species - for (int i = 0; i < ARRAYSIZE(s_pHeadcrabClass); i++) + for ( int i = 0; i < ARRAYSIZE( s_pHeadcrabClass ); i++ ) { UTIL_PrecacheOther( s_pHeadcrabClass[i] ); } @@ -753,9 +753,9 @@ void CEnvHeadcrabCanister::HeadcrabCanisterSpawnHeadcrabThink() { #ifdef MAPBASE int iHeadcrabType = m_nHeadcrabType; - if (m_nHeadcrabType == RANDOM_CRAB_TYPE) + if ( m_nHeadcrabType == RANDOM_CRAB_TYPE ) { - iHeadcrabType = RandomInt( 0, ARRAYSIZE(s_pHeadcrabClass) - 1 ); + iHeadcrabType = RandomInt( 0, ARRAYSIZE( s_pHeadcrabClass ) - 1 ); } CBaseEntity *pEnt = CreateEntityByName( s_pHeadcrabClass[iHeadcrabType] ); From 2c001ff2595a3b6414b78027f170ca8ee2291916 Mon Sep 17 00:00:00 2001 From: arbabf Date: Sat, 13 Apr 2024 16:10:01 +1000 Subject: [PATCH 017/103] Change RANDOM_CRAB_TYPE to -1 --- sp/src/game/server/hl2/env_headcrabcanister.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/env_headcrabcanister.cpp b/sp/src/game/server/hl2/env_headcrabcanister.cpp index 15a39f0db01..4cf7fffbdd3 100644 --- a/sp/src/game/server/hl2/env_headcrabcanister.cpp +++ b/sp/src/game/server/hl2/env_headcrabcanister.cpp @@ -34,7 +34,7 @@ ConVar sk_env_headcrabcanister_shake_radius_vehicle( "sk_env_headcrabcanister_sh #define ENV_HEADCRABCANISTER_TRAIL_TIME 3.0f #ifdef MAPBASE -#define RANDOM_CRAB_TYPE 3 +#define RANDOM_CRAB_TYPE -1 #endif //----------------------------------------------------------------------------- From a49cff34f9663f24bdf624b4202f400d0cdcec15 Mon Sep 17 00:00:00 2001 From: azzy <67557558+azzyr@users.noreply.github.com> Date: Sat, 27 Apr 2024 04:31:21 +0300 Subject: [PATCH 018/103] Fix vertex blend swapping in Hammer for SDK_LightmappedGeneric Out of bounds array index corrupting the stack --- .../materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp b/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp index 6f64a15d838..2273db49146 100644 --- a/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp +++ b/sp/src/materialsystem/stdshaders/lightmappedgeneric_dx9_helper.cpp @@ -1537,7 +1537,7 @@ void DrawLightmappedGeneric_DX9_Internal(CBaseVSShader *pShader, IMaterialVar** float envMapOrigin[4] = {0,0,0,0}; params[info.m_nEnvmapOrigin]->GetVecValue( envMapOrigin, 3 ); #ifdef MAPBASE - envMapOrigin[4] = bEditorBlend ? 1.0f : 0.0f; + envMapOrigin[3] = bEditorBlend ? 1.0f : 0.0f; #endif pContextData->m_SemiStaticCmdsOut.SetPixelShaderConstant( 21, envMapOrigin ); From 24f36566320776378c65d09de31262316cd89c39 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 21 Jun 2024 18:22:04 -0500 Subject: [PATCH 019/103] Add capability to +USE serverside ragdolls and toggle cleanup of individual ragdolls --- sp/src/game/server/hl2/weapon_physcannon.cpp | 43 +++++++++++-- sp/src/game/server/physics_prop_ragdoll.cpp | 64 +++++++++++++++++++- sp/src/game/server/physics_prop_ragdoll.h | 10 +++ sp/src/game/shared/ragdoll_shared.cpp | 26 ++++++++ sp/src/game/shared/ragdoll_shared.h | 5 +- 5 files changed, 139 insertions(+), 9 deletions(-) diff --git a/sp/src/game/server/hl2/weapon_physcannon.cpp b/sp/src/game/server/hl2/weapon_physcannon.cpp index 66991e45e6f..8735e2443cb 100644 --- a/sp/src/game/server/hl2/weapon_physcannon.cpp +++ b/sp/src/game/server/hl2/weapon_physcannon.cpp @@ -490,6 +490,9 @@ class CGrabController : public IMotionEvent float GetLoadWeight( void ) const { return m_flLoadWeight; } void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; } void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; } +#ifdef MAPBASE + void SetDontUseListMass( bool bDontUse ) { m_bDontUseListMass = bDontUse; } +#endif QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer ); @@ -531,6 +534,12 @@ class CGrabController : public IMotionEvent // NVNT player controlling this grab controller CBasePlayer* m_pControllingPlayer; +#ifdef MAPBASE + // Prevents using the added mass of every part of the object + // (not saved due to only being used upon attach) + bool m_bDontUseListMass; +#endif + friend class CWeaponPhysCannon; }; @@ -581,6 +590,9 @@ CGrabController::CGrabController( void ) m_flDistanceOffset = 0; // NVNT constructing m_pControllingPlayer to NULL m_pControllingPlayer = NULL; +#ifdef MAPBASE + m_bDontUseListMass = false; +#endif } CGrabController::~CGrabController( void ) @@ -783,12 +795,18 @@ void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, { float mass = pList[i]->GetMass(); pList[i]->GetDamping( NULL, &m_savedRotDamping[i] ); - m_flLoadWeight += mass; m_savedMass[i] = mass; - // reduce the mass to prevent the player from adding crazy amounts of energy to the system - pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor ); - pList[i]->SetDamping( NULL, &damping ); +#ifdef MAPBASE + if (!m_bDontUseListMass || pList[i] == pPhys) +#endif + { + m_flLoadWeight += mass; + + // reduce the mass to prevent the player from adding crazy amounts of energy to the system + pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor ); + pList[i]->SetDamping( NULL, &damping ); + } } // NVNT setting m_pControllingPlayer to the player attached @@ -1077,7 +1095,24 @@ void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject ) Pickup_OnPhysGunPickup( pObject, m_pPlayer, PICKED_UP_BY_PLAYER ); +#ifdef MAPBASE + bool bUseGrabPos = false; + Vector vecGrabPos; + if ( dynamic_cast( pObject ) ) + { + m_grabController.SetDontUseListMass( true ); + + // Approximate where we're grabbing from + vecGrabPos = pPlayer->EyePosition() + (pPlayer->EyeDirection3D() * 16.0f); + bUseGrabPos = true; + } + else + m_grabController.SetDontUseListMass( false ); + + m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vecGrabPos, bUseGrabPos ); +#else m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false ); +#endif // NVNT apply a downward force to simulate the mass of the held object. #if defined( WIN32 ) && !defined( _X360 ) HapticSetConstantForce(m_pPlayer,clamp(m_grabController.GetLoadWeight()*0.1,1,6)*Vector(0,-1,0)); diff --git a/sp/src/game/server/physics_prop_ragdoll.cpp b/sp/src/game/server/physics_prop_ragdoll.cpp index cc788fc6d84..e208e620cfa 100644 --- a/sp/src/game/server/physics_prop_ragdoll.cpp +++ b/sp/src/game/server/physics_prop_ragdoll.cpp @@ -30,6 +30,8 @@ #ifdef MAPBASE ConVar ragdoll_autointeractions("ragdoll_autointeractions", "1", FCVAR_NONE, "Controls whether we should rely on hardcoded keyvalues or automatic flesh checks for ragdoll physgun interactions."); #define IsBody() VPhysicsIsFlesh() + +ConVar ragdoll_always_allow_use( "ragdoll_always_allow_use", "0", FCVAR_NONE, "Allows all ragdolls to be used and, if they aren't explicitly set to prevent pickup, picked up." ); #endif //----------------------------------------------------------------------------- @@ -58,6 +60,8 @@ const float ATTACHED_DAMPING_SCALE = 50.0f; #define SF_RAGDOLLPROP_STARTASLEEP 0x10000 #ifdef MAPBASE #define SF_RAGDOLLPROP_FIXED_CONSTRAINTS 0x20000 +#define SF_RAGDOLLPROP_ALLOW_USE 0x40000 +#define SF_RAGDOLLPROP_PREVENT_PICKUP 0x80000 #endif //----------------------------------------------------------------------------- @@ -104,6 +108,8 @@ BEGIN_DATADESC(CRagdollProp) #ifdef MAPBASE DEFINE_INPUTFUNC( FIELD_VOID, "Wake", InputWake ), DEFINE_INPUTFUNC( FIELD_VOID, "Sleep", InputSleep ), + DEFINE_INPUTFUNC( FIELD_VOID, "AddToLRU", InputAddToLRU ), + DEFINE_INPUTFUNC( FIELD_VOID, "RemoveFromLRU", InputRemoveFromLRU ), #endif DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputTurnOn ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputTurnOff ), @@ -125,6 +131,10 @@ BEGIN_DATADESC(CRagdollProp) DEFINE_FIELD( m_strSourceClassName, FIELD_STRING ), DEFINE_FIELD( m_bHasBeenPhysgunned, FIELD_BOOLEAN ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse" ), +#endif + // think functions DEFINE_THINKFUNC( SetDebrisThink ), DEFINE_THINKFUNC( ClearFlagsThink ), @@ -334,9 +344,39 @@ void CRagdollProp::Precache( void ) int CRagdollProp::ObjectCaps() { - return BaseClass::ObjectCaps() | FCAP_WCEDIT_POSITION; + int caps = FCAP_WCEDIT_POSITION; + +#ifdef MAPBASE + if (HasSpawnFlags( SF_RAGDOLLPROP_ALLOW_USE ) || ragdoll_always_allow_use.GetBool()) + caps |= FCAP_IMPULSE_USE; +#endif + + return BaseClass::ObjectCaps() | caps; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pActivator - +// *pCaller - +// useType - +// value - +//----------------------------------------------------------------------------- +void CRagdollProp::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) +{ + CBasePlayer *pPlayer = ToBasePlayer( pActivator ); + if (pPlayer) + { + m_OnPlayerUse.FireOutput( pActivator, this ); + + if (!HasSpawnFlags( SF_RAGDOLLPROP_PREVENT_PICKUP )) + { + pPlayer->PickupObject( this, false ); + } + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -405,7 +445,7 @@ void CRagdollProp::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t r m_bHasBeenPhysgunned = true; #ifdef MAPBASE - if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" ) ) + if( ((ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" )) && reason != PICKED_UP_BY_PLAYER ) #else if( HasPhysgunInteraction( "onpickup", "boogie" ) ) #endif @@ -447,7 +487,7 @@ void CRagdollProp::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reaso m_flLastPhysicsInfluenceTime = gpGlobals->curtime; #ifdef MAPBASE - if( (ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" ) ) + if( ((ragdoll_autointeractions.GetBool() == true && IsBody()) || HasPhysgunInteraction( "onpickup", "boogie" )) && (Reason != DROPPED_BY_PLAYER && Reason != THROWN_BY_PLAYER) ) #else if( HasPhysgunInteraction( "onpickup", "boogie" ) ) #endif @@ -1844,6 +1884,24 @@ void CRagdollProp::InputSleep( inputdata_t &inputdata ) } } } + +//----------------------------------------------------------------------------- +// Purpose: Adds ragdoll to LRU. +//----------------------------------------------------------------------------- +void CRagdollProp::InputAddToLRU( inputdata_t &inputdata ) +{ + AddSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ); + s_RagdollLRU.MoveToTopOfLRU( this ); +} + +//----------------------------------------------------------------------------- +// Purpose: Removes ragdoll from LRU. +//----------------------------------------------------------------------------- +void CRagdollProp::InputRemoveFromLRU( inputdata_t &inputdata ) +{ + RemoveSpawnFlags( SF_RAGDOLLPROP_USE_LRU_RETIREMENT ); + s_RagdollLRU.RemoveFromLRU( this ); +} #endif void CRagdollProp::InputTurnOn( inputdata_t &inputdata ) diff --git a/sp/src/game/server/physics_prop_ragdoll.h b/sp/src/game/server/physics_prop_ragdoll.h index 46e527c3204..82a3d77aafb 100644 --- a/sp/src/game/server/physics_prop_ragdoll.h +++ b/sp/src/game/server/physics_prop_ragdoll.h @@ -42,6 +42,10 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics int ObjectCaps(); +#ifdef MAPBASE + void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); +#endif + DECLARE_SERVERCLASS(); // Don't treat as a live target virtual bool IsAlive( void ) { return false; } @@ -113,6 +117,8 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics #ifdef MAPBASE void InputWake( inputdata_t &inputdata ); void InputSleep( inputdata_t &inputdata ); + void InputAddToLRU( inputdata_t &inputdata ); + void InputRemoveFromLRU( inputdata_t &inputdata ); #endif void InputTurnOn( inputdata_t &inputdata ); void InputTurnOff( inputdata_t &inputdata ); @@ -158,6 +164,10 @@ class CRagdollProp : public CBaseAnimating, public CDefaultPlayerPickupVPhysics string_t m_strSourceClassName; bool m_bHasBeenPhysgunned; +#ifdef MAPBASE + COutputEvent m_OnPlayerUse; +#endif + // If not 1, then allow underlying sequence to blend in with simulated bone positions CNetworkVar( float, m_flBlendWeight ); CNetworkVar( int, m_nOverlaySequence ); diff --git a/sp/src/game/shared/ragdoll_shared.cpp b/sp/src/game/shared/ragdoll_shared.cpp index a9da2613027..494c9ebe446 100644 --- a/sp/src/game/shared/ragdoll_shared.cpp +++ b/sp/src/game/shared/ragdoll_shared.cpp @@ -1178,6 +1178,32 @@ void CRagdollLRURetirement::MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImpo #endif } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Remove it from the LRU +//----------------------------------------------------------------------------- +void CRagdollLRURetirement::RemoveFromLRU( CBaseAnimating *pRagdoll ) +{ + for (int i = 0; i < m_LRU.Count(); i++) + { + if (m_LRU[i].Get() == pRagdoll) + { + m_LRU.Remove( i ); + return; + } + } + + for (int i = 0; i < m_LRUImportantRagdolls.Count(); i++) + { + if (m_LRUImportantRagdolls[i].Get() == pRagdoll) + { + m_LRUImportantRagdolls.Remove( i ); + return; + } + } +} +#endif + //EFFECT/ENTITY TRANSFERS diff --git a/sp/src/game/shared/ragdoll_shared.h b/sp/src/game/shared/ragdoll_shared.h index 5f4f705808a..db2bd7b2dcd 100644 --- a/sp/src/game/shared/ragdoll_shared.h +++ b/sp/src/game/shared/ragdoll_shared.h @@ -114,8 +114,9 @@ class CRagdollLRURetirement : public CAutoGameSystemPerFrame virtual void FrameUpdatePostEntityThink( void ); // Move it to the top of the LRU -#ifdef MAPBASE // From Alien Swarm SDK - void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false, float flForcedRetireTime = 0.0f ); +#ifdef MAPBASE + void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false, float flForcedRetireTime = 0.0f ); // From Alien Swarm SDK + void RemoveFromLRU( CBaseAnimating *pRagdoll ); #else void MoveToTopOfLRU( CBaseAnimating *pRagdoll, bool bImportant = false ); #endif From 33e047a442a0d7677a50950e3ba2eda873face81 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 22 Jun 2024 01:36:20 -0500 Subject: [PATCH 020/103] Primitive chromatic aberration effect for env_screeneffect --- sp/src/game/client/c_env_screenoverlay.cpp | 39 +++ .../episodic/episodic_screenspaceeffects.cpp | 222 ++++++++++++++++++ .../episodic/episodic_screenspaceeffects.h | 34 +++ 3 files changed, 295 insertions(+) diff --git a/sp/src/game/client/c_env_screenoverlay.cpp b/sp/src/game/client/c_env_screenoverlay.cpp index a31258829d0..e6d7e553219 100644 --- a/sp/src/game/client/c_env_screenoverlay.cpp +++ b/sp/src/game/client/c_env_screenoverlay.cpp @@ -219,6 +219,11 @@ enum SCREENEFFECT_EP2_ADVISOR_STUN, SCREENEFFECT_EP1_INTRO, SCREENEFFECT_EP2_GROGGY, + +#ifdef MAPBASE + SCREENEFFECT_MAPBASE_CHROMATIC_BLUR = 100, // Overlays 3 different frames of red, green, and blue tints respectively with different offsets + SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION, // Similar to above, except it stretches frames in addition to offsetting them +#endif }; // ============================================================================ @@ -303,6 +308,21 @@ void C_EnvScreenEffect::ReceiveMessage( int classID, bf_read &msg ) g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "ep2_groggy", pKeys ); g_pScreenSpaceEffects->EnableScreenSpaceEffect( "ep2_groggy" ); } +#ifdef MAPBASE + else if ( m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_BLUR || m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ) + { + if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 ) + return; + + // Set our keys + pKeys->SetFloat( "duration", m_flDuration ); + pKeys->SetInt( "fadeout", 0 ); + pKeys->SetInt( "stretch", m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ); + + g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "mapbase_chromatic_aberration", pKeys ); + g_pScreenSpaceEffects->EnableScreenSpaceEffect( "mapbase_chromatic_aberration" ); + } +#endif pKeys->deleteThis(); } @@ -349,6 +369,25 @@ void C_EnvScreenEffect::ReceiveMessage( int classID, bf_read &msg ) g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "ep2_groggy", pKeys ); } +#ifdef MAPBASE + else if ( m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_BLUR || m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ) + { + if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 ) + return; + + // Create a keyvalue block to set these params + KeyValues *pKeys = new KeyValues( "keys" ); + if ( pKeys == NULL ) + return; + + // Set our keys + pKeys->SetFloat( "duration", m_flDuration ); + pKeys->SetInt( "fadeout", 1 ); + pKeys->SetInt( "stretch", m_nType == SCREENEFFECT_MAPBASE_CHROMATIC_ABERRATION ); + + g_pScreenSpaceEffects->SetScreenSpaceEffectParams( "mapbase_chromatic_aberration", pKeys ); + } +#endif break; } diff --git a/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp b/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp index a2b59bbcfb8..fd8cafb2827 100644 --- a/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp +++ b/sp/src/game/client/episodic/episodic_screenspaceeffects.cpp @@ -462,3 +462,225 @@ void CEP2StunEffect::Render( int x, int y, int w, int h ) pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->PopMatrix(); } + +// ================================================================================================================ +// +// Chromatic Aberration +// +// ================================================================================================================ + +#ifdef MAPBASE +ConVar r_chromatic_aberration_offset( "r_chromatic_aberration_offset", "8.0" ); +ConVar r_chromatic_aberration_intensity( "r_chromatic_aberration_intensity", "0.2" ); +ConVar r_chromatic_aberration_noise( "r_chromatic_aberration_noise", "4.0" ); + +ConVar r_chromatic_aberration_frame1_clr( "r_chromatic_aberration_frame1_clr", "1.0 0.0 0.0 1.0" ); +ConVar r_chromatic_aberration_frame1_offset_x( "r_chromatic_aberration_frame1_offset_x", "1.0" ); +ConVar r_chromatic_aberration_frame1_offset_y( "r_chromatic_aberration_frame1_offset_y", "4.0" ); + +ConVar r_chromatic_aberration_frame2_clr( "r_chromatic_aberration_frame2_clr", "0.0 1.0 0.0 1.0" ); +ConVar r_chromatic_aberration_frame2_offset_x( "r_chromatic_aberration_frame2_offset_x", "-5.0" ); +ConVar r_chromatic_aberration_frame2_offset_y( "r_chromatic_aberration_frame2_offset_y", "-1.0" ); + +ConVar r_chromatic_aberration_frame3_clr( "r_chromatic_aberration_frame3_clr", "0.0 0.0 1.0 1.0" ); +ConVar r_chromatic_aberration_frame3_offset_x( "r_chromatic_aberration_frame3_offset_x", "3.0" ); +ConVar r_chromatic_aberration_frame3_offset_y( "r_chromatic_aberration_frame3_offset_y", "-3.0" ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CChromaticAberrationEffect::Init( void ) +{ + m_flDuration = 0.0f; + m_flFinishTime = 0.0f; + m_bUpdateView = true; + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetString( "$basetexture", STUN_TEXTURE ); + m_EffectMaterial.Init( "__stuneffect", TEXTURE_GROUP_CLIENT_EFFECTS, pVMTKeyValues ); + m_StunTexture.Init( STUN_TEXTURE, TEXTURE_GROUP_CLIENT_EFFECTS ); +} + +void CChromaticAberrationEffect::Shutdown( void ) +{ + m_EffectMaterial.Shutdown(); + m_StunTexture.Shutdown(); +} + +//------------------------------------------------------------------------------ +// Purpose: Pick up changes in our parameters +//------------------------------------------------------------------------------ +void CChromaticAberrationEffect::SetParameters( KeyValues *params ) +{ + if( params->FindKey( "duration" ) ) + { + m_flDuration = params->GetFloat( "duration" ); + m_flFinishTime = gpGlobals->curtime + m_flDuration; + m_bUpdateView = true; + } + + if( params->FindKey( "fadeout" ) ) + { + m_bFadeOut = ( params->GetInt( "fadeout" ) == 1 ); + } + + if( params->FindKey( "stretch" ) ) + { + m_bStretch = ( params->GetInt( "stretch" ) == 1 ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the effect +//----------------------------------------------------------------------------- +void CChromaticAberrationEffect::RenderColorFrame( CMatRenderContextPtr &pRenderContext, float flEffectPerc, int nColorMode, int x, int y, int w, int h ) +{ + // Change color + float flColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + float viewOffsX = flEffectPerc; + if (m_bStretch) + viewOffsX *= r_chromatic_aberration_offset.GetFloat() * 2; + else + viewOffsX *= r_chromatic_aberration_offset.GetFloat(); + + float viewOffsY = viewOffsX; + + { + char szColor[16] = { 0 }; + float flNoise = sin( gpGlobals->curtime * r_chromatic_aberration_noise.GetFloat() ) * flEffectPerc; + + switch (nColorMode) + { + // Red + case 0: + Q_strncpy( szColor, r_chromatic_aberration_frame1_clr.GetString(), sizeof( szColor ) ); + + viewOffsX *= r_chromatic_aberration_frame1_offset_x.GetFloat(); + viewOffsY *= r_chromatic_aberration_frame1_offset_y.GetFloat(); + + viewOffsX += flNoise; + viewOffsY += flNoise; + break; + + // Green + case 1: + Q_strncpy( szColor, r_chromatic_aberration_frame2_clr.GetString(), sizeof( szColor ) ); + + viewOffsX *= r_chromatic_aberration_frame2_offset_x.GetFloat(); + viewOffsY *= r_chromatic_aberration_frame2_offset_y.GetFloat(); + + viewOffsX += flNoise; + viewOffsY += flNoise; + break; + + // Blue + case 2: + Q_strncpy( szColor, r_chromatic_aberration_frame3_clr.GetString(), sizeof( szColor ) ); + + viewOffsX *= r_chromatic_aberration_frame3_offset_x.GetFloat(); + viewOffsY *= r_chromatic_aberration_frame3_offset_y.GetFloat(); + + viewOffsX += flNoise; + viewOffsY += flNoise; + break; + } + + char *c = strtok( szColor, " " ); + for (int i = 0; i < 4 && c != NULL; i++, c = strtok( NULL, " " )) + { + flColor[i] = atof( c ); + } + } + + if (flColor[3] == 0.0f || g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80) + return; + + m_EffectMaterial->ColorModulate( flColor[0], flColor[1], flColor[2] ); + + // Set alpha blend value + float flOverlayAlpha = clamp( r_chromatic_aberration_intensity.GetFloat() * flEffectPerc * flColor[3], 0.0f, 1.0f); + m_EffectMaterial->AlphaModulate( flOverlayAlpha ); + + // Draw full screen alpha-blended quad + if (m_bStretch) + { + float vX = x - (viewOffsX * 0.5f); + float vY = y - (viewOffsY * 0.5f); + + pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, vX, vY, w + viewOffsX, h + viewOffsY, + 0, 0, (m_StunTexture->GetActualWidth()-1), (m_StunTexture->GetActualHeight()-1), + m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() ); + } + else + { + float vX = x + viewOffsX; + float vY = y + viewOffsY; + + pRenderContext->DrawScreenSpaceRectangle( m_EffectMaterial, 0, 0, w, h, + vX, vY, (m_StunTexture->GetActualWidth()-1)+vX, (m_StunTexture->GetActualHeight()-1)+vY, + m_StunTexture->GetActualWidth(), m_StunTexture->GetActualHeight() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Render the effect +//----------------------------------------------------------------------------- +void CChromaticAberrationEffect::Render( int x, int y, int w, int h ) +{ + // Make sure we're ready to play this effect + if ( !IsEnabled() ) + return; + + if ( m_bFadeOut && m_flFinishTime < gpGlobals->curtime ) + { + g_pScreenSpaceEffects->DisableScreenSpaceEffect( "mapbase_chromatic_aberration" ); + return; + } + + CMatRenderContextPtr pRenderContext( materials ); + + // Set ourselves to the proper rendermode + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + + // Draw the texture if we're using it + if ( m_bUpdateView ) + { + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + m_bUpdateView = false; + } + + float flEffectPerc = SmoothCurve( clamp( ( m_flFinishTime - gpGlobals->curtime ) / m_flDuration, 0.0f, 1.0f ) ); + if (!m_bFadeOut) + flEffectPerc = 1.0f - flEffectPerc; + + RenderColorFrame( pRenderContext, flEffectPerc, 0, x, y, w, h ); + RenderColorFrame( pRenderContext, flEffectPerc, 1, x, y, w, h ); + RenderColorFrame( pRenderContext, flEffectPerc, 2, x, y, w, h ); + + // Save off this pass + Rect_t srcRect; + srcRect.x = x; + srcRect.y = y; + srcRect.width = w; + srcRect.height = h; + pRenderContext->CopyRenderTargetToTextureEx( m_StunTexture, 0, &srcRect, NULL ); + + // Restore our state + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->PopMatrix(); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->PopMatrix(); +} +#endif diff --git a/sp/src/game/client/episodic/episodic_screenspaceeffects.h b/sp/src/game/client/episodic/episodic_screenspaceeffects.h index ff5bc71610e..661aa403d99 100644 --- a/sp/src/game/client/episodic/episodic_screenspaceeffects.h +++ b/sp/src/game/client/episodic/episodic_screenspaceeffects.h @@ -116,4 +116,38 @@ class CEP2StunEffect : public IScreenSpaceEffect ADD_SCREENSPACE_EFFECT( CEP2StunEffect, ep2_groggy ); +#ifdef MAPBASE +class CChromaticAberrationEffect : public IScreenSpaceEffect +{ +public: + CChromaticAberrationEffect( void ) : + m_flDuration( 0.0f ), + m_flFinishTime( 0.0f ), + m_bUpdateView( true ), + m_bEnabled( false ), + m_bFadeOut( false ) {} + + virtual void Init( void ); + virtual void Shutdown( void ); + virtual void SetParameters( KeyValues *params ); + virtual void Enable( bool bEnable ) { m_bEnabled = bEnable; }; + virtual bool IsEnabled( ) { return m_bEnabled; } + + virtual void RenderColorFrame( CMatRenderContextPtr &pRenderContext, float flEffectPerc, int nColorMode, int x, int y, int w, int h ); + virtual void Render( int x, int y, int w, int h ); + +private: + CTextureReference m_StunTexture; + CMaterialReference m_EffectMaterial; + float m_flDuration; + float m_flFinishTime; + bool m_bUpdateView; + bool m_bStretch; + bool m_bFadeOut; + bool m_bEnabled; +}; + +ADD_SCREENSPACE_EFFECT( CChromaticAberrationEffect, mapbase_chromatic_aberration ); +#endif + #endif // EPISODIC_SCREENSPACEEFFECTS_H From 73988dcc7cdae041cff3bed41afc6c5c40bdeadb Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 22 Jun 2024 02:33:19 -0500 Subject: [PATCH 021/103] Fix maps with multiple sky_cameras not using the correct sky_camera after loading a save --- sp/src/game/server/SkyCamera.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sp/src/game/server/SkyCamera.cpp b/sp/src/game/server/SkyCamera.cpp index be9716d4781..f5bb282522e 100644 --- a/sp/src/game/server/SkyCamera.cpp +++ b/sp/src/game/server/SkyCamera.cpp @@ -211,6 +211,11 @@ void CSkyCamera::Activate( ) } } #endif + +#ifdef MAPBASE + if (HasSpawnFlags( SF_SKY_MASTER )) + g_hActiveSkybox = this; +#endif } #ifdef MAPBASE @@ -368,9 +373,11 @@ void CSkyCamera::InputActivateSkybox( inputdata_t &inputdata ) // Deactivate that skybox pActiveSky->SetThink( NULL ); pActiveSky->SetNextThink( TICK_NEVER_THINK ); + pActiveSky->RemoveSpawnFlags( SF_SKY_MASTER ); } g_hActiveSkybox = this; + AddSpawnFlags( SF_SKY_MASTER ); if (HasSpawnFlags( SF_SKY_START_UPDATING )) InputStartUpdating( inputdata ); @@ -384,6 +391,7 @@ void CSkyCamera::InputDeactivateSkybox( inputdata_t &inputdata ) if (GetCurrentSkyCamera() == this) { g_hActiveSkybox = NULL; + RemoveSpawnFlags( SF_SKY_MASTER ); // ClientData doesn't catch this immediately CBasePlayer *pPlayer = NULL; From cd6ec90a0f74d85a430af661a6d6ea5f694b10c8 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Mon, 24 Jun 2024 23:42:07 +0200 Subject: [PATCH 022/103] Fixed the issue. --- sp/src/materialsystem/stdshaders/BlurFilterY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/materialsystem/stdshaders/BlurFilterY.cpp b/sp/src/materialsystem/stdshaders/BlurFilterY.cpp index 57db29c0dc6..f252fea5eb5 100644 --- a/sp/src/materialsystem/stdshaders/BlurFilterY.cpp +++ b/sp/src/materialsystem/stdshaders/BlurFilterY.cpp @@ -85,7 +85,7 @@ BEGIN_VS_SHADER_FLAGS( BlurFilterY, "Help for BlurFilterY", SHADER_NOT_EDITABLE // The temp buffer is 1/4 back buffer size ITexture *src_texture = params[BASETEXTURE]->GetTextureValue(); - int height = src_texture->GetActualWidth(); + int height = src_texture->GetActualHeight(); float dY = 1.0f / height; // dY *= 0.4; float v[4]; From 5cf63d4eb6b6840c26d257c4c122a128764f44c9 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 28 Jun 2024 16:08:45 -0500 Subject: [PATCH 023/103] Spotlight "Ignore solid" keyvalue from MP branch --- sp/src/game/server/point_spotlight.cpp | 32 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/sp/src/game/server/point_spotlight.cpp b/sp/src/game/server/point_spotlight.cpp index d5901714ec1..71aad0b11a9 100644 --- a/sp/src/game/server/point_spotlight.cpp +++ b/sp/src/game/server/point_spotlight.cpp @@ -62,6 +62,7 @@ class CPointSpotlight : public CPointEntity private: bool m_bSpotlightOn; bool m_bEfficientSpotlight; + bool m_bIgnoreSolid; Vector m_vSpotlightTargetPos; Vector m_vSpotlightCurrentPos; Vector m_vSpotlightDir; @@ -100,6 +101,7 @@ BEGIN_DATADESC( CPointSpotlight ) DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ), DEFINE_FIELD( m_nHaloSprite, FIELD_INTEGER ), + DEFINE_KEYFIELD( m_bIgnoreSolid, FIELD_BOOLEAN, "IgnoreSolid" ), DEFINE_KEYFIELD( m_flSpotlightMaxLength,FIELD_FLOAT, "SpotlightLength"), DEFINE_KEYFIELD( m_flSpotlightGoalWidth,FIELD_FLOAT, "SpotlightWidth"), DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), @@ -138,6 +140,7 @@ CPointSpotlight::CPointSpotlight() #endif m_flHDRColorScale = 1.0f; m_nMinDXLevel = 0; + m_bIgnoreSolid = false; #ifdef MAPBASE m_flHaloScale = 60.0f; #endif @@ -380,12 +383,21 @@ void CPointSpotlight::SpotlightCreate(void) AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); - trace_t tr; - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr); + Vector vTargetPos; + if ( m_bIgnoreSolid ) + { + vTargetPos = GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength; + } + else + { + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + vTargetPos = tr.endpos; + } m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" ); m_hSpotlightTarget->Spawn(); - m_hSpotlightTarget->SetAbsOrigin( tr.endpos ); + m_hSpotlightTarget->SetAbsOrigin( vTargetPos ); m_hSpotlightTarget->SetOwnerEntity( this ); m_hSpotlightTarget->m_clrRender = m_clrRender; m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength; @@ -437,9 +449,17 @@ Vector CPointSpotlight::SpotlightCurrentPos(void) AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); // Get beam end point. Only collide with solid objects, not npcs - trace_t tr; - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - return tr.endpos; + Vector vEndPos = GetAbsOrigin() + ( m_vSpotlightDir * 2 * m_flSpotlightMaxLength ); + if ( m_bIgnoreSolid ) + { + return vEndPos; + } + else + { + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), vEndPos, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + return tr.endpos; + } } //------------------------------------------------------------------------------ From 41233cb475750a09e4a571909d069a9bb096a8be Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Sat, 27 Jul 2024 23:49:31 +0200 Subject: [PATCH 024/103] Added Melee attack keyvalue --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 1 + sp/src/game/server/hl2/npc_BaseZombie.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index f751d09a957..1c5cde109bf 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -211,6 +211,7 @@ BEGIN_DATADESC( CNPC_BaseZombie ) DEFINE_FIELD( m_fIsTorso, FIELD_BOOLEAN ), #ifdef MAPBASE DEFINE_KEYFIELD( m_fIsHeadless, FIELD_BOOLEAN, "Headless" ), + DEFINE_KEYFIELD( m_flMeleeReach, FIELD_FLOAT, "MeleeReach" ), #else DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ), #endif diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index fb17f0371c1..aa7541c1f74 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -20,8 +20,6 @@ #define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController()) -#define ZOMBIE_MELEE_REACH 55 - extern int AE_ZOMBIE_ATTACK_RIGHT; extern int AE_ZOMBIE_ATTACK_LEFT; extern int AE_ZOMBIE_ATTACK_BOTH; @@ -141,7 +139,7 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase } int MeleeAttack1Conditions ( float flDot, float flDist ); - virtual float GetClawAttackRange() const { return ZOMBIE_MELEE_REACH; } + virtual float GetClawAttackRange() const { return m_flMeleeReach; } // No range attacks int RangeAttack1Conditions ( float flDot, float flDist ) { return( 0 ); } @@ -257,6 +255,8 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase float m_flNextFlinch; + float m_flMeleeReach; + bool m_bHeadShot; // Used to determine the survival of our crab beyond our death. // From a736b0f96a72136223465cbca9f22d4590f38c14 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Sun, 28 Jul 2024 14:11:06 +0200 Subject: [PATCH 025/103] Added new spawnflag --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 2 +- sp/src/game/server/hl2/npc_BaseZombie.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index 1c5cde109bf..0799fcf6d55 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -787,7 +787,7 @@ bool CNPC_BaseZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDa HeadcrabRelease_t CNPC_BaseZombie::ShouldReleaseHeadcrab( const CTakeDamageInfo &info, float flDamageThreshold ) { #ifdef MAPBASE - if ( m_iHealth <= 0 && !m_fIsHeadless ) + if ( m_iHealth <= 0 && !m_fIsHeadless && !HasSpawnFlags(SF_ZOMBIE_NO_HEADCRAB_SPAWN)) #else if ( m_iHealth <= 0 ) #endif diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index aa7541c1f74..16ca0db9c23 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -43,6 +43,7 @@ extern int AE_ZOMBIE_POUND; #ifdef MAPBASE #define SF_ZOMBIE_NO_TORSO ( 1 << 15 ) +#define SF_ZOMBIE_NO_HEADCRAB_SPAWN ( 1 << 16 ) #endif From 4f14f0c5cc056a8d8a121ccbbbab858e9db74e6d Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Wed, 7 Aug 2024 13:16:37 +0200 Subject: [PATCH 026/103] Fixed the issue. --- sp/src/game/server/hl2/weapon_flaregun.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/weapon_flaregun.cpp b/sp/src/game/server/hl2/weapon_flaregun.cpp index 110f5c666d2..f2b74c46a4f 100644 --- a/sp/src/game/server/hl2/weapon_flaregun.cpp +++ b/sp/src/game/server/hl2/weapon_flaregun.cpp @@ -568,10 +568,13 @@ void CFlare::Start( float lifeTime ) //----------------------------------------------------------------------------- void CFlare::Die( float fadeTime ) { - m_flTimeBurnOut = gpGlobals->curtime + fadeTime; + if (m_bInActiveList) + { + m_flTimeBurnOut = gpGlobals->curtime + fadeTime; - SetThink( &CFlare::FlareThink ); - SetNextThink( gpGlobals->curtime + 0.1f ); + SetThink(&CFlare::FlareThink); + SetNextThink(gpGlobals->curtime + 0.1f); + } } //----------------------------------------------------------------------------- From 178a26203248d1bab641ba4ec874838b4534eb03 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 10 Aug 2024 13:47:53 -0500 Subject: [PATCH 027/103] Add VScript function for checking last hit group on CBaseCombatCharacter --- sp/src/game/server/basecombatcharacter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index a1033586a61..57f6c7a55dd 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -193,6 +193,8 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by DEFINE_SCRIPTFUNC( EyeDirection2D, "Get the eyes' 2D direction." ) DEFINE_SCRIPTFUNC( EyeDirection3D, "Get the eyes' 3D direction." ) + DEFINE_SCRIPTFUNC( LastHitGroup, "Get the last hitgroup." ) + // // Hooks // From 06596695a30e89bf3bf78259e7177f2187e27f09 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 10 Aug 2024 13:48:48 -0500 Subject: [PATCH 028/103] Fix filter_activator_model issues with save/restore and null activator --- sp/src/game/server/filters.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/filters.cpp b/sp/src/game/server/filters.cpp index e95ba310a31..ca2f6c7af4a 100644 --- a/sp/src/game/server/filters.cpp +++ b/sp/src/game/server/filters.cpp @@ -996,6 +996,9 @@ class CFilterModel : public CBaseFilter bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity ) { + if (!pEntity) + return false; + if (FStrEq(STRING(m_strFilterSkin), "-1") /*m_strFilterSkin == NULL_STRING|| FStrEq(STRING(m_strFilterSkin), "")*/) return Matcher_NamesMatch(STRING(m_iFilterModel), STRING(pEntity->GetModelName())); else if (pEntity->GetBaseAnimating()) @@ -1011,6 +1014,17 @@ class CFilterModel : public CBaseFilter inputdata.value.Convert(FIELD_STRING); m_iFilterModel = inputdata.value.StringID(); } + + bool KeyValue( const char *szKeyName, const char *szValue ) + { + if (FStrEq( szKeyName, "filtername" )) + { + m_iFilterModel = AllocPooledString( szValue ); + return true; + } + else + return BaseClass::KeyValue( szKeyName, szValue ); + } }; LINK_ENTITY_TO_CLASS( filter_activator_model, CFilterModel ); @@ -1019,7 +1033,6 @@ BEGIN_DATADESC( CFilterModel ) // Keyfields DEFINE_KEYFIELD( m_iFilterModel, FIELD_STRING, "filtermodel" ), - DEFINE_KEYFIELD( m_iFilterModel, FIELD_STRING, "filtername" ), DEFINE_KEYFIELD( m_strFilterSkin, FIELD_STRING, "skin" ), END_DATADESC() From 102c7c3015343d07661f646e3eaa3c7fd2682331 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 10 Aug 2024 13:49:19 -0500 Subject: [PATCH 029/103] New "StopActionLoop" input for scripted_sequence --- sp/src/game/server/scripted.cpp | 11 +++++++++++ sp/src/game/server/scripted.h | 1 + 2 files changed, 12 insertions(+) diff --git a/sp/src/game/server/scripted.cpp b/sp/src/game/server/scripted.cpp index f02a1dcd305..1cc14d77a99 100644 --- a/sp/src/game/server/scripted.cpp +++ b/sp/src/game/server/scripted.cpp @@ -113,6 +113,9 @@ BEGIN_DATADESC( CAI_ScriptedSequence ) DEFINE_INPUTFUNC( FIELD_VOID, "MoveToPosition", InputMoveToPosition ), DEFINE_INPUTFUNC( FIELD_VOID, "BeginSequence", InputBeginSequence ), DEFINE_INPUTFUNC( FIELD_VOID, "CancelSequence", InputCancelSequence ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "StopActionLoop", InputStopActionLoop ), +#endif DEFINE_KEYFIELD( m_iPlayerDeathBehavior, FIELD_INTEGER, "onplayerdeath" ), DEFINE_INPUTFUNC( FIELD_VOID, "ScriptPlayerDeath", InputScriptPlayerDeath ), @@ -382,6 +385,14 @@ void CAI_ScriptedSequence::InputSetTarget( inputdata_t &inputdata ) m_iszEntity = AllocPooledString(inputdata.value.String()); m_hTargetEnt = NULL; } + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CAI_ScriptedSequence::InputStopActionLoop( inputdata_t &inputdata ) +{ + StopActionLoop( false ); +} #endif diff --git a/sp/src/game/server/scripted.h b/sp/src/game/server/scripted.h index 7b1d825441f..82aae6b3605 100644 --- a/sp/src/game/server/scripted.h +++ b/sp/src/game/server/scripted.h @@ -112,6 +112,7 @@ class CAI_ScriptedSequence : public CBaseEntity void InputCancelSequence( inputdata_t &inputdata ); void InputMoveToPosition( inputdata_t &inputdata ); #ifdef MAPBASE + void InputStopActionLoop( inputdata_t &inputdata ); void InputSetTarget( inputdata_t &inputdata ); #endif From 5d50335c02353f9a7e7e4e7617b2a06f9237f499 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 10 Aug 2024 13:51:06 -0500 Subject: [PATCH 030/103] New "Destroy" input for func_combine_ball_spawner --- sp/src/game/server/hl2/prop_combine_ball.cpp | 139 +++++++++++++++++++ sp/src/game/server/hl2/prop_combine_ball.h | 6 + 2 files changed, 145 insertions(+) diff --git a/sp/src/game/server/hl2/prop_combine_ball.cpp b/sp/src/game/server/hl2/prop_combine_ball.cpp index 7a6f287bffb..1606e5b1964 100644 --- a/sp/src/game/server/hl2/prop_combine_ball.cpp +++ b/sp/src/game/server/hl2/prop_combine_ball.cpp @@ -1080,6 +1080,113 @@ void CPropCombineBall::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t R StopAnimating(); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPropCombineBall::SpawnerDestroyed( CBaseEntity *pActivator, bool *bSeekEnemy ) +{ + SetState( STATE_THROWN ); + WhizSoundThink(); + + m_bHeld = false; + m_bLaunched = true; + + // Stop with the dissolving + SetContextThink( NULL, gpGlobals->curtime, s_pHoldDissolveContext ); + + // We're ready to start colliding again. + SetCollisionGroup( HL2COLLISION_GROUP_COMBINE_BALL ); + + if ( m_pGlowTrail ) + { + m_pGlowTrail->TurnOn(); + m_pGlowTrail->SetRenderColor( 255, 255, 255, 255 ); + } + + // Set our desired speed to be launched at + SetSpeed( 1500.0f ); + + SetOwnerEntity( pActivator ); + SetWeaponLaunched( false ); + + if (!VPhysicsGetObject()) + return; + + if (pActivator->IsPlayer()) + { + PhysClearGameFlags( VPhysicsGetObject(), FVPHYSICS_NO_NPC_IMPACT_DMG ); + PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_DMG_DISSOLVE | FVPHYSICS_HEAVY_OBJECT ); + } + else + { + // Don't do impact damage. Just touch them and do your dissolve damage and move on. + PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_NO_NPC_IMPACT_DMG ); + } + + //if (pActivator->IsPlayer()) + //{ + // SetPlayerLaunched( ToBasePlayer( pActivator ) ); + //} + + Vector vecVelocity; + + if (bSeekEnemy) + { + CBaseEntity *pBestTarget = NULL; + CBaseEntity *list[256]; + + float distance; + float flBestDist = MAX_COORD_FLOAT; + int nCount = UTIL_EntitiesInSphere( list, 256, GetAbsOrigin(), sk_combine_ball_search_radius.GetFloat(), FL_NPC | FL_CLIENT ); + + for ( int i = 0; i < nCount; i++ ) + { + if ( !IsAttractiveTarget( list[i] ) ) + continue; + + distance = (list[i]->WorldSpaceCenter() - GetAbsOrigin()).LengthSqr(); + if ( distance < flBestDist ) + { + pBestTarget = list[i]; + flBestDist = distance; + } + } + + if ( pBestTarget ) + { + VectorSubtract( pBestTarget->WorldSpaceCenter(), GetAbsOrigin(), vecVelocity ); + VectorNormalize( vecVelocity ); + } + + *bSeekEnemy = (pBestTarget != NULL); + } + + if (bSeekEnemy == NULL || *bSeekEnemy == false) + { + // Choose a random direction based on current velocity + VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL ); + VectorNormalize( vecVelocity ); + + QAngle shotAng; + VectorAngles( vecVelocity, shotAng ); + + // Offset by some small cone + shotAng[PITCH] += random->RandomInt( -75, 75 ); + shotAng[YAW] += random->RandomInt( -75, 75 ); + + AngleVectors( shotAng, &vecVelocity, NULL, NULL ); + } + + vecVelocity *= GetSpeed(); + + VPhysicsGetObject()->SetVelocity( &vecVelocity, &vec3_origin ); + + SetBallAsLaunched(); + StopAnimating(); +} +#endif + //------------------------------------------------------------------------------ // Stop looping sounds //------------------------------------------------------------------------------ @@ -1849,6 +1956,9 @@ BEGIN_DATADESC( CFuncCombineBallSpawner ) DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ), DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ), +#ifdef MAPBASE + DEFINE_INPUTFUNC( FIELD_VOID, "Destroy", InputDestroy ), +#endif DEFINE_OUTPUT( m_OnBallGrabbed, "OnBallGrabbed" ), DEFINE_OUTPUT( m_OnBallReinserted, "OnBallReinserted" ), @@ -2001,6 +2111,35 @@ void CFuncCombineBallSpawner::InputDisable( inputdata_t &inputdata ) SetThink( NULL ); } +#ifdef MAPBASE +void CFuncCombineBallSpawner::InputDestroy( inputdata_t &inputdata ) +{ + if ( !m_bEnabled ) + { + UTIL_Remove( this ); + return; + } + + // One ball always seeks the nearest enemy + bool bSoughtEnemy = false; + + CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "prop_combine_ball" ); + while (pEnt) + { + CPropCombineBall *pBall = static_cast(pEnt); + if (pBall && pBall->GetSpawner() == this) + { + BallGrabbed( pBall ); + pBall->SpawnerDestroyed( inputdata.pActivator, bSoughtEnemy ? NULL : &bSoughtEnemy ); + } + + pEnt = gEntList.FindEntityByClassname( pEnt, "prop_combine_ball" ); + } + + UTIL_Remove( this ); +} +#endif + //----------------------------------------------------------------------------- // Choose a random point inside the cylinder diff --git a/sp/src/game/server/hl2/prop_combine_ball.h b/sp/src/game/server/hl2/prop_combine_ball.h index d750d6ad2cb..d798fde1ee3 100644 --- a/sp/src/game/server/hl2/prop_combine_ball.h +++ b/sp/src/game/server/hl2/prop_combine_ball.h @@ -98,6 +98,9 @@ class CPropCombineBall : public CBaseAnimating, public CDefaultPlayerPickupVPhys void SetSpawner( CFuncCombineBallSpawner *pSpawner ) { m_hSpawner = pSpawner; } void NotifySpawnerOfRemoval( void ); +#ifdef MAPBASE + void SpawnerDestroyed( CBaseEntity *pActivator, bool *bSeekEnemy ); +#endif float LastCaptureTime() const; @@ -243,6 +246,9 @@ class CFuncCombineBallSpawner : public CBaseEntity // Input void InputEnable( inputdata_t &inputdata ); void InputDisable( inputdata_t &inputdata ); +#ifdef MAPBASE + void InputDestroy( inputdata_t &inputdata ); +#endif // Fire ball grabbed output void GrabBallTouch( CBaseEntity *pOther ); From e9c45e5235a71045c7fb994e7853b8ea22ccd3ed Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Thu, 5 Sep 2024 23:35:46 +0200 Subject: [PATCH 031/103] Implement GetColorForSurface() failure workaround This function is used to color impact particles. On Linux I've noticed that this function sometimes is not successful on retrieving the surface color, leaving an odd color to render the particles with. The engine function TraceLineMaterialAndLighting() even has a boolean return value indicating success. GetModelMaterialColorAndLighting() does not though, but I still observe failures (the color parameter is not modified). The color is initialized with an invalid value. If it detects that retrieving the color failed (engine function said so or the invalid value was left in place), this now hamfistedly assumes a lightish grey color, but at least still correctly (presumably) incorporates lighting information. When this situation is detected, a warning is also printed to the console. Because why not. --- sp/src/game/client/c_impact_effects.cpp | 41 +++++++++++++++---------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/sp/src/game/client/c_impact_effects.cpp b/sp/src/game/client/c_impact_effects.cpp index 5b350c184c9..64a51bf37bd 100644 --- a/sp/src/game/client/c_impact_effects.cpp +++ b/sp/src/game/client/c_impact_effects.cpp @@ -95,18 +95,23 @@ extern PMaterialHandle g_Material_Spark; //----------------------------------------------------------------------------- void GetColorForSurface( trace_t *trace, Vector *color ) { - Vector baseColor, diffuseColor; - Vector end = trace->startpos + ( ( Vector )trace->endpos - ( Vector )trace->startpos ) * 1.1f; - + Vector baseColor = vec3_invalid, diffuseColor; + const char *kind; + if ( trace->DidHitWorld() ) { if ( trace->hitbox == 0 ) { + kind = "World"; + Vector end = trace->startpos + ( trace->endpos - trace->startpos ) * 1.1f; // If we hit the world, then ask the world for the fleck color - engine->TraceLineMaterialAndLighting( trace->startpos, end, diffuseColor, baseColor ); + if ( !engine->TraceLineMaterialAndLighting( trace->startpos, end, diffuseColor, baseColor ) ) { + baseColor = vec3_invalid; // Make sure this wasn't modified + } } else { + kind = "Static Prop"; // In this case we hit a static prop. staticpropmgr->GetStaticPropMaterialColorAndLighting( trace, trace->hitbox - 1, diffuseColor, baseColor ); } @@ -117,20 +122,24 @@ void GetColorForSurface( trace_t *trace, Vector *color ) C_BaseEntity *pEnt = trace->m_pEnt; if ( !pEnt ) { - Msg("Couldn't find surface in GetColorForSurface()\n"); - color->x = 255; - color->y = 255; - color->z = 255; - return; + kind = "Null-Entity"; + } else { + kind = "Entity"; + ICollideable *pCollide = pEnt->GetCollideable(); + int modelIndex = pCollide->GetCollisionModelIndex(); + model_t* pModel = const_cast(modelinfo->GetModel( modelIndex )); + + // Ask the model info about what we need to know + modelinfo->GetModelMaterialColorAndLighting( pModel, pCollide->GetCollisionOrigin(), + pCollide->GetCollisionAngles(), trace, diffuseColor, baseColor ); } + } - ICollideable *pCollide = pEnt->GetCollideable(); - int modelIndex = pCollide->GetCollisionModelIndex(); - model_t* pModel = const_cast(modelinfo->GetModel( modelIndex )); - - // Ask the model info about what we need to know - modelinfo->GetModelMaterialColorAndLighting( pModel, pCollide->GetCollisionOrigin(), - pCollide->GetCollisionAngles(), trace, diffuseColor, baseColor ); + if ( baseColor == vec3_invalid ) + { + Warning( "Couldn't find surface color of %s\n", kind ); + baseColor = Vector( .5f, .5f, .5f ); + diffuseColor = engine->GetLightForPoint( trace->endpos, true ); } //Get final light value From df6adcc5e9f7aa47947219f556c981b8bb191d6e Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Thu, 5 Sep 2024 23:42:16 +0200 Subject: [PATCH 032/103] Fix -Wdelete-incomplete in CDefaultCustomWeaponEntityFactory::ReleaseData() GCC warns about attempting to delete a void-pointer, since it will not be able to invoke its destructor. Fix by casing it to the expected type. --- sp/src/game/server/mapbase/custom_weapon_factory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/mapbase/custom_weapon_factory.h b/sp/src/game/server/mapbase/custom_weapon_factory.h index 3abb17edfca..bf46880b1f6 100644 --- a/sp/src/game/server/mapbase/custom_weapon_factory.h +++ b/sp/src/game/server/mapbase/custom_weapon_factory.h @@ -107,7 +107,7 @@ class CDefaultCustomWeaponEntityFactory : public CCustomWeaponEntityFactoryBase< virtual void ReleaseData(const void* pData) const { - delete pData; + delete (Data*)pData; } }; From a999c794f783048ef47ffc6410e480d9a6eb47ca Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Thu, 5 Sep 2024 23:44:50 +0200 Subject: [PATCH 033/103] Fix minor case of const-correctness --- sp/src/game/server/sound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/sound.cpp b/sp/src/game/server/sound.cpp index cb7df905d8f..167644ab0af 100644 --- a/sp/src/game/server/sound.cpp +++ b/sp/src/game/server/sound.cpp @@ -938,7 +938,7 @@ void CAmbientGeneric::SendSound( SoundFlags_t flags) { #ifdef MAPBASE int iFlags = flags != SND_STOP ? ((int)flags | m_iSoundFlags) : flags; - char *szSoundFile = (char *)STRING( m_iszSound ); + const char *szSoundFile = STRING( m_iszSound ); CBaseEntity* pSoundSource = m_hSoundSource; if ( pSoundSource ) { From 359fcb7196acc4c6689e18a6bfa0d8c4e4d544c9 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Thu, 5 Sep 2024 23:45:20 +0200 Subject: [PATCH 034/103] Ditch attempts trying to obtain sound duration of MP3s Crashes on Linux. Apparently returns incorrect values on Windows. --- sp/src/game/shared/SoundEmitterSystem.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sp/src/game/shared/SoundEmitterSystem.cpp b/sp/src/game/shared/SoundEmitterSystem.cpp index 6b10c6fead6..131639bd84d 100644 --- a/sp/src/game/shared/SoundEmitterSystem.cpp +++ b/sp/src/game/shared/SoundEmitterSystem.cpp @@ -1001,7 +1001,11 @@ class CSoundEmitterSystem : public CBaseGameSystem if ( duration ) { - *duration = enginesound->GetSoundDuration( pSample ); + if ( Q_stristr( pSample, ".mp3" ) ) { + *duration = 0; + } else { + *duration = enginesound->GetSoundDuration( pSample ); + } } TraceEmitSound( "EmitAmbientSound: Raw wave emitted '%s' (ent %i)\n", From ead5668dc82a3618f463d8a09710ca7d43226bf7 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:25:23 +0300 Subject: [PATCH 035/103] Fix GetPropVector return type --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 6cdf66df043..88bb41ffbec 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -1615,7 +1615,7 @@ class CScriptNetPropManager #define SetProp( type, name )\ void SetProp##name( HSCRIPT hEnt, const char* szProp, type value )\ {\ - return SetProp##name##Array( hEnt, szProp, value, 0 );\ + SetProp##name##Array( hEnt, szProp, value, 0 );\ } GetProp( int, Int ); @@ -1624,8 +1624,8 @@ class CScriptNetPropManager SetProp( float, Float ); GetProp( HSCRIPT, Entity ); SetProp( HSCRIPT, Entity ); - GetProp( Vector, Vector ); - SetProp( Vector, Vector ); + GetProp( const Vector&, Vector ); + SetProp( const Vector&, Vector ); GetProp( const char*, String ); SetProp( const char*, String ); From 337b58eb3c6d4952448069d2dab52cca57928913 Mon Sep 17 00:00:00 2001 From: Yui <50331474+SirYodaJedi@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:33:07 -0400 Subject: [PATCH 036/103] Don't network info_null Reduces potential for entity limit crashes, as it still exists for the first tick. --- mp/src/game/server/subs.cpp | 4 ++-- sp/src/game/server/subs.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mp/src/game/server/subs.cpp b/mp/src/game/server/subs.cpp index b2bf003f444..e7ad735453c 100644 --- a/mp/src/game/server/subs.cpp +++ b/mp/src/game/server/subs.cpp @@ -22,10 +22,10 @@ void CPointEntity::Spawn( void ) } -class CNullEntity : public CBaseEntity +class CNullEntity : public CServerOnlyEntity { public: - DECLARE_CLASS( CNullEntity, CBaseEntity ); + DECLARE_CLASS( CNullEntity, CServerOnlyEntity ); void Spawn( void ); }; diff --git a/sp/src/game/server/subs.cpp b/sp/src/game/server/subs.cpp index 0a37e4c126e..53ba8f06c65 100644 --- a/sp/src/game/server/subs.cpp +++ b/sp/src/game/server/subs.cpp @@ -22,10 +22,10 @@ void CPointEntity::Spawn( void ) } -class CNullEntity : public CBaseEntity +class CNullEntity : public CServerOnlyEntity { public: - DECLARE_CLASS( CNullEntity, CBaseEntity ); + DECLARE_CLASS( CNullEntity, CServerOnlyEntity ); void Spawn( void ); }; From 696036219e6d545ac844a98729df32a5841f4c8d Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Wed, 13 Nov 2024 00:25:51 +0100 Subject: [PATCH 037/103] Prevent signed overflow (UB) in SquirrelVM::GenerateUniqueKey() --- sp/src/vscript/vscript_squirrel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/vscript/vscript_squirrel.cpp b/sp/src/vscript/vscript_squirrel.cpp index 5efb2314a38..9f46d68c20e 100644 --- a/sp/src/vscript/vscript_squirrel.cpp +++ b/sp/src/vscript/vscript_squirrel.cpp @@ -2776,7 +2776,7 @@ void* SquirrelVM::GetInstanceValue(HSCRIPT hInstance, ScriptClassDesc_t* pExpect bool SquirrelVM::GenerateUniqueKey(const char* pszRoot, char* pBuf, int nBufSize) { - static int keyIdx = 0; + static unsigned keyIdx = 0; // This gets used for script scope, still confused why it needs to be inside IScriptVM // is it just to be a compatible name for CreateScope? V_snprintf(pBuf, nBufSize, "%08X_%s", ++keyIdx, pszRoot); From 7ebb26c9963160ec662349fa8fe4527e11f23995 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:35:33 +0300 Subject: [PATCH 038/103] Fix OOB access on CBaseCombatCharacter::m_hMyWeapons --- sp/src/game/server/ai_basenpc.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 002f3f36e53..927e39a191a 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -8045,10 +8045,13 @@ int CAI_BaseNPC::UnholsterWeapon( void ) if (i == -1) { // Set i to the first weapon you can find - for (i = 0; i < WeaponCount(); i++) + for (i = 0;;) { if (GetWeapon(i)) break; + + if (++i >= WeaponCount()) + return -1; } } #else From 47aa8ac3f42dd1dac8f7db39d5b21279a50395a5 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Mon, 25 Nov 2024 22:16:57 +0100 Subject: [PATCH 039/103] Update triggers.cpp --- sp/src/game/server/triggers.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/triggers.cpp b/sp/src/game/server/triggers.cpp index 2361f8facd8..21b7b256483 100644 --- a/sp/src/game/server/triggers.cpp +++ b/sp/src/game/server/triggers.cpp @@ -47,6 +47,7 @@ #define DEBUG_TRANSITIONS_VERBOSE 2 ConVar g_debug_transitions( "g_debug_transitions", "0", FCVAR_NONE, "Set to 1 and restart the map to be warned if the map has no trigger_transition volumes. Set to 2 to see a dump of all entities & associated results during a transition." ); +ConVar noclip_changelevel("noclip_changelevel", "0", FCVAR_CHEAT); // Global list of triggers that care about weapon fire // Doesn't need saving, the triggers re-add themselves on restore. @@ -1848,7 +1849,8 @@ void CChangeLevel::TouchChangeLevel( CBaseEntity *pOther ) return; } - if ( !pPlayer->IsInAVehicle() && pPlayer->GetMoveType() == MOVETYPE_NOCLIP ) + + if ( !pPlayer->IsInAVehicle() && pPlayer->GetMoveType() == MOVETYPE_NOCLIP && !noclip_changelevel.GetBool()) { DevMsg("In level transition: %s %s\n", st_szNextMap, st_szNextSpot ); return; From 75917390445930293ea7f3f03719c7af14450026 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Mon, 16 Dec 2024 09:21:29 -0600 Subject: [PATCH 040/103] Wilson looks at targets through cameras --- sp/src/game/server/ai_baseactor.cpp | 20 ++ sp/src/game/server/ai_baseactor.h | 8 + sp/src/game/server/ai_playerally.cpp | 14 +- sp/src/game/server/ai_playerally.h | 3 + sp/src/game/server/ez2/npc_wilson.cpp | 272 +++++++++++++++++++++++++- sp/src/game/server/ez2/npc_wilson.h | 14 ++ 6 files changed, 324 insertions(+), 7 deletions(-) diff --git a/sp/src/game/server/ai_baseactor.cpp b/sp/src/game/server/ai_baseactor.cpp index 0238df79ee5..fcfd236853d 100644 --- a/sp/src/game/server/ai_baseactor.cpp +++ b/sp/src/game/server/ai_baseactor.cpp @@ -1145,6 +1145,10 @@ void CAI_BaseActor::UpdateHeadControl( const Vector &vHeadTarget, float flHeadIn ConcatTransforms( worldToForward, targetXform, headXform ); MatrixAngles( headXform, vTargetAngles ); +#ifdef EZ2 + HeadAngleOverride( vTargetAngles ); +#endif + #ifdef MAPBASE // This is here to cover an edge case where pose parameters set to NaN invalidate the model. if (!vTargetAngles.IsValid()) @@ -1273,6 +1277,16 @@ bool CAI_BaseActor::HasActiveLookTargets( void ) return m_lookQueue.Count() != 0; } +#ifdef EZ2 +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CAI_BaseActor::HasActiveRandomLookTargets( void ) +{ + return m_randomLookQueue.Count() != 0; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Clear any active look targets for the specified entity //----------------------------------------------------------------------------- @@ -1755,6 +1769,12 @@ void CAI_BaseActor::MaintainLookTargets( float flInterval ) dir = HeadDirection3D(); } } +#ifdef EZ2 + else if ( HeadTargetPosOverride( active[i]->GetPosition(), dir, flDist ) ) + { + flInterest = flInterest * HeadTargetValidity( active[i]->GetPosition() ); + } +#endif else { dir = active[i]->GetPosition() - vEyePosition; diff --git a/sp/src/game/server/ai_baseactor.h b/sp/src/game/server/ai_baseactor.h index e293cc8b20b..cf76c78c49a 100644 --- a/sp/src/game/server/ai_baseactor.h +++ b/sp/src/game/server/ai_baseactor.h @@ -129,6 +129,9 @@ class CAI_BaseActor : public CAI_ExpresserHost virtual bool PickRandomLookTarget( AILookTargetArgs_t *pArgs ); virtual void MakeRandomLookTarget( AILookTargetArgs_t *pArgs, float minTime, float maxTime ); virtual bool HasActiveLookTargets( void ); +#ifdef EZ2 + virtual bool HasActiveRandomLookTargets( void ); +#endif virtual void OnSelectedLookTarget( AILookTargetArgs_t *pArgs ) { return; } virtual void ClearLookTarget( CBaseEntity *pTarget ); virtual void ExpireCurrentRandomLookTarget() { m_flNextRandomLookTime = gpGlobals->curtime - 0.1f; } @@ -153,6 +156,11 @@ class CAI_BaseActor : public CAI_ExpresserHost virtual bool ValidEyeTarget(const Vector &lookTargetPos); virtual bool ValidHeadTarget(const Vector &lookTargetPos); virtual float HeadTargetValidity(const Vector &lookTargetPos); +#ifdef EZ2 + // Used by Wilson for looking from cameras + virtual bool HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vecDir, float &flDist ) { return false; } + virtual bool HeadAngleOverride( QAngle &vTargetAngles ) { return false; } +#endif virtual bool ShouldBruteForceFailedNav() { return true; } diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index 1f68747dcb0..1eebe9563d2 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -478,9 +478,17 @@ void CAI_PlayerAlly::GatherConditions( void ) { bool bPlayerIsLooking = false; +#ifdef EZ2 + if ( ( pLocalPlayer->GetAbsOrigin() - GetAbsOriginForSpeech( pLocalPlayer ) ).Length2DSqr() < Square(TALKER_STARE_DIST) ) +#else if ( ( pLocalPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2DSqr() < Square(TALKER_STARE_DIST) ) +#endif { +#ifdef EZ2 + if ( pLocalPlayer->FInViewCone( GetEyePositionForSpeech( pLocalPlayer ) ) ) +#else if ( pLocalPlayer->FInViewCone( EyePosition() ) ) +#endif { if ( pLocalPlayer->GetSmoothedVelocity().LengthSqr() < Square( 100 ) ) bPlayerIsLooking = true; @@ -781,7 +789,7 @@ bool CAI_PlayerAlly::SelectAlertSpeech( AISpeechSelection_t *pSelection ) // NPCs can comment on smells in EZ2 if ( pSmellTarget && HasCondition( COND_SMELL ) && GetBestScent() && GetExpresser() && GetExpresser()->CanSpeakConcept( TLK_SMELL ) ) { - if( SelectSpeechResponse( TLK_SMELL, UTIL_VarArgs("distancetosmell:%f", GetAbsOrigin().DistTo( GetBestScent()->GetSoundReactOrigin() ) ), pSmellTarget, pSelection ) ) + if( SelectSpeechResponse( TLK_SMELL, UTIL_VarArgs("distancetosmell:%f", GetAbsOriginForSpeech( pSmellTarget ).DistTo( GetBestScent()->GetSoundReactOrigin() ) ), pSmellTarget, pSelection ) ) return true; } #endif @@ -1405,7 +1413,11 @@ void CAI_PlayerAlly::OnEnemyRangeAttackedMe( CBaseEntity *pEnemy, const Vector & AI_CriteriaSet modifiers; ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); +#ifdef EZ2 + Vector vecEntDir = (pEnemy->EyePosition() - GetEyePositionForSpeech(pEnemy)); +#else Vector vecEntDir = (pEnemy->EyePosition() - EyePosition()); +#endif float flDot = DotProduct( vecEntDir.Normalized(), vecDir ); modifiers.AppendCriteria( "shot_dot", CNumStr( flDot ) ); diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index fa43e3b5f02..fde8f6ad73a 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -387,6 +387,9 @@ class CAI_PlayerAlly : public CAI_BaseActor #ifdef EZ2 // Used by Wilson camera targets virtual const Vector &GetSpeechTargetSearchOrigin() { return GetAbsOrigin(); } + virtual const Vector GetEyePositionForSpeech( CBaseEntity *pSpeechTarget ) { return EyePosition(); } + virtual const Vector &GetWorldSpaceCenterForSpeech( CBaseEntity *pSpeechTarget ) { return WorldSpaceCenter(); } + virtual const Vector &GetAbsOriginForSpeech( CBaseEntity *pSpeechTarget ) { return GetAbsOrigin(); } #endif CBaseEntity *GetSpeechTarget() { return m_hTalkTarget.Get(); } diff --git a/sp/src/game/server/ez2/npc_wilson.cpp b/sp/src/game/server/ez2/npc_wilson.cpp index 7a963fb564d..d5102153b87 100644 --- a/sp/src/game/server/ez2/npc_wilson.cpp +++ b/sp/src/game/server/ez2/npc_wilson.cpp @@ -82,6 +82,10 @@ ConVar npc_wilson_depressing_death("npc_wilson_depressing_death", "0", FCVAR_NON ConVar npc_wilson_clearance_speed_threshold( "npc_wilson_clearance_speed_threshold", "250.0", FCVAR_NONE, "The speed at which Will-E starts to think he's gonna get knocked off if approaching a surface." ); ConVar npc_wilson_clearance_debug( "npc_wilson_clearance_debug", "0", FCVAR_NONE, "Debugs Will-E's low clearance detection." ); +ConVar npc_wilson_camera_look_scale_pitch( "npc_wilson_camera_look_scale_pitch", "0.025", FCVAR_NONE, "How much to scale Wilson's eye movements when looking at a target through a camera." ); +ConVar npc_wilson_camera_look_scale_yaw( "npc_wilson_camera_look_scale_yaw", "0.05", FCVAR_NONE, "How much to scale Wilson's eye movements when looking at a target through a camera." ); +ConVar npc_wilson_camera_look_scale_roll( "npc_wilson_camera_look_scale_roll", "0.1", FCVAR_NONE, "How much to scale Wilson's eye movements when looking at a target through a camera." ); + static const char *g_DamageZapContext = "DamageZapEffect"; static const char *g_AutoSetLocatorContext = "AutoSetLocator"; @@ -290,6 +294,12 @@ void CNPC_Wilson::Spawn() CapabilitiesAdd( bits_CAP_SQUAD ); + if ( LookupAttachment( "forward" ) > 0 ) + { + // If we have the "forward" attachment, we can turn our head + CapabilitiesAdd( bits_CAP_TURN_HEAD ); + } + // Add to the player's squad if we have no squad name if (!GetSquad()) AddToSquad( GetPlayerSquadName() ); @@ -951,7 +961,7 @@ void CNPC_Wilson::GatherEnemyConditions( CBaseEntity *pEnemy ) if ( GetLastEnemyTime() == 0 || gpGlobals->curtime - GetLastEnemyTime() > 30 ) { - if ( HasCondition( COND_SEE_ENEMY ) && (WorldSpaceCenter() - pEnemy->WorldSpaceCenter()).LengthSqr() <= Square(384.0f) && pEnemy->Classify() != CLASS_BULLSEYE ) + if ( HasCondition( COND_SEE_ENEMY ) && (GetWorldSpaceCenterForSpeech( pEnemy ) - pEnemy->WorldSpaceCenter()).LengthSqr() <= Square(384.0f) && pEnemy->Classify() != CLASS_BULLSEYE ) { AI_CriteriaSet modifiers; @@ -1050,7 +1060,7 @@ void CNPC_Wilson::OnFriendDamaged( CBaseCombatCharacter *pSquadmate, CBaseEntity if ( pAttacker ) { CBasePlayer *pPlayer = AI_GetSinglePlayer(); - if ( pPlayer && ( pPlayer->GetAbsOrigin().AsVector2D() - GetAbsOrigin().AsVector2D() ).LengthSqr() < Square( 25*12 ) && IsAllowedToSpeak( TLK_WATCHOUT ) ) + if ( pPlayer && ( pPlayer->GetAbsOrigin().AsVector2D() - GetAbsOriginForSpeech( pPlayer ).AsVector2D() ).LengthSqr() < Square( 25*12 ) && IsAllowedToSpeak( TLK_WATCHOUT ) ) { if ( !pPlayer->FInViewCone( pAttacker ) ) { @@ -1341,6 +1351,39 @@ CWilsonCamera *CNPC_Wilson::GetCameraForTarget( CBaseEntity *pTarget ) return NULL; } +//----------------------------------------------------------------------------- +// Purpose: Gets the first camera which can see the target +//----------------------------------------------------------------------------- +CWilsonCamera *CNPC_Wilson::GetCameraForTarget( const Vector &vecTarget ) +{ + FOR_EACH_VEC( m_hCameraTargets, i ) + { + if ( !m_hCameraTargets[i]->IsEnabled() ) + continue; + + if ( m_hCameraTargets[i]->FInViewCone( vecTarget ) && m_hCameraTargets[i]->FVisible( vecTarget ) ) + return m_hCameraTargets[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Position of ears +//----------------------------------------------------------------------------- +Vector CNPC_Wilson::EarPosition( void ) +{ + if ( m_bSeeThroughPlayer ) + { + // Bad Cop has special ears + CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); + if (pPlayer) + return pPlayer->EarPosition(); + } + + return BaseClass::EarPosition(); +} + //----------------------------------------------------------------------------- // Purpose: Return true if this NPC can hear the specified sound //----------------------------------------------------------------------------- @@ -1445,7 +1488,7 @@ bool CNPC_Wilson::DoCustomSpeechAI() } // If we're in a vehicle, use the vehicle's origin instead - Vector vecSearchOrigin = m_hAttachedVehicle ? m_hAttachedVehicle->GetAbsOrigin() : GetAbsOrigin(); + Vector vecSearchOrigin = m_hAttachedVehicle ? m_hAttachedVehicle->GetAbsOrigin() : GetAbsOriginForSpeech( pPlayer ); float flDistSqr = (vecSearchOrigin - pPlayer->GetAbsOrigin()).LengthSqr(); if (flDistSqr >= Square( 300 )) { @@ -1517,6 +1560,14 @@ bool CNPC_Wilson::DoIdleSpeechAI( AISpeechSelection_t *pSelection, int iState ) return true; } + // From SelectIdleSpeech() + // This was implemented for Axon Pariah, although it can be used with turret Wilson too. + if ( GetTimePlayerStaring() > 6 && GetSmoothedVelocity().IsZero() ) + { + if ( SelectSpeechResponse( TLK_STARE, NULL, pTarget, pSelection ) ) + return true; + } + // From SelectIdleSpeech() // Player allies normally reduce how much this is spoken while moving, // but Will-E is supposed to be moved, so that doesn't apply here. @@ -1878,7 +1929,7 @@ void CNPC_Wilson::ModifyOrAppendCriteria(AI_CriteriaSet& set) CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); if (pPlayer) { - set.AppendCriteria( "wilson_distance", CFmtStrN<32>( "%f.3", (GetAbsOrigin() - pPlayer->GetAbsOrigin()).Length() ) ); + set.AppendCriteria( "wilson_distance", CFmtStrN<32>( "%f.3", (GetAbsOriginForSpeech( pPlayer ) - pPlayer->GetAbsOrigin()).Length() ) ); if (pPlayer->IsInAVehicle()) { @@ -2012,8 +2063,8 @@ bool CNPC_Wilson::IsOkToSpeak( ConceptCategory_t category, bool fRespondingToPla } else { - // If we're not responding to the player, don't talk if running a logic_choreo - if ( IsRunningScriptedSceneAndNotPaused( this, false ) ) + // If we're not responding to the player, don't talk if running a non-instanced logic_choreo OR an instanced logic_choreo with speech in it + if ( IsRunningScriptedSceneAndNotPaused( this ) || IsRunningScriptedSceneWithSpeechAndNotPaused( this, false ) ) { return false; } @@ -2109,6 +2160,215 @@ const Vector &CNPC_Wilson::GetSpeechTargetSearchOrigin() return BaseClass::GetSpeechTargetSearchOrigin(); } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector &CNPC_Wilson::GetAbsOriginForSpeech( CBaseEntity *pSpeechTarget ) +{ + CWilsonCamera *pNearestCamera = GetCameraForTarget( pSpeechTarget ); + if (!pNearestCamera) + return BaseClass::GetAbsOriginForSpeech( pSpeechTarget ); + + return pNearestCamera->GetAbsOrigin(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector &CNPC_Wilson::GetWorldSpaceCenterForSpeech( CBaseEntity *pSpeechTarget ) +{ + CWilsonCamera *pNearestCamera = GetCameraForTarget( pSpeechTarget ); + if (!pNearestCamera) + return BaseClass::GetWorldSpaceCenterForSpeech( pSpeechTarget ); + + return pNearestCamera->WorldSpaceCenter(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const Vector CNPC_Wilson::GetEyePositionForSpeech( CBaseEntity *pSpeechTarget ) +{ + CWilsonCamera *pNearestCamera = GetCameraForTarget( pSpeechTarget ); + if (!pNearestCamera) + return BaseClass::GetEyePositionForSpeech( pSpeechTarget ); + + return pNearestCamera->EyePosition(); +} + +#define MIN_LOOK_TARGET_DIST 1.0f +#define MAX_FULL_LOOK_TARGET_DIST 10.0f + +//----------------------------------------------------------------------------- +// Purpose: Returns true if target is in legal range of eye movement for the current head position +//----------------------------------------------------------------------------- +bool CNPC_Wilson::ValidEyeTarget(const Vector &lookTargetPos) +{ + if (!m_bOmniscient) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( lookTargetPos ); + if (!pNearestCamera) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + // Just use head target code for now + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if target is in legal range of possible head movements +//----------------------------------------------------------------------------- +bool CNPC_Wilson::ValidHeadTarget(const Vector &lookTargetPos) +{ + if (!m_bOmniscient) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( lookTargetPos ); + if (!pNearestCamera) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + Vector vFacing; + pNearestCamera->GetVectors( &vFacing, NULL, NULL ); + + Vector lookTargetDir = lookTargetPos - pNearestCamera->EyePosition(); + float flDist = VectorNormalize(lookTargetDir); + + if (flDist < MIN_LOOK_TARGET_DIST) + { + return false; + } + + // Only look if it doesn't crank my head too far + float dotPr = DotProduct(lookTargetDir, vFacing); + if (dotPr > 0 && fabs( lookTargetDir.z ) < 0.7) // +- 90 degrees side to side, +- 45 up/down + { + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns how much to try to look at the target +//----------------------------------------------------------------------------- +float CNPC_Wilson::HeadTargetValidity(const Vector &lookTargetPos) +{ + if (!m_bOmniscient) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( lookTargetPos ); + if (!pNearestCamera) + return BaseClass::ValidEyeTarget( lookTargetPos ); + + Vector vFacing; + pNearestCamera->GetVectors( &vFacing, NULL, NULL ); + + Vector lookTargetDir = lookTargetPos - pNearestCamera->EyePosition(); + float flDist = lookTargetDir.Length2D(); + VectorNormalize(lookTargetDir); + + if (flDist <= MIN_LOOK_TARGET_DIST) + { + return 0; + } + + // Only look if it doesn't crank my head too far + float dotPr = DotProduct(lookTargetDir, vFacing); + // only look if target is within +-135 degrees + // scale 1..-0.707 == 1..1, -.707..-1 == 1..0 + // X * b + b = 1 == 1 / (X + 1) = b, 3.4142 + float flInterest = clamp( 3.4142f + 3.4142f * dotPr, 0.f, 1.f ); + + // stop looking when point too close + if (flDist < MAX_FULL_LOOK_TARGET_DIST) + { + flInterest = flInterest * (flDist - MIN_LOOK_TARGET_DIST ) / (MAX_FULL_LOOK_TARGET_DIST - MIN_LOOK_TARGET_DIST); + } + + return flInterest; +} + +//----------------------------------------------------------------------------- +// Purpose: Overrides head target parameters +//----------------------------------------------------------------------------- +bool CNPC_Wilson::HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vecDir, float &flDist ) +{ + if (!m_bOmniscient) + return BaseClass::HeadTargetPosOverride( vecTargetPos, vecDir, flDist ); + + CWilsonCamera *pNearestCamera = GetCameraForTarget( vecTargetPos ); + if (!pNearestCamera) + return BaseClass::HeadTargetPosOverride( vecTargetPos, vecDir, flDist ); + + // Translate this target position from the camera to Wilson's actual entity + matrix3x4_t matWorldToTarget, matCameraToTarget, matTargetToCamera; + AngleIMatrix( QAngle(), vecTargetPos, matWorldToTarget ); + ConcatTransforms( matWorldToTarget, pNearestCamera->EntityToWorldTransform(), matCameraToTarget ); + MatrixInvert( matCameraToTarget, matTargetToCamera ); + + matrix3x4_t matWilson; + GetAttachment( LookupAttachment( "forward" ), matWilson ); + + matrix3x4_t matTargetToWilson; + ConcatTransforms( matWilson, matTargetToCamera, matTargetToWilson ); + + Vector vecOrigin; + QAngle angAngles; + MatrixAngles( matTargetToWilson, angAngles, vecOrigin ); + + vecDir = vecOrigin - EyePosition(); + flDist = VectorNormalize( vecDir ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Overrides head target parameters +//----------------------------------------------------------------------------- +bool CNPC_Wilson::HeadAngleOverride( QAngle &vTargetAngles ) +{ + if (!m_bOmniscient) + return BaseClass::HeadAngleOverride( vTargetAngles ); + + vTargetAngles -= EyeAngles(); + + vTargetAngles.x *= npc_wilson_camera_look_scale_pitch.GetFloat(); + vTargetAngles.y *= npc_wilson_camera_look_scale_yaw.GetFloat(); + vTargetAngles.z *= npc_wilson_camera_look_scale_roll.GetFloat(); + + vTargetAngles += EyeAngles(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CNPC_Wilson::PickTacticalLookTarget( AILookTargetArgs_t *pArgs ) +{ + if (BaseClass::PickTacticalLookTarget( pArgs )) + { + return true; + } + + if ( HasCondition( COND_SEE_PLAYER ) ) + { + // 1/3rd chance to look at player + if (random->RandomInt( 0, 2 ) == 0) + { + pArgs->flDuration = RandomFloat( 3.0f, 5.0f ); + if (GetState() == NPC_STATE_ALERT) + pArgs->flDuration *= 0.5f; + + pArgs->flInfluence = 0.5f; + pArgs->hTarget = AI_GetSinglePlayer(); + + return true; + } + } + + return false; +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ez2/npc_wilson.h b/sp/src/game/server/ez2/npc_wilson.h index 0d3f3e0bf4e..52c4af183a5 100644 --- a/sp/src/game/server/ez2/npc_wilson.h +++ b/sp/src/game/server/ez2/npc_wilson.h @@ -106,7 +106,9 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); bool FVisible( const Vector &vecTarget, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL ); CWilsonCamera* GetCameraForTarget( CBaseEntity *pTarget ); + CWilsonCamera* GetCameraForTarget( const Vector &vecTarget ); + virtual Vector EarPosition( void ); // position of ears bool QueryHearSound( CSound *pSound ); // Wilson hardly cares about his NPC state because he's just a vessel for choreography and player attachment, not a useful combat ally. @@ -163,6 +165,18 @@ class CNPC_Wilson : public CAI_WilsonBase, public CDefaultPlayerPickupVPhysics, void RefreshCameraTargets(); const Vector& GetSpeechTargetSearchOrigin(); + const Vector& GetAbsOriginForSpeech( CBaseEntity *pSpeechTarget ); + const Vector& GetWorldSpaceCenterForSpeech( CBaseEntity *pSpeechTarget ); + const Vector GetEyePositionForSpeech( CBaseEntity *pSpeechTarget ); + + bool ValidEyeTarget( const Vector &lookTargetPos ); + bool ValidHeadTarget( const Vector &lookTargetPos ); + float HeadTargetValidity( const Vector &lookTargetPos ); + bool HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vecDir, float &flDist ); + bool HeadAngleOverride( QAngle &vTargetAngles ); + float GetHeadDebounce( void ) { return 0.5f; } // how much of previous head turn to use + + bool PickTacticalLookTarget( AILookTargetArgs_t *pArgs ); void InputEnableMotion( inputdata_t &inputdata ); void InputDisableMotion( inputdata_t &inputdata ); From b19f329d5f1e74b975f7d27fe2ed833b16514b91 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 25 Feb 2024 18:30:36 -0600 Subject: [PATCH 041/103] Fix ai_dynint_always_enabled bypassing misc. criteria --- sp/src/game/server/ai_basenpc.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 002f3f36e53..7701ea63910 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -15890,12 +15890,11 @@ bool CAI_BaseNPC::InteractionIsAllowed( CAI_BaseNPC *pOtherNPC, ScriptedNPCInter if (pOtherNPC->Classify() == CLASS_PLAYER_ALLY_VITAL) return false; - // This convar allows all NPCs to perform Mapbase interactions for both testing and player fun. - if (ai_dynint_always_enabled.GetBool() && m_iDynamicInteractionsAllowed != TRS_FALSE) - return true; + if (m_iDynamicInteractionsAllowed == TRS_FALSE) + return false; - // m_iDynamicInteractionsAllowed == TRS_FALSE case is already handled in CanRunAScriptedNPCInteraction(). - if (pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE) + // To maintain existing behavior, Mapbase additions require either explicit TRS_YES or ai_dynint_always_enabled. + if (pInteraction->iFlags & SCNPC_FLAG_MAPBASE_ADDITION && m_iDynamicInteractionsAllowed == TRS_NONE && !ai_dynint_always_enabled.GetBool()) return false; // Test misc. criteria here since some of it may not have been valid on initial calculation, but could be now From f5db760a66f98fa25f490c804c0eb2e1aebdfe53 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 7 Mar 2024 00:13:41 -0600 Subject: [PATCH 042/103] Add raw scene file support to GetSceneDuration() --- sp/src/game/server/sceneentity.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sp/src/game/server/sceneentity.cpp b/sp/src/game/server/sceneentity.cpp index ab70e1974fa..06f8f8fdf68 100644 --- a/sp/src/game/server/sceneentity.cpp +++ b/sp/src/game/server/sceneentity.cpp @@ -5298,6 +5298,23 @@ float GetSceneDuration( char const *pszScene ) { msecs = cachedData.msecs; } +#ifdef MAPBASE + else + { + // Raw scene file support + void *pBuffer = NULL; + if (filesystem->ReadFileEx( pszScene, "MOD", &pBuffer, true )) + { + g_TokenProcessor.SetBuffer((char*)pBuffer); + CChoreoScene *pScene = ChoreoLoadScene( pszScene, NULL, &g_TokenProcessor, LocalScene_Printf ); + g_TokenProcessor.SetBuffer(NULL); + + float flDuration = pScene->GetDuration(); + delete pScene; + return flDuration; + } + } +#endif return (float)msecs * 0.001f; } From e954f00f79ee91a3353d388b73d9b5748e7ce2b6 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 13 Mar 2024 14:00:21 -0500 Subject: [PATCH 043/103] Fix antlions using wrong angles for dynamic interactions --- sp/src/game/server/hl2/npc_antlion.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/hl2/npc_antlion.cpp b/sp/src/game/server/hl2/npc_antlion.cpp index 2d59c9a05f5..bda685eaa16 100644 --- a/sp/src/game/server/hl2/npc_antlion.cpp +++ b/sp/src/game/server/hl2/npc_antlion.cpp @@ -378,13 +378,14 @@ void CNPC_Antlion::Spawn( void ) sInteraction01.vecRelativeOrigin = Vector(224, 0, 0); sInteraction01.angRelativeAngles = QAngle(0, 180, 0); - //sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; sInteraction01.iFlags |= SCNPC_FLAG_TEST_END_POSITION; sInteraction01.vecRelativeEndPos = Vector(312, -10, 0); sInteraction01.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; sInteraction01.flDelay = 15.0f; sInteraction01.iFlags |= SCNPC_FLAG_MAPBASE_ADDITION; sInteraction01.flDistSqr = (8 * 8); + sInteraction01.flMaxAngleDiff = 180.0f; // Initiate from any angle ScriptedNPCInteraction_t sInteraction02; @@ -393,11 +394,12 @@ void CNPC_Antlion::Spawn( void ) sInteraction02.vecRelativeOrigin = Vector(64, 0, 0); sInteraction02.angRelativeAngles = QAngle(0, 180, 0); - //sInteraction01.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; + sInteraction02.iFlags |= SCNPC_FLAG_TEST_OTHER_ANGLES; sInteraction02.iTriggerMethod = SNPCINT_AUTOMATIC_IN_COMBAT; sInteraction02.flDelay = 7.5f; sInteraction02.iFlags |= SCNPC_FLAG_MAPBASE_ADDITION; sInteraction02.flDistSqr = (8 * 8); + sInteraction02.flMaxAngleDiff = 180.0f; // Initiate from any angle AddScriptedNPCInteraction(&sInteraction01); From b59214ca72dae27ac7a3bbb2bd79733b8c40e6af Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 13 Mar 2024 13:59:41 -0500 Subject: [PATCH 044/103] Fix dynamic interactions using "their_" keyvalues always assuming separate sequence names --- sp/src/game/server/ai_basenpc.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 7701ea63910..2d4c064c773 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -14895,22 +14895,39 @@ void CAI_BaseNPC::ParseScriptedNPCInteractions(void) else if (!Q_strncmp(szName, "their_", 6)) { const char *szTheirName = szName + 6; - sInteraction.bHasSeparateSequenceNames = true; if (!Q_strncmp(szTheirName, "entry_sequence", 14)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_ENTRY].iszSequence = AllocPooledString(szValue); + } else if (!Q_strncmp(szTheirName, "entry_activity", 14)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_ENTRY].iActivity = GetOrRegisterActivity(szValue); + } else if (!Q_strncmp(szTheirName, "sequence", 8)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iszSequence = AllocPooledString(szValue); + } else if (!Q_strncmp(szTheirName, "activity", 8)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_SEQUENCE].iActivity = GetOrRegisterActivity(szValue); + } else if (!Q_strncmp(szTheirName, "exit_sequence", 13)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_EXIT].iszSequence = AllocPooledString(szValue); + } else if (!Q_strncmp(szTheirName, "exit_activity", 13)) + { + sInteraction.bHasSeparateSequenceNames = true; sInteraction.sTheirPhases[SNPCINT_EXIT].iActivity = GetOrRegisterActivity(szValue); + } // Add anything else to our miscellaneous criteria else From bdc34e4badd82972b81d7901ef1b35e332d19c86 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 21 Jun 2024 19:30:40 -0500 Subject: [PATCH 045/103] Fix autocubemap crashing when not in a level --- sp/src/game/client/mapbase/mapbase_autocubemap.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sp/src/game/client/mapbase/mapbase_autocubemap.cpp b/sp/src/game/client/mapbase/mapbase_autocubemap.cpp index 03602ef2b4c..79f2497d96a 100644 --- a/sp/src/game/client/mapbase/mapbase_autocubemap.cpp +++ b/sp/src/game/client/mapbase/mapbase_autocubemap.cpp @@ -125,6 +125,12 @@ class CAutoCubemapSystem : public CAutoGameSystem //Msg("No maps to cubemap with!\n"); //return; + if (C_BasePlayer::GetLocalPlayer() == NULL) + { + Msg( "Must be in a level (or have a loaded map list) to begin autocubemap\n" ); + return; + } + // Just do this map m_AutoCubemapMaps.AddToTail( strdup( g_MapName ) ); } From 4efb50283d984416519c720c6a970e94b5589a15 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 21 Jun 2024 20:24:25 -0500 Subject: [PATCH 046/103] Fix "Fade Corpse" spawnflag on NPCs not applying to serverside ragdolls --- sp/src/game/server/basecombatcharacter.cpp | 21 +++++++++++++++------ sp/src/game/server/basecombatcharacter.h | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 57f6c7a55dd..6b21a23a47f 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -1610,6 +1610,15 @@ void CBaseCombatCharacter::FixupBurningServerRagdoll( CBaseEntity *pRagdoll ) } } +inline bool CBaseCombatCharacter::ShouldFadeServerRagdolls() const +{ +#ifdef MAPBASE + return IsNPC() ? HasSpawnFlags( SF_NPC_FADE_CORPSE ) : true; +#else + return true; +#endif +} + bool CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags ) { Assert( CanBecomeRagdoll() ); @@ -1618,7 +1627,7 @@ bool CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, const Vect info.SetDamageForce( forceVector ); - CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); pRagdoll->SetCollisionBounds( CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs() ); @@ -1641,7 +1650,7 @@ CBaseEntity *CBaseCombatCharacter::BecomeRagdollBoogie( CBaseEntity *pKiller, co info.SetDamageForce( forceVector ); - CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, 0, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); pRagdoll->SetCollisionBounds( CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs() ); @@ -1690,7 +1699,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec #endif // in single player create ragdolls on the server when the player hits someone // with their vehicle - for more dramatic death/collisions - CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, info2, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, info2, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); FixupBurningServerRagdoll( pRagdoll ); RemoveDeferred(); return true; @@ -1704,7 +1713,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec // Burning corpses are server-side in episodic, if we're in darkness mode if ( IsOnFire() && HL2GameRules()->IsAlyxInDarknessMode() ) { - CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_DEBRIS ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_DEBRIS, ShouldFadeServerRagdolls() ); FixupBurningServerRagdoll( pRagdoll ); RemoveDeferred(); return true; @@ -1725,7 +1734,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec return false; //FIXME: This is fairly leafy to be here, but time is short! - CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); FixupBurningServerRagdoll( pRagdoll ); PhysSetEntityGameFlags( pRagdoll, FVPHYSICS_NO_SELF_COLLISIONS ); RemoveDeferred(); @@ -1735,7 +1744,7 @@ bool CBaseCombatCharacter::BecomeRagdoll( const CTakeDamageInfo &info, const Vec if( hl2_episodic.GetBool() && Classify() == CLASS_PLAYER_ALLY_VITAL ) { - CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, true ); + CreateServerRagdoll( this, m_nForceBone, newinfo, COLLISION_GROUP_INTERACTIVE_DEBRIS, ShouldFadeServerRagdolls() ); RemoveDeferred(); return true; } diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index cdae243da6b..20d3973e3aa 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -350,6 +350,8 @@ class CBaseCombatCharacter : public CBaseFlex // A version of BecomeRagdollBoogie() that allows the color to change and returns the entity itself instead. // In order to avoid breaking anything, it doesn't change the original function. virtual CBaseEntity *BecomeRagdollBoogie( CBaseEntity *pKiller, const Vector &forceVector, float duration, int flags, const Vector *vecColor ); + + bool ShouldFadeServerRagdolls() const; #endif CBaseEntity *FindHealthItem( const Vector &vecPosition, const Vector &range ); From e5d30605d557ed6524d0d11154971a6958079e1d Mon Sep 17 00:00:00 2001 From: celisej567 Date: Fri, 3 Jan 2025 02:26:39 +0300 Subject: [PATCH 047/103] float NaN undefined behavour fix --- mp/src/game/client/particlemgr.cpp | 2 ++ sp/src/game/client/particlemgr.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/mp/src/game/client/particlemgr.cpp b/mp/src/game/client/particlemgr.cpp index 0eb09a43a9c..fae2ecf3fe8 100644 --- a/mp/src/game/client/particlemgr.cpp +++ b/mp/src/game/client/particlemgr.cpp @@ -146,6 +146,8 @@ CParticleEffectBinding::CParticleEffectBinding() m_LastMin = m_Min; m_LastMax = m_Max; + m_flParticleCullRadius = 0.0f + SetParticleCullRadius( 0.0f ); m_nActiveParticles = 0; diff --git a/sp/src/game/client/particlemgr.cpp b/sp/src/game/client/particlemgr.cpp index d62c6e73874..2376858685f 100644 --- a/sp/src/game/client/particlemgr.cpp +++ b/sp/src/game/client/particlemgr.cpp @@ -146,6 +146,8 @@ CParticleEffectBinding::CParticleEffectBinding() m_LastMin = m_Min; m_LastMax = m_Max; + m_flParticleCullRadius = 0.0f + SetParticleCullRadius( 0.0f ); m_nActiveParticles = 0; From 138f51e791e6bf39f810eca919730b34352bc94f Mon Sep 17 00:00:00 2001 From: celisej567 Date: Fri, 3 Jan 2025 04:01:44 +0300 Subject: [PATCH 048/103] Update particlemgr.cpp --- mp/src/game/client/particlemgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mp/src/game/client/particlemgr.cpp b/mp/src/game/client/particlemgr.cpp index fae2ecf3fe8..615b7cc43e6 100644 --- a/mp/src/game/client/particlemgr.cpp +++ b/mp/src/game/client/particlemgr.cpp @@ -146,7 +146,7 @@ CParticleEffectBinding::CParticleEffectBinding() m_LastMin = m_Min; m_LastMax = m_Max; - m_flParticleCullRadius = 0.0f + m_flParticleCullRadius = 0.0f; SetParticleCullRadius( 0.0f ); m_nActiveParticles = 0; From 93bec9d7c0d65a63d2660c6134725d65cea1f64b Mon Sep 17 00:00:00 2001 From: celisej567 Date: Fri, 3 Jan 2025 04:28:09 +0300 Subject: [PATCH 049/103] Update particlemgr.cpp --- sp/src/game/client/particlemgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/client/particlemgr.cpp b/sp/src/game/client/particlemgr.cpp index 2376858685f..b37a2000389 100644 --- a/sp/src/game/client/particlemgr.cpp +++ b/sp/src/game/client/particlemgr.cpp @@ -146,7 +146,7 @@ CParticleEffectBinding::CParticleEffectBinding() m_LastMin = m_Min; m_LastMax = m_Max; - m_flParticleCullRadius = 0.0f + m_flParticleCullRadius = 0.0f; SetParticleCullRadius( 0.0f ); m_nActiveParticles = 0; From 959af0b130dfa29f74c34a60874a555c1dec2952 Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Sun, 5 Jan 2025 16:13:16 +0100 Subject: [PATCH 050/103] m_iMeleeReach now defaults to 55 --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 6 +++++- sp/src/game/server/hl2/npc_BaseZombie.h | 21 +++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index 0799fcf6d55..6e389defbdf 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -211,7 +211,7 @@ BEGIN_DATADESC( CNPC_BaseZombie ) DEFINE_FIELD( m_fIsTorso, FIELD_BOOLEAN ), #ifdef MAPBASE DEFINE_KEYFIELD( m_fIsHeadless, FIELD_BOOLEAN, "Headless" ), - DEFINE_KEYFIELD( m_flMeleeReach, FIELD_FLOAT, "MeleeReach" ), + DEFINE_KEYFIELD( m_iMeleeReach, FIELD_INTEGER, "MeleeReach" ), #else DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ), #endif @@ -257,6 +257,10 @@ CNPC_BaseZombie::CNPC_BaseZombie() // moan loop. m_iMoanSound = g_numZombies; +#ifdef MAPBASE + m_iMeleeReach = 55; +#endif + g_numZombies++; } diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index 16ca0db9c23..b515144f6ac 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -20,6 +20,10 @@ #define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController()) +#ifndef MAPBASE + #define ZOMBIE_MELEE_REACH 55 +#endif + extern int AE_ZOMBIE_ATTACK_RIGHT; extern int AE_ZOMBIE_ATTACK_LEFT; extern int AE_ZOMBIE_ATTACK_BOTH; @@ -42,8 +46,8 @@ extern int AE_ZOMBIE_POUND; #define ZOMBIE_BLOOD_BITE 3 #ifdef MAPBASE -#define SF_ZOMBIE_NO_TORSO ( 1 << 15 ) -#define SF_ZOMBIE_NO_HEADCRAB_SPAWN ( 1 << 16 ) + #define SF_ZOMBIE_NO_TORSO ( 1 << 15 ) + #define SF_ZOMBIE_NO_HEADCRAB_SPAWN ( 1 << 16 ) #endif @@ -140,7 +144,14 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase } int MeleeAttack1Conditions ( float flDot, float flDist ); - virtual float GetClawAttackRange() const { return m_flMeleeReach; } + virtual float GetClawAttackRange() const + { +#ifdef MAPBASE + return m_iMeleeReach; +#else + return ZOMBIE_MELEE_REACH; +#endif + } // No range attacks int RangeAttack1Conditions ( float flDot, float flDist ) { return( 0 ); } @@ -256,7 +267,9 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase float m_flNextFlinch; - float m_flMeleeReach; +#ifdef MAPBASE + int m_iMeleeReach; +#endif bool m_bHeadShot; // Used to determine the survival of our crab beyond our death. From 99c94f058b9c9b461a562cf51b8f3d9d3dcee75a Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:54:54 +0100 Subject: [PATCH 051/103] Added more customizability --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 25 ++++++++++++++++++----- sp/src/game/server/hl2/npc_BaseZombie.h | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index 6e389defbdf..0c392f747b6 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -85,9 +85,16 @@ envelopePoint_t envDefaultZombieMoanVolume[] = #define ZOMBIE_FARTHEST_PHYSICS_OBJECT 40.0*12.0 #define ZOMBIE_PHYSICS_SEARCH_DEPTH 100 +#ifndef MAPBASE + // Don't swat objects unless player is closer than this. #define ZOMBIE_PLAYER_MAX_SWAT_DIST 1000 +// The heaviest physics object that a zombie should try to swat. (kg) +#define ZOMBIE_MAX_PHYSOBJ_MASS 60 + +#endif + // // How much health a Zombie torso gets when a whole zombie is broken // It's whole zombie's MAX Health * this value @@ -98,10 +105,6 @@ envelopePoint_t envDefaultZombieMoanVolume[] = // try to release its headcrab. #define ZOMBIE_RELEASE_HEALTH_FACTOR 0.5 -// -// The heaviest physics object that a zombie should try to swat. (kg) -#define ZOMBIE_MAX_PHYSOBJ_MASS 60 - // // Zombie tries to get this close to a physics object's origin to swat it #define ZOMBIE_PHYSOBJ_SWATDIST 80 @@ -212,6 +215,8 @@ BEGIN_DATADESC( CNPC_BaseZombie ) #ifdef MAPBASE DEFINE_KEYFIELD( m_fIsHeadless, FIELD_BOOLEAN, "Headless" ), DEFINE_KEYFIELD( m_iMeleeReach, FIELD_INTEGER, "MeleeReach" ), + DEFINE_KEYFIELD( m_iMaxPlayerDistToSwat, FIELD_INTEGER, "MaxPlayerDistToSwat" ), + DEFINE_KEYFIELD( m_iMaxObjWeightToSwat, FIELD_INTEGER, "MaxObjWeightToSwat" ), #else DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ), #endif @@ -259,6 +264,8 @@ CNPC_BaseZombie::CNPC_BaseZombie() #ifdef MAPBASE m_iMeleeReach = 55; + m_iMaxPlayerDistToSwat = 1000; + m_iMaxObjWeightToSwat = 60; #endif g_numZombies++; @@ -300,7 +307,11 @@ bool CNPC_BaseZombie::FindNearestPhysicsObject( int iMaxMass ) float dist = VectorNormalize(vecDirToEnemy); vecDirToEnemy.z = 0; - if( dist > ZOMBIE_PLAYER_MAX_SWAT_DIST ) +#ifndef MAPBASE + if (dist > ZOMBIE_PLAYER_MAX_SWAT_DIST) +#else + if (dist > m_iMaxPlayerDistToSwat) +#endif { // Player is too far away. Don't bother // trying to swat anything at them until @@ -2159,7 +2170,11 @@ void CNPC_BaseZombie::GatherConditions( void ) // between him and the object he's heading for already. if( gpGlobals->curtime >= m_flNextSwatScan && (m_hPhysicsEnt == NULL) ) { +#ifdef MAPBASE + FindNearestPhysicsObject( m_iMaxObjWeightToSwat ); +#else FindNearestPhysicsObject( ZOMBIE_MAX_PHYSOBJ_MASS ); +#endif m_flNextSwatScan = gpGlobals->curtime + 2.0; } } diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index b515144f6ac..c6d309cb6ae 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -269,6 +269,8 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase #ifdef MAPBASE int m_iMeleeReach; + int m_iMaxPlayerDistToSwat; + int m_iMaxObjWeightToSwat; #endif bool m_bHeadShot; // Used to determine the survival of our crab beyond our death. From a00c8234e43ccf6b166d29f29b74e40047f019a5 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 4 Jan 2025 08:35:16 -0600 Subject: [PATCH 052/103] Expose prop_door_rotating to VScript --- sp/src/game/server/BasePropDoor.h | 31 ++++++++++++ sp/src/game/server/props.cpp | 82 +++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/sp/src/game/server/BasePropDoor.h b/sp/src/game/server/BasePropDoor.h index 7e8400d22c8..ea14d20d354 100644 --- a/sp/src/game/server/BasePropDoor.h +++ b/sp/src/game/server/BasePropDoor.h @@ -38,6 +38,9 @@ abstract_class CBasePropDoor : public CDynamicProp DECLARE_CLASS( CBasePropDoor, CDynamicProp ); DECLARE_SERVERCLASS(); +#ifdef MAPBASE_VSCRIPT + DECLARE_ENT_SCRIPTDESC(); +#endif CBasePropDoor( void ); @@ -79,6 +82,28 @@ abstract_class CBasePropDoor : public CDynamicProp virtual bool PassesDoorFilter(CBaseEntity *pEntity) { return true; } virtual bool KeyValue( const char *szKeyName, const char *szValue ); + + float GetSpeed() const { return m_flSpeed; } +#endif + +#ifdef MAPBASE_VSCRIPT + bool ScriptIsDoorOpen() { return IsDoorOpen(); } + bool ScriptIsDoorAjar() { return IsDoorAjar(); } + bool ScriptIsDoorOpening() { return IsDoorOpening(); } + bool ScriptIsDoorClosed() { return IsDoorClosed(); } + bool ScriptIsDoorClosing() { return IsDoorClosing(); } + bool ScriptIsDoorLocked() { return IsDoorLocked(); } + bool ScriptIsDoorBlocked() const { return IsDoorBlocked(); } + HSCRIPT ScriptGetActivator() { return ToHScript( m_hActivator.Get() ); } + + HSCRIPT ScriptGetDoorList( int i ) { return m_hDoorList.IsValidIndex(i) ? ToHScript( m_hDoorList[i] ) : NULL; } + int GetDoorListCount() { return m_hDoorList.Count(); } + + const char *ScriptGetFullyOpenSound() { return STRING( m_SoundOpen ); } + const char *ScriptGetFullyClosedSound() { return STRING( m_SoundClose ); } + const char *ScriptGetMovingSound() { return STRING( m_SoundMoving ); } + const char *ScriptGetLockedSound() { return STRING( m_ls.sLockedSound ); } + const char *ScriptGetUnlockedSound() { return STRING( m_ls.sUnlockedSound ); } #endif protected: @@ -178,6 +203,12 @@ abstract_class CBasePropDoor : public CDynamicProp #ifdef MAPBASE void InputAllowPlayerUse(inputdata_t &inputdata); void InputDisallowPlayerUse(inputdata_t &inputdata); + + void InputSetFullyOpenSound(inputdata_t &inputdata); + void InputSetFullyClosedSound(inputdata_t &inputdata); + void InputSetMovingSound(inputdata_t &inputdata); + void InputSetLockedSound(inputdata_t &inputdata); + void InputSetUnlockedSound(inputdata_t &inputdata); #endif void SetDoorBlocker( CBaseEntity *pBlocker ); diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index 33aba2638f9..57e33d87421 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -4206,6 +4206,12 @@ BEGIN_DATADESC(CBasePropDoor) #ifdef MAPBASE DEFINE_INPUTFUNC(FIELD_VOID, "AllowPlayerUse", InputAllowPlayerUse), DEFINE_INPUTFUNC(FIELD_VOID, "DisallowPlayerUse", InputDisallowPlayerUse), + + DEFINE_INPUTFUNC( FIELD_STRING, "SetFullyOpenSound", InputSetFullyOpenSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetFullyClosedSound", InputSetFullyClosedSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetMovingSound", InputSetMovingSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetLockedSound", InputSetLockedSound ), + DEFINE_INPUTFUNC( FIELD_STRING, "SetUnlockedSound", InputSetUnlockedSound ), #endif DEFINE_OUTPUT(m_OnBlockedOpening, "OnBlockedOpening"), @@ -4228,6 +4234,34 @@ END_DATADESC() IMPLEMENT_SERVERCLASS_ST(CBasePropDoor, DT_BasePropDoor) END_SEND_TABLE() +#ifdef MAPBASE_VSCRIPT +BEGIN_ENT_SCRIPTDESC( CBasePropDoor, CBaseAnimating, "The base class used by prop doors, such as prop_door_rotating." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorOpen, "IsDoorOpen", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorAjar, "IsDoorAjar", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorOpening, "IsDoorOpening", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorClosed, "IsDoorClosed", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorClosing, "IsDoorClosing", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorLocked, "IsDoorLocked", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptIsDoorBlocked, "IsDoorBlocked", "" ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetActivator, "GetActivator", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetDoorList, "GetDoorList", "Get connected door entity by index." ) + DEFINE_SCRIPTFUNC( GetDoorListCount, "Get number of connected doors." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFullyOpenSound, "GetFullyOpenSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetFullyClosedSound, "GetFullyClosedSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetMovingSound, "GetMovingSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetLockedSound, "GetLockedSound", "" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUnlockedSound, "GetUnlockedSound", "" ) + + DEFINE_SCRIPTFUNC( DoorCanClose, "Return true if the door has room to close. Boolean is for whether or not this is an automatic close and not manually triggered by someone." ) + DEFINE_SCRIPTFUNC( DoorCanOpen, "Return true if there are other doors connected to this one." ) + DEFINE_SCRIPTFUNC( HasSlaves, "" ) + +END_SCRIPTDESC(); +#endif + CBasePropDoor::CBasePropDoor( void ) { m_hMaster = NULL; @@ -4701,6 +4735,54 @@ void CBasePropDoor::InputOpenAwayFrom(inputdata_t &inputdata) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetFullyOpenSound( inputdata_t &inputdata ) +{ + m_SoundOpen = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_SoundOpen ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetFullyClosedSound( inputdata_t &inputdata ) +{ + m_SoundClose = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_SoundClose ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetMovingSound( inputdata_t &inputdata ) +{ + m_SoundMoving = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_SoundMoving ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetLockedSound( inputdata_t &inputdata ) +{ + m_ls.sLockedSound = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_ls.sLockedSound ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBasePropDoor::InputSetUnlockedSound( inputdata_t &inputdata ) +{ + m_ls.sUnlockedSound = inputdata.value.StringID(); + PrecacheScriptSound( STRING( m_ls.sUnlockedSound ) ); +} +#endif + + //----------------------------------------------------------------------------- // Purpose: // From ed476cbbe71a45c75b696bccfe96d3cfe427893c Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 8 Jan 2025 09:28:38 -0600 Subject: [PATCH 053/103] Increment Mapbase version to v7.3 --- sp/src/public/tier0/platform.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/public/tier0/platform.h b/sp/src/public/tier0/platform.h index 457e5ec985b..25bf9bc1644 100644 --- a/sp/src/public/tier0/platform.h +++ b/sp/src/public/tier0/platform.h @@ -1281,8 +1281,8 @@ PLATFORM_INTERFACE bool Is64BitOS(); //----------------------------------------------------------------------------- // General Mapbase version constants compiled into projects for versioning purposes //----------------------------------------------------------------------------- -#define MAPBASE_VERSION "7.2" -#define MAPBASE_VER_INT 7200 // For use in #if in a similar fashion to macros like _MSC_VER +#define MAPBASE_VERSION "7.3" +#define MAPBASE_VER_INT 7300 // For use in #if in a similar fashion to macros like _MSC_VER #endif From 0921f7409be0557011d41ca626bbfa13afdaed0a Mon Sep 17 00:00:00 2001 From: Wikot235 <149392035+Wikot235@users.noreply.github.com> Date: Wed, 8 Jan 2025 21:22:06 +0100 Subject: [PATCH 054/103] Fixed miscellaneous things. --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 21 +++++++++------------ sp/src/game/server/hl2/npc_BaseZombie.h | 6 ++---- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index 0c392f747b6..bb237242d93 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -85,16 +85,9 @@ envelopePoint_t envDefaultZombieMoanVolume[] = #define ZOMBIE_FARTHEST_PHYSICS_OBJECT 40.0*12.0 #define ZOMBIE_PHYSICS_SEARCH_DEPTH 100 -#ifndef MAPBASE - // Don't swat objects unless player is closer than this. #define ZOMBIE_PLAYER_MAX_SWAT_DIST 1000 -// The heaviest physics object that a zombie should try to swat. (kg) -#define ZOMBIE_MAX_PHYSOBJ_MASS 60 - -#endif - // // How much health a Zombie torso gets when a whole zombie is broken // It's whole zombie's MAX Health * this value @@ -105,6 +98,10 @@ envelopePoint_t envDefaultZombieMoanVolume[] = // try to release its headcrab. #define ZOMBIE_RELEASE_HEALTH_FACTOR 0.5 +// +// The heaviest physics object that a zombie should try to swat. (kg) +#define ZOMBIE_MAX_PHYSOBJ_MASS 60 + // // Zombie tries to get this close to a physics object's origin to swat it #define ZOMBIE_PHYSOBJ_SWATDIST 80 @@ -216,7 +213,7 @@ BEGIN_DATADESC( CNPC_BaseZombie ) DEFINE_KEYFIELD( m_fIsHeadless, FIELD_BOOLEAN, "Headless" ), DEFINE_KEYFIELD( m_iMeleeReach, FIELD_INTEGER, "MeleeReach" ), DEFINE_KEYFIELD( m_iMaxPlayerDistToSwat, FIELD_INTEGER, "MaxPlayerDistToSwat" ), - DEFINE_KEYFIELD( m_iMaxObjWeightToSwat, FIELD_INTEGER, "MaxObjWeightToSwat" ), + DEFINE_KEYFIELD( m_iMaxObjMassToSwat, FIELD_INTEGER, "MaxObjMassToSwat" ), #else DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ), #endif @@ -263,9 +260,9 @@ CNPC_BaseZombie::CNPC_BaseZombie() m_iMoanSound = g_numZombies; #ifdef MAPBASE - m_iMeleeReach = 55; - m_iMaxPlayerDistToSwat = 1000; - m_iMaxObjWeightToSwat = 60; + m_iMeleeReach = ZOMBIE_MELEE_REACH; + m_iMaxPlayerDistToSwat = ZOMBIE_PLAYER_MAX_SWAT_DIST; + m_iMaxObjMassToSwat = ZOMBIE_MAX_PHYSOBJ_MASS; #endif g_numZombies++; @@ -2171,7 +2168,7 @@ void CNPC_BaseZombie::GatherConditions( void ) if( gpGlobals->curtime >= m_flNextSwatScan && (m_hPhysicsEnt == NULL) ) { #ifdef MAPBASE - FindNearestPhysicsObject( m_iMaxObjWeightToSwat ); + FindNearestPhysicsObject(m_iMaxObjMassToSwat); #else FindNearestPhysicsObject( ZOMBIE_MAX_PHYSOBJ_MASS ); #endif diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index c6d309cb6ae..789d7d9bede 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -20,9 +20,7 @@ #define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController()) -#ifndef MAPBASE - #define ZOMBIE_MELEE_REACH 55 -#endif +#define ZOMBIE_MELEE_REACH 55 extern int AE_ZOMBIE_ATTACK_RIGHT; extern int AE_ZOMBIE_ATTACK_LEFT; @@ -270,7 +268,7 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase #ifdef MAPBASE int m_iMeleeReach; int m_iMaxPlayerDistToSwat; - int m_iMaxObjWeightToSwat; + int m_iMaxObjMassToSwat; #endif bool m_bHeadShot; // Used to determine the survival of our crab beyond our death. From aa31835f36fdd50568a1110a64981b044382f2fc Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 9 Jan 2025 12:50:19 -0600 Subject: [PATCH 055/103] Change new CNPC_BaseZombie customization vars to match actual data types --- sp/src/game/server/hl2/npc_BaseZombie.cpp | 10 +++++----- sp/src/game/server/hl2/npc_BaseZombie.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sp/src/game/server/hl2/npc_BaseZombie.cpp b/sp/src/game/server/hl2/npc_BaseZombie.cpp index bb237242d93..f8609f493ac 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.cpp +++ b/sp/src/game/server/hl2/npc_BaseZombie.cpp @@ -211,8 +211,8 @@ BEGIN_DATADESC( CNPC_BaseZombie ) DEFINE_FIELD( m_fIsTorso, FIELD_BOOLEAN ), #ifdef MAPBASE DEFINE_KEYFIELD( m_fIsHeadless, FIELD_BOOLEAN, "Headless" ), - DEFINE_KEYFIELD( m_iMeleeReach, FIELD_INTEGER, "MeleeReach" ), - DEFINE_KEYFIELD( m_iMaxPlayerDistToSwat, FIELD_INTEGER, "MaxPlayerDistToSwat" ), + DEFINE_KEYFIELD( m_flMeleeReach, FIELD_FLOAT, "MeleeReach" ), + DEFINE_KEYFIELD( m_flMaxDistToSwat, FIELD_FLOAT, "MaxDistToSwat" ), DEFINE_KEYFIELD( m_iMaxObjMassToSwat, FIELD_INTEGER, "MaxObjMassToSwat" ), #else DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ), @@ -260,8 +260,8 @@ CNPC_BaseZombie::CNPC_BaseZombie() m_iMoanSound = g_numZombies; #ifdef MAPBASE - m_iMeleeReach = ZOMBIE_MELEE_REACH; - m_iMaxPlayerDistToSwat = ZOMBIE_PLAYER_MAX_SWAT_DIST; + m_flMeleeReach = ZOMBIE_MELEE_REACH; + m_flMaxDistToSwat = ZOMBIE_PLAYER_MAX_SWAT_DIST; m_iMaxObjMassToSwat = ZOMBIE_MAX_PHYSOBJ_MASS; #endif @@ -307,7 +307,7 @@ bool CNPC_BaseZombie::FindNearestPhysicsObject( int iMaxMass ) #ifndef MAPBASE if (dist > ZOMBIE_PLAYER_MAX_SWAT_DIST) #else - if (dist > m_iMaxPlayerDistToSwat) + if (dist > m_flMaxDistToSwat) #endif { // Player is too far away. Don't bother diff --git a/sp/src/game/server/hl2/npc_BaseZombie.h b/sp/src/game/server/hl2/npc_BaseZombie.h index 789d7d9bede..cfc115667ad 100644 --- a/sp/src/game/server/hl2/npc_BaseZombie.h +++ b/sp/src/game/server/hl2/npc_BaseZombie.h @@ -145,7 +145,7 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase virtual float GetClawAttackRange() const { #ifdef MAPBASE - return m_iMeleeReach; + return m_flMeleeReach; #else return ZOMBIE_MELEE_REACH; #endif @@ -266,8 +266,8 @@ abstract_class CNPC_BaseZombie : public CAI_BaseZombieBase float m_flNextFlinch; #ifdef MAPBASE - int m_iMeleeReach; - int m_iMaxPlayerDistToSwat; + float m_flMeleeReach; + float m_flMaxDistToSwat; int m_iMaxObjMassToSwat; #endif From a5c754dd00702f7b9d6bcde19116b987a94dadcb Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 2 Jan 2025 11:22:51 -0600 Subject: [PATCH 056/103] Fixes and expansions for Lost/FoundEnemySound on NPCs --- sp/src/game/server/ai_basenpc.cpp | 8 +++++++ sp/src/game/server/ai_basenpc.h | 5 +++++ sp/src/game/server/ai_playerally.cpp | 25 ++++++++++++++++++++++ sp/src/game/server/ai_playerally.h | 7 ++++++ sp/src/game/server/hl2/npc_combine.cpp | 20 +++++++++++++++-- sp/src/game/server/hl2/npc_combine.h | 5 +++++ sp/src/game/server/hl2/npc_metropolice.cpp | 20 +++++++++++++++-- sp/src/game/server/hl2/npc_metropolice.h | 5 +++++ 8 files changed, 91 insertions(+), 4 deletions(-) diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index 1622ec8d895..f087f8a40e0 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -6110,7 +6110,11 @@ bool CAI_BaseNPC::UpdateEnemyMemory( CBaseEntity *pEnemy, const Vector &position // If the was eluding me and allow the NPC to play a sound if (GetEnemies()->HasEludedMe(pEnemy)) { +#ifdef MAPBASE + FoundEnemySound( pEnemy ); +#else FoundEnemySound(); +#endif } float reactionDelay = ( !pInformer || pInformer == this ) ? GetReactionDelay( pEnemy ) : 0.0; bool result = GetEnemies()->UpdateMemory(GetNavigator()->GetNetwork(), pEnemy, position, reactionDelay, firstHand); @@ -11734,7 +11738,11 @@ bool CAI_BaseNPC::ChooseEnemy( void ) if ( fEnemyEluded ) { SetCondition( COND_LOST_ENEMY ); +#ifdef MAPBASE + LostEnemySound( pInitialEnemy ); +#else LostEnemySound(); +#endif } if ( fEnemyWasPlayer ) diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index dbc229e22f9..7db67d33e41 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -1401,6 +1401,11 @@ class CAI_BaseNPC : public CBaseCombatCharacter, virtual void FearSound( void ) { return; }; virtual void LostEnemySound( void ) { return; }; virtual void FoundEnemySound( void ) { return; }; +#ifdef MAPBASE + // New versions of the above functions which pass the enemy in question as a parameter. Chains to the original by default + virtual void LostEnemySound( CBaseEntity *pEnemy ) { LostEnemySound(); }; + virtual void FoundEnemySound( CBaseEntity *pEnemy ) { FoundEnemySound(); }; +#endif virtual void BarnacleDeathSound( void ) { CTakeDamageInfo info; PainSound( info ); } virtual void SpeakSentence( int sentenceType ) { return; }; diff --git a/sp/src/game/server/ai_playerally.cpp b/sp/src/game/server/ai_playerally.cpp index af969868746..ea7c986f170 100644 --- a/sp/src/game/server/ai_playerally.cpp +++ b/sp/src/game/server/ai_playerally.cpp @@ -14,6 +14,7 @@ #include "gameinterface.h" #ifdef MAPBASE #include "mapbase/matchers.h" +#include "ai_memory.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -133,6 +134,8 @@ ConceptInfo_t g_ConceptInfos[] = { TLK_TAKING_FIRE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, { TLK_NEW_ENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, { TLK_COMBAT_IDLE, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, + { TLK_LOSTENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, + { TLK_REFINDENEMY, SPEECH_IMPORTANT,-1, -1, -1, -1, -1, -1, AICF_DEFAULT, }, #endif }; @@ -1426,6 +1429,28 @@ void CAI_PlayerAlly::PainSound( const CTakeDamageInfo &info ) SpeakIfAllowed( TLK_WOUND ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +void CAI_PlayerAlly::LostEnemySound( CBaseEntity *pEnemy ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + modifiers.AppendCriteria( "lastseenenemy", gpGlobals->curtime - GetEnemies()->LastTimeSeen( pEnemy ) ); + + SpeakIfAllowed( TLK_LOSTENEMY, modifiers ); +} + +//----------------------------------------------------------------------------- +void CAI_PlayerAlly::FoundEnemySound( CBaseEntity *pEnemy ) +{ + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + SpeakIfAllowed( TLK_REFINDENEMY, modifiers ); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Implemented to look at talk target //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index 47ddf91631f..8c34333a098 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -137,6 +137,8 @@ #define TLK_TAKING_FIRE "TLK_TAKING_FIRE" // Someone fired at me (regardless of whether I was hit) #define TLK_NEW_ENEMY "TLK_NEW_ENEMY" // A new enemy appeared while combat was already in progress #define TLK_COMBAT_IDLE "TLK_COMBAT_IDLE" // Similar to TLK_ATTACKING, but specifically for when *not* currently attacking (e.g. when in cover or reloading) +#define TLK_LOSTENEMY "TLK_LOSTENEMY" // Current enemy has eluded squad +#define TLK_REFINDENEMY "TLK_REFINDENEMY" // Found a previously eluded enemy #endif //----------------------------------------------------------------------------- @@ -339,6 +341,11 @@ class CAI_PlayerAlly : public CAI_BaseActor virtual void PainSound( const CTakeDamageInfo &info ); +#ifdef MAPBASE + virtual void LostEnemySound( CBaseEntity *pEnemy ); + virtual void FoundEnemySound( CBaseEntity *pEnemy ); +#endif + //--------------------------------- // Speech & Acting //--------------------------------- diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index c2cac902395..93116b4d8fd 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -3145,13 +3145,22 @@ void CNPC_Combine::PainSound ( void ) // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_Combine::LostEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_Combine::LostEnemySound( void) +#endif { if ( gpGlobals->curtime <= m_flNextLostSoundTime ) return; #ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM - if (SpeakIfAllowed( TLK_CMB_LOSTENEMY, UTIL_VarArgs("lastseenenemy:%d", GetEnemyLastTimeSeen()) )) + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + modifiers.AppendCriteria( "lastseenenemy", gpGlobals->curtime - GetEnemies()->LastTimeSeen( pEnemy ) ); + + if (SpeakIfAllowed( TLK_CMB_LOSTENEMY, modifiers )) { m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); } @@ -3179,10 +3188,17 @@ void CNPC_Combine::LostEnemySound( void) // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_Combine::FoundEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_Combine::FoundEnemySound( void) +#endif { #ifdef COMBINE_SOLDIER_USES_RESPONSE_SYSTEM - SpeakIfAllowed( TLK_CMB_REFINDENEMY, SENTENCE_PRIORITY_HIGH ); + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + SpeakIfAllowed( TLK_CMB_REFINDENEMY, modifiers, SENTENCE_PRIORITY_HIGH ); #else m_Sentences.Speak( "COMBINE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH ); #endif diff --git a/sp/src/game/server/hl2/npc_combine.h b/sp/src/game/server/hl2/npc_combine.h index c9af8010639..d6a500b94dd 100644 --- a/sp/src/game/server/hl2/npc_combine.h +++ b/sp/src/game/server/hl2/npc_combine.h @@ -181,8 +181,13 @@ class CNPC_Combine : public CAI_BaseActor #endif void IdleSound( void ); void AlertSound( void ); +#ifdef MAPBASE + void LostEnemySound( CBaseEntity *pEnemy ); + void FoundEnemySound( CBaseEntity *pEnemy ); +#else void LostEnemySound( void ); void FoundEnemySound( void ); +#endif void AnnounceAssault( void ); void AnnounceEnemyType( CBaseEntity *pEnemy ); void AnnounceEnemyKill( CBaseEntity *pEnemy ); diff --git a/sp/src/game/server/hl2/npc_metropolice.cpp b/sp/src/game/server/hl2/npc_metropolice.cpp index ed013fa34cb..40f1d5be90a 100644 --- a/sp/src/game/server/hl2/npc_metropolice.cpp +++ b/sp/src/game/server/hl2/npc_metropolice.cpp @@ -2938,7 +2938,11 @@ void CNPC_MetroPolice::DeathSound( const CTakeDamageInfo &info ) // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_MetroPolice::LostEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_MetroPolice::LostEnemySound( void) +#endif { // Don't announce enemies when the player isn't a criminal if ( !PlayerIsCriminal() ) @@ -2948,7 +2952,12 @@ void CNPC_MetroPolice::LostEnemySound( void) return; #ifdef METROPOLICE_USES_RESPONSE_SYSTEM - if (SpeakIfAllowed(TLK_COP_LOSTENEMY)) + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + modifiers.AppendCriteria( "lastseenenemy", gpGlobals->curtime - GetEnemies()->LastTimeSeen( pEnemy ) ); + + if (SpeakIfAllowed(TLK_COP_LOSTENEMY, modifiers )) { m_flNextLostSoundTime = gpGlobals->curtime + random->RandomFloat(5.0,15.0); } @@ -2977,14 +2986,21 @@ void CNPC_MetroPolice::LostEnemySound( void) // Input : // Output : //----------------------------------------------------------------------------- +#ifdef MAPBASE +void CNPC_MetroPolice::FoundEnemySound( CBaseEntity *pEnemy ) +#else void CNPC_MetroPolice::FoundEnemySound( void) +#endif { // Don't announce enemies when I'm in arrest behavior if ( HasSpawnFlags( SF_METROPOLICE_ARREST_ENEMY ) ) return; #ifdef METROPOLICE_USES_RESPONSE_SYSTEM - SpeakIfAllowed( TLK_COP_REFINDENEMY, SENTENCE_PRIORITY_HIGH ); + AI_CriteriaSet modifiers; + ModifyOrAppendEnemyCriteria( modifiers, pEnemy ); + + SpeakIfAllowed( TLK_COP_REFINDENEMY, modifiers, SENTENCE_PRIORITY_HIGH ); #else m_Sentences.Speak( "METROPOLICE_REFIND_ENEMY", SENTENCE_PRIORITY_HIGH ); #endif diff --git a/sp/src/game/server/hl2/npc_metropolice.h b/sp/src/game/server/hl2/npc_metropolice.h index 742de7e3a79..1027786bfe8 100644 --- a/sp/src/game/server/hl2/npc_metropolice.h +++ b/sp/src/game/server/hl2/npc_metropolice.h @@ -170,8 +170,13 @@ class CNPC_MetroPolice : public CAI_BaseActor void SpeakAssaultSentence( int nSentenceType ); void SpeakStandoffSentence( int nSentenceType ); +#ifdef MAPBASE + virtual void LostEnemySound( CBaseEntity *pEnemy ); + virtual void FoundEnemySound( CBaseEntity *pEnemy ); +#else virtual void LostEnemySound( void ); virtual void FoundEnemySound( void ); +#endif virtual void AlertSound( void ); virtual void PainSound( const CTakeDamageInfo &info ); virtual void DeathSound( const CTakeDamageInfo &info ); From ad423d1d0775d1f55e74e0fa62de9939e063e153 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 5 Jan 2025 13:06:06 -0600 Subject: [PATCH 057/103] OnStartTipped output for npc_turret_floor --- sp/src/game/server/hl2/npc_turret_floor.cpp | 7 +++++++ sp/src/game/server/hl2/npc_turret_floor.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/sp/src/game/server/hl2/npc_turret_floor.cpp b/sp/src/game/server/hl2/npc_turret_floor.cpp index 8698e742e42..f365dfd74ab 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.cpp +++ b/sp/src/game/server/hl2/npc_turret_floor.cpp @@ -152,6 +152,9 @@ BEGIN_DATADESC( CNPC_FloorTurret ) DEFINE_OUTPUT( m_OnTipped, "OnTipped" ), DEFINE_OUTPUT( m_OnPhysGunPickup, "OnPhysGunPickup" ), DEFINE_OUTPUT( m_OnPhysGunDrop, "OnPhysGunDrop" ), +#ifdef MAPBASE + DEFINE_OUTPUT( m_OnStartTipped, "OnStartTipped" ), +#endif DEFINE_BASENPCINTERACTABLE_DATADESC(), @@ -1526,6 +1529,10 @@ bool CNPC_FloorTurret::PreThink( turretState_e state ) SetEyeState( TURRET_EYE_DEAD ); } +#ifdef MAPBASE + m_OnStartTipped.FireOutput( this, this ); +#endif + //Stop being targetted SetState( NPC_STATE_DEAD ); m_lifeState = LIFE_DEAD; diff --git a/sp/src/game/server/hl2/npc_turret_floor.h b/sp/src/game/server/hl2/npc_turret_floor.h index 8600e13a22f..af006a01434 100644 --- a/sp/src/game/server/hl2/npc_turret_floor.h +++ b/sp/src/game/server/hl2/npc_turret_floor.h @@ -265,6 +265,9 @@ class CNPC_FloorTurret : public CNPCBaseInteractive, public CDefaul COutputEvent m_OnTipped; COutputEvent m_OnPhysGunPickup; COutputEvent m_OnPhysGunDrop; +#ifdef MAPBASE + COutputEvent m_OnStartTipped; +#endif bool m_bHackedByAlyx; HSOUNDSCRIPTHANDLE m_ShotSounds; From 468fa81f47c5900e7ffd94d2e38b79198988f91e Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 2 Jan 2025 10:57:11 -0600 Subject: [PATCH 058/103] Expose VPhysicsInitNormal and VPhysicsDestroyObject to VScript --- sp/src/game/client/c_baseentity.cpp | 2 ++ sp/src/game/client/c_baseentity.h | 2 ++ sp/src/game/server/baseentity.cpp | 2 ++ sp/src/game/server/baseentity.h | 2 ++ sp/src/game/shared/baseentity_shared.cpp | 8 ++++++++ sp/src/game/shared/mapbase/vscript_consts_shared.cpp | 11 +++++++++++ 6 files changed, 27 insertions(+) diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 1d3de004b03..4a72bdb709e 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -485,6 +485,8 @@ BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsInitNormal, "PhysicsInitNormal", "Initializes the entity's physics object with the specified solid type, solid flags, and whether to start asleep" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsDestroyObject, "PhysicsDestroyObject", "Destroys the entity's physics object" ) DEFINE_SCRIPTFUNC( GetWaterLevel, "Get current level of water submergence" ) diff --git a/sp/src/game/client/c_baseentity.h b/sp/src/game/client/c_baseentity.h index 422a59d48f1..aa383c73992 100644 --- a/sp/src/game/client/c_baseentity.h +++ b/sp/src/game/client/c_baseentity.h @@ -1206,6 +1206,8 @@ class C_BaseEntity : public IClientEntity HSCRIPT ScriptEntityToWorldTransform( void ); HSCRIPT ScriptGetPhysicsObject( void ); + void ScriptPhysicsInitNormal( int nSolidType, int nSolidFlags, bool createAsleep ); + void ScriptPhysicsDestroyObject() { VPhysicsDestroyObject(); } void ScriptSetParent( HSCRIPT hParent, const char *szAttachment ); HSCRIPT ScriptGetMoveParent( void ); diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 9df5cc09df1..d62e95364b5 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -2312,6 +2312,8 @@ BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" DEFINE_SCRIPTFUNC_NAMED( ScriptEntityToWorldTransform, "EntityToWorldTransform", "Get the entity's transform" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetPhysicsObject, "GetPhysicsObject", "Get the entity's physics object if it has one" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsInitNormal, "PhysicsInitNormal", "Initializes the entity's physics object with the specified solid type, solid flags, and whether to start asleep" ) + DEFINE_SCRIPTFUNC_NAMED( ScriptPhysicsDestroyObject, "PhysicsDestroyObject", "Destroys the entity's physics object" ) DEFINE_SCRIPTFUNC( ApplyAbsVelocityImpulse, "" ) DEFINE_SCRIPTFUNC( ApplyLocalAngularVelocityImpulse, "" ) diff --git a/sp/src/game/server/baseentity.h b/sp/src/game/server/baseentity.h index 7cd4670c281..e589923121a 100644 --- a/sp/src/game/server/baseentity.h +++ b/sp/src/game/server/baseentity.h @@ -2107,6 +2107,8 @@ class CBaseEntity : public IServerEntity HSCRIPT ScriptEntityToWorldTransform( void ); HSCRIPT ScriptGetPhysicsObject( void ); + void ScriptPhysicsInitNormal( int nSolidType, int nSolidFlags, bool createAsleep ); + void ScriptPhysicsDestroyObject() { VPhysicsDestroyObject(); } void ScriptSetParent(HSCRIPT hParent, const char *szAttachment); #endif diff --git a/sp/src/game/shared/baseentity_shared.cpp b/sp/src/game/shared/baseentity_shared.cpp index b2cd6bdc7a0..5f95761216e 100644 --- a/sp/src/game/shared/baseentity_shared.cpp +++ b/sp/src/game/shared/baseentity_shared.cpp @@ -2811,6 +2811,14 @@ HSCRIPT CBaseEntity::ScriptGetPhysicsObject( void ) return NULL; } +//----------------------------------------------------------------------------- +// Vscript: Gets the entity's physics object if it has one +//----------------------------------------------------------------------------- +void CBaseEntity::ScriptPhysicsInitNormal( int nSolidType, int nSolidFlags, bool createAsleep ) +{ + VPhysicsInitNormal( (SolidType_t)nSolidType, nSolidFlags, createAsleep ); +} + #ifdef GAME_DLL #define SCRIPT_NEVER_THINK TICK_NEVER_THINK diff --git a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp index 2ced70863e8..bb47bcb17cb 100644 --- a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -290,6 +290,17 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, EF_ITEM_BLINK, "Effect flag used in GetEffects(), etc." ); ScriptRegisterConstant( g_pScriptVM, EF_PARENT_ANIMATES, "Effect flag used in GetEffects(), etc." ); + // + // Solid Types + // + ScriptRegisterConstant( g_pScriptVM, SOLID_NONE, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_BSP, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_BBOX, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_OBB, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_OBB_YAW, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_CUSTOM, "Solid type used by VPhysics" ); + ScriptRegisterConstant( g_pScriptVM, SOLID_VPHYSICS, "Solid type used by VPhysics" ); + // // Solid Flags // From 10f08829e57c83ce2664d7cc4c5f8aa004491e23 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 5 Jan 2025 13:37:40 -0600 Subject: [PATCH 059/103] Fix vgui_screen crashing for existing saves until v8.0 --- sp/src/game/server/vguiscreen.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sp/src/game/server/vguiscreen.cpp b/sp/src/game/server/vguiscreen.cpp index 867953429c3..e46afbf585b 100644 --- a/sp/src/game/server/vguiscreen.cpp +++ b/sp/src/game/server/vguiscreen.cpp @@ -184,6 +184,11 @@ CVGuiScreen::~CVGuiScreen() int CVGuiScreen::Save(ISave& save) { +#if MAPBASE_VER_INT < 8000 + // HACKHACK: Until v8.0, mark this screen as using the new save system to prevent existing saves with vgui_screen from crashing + AddContext( "uses_new_save", "1" ); +#endif + int status = BaseClass::Save(save); if (!status) return 0; @@ -209,6 +214,12 @@ int CVGuiScreen::Restore(IRestore& restore) if (!status) return 0; +#if MAPBASE_VER_INT < 8000 + // HACKHACK: Until v8.0, mark this screen as using the new save system to prevent existing saves with vgui_screen from crashing + if (!HasContext( "uses_new_save", "1" )) + return status; +#endif + const int iCount = restore.ReadInt(); m_PanelOutputs.EnsureCapacity(iCount); for (int i = 0; i < iCount; i++) From b055d776639083aa49ca8c71649bb35f153d6de8 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 9 Jan 2025 12:29:03 -0600 Subject: [PATCH 060/103] Move npc_metropolice enemy kill concept to Event_KilledOther() --- sp/src/game/server/hl2/npc_metropolice.cpp | 17 +++++++++++++++++ sp/src/game/server/hl2/npc_metropolice.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/sp/src/game/server/hl2/npc_metropolice.cpp b/sp/src/game/server/hl2/npc_metropolice.cpp index 40f1d5be90a..f515ab9213e 100644 --- a/sp/src/game/server/hl2/npc_metropolice.cpp +++ b/sp/src/game/server/hl2/npc_metropolice.cpp @@ -534,10 +534,12 @@ void CNPC_MetroPolice::OnScheduleChange() { BaseClass::OnScheduleChange(); +#ifndef MAPBASE // Moved to Event_KilledOther() if ( GetEnemy() && HasCondition( COND_ENEMY_DEAD ) ) { AnnounceEnemyKill( GetEnemy() ); } +#endif } @@ -3722,6 +3724,21 @@ void CNPC_MetroPolice::Event_Killed( const CTakeDamageInfo &info ) BaseClass::Event_Killed( info ); } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CNPC_MetroPolice::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) +{ + BaseClass::Event_KilledOther( pVictim, info ); + +#ifdef MAPBASE // Moved from OnScheduleChange() + if ( pVictim && (pVictim->IsPlayer() || pVictim->IsNPC()) ) + { + AnnounceEnemyKill( pVictim ); + } +#endif +} + //----------------------------------------------------------------------------- // Try to enter a slot where we shoot a pistol //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_metropolice.h b/sp/src/game/server/hl2/npc_metropolice.h index 1027786bfe8..690bf2ae8dd 100644 --- a/sp/src/game/server/hl2/npc_metropolice.h +++ b/sp/src/game/server/hl2/npc_metropolice.h @@ -79,6 +79,8 @@ class CNPC_MetroPolice : public CAI_BaseActor virtual void Event_Killed( const CTakeDamageInfo &info ); + virtual void Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ); + virtual void OnScheduleChange(); float GetIdealAccel( void ) const; From 17af09b3d12bc223223d1eadf67d2218bb73a12a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 9 Jan 2025 11:50:48 -0600 Subject: [PATCH 061/103] Fix workflow not terminating on compile error --- .github/workflows/mapbase_build-base.yml | 44 ++++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/mapbase_build-base.yml b/.github/workflows/mapbase_build-base.yml index fac52365fef..0a4905d0a22 100644 --- a/.github/workflows/mapbase_build-base.yml +++ b/.github/workflows/mapbase_build-base.yml @@ -85,45 +85,45 @@ jobs: working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} mathlib\mathlib.vcxproj + msbuild -m -p:Configuration=${{inputs.configuration}} mathlib\mathlib.vcxproj || exit /b - name: Build Base Libraries if: inputs.project-group == 'all' || inputs.project-group == 'game' || inputs.project-group == 'maptools' working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} raytrace\raytrace.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} tier1\tier1.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} vscript\vscript.vcxproj + msbuild -m -p:Configuration=${{inputs.configuration}} raytrace\raytrace.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} tier1\tier1.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} vscript\vscript.vcxproj || exit /b - name: Build Map Tools if: inputs.project-group == 'all' || inputs.project-group == 'maptools' working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} fgdlib\fgdlib.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vbsp\vbsp.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis\vvis_dll.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis_launcher\vvis_launcher.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad\vrad_dll.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad_launcher\vrad_launcher.vcxproj + msbuild -m -p:Configuration=${{inputs.configuration}} fgdlib\fgdlib.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} utils\vbsp\vbsp.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis\vvis_dll.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis_launcher\vvis_launcher.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad\vrad_dll.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad_launcher\vrad_launcher.vcxproj || exit /b - name: Build Shaders if: inputs.project-group == 'shaders' working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_${{inputs.game}}.vcxproj + msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_${{inputs.game}}.vcxproj || exit /b - name: Build Game if: inputs.project-group == 'game' working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_${{inputs.game}}.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_${{inputs.game}}.vcxproj + msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_${{inputs.game}}.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_${{inputs.game}}.vcxproj || exit /b # TODO: Hook to game naming? - name: Build everything @@ -131,13 +131,13 @@ jobs: working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_episodic.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_hl2.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_episodic.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_hl2.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_episodic.vcxproj - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_hl2.vcxproj + msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_episodic.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_hl2.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_episodic.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_hl2.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_episodic.vcxproj || exit /b + msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_hl2.vcxproj || exit /b # -------------------------------------------------------------------- From 466183c4de9ab9267db7bdda9d389dcb4e95903b Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 5 Jan 2025 13:01:28 -0600 Subject: [PATCH 062/103] New VScript functions for player use entity and pickup --- sp/src/game/server/player.cpp | 9 ++++ sp/src/game/server/player.h | 3 ++ sp/src/game/shared/baseplayer_shared.cpp | 34 +++++++++++++ .../game/shared/mapbase/vscript_funcs_hl2.cpp | 49 +++++++++++++++++++ .../shared/mapbase/vscript_funcs_shared.cpp | 22 +++++++++ 5 files changed, 117 insertions(+) diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index 28c767c5d41..6fd1c504159 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -498,6 +498,7 @@ END_DATADESC() #ifdef MAPBASE_VSCRIPT // TODO: Better placement? ScriptHook_t g_Hook_PlayerRunCommand; +ScriptHook_t g_Hook_FindUseEntity; BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) @@ -552,6 +553,9 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetEyeUp, "GetEyeUp", "Gets the player's up eye vector." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetViewModel, "GetViewModel", "Returns the viewmodel of the specified index." ) + + DEFINE_SCRIPTFUNC_NAMED( ScriptGetUseEntity, "GetUseEntity", "Gets the player's current use entity." ) + DEFINE_SCRIPTFUNC_NAMED( ScriptGetHeldObject, "GetHeldObject", "Gets the player's currently held object IF it is being held by a gravity gun. To check for the player's held +USE object, use the standalone GetPlayerHeldEntity function." ) // // Hooks @@ -559,6 +563,11 @@ BEGIN_ENT_SCRIPTDESC( CBasePlayer, CBaseCombatCharacter, "The player entity." ) BEGIN_SCRIPTHOOK( g_Hook_PlayerRunCommand, "PlayerRunCommand", FIELD_VOID, "Called when running a player command on the server." ) DEFINE_SCRIPTHOOK_PARAM( "command", FIELD_HSCRIPT ) END_SCRIPTHOOK() + + BEGIN_SCRIPTHOOK( g_Hook_FindUseEntity, "FindUseEntity", FIELD_HSCRIPT, "Called when finding an entity to use. The 'entity' parameter is for the entity found by the default function. If 'is_radius' is true, then this entity was found by searching in a radius around the cursor, rather than being directly used. Return a different entity to use something else." ) + DEFINE_SCRIPTHOOK_PARAM( "entity", FIELD_HSCRIPT ) + DEFINE_SCRIPTHOOK_PARAM( "is_radius", FIELD_BOOLEAN ) + END_SCRIPTHOOK() END_SCRIPTDESC(); #else diff --git a/sp/src/game/server/player.h b/sp/src/game/server/player.h index 72a1e38e38e..c3ef666a28c 100644 --- a/sp/src/game/server/player.h +++ b/sp/src/game/server/player.h @@ -414,6 +414,9 @@ class CBasePlayer : public CBaseCombatCharacter const Vector& ScriptGetEyeUp() { static Vector vecUp; EyeVectors( NULL, NULL, &vecUp ); return vecUp; } HSCRIPT ScriptGetViewModel( int viewmodelindex ); + + HSCRIPT ScriptGetUseEntity() { return ToHScript( GetUseEntity() ); } + HSCRIPT ScriptGetHeldObject() { return ToHScript( GetHeldObject() ); } #endif // View model prediction setup diff --git a/sp/src/game/shared/baseplayer_shared.cpp b/sp/src/game/shared/baseplayer_shared.cpp index 5ad7d453932..9296bfcfad4 100644 --- a/sp/src/game/shared/baseplayer_shared.cpp +++ b/sp/src/game/shared/baseplayer_shared.cpp @@ -1069,6 +1069,10 @@ float IntervalDistance( float x, float x0, float x1 ) return 0; } +#if !defined(CLIENT_DLL) && defined(MAPBASE_VSCRIPT) +extern ScriptHook_t g_Hook_FindUseEntity; +#endif + CBaseEntity *CBasePlayer::FindUseEntity() { Vector forward, up; @@ -1160,7 +1164,24 @@ CBaseEntity *CBasePlayer::FindUseEntity() // if this is directly under the cursor just return it now if ( i == 0 ) + { +#if !defined(CLIENT_DLL) && defined(MAPBASE_VSCRIPT) + if (m_ScriptScope.IsInitialized() && g_Hook_FindUseEntity.CanRunInScope( m_ScriptScope )) + { + // entity, is_radius + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pNearest ), false }; + if (g_Hook_FindUseEntity.Call( m_ScriptScope, &functionReturn, args )) + { + pObject = ToEnt( functionReturn.m_hScript ); + pNearest = pObject; + } + } + + if (pObject) +#endif return pObject; + } } } } @@ -1245,6 +1266,19 @@ CBaseEntity *CBasePlayer::FindUseEntity() { pNearest = DoubleCheckUseNPC( pNearest, searchCenter, forward ); } + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_FindUseEntity.CanRunInScope(m_ScriptScope)) + { + // entity, is_radius + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { ToHScript( pNearest ), true }; + if (g_Hook_FindUseEntity.Call( m_ScriptScope, &functionReturn, args )) + { + pNearest = ToEnt( functionReturn.m_hScript ); + } + } +#endif if ( sv_debug_player_use.GetBool() ) { diff --git a/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp index 189f8883d89..529d02913dc 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_hl2.cpp @@ -10,6 +10,8 @@ #include "hl2_gamerules.h" #ifndef CLIENT_DLL #include "eventqueue.h" +#include "weapon_physcannon.h" +#include "player_pickup.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -47,6 +49,48 @@ bool ScriptMegaPhyscannonActive() { return HL2GameRules()->MegaPhyscannonActive(); } + +void ScriptPickup_ForcePlayerToDropThisObject( HSCRIPT hTarget ) +{ + Pickup_ForcePlayerToDropThisObject( ToEnt( hTarget ) ); +} + +float ScriptPlayerPickupGetHeldObjectMass( HSCRIPT hPickupControllerEntity, HSCRIPT hHeldObject ) +{ + IPhysicsObject *pPhysObj = HScriptToClass( hHeldObject ); + if (!pPhysObj) + { + CBaseEntity *pEnt = ToEnt( hHeldObject ); + if (pEnt) + pPhysObj = pEnt->VPhysicsGetObject(); + } + + if (!pPhysObj) + { + Warning( "PlayerPickupGetHeldObjectMass: Invalid physics object\n" ); + return 0.0f; + } + + return PlayerPickupGetHeldObjectMass( ToEnt( hPickupControllerEntity ), pPhysObj ); +} + +HSCRIPT ScriptGetPlayerHeldEntity( HSCRIPT hPlayer ) +{ + CBasePlayer *pPlayer = ToBasePlayer( ToEnt( hPlayer ) ); + if (!pPlayer) + return NULL; + + return ToHScript( GetPlayerHeldEntity( pPlayer ) ); +} + +HSCRIPT ScriptPhysCannonGetHeldEntity( HSCRIPT hWeapon ) +{ + CBaseEntity *pEnt = ToEnt( hWeapon ); + if (!pEnt) + return NULL; + + return ToHScript( PhysCannonGetHeldEntity( pEnt->MyCombatWeaponPointer() ) ); +} #endif //----------------------------------------------------------------------------- @@ -59,5 +103,10 @@ void CHalfLife2::RegisterScriptFunctions( void ) #ifndef CLIENT_DLL ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGameOver, "GameOver", "Ends the game and reloads the last save." ); ScriptRegisterFunctionNamed( g_pScriptVM, ScriptMegaPhyscannonActive, "MegaPhyscannonActive", "Checks if supercharged gravity gun mode is enabled." ); + + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPickup_ForcePlayerToDropThisObject, "Pickup_ForcePlayerToDropThisObject", "If the specified entity is being carried, instantly drops it." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPlayerPickupGetHeldObjectMass, "PlayerPickupGetHeldObjectMass", "Gets the mass of the specified player_pickup controller, with the second parameter the held object's physics object." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptGetPlayerHeldEntity, "GetPlayerHeldEntity", "Gets the specified player's currently held entity." ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPhysCannonGetHeldEntity, "PhysCannonGetHeldEntity", "Gets the specified gravity gun's currently held entity." ); #endif } diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp index 4c6d4817943..227103dff83 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -771,6 +771,26 @@ static void AddPhysVelocity( HSCRIPT hPhys, const Vector& vecVelocity, const Vec pPhys->AddVelocity( &vecVelocity, &vecAngVelocity ); } +static void ScriptPhysEnableEntityCollisions( HSCRIPT hPhys1, HSCRIPT hPhys2 ) +{ + IPhysicsObject *pPhys1 = HScriptToClass( hPhys1 ); + IPhysicsObject *pPhys2 = HScriptToClass( hPhys2 ); + if (!pPhys1 || !pPhys2) + return; + + PhysEnableEntityCollisions( pPhys1, pPhys2 ); +} + +static void ScriptPhysDisableEntityCollisions( HSCRIPT hPhys1, HSCRIPT hPhys2 ) +{ + IPhysicsObject *pPhys1 = HScriptToClass( hPhys1 ); + IPhysicsObject *pPhys2 = HScriptToClass( hPhys2 ); + if (!pPhys1 || !pPhys2) + return; + + PhysDisableEntityCollisions( pPhys1, pPhys2 ); +} + //============================================================================= //============================================================================= @@ -1045,6 +1065,8 @@ void RegisterSharedScriptFunctions() ScriptRegisterFunction( g_pScriptVM, GetPhysAngVelocity, "Gets physics angular velocity for the given VPhysics object" ); ScriptRegisterFunction( g_pScriptVM, SetPhysVelocity, "Sets physics velocity for the given VPhysics object" ); ScriptRegisterFunction( g_pScriptVM, AddPhysVelocity, "Adds physics velocity for the given VPhysics object" ); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPhysEnableEntityCollisions, "PhysEnableEntityCollisions", "Enables collisions between two VPhysics objects"); + ScriptRegisterFunctionNamed( g_pScriptVM, ScriptPhysDisableEntityCollisions, "PhysDisableEntityCollisions", "Disables collisions between two VPhysics objects"); // // Precaching From d51d44eb73bb9dcccff971ffa688bb722c1893da Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 2 Jan 2025 11:00:30 -0600 Subject: [PATCH 063/103] Fix forced interaction partners being made to face each other even when interaction doesn't require it --- sp/src/game/server/ai_basenpc_schedule.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sp/src/game/server/ai_basenpc_schedule.cpp b/sp/src/game/server/ai_basenpc_schedule.cpp index 5f57b51d782..56e20e035fc 100644 --- a/sp/src/game/server/ai_basenpc_schedule.cpp +++ b/sp/src/game/server/ai_basenpc_schedule.cpp @@ -3506,6 +3506,12 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // as this should only run with the NPC "receiving" the interaction ScriptedNPCInteraction_t *pInteraction = m_hForcedInteractionPartner->GetRunningDynamicInteraction(); + if ( !(pInteraction->iFlags & SCNPC_FLAG_TEST_OTHER_ANGLES) ) + { + TaskComplete(); + return; + } + // Get our target's origin Vector vecTarget = m_hForcedInteractionPartner->GetAbsOrigin(); From d4b66b16cdf0cdf55ccdcd0f7605c8e029e9a0c1 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 2 Jan 2025 10:12:09 -0600 Subject: [PATCH 064/103] Expose CBaseCombatCharacter glow funcs to VScript + add color functionality Co-authored-by: Derek <1upderek@gmail.com> --- sp/src/game/client/c_basecombatcharacter.cpp | 26 ++++++++++++++------ sp/src/game/client/c_basecombatcharacter.h | 6 ++++- sp/src/game/server/basecombatcharacter.cpp | 17 +++++++++++++ sp/src/game/server/basecombatcharacter.h | 3 +++ 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/sp/src/game/client/c_basecombatcharacter.cpp b/sp/src/game/client/c_basecombatcharacter.cpp index 0afe4cbf942..85dbb9d52e0 100644 --- a/sp/src/game/client/c_basecombatcharacter.cpp +++ b/sp/src/game/client/c_basecombatcharacter.cpp @@ -34,6 +34,10 @@ C_BaseCombatCharacter::C_BaseCombatCharacter() m_pGlowEffect = NULL; m_bGlowEnabled = false; m_bOldGlowEnabled = false; + m_GlowColor.Init( 0.76f, 0.76f, 0.76f ); + m_OldGlowColor = m_GlowColor; + m_GlowAlpha = 1.0f; + m_OldGlowAlpha = 1.0f; #endif // GLOWS_ENABLE } @@ -66,6 +70,8 @@ void C_BaseCombatCharacter::OnPreDataChanged( DataUpdateType_t updateType ) #ifdef GLOWS_ENABLE m_bOldGlowEnabled = m_bGlowEnabled; + m_OldGlowColor = m_GlowColor; + m_OldGlowAlpha = m_GlowAlpha; #endif // GLOWS_ENABLE } @@ -77,7 +83,7 @@ void C_BaseCombatCharacter::OnDataChanged( DataUpdateType_t updateType ) BaseClass::OnDataChanged( updateType ); #ifdef GLOWS_ENABLE - if ( m_bOldGlowEnabled != m_bGlowEnabled ) + if ( m_bOldGlowEnabled != m_bGlowEnabled || m_OldGlowColor != m_GlowColor || m_OldGlowAlpha != m_GlowAlpha ) { UpdateGlowEffect(); } @@ -106,11 +112,13 @@ void C_BaseCombatCharacter::DoMuzzleFlash() //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void C_BaseCombatCharacter::GetGlowEffectColor( float *r, float *g, float *b ) +void C_BaseCombatCharacter::GetGlowEffectColor( float *r, float *g, float *b, float *a ) { - *r = 0.76f; - *g = 0.76f; - *b = 0.76f; + *r = m_GlowColor.x; + *g = m_GlowColor.y; + *b = m_GlowColor.z; + if (a) + *a = m_GlowAlpha; } //----------------------------------------------------------------------------- @@ -127,10 +135,10 @@ void C_BaseCombatCharacter::UpdateGlowEffect( void ) // create a new effect if ( m_bGlowEnabled ) { - float r, g, b; - GetGlowEffectColor( &r, &g, &b ); + float r, g, b, a; + GetGlowEffectColor( &r, &g, &b, &a ); - m_pGlowEffect = new CGlowObject( this, Vector( r, g, b ), 1.0, true ); + m_pGlowEffect = new CGlowObject( this, Vector( r, g, b ), a, true ); } } @@ -161,6 +169,8 @@ BEGIN_RECV_TABLE(C_BaseCombatCharacter, DT_BaseCombatCharacter) RecvPropArray3( RECVINFO_ARRAY(m_hMyWeapons), RecvPropEHandle( RECVINFO( m_hMyWeapons[0] ) ) ), #ifdef GLOWS_ENABLE RecvPropBool( RECVINFO( m_bGlowEnabled ) ), + RecvPropVector( RECVINFO( m_GlowColor ) ), + RecvPropFloat( RECVINFO( m_GlowAlpha ) ), #endif // GLOWS_ENABLE #ifdef INVASION_CLIENT_DLL diff --git a/sp/src/game/client/c_basecombatcharacter.h b/sp/src/game/client/c_basecombatcharacter.h index f580fe46b13..4941b1369bb 100644 --- a/sp/src/game/client/c_basecombatcharacter.h +++ b/sp/src/game/client/c_basecombatcharacter.h @@ -99,7 +99,7 @@ class C_BaseCombatCharacter : public C_BaseFlex #ifdef GLOWS_ENABLE CGlowObject *GetGlowObject( void ){ return m_pGlowEffect; } - virtual void GetGlowEffectColor( float *r, float *g, float *b ); + virtual void GetGlowEffectColor( float *r, float *g, float *b, float *a = NULL ); #endif // GLOWS_ENABLE #ifdef MAPBASE_VSCRIPT @@ -133,6 +133,10 @@ class C_BaseCombatCharacter : public C_BaseFlex bool m_bGlowEnabled; bool m_bOldGlowEnabled; CGlowObject *m_pGlowEffect; + Vector m_GlowColor; + Vector m_OldGlowColor; + float m_GlowAlpha; + int m_OldGlowAlpha; #endif // GLOWS_ENABLE private: diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 6b21a23a47f..b7a2692d689 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -195,6 +195,13 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatCharacter, CBaseFlex, "The base class shared by DEFINE_SCRIPTFUNC( LastHitGroup, "Get the last hitgroup." ) +#ifdef GLOWS_ENABLE + DEFINE_SCRIPTFUNC( AddGlowEffect, "" ) + DEFINE_SCRIPTFUNC( RemoveGlowEffect, "" ) + DEFINE_SCRIPTFUNC( IsGlowEffectActive, "" ) + DEFINE_SCRIPTFUNC( SetGlowColor, "" ) +#endif + // // Hooks // @@ -293,6 +300,8 @@ END_SEND_TABLE(); IMPLEMENT_SERVERCLASS_ST(CBaseCombatCharacter, DT_BaseCombatCharacter) #ifdef GLOWS_ENABLE SendPropBool( SENDINFO( m_bGlowEnabled ) ), + SendPropVector( SENDINFO( m_GlowColor ), 8, 0, 0, 1 ), + SendPropFloat( SENDINFO( m_GlowAlpha ) ), #endif // GLOWS_ENABLE // Data that only gets sent to the local player. SendPropDataTable( "bcc_localdata", 0, &REFERENCE_SEND_TABLE(DT_BCCLocalPlayerExclusive), SendProxy_SendBaseCombatCharacterLocalDataTable ), @@ -878,6 +887,8 @@ CBaseCombatCharacter::CBaseCombatCharacter( void ) #ifdef GLOWS_ENABLE m_bGlowEnabled.Set( false ); + m_GlowColor.GetForModify().Init( 0.76f, 0.76f, 0.76f ); + m_GlowAlpha.Set(1.0f); #endif // GLOWS_ENABLE } @@ -4098,6 +4109,12 @@ bool CBaseCombatCharacter::IsGlowEffectActive( void ) { return m_bGlowEnabled; } + +void CBaseCombatCharacter::SetGlowColor( float red, float green, float blue, float alpha ) +{ + m_GlowColor.GetForModify().Init( red, green, blue ); + m_GlowAlpha.Set( alpha ); +} #endif // GLOWS_ENABLE //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index 20d3973e3aa..f7aa3d8e67f 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -532,6 +532,7 @@ class CBaseCombatCharacter : public CBaseFlex void AddGlowEffect( void ); void RemoveGlowEffect( void ); bool IsGlowEffectActive( void ); + void SetGlowColor( float red, float green, float blue, float alpha ); #endif // GLOWS_ENABLE #ifdef INVASION_DLL @@ -576,6 +577,8 @@ class CBaseCombatCharacter : public CBaseFlex #ifdef GLOWS_ENABLE protected: CNetworkVar( bool, m_bGlowEnabled ); + CNetworkVector( m_GlowColor ); + CNetworkVar( float, m_GlowAlpha ); #endif // GLOWS_ENABLE private: From b03ffdcac494d4ce3d94665ac71a565fa0454325 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 5 Jan 2025 13:04:34 -0600 Subject: [PATCH 065/103] Fix VScript not having "self" during precache, props not using death hook, VScript proxies not cleaning up correctly --- sp/src/game/client/vscript_client.cpp | 2 ++ sp/src/game/server/props.cpp | 6 ++++++ sp/src/game/shared/vscript_shared.cpp | 3 +++ 3 files changed, 11 insertions(+) diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 7bece925a43..2312d6ca9a8 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -272,6 +272,8 @@ void CScriptMaterialProxy::Release( void ) m_hScriptInstance = NULL; } + g_ScriptPersistableList.FindAndRemove( this ); + delete this; } diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index 57e33d87421..037a0226efe 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -1338,6 +1338,12 @@ int CBreakableProp::OnTakeDamage( const CTakeDamageInfo &inputInfo ) //----------------------------------------------------------------------------- void CBreakableProp::Event_Killed( const CTakeDamageInfo &info ) { +#ifdef MAPBASE_VSCRIPT + // False = Cheat death + if (ScriptDeathHook( const_cast(&info) ) == false) + return; +#endif + IPhysicsObject *pPhysics = VPhysicsGetObject(); if ( pPhysics && !pPhysics->IsMoveable() ) { diff --git a/sp/src/game/shared/vscript_shared.cpp b/sp/src/game/shared/vscript_shared.cpp index 8ab2ac97d61..c41ddea6fc5 100644 --- a/sp/src/game/shared/vscript_shared.cpp +++ b/sp/src/game/shared/vscript_shared.cpp @@ -624,6 +624,9 @@ class CVScriptSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler if ( g_pScriptVM->GetValue( STRING(pEnt->m_iszScriptId), &variant ) && variant.m_type == FIELD_HSCRIPT ) { pEnt->m_ScriptScope.Init( variant.m_hScript, false ); +#ifdef MAPBASE_VSCRIPT + g_pScriptVM->SetValue( pEnt->m_ScriptScope, "self", pEnt->m_hScriptInstance ); +#endif #ifndef CLIENT_DLL pEnt->RunPrecacheScripts(); #endif From 5bc5a3beece0bb86dd7da159e091a5f783d6a01b Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 12 Jan 2025 15:30:03 -0600 Subject: [PATCH 066/103] Fix vgui_controls being considered a part of map tools in build workflow --- .github/workflows/mapbase_build-base.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mapbase_build-base.yml b/.github/workflows/mapbase_build-base.yml index 0a4905d0a22..ccf39580077 100644 --- a/.github/workflows/mapbase_build-base.yml +++ b/.github/workflows/mapbase_build-base.yml @@ -94,7 +94,6 @@ jobs: run: | msbuild -m -p:Configuration=${{inputs.configuration}} raytrace\raytrace.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} tier1\tier1.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} vscript\vscript.vcxproj || exit /b - name: Build Map Tools @@ -121,6 +120,7 @@ jobs: working-directory: '${{inputs.branch}}/src' shell: cmd run: | + msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_${{inputs.game}}.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_${{inputs.game}}.vcxproj || exit /b From 93d586a82e1ac601e74dd157682ce954b8a42976 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 12 Jan 2025 15:56:02 -0600 Subject: [PATCH 067/103] Add vgui_controls to workflow for all projects --- .github/workflows/mapbase_build-base.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/mapbase_build-base.yml b/.github/workflows/mapbase_build-base.yml index ccf39580077..d7859284b6b 100644 --- a/.github/workflows/mapbase_build-base.yml +++ b/.github/workflows/mapbase_build-base.yml @@ -131,6 +131,7 @@ jobs: working-directory: '${{inputs.branch}}/src' shell: cmd run: | + msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_episodic.vcxproj || exit /b msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_hl2.vcxproj || exit /b From cfe649e0e0394c0a516097b20e8afe39a7f02ad7 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Mon, 13 Jan 2025 09:53:06 -0600 Subject: [PATCH 068/103] Change workflow to use .sln and consolidate artifact tasks --- .github/workflows/mapbase_build-base.yml | 135 +++++------------------ 1 file changed, 29 insertions(+), 106 deletions(-) diff --git a/.github/workflows/mapbase_build-base.yml b/.github/workflows/mapbase_build-base.yml index d7859284b6b..45602a72737 100644 --- a/.github/workflows/mapbase_build-base.yml +++ b/.github/workflows/mapbase_build-base.yml @@ -54,7 +54,7 @@ jobs: - uses: actions/checkout@v4 - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.1 + uses: compnerd/gha-setup-vsdevenv@v6 - name: Enable VS2022 working-directory: '${{inputs.branch}}/src/vpc_scripts' @@ -77,108 +77,40 @@ jobs: # -------------------------------------------------------------------- - # "I'm invoking msbuild for each project individually, which looks a bit odd considering there is a solution file which should be able to invoke the builds in their proper order automatically, but passing the solution to msbuild doesn't seem to work." - # https://github.com/mapbase-source/source-sdk-2013/pull/162 - - - name: Build mathlib + - name: Build #if: steps.filter.outputs.game == 'true' working-directory: '${{inputs.branch}}/src' shell: cmd run: | - msbuild -m -p:Configuration=${{inputs.configuration}} mathlib\mathlib.vcxproj || exit /b - - - name: Build Base Libraries - if: inputs.project-group == 'all' || inputs.project-group == 'game' || inputs.project-group == 'maptools' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} raytrace\raytrace.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} tier1\tier1.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} vscript\vscript.vcxproj || exit /b - - - name: Build Map Tools - if: inputs.project-group == 'all' || inputs.project-group == 'maptools' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} fgdlib\fgdlib.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vbsp\vbsp.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis\vvis_dll.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vvis_launcher\vvis_launcher.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad\vrad_dll.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} utils\vrad_launcher\vrad_launcher.vcxproj || exit /b - - - name: Build Shaders - if: inputs.project-group == 'shaders' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_${{inputs.game}}.vcxproj || exit /b - - - name: Build Game - if: inputs.project-group == 'game' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_${{inputs.game}}.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_${{inputs.game}}.vcxproj || exit /b - - # TODO: Hook to game naming? - - name: Build everything - if: inputs.project-group == 'all' - working-directory: '${{inputs.branch}}/src' - shell: cmd - run: | - msbuild -m -p:Configuration=${{inputs.configuration}} vgui2\vgui_controls\vgui_controls.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} responserules\runtime\responserules.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_episodic.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} materialsystem\stdshaders\game_shader_dx9_hl2.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_episodic.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} game\client\client_hl2.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_episodic.vcxproj || exit /b - msbuild -m -p:Configuration=${{inputs.configuration}} game\server\server_hl2.vcxproj || exit /b + devenv ${{inputs.solution-name}}.sln /upgrade + msbuild -m -t:Rebuild -p:Configuration=${{inputs.configuration}};Platform=x86 ${{inputs.solution-name}}.sln # -------------------------------------------------------------------- - - name: Publish Windows game DLLs - if: inputs.project-group == 'game' - uses: actions/upload-artifact@v3 - with: - name: 'Windows Game DLLs (server & client.dll) [${{ inputs.configuration }}]' - path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/client.dll - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/server.dll - if-no-files-found: error - - - name: Publish Windows shader DLL - if: inputs.project-group == 'shaders' - uses: actions/upload-artifact@v3 + - name: Publish game binaries + if: inputs.project-group == 'game' || inputs.project-group == 'shaders' + uses: actions/upload-artifact@v4 with: - name: 'Windows Shader DLL (game_shader_dx9.dll) [${{ inputs.configuration }}]' + name: '${{inputs.project-group}}_${{inputs.game}}_win32_${{ inputs.configuration }}' path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/game_shader_dx9.dll + ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/*.dll if-no-files-found: error - - name: Publish Windows map tools + - name: Publish map tools if: inputs.project-group == 'maptools' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: 'Windows Map Tools [${{ inputs.configuration }}]' + name: '${{inputs.project-group}}_win32_${{ inputs.configuration }}' path: | - ${{inputs.branch}}/game/bin/vbsp.exe - ${{inputs.branch}}/game/bin/vvis.exe - ${{inputs.branch}}/game/bin/vvis_dll.dll - ${{inputs.branch}}/game/bin/vrad.exe - ${{inputs.branch}}/game/bin/vrad_dll.dll + ${{inputs.branch}}/game/bin/*.exe + ${{inputs.branch}}/game/bin/*.dll if-no-files-found: error - - name: Publish everything (Windows) + - name: Publish everything if: inputs.project-group == 'all' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: 'Everything (Windows) [${{ inputs.configuration }}]' + name: 'everything_win32_${{ inputs.configuration }}' path: | ${{inputs.branch}}/game/bin ${{inputs.branch}}/game/mod_*/bin @@ -225,30 +157,21 @@ jobs: # -------------------------------------------------------------------- - - name: Publish Linux game SOs - if: inputs.project-group == 'game' - uses: actions/upload-artifact@v3 - with: - name: 'Linux Game SOs (server & client.so) [${{ inputs.configuration }}]' - path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/client.so - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/server.so - if-no-files-found: error - - - name: Publish Linux shader SO - if: inputs.project-group == 'shaders' - uses: actions/upload-artifact@v3 + - name: Publish game binaries + if: inputs.project-group == 'game' || inputs.project-group == 'shaders' + uses: actions/upload-artifact@v4 with: - name: 'Linux Shader SO (game_shader_dx9.so) [${{ inputs.configuration }}]' + name: '${{inputs.project-group}}_${{inputs.game}}_linux32_${{ inputs.configuration }}' path: | - ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/game_shader_dx9.so + ${{inputs.branch}}/game/mod_${{inputs.game}}/bin/*.so + !${{inputs.branch}}/game/mod_${{inputs.game}}/bin/*_srv.so if-no-files-found: error - #- name: Publish Linux map tools + #- name: Publish map tools # if: inputs.project-group == 'maptools' - # uses: actions/upload-artifact@v3 + # uses: actions/upload-artifact@v4 # with: - # name: 'Linux Map Tools [${{ inputs.configuration }}]' + # name: '${{inputs.project-group}}_linux32_${{ inputs.configuration }}' # path: | # ${{inputs.branch}}/game/bin/vbsp # ${{inputs.branch}}/game/bin/vvis @@ -259,11 +182,11 @@ jobs: # For now, don't publish the .dbg files even though we publish .pdb files on Windows # (they're too big) - - name: Publish everything (Linux) + - name: Publish everything if: inputs.project-group == 'all' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: 'Everything (Linux) [${{ inputs.configuration }}]' + name: 'everything_linux32_${{ inputs.configuration }}' path: | ${{inputs.branch}}/game/bin/*.so !${{inputs.branch}}/game/bin/*_srv.so From f79f07e9158d0461a88a1af91572a66dc3543def Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 5 Jan 2025 12:25:34 -0600 Subject: [PATCH 069/103] Support for multiple colors for different outro credits columns on the same row --- sp/src/game/client/hl2/hud_credits.cpp | 78 +++++++++++++++++++++----- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/sp/src/game/client/hl2/hud_credits.cpp b/sp/src/game/client/hl2/hud_credits.cpp index fb4354f5495..bf0e58fd6ba 100644 --- a/sp/src/game/client/hl2/hud_credits.cpp +++ b/sp/src/game/client/hl2/hud_credits.cpp @@ -35,8 +35,7 @@ struct creditname_t #ifdef MAPBASE // New credits stuff - - Color cColorOverride; + CCopyableUtlVector cColorOverride; // Images int iImageID = -1; @@ -408,8 +407,8 @@ void CHudCredits::DrawOutroCreditsName( void ) Color cColor = m_TextColor; #ifdef MAPBASE - if (pCredit->cColorOverride.a() > 0) - cColor = pCredit->cColorOverride; + if (pCredit->cColorOverride.Count() > 0) + cColor.SetRawColor( pCredit->cColorOverride[0] ); // Some lines should stick around and fade out if ( i >= m_CreditsList.Count()-m_iEndLines ) @@ -473,6 +472,12 @@ void CHudCredits::DrawOutroCreditsName( void ) { FOR_EACH_VEC( outStrings, i ) { + if (i < pCredit->cColorOverride.Count()) + { + // Change color to this particular column's color + cColor.SetRawColor( pCredit->cColorOverride[i] ); + } + int iImageID = GetOrAllocateImageID( outStrings[i] ); // Center the image if needed @@ -492,6 +497,12 @@ void CHudCredits::DrawOutroCreditsName( void ) { FOR_EACH_VEC( outStrings, i ) { + if (i < pCredit->cColorOverride.Count()) + { + // Change color to this particular column's color + cColor.SetRawColor( pCredit->cColorOverride[i] ); + } + DrawOutroCreditFont( outStrings[i], pCredit->flYPos, m_hTFont, cColor, iWidth*(i + 1), iDivisor ); } } @@ -768,8 +779,8 @@ void CHudCredits::DrawIntroCreditsName( void ) surface()->DrawSetTextFont( m_hTFont ); #ifdef MAPBASE Color cColor = m_cColor; - if (pCredit->cColorOverride.a() > 0) - cColor = pCredit->cColorOverride; + if (pCredit->cColorOverride.Count() > 0) + cColor.SetRawColor( pCredit->cColorOverride[0] ); surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], FadeBlend( m_flFadeInTime, m_flFadeOutTime, m_flFadeHoldTime + pCredit->flTimeAdd, localTime ) * cColor[3] ); #else @@ -941,9 +952,25 @@ void CHudCredits::PrepareOutroCredits( void ) // Get color case 2: - int tmp[4]; - UTIL_StringToIntArray( tmp, 4, outStrings[i] ); - pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + char *pToken = strtok( outStrings[i], "," ); + if (pToken) + { + // Multiple colors for multiple columns + while (pToken != NULL) + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, pToken ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + + pToken = strtok( NULL, "," ); + } + } + else + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, outStrings[i] ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + } break; } } @@ -999,9 +1026,25 @@ void CHudCredits::PrepareOutroCredits( void ) { // Get color case 1: - int tmp[4]; - UTIL_StringToIntArray( tmp, 4, outStrings[i] ); - pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + char *pToken = strtok( outStrings[i], "," ); + if (pToken) + { + // Multiple colors for multiple columns + while (pToken != NULL) + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, pToken ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + + pToken = strtok( NULL, "," ); + } + } + else + { + int tmp[4]; + UTIL_StringToIntArray( tmp, 4, outStrings[i] ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); + } break; } } @@ -1024,8 +1067,12 @@ void CHudCredits::PrepareOutroCredits( void ) #ifdef MAPBASE // Check if the last line has a color override. If it does, use that as the alpha for the fadeout - if (m_CreditsList.Tail().cColorOverride.a() != 0) - m_Alpha = m_CreditsList.Tail().cColorOverride.a(); + if (m_CreditsList.Tail().cColorOverride.Count() > 0) + { + Color clr; + clr.SetRawColor( m_CreditsList.Tail().cColorOverride[0] ); + m_Alpha = clr.a(); + } #endif SetActive( true ); @@ -1057,9 +1104,10 @@ void CHudCredits::PrepareIntroCredits( void ) { // Get color case 1: + // TODO: Columns? int tmp[4]; UTIL_StringToIntArray( tmp, 4, outStrings[i] ); - pCredit->cColorOverride = Color( tmp[0], tmp[1], tmp[2], tmp[3] ); + pCredit->cColorOverride.AddToTail( Color( tmp[0], tmp[1], tmp[2], tmp[3] ).GetRawColor() ); break; } } From 365d560f12ae1baefa32d05cc980f865a73a8dfe Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Tue, 14 Jan 2025 07:54:52 -0600 Subject: [PATCH 070/103] Updated README --- README | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README b/README index d9d110246f8..b2fcc972638 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ //========================================================================================================================= - Mapbase v7.2 - Source 2013 + Mapbase v7.3 - Source 2013 https://github.com/mapbase-source/source-sdk-2013 https://www.moddb.com/mods/mapbase @@ -117,7 +117,18 @@ Direct contributions: - https://github.com/mapbase-source/source-sdk-2013/pull/237 (Commander goal trace fix by Agrimar) - https://github.com/mapbase-source/source-sdk-2013/pull/245 (ViewPunch random fix by Mr0maks) - https://github.com/mapbase-source/source-sdk-2013/pull/248 (soundlevel_t conversation warning fix by Mechami) +- https://github.com/mapbase-source/source-sdk-2013/pull/266 ("OnPhysGunPull" output in CPhysicsProp by rlenhub) +- https://github.com/mapbase-source/source-sdk-2013/pull/292 (env_headcrabcanister random spawn type by arbabf) +- https://github.com/mapbase-source/source-sdk-2013/pull/294 (SDK_LightmappedGeneric editor blend swap fix by azzyr) +- https://github.com/mapbase-source/source-sdk-2013/pull/308 (BlurFilterY fix by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/312 (Zombie improvements by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/315 (env_flare crash fix by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/324 (server-only info/func_null by SirYodaJedi) +- https://github.com/mapbase-source/source-sdk-2013/pull/333 (Cvar to transition levels while in MOVETYPE_NOCLIP by Wikot235) +- https://github.com/mapbase-source/source-sdk-2013/pull/342 (NaN particle cull radius fix by celisej567) - https://github.com/mapbase-source/mapbase-game-src/pull/1 (Advanced video options duplicate field name fix by arbabf; This is asset-based and not reflected in the code) +- https://github.com/mapbase-source/mapbase-game-src/pull/2 (gameinfo.txt typo fix by CarePackage17; This is asset-based and not reflected in the code) +- https://github.com/mapbase-source/mapbase-game-src/pull/3 (HudMessage cutoff fix by arbabf; This is asset-based and not reflected in the code) - Demo autorecord code provided by Klems - cc_emit crash fix provided by 1upD - Custom HL2 ammo crate models created by Rykah (Textures created by Blixibon; This is asset-based and, aside from the SLAM crate, not reflected in the code) @@ -144,8 +155,10 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/206 (Fix CScriptNetMsgHelper::WriteEntity()) =-- https://github.com/mapbase-source/source-sdk-2013/pull/213 (VScript HUD visibility control, optimizations for 3D skybox angles/fake worldportals) =-- https://github.com/mapbase-source/source-sdk-2013/pull/229 (VScript VGUI HUD viewport parenting, game_text and vgui_text_display VScript font fallback) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/260 (CScriptNetPropManager rewrite) =-- https://github.com/mapbase-source/source-sdk-2013/pull/261 (Misc VScript additions) =-- https://github.com/mapbase-source/source-sdk-2013/pull/279 (weapon_custom_scripted fixes) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/332 (Fix OOB access) == Contributions from z33ky: =-- https://github.com/mapbase-source/source-sdk-2013/pull/21 (Various GCC/Linux compilation fixes) @@ -159,6 +172,7 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/159 (Additional GCC/Linux compilation fixes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/162 (VS2019 exception specification fix) =-- https://github.com/mapbase-source/source-sdk-2013/pull/170 (HL2 non-Episodic build fix) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/322 (Small Mapbase fixes) == Contributions from Petercov: =-- https://github.com/mapbase-source/source-sdk-2013/pull/182 (NPCs load dynamic interactions from all animation MDLs) @@ -168,6 +182,7 @@ Direct contributions: =-- https://github.com/mapbase-source/source-sdk-2013/pull/183 (Enhanced custom weapons support) =-- https://github.com/mapbase-source/source-sdk-2013/pull/230 (Caption fixes) =-- https://github.com/mapbase-source/source-sdk-2013/pull/231 (Sentence source bug fix) +=-- https://github.com/mapbase-source/source-sdk-2013/pull/264 (Outputs for vgui_screen) //------------------------------------------------------------------------------------------------------------------------- From 7976a21b51aee121c3944c85a7d94571a56fe45a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 18 Jan 2025 10:02:53 -0600 Subject: [PATCH 071/103] Fix crash when objects are pulled with the gravity gun --- sp/src/game/server/hl2/weapon_physcannon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/weapon_physcannon.cpp b/sp/src/game/server/hl2/weapon_physcannon.cpp index 2947e291bc7..c2a3c4ca3d3 100644 --- a/sp/src/game/server/hl2/weapon_physcannon.cpp +++ b/sp/src/game/server/hl2/weapon_physcannon.cpp @@ -2758,7 +2758,7 @@ CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void ) pullDir *= (mass + 0.5) * (1/50.0f); } - CPhysicsProp* pProp = dynamic_cast(pObj); + CPhysicsProp* pProp = dynamic_cast(pEntity); if (pProp) { pProp->OnPhysGunPull( pOwner ); } From fe92cb2334023aa28d535b24d0c33d403396bbb1 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 9 Jan 2025 07:19:28 -0600 Subject: [PATCH 072/103] Fix overflow in constant arithmetic error in npc_lost_soul --- sp/src/game/server/mod/npc_lost_soul.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/mod/npc_lost_soul.cpp b/sp/src/game/server/mod/npc_lost_soul.cpp index 154522a277d..c0c517bd42f 100644 --- a/sp/src/game/server/mod/npc_lost_soul.cpp +++ b/sp/src/game/server/mod/npc_lost_soul.cpp @@ -225,7 +225,7 @@ void CNPC_LostSoul::Spawn( void ) CEntityFlame *pFlame = CEntityFlame::Create(this); if (pFlame) { - pFlame->SetLifetime(HUGE_VAL); + pFlame->SetLifetime(FLT_MAX); SetEffectEntity(pFlame); pFlame->SetSize(8); pFlame->SetDamage(0.0f); From f7aaa7515dad9767493389e4f7e50762b85d4656 Mon Sep 17 00:00:00 2001 From: Derek <1upderek@gmail.com> Date: Mon, 20 Jan 2025 14:45:26 -0500 Subject: [PATCH 073/103] fix: Vortigaunts and zombigaunts clean up env_physexplosion entity --- sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp index 65d23587153..beb0c00962e 100644 --- a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp +++ b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp @@ -3316,6 +3316,7 @@ void CNPC_Vortigaunt::DispelAntlions( const Vector &vecOrigin, float flRadius, b // EXPLOSION! pPhysExplosion->Spawn(); pPhysExplosion->Explode( this, this ); + UTIL_Remove(pPhysExplosion); } // It's dangerous to go alone. Take this with you! From f3b084e22f66d9a171dae87aeb21294de3137364 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 15 Dec 2024 18:32:48 -0600 Subject: [PATCH 074/103] Vort barrel propdata interaction (explode_zap) --- sp/src/game/server/props.cpp | 75 +++++++++++++++++++++++++++++ sp/src/game/server/props.h | 7 +++ sp/src/game/shared/props_shared.cpp | 8 +++ sp/src/game/shared/props_shared.h | 4 ++ 4 files changed, 94 insertions(+) diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index e68b47f701d..c498331213d 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -50,6 +50,8 @@ #ifdef EZ2 #include "ez2/ez2_player.h" #include "eventqueue.h" +#include "RagdollBoogie.h" +#include "particle_parse.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -107,6 +109,9 @@ ConVar mapbase_prop_consistency_noremove("mapbase_prop_consistency_noremove", "1 #ifdef EZ2 #define PROP_PHYSICS_KICK_MULTIPLIER 3 + + // Unique vort barrel boogie color + static const Vector g_vecVortBarrelBoogieColor( 0.1675, 0.90, 0.1675 ); #endif //----------------------------------------------------------------------------- @@ -280,6 +285,10 @@ void CBaseProp::Spawn( void ) #endif } +#ifdef EZ2 + PostPropDataPrecache(); +#endif + SetMoveType( MOVETYPE_PUSH ); m_takedamage = DAMAGE_NO; SetNextThink( TICK_NEVER_THINK ); @@ -831,6 +840,9 @@ bool CBreakableProp::HandleInteraction( int interactionType, void *data, CBaseCo // If we're an explosive barrel, DON'T explode violently! if ( HasInteraction( PROPINTER_PHYSGUN_BREAK_EXPLODE ) || +#ifdef EZ2 + HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP ) || +#endif HasInteraction( PROPINTER_PHYSGUN_FIRST_BREAK ) || HasInteraction( PROPINTER_FIRE_FLAMMABLE ) || HasInteraction( PROPINTER_FIRE_IGNITE_HALFHEALTH ) || @@ -1034,6 +1046,9 @@ void CBreakableProp::Spawn() if ( ( m_iHealth == 0 ) || ( !m_iNumBreakableChunks && !HasInteraction( PROPINTER_PHYSGUN_BREAK_EXPLODE ) && +#ifdef EZ2 + !HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP ) && +#endif !HasInteraction( PROPINTER_PHYSGUN_FIRST_BREAK ) && !HasInteraction( PROPINTER_FIRE_FLAMMABLE ) && !HasInteraction( PROPINTER_FIRE_IGNITE_HALFHEALTH ) && @@ -1049,6 +1064,9 @@ void CBreakableProp::Spawn() if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE ) { if ( HasInteraction( PROPINTER_PHYSGUN_BREAK_EXPLODE ) || +#ifdef EZ2 + HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP ) || +#endif HasInteraction( PROPINTER_FIRE_IGNITE_HALFHEALTH ) ) { // Exploding barrels, exploding gas cans @@ -1842,9 +1860,30 @@ void CBreakableProp::Precache() PrecacheScriptSound( STRING(m_iszPuntSound) ); } +#ifdef EZ2 + // Prop data isn't initialized until spawn, but this is needed for save/restore + PostPropDataPrecache(); +#endif + BaseClass::Precache(); } +#ifdef EZ2 +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBreakableProp::PostPropDataPrecache( void ) +{ + if (HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP )) + { + PrecacheScriptSound( "VortBarrel.Burst" ); + PrecacheParticleSystem( "vortbarrel_explode" ); + } + + BaseClass::PostPropDataPrecache(); +} +#endif + // Get the root physics object from which all broken pieces will // derive their positions and velocities IPhysicsObject *CBreakableProp::GetRootPhysicsObjectForBreak() @@ -1953,6 +1992,17 @@ void CBreakableProp::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ) 0.0f, this ); EmitSound("PropaneTank.Burst"); } +#ifdef EZ2 + else if (HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP )) + { + ExplosionCreate( origin, angles, pAttacker, m_explodeDamage, m_explodeRadius, + SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALL | SF_ENVEXPLOSION_NOPARTICLES | SF_ENVEXPLOSION_NOSOUND, + 0.0f, this, DMG_SHOCK | DMG_BLAST ); + + DispatchParticleEffect( "vortbarrel_explode", GetAbsOrigin(), GetAbsAngles() ); + EmitSound( "VortBarrel.Burst" ); + } +#endif else { float flScale = GetModelScale(); @@ -2052,6 +2102,31 @@ void CBreakableProp::Break( CBaseEntity *pBreaker, const CTakeDamageInfo &info ) } } } +#ifdef EZ2 + else if (HasInteraction( PROPINTER_PHYSGUN_BREAK_ZAP )) + { + if ( bExploded == false ) + { + ExplosionCreate( origin, angles, pAttacker, m_explodeDamage, m_explodeRadius, + SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALL | SF_ENVEXPLOSION_NOPARTICLES | SF_ENVEXPLOSION_NOSOUND, + 0.0f, this, DMG_SHOCK | DMG_BLAST ); + + DispatchParticleEffect( "vortbarrel_explode", GetAbsOrigin(), GetAbsAngles() ); + EmitSound( "VortBarrel.Burst" ); + } + + // Find and boogie all dead NPCs within the radius + // TODO: Boogie ragdolls directly? That means ragdolls already on the ground would boogie + CBaseEntity *pEntity = NULL; + for ( CEntitySphereQuery sphere( origin, m_explodeRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) + { + if( pEntity && !pEntity->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pEntity->MyCombatCharacterPointer() && pEntity->MyCombatCharacterPointer()->m_hDeathRagdoll ) + { + CRagdollBoogie::Create( pEntity->MyCombatCharacterPointer()->m_hDeathRagdoll, 200, gpGlobals->curtime, 4.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL, &g_vecVortBarrelBoogieColor ); + } + } + } +#endif #ifndef HL2MP UTIL_Remove( this ); diff --git a/sp/src/game/server/props.h b/sp/src/game/server/props.h index b9e10dd8aae..3966d8c70bc 100644 --- a/sp/src/game/server/props.h +++ b/sp/src/game/server/props.h @@ -44,6 +44,10 @@ class CBaseProp : public CBaseAnimating // Attempt to replace a dynamic_cast virtual bool IsPropPhysics() { return false; } #endif + +#ifdef EZ2 + virtual void PostPropDataPrecache( void ) {} +#endif }; @@ -61,6 +65,9 @@ class CBreakableProp : public CBaseProp, public IBreakableWithPropData, public C virtual void Spawn(); virtual void Precache(); +#ifdef EZ2 + virtual void PostPropDataPrecache( void ); +#endif virtual float GetAutoAimRadius() { return 24.0f; } #ifdef MAPBASE diff --git a/sp/src/game/shared/props_shared.cpp b/sp/src/game/shared/props_shared.cpp index 6d0f920973b..d6a7d29ae88 100644 --- a/sp/src/game/shared/props_shared.cpp +++ b/sp/src/game/shared/props_shared.cpp @@ -172,6 +172,14 @@ propdata_interaction_s sPropdataInteractionSections[PROPINTER_NUM_INTERACTIONS] { "physgun_interactions", "allow_overhead", "yes" }, // PROPINTER_PHYSGUN_ALLOW_OVERHEAD, { "world_interactions", "onworldimpact", "bloodsplat" }, // PROPINTER_WORLD_BLOODSPLAT, + +#ifdef EZ2 + // HACKHACK: This interaction didn't have an entry in this array and is needed for subsequent interactions to work. + // TODO: This change would be more fitting for Mapbase since it's needed to add any new interactions. + { "physgun_interactions", "onfirstimpact", "notify_children" }, // PROPINTER_PHYSGUN_NOTIFY_CHILDREN, + + { "physgun_interactions", "onbreak", "explode_zap" }, // PROPINTER_PHYSGUN_BREAK_ZAP, +#endif }; #else extern propdata_interaction_s sPropdataInteractionSections[PROPINTER_NUM_INTERACTIONS]; diff --git a/sp/src/game/shared/props_shared.h b/sp/src/game/shared/props_shared.h index 87ce823d227..9ea4d8a5909 100644 --- a/sp/src/game/shared/props_shared.h +++ b/sp/src/game/shared/props_shared.h @@ -72,6 +72,10 @@ enum propdata_interactions_t PROPINTER_PHYSGUN_NOTIFY_CHILDREN, // "onfirstimpact" cause attached flechettes to explode +#ifdef EZ2 + PROPINTER_PHYSGUN_BREAK_ZAP, // "onbreak" "explode_zap" +#endif + // If we get more than 32 of these, we'll need a different system PROPINTER_NUM_INTERACTIONS, From 58db34ab970d88687bccdc27bca71efe986ce830 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 23 Aug 2024 18:37:37 -0500 Subject: [PATCH 075/103] weapon_oicw --- .../game/client/hl2/c_weapon__stubs_hl2.cpp | 1 + sp/src/game/server/ez2/weapon_oicw.cpp | 627 ++++++++++++++++++ sp/src/game/server/mapbase/ai_grenade.h | 6 +- sp/src/game/server/server_ez2.vpc | 1 + 4 files changed, 634 insertions(+), 1 deletion(-) create mode 100644 sp/src/game/server/ez2/weapon_oicw.cpp diff --git a/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp b/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp index 0671bbce4ef..a6581c958a7 100644 --- a/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp +++ b/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp @@ -59,6 +59,7 @@ STUB_WEAPON_CLASS( weapon_arbeit_clipboard, WeaponArbeitClipboard, C_WeaponCitiz STUB_WEAPON_CLASS( weapon_flechette_shotgun, WeaponFlechetteShotgun, C_BaseHLCombatWeapon ); STUB_WEAPON_CLASS( weapon_stasis_grenade, WeaponStasisGrenade, C_BaseHLCombatWeapon ); +STUB_WEAPON_CLASS( weapon_oicw, WeaponOICW, C_HLSelectFireMachineGun ); #endif diff --git a/sp/src/game/server/ez2/weapon_oicw.cpp b/sp/src/game/server/ez2/weapon_oicw.cpp new file mode 100644 index 00000000000..9ac7dfb3717 --- /dev/null +++ b/sp/src/game/server/ez2/weapon_oicw.cpp @@ -0,0 +1,627 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "basehlcombatweapon.h" +#include "npcevent.h" +#include "basecombatcharacter.h" +#include "ai_basenpc.h" +#include "player.h" +#include "game.h" +#include "in_buttons.h" +#include "grenade_ar2.h" +#include "ai_memory.h" +#include "soundent.h" +#include "rumble_shared.h" +#include "gamestats.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ConVar sk_plr_dmg_smg1_grenade; +#ifdef MAPBASE +extern ConVar sk_npc_dmg_smg1_grenade; +#endif + +class CWeaponOICW : public CHLSelectFireMachineGun +{ + DECLARE_DATADESC(); +public: + DECLARE_CLASS( CWeaponOICW, CHLSelectFireMachineGun ); + + CWeaponOICW(); + + DECLARE_SERVERCLASS(); + + void Precache( void ); + void AddViewKick( void ); + void SecondaryAttack( void ); + int GetMinBurst() { return 2; } + int GetMaxBurst() { return 5; } + + bool Reload( void ); + void ItemPostFrame( void ); + + float GetFireRate( void ) { return m_bZoomed ? 0.15 : 0.1f; } + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + int WeaponRangeAttack2Condition( float flDot, float flDist ); + Activity GetPrimaryAttackActivity( void ); + + virtual const Vector& GetBulletSpread( void ) + { + static const Vector cone = VECTOR_CONE_2DEGREES; + static const Vector zoomedCone = VECTOR_CONE_1DEGREES; + + if (GetOwner() && GetOwner()->IsNPC()) + { + static const Vector npcCone = VECTOR_CONE_3DEGREES; + return npcCone; + } + + return m_bZoomed ? zoomedCone : cone; + } + + const WeaponProficiencyInfo_t *GetProficiencyValues(); + + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + + DECLARE_ACTTABLE(); + +protected: + + Vector m_vecTossVelocity; + float m_flNextGrenadeCheck; + bool m_bZoomed; +}; + +IMPLEMENT_SERVERCLASS_ST(CWeaponOICW, DT_WeaponOICW) +END_SEND_TABLE() + +//LINK_ENTITY_TO_CLASS( weapon_ar1, CWeaponOICW ); +LINK_ENTITY_TO_CLASS( weapon_oicw, CWeaponOICW ); + +// Don't need to precache this in every game +//PRECACHE_WEAPON_REGISTER( weapon_oicw ); + +BEGIN_DATADESC( CWeaponOICW ) + + DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), + DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ), + DEFINE_FIELD( m_bZoomed, FIELD_BOOLEAN ), + +END_DATADESC() + +acttable_t CWeaponOICW::m_acttable[] = +{ + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR1, true }, + +#if EXPANDED_HL2_UNUSED_WEAPON_ACTIVITIES + // Optional new NPC activities + // (these should fall back to AR2 animations when they don't exist on an NPC) + { ACT_RELOAD, ACT_RELOAD_AR1, true }, + { ACT_IDLE, ACT_IDLE_AR1, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_AR1, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_AR1_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_AR1_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_AR1, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_AR1_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_AR1_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_AR1, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_AR1_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_AR1_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_AR1, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_AR1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_AR1_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_AR1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_AR1_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_AR1_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_AR1, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_AR1_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_AR1_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_AR1, false },//always aims +//End readiness activities + + { ACT_WALK, ACT_WALK_AR1, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_AR1, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_AR1, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_AR1, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_AR1, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_AR1_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_AR1_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR1_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_AR1_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR1, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, + +#if EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_AR1_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_AR1_MED, false }, +#endif + +#if EXPANDED_HL2DM_ACTIVITIES + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR1, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR1, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR1, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR1, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR1, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR1, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_AR1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR1, false }, +#endif +#endif +}; + +IMPLEMENT_ACTTABLE(CWeaponOICW); + +//========================================================= +CWeaponOICW::CWeaponOICW( ) +{ + m_fMinRange1 = 65; + m_fMaxRange1 = 2048; + + m_bAltFiresUnderwater = false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::Precache( void ) +{ + UTIL_PrecacheOther("grenade_ar2"); + + BaseClass::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + WeaponSoundRealtime( SINGLE_NPC ); + + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, + MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2, entindex(), 0 ); + + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +} + +#ifdef MAPBASE +float GetCurrentGravity( void ); +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_SMG1: + case EVENT_WEAPON_AR1: + case EVENT_WEAPON_AR2: + { + Vector vecShootOrigin, vecShootDir; + QAngle angDiscard; + + // Support old style attachment point firing + if ((pEvent->options == NULL) || (pEvent->options[0] == '\0') || (!pOperator->GetAttachment(pEvent->options, vecShootOrigin, angDiscard))) + { + vecShootOrigin = pOperator->Weapon_ShootPosition(); + } + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + ASSERT( npc != NULL ); + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); + } + break; + +#ifdef MAPBASE + case EVENT_WEAPON_AR2_ALTFIRE: + { + WeaponSound( WPN_DOUBLE ); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + if (!npc) + return; + + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetShootEnemyDir( vecShootOrigin ); + + Vector vecTarget = npc->GetAltFireTarget(); + Vector vecThrow; + if (vecTarget == vec3_origin) + AngleVectors( npc->EyeAngles(), &vecThrow ); // Not much else to do, unfortunately + else + { + // Because this is happening right now, we can't "VecCheckThrow" and can only "VecDoThrow", you know what I mean? + // ...Anyway, this borrows from that so we'll never return vec3_origin. + //vecThrow = VecCheckThrow( this, vecShootOrigin, vecTarget, 600.0, 0.5 ); + + vecThrow = (vecTarget - vecShootOrigin); + + // throw at a constant time + float time = vecThrow.Length() / 600.0; + vecThrow = vecThrow * (1.0 / time); + + // adjust upward toss to compensate for gravity loss + vecThrow.z += (GetCurrentGravity() * 0.5) * time * 0.5; + } + + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc ); + pGrenade->SetAbsVelocity( vecThrow ); + pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + + pGrenade->SetThrower( npc ); + + pGrenade->SetGravity(0.5); // lower gravity since grenade is aerodynamic and engine doesn't know it. + + pGrenade->SetDamage(sk_npc_dmg_smg1_grenade.GetFloat()); + + variant_t var; + var.SetEntity(pGrenade); + npc->FireNamedOutput("OnThrowGrenade", var, pGrenade, npc); + } + break; +#else + /*//FIXME: Re-enable + case EVENT_WEAPON_AR2_GRENADE: + { + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetShootEnemyDir( vecShootOrigin ); + + Vector vecThrow = m_vecTossVelocity; + + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc ); + pGrenade->SetAbsVelocity( vecThrow ); + pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY ); + pGrenade->m_hOwner = npc; + pGrenade->m_pMyWeaponAR2 = this; + pGrenade->SetDamage(sk_npc_dmg_ar2_grenade.GetFloat()); + + // FIXME: arrgg ,this is hard coded into the weapon??? + m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + + m_iClip2--; + } + break; + */ +#endif + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Activity +//----------------------------------------------------------------------------- +Activity CWeaponOICW::GetPrimaryAttackActivity( void ) +{ + if ( m_nShotsFired < 2 ) + return ACT_VM_PRIMARYATTACK; + + if ( m_nShotsFired < 3 ) + return ACT_VM_RECOIL1; + + if ( m_nShotsFired < 4 ) + return ACT_VM_RECOIL2; + + return ACT_VM_RECOIL3; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::ItemPostFrame( void ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner ) + { + if ( pOwner->m_nButtons & IN_ZOOM ) + { + m_bZoomed = true; + } + else + { + m_bZoomed = false; + } + } + + BaseClass::ItemPostFrame(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CWeaponOICW::Reload( void ) +{ + bool fRet; + float fCacheTime = m_flNextSecondaryAttack; + + fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD ); + if ( fRet ) + { + // Undo whatever the reload process has done to our secondary + // attack timer. We allow you to interrupt reloading to fire + // a grenade. + m_flNextSecondaryAttack = GetOwner()->m_flNextAttack = fCacheTime; + + WeaponSound( RELOAD ); + } + + return fRet; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::AddViewKick( void ) +{ + #define EASY_DAMPEN 0.5f + #define MAX_VERTICAL_KICK 8.0f //Degrees + #define SLIDE_LIMIT 1.5f //Seconds + + #define ZOOMED_EASY_DAMPEN 0.5f + #define ZOOMED_MAX_VERTICAL_KICK 4.0f //Degrees + #define ZOOMED_SLIDE_LIMIT 3.5f //Seconds + + //Get the view kick + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if ( pPlayer == NULL ) + return; + + if ( m_bZoomed ) + { + DoMachineGunKick( pPlayer, ZOOMED_EASY_DAMPEN, ZOOMED_MAX_VERTICAL_KICK, m_fFireDuration, ZOOMED_SLIDE_LIMIT ); + } + else + { + DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::SecondaryAttack( void ) +{ + // Only the player fires this way so we can cast + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if ( pPlayer == NULL ) + return; + + //Must have ammo + if ( ( pPlayer->GetAmmoCount( m_iSecondaryAmmoType ) <= 0 ) || ( pPlayer->GetWaterLevel() == 3 ) ) + { + SendWeaponAnim( ACT_VM_DRYFIRE ); + BaseClass::WeaponSound( EMPTY ); + m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f; + return; + } + + if( m_bInReload ) + m_bInReload = false; + + // MUST call sound before removing a round from the clip of a CMachineGun + BaseClass::WeaponSound( WPN_DOUBLE ); + + pPlayer->RumbleEffect( RUMBLE_357, 0, RUMBLE_FLAGS_NONE ); + + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecThrow; + // Don't autoaim on grenade tosses + AngleVectors( pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vecThrow ); + VectorScale( vecThrow, 1000.0f, vecThrow ); + + //Create the grenade + QAngle angles; + VectorAngles( vecThrow, angles ); + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecSrc, angles, pPlayer ); + pGrenade->SetAbsVelocity( vecThrow ); + + pGrenade->SetLocalAngularVelocity( RandomAngle( -400, 400 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + pGrenade->SetThrower( GetOwner() ); + pGrenade->SetDamage( sk_plr_dmg_smg1_grenade.GetFloat() ); + + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON ); + + // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif + + // Decrease ammo + pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType ); + + // Can shoot again immediately + m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f; + + // Can blow up after a short delay (so have time to release mouse button) + m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f; + + // Register a muzzleflash for the AI. + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); + + m_iSecondaryAttacks++; + gamestats->Event_WeaponFired( pPlayer, false, GetClassname() ); +} + +#define COMBINE_MIN_GRENADE_CLEAR_DIST 256 + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flDot - +// flDist - +// Output : int +//----------------------------------------------------------------------------- +int CWeaponOICW::WeaponRangeAttack2Condition( float flDot, float flDist ) +{ + CAI_BaseNPC *npcOwner = GetOwner()->MyNPCPointer(); + + return COND_NONE; + +/* + // -------------------------------------------------------- + // Assume things haven't changed too much since last time + // -------------------------------------------------------- + if (gpGlobals->curtime < m_flNextGrenadeCheck ) + return m_lastGrenadeCondition; +*/ + + // ----------------------- + // If moving, don't check. + // ----------------------- + if ( npcOwner->IsMoving()) + return COND_NONE; + + CBaseEntity *pEnemy = npcOwner->GetEnemy(); + + if (!pEnemy) + return COND_NONE; + + Vector vecEnemyLKP = npcOwner->GetEnemyLKP(); + if ( !( pEnemy->GetFlags() & FL_ONGROUND ) && pEnemy->GetWaterLevel() == 0 && vecEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) ) + { + //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to + // be grenaded. + // don't throw grenades at anything that isn't on the ground! + return COND_NONE; + } + + // -------------------------------------- + // Get target vector + // -------------------------------------- + Vector vecTarget; + if (random->RandomInt(0,1)) + { + // magically know where they are + vecTarget = pEnemy->WorldSpaceCenter(); + } + else + { + // toss it to where you last saw them + vecTarget = vecEnemyLKP; + } + // vecTarget = m_vecEnemyLKP + (pEnemy->BodyTarget( GetLocalOrigin() ) - pEnemy->GetLocalOrigin()); + // estimate position + // vecTarget = vecTarget + pEnemy->m_vecVelocity * 2; + + + if ( ( vecTarget - npcOwner->GetLocalOrigin() ).Length2D() <= COMBINE_MIN_GRENADE_CLEAR_DIST ) + { + // crap, I don't want to blow myself up + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return (COND_NONE); + } + + // --------------------------------------------------------------------- + // Are any friendlies near the intended grenade impact area? + // --------------------------------------------------------------------- + CBaseEntity *pTarget = NULL; + + while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST ) ) != NULL ) + { + //Check to see if the default relationship is hatred, and if so intensify that + if ( npcOwner->IRelationType( pTarget ) == D_LI ) + { + // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return (COND_WEAPON_BLOCKED_BY_FRIEND); + } + } + + // --------------------------------------------------------------------- + // Check that throw is legal and clear + // --------------------------------------------------------------------- + // FIXME: speed is based on difficulty... + + Vector vecToss = VecCheckThrow( this, npcOwner->GetLocalOrigin() + Vector(0,0,60), vecTarget, 600.0, 0.5 ); + if ( vecToss != vec3_origin ) + { + m_vecTossVelocity = vecToss; + + // don't check again for a while. + // JAY: HL1 keeps checking - test? + //m_flNextGrenadeCheck = gpGlobals->curtime; + m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second. + return COND_CAN_RANGE_ATTACK2; + } + else + { + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return COND_WEAPON_SIGHT_OCCLUDED; + } +} + +//----------------------------------------------------------------------------- +const WeaponProficiencyInfo_t *CWeaponOICW::GetProficiencyValues() +{ + static WeaponProficiencyInfo_t proficiencyTable[] = + { + { 7.0, 0.75 }, + { 5.00, 0.75 }, + { 10.0/3.0, 0.85 }, + { 5.0/3.0, 0.75 }, + { 1.00, 1.0 }, + }; + + COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); + + return proficiencyTable; +} diff --git a/sp/src/game/server/mapbase/ai_grenade.h b/sp/src/game/server/mapbase/ai_grenade.h index db8bc1f58bc..2148880a033 100644 --- a/sp/src/game/server/mapbase/ai_grenade.h +++ b/sp/src/game/server/mapbase/ai_grenade.h @@ -416,7 +416,7 @@ bool CAI_GrenadeUser::CanAltFireEnemy( bool bUseFreeKnowledge ) return false; #ifdef EZ2 - if (!EntIsClass(this->GetActiveWeapon(), gm_isz_class_AR2) && !EntIsClass(this->GetActiveWeapon(), gm_isz_class_SMG1) && !FClassnameIs(this->GetActiveWeapon(), "weapon_pulsepistol")) + if (!EntIsClass(this->GetActiveWeapon(), gm_isz_class_AR2) && !EntIsClass(this->GetActiveWeapon(), gm_isz_class_SMG1) && !FClassnameIs(this->GetActiveWeapon(), "weapon_pulsepistol") && !FClassnameIs(this->GetActiveWeapon(), "weapon_oicw")) #else if (!EntIsClass(this->GetActiveWeapon(), gm_isz_class_AR2) && !EntIsClass(this->GetActiveWeapon(), gm_isz_class_SMG1)) #endif @@ -725,7 +725,11 @@ void CAI_GrenadeUser::DropGrenadeItemsOnDeath( const CTakeDamageInfo & CBaseEntity *pItem; if (this->GetActiveWeapon()) { +#ifdef EZ2 + if (FClassnameIs( this->GetActiveWeapon(), "weapon_smg1" ) || FClassnameIs( this->GetActiveWeapon(), "weapon_oicw" )) +#else if (FClassnameIs( this->GetActiveWeapon(), "weapon_smg1" )) +#endif pItem = this->DropItem( "item_ammo_smg1_grenade", this->WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) ); #ifdef EZ2 else if (FClassnameIs( this->GetActiveWeapon(), "weapon_pulsepistol" )) diff --git a/sp/src/game/server/server_ez2.vpc b/sp/src/game/server/server_ez2.vpc index 1f53853544b..4f0a0e06ca2 100644 --- a/sp/src/game/server/server_ez2.vpc +++ b/sp/src/game/server/server_ez2.vpc @@ -89,6 +89,7 @@ $Project "Server (Entropy Zero 2)" $File "ez2\npc_zombigaunt.h" $File "ez2\npc_zombigaunt.cpp" $File "ez2\prop_command_point.cpp" + $File "ez2\weapon_oicw.cpp" $File "ez2\weapon_displacer_pistol.h" $File "ez2\weapon_displacer_pistol.cpp" } From 56b30729bccbd82bbd2e08d986676915e450a964 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 24 Aug 2024 22:25:03 -0500 Subject: [PATCH 076/103] Player dual wielding support --- sp/src/game/client/weapons_resource.cpp | 22 +++ sp/src/game/server/ez2/npc_assassin.cpp | 21 +-- sp/src/game/server/hl2/weapon_357.cpp | 6 - sp/src/game/server/hl2/weapon_pistol.cpp | 103 ++++++++--- .../shared/hl2/basehlcombatweapon_shared.cpp | 173 +++++++++++++++++- .../shared/hl2/basehlcombatweapon_shared.h | 40 +++- sp/src/game/shared/weapon_parse.cpp | 3 + sp/src/game/shared/weapon_parse.h | 8 + 8 files changed, 315 insertions(+), 61 deletions(-) diff --git a/sp/src/game/client/weapons_resource.cpp b/sp/src/game/client/weapons_resource.cpp index 8f8d58e86de..7bba99670d3 100644 --- a/sp/src/game/client/weapons_resource.cpp +++ b/sp/src/game/client/weapons_resource.cpp @@ -176,6 +176,28 @@ void WeaponsResource::LoadWeaponSprites( WEAPON_FILE_INFO_HANDLE hWeaponFileInfo } } +#ifdef EZ2 + p = FindHudTextureInDict( tempList, "weapon_dual" ); + if ( p ) + { + pWeaponInfo->iconInactiveDual = gHUD.AddUnsearchableHudIconToList( *p ); + if ( pWeaponInfo->iconInactiveDual) + { + pWeaponInfo->iconInactiveDual->Precache(); + } + } + + p = FindHudTextureInDict( tempList, "weapon_dual_s" ); + if ( p ) + { + pWeaponInfo->iconActiveDual = gHUD.AddUnsearchableHudIconToList( *p ); + if ( pWeaponInfo->iconActiveDual) + { + pWeaponInfo->iconActiveDual->Precache(); + } + } +#endif + p = FindHudTextureInDict( tempList, "weapon_small" ); if ( p ) { diff --git a/sp/src/game/server/ez2/npc_assassin.cpp b/sp/src/game/server/ez2/npc_assassin.cpp index 161bd19247e..0be377085b9 100644 --- a/sp/src/game/server/ez2/npc_assassin.cpp +++ b/sp/src/game/server/ez2/npc_assassin.cpp @@ -1880,26 +1880,7 @@ void CNPC_Assassin::AddLeftHandGun( CBaseCombatWeapon *pWeapon ) return; } - // Create a fake second pistol - CBaseEntity *pEnt = CBaseEntity::CreateNoSpawn( "prop_dynamic_override", this->GetLocalOrigin(), this->GetLocalAngles(), this ); - if (pEnt) - { - // HACKHACK: Just add "_left" to the end of the model name - char szLeftModel[MAX_PATH]; - V_StripExtension( pWeapon->GetWorldModel(), szLeftModel, sizeof( szLeftModel ) ); - V_strncat( szLeftModel, "_left.mdl", sizeof( szLeftModel ) ); - - pEnt->SetModelName( MAKE_STRING( szLeftModel ) ); - pEnt->SetRenderMode( kRenderTransColor ); - DispatchSpawn( pEnt ); - pEnt->FollowEntity( this, true ); - pEnt->SetOwnerEntity( pWeapon ); - - m_hLeftHandGun = static_cast(pEnt); - - // Make it dual-wielded - assert_cast(pWeapon)->SetLeftHandGun( m_hLeftHandGun ); - } + m_hLeftHandGun = assert_cast(pWeapon)->CreateLeftHandGun(); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/weapon_357.cpp b/sp/src/game/server/hl2/weapon_357.cpp index e38c16495f7..2deb065c5b0 100644 --- a/sp/src/game/server/hl2/weapon_357.cpp +++ b/sp/src/game/server/hl2/weapon_357.cpp @@ -98,12 +98,6 @@ class CWeapon357 : public CBaseHLCombatWeapon virtual void SetActivity( Activity act, float duration ); bool CanDualWield() const { return true; } - CBaseAnimating *GetLeftHandGun() const { return m_hLeftHandGun; } - void SetLeftHandGun( CBaseAnimating *pGun ) { m_hLeftHandGun = pGun; } - -private: - - CHandle m_hLeftHandGun; #endif }; diff --git a/sp/src/game/server/hl2/weapon_pistol.cpp b/sp/src/game/server/hl2/weapon_pistol.cpp index 00ede3df6c9..ce575c082b7 100644 --- a/sp/src/game/server/hl2/weapon_pistol.cpp +++ b/sp/src/game/server/hl2/weapon_pistol.cpp @@ -92,7 +92,12 @@ class CWeaponPistol : public CHLMachineGun 1.0f ); // We lerp from very accurate to inaccurate over time +#ifdef EZ2 + // Twice as inaccurate when dual wielding + VectorLerp( VECTOR_CONE_1DEGREES, IsDualWielding() ? VECTOR_CONE_6DEGREES*2 : VECTOR_CONE_6DEGREES, ramp, cone ); +#else VectorLerp( VECTOR_CONE_1DEGREES, VECTOR_CONE_6DEGREES, ramp, cone ); +#endif } else { @@ -116,7 +121,7 @@ class CWeaponPistol : public CHLMachineGun virtual float GetFireRate( void ) { #ifdef EZ2 - if (m_hLeftHandGun != NULL) + if (IsDualWielding() && (GetOwner() && GetOwner()->IsNPC())) return 0.25f; #endif @@ -136,12 +141,6 @@ class CWeaponPistol : public CHLMachineGun virtual void SetActivity( Activity act, float duration ); bool CanDualWield() const { return true; } - CBaseAnimating *GetLeftHandGun() const { return m_hLeftHandGun; } - void SetLeftHandGun( CBaseAnimating *pGun ) { m_hLeftHandGun = pGun; } - -private: - - CHandle m_hLeftHandGun; #endif protected: @@ -370,7 +369,7 @@ void CWeaponPistol::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCh { #ifdef EZ2 // HACKHACK: Ignore the regular firing event while dual-wielding - if (GetLeftHandGun()) + if (IsDualWielding()) return; #endif @@ -467,7 +466,11 @@ void CWeaponPistol::PrimaryAttack( void ) } m_flLastAttackTime = gpGlobals->curtime; +#ifdef EZ2 + m_flSoonestPrimaryAttack = gpGlobals->curtime + (IsDualWielding() ? PISTOL_FASTEST_REFIRE_TIME * 0.5f : PISTOL_FASTEST_REFIRE_TIME); +#else m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME; +#endif CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, GetOwner() ); CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); @@ -664,7 +667,20 @@ class CWeaponPulsePistol : public CWeaponPistol virtual bool Reload( void ) { return false; } // The pulse pistol does not reload - virtual int GetMaxClip2( void ) const { int iBase = BaseClass::GetMaxClip2(); return iBase > 0 ? iBase : sv_pulse_pistol_max_charge.GetInt(); } + virtual int GetMaxClip2( void ) const + { + int iBase = BaseClass::GetMaxClip2(); + if (iBase > 0) + { + return iBase; + } + else + { + return IsDualWielding() ? (sv_pulse_pistol_max_charge.GetInt() * 2) : sv_pulse_pistol_max_charge.GetInt(); + } + } + + int GetMinShotClip() const { return IsDualWielding() ? 20 : 10; } virtual const Vector& GetBulletSpread( void ) { @@ -685,7 +701,12 @@ class CWeaponPulsePistol : public CWeaponPistol 1.0f ); // We lerp from very accurate to inaccurate over time +#ifdef EZ2 + // Twice as inaccurate when dual wielding + VectorLerp( VECTOR_CONE_2DEGREES, IsDualWielding() ? VECTOR_CONE_15DEGREES * 2 : VECTOR_CONE_15DEGREES, ramp, cone); +#else VectorLerp( VECTOR_CONE_2DEGREES, VECTOR_CONE_15DEGREES, ramp, cone ); +#endif return cone; } @@ -697,7 +718,7 @@ class CWeaponPulsePistol : public CWeaponPistol return 3.0f; } - if (GetLeftHandGun() != NULL) + if (IsDualWielding() != NULL) return 0.5f; return 1.0f; @@ -725,6 +746,7 @@ class CWeaponPulsePistol : public CWeaponPistol protected: CHandle m_hChargeSprite; + CHandle m_hChargeSprite2; // For dual pistols private: // For recharging the ammo @@ -811,7 +833,7 @@ bool CWeaponPulsePistol::IsChargePressed( int chargeButton, CBasePlayer * pOwner return false; // If "no charge hold" is set, we're out of ammo, but we have a charge, treat it as though the player let go of the attack button - if (sv_pulse_pistol_no_charge_hold.GetBool() && m_iClip2 > 0 && m_iClip1 <= 10) + if (sv_pulse_pistol_no_charge_hold.GetBool() && m_iClip2 > 0 && m_iClip1 <= GetMinShotClip()) return false; return (pOwner->m_nButtons & chargeButton) > 0; @@ -903,7 +925,7 @@ void CWeaponPulsePistol::Operator_HandleAnimEvent( animevent_t * pEvent, CBaseCo { case AE_WPN_INCREMENTAMMO: case AE_SLIDERETURN: - m_iClip1 = MAX( m_iClip1, sv_pulse_pistol_slide_return_charge.GetInt() ); + m_iClip1 = MAX( m_iClip1, IsDualWielding() ? sv_pulse_pistol_slide_return_charge.GetInt()*2 : sv_pulse_pistol_slide_return_charge.GetInt() ); WeaponSound( RELOAD, m_flNextPrimaryAttack ); break; @@ -946,14 +968,14 @@ void CWeaponPulsePistol::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, pOperator->FireBullets( info ); pOperator->DoMuzzleFlash(); - if (m_iClip1 < 10) + if (m_iClip1 < GetMinShotClip()) { // Reload immediately m_iClip1 = 0; } else { - m_iClip1 -= 10; + m_iClip1 -= GetMinShotClip(); // NPCs handle recharging after every shot RechargeAmmo(); @@ -1038,14 +1060,17 @@ void CWeaponPulsePistol::RechargeAmmo( void ) float flChargeInterval = gpGlobals->curtime - m_flLastChargeTime; m_flLastChargeTime = gpGlobals->curtime; - // This code is inherited from the airboat. I've changed it to work for the pistol. - int nMaxAmmo = 50; - if (m_iClip1 == nMaxAmmo) + if (m_iClip1 == GetMaxClip1()) { return; } + // This code is inherited from the airboat. I've changed it to work for the pistol. float flRechargeRate = 5; + + if (IsDualWielding()) + flRechargeRate *= 2.0f; + float flChargeAmount = flRechargeRate * flChargeInterval; if (m_flDrainRemainder != 0.0f) { @@ -1065,9 +1090,9 @@ void CWeaponPulsePistol::RechargeAmmo( void ) int nAmmoToAdd = (int)m_flChargeRemainder; m_flChargeRemainder -= nAmmoToAdd; m_iClip1 += nAmmoToAdd; - if (m_iClip1 > nMaxAmmo) + if (m_iClip1 > GetMaxClip1()) { - m_iClip1 = nMaxAmmo; + m_iClip1 = GetMaxClip1(); m_flChargeRemainder = 0.0f; } } @@ -1093,7 +1118,7 @@ void CWeaponPulsePistol::MakeTracer( const Vector &vecTracerSrc, const trace_t & //----------------------------------------------------------------------------- void CWeaponPulsePistol::PrimaryAttack( void ) { - if (m_iClip1 < 10) // Don't allow firing if we only have one shot left + if (m_iClip1 < GetMinShotClip()) // Don't allow firing if we only have one shot left { DryFire(); } @@ -1148,7 +1173,7 @@ void CWeaponPulsePistol::PrimaryAttack( void ) } // Subtract the charge - m_iClip1 = MAX(m_iClip1 - 10, 1); // Never drop the charge below 1 + m_iClip1 = MAX(m_iClip1 - GetMinShotClip(), 1); // Never drop the charge below 1 m_iClip2 = 0; m_iPrimaryAttacks++; @@ -1192,7 +1217,7 @@ void CWeaponPulsePistol::PrimaryAttack( void ) m_iPrimaryAttacks++; gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); - m_flSoonestPrimaryAttack = gpGlobals->curtime + PULSE_PISTOL_FASTEST_REFIRE_TIME; + m_flSoonestPrimaryAttack = gpGlobals->curtime + (IsDualWielding() ? PULSE_PISTOL_FASTEST_REFIRE_TIME * 0.5f : PULSE_PISTOL_FASTEST_REFIRE_TIME); m_flNextPrimaryAttack = gpGlobals->curtime + PULSE_PISTOL_FASTEST_REFIRE_TIME; } } @@ -1203,7 +1228,7 @@ void CWeaponPulsePistol::PrimaryAttack( void ) void CWeaponPulsePistol::ChargeAttack( void ) { // Play the slide rack animation if we're trying to charge but have no ammo - if ( m_iClip1 <= 10 && m_iClip2 <= 0 ) { + if ( m_iClip1 <= GetMinShotClip() && m_iClip2 <= 0) { if( m_flNextPrimaryAttack <= gpGlobals->curtime ) DryFire(); return; @@ -1211,7 +1236,7 @@ void CWeaponPulsePistol::ChargeAttack( void ) int nMaxCharge = GetMaxClip2(); // If there is only one shot left or the charge has reached maximum, do not charge! - if (m_iClip1 <= 10 || m_iClip2 == nMaxCharge) + if (m_iClip1 <= GetMinShotClip() || m_iClip2 == nMaxCharge) { RechargeAmmo(); return; @@ -1229,6 +1254,10 @@ void CWeaponPulsePistol::ChargeAttack( void ) // The charge attack charges twice as quickly as ammo recharges float flRechargeRate = 10; + + if (IsDualWielding()) + flRechargeRate *= 2.0f; + float flChargeAmount = flRechargeRate * flChargeInterval; m_flChargeRemainder += flChargeAmount; @@ -1337,6 +1366,18 @@ void CWeaponPulsePistol::StartChargeEffects() m_hChargeSprite->SetBrightness( 0, 0.1f ); m_hChargeSprite->SetScale( 0.05f, 0.05f ); m_hChargeSprite->TurnOn(); + + if (IsDualWielding()) + { + m_hChargeSprite2 = CSprite::SpriteCreate( "effects/fluttercore.vmt", GetAbsOrigin(), false ); + + m_hChargeSprite2->SetAsTemporary(); + m_hChargeSprite2->SetAttachment( pOwner->GetViewModel(), 3 ); + m_hChargeSprite2->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone ); + m_hChargeSprite2->SetBrightness( 0, 0.1f ); + m_hChargeSprite2->SetScale( 0.05f, 0.05f ); + m_hChargeSprite2->TurnOn(); + } } } @@ -1348,6 +1389,11 @@ void CWeaponPulsePistol::SetChargeEffectBrightness( float alpha ) if (m_hChargeSprite != NULL) { m_hChargeSprite->SetBrightness( m_iClip2, 0.1f ); + + if (m_hChargeSprite2 != NULL) + { + m_hChargeSprite2->SetBrightness( m_iClip2, 0.1f ); + } } } @@ -1361,6 +1407,12 @@ void CWeaponPulsePistol::KillChargeEffects() UTIL_Remove( m_hChargeSprite ); m_hChargeSprite = NULL; } + + if (m_hChargeSprite2 != NULL) + { + UTIL_Remove( m_hChargeSprite2 ); + m_hChargeSprite2 = NULL; + } } //----------------------------------------------------------------------------- @@ -1482,6 +1534,7 @@ PRECACHE_WEAPON_REGISTER( weapon_pulsepistol ); BEGIN_DATADESC( CWeaponPulsePistol ) DEFINE_FIELD( m_flLastChargeTime, FIELD_TIME ), DEFINE_FIELD( m_flLastChargeSoundTime, FIELD_TIME ), - DEFINE_FIELD( m_hChargeSprite, FIELD_EHANDLE ) + DEFINE_FIELD( m_hChargeSprite, FIELD_EHANDLE ), + DEFINE_FIELD( m_hChargeSprite2, FIELD_EHANDLE ), END_DATADESC() #endif diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp index b9ba06cf433..eb62441d8d1 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp @@ -22,8 +22,14 @@ IMPLEMENT_NETWORKCLASS_ALIASED( BaseHLCombatWeapon , DT_BaseHLCombatWeapon ) BEGIN_NETWORK_TABLE( CBaseHLCombatWeapon , DT_BaseHLCombatWeapon ) #if !defined( CLIENT_DLL ) // SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ), +#ifdef EZ2 + SendPropEHandle( SENDINFO( m_hLeftHandGun ) ), +#endif #else // RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ), +#ifdef EZ2 + RecvPropEHandle( RECVINFO( m_hLeftHandGun ) ), +#endif #endif END_NETWORK_TABLE() @@ -43,6 +49,10 @@ BEGIN_DATADESC( CBaseHLCombatWeapon ) DEFINE_FIELD( m_iPrimaryAttacks, FIELD_INTEGER ), DEFINE_FIELD( m_iSecondaryAttacks, FIELD_INTEGER ), +#ifdef EZ2 + DEFINE_INPUTFUNC( FIELD_VOID, "CreateLeftHandGun", InputCreateLeftHandGun ), +#endif + END_DATADESC() #endif @@ -250,7 +260,41 @@ void CBaseHLCombatWeapon::WeaponIdle( void ) } } -#if defined(EZ2) && defined(GAME_DLL) +#ifdef EZ2 +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CBaseHLCombatWeapon::GetViewModel( int viewmodelindex ) const +{ + if (GetLeftHandGun() && GetWpnData().szViewModelDual[0]) + return GetWpnData().szViewModelDual; + + return BaseClass::GetViewModel( viewmodelindex ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture const *CBaseHLCombatWeapon::GetSpriteActive( void ) const +{ + if (GetLeftHandGun() && GetWpnData().iconActiveDual) + return GetWpnData().iconActiveDual; + + return GetWpnData().iconActive; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CHudTexture const *CBaseHLCombatWeapon::GetSpriteInactive( void ) const +{ + if (GetLeftHandGun() && GetWpnData().iconInactiveDual) + return GetWpnData().iconInactiveDual; + + return GetWpnData().iconInactive; +} + +#ifdef GAME_DLL // HACKHACK: Draws directly from npc_assassin private animevents extern int AE_PISTOL_FIRE_LEFT; extern int AE_PISTOL_FIRE_RIGHT; @@ -412,6 +456,133 @@ Activity CBaseHLCombatWeapon::ActivityOverride( Activity baseAct, bool *pRequire return BaseClass::ActivityOverride( baseAct, pRequired ); } + +//----------------------------------------------------------------------------- +// Purpose: Drop/throw the weapon with the given velocity. +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::Drop( const Vector &vecVelocity ) +{ + if (GetLeftHandGun()) + { + m_iClip1 /= 2; + m_iClip2 /= 2; + } + + CBaseCombatCharacter *pOwner = GetOwner(); + + BaseClass::Drop( vecVelocity ); + + if (pOwner && pOwner->IsPlayer()) + { + // Spawn left hand gun when regular one is dropped + if (GetLeftHandGun()) + { + // Drop the fake gun + CBaseEntity *pRealGun = CreateNoSpawn( GetClassname(), GetAbsOrigin() - Vector( 0, 0, BoundingRadius() ), GetAbsAngles(), this ); + DispatchSpawn( pRealGun ); + + if (pRealGun) + { + if (pRealGun->VPhysicsGetObject()) + { + // Copy velocity + Vector vecVelocity; + AngularImpulse vecAngVelocity; + VPhysicsGetObject()->GetVelocity( &vecVelocity, &vecAngVelocity ); + pRealGun->VPhysicsGetObject()->SetVelocity( &vecVelocity, &vecAngVelocity ); + } + + pRealGun->MyCombatWeaponPointer()->m_iClip1 = m_iClip1; + pRealGun->MyCombatWeaponPointer()->m_iClip2 = m_iClip2; + + // Act as if it was dropped the same way + pRealGun->SetThink( &CBaseCombatWeapon::SetPickupTouch ); + pRealGun->SetTouch( NULL ); + pRealGun->SetNextThink( gpGlobals->curtime + 1.0f ); + } + + UTIL_Remove( GetLeftHandGun() ); + + SetLeftHandGun( NULL ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseAnimating *CBaseHLCombatWeapon::CreateLeftHandGun() +{ + if (!GetOwner()) + { + Warning( "%s cannot create left hand gun because it has no owner\n", GetDebugName() ); + return NULL; + } + + if (!CanDualWield()) + { + Warning( "%s not capable of dual wielding\n", GetDebugName() ); + return NULL; + } + + if (GetLeftHandGun()) + { + Warning("%s already has a left-handed weapon (%s)\n", GetDebugName(), GetLeftHandGun()->GetDebugName()); + return NULL; + } + + // Create a fake second gun + CBaseEntity *pEnt = CBaseEntity::CreateNoSpawn( "prop_dynamic_override", this->GetLocalOrigin(), this->GetLocalAngles(), this ); + if (pEnt) + { + char szLeftModel[MAX_PATH]; + if (GetWpnData().szWorldModelDual[0]) + { + V_strncpy( szLeftModel, GetWpnData().szWorldModelDual, sizeof( szLeftModel ) ); + } + else + { + // If there is no specific worldmodel, then just try adding "_left" to the end of the model name + V_StripExtension( GetWorldModel(), szLeftModel, sizeof( szLeftModel ) ); + V_strncat( szLeftModel, "_left.mdl", sizeof( szLeftModel ) ); + } + + pEnt->SetModelName( MAKE_STRING( szLeftModel ) ); + pEnt->SetRenderMode( kRenderTransColor ); + DispatchSpawn( pEnt ); + pEnt->FollowEntity( GetOwner(), true ); + pEnt->SetOwnerEntity( this ); + pEnt->AddEffects( EF_NOSHADOW ); + + CBaseAnimating *pAnimating = static_cast(pEnt); + SetLeftHandGun( pAnimating ); + + if (GetOwner()->IsPlayer()) + { + SetModel( GetViewModel() ); + + if (GetOwner()->GetActiveWeapon() == this) + { + // Play the first draw animation and re-deploy the model + m_bFirstDraw = true; + DefaultDeploy( (char *)GetViewModel(), (char *)GetWorldModel(), GetDrawActivity(), (char *)GetAnimPrefix() ); + } + } + + return pAnimating; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::InputCreateLeftHandGun( inputdata_t &inputdata ) +{ + CreateLeftHandGun(); +} +#endif #endif float g_lateralBob; diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h index 06b71663fba..8468bba83b2 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h @@ -53,30 +53,52 @@ class CBaseHLCombatWeapon : public CBaseCombatWeapon virtual void ItemHolsterFrame( void ); -#if defined(EZ2) && defined(GAME_DLL) - virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); - virtual void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) {} - virtual Activity ActivityOverride( Activity baseAct, bool *pRequired ); +#ifdef EZ2 + // + // Dual wielding + // + virtual const char *GetViewModel( int viewmodelindex = 0 ) const; + + virtual CHudTexture const *GetSpriteActive( void ) const; + virtual CHudTexture const *GetSpriteInactive( void ) const; - // Dual wielding stubs (used for the Combine assassin) virtual bool CanDualWield() const { return false; } - virtual CBaseAnimating *GetLeftHandGun() const { return NULL; } - virtual void SetLeftHandGun( CBaseAnimating *pGun ) {} + bool IsDualWielding() const { return m_hLeftHandGun != NULL; } + + CBaseAnimating *GetLeftHandGun() const { return m_hLeftHandGun; } + void SetLeftHandGun( CBaseAnimating *pGun ) { m_hLeftHandGun = pGun; } int GetMaxClip1( void ) const { - return (GetLeftHandGun() != NULL) ? BaseClass::GetMaxClip1() * 2 : BaseClass::GetMaxClip1(); + return IsDualWielding() ? BaseClass::GetMaxClip1() * 2 : BaseClass::GetMaxClip1(); } int GetDefaultClip1( void ) const { - return (GetLeftHandGun() != NULL) ? BaseClass::GetDefaultClip1() * 2 : BaseClass::GetDefaultClip1(); + return IsDualWielding() ? BaseClass::GetDefaultClip1() * 2 : BaseClass::GetDefaultClip1(); } +#ifdef GAME_DLL + virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + virtual void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) {} + virtual Activity ActivityOverride( Activity baseAct, bool *pRequired ); + + virtual void Drop( const Vector &vecVelocity ); + + CBaseAnimating *CreateLeftHandGun(); + void InputCreateLeftHandGun( inputdata_t &inputdata ); + +protected: + + CNetworkHandle( CBaseAnimating, m_hLeftHandGun ); + private: static acttable_t m_dual_acttable[]; public: +#else + CHandle m_hLeftHandGun; +#endif #endif int m_iPrimaryAttacks; // # of primary attacks performed with this weapon diff --git a/sp/src/game/shared/weapon_parse.cpp b/sp/src/game/shared/weapon_parse.cpp index e70e51187cb..182e64dc76a 100644 --- a/sp/src/game/shared/weapon_parse.cpp +++ b/sp/src/game/shared/weapon_parse.cpp @@ -528,6 +528,9 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam #ifdef EZ2 m_bAlwaysFirstDraw = (pKeyValuesData->GetInt( "AlwaysFirstDraw", 0 ) != 0) ? true : false; m_bPreventPlayerSwap = (pKeyValuesData->GetInt( "PreventPlayerSwap", 0 ) != 0) ? true : false; + + Q_strncpy( szWorldModelDual, pKeyValuesData->GetString( "playermodel_dual" ), MAX_WEAPON_STRING ); + Q_strncpy( szViewModelDual, pKeyValuesData->GetString( "viewmodel_dual" ), MAX_WEAPON_STRING ); #endif #ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. diff --git a/sp/src/game/shared/weapon_parse.h b/sp/src/game/shared/weapon_parse.h index 5ae75f87dfb..71b1c92c05f 100644 --- a/sp/src/game/shared/weapon_parse.h +++ b/sp/src/game/shared/weapon_parse.h @@ -149,6 +149,9 @@ class FileWeaponInfo_t #ifdef EZ2 bool m_bAlwaysFirstDraw; // This weapon defaults to playing the first draw animation, even if dropped by an enemy bool m_bPreventPlayerSwap; // If the player is holding another weapon in the same slot as this weapon, prevent picking up this weapon + + char szWorldModelDual[MAX_WEAPON_STRING]; // World model when dual wielding + char szViewModelDual[MAX_WEAPON_STRING]; // View model when dual wielding #endif // CLIENT DLL @@ -163,6 +166,11 @@ class FileWeaponInfo_t CHudTexture *iconZoomedCrosshair; CHudTexture *iconZoomedAutoaim; CHudTexture *iconSmall; +#ifdef EZ2 + // For dual wielding + CHudTexture *iconActiveDual; + CHudTexture *iconInactiveDual; +#endif // TF2 specific bool bShowUsageHint; // if true, then when you receive the weapon, show a hint about it From b547554782b6a71c6c6f02f18f378afe010cc8d7 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 8 Jan 2025 11:53:21 -0600 Subject: [PATCH 077/103] Alternate infinite sprint which doesn't disable other aux power devices --- sp/src/game/server/hl2/hl2_player.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index 8e41f21fab2..e58b181ffc8 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -147,7 +147,15 @@ ConVar autoaim_unlock_target( "autoaim_unlock_target", "0.8666" ); ConVar sv_stickysprint("sv_stickysprint", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX); #ifdef EZ2 +enum +{ + INFINITE_SPRINT_OFF, + INFINITE_SPRINT_DEFAULT, // Aux power itself is infinite + INFINITE_SPRINT_TRUE, // Only sprinting is affected, other suit devices continue to drain power +}; + ConVar sv_infinite_sprint_power( "sv_infinite_sprint_power", "1", FCVAR_CHEAT ); + ConVar sv_infinite_flashlight_power( "sv_infinite_flashlight_power", "0", FCVAR_CHEAT ); ConVar sv_player_death_smell( "sv_player_death_smell", "1", FCVAR_REPLICATED ); ConVar sv_player_kick_attack_enabled( "sv_player_kick_attack_enabled", "1", FCVAR_REPLICATED ); @@ -1760,7 +1768,12 @@ void CHL2_Player::StartAutoSprint() //----------------------------------------------------------------------------- void CHL2_Player::StartSprinting( void ) { +#ifdef EZ2 + // Ignore when infinite sprint is active + if( sv_infinite_sprint_power.GetInt() != INFINITE_SPRINT_TRUE && m_HL2Local.m_flSuitPower < 10 ) +#else if( m_HL2Local.m_flSuitPower < 10 ) +#endif { // Don't sprint unless there's a reasonable // amount of suit power. @@ -1775,7 +1788,12 @@ void CHL2_Player::StartSprinting( void ) return; } +#ifdef EZ2 + // "True" infinite sprinting: Aux power continues to drain, but sprinting device is not added + if( sv_infinite_sprint_power.GetInt() != INFINITE_SPRINT_TRUE && !SuitPower_AddDevice( SuitDeviceSprint ) ) +#else if( !SuitPower_AddDevice( SuitDeviceSprint ) ) +#endif return; CPASAttenuationFilter filter( this ); @@ -2659,7 +2677,7 @@ bool CHL2_Player::SuitPower_Drain( float flPower ) #ifdef EZ2 // Sprint cheat on? // Why is this separate from sv_infinite_aux_power? So that we can toggle sprint and flashlight separately. - if (sv_infinite_sprint_power.GetBool()) + if (sv_infinite_sprint_power.GetInt() == INFINITE_SPRINT_DEFAULT) return true; #endif From de3a505c77adbf76b66ec7c9d96ddeeeec788817 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 8 Jan 2025 11:57:53 -0600 Subject: [PATCH 078/103] Option to allow citizen suppressive fire to respect enemy memory --- sp/src/game/server/hl2/npc_citizen17.cpp | 33 ++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index 4c0e0a2af2c..81f21dadf46 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -136,6 +136,7 @@ ConVar ai_min_suppression_distance("ai_min_suppression_distance", "256", FCVAR_R ConVar ai_suppression_distance_ratio("ai_suppression_distance_ratio", "0.5", FCVAR_REPLICATED); // 1upD - What percent of distance to suppression target must be covered ConVar ai_willpower_translate_schedules("ai_willpower_translate_schedules", "1", FCVAR_REPLICATED); // 1upD - Should new EZ2 schedules be used? ConVar ai_suppression_shoot_props( "ai_suppression_shoot_props", "1", FCVAR_REPLICATED ); // 1upD - Should rebels with suppressing fire shoot at props? +ConVar ai_suppression_use_enemy_memory( "ai_suppression_use_enemy_memory", "0", FCVAR_REPLICATED ); // Blixibon - Allows suppression AI to respect enemy memory #ifdef EZ1 // Disable this in EZ1 ConVar npc_citizen_gib( "npc_citizen_gib", "0" ); @@ -2566,6 +2567,23 @@ int CNPC_Citizen::TranslateSuppressingFireSchedule(int scheduleType) return scheduleType; } + if (ai_suppression_use_enemy_memory.GetBool() && GetSquad() && GetSquad()->NumMembers() > 1) + { + // If the enemy has eluded any of our squadmates, then someone must've peeked ahead to establish LOF and found no one there + // This means they're not at the position we think they're in + AISquadIter_t iter; + for ( CAI_BaseNPC *pSquadmate = m_pSquad ? m_pSquad->GetFirstMember(&iter) : this; pSquadmate; pSquadmate = m_pSquad ? m_pSquad->GetNextMember(&iter) : NULL ) + { + AI_EnemyInfo_t *pMemory = pSquadmate->GetEnemies()->Find( pEnemy ); + if( pMemory && pMemory->bEludedMe ) + { + if(ai_debug_rebel_suppressing_fire.GetBool()) + DevMsg("NPC_Citizen::TranslateSuppressingFireSchedule: %s not using suppressive fire due to enemy eluded by squad\n", GetDebugName()); + return scheduleType; + } + } + } + #ifdef EZ1 if(m_flLastAttackTime == 0) { @@ -2613,7 +2631,13 @@ bool CNPC_Citizen::FindDecoyObject(void) CBaseEntity *pCurrent; int count; int i; - Vector vecTarget = GetEnemy()->WorldSpaceCenter(); + + Vector vecTarget; + if (ai_suppression_use_enemy_memory.GetBool()) + vecTarget = GetEnemyLKP() + (GetEnemy()->WorldSpaceCenter() - GetEnemy()->GetAbsOrigin()); // Worldspace center relative to LKP + else + vecTarget = GetEnemy()->WorldSpaceCenter(); + Vector vecDelta; for (i = 0; i < CITIZEN_NUM_DECOYS; i++) @@ -2754,7 +2778,12 @@ bool CNPC_Citizen::FindEnemyCoverTarget(void) trace_t tr; Vector startpos = this->Weapon_ShootPosition(); - Vector targetpos = GetEnemy()->EyePosition(); + + Vector targetpos; + if (ai_suppression_use_enemy_memory.GetBool()) + targetpos = GetEnemyLKP() + GetEnemy()->GetViewOffset(); // Eye position relative to LKP + else + targetpos = GetEnemy()->EyePosition(); float distance = UTIL_DistApprox(startpos, targetpos); if (ai_debug_rebel_suppressing_fire.GetBool() && distance <= ai_min_suppression_distance.GetFloat()) From acfed31a3bd0c26fa81fbc40008d9601ec24a65a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Wed, 8 Jan 2025 11:58:38 -0600 Subject: [PATCH 079/103] Fix citizens continuing to aim at hints while investigating sounds --- sp/src/game/server/hl2/npc_citizen17.cpp | 11 +++++++++++ sp/src/game/server/hl2/npc_citizen17.h | 1 + 2 files changed, 12 insertions(+) diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index 81f21dadf46..4aa50c9e2cc 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -2614,6 +2614,17 @@ int CNPC_Citizen::SelectRangeAttack2Schedule() return BaseClass::SelectRangeAttack2Schedule(); } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CNPC_Citizen::OnStartSchedule( int schedule ) +{ + // Stop aiming our gun at hints if we're investigating a sound + if ( schedule == SCHED_INVESTIGATE_SOUND ) + StopAiming( "Investigating sound" ); + + BaseClass::OnStartSchedule( schedule ); +} + #define CITIZEN_DECOY_RADIUS 128.0f #define CITIZEN_NUM_DECOYS 10 #define CITIZEN_DECOY_MAX_MASS 200.0f diff --git a/sp/src/game/server/hl2/npc_citizen17.h b/sp/src/game/server/hl2/npc_citizen17.h index 8080e633141..5c511822935 100644 --- a/sp/src/game/server/hl2/npc_citizen17.h +++ b/sp/src/game/server/hl2/npc_citizen17.h @@ -232,6 +232,7 @@ class CNPC_Citizen : public CNPC_PlayerCompanion int TranslateWillpowerSchedule(int scheduleType); int TranslateSuppressingFireSchedule(int scheduleType); int SelectRangeAttack2Schedule(); + void OnStartSchedule( int scheduleType ); bool FindDecoyObject(void); bool FindEnemyCoverTarget(void); void AimGun(); From de2e26a4c5934ba7d9963af9caab91bc1dc7cfe3 Mon Sep 17 00:00:00 2001 From: Derek <1upderek@gmail.com> Date: Mon, 20 Jan 2025 15:45:00 -0500 Subject: [PATCH 080/103] Apply suggestions from code review Co-authored-by: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> --- sp/src/game/server/ez2/npc_wilson.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sp/src/game/server/ez2/npc_wilson.cpp b/sp/src/game/server/ez2/npc_wilson.cpp index d5102153b87..6f4d7471ade 100644 --- a/sp/src/game/server/ez2/npc_wilson.cpp +++ b/sp/src/game/server/ez2/npc_wilson.cpp @@ -2300,8 +2300,11 @@ bool CNPC_Wilson::HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vec return BaseClass::HeadTargetPosOverride( vecTargetPos, vecDir, flDist ); // Translate this target position from the camera to Wilson's actual entity - matrix3x4_t matWorldToTarget, matCameraToTarget, matTargetToCamera; - AngleIMatrix( QAngle(), vecTargetPos, matWorldToTarget ); + matrix3x4_t matCameraToTarget, matTargetToCamera; + matrix3x4_t matWorldToTarget( + 1.0f, 0.0f, 0.0f, -vecTargetPos.x, + 0.0f, 1.0f, 0.0f, -vecTargetPos.y, + 0.0f, 0.0f, 1.0f, -vecTargetPos.z ); ConcatTransforms( matWorldToTarget, pNearestCamera->EntityToWorldTransform(), matCameraToTarget ); MatrixInvert( matCameraToTarget, matTargetToCamera ); @@ -2312,8 +2315,7 @@ bool CNPC_Wilson::HeadTargetPosOverride( const Vector &vecTargetPos, Vector &vec ConcatTransforms( matWilson, matTargetToCamera, matTargetToWilson ); Vector vecOrigin; - QAngle angAngles; - MatrixAngles( matTargetToWilson, angAngles, vecOrigin ); + MatrixGetColumn( matTargetToWilson, 3, vecOrigin ); vecDir = vecOrigin - EyePosition(); flDist = VectorNormalize( vecDir ); From 06e41e2091ff6f75387e9fe41793afd1d1e13892 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 11:17:00 -0600 Subject: [PATCH 081/103] OICW dynamic RT scope support and material proxy --- sp/src/game/client/client_ez2.vpc | 1 + sp/src/game/client/ez2/c_weapon_oicw.cpp | 255 ++++++++++++++++++ .../game/client/hl2/c_weapon__stubs_hl2.cpp | 1 - sp/src/game/client/rendertexture.cpp | 21 ++ sp/src/game/client/rendertexture.h | 4 + sp/src/game/client/viewrender.cpp | 73 +++++ sp/src/game/client/viewrender.h | 3 + sp/src/game/server/ez2/weapon_oicw.cpp | 77 +++++- sp/src/game/server/hl2/hl2_player.cpp | 8 + .../game/shared/basecombatweapon_shared.cpp | 12 + sp/src/game/shared/basecombatweapon_shared.h | 5 + sp/src/game/shared/baseviewmodel_shared.cpp | 9 + sp/src/game/shared/weapon_parse.cpp | 3 + sp/src/game/shared/weapon_parse.h | 3 + 14 files changed, 467 insertions(+), 8 deletions(-) create mode 100644 sp/src/game/client/ez2/c_weapon_oicw.cpp diff --git a/sp/src/game/client/client_ez2.vpc b/sp/src/game/client/client_ez2.vpc index b3be97355ba..71ab3ea6091 100644 --- a/sp/src/game/client/client_ez2.vpc +++ b/sp/src/game/client/client_ez2.vpc @@ -43,6 +43,7 @@ $Project "Client (Entropy Zero 2)" $File "ez2\c_npc_crabsynth.h" $File "ez2\c_npc_wilson.cpp" $File "ez2\c_npc_wilson.h" + $File "ez2\c_weapon_oicw.cpp" $File "ez2\c_weapon_pulse_pistol.cpp" $File "ez2\zombiegooproxy.cpp" $File "ez2\hud_slamstatus.cpp" diff --git a/sp/src/game/client/ez2/c_weapon_oicw.cpp b/sp/src/game/client/ez2/c_weapon_oicw.cpp new file mode 100644 index 00000000000..439dfc1fe05 --- /dev/null +++ b/sp/src/game/client/ez2/c_weapon_oicw.cpp @@ -0,0 +1,255 @@ +//=============================================================================// +// +// Purpose: OICW scope effects. +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" +#include "c_basehlcombatweapon.h" +#include "tier0/icommandline.h" +#include "proxyentity.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialvar.h" +#include "baseviewmodel_shared.h" +#include "hud_crosshair.h" + +class C_WeaponOICW : public C_BaseHLCombatWeapon +{ +public: + DECLARE_CLASS( C_WeaponOICW, C_BaseHLCombatWeapon ); + DECLARE_CLIENTCLASS(); + DECLARE_PREDICTABLE(); + + C_WeaponOICW(); + ~C_WeaponOICW(); + + bool IsDynamicScopeZoomed( void ) const { return m_bZoomed; } + + virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ); + virtual float CalcViewmodelBob( void ); + void DrawCrosshair( void ); + + bool m_bZoomed; + bool m_bZoomTransition; +}; + +LINK_ENTITY_TO_CLASS( weapon_oicw, C_WeaponOICW ); + +IMPLEMENT_CLIENTCLASS_DT( C_WeaponOICW, DT_WeaponOICW, CWeaponOICW ) + RecvPropBool( RECVINFO( m_bZoomed ) ), + RecvPropBool( RECVINFO( m_bZoomTransition ) ), +END_RECV_TABLE() + +BEGIN_PREDICTION_DATA( C_WeaponOICW ) +END_PREDICTION_DATA() + +C_WeaponOICW::C_WeaponOICW() +{ +} + +C_WeaponOICW::~C_WeaponOICW() +{ +} + +void C_WeaponOICW::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles ) +{ + //if (m_bZoomed) + // return; + + BaseClass::AddViewmodelBob( viewmodel, origin, angles ); +} + +extern float g_lateralBob; +extern float g_verticalBob; + +float C_WeaponOICW::CalcViewmodelBob( void ) +{ + BaseClass::CalcViewmodelBob(); + + // Reduce bob while zoomed + if (m_bZoomed) + { + g_lateralBob *= 0.05f; + g_verticalBob *= 0.1f; + } + + // Return value not used (see base class) + return 0.0f; +} + +void C_WeaponOICW::DrawCrosshair( void ) +{ + // Don't draw crosshair when zoomed + if (m_bZoomed) + { + CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair ); + if ( !crosshair ) + return; + + crosshair->ResetCrosshair(); + return; + } + + BaseClass::DrawCrosshair(); +} + +//----------------------------------------------------------------------------- +// Apply effects when the pulse pistol is charging +//----------------------------------------------------------------------------- +class COICWScopeMaterialProxy : public CEntityMaterialProxy +{ +public: + COICWScopeMaterialProxy() { m_ZoomVar = NULL; } + virtual ~COICWScopeMaterialProxy() {} + virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues ); + virtual void OnBind( C_BaseEntity *pEntity ); + virtual IMaterial *GetMaterial(); + +private: + IMaterialVar *m_ZoomVar; +}; + +bool COICWScopeMaterialProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues ) +{ + bool foundAnyVar = false; + bool foundVar; + + const char *pszChargeVar = pKeyValues->GetString( "outputZoom" ); + m_ZoomVar = pMaterial->FindVar( pszChargeVar, &foundVar, true ); + if (foundVar) + foundAnyVar = true; + + return foundAnyVar; +} + +void COICWScopeMaterialProxy::OnBind( C_BaseEntity *pEnt ) +{ + if (!m_ZoomVar) + return; + + // Need both the weapon and the viewmodel + C_BaseViewModel *pVM = NULL; + C_WeaponOICW *pOICW = NULL; + if ( pEnt->IsBaseCombatWeapon() ) + { + pOICW = assert_cast( pEnt ); + if (C_BasePlayer *pPlayer = ToBasePlayer( pOICW->GetOwner() )) + pVM = pPlayer->GetViewModel( pOICW->m_nViewModelIndex ); + } + else + { + pVM = dynamic_cast( pEnt ); + if (pVM) + pOICW = assert_cast( pVM->GetOwningWeapon() ); + } + + if (!pOICW || !pVM) + return; + + if ( pOICW ) + { + if (pOICW->IsDynamicScopeZoomed()) + { + // Zoomed + if (pOICW->m_bZoomTransition) + { + // Transition to zoom using viewmodel cycle + m_ZoomVar->SetFloatValue( pVM->GetCycle() ); + } + else + m_ZoomVar->SetFloatValue( 1.0f ); + } + else + { + // Not zoomed + if (pOICW->m_bZoomTransition) + { + // Transition from zoom using viewmodel cycle + m_ZoomVar->SetFloatValue( 1.0f - pVM->GetCycle() ); + } + else + m_ZoomVar->SetFloatValue( 0.0f ); + } + } + else + { + m_ZoomVar->SetFloatValue( 0.0f ); + } +} + +IMaterial *COICWScopeMaterialProxy::GetMaterial() +{ + if ( !m_ZoomVar ) + return NULL; + + return m_ZoomVar->GetOwningMaterial(); +} + +EXPOSE_INTERFACE( COICWScopeMaterialProxy, IMaterialProxy, "OICWScope" IMATERIAL_PROXY_INTERFACE_VERSION ); + +//----------------------------------------------------------------------------- +// Purpose: System for managing the OICW scope render target +// +// TODO: More formal system for managing weapon scope RTs? I don't know how this +// is "supposed" to work relative to games which actually use these +// Maybe something like what's described in baseclientrendertargets.cpp's header? +//----------------------------------------------------------------------------- +class CWeaponOICWScopeSystem : public CAutoGameSystem +{ +public: + CWeaponOICWScopeSystem() : CAutoGameSystem( "CWeaponOICWScopeSystem" ) + { + } + + virtual bool Init() + { + InitializeRTs(); + return true; + } + + //----------------------------------------------------------------------------- + // Initialize custom RT textures if necessary + //----------------------------------------------------------------------------- + void InitializeRTs() + { + if (!m_bInitializedRTs) + { + // Cancel if we shouldn't run with the scope RT + int nNoScopeRT = CommandLine()->ParmValue( "-no_scope_rt", 0 ); + if (nNoScopeRT == 1) + return; + + materials->BeginRenderTargetAllocation(); + + m_ScopeTexture.Init( g_pMaterialSystem->CreateNamedRenderTargetTextureEx2( + "_rt_Scope", + 512, 512, RT_SIZE_OFFSCREEN, + g_pMaterialSystem->GetBackBufferFormat(), + MATERIAL_RT_DEPTH_SHARED, + TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT, + CREATERENDERTARGETFLAGS_HDR ) ); + + materials->EndRenderTargetAllocation(); + + m_bInitializedRTs = true; + } + } + + void Shutdown() + { + if (m_bInitializedRTs) + { + m_ScopeTexture.Shutdown(); + m_bInitializedRTs = false; + } + } + +private: + + CTextureReference m_ScopeTexture; + bool m_bInitializedRTs = false; +}; + +CWeaponOICWScopeSystem m_OICWScopeSystem; diff --git a/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp b/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp index a6581c958a7..0671bbce4ef 100644 --- a/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp +++ b/sp/src/game/client/hl2/c_weapon__stubs_hl2.cpp @@ -59,7 +59,6 @@ STUB_WEAPON_CLASS( weapon_arbeit_clipboard, WeaponArbeitClipboard, C_WeaponCitiz STUB_WEAPON_CLASS( weapon_flechette_shotgun, WeaponFlechetteShotgun, C_BaseHLCombatWeapon ); STUB_WEAPON_CLASS( weapon_stasis_grenade, WeaponStasisGrenade, C_BaseHLCombatWeapon ); -STUB_WEAPON_CLASS( weapon_oicw, WeaponOICW, C_HLSelectFireMachineGun ); #endif diff --git a/sp/src/game/client/rendertexture.cpp b/sp/src/game/client/rendertexture.cpp index 78780e93022..758f505f5c5 100644 --- a/sp/src/game/client/rendertexture.cpp +++ b/sp/src/game/client/rendertexture.cpp @@ -80,6 +80,23 @@ ITexture *GetCameraTexture( void ) return s_pCameraTexture; } +#ifdef EZ2 +//============================================================================= +// Dynamic Scope Texture +//============================================================================= +static CTextureReference s_pScopeTexture; +ITexture *GetScopeTexture( void ) +{ + if ( !s_pScopeTexture ) + { + s_pScopeTexture.Init( materials->FindTexture( "_rt_Scope", TEXTURE_GROUP_RENDER_TARGET ) ); + Assert( !IsErrorTexture( s_pScopeTexture ) ); + AddReleaseFunc(); + } + return s_pScopeTexture; +} +#endif + //============================================================================= // Full Frame Depth Texture //============================================================================= @@ -247,6 +264,10 @@ void ReleaseRenderTargets( void ) { s_pPowerOfTwoFrameBufferTexture.Shutdown(); s_pCameraTexture.Shutdown(); +#ifdef EZ2 + //Release the scope render target too + s_pScopeTexture.Shutdown(); +#endif s_pWaterReflectionTexture.Shutdown(); s_pWaterRefractionTexture.Shutdown(); s_pQuarterSizedFB0.Shutdown(); diff --git a/sp/src/game/client/rendertexture.h b/sp/src/game/client/rendertexture.h index 2afdc6aeb86..39f57a175d9 100644 --- a/sp/src/game/client/rendertexture.h +++ b/sp/src/game/client/rendertexture.h @@ -15,6 +15,10 @@ ITexture *GetWaterReflectionTexture( void ); ITexture *GetWaterRefractionTexture( void ); ITexture *GetFullscreenTexture( void ); ITexture *GetCameraTexture( void ); +#ifdef EZ2 +//Dynamic Scope Texture +ITexture *GetScopeTexture(); +#endif ITexture *GetFullFrameDepthTexture( void ); // SmallBufferHDRx=r16g16b16a16 quarter-sized texture diff --git a/sp/src/game/client/viewrender.cpp b/sp/src/game/client/viewrender.cpp index 6bcff8da90e..8c26c53b794 100644 --- a/sp/src/game/client/viewrender.cpp +++ b/sp/src/game/client/viewrender.cpp @@ -2291,6 +2291,13 @@ void CViewRender::RenderView( const CViewSetup &view, int nClearFlags, int whatT PerformScreenSpaceEffects( 0, 0, view.width, view.height ); +#ifdef EZ2 + if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 70 ) + { + DrawScope( view ); + } +#endif + if ( g_pMaterialSystemHardwareConfig->GetHDRType() == HDR_TYPE_INTEGER ) { pRenderContext.GetFrom( materials ); @@ -3751,6 +3758,72 @@ void CViewRender::DrawMonitors( const CViewSetup &cameraView ) #endif // USE_MONITORS } +#ifdef EZ2 +void CViewRender::DrawScope( const CViewSetup &viewSet ) +{ + C_BasePlayer *localPlayer = C_BasePlayer::GetLocalPlayer(); + + if(!localPlayer) + return; + + if( !localPlayer->GetActiveWeapon() ) + return; + + if( !localPlayer->GetActiveWeapon()->GetViewModel() ) + return; + + if ( localPlayer->GetActiveWeapon()->GetDynamicScopeFOV() == 0.0f ) + return; + + //Copy our current View. + CViewSetup scopeView = viewSet; + + //Get our camera render target. + ITexture *pRenderTarget = GetScopeTexture(); + + if( pRenderTarget == NULL ) + return; + + //Our view information, Origin, View Direction, window size + // location on material, and visual ratios. + scopeView.width = pRenderTarget->GetActualWidth(); + scopeView.height = pRenderTarget->GetActualHeight(); + scopeView.x = 0; + scopeView.y = 0; + scopeView.fov = localPlayer->GetActiveWeapon()->GetDynamicScopeFOV(); + scopeView.m_bOrtho = false; + + scopeView.m_flAspectRatio = 1.0f; + + if (/*!localPlayer->GetActiveWeapon()->IsDynamicScopeZoomed() &&*/ localPlayer->GetViewModel()) + { + int nAttach = localPlayer->GetViewModel()->LookupAttachment( "scope_cam" ); + if (nAttach != -1) + { + // Set the view to the attachment + localPlayer->GetViewModel()->GetAttachment( nAttach, scopeView.origin, scopeView.angles ); + } + } + + //Set the view up and output the scene to our RenderTarget (Scope Material). + Frustum frustum; + render->Push3DView( scopeView, VIEW_CLEAR_DEPTH | VIEW_CLEAR_COLOR, pRenderTarget, (VPlane *)frustum ); + + SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE; + int ClearFlags = 0; + CSkyboxView *pSkyView = new CSkyboxView( this ); + if( pSkyView->Setup( scopeView, &ClearFlags, &nSkyboxVisible ) != false ) + AddViewToScene( pSkyView ); + SafeRelease( pSkyView ); + + ViewDrawScene( false, SKYBOX_3DSKYBOX_VISIBLE, scopeView, VIEW_CLEAR_DEPTH, VIEW_INTRO_CAMERA ); // Using VIEW_INTRO_CAMERA to indicate this is a perspective view + + //GetClientModeNormal()->DoPostScreenSpaceEffects( &scopeView ); + + render->PopView( m_Frustum ); +} +#endif + //----------------------------------------------------------------------------- // diff --git a/sp/src/game/client/viewrender.h b/sp/src/game/client/viewrender.h index 3ba6cd9fc43..eb2e63a752f 100644 --- a/sp/src/game/client/viewrender.h +++ b/sp/src/game/client/viewrender.h @@ -447,6 +447,9 @@ class CViewRender : public IViewRender, void ViewDrawScene( bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxVisible, const CViewSetup &view, int nClearFlags, view_id_t viewID, bool bDrawViewModel = false, int baseDrawFlags = 0, ViewCustomVisibility_t *pCustomVisibility = NULL ); void DrawMonitors( const CViewSetup &cameraView ); +#ifdef EZ2 + void DrawScope( const CViewSetup &viewSet ); +#endif bool DrawOneMonitor( ITexture *pRenderTarget, int cameraNum, C_PointCamera *pCameraEnt, const CViewSetup &cameraView, C_BasePlayer *localPlayer, int x, int y, int width, int height ); diff --git a/sp/src/game/server/ez2/weapon_oicw.cpp b/sp/src/game/server/ez2/weapon_oicw.cpp index 9ac7dfb3717..d55f3355160 100644 --- a/sp/src/game/server/ez2/weapon_oicw.cpp +++ b/sp/src/game/server/ez2/weapon_oicw.cpp @@ -17,6 +17,7 @@ #include "soundent.h" #include "rumble_shared.h" #include "gamestats.h" +#include "hl2_player.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -49,11 +50,13 @@ class CWeaponOICW : public CHLSelectFireMachineGun int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } int WeaponRangeAttack2Condition( float flDot, float flDist ); Activity GetPrimaryAttackActivity( void ); + bool SendWeaponAnim( int iActivity ); + void WeaponIdle( void ); virtual const Vector& GetBulletSpread( void ) { static const Vector cone = VECTOR_CONE_2DEGREES; - static const Vector zoomedCone = VECTOR_CONE_1DEGREES; + static const Vector zoomedCone = VECTOR_CONE_1DEGREES * 0.25f; if (GetOwner() && GetOwner()->IsNPC()) { @@ -64,6 +67,8 @@ class CWeaponOICW : public CHLSelectFireMachineGun return m_bZoomed ? zoomedCone : cone; } + bool IsDynamicScopeZoomed( void ) const { return m_bZoomed; } + const WeaponProficiencyInfo_t *GetProficiencyValues(); void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); @@ -76,10 +81,13 @@ class CWeaponOICW : public CHLSelectFireMachineGun Vector m_vecTossVelocity; float m_flNextGrenadeCheck; - bool m_bZoomed; + CNetworkVar( bool, m_bZoomed ); + CNetworkVar( bool, m_bZoomTransition ); }; IMPLEMENT_SERVERCLASS_ST(CWeaponOICW, DT_WeaponOICW) + SendPropBool( SENDINFO( m_bZoomed ) ), + SendPropBool( SENDINFO( m_bZoomTransition ) ), END_SEND_TABLE() //LINK_ENTITY_TO_CLASS( weapon_ar1, CWeaponOICW ); @@ -346,6 +354,9 @@ void CWeaponOICW::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatChar //----------------------------------------------------------------------------- Activity CWeaponOICW::GetPrimaryAttackActivity( void ) { + if ( m_bZoomed ) + return ACT_VM_PRIMARYATTACK_SPECIAL; + if ( m_nShotsFired < 2 ) return ACT_VM_PRIMARYATTACK; @@ -358,6 +369,35 @@ Activity CWeaponOICW::GetPrimaryAttackActivity( void ) return ACT_VM_RECOIL3; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponOICW::SendWeaponAnim( int iActivity ) +{ + // Finished transitioning to or from zoom + if ( m_bZoomTransition && iActivity == GetIdealActivity() ) + m_bZoomTransition = false; + + return BaseClass::SendWeaponAnim( iActivity ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponOICW::WeaponIdle( void ) +{ + if ( m_bZoomed ) + { + // Override base weapon idle when zoomed + if ( HasWeaponIdleTimeElapsed() ) + SendWeaponAnim( ACT_VM_IDLE_SPECIAL ); + + return; + } + + return BaseClass::WeaponIdle(); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -366,13 +406,28 @@ void CWeaponOICW::ItemPostFrame( void ) CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if ( pOwner ) { - if ( pOwner->m_nButtons & IN_ZOOM ) + CHL2_Player *pHL2Player = static_cast(GetOwner()); + if ( pHL2Player->IsZooming() ) { - m_bZoomed = true; + if (!m_bZoomed) + { + // Reset to new idle sequence + SetWeaponIdleTime( gpGlobals->curtime ); + + m_bZoomed = true; + m_bZoomTransition = true; + } } else { - m_bZoomed = false; + if (m_bZoomed) + { + // Reset to new idle sequence + SetWeaponIdleTime( gpGlobals->curtime ); + + m_bZoomed = false; + m_bZoomTransition = true; + } } } @@ -395,6 +450,14 @@ bool CWeaponOICW::Reload( void ) m_flNextSecondaryAttack = GetOwner()->m_flNextAttack = fCacheTime; WeaponSound( RELOAD ); + + if (m_bZoomed) + { + // Make the player stop zooming + CHL2_Player *pHL2Player = static_cast(GetOwner()); + if (pHL2Player->IsZooming()) + pHL2Player->StopZooming(); + } } return fRet; @@ -410,8 +473,8 @@ void CWeaponOICW::AddViewKick( void ) #define SLIDE_LIMIT 1.5f //Seconds #define ZOOMED_EASY_DAMPEN 0.5f - #define ZOOMED_MAX_VERTICAL_KICK 4.0f //Degrees - #define ZOOMED_SLIDE_LIMIT 3.5f //Seconds + #define ZOOMED_MAX_VERTICAL_KICK 3.5f //Degrees + #define ZOOMED_SLIDE_LIMIT 8.0f //Seconds //Get the view kick CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index b9c844f815a..31d91d66237 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -1899,6 +1899,14 @@ void CHL2_Player::StartZooming( void ) { #ifdef MAPBASE int iFOV = GetPlayerProxy() ? GetPlayerProxy()->m_SuitZoomFOV : 25; + +#ifdef EZ2 + if (GetActiveWeapon() && GetActiveWeapon()->GetDynamicScopeSuitFOV() != 0.0f) + { + // The weapon has its own suit zoom FOV + iFOV = GetActiveWeapon()->GetDynamicScopeSuitFOV(); + } +#endif #else int iFOV = 25; #endif diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 8a60ed98986..7eebd09daae 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -525,6 +525,18 @@ bool CBaseCombatWeapon::UsesHands() const } #endif +#ifdef EZ2 +float CBaseCombatWeapon::GetDynamicScopeFOV() const +{ + return GetWpnData().m_flDynamicScopeFOV; +} + +float CBaseCombatWeapon::GetDynamicScopeSuitFOV() const +{ + return GetWpnData().m_flDynamicScopeSuitFOV; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index f807d6e3119..e5e083ce2d3 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -427,6 +427,11 @@ class CBaseCombatWeapon : public BASECOMBATWEAPON_DERIVED_FROM virtual const char *GetDroppedModel( void ) const; bool UsesHands( void ) const; #endif +#ifdef EZ2 + float GetDynamicScopeFOV( void ) const; + float GetDynamicScopeSuitFOV( void ) const; + virtual bool IsDynamicScopeZoomed( void ) const { return false; } +#endif // derive this function if you mod uses encrypted weapon info files virtual const unsigned char *GetEncryptionKey( void ); diff --git a/sp/src/game/shared/baseviewmodel_shared.cpp b/sp/src/game/shared/baseviewmodel_shared.cpp index 5f9c0f10c64..d6124263572 100644 --- a/sp/src/game/shared/baseviewmodel_shared.cpp +++ b/sp/src/game/shared/baseviewmodel_shared.cpp @@ -581,6 +581,15 @@ void CBaseViewModel::CalcViewModelLag( Vector& origin, QAngle& angles, QAngle& o angles = vOriginalAngles; } +#ifdef EZ2 + CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); + if (pWeapon && pWeapon->GetDynamicScopeFOV() != 0.0f && pWeapon->IsDynamicScopeZoomed()) + { + // Ignore the below + return; + } +#endif + //FIXME: These are the old settings that caused too many exposed polys on some models VectorMA( origin, -pitch * 0.035f, forward, origin ); VectorMA( origin, -pitch * 0.03f, right, origin ); diff --git a/sp/src/game/shared/weapon_parse.cpp b/sp/src/game/shared/weapon_parse.cpp index 182e64dc76a..cd1ab224015 100644 --- a/sp/src/game/shared/weapon_parse.cpp +++ b/sp/src/game/shared/weapon_parse.cpp @@ -531,6 +531,9 @@ void FileWeaponInfo_t::Parse( KeyValues *pKeyValuesData, const char *szWeaponNam Q_strncpy( szWorldModelDual, pKeyValuesData->GetString( "playermodel_dual" ), MAX_WEAPON_STRING ); Q_strncpy( szViewModelDual, pKeyValuesData->GetString( "viewmodel_dual" ), MAX_WEAPON_STRING ); + + m_flDynamicScopeFOV = pKeyValuesData->GetFloat( "dynamic_scope_fov", 0.0f ); + m_flDynamicScopeSuitFOV = pKeyValuesData->GetFloat( "dynamic_scope_suit_fov", 0.0f ); #endif #ifndef MAPBASE // Mapbase makes weapons in the same slot & position swap each other out, which is a feature mods can intentionally use. diff --git a/sp/src/game/shared/weapon_parse.h b/sp/src/game/shared/weapon_parse.h index 71b1c92c05f..42614614e16 100644 --- a/sp/src/game/shared/weapon_parse.h +++ b/sp/src/game/shared/weapon_parse.h @@ -152,6 +152,9 @@ class FileWeaponInfo_t char szWorldModelDual[MAX_WEAPON_STRING]; // World model when dual wielding char szViewModelDual[MAX_WEAPON_STRING]; // View model when dual wielding + + float m_flDynamicScopeFOV; // Dynamic scope FOV. 0 = No dynamic scope + float m_flDynamicScopeSuitFOV; // If overridden, makes the suit zoom use this FOV (complements dynamic scope) #endif // CLIENT DLL From 0c441f45124cadcb2a4806b75cf0f036faf09a53 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 11:45:51 -0600 Subject: [PATCH 082/103] Add husk responses for gifts + fix OnGiftAccept not firing --- sp/src/game/server/ez2/npc_husk_soldier.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sp/src/game/server/ez2/npc_husk_soldier.cpp b/sp/src/game/server/ez2/npc_husk_soldier.cpp index 9ff63f03fa5..391f880b6a7 100644 --- a/sp/src/game/server/ez2/npc_husk_soldier.cpp +++ b/sp/src/game/server/ez2/npc_husk_soldier.cpp @@ -555,6 +555,16 @@ void CNPC_HuskSoldier::StartPlayerGive( CBasePlayer *pPlayer ) // We like this person now AddPassiveTarget( pPlayer ); + + m_OnGiftAccept.FireOutput( GetTarget(), this ); + + AI_CriteriaSet modifiers; + modifiers.AppendCriteria( "gift", GetTarget()->GetClassname() ); + modifiers.AppendCriteria( "gift_name", STRING( GetTarget()->GetEntityName() ) ); + GetTarget()->AppendContextToCriteria( modifiers, "gift_" ); + + // TODO: Add to CAI_PlayerAlly concept manager? + SpeakIfAllowed( "TLK_GIFT_ACCEPT", modifiers, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); } //----------------------------------------------------------------------------- @@ -568,6 +578,15 @@ void CNPC_HuskSoldier::OnCantBeGivenObject( CBaseEntity *pItem ) SetSchedule( SCHED_HUSK_SOLDIER_REJECT_GIFT ); m_OnGiftReject.FireOutput( pItem, this ); + + AI_CriteriaSet modifiers; + + modifiers.AppendCriteria( "gift", pItem->GetClassname() ); + modifiers.AppendCriteria( "gift_name", STRING( pItem->GetEntityName() ) ); + pItem->AppendContextToCriteria( modifiers, "gift_" ); + + // TODO: Add to CAI_PlayerAlly concept manager? + SpeakIfAllowed( "TLK_GIFT_REJECT", modifiers, SENTENCE_PRIORITY_NORMAL, SENTENCE_CRITERIA_NORMAL ); } } From 604c46f8ea7202cd1e7e41ed6faeafc1183a0acc Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 12:22:51 -0600 Subject: [PATCH 083/103] Fix antlions not using interactions on commandable soldiers --- sp/src/game/server/hl2/npc_antlion.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sp/src/game/server/hl2/npc_antlion.cpp b/sp/src/game/server/hl2/npc_antlion.cpp index 6b32cad240f..d634e148c43 100644 --- a/sp/src/game/server/hl2/npc_antlion.cpp +++ b/sp/src/game/server/hl2/npc_antlion.cpp @@ -424,6 +424,8 @@ void CNPC_Antlion::Spawn( void ) sInteraction01.flMaxAngleDiff = 180.0f; // Initiate from any angle #ifdef EZ sInteraction01.iFlags |= SCNPC_FLAG_TEST_SQUADMATE_HEALTH; + sInteraction01.flHealthRatio = 0.25f; + sInteraction01.MiscCriteria = AllocPooledString( "their_health:<100" ); #endif ScriptedNPCInteraction_t sInteraction02; @@ -440,6 +442,8 @@ void CNPC_Antlion::Spawn( void ) sInteraction02.flMaxAngleDiff = 180.0f; // Initiate from any angle #ifdef EZ sInteraction02.iFlags |= SCNPC_FLAG_TEST_SQUADMATE_HEALTH; + sInteraction02.flHealthRatio = 0.25f; + sInteraction02.MiscCriteria = AllocPooledString( "their_health:<100" ); #endif AddScriptedNPCInteraction(&sInteraction01); AddScriptedNPCInteraction(&sInteraction02); From ac7b1758aadf151658b757337e8e7a1530e096ca Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 12:25:52 -0600 Subject: [PATCH 084/103] Vortigaunt standoff behavior + misc. AP vort fixes --- sp/src/game/server/ai_behavior_standoff.h | 4 + .../server/hl2/npc_vortigaunt_episodic.cpp | 86 ++++++++++++++++++- .../game/server/hl2/npc_vortigaunt_episodic.h | 26 ++++++ 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/ai_behavior_standoff.h b/sp/src/game/server/ai_behavior_standoff.h index a1a0ece77c5..271a905aeb6 100644 --- a/sp/src/game/server/ai_behavior_standoff.h +++ b/sp/src/game/server/ai_behavior_standoff.h @@ -163,6 +163,10 @@ class CAI_StandoffBehavior : public CAI_MappedActivityBehavior_Temporary // Don't do death poses while crouching bool ShouldPickADeathPose( void ) { return (GetPosture() != AIP_CROUCHING && GetPosture() != AIP_PEEKING) && BaseClass::ShouldPickADeathPose(); } #endif + +#ifdef EZ2 + const AI_StandoffParams_t &GetParams() { return m_params; } +#endif private: diff --git a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp index beb0c00962e..10441da6f06 100644 --- a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp +++ b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp @@ -2657,6 +2657,42 @@ bool CNPC_Vortigaunt::IsValidEnemy( CBaseEntity *pEnemy ) return BaseClass::IsValidEnemy( pEnemy ); } +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +Vector CNPC_Vortigaunt::GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy ) +{ + Vector vecBase = BaseClass::GetShootEnemyDir( shootOrigin, bNoisy ); + +#ifdef EZ2 + CBaseEntity *pEnemy = GetEnemy(); + if ( IsPlayerAlly() && pEnemy ) + { + trace_t tr; + AI_TraceLine( shootOrigin, shootOrigin + (vecBase * InnateRange1MaxRange()), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + + // If it won't reach, try the same thing as base class but with the head target + if (tr.m_pEnt != pEnemy) + { + Vector vecEnemyLKP = GetEnemyLKP(); + Vector vecEnemyOffset = pEnemy->HeadTarget( shootOrigin ) - pEnemy->GetAbsOrigin(); + + Vector retval = vecEnemyOffset + vecEnemyLKP - shootOrigin; + VectorNormalize( retval ); + AI_TraceLine( shootOrigin, shootOrigin + (retval * InnateRange1MaxRange()), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + if (tr.m_pEnt == pEnemy) + { + return retval; + } + } + } +#endif + + return vecBase; +} + //----------------------------------------------------------------------------- // Purpose: Creates a blast where the beam has struck a target // Input : &vecOrigin - position to eminate from @@ -3360,6 +3396,10 @@ void CNPC_Vortigaunt::DispelAntlions( const Vector &vecOrigin, float flRadius, b if ( pEntity == this || pEntity == NULL ) continue; + // Don't hit friendly or neutral targets + if ( IRelationType( pEntity ) >= D_LI ) + continue; + // Antlions react differently if ( IsAntlion( pEntity ) ) { @@ -3397,7 +3437,7 @@ void CNPC_Vortigaunt::DispelAntlions( const Vector &vecOrigin, float flRadius, b } } } - else if ( bDispel && pEntity->MyNPCPointer() != NULL && IRelationType( pEntity ) <= D_FR ) + else if ( bDispel && pEntity->MyNPCPointer() != NULL ) { CAI_BaseNPC *pNPC = pEntity->MyNPCPointer(); // Attempt to trace a line to hit the target @@ -3799,7 +3839,7 @@ AI_BEGIN_CUSTOM_NPC( npc_vortigaunt, CNPC_Vortigaunt ) " TASK_GET_PATH_TO_TARGET 0" " TASK_MOVE_TO_TARGET_RANGE 350" " TASK_STOP_MOVING 0" - " TASK_FACE_PLAYER 0" + " TASK_FACE_TARGET 0" " TASK_VORTIGAUNT_HEAL 0" "" " Interrupts" @@ -3993,6 +4033,48 @@ AI_BEGIN_CUSTOM_NPC( npc_vortigaunt, CNPC_Vortigaunt ) AI_END_CUSTOM_NPC() +#ifdef EZ2 +//============================================================================= +// +// Vortigaunt Standoff Behavior +// +//============================================================================= +int CNPC_Vortigaunt::CVortigauntStandoffBehavior::SelectScheduleUpdateWeapon( void ) +{ + if (GetOuter()->GetActiveWeapon()) + return BaseClass::SelectScheduleUpdateWeapon(); + + if ( HasCondition( COND_LIGHT_DAMAGE ) ) + { + // if hurt: + int iPercent = random->RandomInt(0,99); + + if ( iPercent <= GetParams().oddsCover && GetEnemy() != NULL) + { + // Vorts go into cover right away + SetReuseCurrentCover(); + GetOuter()->GetShotRegulator()->Reset( false ); + } + } + + return SCHED_NONE; +} + +int CNPC_Vortigaunt::CVortigauntStandoffBehavior::TranslateSchedule( int schedule ) +{ + int iBase = BaseClass::TranslateSchedule( schedule ); + + /*if (iBase == SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK) + { + // TODO: Vorts often fail LOS when enemies are in cover, do something different + return ???; + }*/ + + return iBase; +} +#endif + + //============================================================================= // // Charge Token diff --git a/sp/src/game/server/hl2/npc_vortigaunt_episodic.h b/sp/src/game/server/hl2/npc_vortigaunt_episodic.h index d0e290c5bf5..b1d16297f87 100644 --- a/sp/src/game/server/hl2/npc_vortigaunt_episodic.h +++ b/sp/src/game/server/hl2/npc_vortigaunt_episodic.h @@ -100,6 +100,7 @@ class CNPC_Vortigaunt : public CNPC_PlayerCompanion virtual int SelectSchedule( void ); virtual int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode ); virtual bool IsValidEnemy( CBaseEntity *pEnemy ); + virtual Vector GetShootEnemyDir( const Vector &shootOrigin, bool bNoisy = true ); bool IsLeading( void ) { return ( GetRunningBehavior() == &m_LeadBehavior && m_LeadBehavior.HasGoal() ); } void DeathSound( const CTakeDamageInfo &info ); @@ -306,6 +307,31 @@ class CNPC_Vortigaunt : public CNPC_PlayerCompanion COutputEvent m_OnFinishedChargingTarget; COutputEvent m_OnPlayerUse; +#ifdef EZ2 + class CVortigauntStandoffBehavior : public CAI_StandoffBehavior + { + typedef CAI_StandoffBehavior BaseClass; + + public: + virtual int SelectScheduleUpdateWeapon(); + virtual int SelectScheduleAttack() + { + int result = GetOuterVort()->SelectRangeAttack2Schedule(); + if ( result == SCHED_NONE ) + result = BaseClass::SelectScheduleAttack(); + return result; + } + + virtual int TranslateSchedule( int schedule ); + + inline CNPC_Vortigaunt *GetOuterVort() { return static_cast(GetOuter()); } + }; + + virtual CAI_StandoffBehavior &GetStandoffBehavior( void ) { return m_StandoffBehavior; } + + CVortigauntStandoffBehavior m_StandoffBehavior; +#endif + #ifdef EZ // Child classes need these attachments! protected: From b4f6569d9f1edb39be5c24479977585e30db4054 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 19:30:45 -0600 Subject: [PATCH 085/103] New cheat field in save metadata --- sp/src/game/server/ez2/ez2_save_metadata.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sp/src/game/server/ez2/ez2_save_metadata.cpp b/sp/src/game/server/ez2/ez2_save_metadata.cpp index 4516aa44758..6ed3ed95b1f 100644 --- a/sp/src/game/server/ez2/ez2_save_metadata.cpp +++ b/sp/src/game/server/ez2/ez2_save_metadata.cpp @@ -7,6 +7,7 @@ #include "cbase.h" #include "tier3/tier3.h" #include "vgui/ILocalize.h" +#include "achievementmgr.h" #include "npc_wilson.h" //============================================================================= @@ -85,6 +86,13 @@ class CCustomSaveMetadata : public CAutoGameSystem pCustomSaveMetadata->SetBool("wilson", true ); } + // Cheated + CAchievementMgr *pAchievementMgr = dynamic_cast(engine->GetAchievementMgr()); + if (pAchievementMgr) + { + pCustomSaveMetadata->SetBool( "cheated", pAchievementMgr->WereCheatsEverOn() ); + } + Msg( "Saving custom metadata to %s\n", name ); pCustomSaveMetadata->SaveToFile( filesystem, name, "MOD" ); From 5bf4f8084257398e4eda74b28cd48c812e53d19b Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 19:27:18 -0600 Subject: [PATCH 086/103] Stukabat squad AI and divebomb changes --- sp/src/game/server/ez2/npc_flyingpredator.cpp | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/ez2/npc_flyingpredator.cpp b/sp/src/game/server/ez2/npc_flyingpredator.cpp index 6f77aa45a31..54573b1d229 100644 --- a/sp/src/game/server/ez2/npc_flyingpredator.cpp +++ b/sp/src/game/server/ez2/npc_flyingpredator.cpp @@ -320,9 +320,25 @@ int CNPC_FlyingPredator::RangeAttack1Conditions( float flDot, float flDist ) if (flDist <= InnateRange1MinRange()) return COND_TOO_CLOSE_TO_ATTACK; - // Trace hull to enemy if (GetEnemy()) { + // If the enemy isn't facing me, delegate attacks to squadmates which the enemy does see + // If the enemy isn't facing any squadmate, this doesn't matter + if (GetSquad() && !HasCondition( COND_ENEMY_FACING_ME )) + { + AISquadIter_t iter; + for ( CAI_BaseNPC *pNPC = m_pSquad->GetFirstMember(&iter); pNPC; pNPC = m_pSquad->GetNextMember(&iter) ) + { + // UNDONE: Classname check + //if (GetClassname() == pNPC->GetClassname()) + { + if (pNPC->GetEnemy() == GetEnemy() && pNPC->HasCondition( COND_ENEMY_FACING_ME )) + return COND_NONE; + } + } + } + + // Trace hull to enemy trace_t tr; AI_TraceHull( GetAbsOrigin(), GetEnemyLKP(), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); @@ -1158,6 +1174,10 @@ void CNPC_FlyingPredator::DiveBombAttack() // Fly towards my enemy Vector vEnemyPos = GetEnemyLKP(); m_vecDiveBombDirection = vEnemyPos - GetLocalOrigin(); + + // Roughly guess how long the dive will be + float flSoundDuration = m_vecDiveBombDirection.Length() / 300.0f; + CSoundEnt::InsertSound( SOUND_DANGER, vEnemyPos, 64.0f, flSoundDuration, this, 0, GetEnemy() ); } else { @@ -1629,6 +1649,19 @@ void CNPC_FlyingPredator::StartTouch( CBaseEntity *pOther ) if ( IRelationType( pOther ) != D_LI ) { CTakeDamageInfo info( this, this, m_bIsBaby ? sk_flyingpredator_dmg_dive.GetFloat() / 3.0f : sk_flyingpredator_dmg_dive.GetFloat(), DMG_CLUB | DMG_ALWAYSGIB ); + + Vector vecForce = GetAbsVelocity(); + + // Push based on how much mass the object has. Don't boost if it's lower than ours + if (pOther->VPhysicsGetObject() && VPhysicsGetObject()) + vecForce *= MIN( (VPhysicsGetObject()->GetMass() / pOther->VPhysicsGetObject()->GetMass()), 1.0f ); + + info.SetDamageForce( vecForce ); + if (ShouldApplyHitVelocityToTarget( pOther )) + { + pOther->ApplyAbsVelocityImpulse( vecForce ); + } + pOther->TakeDamage( info ); if ( m_tFlyState == FlyState_Falling ) @@ -1639,6 +1672,7 @@ void CNPC_FlyingPredator::StartTouch( CBaseEntity *pOther ) else { EmitSound( "NPC_FlyingPredator.Bite" ); + VacateStrategySlot(); } } From 0a40cbd8878d85ea761e702ca4609aa9391c3da4 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 19:31:49 -0600 Subject: [PATCH 087/103] Expose E:Z2 classify classes to VScript --- .../game/shared/mapbase/vscript_consts_shared.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp index bb47bcb17cb..1a5d12a9d4f 100644 --- a/sp/src/game/shared/mapbase/vscript_consts_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_consts_shared.cpp @@ -534,14 +534,14 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, CLASS_ANTLION, "Used by antlions, antlion guards, etc." ); ScriptRegisterConstant( g_pScriptVM, CLASS_BARNACLE, "Used by barnacles." ); ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSEYE, "Used by npc_bullseye." ); - //ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSQUID, "Used by bullsquids." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_BULLSQUID, "Used by bullsquids." ); ScriptRegisterConstant( g_pScriptVM, CLASS_CITIZEN_PASSIVE, "Used by citizens when the \"gordon_precriminal\" or \"citizens_passive\" states are enabled." ); ScriptRegisterConstant( g_pScriptVM, CLASS_CITIZEN_REBEL, "UNUSED IN HL2. Rebels normally use CLASS_PLAYER_ALLY." ); ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE, "Used by Combine soldiers, Combine turrets, and other misc. Combine NPCs." ); ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_GUNSHIP, "Used by Combine gunships, helicopters, etc." ); ScriptRegisterConstant( g_pScriptVM, CLASS_CONSCRIPT, "UNUSED IN HL2. Would've been used by conscripts." ); ScriptRegisterConstant( g_pScriptVM, CLASS_HEADCRAB, "Used by headcrabs." ); - //ScriptRegisterConstant( g_pScriptVM, CLASS_HOUNDEYE, "Used by houndeyes." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_HOUNDEYE, "Used by houndeyes." ); ScriptRegisterConstant( g_pScriptVM, CLASS_MANHACK, "Used by Combine manhacks." ); ScriptRegisterConstant( g_pScriptVM, CLASS_METROPOLICE, "Used by Combine metrocops." ); ScriptRegisterConstant( g_pScriptVM, CLASS_MILITARY, "In HL2, this is only used by npc_combinecamera and func_guntarget. This appears to be recognized as a Combine class." ); @@ -555,6 +555,15 @@ void RegisterSharedScriptConstants() ScriptRegisterConstant( g_pScriptVM, CLASS_EARTH_FAUNA, "Used by birds and other terrestrial animals." ); ScriptRegisterConstant( g_pScriptVM, CLASS_HACKED_ROLLERMINE, "Used by rollermines which were hacked by Alyx." ); ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_HUNTER, "Used by Combine hunters." ); +#ifdef EZ + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_FAUNA, "Used by boids and other passive Xen fauna." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_PREDATOR, "Used by Xen predators." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ALIEN_FLORA, "Used by Xen plants." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_RACE_X, "Used by Race X." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_NEMESIS, "Used by Clone Cop and his manhacks." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_ARBEIT_TECH, "Used by Wilson and other Arbeit technology." ); + ScriptRegisterConstant( g_pScriptVM, CLASS_COMBINE_HUSK, "Used by husks." ); +#endif #elif defined( HL1_DLL ) From bf9de824cc4b379cd34f139441fa256e95db1eec Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sat, 25 Jan 2025 19:28:33 -0600 Subject: [PATCH 088/103] info_remarkable support for CAI_PlayerAlly --- sp/src/game/server/ai_playerally.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sp/src/game/server/ai_playerally.h b/sp/src/game/server/ai_playerally.h index 76827e6c769..c3338f574c8 100644 --- a/sp/src/game/server/ai_playerally.h +++ b/sp/src/game/server/ai_playerally.h @@ -423,6 +423,8 @@ class CAI_PlayerAlly : public CAI_BaseActor // So Will-E can override idle speech stuff virtual void HandlePrescheduleIdleSpeech(); inline void SetNextIdleSpeechTime( float flTime ) { m_flNextIdleSpeechTime = flTime; } + + bool Remark( AI_CriteriaSet &modifiers, CBaseEntity *pRemarkable ) { return SpeakIfAllowed( TLK_REMARK, modifiers ); } #endif //--------------------------------- From 02372c44ead9ab4b820e2d751ad484ae7ac63e20 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 09:14:44 -0600 Subject: [PATCH 089/103] Predators ignore props with 2x mass in obstruction behavior --- sp/src/game/server/ez2/npc_basepredator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/ez2/npc_basepredator.cpp b/sp/src/game/server/ez2/npc_basepredator.cpp index 46e850fa9c3..3e2474e3b00 100644 --- a/sp/src/game/server/ez2/npc_basepredator.cpp +++ b/sp/src/game/server/ez2/npc_basepredator.cpp @@ -485,8 +485,8 @@ bool CNPC_BasePredator::ShouldAttackObstruction( CBaseEntity *pEntity ) if ( !pEntity->MyCombatCharacterPointer() && pEntity->GetMoveType() != MOVETYPE_VPHYSICS ) return false; - // Don't attack props which don't have motion enabled - if ( pEntity->VPhysicsGetObject() && !pEntity->VPhysicsGetObject()->IsMotionEnabled() ) + // Don't attack props which don't have motion enabled or have more than 2x mass than us + if ( pEntity->VPhysicsGetObject() && (!pEntity->VPhysicsGetObject()->IsMotionEnabled() || pEntity->VPhysicsGetObject()->GetMass()*2 > GetMass()) ) return false; // Don't attack NPCs in the same squad From 62eaef11544d8f03621e647706111fe47c45a146 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 09:17:04 -0600 Subject: [PATCH 090/103] Fix headcrabs producing carcass decal and scent after dying with no ragdoll --- sp/src/game/server/hl2/npc_headcrab.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/hl2/npc_headcrab.cpp b/sp/src/game/server/hl2/npc_headcrab.cpp index 3ed08f56332..8e67f97a26f 100644 --- a/sp/src/game/server/hl2/npc_headcrab.cpp +++ b/sp/src/game/server/hl2/npc_headcrab.cpp @@ -1881,9 +1881,11 @@ void CBaseHeadcrab::Event_Killed( const CTakeDamageInfo &info ) #ifdef EZ // Create a little decal underneath the headcrab if ( - sk_headcrab_carcass_smell.GetBool() || + (sk_headcrab_carcass_smell.GetBool() || // This type of damage combination happens from dynamic scripted sequences - info.GetDamageType() & (DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE) + info.GetDamageType() & (DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE)) + // Don't do this if there was no carcass + && !(info.GetDamageType() & DMG_REMOVENORAGDOLL) ) { trace_t tr; From 12fd3c66ac3503673840eca7a526b4dbd14c451a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 10:26:31 -0600 Subject: [PATCH 091/103] Purple eye glow for Athenaeum variant advisor --- sp/src/game/server/episodic/npc_advisor.cpp | 39 ++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/sp/src/game/server/episodic/npc_advisor.cpp b/sp/src/game/server/episodic/npc_advisor.cpp index 93bf5065b16..711d3ca74ef 100644 --- a/sp/src/game/server/episodic/npc_advisor.cpp +++ b/sp/src/game/server/episodic/npc_advisor.cpp @@ -616,6 +616,8 @@ class CNPC_Advisor : public CAI_BaseActor #ifdef EZ2 virtual void SetModel( const char *szModelName ); + + void GetDefaultSpriteColor(int &r, int &g, int &b); virtual CSprite *GetGlowSpritePtr( int i ); virtual void SetGlowSpritePtr( int i, CSprite *sprite ); @@ -1242,6 +1244,28 @@ void CNPC_Advisor::SetModel( const char *szModelName ) gm_nRRearTopJetAttachment = LookupAttachment( "attach_r_rear_top_jet" ); } +//----------------------------------------------------------------------------- +// Purpose: The advisor's default sprite color +//----------------------------------------------------------------------------- +void CNPC_Advisor::GetDefaultSpriteColor( int &r, int &g, int &b ) +{ + EZ_VARIANT EZvar = GetEZVariant(); + switch (EZvar) + { + case EZ_VARIANT_ATHENAEUM: + r = 160; + g = 48; + b = 255; + break; + + default: + r = 0; + g = 255; + b = 255; + break; + } +} + //----------------------------------------------------------------------------- // Purpose: Return the pointer for a given sprite given an index //----------------------------------------------------------------------------- @@ -1291,9 +1315,7 @@ EyeGlow_t *CNPC_Advisor::GetEyeGlowData( int index ) eyeGlow->alpha = 255; eyeGlow->brightness = 224; - eyeGlow->red = 0; - eyeGlow->green = 255; - eyeGlow->blue = 255; + GetDefaultSpriteColor( eyeGlow->red, eyeGlow->green, eyeGlow->blue ); eyeGlow->renderMode = kRenderWorldGlow; eyeGlow->scale = 0.1f; @@ -1307,9 +1329,7 @@ EyeGlow_t *CNPC_Advisor::GetEyeGlowData( int index ) eyeGlow->alpha = 255; eyeGlow->brightness = 224; - eyeGlow->red = 0; - eyeGlow->green = 255; - eyeGlow->blue = 255; + GetDefaultSpriteColor( eyeGlow->red, eyeGlow->green, eyeGlow->blue ); eyeGlow->renderMode = kRenderWorldGlow; eyeGlow->scale = 0.1f; @@ -1343,8 +1363,11 @@ void CNPC_Advisor::SetEyeState( AdvisorEyeState_t state ) } else { - m_pEyeGlow->SetRenderColor( 0, 255, 255 ); - m_pCameraGlow2->SetRenderColor( 0, 255, 255 ); + int r, g, b; + GetDefaultSpriteColor( r, g, b ); + + m_pEyeGlow->SetRenderColor( r, g, b ); + m_pCameraGlow2->SetRenderColor( r, g, b ); m_pEyeGlow->SetScale( 0.1 ); m_pCameraGlow2->SetScale( 0.1 ); From 9bfbbe407666b09cbb3a0c509339d529102a8384 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 12:04:15 -0600 Subject: [PATCH 092/103] Make dual wielded pistols capable of being fired with IN_ATTACK2 (both mouse buttons) --- sp/src/game/server/hl2/weapon_pistol.cpp | 2 + .../shared/hl2/basehlcombatweapon_shared.cpp | 37 +++++++++++++++++++ .../shared/hl2/basehlcombatweapon_shared.h | 2 + 3 files changed, 41 insertions(+) diff --git a/sp/src/game/server/hl2/weapon_pistol.cpp b/sp/src/game/server/hl2/weapon_pistol.cpp index ce575c082b7..d206554eda5 100644 --- a/sp/src/game/server/hl2/weapon_pistol.cpp +++ b/sp/src/game/server/hl2/weapon_pistol.cpp @@ -667,6 +667,8 @@ class CWeaponPulsePistol : public CWeaponPistol virtual bool Reload( void ) { return false; } // The pulse pistol does not reload + bool DualWieldOverridesSecondary() const { return false; } + virtual int GetMaxClip2( void ) const { int iBase = BaseClass::GetMaxClip2(); diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp index eb62441d8d1..fc4592b1dc2 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp @@ -8,6 +8,7 @@ #include "basehlcombatweapon_shared.h" #if defined(EZ2) && defined(GAME_DLL) #include "npcevent.h" +#include "in_buttons.h" #endif #include "hl2_player_shared.h" @@ -261,6 +262,42 @@ void CBaseHLCombatWeapon::WeaponIdle( void ) } #ifdef EZ2 +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::ItemPostFrame( void ) +{ +#ifdef GAME_DLL + // When dual wielding, secondary attack is synonymous with primary attack + if (IsDualWielding() && DualWieldOverridesSecondary()) + { + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if ( pOwner != NULL ) + { + if (pOwner->m_nButtons & IN_ATTACK2) + { + pOwner->m_nButtons |= IN_ATTACK; + pOwner->m_nButtons &= ~IN_ATTACK2; + } + + if (pOwner->m_afButtonPressed & IN_ATTACK2) + { + pOwner->m_afButtonPressed |= IN_ATTACK; + pOwner->m_afButtonPressed &= ~IN_ATTACK2; + } + + if (pOwner->m_afButtonReleased & IN_ATTACK2) + { + pOwner->m_afButtonReleased |= IN_ATTACK; + pOwner->m_afButtonReleased &= ~IN_ATTACK2; + } + } + } +#endif + + BaseClass::ItemPostFrame(); +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h index 8468bba83b2..8464bc9ef15 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h @@ -57,12 +57,14 @@ class CBaseHLCombatWeapon : public CBaseCombatWeapon // // Dual wielding // + virtual void ItemPostFrame( void ); virtual const char *GetViewModel( int viewmodelindex = 0 ) const; virtual CHudTexture const *GetSpriteActive( void ) const; virtual CHudTexture const *GetSpriteInactive( void ) const; virtual bool CanDualWield() const { return false; } + virtual bool DualWieldOverridesSecondary() const { return true; } bool IsDualWielding() const { return m_hLeftHandGun != NULL; } CBaseAnimating *GetLeftHandGun() const { return m_hLeftHandGun; } From f78fe66ff94189364f223e4c390849c165823c05 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 12:13:19 -0600 Subject: [PATCH 093/103] Natural player dual wielding + unison with npc_assassin dual wielding --- sp/src/game/server/basecombatcharacter.cpp | 8 ++++ sp/src/game/server/basecombatcharacter.h | 4 ++ sp/src/game/server/ez2/ez2_player.cpp | 47 +++++++++++++++++++ sp/src/game/server/ez2/ez2_player.h | 6 +++ sp/src/game/server/ez2/npc_assassin.cpp | 30 +++++++----- sp/src/game/server/ez2/npc_assassin.h | 2 +- sp/src/game/server/player.cpp | 10 +++- .../shared/hl2/basehlcombatweapon_shared.cpp | 16 +++++++ .../shared/hl2/basehlcombatweapon_shared.h | 1 + 9 files changed, 109 insertions(+), 15 deletions(-) diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 77c19ea0f8e..d73aa59334d 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -2476,6 +2476,14 @@ void CBaseCombatCharacter::Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ) if (pWeapon->GetSlot() == m_hMyWeapons[i]->GetSlot() && pWeapon->GetPosition() == m_hMyWeapons[i]->GetPosition()) { +#ifdef EZ2 + if (Weapon_EquipDual( pWeapon, m_hMyWeapons[i] )) + { + // Weapon has been merged with the existing weapon + return; + } +#endif + // Replace our existing weapon in this slot Weapon_Drop(m_hMyWeapons[i]); { diff --git a/sp/src/game/server/basecombatcharacter.h b/sp/src/game/server/basecombatcharacter.h index a2d3f7f5eeb..da0d37d9ae9 100644 --- a/sp/src/game/server/basecombatcharacter.h +++ b/sp/src/game/server/basecombatcharacter.h @@ -239,6 +239,10 @@ class CBaseCombatCharacter : public CBaseFlex virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); // Adds weapon to player virtual void Weapon_EquipHolstered( CBaseCombatWeapon *pWeapon ); // Pretty much only useful for NPCs virtual void Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ); +#ifdef EZ2 + // Used by CEZ2_Player and CNPC_Assassin + virtual bool Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ) { return false; } +#endif #else virtual void Weapon_Equip( CBaseCombatWeapon *pWeapon ); // Adds weapon to player #endif diff --git a/sp/src/game/server/ez2/ez2_player.cpp b/sp/src/game/server/ez2/ez2_player.cpp index 3f96e47471f..9a965b025fb 100644 --- a/sp/src/game/server/ez2/ez2_player.cpp +++ b/sp/src/game/server/ez2/ez2_player.cpp @@ -69,6 +69,8 @@ BEGIN_DATADESC(CEZ2_Player) DEFINE_FIELD(m_hSpeechTarget, FIELD_EHANDLE), + DEFINE_KEYFIELD( m_bCanDualWield, FIELD_BOOLEAN, "CanDualWield" ), + // These don't need to be saved //DEFINE_FIELD(m_iVisibleEnemies, FIELD_INTEGER), //DEFINE_FIELD(m_iCloseEnemies, FIELD_INTEGER), @@ -80,6 +82,9 @@ BEGIN_DATADESC(CEZ2_Player) DEFINE_INPUTFUNC(FIELD_VOID, "StopScripting", InputStopScripting), DEFINE_INPUTFUNC(FIELD_VOID, "__FinishBonusChallenge", InputFinishBonusChallenge), + + DEFINE_INPUTFUNC( FIELD_VOID, "EnableDualWield", InputEnableDualWield ), + DEFINE_INPUTFUNC( FIELD_VOID, "DisableDualWield", InputDisableDualWield ), END_DATADESC() BEGIN_ENT_SCRIPTDESC( CEZ2_Player, CBasePlayer, "E:Z2's player entity." ) @@ -1993,6 +1998,48 @@ void CEZ2_Player::Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ) } } +//----------------------------------------------------------------------------- +// Purpose: Equips a dual weapon +//----------------------------------------------------------------------------- +bool CEZ2_Player::Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ) +{ + if (!m_bCanDualWield) + return false; + + // For now, only identical weapons can be dual wielded + if (pWeapon->GetClassname() != pExistingWeapon->GetClassname()) + return false; + + CBaseHLCombatWeapon *pHLWeapon = dynamic_cast(pWeapon); + if (pHLWeapon && pHLWeapon->CanDualWield() && pHLWeapon->GetWpnData().szViewModelDual[0]) + { + CBaseHLCombatWeapon *pHLExistingWeapon = static_cast(pExistingWeapon); + Assert( pHLExistingWeapon ); + + if (pHLExistingWeapon && !pHLExistingWeapon->GetLeftHandGun()) + { + pHLExistingWeapon->CreateLeftHandGun(); + + // Combine the ammo + pHLExistingWeapon->m_iClip1 += pWeapon->m_iClip1; + pHLExistingWeapon->m_iClip2 += pWeapon->m_iClip2; + + // Switch to the new dual weapon + Weapon_Switch( pExistingWeapon ); + + // Remove the weapon on the ground + UTIL_Remove( pWeapon ); + + // Emit a pickup sound + EmitSound( "BaseCombatCharacter.AmmoPickup" ); + + return true; + } + } + + return false; +} + //----------------------------------------------------------------------------- // Purpose: Event fired upon picking up a new weapon //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ez2/ez2_player.h b/sp/src/game/server/ez2/ez2_player.h index 6374663b16c..8be1fe7d411 100644 --- a/sp/src/game/server/ez2/ez2_player.h +++ b/sp/src/game/server/ez2/ez2_player.h @@ -184,6 +184,7 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList bool HandleRemoveFromPlayerSquad( CAI_BaseNPC *pNPC ); void Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ); + bool Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ); void Event_FirstDrawWeapon( CBaseCombatWeapon *pWeapon ); void Event_ThrewGrenade( CBaseCombatWeapon *pWeapon ); @@ -210,6 +211,9 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList void InputStartScripting( inputdata_t &inputdata ); void InputStopScripting( inputdata_t &inputdata ); + void InputEnableDualWield( inputdata_t &inputdata ) { m_bCanDualWield = true; } + void InputDisableDualWield( inputdata_t &inputdata ) { m_bCanDualWield = false; } + // Blixibon - Speech thinking implementation void DoSpeechAI(); bool DoIdleSpeech(); @@ -302,6 +306,8 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList EHANDLE m_hSpeechTarget; + bool m_bCanDualWield; + int m_iVisibleEnemies; int m_iCloseEnemies; diff --git a/sp/src/game/server/ez2/npc_assassin.cpp b/sp/src/game/server/ez2/npc_assassin.cpp index 0be377085b9..0561d9b6194 100644 --- a/sp/src/game/server/ez2/npc_assassin.cpp +++ b/sp/src/game/server/ez2/npc_assassin.cpp @@ -1999,24 +1999,28 @@ void CNPC_Assassin::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecT } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: Equips a dual weapon //----------------------------------------------------------------------------- -void CNPC_Assassin::Weapon_Equip( CBaseCombatWeapon *pWeapon ) +bool CNPC_Assassin::Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ) { - if ( GetActiveWeapon() && GetActiveWeapon()->GetClassname() == pWeapon->GetClassname() ) + if (GetActiveWeapon() != pExistingWeapon) + return false; + + // For now, only identical weapons can be dual wielded + if (pWeapon->GetClassname() != pExistingWeapon->GetClassname()) + return false; + + CBaseHLCombatWeapon *pHLWeapon = dynamic_cast(pWeapon); + if (pHLWeapon && pHLWeapon->CanDualWield() && !m_bDualWeapons) { - CBaseHLCombatWeapon *pHLWeapon = dynamic_cast(pWeapon); - if ( pHLWeapon && pHLWeapon->CanDualWield() && !m_bDualWeapons ) - { - // Add left hand gun for weapon that already exists - AddLeftHandGun( GetActiveWeapon() ); - m_bDualWeapons = true; - UTIL_Remove( pWeapon ); - return; - } + // Add left hand gun for weapon that already exists + AddLeftHandGun( GetActiveWeapon() ); + m_bDualWeapons = true; + UTIL_Remove( pWeapon ); + return true; } - BaseClass::Weapon_Equip( pWeapon ); + return false; } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/ez2/npc_assassin.h b/sp/src/game/server/ez2/npc_assassin.h index 42bf0413175..c0ba340437e 100644 --- a/sp/src/game/server/ez2/npc_assassin.h +++ b/sp/src/game/server/ez2/npc_assassin.h @@ -76,7 +76,7 @@ class CNPC_Assassin : public CNPC_Combine void Weapon_HandleEquip( CBaseCombatWeapon *pWeapon ); void Weapon_EquipHolstered( CBaseCombatWeapon *pWeapon ); void Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL ); - void Weapon_Equip( CBaseCombatWeapon *pWeapon ); + bool Weapon_EquipDual( CBaseCombatWeapon *pWeapon, CBaseCombatWeapon *pExistingWeapon ); Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL ); float GetSpreadBias( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ); diff --git a/sp/src/game/server/player.cpp b/sp/src/game/server/player.cpp index 480bd6208e8..492174b317a 100644 --- a/sp/src/game/server/player.cpp +++ b/sp/src/game/server/player.cpp @@ -7151,8 +7151,16 @@ bool CBasePlayer::BumpWeapon( CBaseCombatWeapon *pWeapon ) // ---------------------------------------- // If I already have it just take the ammo // ---------------------------------------- - if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) + if (CBaseCombatWeapon *pOwnsThisType = Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType())) { +#ifdef EZ2 + if ( Weapon_EquipDual( pWeapon, pOwnsThisType ) ) + { + // Weapon has been merged with the existing weapon + return true; + } + else +#endif if( Weapon_EquipAmmoOnly( pWeapon ) ) { // Only remove me if I have no ammo left diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp index fc4592b1dc2..67dcaab8f53 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.cpp @@ -52,6 +52,7 @@ BEGIN_DATADESC( CBaseHLCombatWeapon ) #ifdef EZ2 DEFINE_INPUTFUNC( FIELD_VOID, "CreateLeftHandGun", InputCreateLeftHandGun ), + DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetLeftHandGun", InputSetLeftHandGun ), #endif END_DATADESC() @@ -619,6 +620,21 @@ void CBaseHLCombatWeapon::InputCreateLeftHandGun( inputdata_t &inputdata ) { CreateLeftHandGun(); } + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseHLCombatWeapon::InputSetLeftHandGun( inputdata_t &inputdata ) +{ + CBaseEntity *pGun = inputdata.value.Entity(); + if (!pGun || !pGun->GetBaseAnimating()) + { + SetLeftHandGun( NULL ); + return; + } + + SetLeftHandGun( pGun->GetBaseAnimating() ); +} #endif #endif diff --git a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h index 8464bc9ef15..670eae0c472 100644 --- a/sp/src/game/shared/hl2/basehlcombatweapon_shared.h +++ b/sp/src/game/shared/hl2/basehlcombatweapon_shared.h @@ -88,6 +88,7 @@ class CBaseHLCombatWeapon : public CBaseCombatWeapon CBaseAnimating *CreateLeftHandGun(); void InputCreateLeftHandGun( inputdata_t &inputdata ); + void InputSetLeftHandGun( inputdata_t &inputdata ); protected: From 59aa540d4de794ddb4af2c194d61eb32d27546d4 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 12:58:06 -0600 Subject: [PATCH 094/103] Fix Bad Cop interactions not being accessible in VScript --- sp/src/game/server/hl2/hl2_player.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sp/src/game/server/hl2/hl2_player.cpp b/sp/src/game/server/hl2/hl2_player.cpp index 31d91d66237..f030a54c347 100644 --- a/sp/src/game/server/hl2/hl2_player.cpp +++ b/sp/src/game/server/hl2/hl2_player.cpp @@ -814,18 +814,18 @@ void CHL2_Player::Precache( void ) // Interactions if ( g_interactionBadCopKick == 0 ) { - g_interactionBadCopKick = CBaseCombatCharacter::GetInteractionID(); + CBaseCombatCharacter::AddInteractionWithString( g_interactionBadCopKick, "g_interactionBadCopKick" ); } if ( g_interactionBadCopKickWarn == 0 ) { - g_interactionBadCopKickWarn = CBaseCombatCharacter::GetInteractionID(); + CBaseCombatCharacter::AddInteractionWithString( g_interactionBadCopKickWarn, "g_interactionBadCopKickWarn" ); } // Interactions if (g_interactionBadCopOrderSurrender == 0) { - g_interactionBadCopOrderSurrender = CBaseCombatCharacter::GetInteractionID(); + CBaseCombatCharacter::AddInteractionWithString( g_interactionBadCopOrderSurrender, "g_interactionBadCopOrderSurrender" ); } #endif From a0dccd4edffe02db0fafef607ba8746a946778ba Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 13:31:43 -0600 Subject: [PATCH 095/103] Fix soldiers looking at enemies when they don't see them --- sp/src/game/server/hl2/npc_combine.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index 7c36d0f0e1b..2216ec1fecd 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -5891,10 +5891,10 @@ void CNPC_Combine::OnEndMoveAndShoot() //----------------------------------------------------------------------------- bool CNPC_Combine::PickTacticalLookTarget( AILookTargetArgs_t *pArgs ) { - if( GetState() == NPC_STATE_COMBAT ) + if ( HasCondition( COND_SEE_ENEMY ) ) { CBaseEntity *pEnemy = GetEnemy(); - if ( pEnemy && FVisible( pEnemy ) && ValidHeadTarget(pEnemy->EyePosition()) ) + if ( pEnemy && ValidHeadTarget( pEnemy->EyePosition() ) ) { // Look at the enemy if possible. pArgs->hTarget = pEnemy; @@ -5903,8 +5903,12 @@ bool CNPC_Combine::PickTacticalLookTarget( AILookTargetArgs_t *pArgs ) } else { - // Look at yourself instead. We can't be looking in random directions. - pArgs->hTarget = this; + // Look ahead instead. We can't be looking in random directions. + Vector vecForward; + GetVectors( &vecForward, NULL, NULL ); + + pArgs->vTarget = EyePosition() + (vecForward * 16.0f); + pArgs->hTarget = NULL; pArgs->flInfluence = random->RandomFloat( 0.8, 1.0 ); pArgs->flRamp = 0; } From 493c0c4b313f27ca9e8a6aaae9f7bc39f4050223 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 09:17:42 -0600 Subject: [PATCH 096/103] Fix dropship containers being displaceable --- sp/src/game/server/hl2/npc_combinedropship.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sp/src/game/server/hl2/npc_combinedropship.cpp b/sp/src/game/server/hl2/npc_combinedropship.cpp index 75adafa7193..19f62e2ca8c 100644 --- a/sp/src/game/server/hl2/npc_combinedropship.cpp +++ b/sp/src/game/server/hl2/npc_combinedropship.cpp @@ -180,6 +180,11 @@ class CCombineDropshipContainer : public CPhysicsProp bool AllowsAnyDamage( const CTakeDamageInfo &info ); #endif +#ifdef EZ + // Don't displace dropship containers + virtual bool IsDisplacementImpossible() { return true; } +#endif + private: enum { From 33cde69ab8f7905d5a819131372a17fcda6a2970 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Thu, 30 Jan 2025 23:27:25 -0600 Subject: [PATCH 097/103] Steam Workshop mounting system --- sp/src/game/client/client_ez2.vpc | 1 + sp/src/game/server/server_ez2.vpc | 2 + sp/src/game/shared/workshop_mount.cpp | 279 ++++++++++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 sp/src/game/shared/workshop_mount.cpp diff --git a/sp/src/game/client/client_ez2.vpc b/sp/src/game/client/client_ez2.vpc index 71ab3ea6091..96137862542 100644 --- a/sp/src/game/client/client_ez2.vpc +++ b/sp/src/game/client/client_ez2.vpc @@ -50,6 +50,7 @@ $Project "Client (Entropy Zero 2)" $File "ezu\hud_healthvialstatus.cpp" $File "workshop_publish.cpp" [!$NO_STEAM] + $File "$SRCDIR\game\shared\workshop_mount.cpp" [!$NO_STEAM] } $Folder "HL2 DLL" { diff --git a/sp/src/game/server/server_ez2.vpc b/sp/src/game/server/server_ez2.vpc index 4f0a0e06ca2..eca2e85e250 100644 --- a/sp/src/game/server/server_ez2.vpc +++ b/sp/src/game/server/server_ez2.vpc @@ -92,6 +92,8 @@ $Project "Server (Entropy Zero 2)" $File "ez2\weapon_oicw.cpp" $File "ez2\weapon_displacer_pistol.h" $File "ez2\weapon_displacer_pistol.cpp" + + $File "$SRCDIR\game\shared\workshop_mount.cpp" [!$NO_STEAM] } $File "ai_eventresponse.cpp" $File "ai_eventresponse.h" diff --git a/sp/src/game/shared/workshop_mount.cpp b/sp/src/game/shared/workshop_mount.cpp new file mode 100644 index 00000000000..1bcef89397d --- /dev/null +++ b/sp/src/game/shared/workshop_mount.cpp @@ -0,0 +1,279 @@ +//=============================================================================// +// +// Purpose: Workshop mounting system. +// +// Author: Blixibon +// +//=============================================================================// + +#include "cbase.h" + +#include "filesystem.h" +#include "tier0/icommandline.h" +#include "steam/steam_api.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define MAX_WORKSHOP_ITEMS 128 + +#define WORKSHOP_MANIFEST_NAME "addoninfo.txt" + +ConVar workshop_mount_vpks( "workshop_mount_vpks", "1" ); +ConVar workshop_game_override( "workshop_game_override", "", FCVAR_CHEAT | FCVAR_REPLICATED, "If a value is specified, then the workshop mounting system will pretend this is the name of the current mod. Use * to mount addons from any game." ); + +//============================================================================= +//============================================================================= +class CWorkshopMountSystem : public CAutoGameSystem +{ +public: + bool Init(); + + void LoadWorkshopItems(); + bool ParseGameParam( const char *pszToken, const char *szModDir ); +}; + +//----------------------------------------------------------------------------- + +CWorkshopMountSystem g_WorkshopMountSystem; + +static int SortStricmp( char *const *sz1, char *const *sz2 ) +{ + return V_stricmp( *sz1, *sz2 ); +} + +bool CWorkshopMountSystem::Init() +{ + LoadWorkshopItems(); + return true; +} + +void CWorkshopMountSystem::LoadWorkshopItems() +{ + if (CommandLine()->FindParm( "-noworkshop" ) != 0) + return; + + uint64 nItemIDs[MAX_WORKSHOP_ITEMS]; + int nNumItems = steamapicontext->SteamUGC()->GetSubscribedItems( nItemIDs, MAX_WORKSHOP_ITEMS ); + + char szFolder[MAX_PATH]; + + char szManifestPath[MAX_PATH]; + + // These are needed for the function even though we don't use them + uint64 nSizeOnDisk; + bool bLegacyItem; + + for (int i = 0; i < nNumItems; i++) + { + if (!steamapicontext->SteamUGC()->GetItemInstallInfo( nItemIDs[i], &nSizeOnDisk, szFolder, sizeof( szFolder ), &bLegacyItem )) + continue; + + V_snprintf( szManifestPath, sizeof( szManifestPath ), "%s%c" WORKSHOP_MANIFEST_NAME, szFolder, CORRECT_PATH_SEPARATOR ); + + // Add path by default in case there is no manifest + bool bAddPath = true; + + KeyValues *pManifest = new KeyValues( "AddonInfo" ); + if (pManifest->LoadFromFile( g_pFullFileSystem, szManifestPath )) + { + //---------------------------------------------------------------------------- + // Addons may target specific games/mods + //---------------------------------------------------------------------------- + const char *pszTargetGames = pManifest->GetString( "game", NULL ); + if (pszTargetGames) + { + // Don't load until we confirm this mod is part of the list + bAddPath = false; + +#ifdef CLIENT_DLL + char szModDir[MAX_PATH]; + const char *pGameDir = CommandLine()->ParmValue( "-game", "hl2" ); + + // Copied from UTIL_GetModDir + Q_strncpy( szModDir, pGameDir, sizeof( szModDir ) ); + if ( Q_strnchr( szModDir, '/', sizeof( szModDir ) ) || Q_strnchr( szModDir, '\\', sizeof( szModDir ) ) ) + { + // Strip the last directory off (which will be our game dir) + Q_StripLastDir( szModDir, sizeof( szModDir ) ); + + // Find the difference in string lengths and take that difference from the original string as the mod dir + int dirlen = Q_strlen( szModDir ); + Q_strncpy( szModDir, pGameDir + dirlen, Q_strlen( pGameDir ) - dirlen + 1 ); + } + + bool bValidGame = true; +#else + char szModDir[MAX_PATH]; + bool bValidGame = UTIL_GetModDir( szModDir, sizeof( szModDir ) ); +#endif + + if (workshop_game_override.GetString()[0]) + { + V_strncpy( szModDir, workshop_game_override.GetString(), sizeof( szModDir ) ); + + if (szModDir[0] == '*') + { + // Just pass without checking the game + bAddPath = true; + bValidGame = false; + } + } + + if (bValidGame) + { + char szTargetGames[128]; + V_strncpy( szTargetGames, pszTargetGames, sizeof( szTargetGames ) ); + + char *pszToken = strtok( szTargetGames, "+" ); + for (; pszToken != NULL; pszToken = strtok( NULL, "+" )) + { + if (!pszToken || !*pszToken) + continue; + + bool bInvert = false; + if (pszToken[0] == '!') // Not this game + { + pszToken++; + bInvert = true; + } + else if (bAddPath) + { + // If we've already been included, then don't care about this game unless it might exclude us + continue; + } + + bool bAllowed = ParseGameParam( pszToken, szModDir ); + if (bInvert) + { + if (bAllowed) + { + // This game was specifically excluded. Early out + bAddPath = false; + break; + } + + // Just because this game wasn't excluded doesn't mean it'll be allowed, so don't do anything + } + else + { + bAddPath = bAllowed; + } + } + } + } + } + pManifest->deleteThis(); + + if (bAddPath) + { + //---------------------------------------------------------------------------- + // Check for VPKs + //---------------------------------------------------------------------------- + CUtlStringList vecVPKs; + if (workshop_mount_vpks.GetBool()) + { + char szVPKSearchPath[MAX_PATH]; + V_snprintf( szVPKSearchPath, sizeof( szVPKSearchPath ), "%s%c*.vpk", szFolder, CORRECT_PATH_SEPARATOR ); + + FileFindHandle_t hFindHandle = NULL; + const char *pszFoundName = g_pFullFileSystem->FindFirst( szVPKSearchPath, &hFindHandle ); + if (pszFoundName) + { + do + { + vecVPKs.CopyAndAddToTail( pszFoundName ); + + pszFoundName = g_pFullFileSystem->FindNext( hFindHandle ); + + } while (pszFoundName); + + g_pFullFileSystem->FindClose( hFindHandle ); + + vecVPKs.Sort( SortStricmp ); + + // Now for any _dir.vpk files, remove the _nnn.vpk ones. + // (Copied from filesystem_init.cpp) + int idx = vecVPKs.Count()-1; + while ( idx > 0 ) + { + char szTemp[ MAX_PATH ]; + V_strcpy_safe( szTemp, vecVPKs[ idx ] ); + --idx; + + char *szDirVpk = V_stristr( szTemp, "_dir.vpk" ); + if ( szDirVpk != NULL ) + { + *szDirVpk = '\0'; + while ( idx >= 0 ) + { + char *pszPath = vecVPKs[ idx ]; + if ( V_stristr( pszPath, szTemp ) != pszPath ) + break; + delete pszPath; + vecVPKs.Remove( idx ); + --idx; + } + } + } + } + } + + if (vecVPKs.Count() > 0) + { + for (int i = 0; i < vecVPKs.Count(); i++) + { + char szVPK[MAX_PATH]; + V_snprintf( szVPK, sizeof( szVPK ), "%s%c%s", szFolder, CORRECT_PATH_SEPARATOR, vecVPKs[i] ); + + DevMsg( "Steam Workshop: Mounting VPK \"%s\"\n", szVPK ); + + filesystem->AddSearchPath( szVPK, "GAME"); + filesystem->AddSearchPath( szVPK, "MOD" ); + filesystem->AddSearchPath( szVPK, "ADDON" ); + } + } + + DevMsg( "Steam Workshop: Mounting folder \"%s\"\n", szFolder ); + + filesystem->AddSearchPath( szFolder, "GAME" ); + filesystem->AddSearchPath( szFolder, "MOD" ); + filesystem->AddSearchPath( szFolder, "ADDON" ); + } + } +} + +bool CWorkshopMountSystem::ParseGameParam( const char *pszToken, const char *pszModDir ) +{ + if (pszToken[0] == '#') // Special commands + { + pszToken++; + + if (FStrEq( pszToken, "episodes" )) + { + // Return true for AP, etc. + if (FStrEq( pszModDir, "axonpariah" ) || FStrEq( pszModDir, "progenitors" )) + return true; + else + return false; + } + else if (FStrEq( pszToken, "all" )) + { + return true; + } + + return false; + } + + return FStrEq( pszModDir, pszToken ); +} + +#ifdef CLIENT_DLL +CON_COMMAND_F( workshop_reload_addons_client, "Reloads addons", FCVAR_CHEAT ) +#else +CON_COMMAND_F( workshop_reload_addons, "Reloads addons", FCVAR_CHEAT ) +#endif +{ + // TODO: Remove addon paths before reloading them? Not sure if necessary + g_WorkshopMountSystem.LoadWorkshopItems(); +} From 442bc790b2aa44fb58783738ba5222fb71bbcfff Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Fri, 31 Jan 2025 09:28:42 -0600 Subject: [PATCH 098/103] Start using Mapbase default player and hand model values --- sp/src/game/server/ez2/ez2_player.cpp | 2 -- sp/src/game/shared/mapbase/mapbase_shared.cpp | 9 +++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/ez2/ez2_player.cpp b/sp/src/game/server/ez2/ez2_player.cpp index 9a965b025fb..a3066c5e7cd 100644 --- a/sp/src/game/server/ez2/ez2_player.cpp +++ b/sp/src/game/server/ez2/ez2_player.cpp @@ -514,8 +514,6 @@ void CEZ2_Player::Spawn( void ) BaseClass::Spawn(); - SetModel( "models/bad_cop.mdl" ); - Activate(); if (GetBonusChallenge() != EZ_CHALLENGE_NONE) diff --git a/sp/src/game/shared/mapbase/mapbase_shared.cpp b/sp/src/game/shared/mapbase/mapbase_shared.cpp index 500c5e2c394..ba0ff5dfb7b 100644 --- a/sp/src/game/shared/mapbase/mapbase_shared.cpp +++ b/sp/src/game/shared/mapbase/mapbase_shared.cpp @@ -254,10 +254,19 @@ class CMapbaseSystem : public CAutoGameSystem } #ifdef GAME_DLL +#ifdef EZ2 + Q_strncpy( g_szDefaultPlayerModel, gameinfo->GetString( "player_default_model", "models/bad_cop.mdl" ), sizeof( g_szDefaultPlayerModel ) ); +#else Q_strncpy( g_szDefaultPlayerModel, gameinfo->GetString( "player_default_model", "models/player.mdl" ), sizeof( g_szDefaultPlayerModel ) ); +#endif g_bDefaultPlayerDrawExternally = gameinfo->GetBool( "player_default_draw_externally", false ); +#ifdef EZ2 + extern ConVar sv_player_hands_modelname; + Q_strncpy( g_szDefaultHandsModel, gameinfo->GetString( "player_default_hands", sv_player_hands_modelname.GetString() ), sizeof( g_szDefaultHandsModel ) ); +#else Q_strncpy( g_szDefaultHandsModel, gameinfo->GetString( "player_default_hands", "models/weapons/v_hands.mdl" ), sizeof( g_szDefaultHandsModel ) ); +#endif g_iDefaultHandsSkin = gameinfo->GetInt( "player_default_hands_skin", 0 ); g_iDefaultHandsBody = gameinfo->GetInt( "player_default_hands_body", 0 ); #endif From 679dddc21fba8e06dca56399320b1152eefe6970 Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Tue, 11 Feb 2025 09:16:29 -0600 Subject: [PATCH 099/103] Add cvar for SMG1 to use EZ1 accuracy --- sp/src/game/server/hl2/weapon_smg1.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/hl2/weapon_smg1.cpp b/sp/src/game/server/hl2/weapon_smg1.cpp index 6c7163dac14..d329e499b02 100644 --- a/sp/src/game/server/hl2/weapon_smg1.cpp +++ b/sp/src/game/server/hl2/weapon_smg1.cpp @@ -26,6 +26,10 @@ extern ConVar sk_plr_dmg_smg1_grenade; extern ConVar sk_npc_dmg_smg1_grenade; #endif +#ifdef EZ2 +ConVar weapon_smg1_use_ez1_accuracy( "weapon_smg1_use_ez1_accuracy", "0" ); +#endif + class CWeaponSMG1 : public CHLSelectFireMachineGun { DECLARE_DATADESC(); @@ -59,10 +63,12 @@ class CWeaponSMG1 : public CHLSelectFireMachineGun { #ifdef EZ1 static const Vector cone = VECTOR_CONE_2DEGREES; + return cone; #else + static const Vector coneEZ1 = VECTOR_CONE_2DEGREES; static const Vector cone = VECTOR_CONE_5DEGREES; + return weapon_smg1_use_ez1_accuracy.GetBool() ? coneEZ1 : cone; #endif - return cone; } const WeaponProficiencyInfo_t *GetProficiencyValues(); From c86f3ecd4bb45fbbf4786fb3f7b201522569453e Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 12:21:11 -0600 Subject: [PATCH 100/103] Move E:Z2 player to followup-capable expresser --- sp/src/game/server/ez2/ez2_player.cpp | 2 +- sp/src/game/server/ez2/ez2_player.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sp/src/game/server/ez2/ez2_player.cpp b/sp/src/game/server/ez2/ez2_player.cpp index a3066c5e7cd..3e932210091 100644 --- a/sp/src/game/server/ez2/ez2_player.cpp +++ b/sp/src/game/server/ez2/ez2_player.cpp @@ -1604,7 +1604,7 @@ bool CEZ2_Player::GetGameTextSpeechParams( hudtextparms_t ¶ms ) //----------------------------------------------------------------------------- CAI_Expresser *CEZ2_Player::CreateExpresser(void) { - m_pExpresser = new CAI_Expresser(this); + m_pExpresser = new CAI_ExpresserWithFollowup(this); if (!m_pExpresser) return NULL; diff --git a/sp/src/game/server/ez2/ez2_player.h b/sp/src/game/server/ez2/ez2_player.h index 8be1fe7d411..ef22414ea45 100644 --- a/sp/src/game/server/ez2/ez2_player.h +++ b/sp/src/game/server/ez2/ez2_player.h @@ -126,6 +126,8 @@ class CEZ2_Player : public CAI_ExpresserHost, public CGameEventList virtual CAI_Expresser * CreateExpresser(void); virtual CAI_Expresser * GetExpresser() { return m_pExpresser; } + bool IsAllowedToSpeakFollowup( AIConcept_t concept ) { return IsAllowedToSpeak( concept ); } + bool GetGameTextSpeechParams( hudtextparms_t ¶ms ); void ModifyOrAppendDamageCriteria(AI_CriteriaSet & set, const CTakeDamageInfo & info, bool bPlayer = true); From 098587358143c800744ee3bf52adf812ad9a74bb Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Sun, 26 Jan 2025 12:22:14 -0600 Subject: [PATCH 101/103] Fix E:Z2 player deriving from base player instead of HL2 player in VScript --- sp/src/game/server/ez2/ez2_player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/server/ez2/ez2_player.cpp b/sp/src/game/server/ez2/ez2_player.cpp index 3e932210091..faaaf617d69 100644 --- a/sp/src/game/server/ez2/ez2_player.cpp +++ b/sp/src/game/server/ez2/ez2_player.cpp @@ -87,7 +87,7 @@ BEGIN_DATADESC(CEZ2_Player) DEFINE_INPUTFUNC( FIELD_VOID, "DisableDualWield", InputDisableDualWield ), END_DATADESC() -BEGIN_ENT_SCRIPTDESC( CEZ2_Player, CBasePlayer, "E:Z2's player entity." ) +BEGIN_ENT_SCRIPTDESC( CEZ2_Player, CHL2_Player, "E:Z2's player entity." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetNPCComponent, "GetNPCComponent", "Gets the player's NPC component." ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetStaringEntity, "GetStaringEntity", "Gets the player's staring entity." ) From cd19f0d4343fbbf131884158d417ee12281a3a73 Mon Sep 17 00:00:00 2001 From: 1upD <1upderek@gmail.com> Date: Sun, 9 Mar 2025 18:01:36 -0400 Subject: [PATCH 102/103] fix: Stunstick alt-fire checks damage filters and excludes barnacles --- sp/src/game/shared/hl2mp/weapon_stunstick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp index a10546b11f0..4bf25c4111a 100644 --- a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp +++ b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp @@ -460,7 +460,7 @@ void CWeaponStunStick::Hit( trace_t &traceHit, Activity nHitActivity, bool bIsSe // If the hit object is an NPC, and that NPC is now dead - become a server ragdoll and electrify! CAI_BaseNPC * pNPC = pHitEntity->MyNPCPointer(); - if ( flDamage > flBaseDamage && pHitEntity->IsNPC() && pNPC != NULL && pNPC->CanBecomeServerRagdoll() && !pNPC->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pNPC->m_iHealth - info.GetDamage() <= 0.0f) + if ( flDamage > flBaseDamage && pHitEntity->IsNPC() && pNPC != NULL && pNPC->CanBecomeServerRagdoll() && pNPC->PassesDamageFilter(info) && !pNPC->IsEFlagSet( EFL_NO_MEGAPHYSCANNON_RAGDOLL ) && pNPC->m_iHealth - info.GetDamage() <= 0.0f && pNPC->Classify() != CLASS_BARNACLE) { pNPC->BecomeRagdollBoogie( GetOwner(), info.GetDamageForce(), 5.0f, SF_RAGDOLL_BOOGIE_ELECTRICAL ); } From 2e15b588f9b81bb6af853eb7b02881f4ad672f37 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Thu, 27 Feb 2025 19:00:21 +0300 Subject: [PATCH 103/103] Ignore m_bUnableToFire in vehicle_drivable_apc --- sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp b/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp index fba67d42db3..68d248bc252 100644 --- a/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp +++ b/sp/src/game/server/Human_Error/vehicle_drivable_apc.cpp @@ -842,11 +842,15 @@ void CPropDrivableAPC::AimGunAt( Vector *endPos, float flInterval ) //DevMsg("newTargetPitch: %f\n", newTargetPitch); + // EZ2: Invalid check, this always resolves to true + // m_bUnableToFire is unused, ignore block +#if 0 // If the angles have been clamped, we're looking outside of our valid range if ( fabs(newTargetYaw-targetYaw) > 1e-4 || fabs(newTargetPitch-targetPitch) > 1e-4 ) { m_bUnableToFire = true; } +#endif targetYaw = newTargetYaw; targetPitch = newTargetPitch;