diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/server/util.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/server/util.cpp')
| -rw-r--r-- | sp/src/game/server/util.cpp | 6582 |
1 files changed, 3291 insertions, 3291 deletions
diff --git a/sp/src/game/server/util.cpp b/sp/src/game/server/util.cpp index 431c8561..b273520a 100644 --- a/sp/src/game/server/util.cpp +++ b/sp/src/game/server/util.cpp @@ -1,3291 +1,3291 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Utility code.
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "saverestore.h"
-#include "globalstate.h"
-#include <stdarg.h>
-#include "shake.h"
-#include "decals.h"
-#include "player.h"
-#include "gamerules.h"
-#include "entitylist.h"
-#include "bspfile.h"
-#include "mathlib/mathlib.h"
-#include "IEffects.h"
-#include "vstdlib/random.h"
-#include "soundflags.h"
-#include "ispatialpartition.h"
-#include "igamesystem.h"
-#include "saverestoretypes.h"
-#include "checksum_crc.h"
-#include "hierarchy.h"
-#include "iservervehicle.h"
-#include "te_effect_dispatch.h"
-#include "utldict.h"
-#include "collisionutils.h"
-#include "movevars_shared.h"
-#include "inetchannelinfo.h"
-#include "tier0/vprof.h"
-#include "ndebugoverlay.h"
-#include "engine/ivdebugoverlay.h"
-#include "datacache/imdlcache.h"
-#include "util.h"
-#include "cdll_int.h"
-
-#ifdef PORTAL
-#include "PortalSimulation.h"
-//#include "Portal_PhysicsEnvironmentMgr.h"
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
-extern short g_sModelIndexBloodDrop; // (in combatweapon.cpp) holds the sprite index for the initial blood
-extern short g_sModelIndexBloodSpray; // (in combatweapon.cpp) holds the sprite index for splattered blood
-
-#ifdef DEBUG
-void DBG_AssertFunction( bool fExpr, const char *szExpr, const char *szFile, int szLine, const char *szMessage )
-{
- if (fExpr)
- return;
- char szOut[512];
- if (szMessage != NULL)
- Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage);
- else
- Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine);
- Warning( szOut);
-}
-#endif // DEBUG
-
-
-//-----------------------------------------------------------------------------
-// Entity creation factory
-//-----------------------------------------------------------------------------
-class CEntityFactoryDictionary : public IEntityFactoryDictionary
-{
-public:
- CEntityFactoryDictionary();
-
- virtual void InstallFactory( IEntityFactory *pFactory, const char *pClassName );
- virtual IServerNetworkable *Create( const char *pClassName );
- virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable );
- virtual const char *GetCannonicalName( const char *pClassName );
- void ReportEntitySizes();
-
-private:
- IEntityFactory *FindFactory( const char *pClassName );
-public:
- CUtlDict< IEntityFactory *, unsigned short > m_Factories;
-};
-
-//-----------------------------------------------------------------------------
-// Singleton accessor
-//-----------------------------------------------------------------------------
-IEntityFactoryDictionary *EntityFactoryDictionary()
-{
- static CEntityFactoryDictionary s_EntityFactory;
- return &s_EntityFactory;
-}
-
-void DumpEntityFactories_f()
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- CEntityFactoryDictionary *dict = ( CEntityFactoryDictionary * )EntityFactoryDictionary();
- if ( dict )
- {
- for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) )
- {
- Warning( "%s\n", dict->m_Factories.GetElementName( i ) );
- }
- }
-}
-
-static ConCommand dumpentityfactories( "dumpentityfactories", DumpEntityFactories_f, "Lists all entity factory names.", FCVAR_GAMEDLL );
-
-
-//-----------------------------------------------------------------------------
-//
-//-----------------------------------------------------------------------------
-CON_COMMAND( dump_entity_sizes, "Print sizeof(entclass)" )
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- ((CEntityFactoryDictionary*)EntityFactoryDictionary())->ReportEntitySizes();
-}
-
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CEntityFactoryDictionary::CEntityFactoryDictionary() : m_Factories( true, 0, 128 )
-{
-}
-
-
-//-----------------------------------------------------------------------------
-// Finds a new factory
-//-----------------------------------------------------------------------------
-IEntityFactory *CEntityFactoryDictionary::FindFactory( const char *pClassName )
-{
- unsigned short nIndex = m_Factories.Find( pClassName );
- if ( nIndex == m_Factories.InvalidIndex() )
- return NULL;
- return m_Factories[nIndex];
-}
-
-
-//-----------------------------------------------------------------------------
-// Install a new factory
-//-----------------------------------------------------------------------------
-void CEntityFactoryDictionary::InstallFactory( IEntityFactory *pFactory, const char *pClassName )
-{
- Assert( FindFactory( pClassName ) == NULL );
- m_Factories.Insert( pClassName, pFactory );
-}
-
-
-//-----------------------------------------------------------------------------
-// Instantiate something using a factory
-//-----------------------------------------------------------------------------
-IServerNetworkable *CEntityFactoryDictionary::Create( const char *pClassName )
-{
- IEntityFactory *pFactory = FindFactory( pClassName );
- if ( !pFactory )
- {
- Warning("Attempted to create unknown entity type %s!\n", pClassName );
- return NULL;
- }
-#if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
- MEM_ALLOC_CREDIT_( m_Factories.GetElementName( m_Factories.Find( pClassName ) ) );
-#endif
- return pFactory->Create( pClassName );
-}
-
-//-----------------------------------------------------------------------------
-//
-//-----------------------------------------------------------------------------
-const char *CEntityFactoryDictionary::GetCannonicalName( const char *pClassName )
-{
- return m_Factories.GetElementName( m_Factories.Find( pClassName ) );
-}
-
-//-----------------------------------------------------------------------------
-// Destroy a networkable
-//-----------------------------------------------------------------------------
-void CEntityFactoryDictionary::Destroy( const char *pClassName, IServerNetworkable *pNetworkable )
-{
- IEntityFactory *pFactory = FindFactory( pClassName );
- if ( !pFactory )
- {
- Warning("Attempted to destroy unknown entity type %s!\n", pClassName );
- return;
- }
-
- pFactory->Destroy( pNetworkable );
-}
-
-//-----------------------------------------------------------------------------
-//
-//-----------------------------------------------------------------------------
-void CEntityFactoryDictionary::ReportEntitySizes()
-{
- for ( int i = m_Factories.First(); i != m_Factories.InvalidIndex(); i = m_Factories.Next( i ) )
- {
- Msg( " %s: %d", m_Factories.GetElementName( i ), m_Factories[i]->GetEntitySize() );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// class CFlaggedEntitiesEnum
-//-----------------------------------------------------------------------------
-
-CFlaggedEntitiesEnum::CFlaggedEntitiesEnum( CBaseEntity **pList, int listMax, int flagMask )
-{
- m_pList = pList;
- m_listMax = listMax;
- m_flagMask = flagMask;
- m_count = 0;
-}
-
-bool CFlaggedEntitiesEnum::AddToList( CBaseEntity *pEntity )
-{
- if ( m_count >= m_listMax )
- {
- AssertMsgOnce( 0, "reached enumerated list limit. Increase limit, decrease radius, or make it so entity flags will work for you" );
- return false;
- }
- m_pList[m_count] = pEntity;
- m_count++;
- return true;
-}
-
-IterationRetval_t CFlaggedEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity )
-{
- CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
- if ( pEntity )
- {
- if ( m_flagMask && !(pEntity->GetFlags() & m_flagMask) ) // Does it meet the criteria?
- return ITERATION_CONTINUE;
-
- if ( !AddToList( pEntity ) )
- return ITERATION_STOP;
- }
-
- return ITERATION_CONTINUE;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int UTIL_PrecacheDecal( const char *name, bool preload )
-{
- // If this is out of order, make sure to warn.
- if ( !CBaseEntity::IsPrecacheAllowed() )
- {
- if ( !engine->IsDecalPrecached( name ) )
- {
- Assert( !"UTIL_PrecacheDecal: too late" );
-
- Warning( "Late precache of %s\n", name );
- }
- }
-
- return engine->PrecacheDecal( name, preload );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-float UTIL_GetSimulationInterval()
-{
- if ( CBaseEntity::IsSimulatingOnAlternateTicks() )
- return ( TICK_INTERVAL * 2.0 );
- return TICK_INTERVAL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int UTIL_EntitiesInBox( const Vector &mins, const Vector &maxs, CFlaggedEntitiesEnum *pEnum )
-{
- partition->EnumerateElementsInBox( PARTITION_ENGINE_NON_STATIC_EDICTS, mins, maxs, false, pEnum );
- return pEnum->GetCount();
-}
-
-int UTIL_EntitiesAlongRay( const Ray_t &ray, CFlaggedEntitiesEnum *pEnum )
-{
- partition->EnumerateElementsAlongRay( PARTITION_ENGINE_NON_STATIC_EDICTS, ray, false, pEnum );
- return pEnum->GetCount();
-}
-
-int UTIL_EntitiesInSphere( const Vector ¢er, float radius, CFlaggedEntitiesEnum *pEnum )
-{
- partition->EnumerateElementsInSphere( PARTITION_ENGINE_NON_STATIC_EDICTS, center, radius, false, pEnum );
- return pEnum->GetCount();
-}
-
-CEntitySphereQuery::CEntitySphereQuery( const Vector ¢er, float radius, int flagMask )
-{
- m_listIndex = 0;
- m_listCount = UTIL_EntitiesInSphere( m_pList, ARRAYSIZE(m_pList), center, radius, flagMask );
-}
-
-CBaseEntity *CEntitySphereQuery::GetCurrentEntity()
-{
- if ( m_listIndex < m_listCount )
- return m_pList[m_listIndex];
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// Simple trace filter
-//-----------------------------------------------------------------------------
-class CTracePassFilter : public CTraceFilter
-{
-public:
- CTracePassFilter( IHandleEntity *pPassEnt ) : m_pPassEnt( pPassEnt ) {}
-
- bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
- {
- if ( !StandardFilterRules( pHandleEntity, contentsMask ) )
- return false;
-
- if (!PassServerEntityFilter( pHandleEntity, m_pPassEnt ))
- return false;
-
- return true;
- }
-
-private:
- IHandleEntity *m_pPassEnt;
-};
-
-
-//-----------------------------------------------------------------------------
-// Drops an entity onto the floor
-//-----------------------------------------------------------------------------
-int UTIL_DropToFloor( CBaseEntity *pEntity, unsigned int mask, CBaseEntity *pIgnore )
-{
- // Assume no ground
- pEntity->SetGroundEntity( NULL );
-
- Assert( pEntity );
-
- trace_t trace;
-
-#ifndef HL2MP
- // HACK: is this really the only sure way to detect crossing a terrain boundry?
- UTIL_TraceEntity( pEntity, pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin(), mask, pIgnore, pEntity->GetCollisionGroup(), &trace );
- if (trace.fraction == 0.0)
- return -1;
-#endif // HL2MP
-
- UTIL_TraceEntity( pEntity, pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() - Vector(0,0,256), mask, pIgnore, pEntity->GetCollisionGroup(), &trace );
-
- if (trace.allsolid)
- return -1;
-
- if (trace.fraction == 1)
- return 0;
-
- pEntity->SetAbsOrigin( trace.endpos );
- pEntity->SetGroundEntity( trace.m_pEnt );
-
- return 1;
-}
-
-//-----------------------------------------------------------------------------
-// Returns false if any part of the bottom of the entity is off an edge that
-// is not a staircase.
-//-----------------------------------------------------------------------------
-bool UTIL_CheckBottom( CBaseEntity *pEntity, ITraceFilter *pTraceFilter, float flStepSize )
-{
- Vector mins, maxs, start, stop;
- trace_t trace;
- int x, y;
- float mid, bottom;
-
- Assert( pEntity );
-
- CTracePassFilter traceFilter(pEntity);
- if ( !pTraceFilter )
- {
- pTraceFilter = &traceFilter;
- }
-
- unsigned int mask = pEntity->PhysicsSolidMaskForEntity();
-
- VectorAdd (pEntity->GetAbsOrigin(), pEntity->WorldAlignMins(), mins);
- VectorAdd (pEntity->GetAbsOrigin(), pEntity->WorldAlignMaxs(), maxs);
-
- // if all of the points under the corners are solid world, don't bother
- // with the tougher checks
- // the corners must be within 16 of the midpoint
- start[2] = mins[2] - 1;
- for (x=0 ; x<=1 ; x++)
- {
- for (y=0 ; y<=1 ; y++)
- {
- start[0] = x ? maxs[0] : mins[0];
- start[1] = y ? maxs[1] : mins[1];
- if (enginetrace->GetPointContents(start) != CONTENTS_SOLID)
- goto realcheck;
- }
- }
- return true; // we got out easy
-
-realcheck:
- // check it for real...
- start[2] = mins[2] + flStepSize; // seems to help going up/down slopes.
-
- // the midpoint must be within 16 of the bottom
- start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
- start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
- stop[2] = start[2] - 2*flStepSize;
-
- UTIL_TraceLine( start, stop, mask, pTraceFilter, &trace );
-
- if (trace.fraction == 1.0)
- return false;
- mid = bottom = trace.endpos[2];
-
- // the corners must be within 16 of the midpoint
- for (x=0 ; x<=1 ; x++)
- {
- for (y=0 ; y<=1 ; y++)
- {
- start[0] = stop[0] = x ? maxs[0] : mins[0];
- start[1] = stop[1] = y ? maxs[1] : mins[1];
-
- UTIL_TraceLine( start, stop, mask, pTraceFilter, &trace );
-
- if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
- bottom = trace.endpos[2];
- if (trace.fraction == 1.0 || mid - trace.endpos[2] > flStepSize)
- return false;
- }
- }
- return true;
-}
-
-
-
-bool g_bDisableEhandleAccess = false;
-bool g_bReceivedChainedUpdateOnRemove = false;
-//-----------------------------------------------------------------------------
-// Purpose: Sets the entity up for deletion. Entity will not actually be deleted
-// until the next frame, so there can be no pointer errors.
-// Input : *oldObj - object to delete
-//-----------------------------------------------------------------------------
-void UTIL_Remove( IServerNetworkable *oldObj )
-{
- CServerNetworkProperty* pProp = static_cast<CServerNetworkProperty*>( oldObj );
- if ( !pProp || pProp->IsMarkedForDeletion() )
- return;
-
- if ( PhysIsInCallback() )
- {
- // This assert means that someone is deleting an entity inside a callback. That isn't supported so
- // this code will defer the deletion of that object until the end of the current physics simulation frame
- // Since this is hidden from the calling code it's preferred to call PhysCallbackRemove() directly from the caller
- // in case the deferred delete will have unwanted results (like continuing to receive callbacks). That will make it
- // obvious why the unwanted results are happening so the caller can handle them appropriately. (some callbacks can be masked
- // or the calling entity can be flagged to filter them in most cases)
- Assert(0);
- PhysCallbackRemove(oldObj);
- return;
- }
-
- // mark it for deletion
- pProp->MarkForDeletion( );
-
- CBaseEntity *pBaseEnt = oldObj->GetBaseEntity();
- if ( pBaseEnt )
- {
-#ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment
- CPortalSimulator::Pre_UTIL_Remove( pBaseEnt );
-#endif
- g_bReceivedChainedUpdateOnRemove = false;
- pBaseEnt->UpdateOnRemove();
-
- Assert( g_bReceivedChainedUpdateOnRemove );
-
- // clear oldObj targetname / other flags now
- pBaseEnt->SetName( NULL_STRING );
-
-#ifdef PORTAL
- CPortalSimulator::Post_UTIL_Remove( pBaseEnt );
-#endif
- }
-
- gEntList.AddToDeleteList( oldObj );
-}
-
-void UTIL_Remove( CBaseEntity *oldObj )
-{
- if ( !oldObj )
- return;
- UTIL_Remove( oldObj->NetworkProp() );
-}
-
-static int s_RemoveImmediateSemaphore = 0;
-void UTIL_DisableRemoveImmediate()
-{
- s_RemoveImmediateSemaphore++;
-}
-void UTIL_EnableRemoveImmediate()
-{
- s_RemoveImmediateSemaphore--;
- Assert(s_RemoveImmediateSemaphore>=0);
-}
-//-----------------------------------------------------------------------------
-// Purpose: deletes an entity, without any delay. WARNING! Only use this when sure
-// no pointers rely on this entity.
-// Input : *oldObj - the entity to delete
-//-----------------------------------------------------------------------------
-void UTIL_RemoveImmediate( CBaseEntity *oldObj )
-{
- // valid pointer or already removed?
- if ( !oldObj || oldObj->IsEFlagSet(EFL_KILLME) )
- return;
-
- if ( s_RemoveImmediateSemaphore )
- {
- UTIL_Remove(oldObj);
- return;
- }
-
-#ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment
- CPortalSimulator::Pre_UTIL_Remove( oldObj );
-#endif
-
- oldObj->AddEFlags( EFL_KILLME ); // Make sure to ignore further calls into here or UTIL_Remove.
-
- g_bReceivedChainedUpdateOnRemove = false;
- oldObj->UpdateOnRemove();
- Assert( g_bReceivedChainedUpdateOnRemove );
-
- // Entities shouldn't reference other entities in their destructors
- // that type of code should only occur in an UpdateOnRemove call
- g_bDisableEhandleAccess = true;
- delete oldObj;
- g_bDisableEhandleAccess = false;
-
-#ifdef PORTAL
- CPortalSimulator::Post_UTIL_Remove( oldObj );
-#endif
-}
-
-
-// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected
-// otherwise returns NULL
-// Index is 1 based
-CBasePlayer *UTIL_PlayerByIndex( int playerIndex )
-{
- CBasePlayer *pPlayer = NULL;
-
- if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients )
- {
- edict_t *pPlayerEdict = INDEXENT( playerIndex );
- if ( pPlayerEdict && !pPlayerEdict->IsFree() )
- {
- pPlayer = (CBasePlayer*)GetContainingEntity( pPlayerEdict );
- }
- }
-
- return pPlayer;
-}
-
-CBasePlayer* UTIL_PlayerByName( const char *name )
-{
- if ( !name || !name[0] )
- return NULL;
-
- for (int i = 1; i<=gpGlobals->maxClients; i++ )
- {
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
-
- if ( !pPlayer )
- continue;
-
- if ( !pPlayer->IsConnected() )
- continue;
-
- if ( Q_stricmp( pPlayer->GetPlayerName(), name ) == 0 )
- {
- return pPlayer;
- }
- }
-
- return NULL;
-}
-
-CBasePlayer* UTIL_PlayerByUserId( int userID )
-{
- for (int i = 1; i<=gpGlobals->maxClients; i++ )
- {
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
-
- if ( !pPlayer )
- continue;
-
- if ( !pPlayer->IsConnected() )
- continue;
-
- if ( engine->GetPlayerUserId(pPlayer->edict()) == userID )
- {
- return pPlayer;
- }
- }
-
- return NULL;
-}
-
-//
-// Return the local player.
-// If this is a multiplayer game, return NULL.
-//
-CBasePlayer *UTIL_GetLocalPlayer( void )
-{
- if ( gpGlobals->maxClients > 1 )
- {
- if ( developer.GetBool() )
- {
- Assert( !"UTIL_GetLocalPlayer" );
-
-#ifdef DEBUG
- Warning( "UTIL_GetLocalPlayer() called in multiplayer game.\n" );
-#endif
- }
-
- return NULL;
- }
-
- return UTIL_PlayerByIndex( 1 );
-}
-
-//
-// Get the local player on a listen server - this is for multiplayer use only
-//
-CBasePlayer *UTIL_GetListenServerHost( void )
-{
- // no "local player" if this is a dedicated server or a single player game
- if (engine->IsDedicatedServer())
- {
- Assert( !"UTIL_GetListenServerHost" );
- Warning( "UTIL_GetListenServerHost() called from a dedicated server or single-player game.\n" );
- return NULL;
- }
-
- return UTIL_PlayerByIndex( 1 );
-}
-
-
-//--------------------------------------------------------------------------------------------------------------
-/**
- * Returns true if the command was issued by the listenserver host, or by the dedicated server, via rcon or the server console.
- * This is valid during ConCommand execution.
- */
-bool UTIL_IsCommandIssuedByServerAdmin( void )
-{
- int issuingPlayerIndex = UTIL_GetCommandClientIndex();
-
- if ( engine->IsDedicatedServer() && issuingPlayerIndex > 0 )
- return false;
-
-#if defined( REPLAY_ENABLED )
- // entity 1 is replay?
- player_info_t pi;
- bool bPlayerIsReplay = engine->GetPlayerInfo( 1, &pi ) && pi.isreplay;
-#else
- bool bPlayerIsReplay = false;
-#endif
-
- if ( bPlayerIsReplay )
- {
- if ( issuingPlayerIndex > 2 )
- return false;
- }
- else if ( issuingPlayerIndex > 1 )
- {
- return false;
- }
-
- return true;
-}
-
-
-//--------------------------------------------------------------------------------------------------------------
-/**
- * Returns a CBaseEntity pointer by entindex. Index is 1 based.
- */
-CBaseEntity *UTIL_EntityByIndex( int entityIndex )
-{
- CBaseEntity *entity = NULL;
-
- if ( entityIndex > 0 )
- {
- edict_t *edict = INDEXENT( entityIndex );
- if ( edict && !edict->IsFree() )
- {
- entity = GetContainingEntity( edict );
- }
- }
-
- return entity;
-}
-
-
-int ENTINDEX( CBaseEntity *pEnt )
-{
- // This works just like ENTINDEX for edicts.
- if ( pEnt )
- return pEnt->entindex();
- else
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : playerIndex -
-// ping -
-// packetloss -
-//-----------------------------------------------------------------------------
-void UTIL_GetPlayerConnectionInfo( int playerIndex, int& ping, int &packetloss )
-{
- CBasePlayer *player = UTIL_PlayerByIndex( playerIndex );
-
- INetChannelInfo *nci = engine->GetPlayerNetInfo(playerIndex);
-
- if ( nci && player && !player->IsBot() )
- {
- float latency = nci->GetAvgLatency( FLOW_OUTGOING ); // in seconds
-
- // that should be the correct latency, we assume that cmdrate is higher
- // then updaterate, what is the case for default settings
- const char * szCmdRate = engine->GetClientConVarValue( playerIndex, "cl_cmdrate" );
-
- int nCmdRate = MAX( 1, Q_atoi( szCmdRate ) );
- latency -= (0.5f/nCmdRate) + TICKS_TO_TIME( 1.0f ); // correct latency
-
- // in GoldSrc we had a different, not fixed tickrate. so we have to adjust
- // Source pings by half a tick to match the old GoldSrc pings.
- latency -= TICKS_TO_TIME( 0.5f );
-
- ping = latency * 1000.0f; // as msecs
- ping = clamp( ping, 5, 1000 ); // set bounds, dont show pings under 5 msecs
-
- packetloss = 100.0f * nci->GetAvgLoss( FLOW_INCOMING ); // loss in percentage
- packetloss = clamp( packetloss, 0, 100 );
- }
- else
- {
- ping = 0;
- packetloss = 0;
- }
-}
-
-static unsigned short FixedUnsigned16( float value, float scale )
-{
- int output;
-
- output = value * scale;
- if ( output < 0 )
- output = 0;
- if ( output > 0xFFFF )
- output = 0xFFFF;
-
- return (unsigned short)output;
-}
-
-
-//-----------------------------------------------------------------------------
-// Compute shake amplitude
-//-----------------------------------------------------------------------------
-inline float ComputeShakeAmplitude( const Vector ¢er, const Vector &shakePt, float amplitude, float radius )
-{
- if ( radius <= 0 )
- return amplitude;
-
- float localAmplitude = -1;
- Vector delta = center - shakePt;
- float distance = delta.Length();
-
- if ( distance <= radius )
- {
- // Make the amplitude fall off over distance
- float flPerc = 1.0 - (distance / radius);
- localAmplitude = amplitude * flPerc;
- }
-
- return localAmplitude;
-}
-
-
-//-----------------------------------------------------------------------------
-// Transmits the actual shake event
-//-----------------------------------------------------------------------------
-inline void TransmitShakeEvent( CBasePlayer *pPlayer, float localAmplitude, float frequency, float duration, ShakeCommand_t eCommand )
-{
- if (( localAmplitude > 0 ) || ( eCommand == SHAKE_STOP ))
- {
- if ( eCommand == SHAKE_STOP )
- localAmplitude = 0;
-
- CSingleUserRecipientFilter user( pPlayer );
- user.MakeReliable();
- UserMessageBegin( user, "Shake" );
- WRITE_BYTE( eCommand ); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE)
- WRITE_FLOAT( localAmplitude ); // shake magnitude/amplitude
- WRITE_FLOAT( frequency ); // shake noise frequency
- WRITE_FLOAT( duration ); // shake lasts this long
- MessageEnd();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Shake the screen of all clients within radius.
-// radius == 0, shake all clients
-// UNDONE: Fix falloff model (disabled)?
-// UNDONE: Affect user controls?
-// Input : center - Center of screen shake, radius is measured from here.
-// amplitude - Amplitude of shake
-// frequency -
-// duration - duration of shake in seconds.
-// radius - Radius of effect, 0 shakes all clients.
-// command - One of the following values:
-// SHAKE_START - starts the screen shake for all players within the radius
-// SHAKE_STOP - stops the screen shake for all players within the radius
-// SHAKE_AMPLITUDE - modifies the amplitude of the screen shake
-// for all players within the radius
-// SHAKE_FREQUENCY - modifies the frequency of the screen shake
-// for all players within the radius
-// bAirShake - if this is false, then it will only shake players standing on the ground.
-//-----------------------------------------------------------------------------
-const float MAX_SHAKE_AMPLITUDE = 16.0f;
-void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake )
-{
- int i;
- float localAmplitude;
-
- if ( amplitude > MAX_SHAKE_AMPLITUDE )
- {
- amplitude = MAX_SHAKE_AMPLITUDE;
- }
- for ( i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
-
- //
- // Only start shakes for players that are on the ground unless doing an air shake.
- //
- if ( !pPlayer || (!bAirShake && (eCommand == SHAKE_START) && !(pPlayer->GetFlags() & FL_ONGROUND)) )
- {
- continue;
- }
-
- localAmplitude = ComputeShakeAmplitude( center, pPlayer->WorldSpaceCenter(), amplitude, radius );
-
- // This happens if the player is outside the radius, in which case we should ignore
- // all commands
- if (localAmplitude < 0)
- continue;
-
- TransmitShakeEvent( (CBasePlayer *)pPlayer, localAmplitude, frequency, duration, eCommand );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Shake an object and all players on or near it
-//-----------------------------------------------------------------------------
-void UTIL_ScreenShakeObject( CBaseEntity *pEnt, const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake )
-{
- int i;
- float localAmplitude;
-
- CBaseEntity *pHighestParent = pEnt->GetRootMoveParent();
- for ( i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
- if (!pPlayer)
- continue;
-
- // Shake the object, or anything hierarchically attached to it at maximum amplitude
- localAmplitude = 0;
- if (pHighestParent == pPlayer->GetRootMoveParent())
- {
- localAmplitude = amplitude;
- }
- else if ((pPlayer->GetFlags() & FL_ONGROUND) && (pPlayer->GetGroundEntity()->GetRootMoveParent() == pHighestParent))
- {
- // If the player is standing on the object, use maximum amplitude
- localAmplitude = amplitude;
- }
- else
- {
- // Only shake players that are on the ground.
- if ( !bAirShake && !(pPlayer->GetFlags() & FL_ONGROUND) )
- {
- continue;
- }
-
- localAmplitude = ComputeShakeAmplitude( center, pPlayer->WorldSpaceCenter(), amplitude, radius );
-
- // This happens if the player is outside the radius,
- // in which case we should ignore all commands
- if (localAmplitude < 0)
- continue;
- }
-
- TransmitShakeEvent( (CBasePlayer *)pPlayer, localAmplitude, frequency, duration, eCommand );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Punches the view of all clients within radius.
-// If radius is 0, punches all clients.
-// Input : center - Center of punch, radius is measured from here.
-// radius - Radius of effect, 0 punches all clients.
-// bInAir - if this is false, then it will only punch players standing on the ground.
-//-----------------------------------------------------------------------------
-void UTIL_ViewPunch( const Vector ¢er, QAngle angPunch, float radius, bool bInAir )
-{
- for ( int i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
-
- //
- // Only apply the punch to players that are on the ground unless doing an air punch.
- //
- if ( !pPlayer || (!bInAir && !(pPlayer->GetFlags() & FL_ONGROUND)) )
- {
- continue;
- }
-
- QAngle angTemp = angPunch;
-
- if ( radius > 0 )
- {
- Vector delta = center - pPlayer->GetAbsOrigin();
- float distance = delta.Length();
-
- if ( distance <= radius )
- {
- // Make the punch amplitude fall off over distance.
- float flPerc = 1.0 - (distance / radius);
- angTemp *= flPerc;
- }
- else
- {
- continue;
- }
- }
-
- pPlayer->ViewPunch( angTemp );
- }
-}
-
-
-void UTIL_ScreenFadeBuild( ScreenFade_t &fade, const color32 &color, float fadeTime, float fadeHold, int flags )
-{
- fade.duration = FixedUnsigned16( fadeTime, 1<<SCREENFADE_FRACBITS ); // 7.9 fixed
- fade.holdTime = FixedUnsigned16( fadeHold, 1<<SCREENFADE_FRACBITS ); // 7.9 fixed
- fade.r = color.r;
- fade.g = color.g;
- fade.b = color.b;
- fade.a = color.a;
- fade.fadeFlags = flags;
-}
-
-
-void UTIL_ScreenFadeWrite( const ScreenFade_t &fade, CBaseEntity *pEntity )
-{
- if ( !pEntity || !pEntity->IsNetClient() )
- return;
-
- CSingleUserRecipientFilter user( (CBasePlayer *)pEntity );
- user.MakeReliable();
-
- UserMessageBegin( user, "Fade" ); // use the magic #1 for "one client"
- WRITE_SHORT( fade.duration ); // fade lasts this long
- WRITE_SHORT( fade.holdTime ); // fade lasts this long
- WRITE_SHORT( fade.fadeFlags ); // fade type (in / out)
- WRITE_BYTE( fade.r ); // fade red
- WRITE_BYTE( fade.g ); // fade green
- WRITE_BYTE( fade.b ); // fade blue
- WRITE_BYTE( fade.a ); // fade blue
- MessageEnd();
-}
-
-
-void UTIL_ScreenFadeAll( const color32 &color, float fadeTime, float fadeHold, int flags )
-{
- int i;
- ScreenFade_t fade;
-
-
- UTIL_ScreenFadeBuild( fade, color, fadeTime, fadeHold, flags );
-
- for ( i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
-
- UTIL_ScreenFadeWrite( fade, pPlayer );
- }
-}
-
-
-void UTIL_ScreenFade( CBaseEntity *pEntity, const color32 &color, float fadeTime, float fadeHold, int flags )
-{
- ScreenFade_t fade;
-
- UTIL_ScreenFadeBuild( fade, color, fadeTime, fadeHold, flags );
- UTIL_ScreenFadeWrite( fade, pEntity );
-}
-
-
-void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage )
-{
- CRecipientFilter filter;
-
- if( pToPlayer )
- {
- filter.AddRecipient( pToPlayer );
- }
- else
- {
- filter.AddAllPlayers();
- }
-
- filter.MakeReliable();
-
- UserMessageBegin( filter, "HudMsg" );
- WRITE_BYTE ( textparms.channel & 0xFF );
- WRITE_FLOAT( textparms.x );
- WRITE_FLOAT( textparms.y );
- WRITE_BYTE ( textparms.r1 );
- WRITE_BYTE ( textparms.g1 );
- WRITE_BYTE ( textparms.b1 );
- WRITE_BYTE ( textparms.a1 );
- WRITE_BYTE ( textparms.r2 );
- WRITE_BYTE ( textparms.g2 );
- WRITE_BYTE ( textparms.b2 );
- WRITE_BYTE ( textparms.a2 );
- WRITE_BYTE ( textparms.effect );
- WRITE_FLOAT( textparms.fadeinTime );
- WRITE_FLOAT( textparms.fadeoutTime );
- WRITE_FLOAT( textparms.holdTime );
- WRITE_FLOAT( textparms.fxTime );
- WRITE_STRING( pMessage );
- MessageEnd();
-}
-
-void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage )
-{
- UTIL_HudMessage( NULL, textparms, pMessage );
-}
-
-void UTIL_HudHintText( CBaseEntity *pEntity, const char *pMessage )
-{
- if ( !pEntity )
- return;
-
- CSingleUserRecipientFilter user( (CBasePlayer *)pEntity );
- user.MakeReliable();
- UserMessageBegin( user, "KeyHintText" );
- WRITE_BYTE( 1 ); // one string
- WRITE_STRING( pMessage );
- MessageEnd();
-}
-
-void UTIL_ClientPrintFilter( IRecipientFilter& filter, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
-{
- UserMessageBegin( filter, "TextMsg" );
- WRITE_BYTE( msg_dest );
- WRITE_STRING( msg_name );
-
- if ( param1 )
- WRITE_STRING( param1 );
- else
- WRITE_STRING( "" );
-
- if ( param2 )
- WRITE_STRING( param2 );
- else
- WRITE_STRING( "" );
-
- if ( param3 )
- WRITE_STRING( param3 );
- else
- WRITE_STRING( "" );
-
- if ( param4 )
- WRITE_STRING( param4 );
- else
- WRITE_STRING( "" );
-
- MessageEnd();
-}
-
-void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
-{
- CReliableBroadcastRecipientFilter filter;
-
- UTIL_ClientPrintFilter( filter, msg_dest, msg_name, param1, param2, param3, param4 );
-}
-
-void ClientPrint( CBasePlayer *player, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
-{
- if ( !player )
- return;
-
- CSingleUserRecipientFilter user( player );
- user.MakeReliable();
-
- UTIL_ClientPrintFilter( user, msg_dest, msg_name, param1, param2, param3, param4 );
-}
-
-void UTIL_SayTextFilter( IRecipientFilter& filter, const char *pText, CBasePlayer *pPlayer, bool bChat )
-{
- UserMessageBegin( filter, "SayText" );
- if ( pPlayer )
- {
- WRITE_BYTE( pPlayer->entindex() );
- }
- else
- {
- WRITE_BYTE( 0 ); // world, dedicated server says
- }
- WRITE_STRING( pText );
- WRITE_BYTE( bChat );
- MessageEnd();
-}
-
-void UTIL_SayText2Filter( IRecipientFilter& filter, CBasePlayer *pEntity, bool bChat, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
-{
- UserMessageBegin( filter, "SayText2" );
- if ( pEntity )
- {
- WRITE_BYTE( pEntity->entindex() );
- }
- else
- {
- WRITE_BYTE( 0 ); // world, dedicated server says
- }
-
- WRITE_BYTE( bChat );
-
- WRITE_STRING( msg_name );
-
- if ( param1 )
- WRITE_STRING( param1 );
- else
- WRITE_STRING( "" );
-
- if ( param2 )
- WRITE_STRING( param2 );
- else
- WRITE_STRING( "" );
-
- if ( param3 )
- WRITE_STRING( param3 );
- else
- WRITE_STRING( "" );
-
- if ( param4 )
- WRITE_STRING( param4 );
- else
- WRITE_STRING( "" );
-
- MessageEnd();
-}
-
-void UTIL_SayText( const char *pText, CBasePlayer *pToPlayer )
-{
- if ( !pToPlayer->IsNetClient() )
- return;
-
- CSingleUserRecipientFilter user( pToPlayer );
- user.MakeReliable();
-
- UTIL_SayTextFilter( user, pText, pToPlayer, false );
-}
-
-void UTIL_SayTextAll( const char *pText, CBasePlayer *pPlayer, bool bChat )
-{
- CReliableBroadcastRecipientFilter filter;
- UTIL_SayTextFilter( filter, pText, pPlayer, bChat );
-}
-
-void UTIL_ShowMessage( const char *pString, CBasePlayer *pPlayer )
-{
- CRecipientFilter filter;
-
- if ( pPlayer )
- {
- filter.AddRecipient( pPlayer );
- }
- else
- {
- filter.AddAllPlayers();
- }
-
- filter.MakeReliable();
-
- UserMessageBegin( filter, "HudText" );
- WRITE_STRING( pString );
- MessageEnd();
-}
-
-
-void UTIL_ShowMessageAll( const char *pString )
-{
- UTIL_ShowMessage( pString, NULL );
-}
-
-// So we always return a valid surface
-static csurface_t g_NullSurface = { "**empty**", 0 };
-
-void UTIL_SetTrace(trace_t& trace, const Ray_t &ray, edict_t *ent, float fraction,
- int hitgroup, unsigned int contents, const Vector& normal, float intercept )
-{
- trace.startsolid = (fraction == 0.0f);
- trace.fraction = fraction;
- VectorCopy( ray.m_Start, trace.startpos );
- VectorMA( ray.m_Start, fraction, ray.m_Delta, trace.endpos );
- VectorCopy( normal, trace.plane.normal );
- trace.plane.dist = intercept;
- trace.m_pEnt = CBaseEntity::Instance( ent );
- trace.hitgroup = hitgroup;
- trace.surface = g_NullSurface;
- trace.contents = contents;
-}
-
-void UTIL_ClearTrace( trace_t &trace )
-{
- memset( &trace, 0, sizeof(trace));
- trace.fraction = 1.f;
- trace.fractionleftsolid = 0;
- trace.surface = g_NullSurface;
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Sets the entity size
-//-----------------------------------------------------------------------------
-static void SetMinMaxSize (CBaseEntity *pEnt, const Vector& mins, const Vector& maxs )
-{
- for ( int i=0 ; i<3 ; i++ )
- {
- if ( mins[i] > maxs[i] )
- {
- Error( "%s: backwards mins/maxs", ( pEnt ) ? pEnt->GetDebugName() : "<NULL>" );
- }
- }
-
- Assert( pEnt );
-
- pEnt->SetCollisionBounds( mins, maxs );
-}
-
-
-//-----------------------------------------------------------------------------
-// Sets the model size
-//-----------------------------------------------------------------------------
-void UTIL_SetSize( CBaseEntity *pEnt, const Vector &vecMin, const Vector &vecMax )
-{
- SetMinMaxSize (pEnt, vecMin, vecMax);
-}
-
-
-//-----------------------------------------------------------------------------
-// Sets the model to be associated with an entity
-//-----------------------------------------------------------------------------
-void UTIL_SetModel( CBaseEntity *pEntity, const char *pModelName )
-{
- // check to see if model was properly precached
- int i = modelinfo->GetModelIndex( pModelName );
- if ( i == -1 )
- {
- Error("%i/%s - %s: UTIL_SetModel: not precached: %s\n", pEntity->entindex(),
- STRING( pEntity->GetEntityName() ),
- pEntity->GetClassname(), pModelName);
- }
-
- CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
- if ( pAnimating )
- {
- pAnimating->m_nForceBone = 0;
- }
-
- pEntity->SetModelName( AllocPooledString( pModelName ) );
- pEntity->SetModelIndex( i ) ;
- SetMinMaxSize(pEntity, vec3_origin, vec3_origin);
- pEntity->SetCollisionBoundsFromModel();
-}
-
-
-void UTIL_SetOrigin( CBaseEntity *entity, const Vector &vecOrigin, bool bFireTriggers )
-{
- entity->SetLocalOrigin( vecOrigin );
- if ( bFireTriggers )
- {
- entity->PhysicsTouchTriggers();
- }
-}
-
-
-void UTIL_ParticleEffect( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount )
-{
- Msg( "UTIL_ParticleEffect: Disabled\n" );
-}
-
-void UTIL_Smoke( const Vector &origin, const float scale, const float framerate )
-{
- g_pEffects->Smoke( origin, g_sModelIndexSmoke, scale, framerate );
-}
-
-// snaps a vector to the nearest axis vector (if within epsilon)
-void UTIL_SnapDirectionToAxis( Vector &direction, float epsilon )
-{
- float proj = 1 - epsilon;
- for ( int i = 0; i < 3; i ++ )
- {
- if ( fabs(direction[i]) > proj )
- {
- // snap to axis unit vector
- if ( direction[i] < 0 )
- direction[i] = -1.0f;
- else
- direction[i] = 1.0f;
- direction[(i+1)%3] = 0;
- direction[(i+2)%3] = 0;
- return;
- }
- }
-}
-
-char *UTIL_VarArgs( const char *format, ... )
-{
- va_list argptr;
- static char string[1024];
-
- va_start (argptr, format);
- Q_vsnprintf(string, sizeof(string), format,argptr);
- va_end (argptr);
-
- return string;
-}
-
-bool UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator)
-{
- if (sMaster != NULL_STRING)
- {
- CBaseEntity *pMaster = gEntList.FindEntityByName( NULL, sMaster, NULL, pActivator );
-
- if ( pMaster && (pMaster->ObjectCaps() & FCAP_MASTER) )
- {
- return pMaster->IsTriggered( pActivator );
- }
-
- Warning( "Master was null or not a master!\n");
- }
-
- // if this isn't a master entity, just say yes.
- return true;
-}
-
-void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount )
-{
- if ( !UTIL_ShouldShowBlood( color ) )
- return;
-
- if ( g_Language.GetInt() == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED )
- color = 0;
-
- CPVSFilter filter( origin );
- te->BloodStream( filter, 0.0, &origin, &direction, 247, 63, 14, 255, MIN( amount, 255 ) );
-}
-
-
-Vector UTIL_RandomBloodVector( void )
-{
- Vector direction;
-
- direction.x = random->RandomFloat ( -1, 1 );
- direction.y = random->RandomFloat ( -1, 1 );
- direction.z = random->RandomFloat ( 0, 1 );
-
- return direction;
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose : Creates both an decal and any associated impact effects (such
-// as flecks) for the given iDamageType and the trace's end position
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void UTIL_ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
-{
- CBaseEntity *pEntity = pTrace->m_pEnt;
-
- // Is the entity valid, is the surface sky?
- if ( !pEntity || !UTIL_IsValidEntity( pEntity ) || (pTrace->surface.flags & SURF_SKY) )
- return;
-
- if ( pTrace->fraction == 1.0 )
- return;
-
- pEntity->ImpactTrace( pTrace, iDamageType, pCustomImpactName );
-}
-
-/*
-==============
-UTIL_PlayerDecalTrace
-
-A player is trying to apply his custom decal for the spray can.
-Tell connected clients to display it, or use the default spray can decal
-if the custom can't be loaded.
-==============
-*/
-void UTIL_PlayerDecalTrace( trace_t *pTrace, int playernum )
-{
- if (pTrace->fraction == 1.0)
- return;
-
- CBroadcastRecipientFilter filter;
-
- te->PlayerDecal( filter, 0.0,
- &pTrace->endpos, playernum, pTrace->m_pEnt->entindex() );
-}
-
-bool UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 )
-{
- // Everyone matches unless it's teamplay
- if ( !g_pGameRules->IsTeamplay() )
- return true;
-
- // Both on a team?
- if ( *pTeamName1 != 0 && *pTeamName2 != 0 )
- {
- if ( !stricmp( pTeamName1, pTeamName2 ) ) // Same Team?
- return true;
- }
-
- return false;
-}
-
-
-void UTIL_AxisStringToPointPoint( Vector &start, Vector &end, const char *pString )
-{
- char tmpstr[256];
-
- Q_strncpy( tmpstr, pString, sizeof(tmpstr) );
- char *pVec = strtok( tmpstr, "," );
- int i = 0;
- while ( pVec != NULL && *pVec )
- {
- if ( i == 0 )
- {
- UTIL_StringToVector( start.Base(), pVec );
- i++;
- }
- else
- {
- UTIL_StringToVector( end.Base(), pVec );
- }
- pVec = strtok( NULL, "," );
- }
-}
-
-void UTIL_AxisStringToPointDir( Vector &start, Vector &dir, const char *pString )
-{
- Vector end;
- UTIL_AxisStringToPointPoint( start, end, pString );
- dir = end - start;
- VectorNormalize(dir);
-}
-
-void UTIL_AxisStringToUnitDir( Vector &dir, const char *pString )
-{
- Vector start;
- UTIL_AxisStringToPointDir( start, dir, pString );
-}
-
-/*
-==================================================
-UTIL_ClipPunchAngleOffset
-==================================================
-*/
-
-void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip )
-{
- QAngle final = in + punch;
-
- //Clip each component
- for ( int i = 0; i < 3; i++ )
- {
- if ( final[i] > clip[i] )
- {
- final[i] = clip[i];
- }
- else if ( final[i] < -clip[i] )
- {
- final[i] = -clip[i];
- }
-
- //Return the result
- in[i] = final[i] - punch[i];
- }
-}
-
-float UTIL_WaterLevel( const Vector &position, float minz, float maxz )
-{
- Vector midUp = position;
- midUp.z = minz;
-
- if ( !(UTIL_PointContents(midUp) & MASK_WATER) )
- return minz;
-
- midUp.z = maxz;
- if ( UTIL_PointContents(midUp) & MASK_WATER )
- return maxz;
-
- float diff = maxz - minz;
- while (diff > 1.0)
- {
- midUp.z = minz + diff/2.0;
- if ( UTIL_PointContents(midUp) & MASK_WATER )
- {
- minz = midUp.z;
- }
- else
- {
- maxz = midUp.z;
- }
- diff = maxz - minz;
- }
-
- return midUp.z;
-}
-
-
-//-----------------------------------------------------------------------------
-// Like UTIL_WaterLevel, but *way* less expensive.
-// I didn't replace UTIL_WaterLevel everywhere to avoid breaking anything.
-//-----------------------------------------------------------------------------
-class CWaterTraceFilter : public CTraceFilter
-{
-public:
- bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
- {
- CBaseEntity *pCollide = EntityFromEntityHandle( pHandleEntity );
-
- // Static prop case...
- if ( !pCollide )
- return false;
-
- // Only impact water stuff...
- if ( pCollide->GetSolidFlags() & FSOLID_VOLUME_CONTENTS )
- return true;
-
- return false;
- }
-};
-
-float UTIL_FindWaterSurface( const Vector &position, float minz, float maxz )
-{
- Vector vecStart, vecEnd;
- vecStart.Init( position.x, position.y, maxz );
- vecEnd.Init( position.x, position.y, minz );
-
- Ray_t ray;
- trace_t tr;
- CWaterTraceFilter waterTraceFilter;
- ray.Init( vecStart, vecEnd );
- enginetrace->TraceRay( ray, MASK_WATER, &waterTraceFilter, &tr );
-
- return tr.endpos.z;
-}
-
-
-extern short g_sModelIndexBubbles;// holds the index for the bubbles model
-
-void UTIL_Bubbles( const Vector& mins, const Vector& maxs, int count )
-{
- Vector mid = (mins + maxs) * 0.5;
-
- float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 );
- flHeight = flHeight - mins.z;
-
- CPASFilter filter( mid );
-
- te->Bubbles( filter, 0.0,
- &mins, &maxs, flHeight, g_sModelIndexBubbles, count, 8.0 );
-}
-
-void UTIL_BubbleTrail( const Vector& from, const Vector& to, int count )
-{
- // Find water surface will return from.z if the from point is above water
- float flStartHeight = UTIL_FindWaterSurface( from, from.z, from.z + 256 );
- flStartHeight = flStartHeight - from.z;
-
- float flEndHeight = UTIL_FindWaterSurface( to, to.z, to.z + 256 );
- flEndHeight = flEndHeight - to.z;
-
- if ( ( flStartHeight == 0 ) && ( flEndHeight == 0 ) )
- return;
-
- float flWaterZ = flStartHeight + from.z;
-
- const Vector *pFrom = &from;
- const Vector *pTo = &to;
- Vector vecWaterPoint;
- if ( ( flStartHeight == 0 ) || ( flEndHeight == 0 ) )
- {
- if ( flStartHeight == 0 )
- {
- flWaterZ = flEndHeight + to.z;
- }
-
- float t = IntersectRayWithAAPlane( from, to, 2, 1.0f, flWaterZ );
- Assert( (t >= -1e-3f) && ( t <= 1.0f ) );
- VectorLerp( from, to, t, vecWaterPoint );
- if ( flStartHeight == 0 )
- {
- pFrom = &vecWaterPoint;
-
- // Reduce the count by the actual length
- count = (int)( count * ( 1.0f - t ) );
- }
- else
- {
- pTo = &vecWaterPoint;
-
- // Reduce the count by the actual length
- count = (int)( count * t );
- }
- }
-
- CBroadcastRecipientFilter filter;
- te->BubbleTrail( filter, 0.0, pFrom, pTo, flWaterZ, g_sModelIndexBubbles, count, 8.0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : Start -
-// End -
-// ModelIndex -
-// FrameStart -
-// FrameRate -
-// Life -
-// Width -
-// Noise -
-// Red -
-// Green -
-// Brightness -
-// Speed -
-//-----------------------------------------------------------------------------
-void UTIL_Beam( Vector &Start, Vector &End, int nModelIndex, int nHaloIndex, unsigned char FrameStart, unsigned char FrameRate,
- float Life, unsigned char Width, unsigned char EndWidth, unsigned char FadeLength, unsigned char Noise, unsigned char Red, unsigned char Green,
- unsigned char Blue, unsigned char Brightness, unsigned char Speed)
-{
- CBroadcastRecipientFilter filter;
-
- te->BeamPoints( filter, 0.0,
- &Start,
- &End,
- nModelIndex,
- nHaloIndex,
- FrameStart,
- FrameRate,
- Life,
- Width,
- EndWidth,
- FadeLength,
- Noise,
- Red,
- Green,
- Blue,
- Brightness,
- Speed );
-}
-
-bool UTIL_IsValidEntity( CBaseEntity *pEnt )
-{
- edict_t *pEdict = pEnt->edict();
- if ( !pEdict || pEdict->IsFree() )
- return false;
- return true;
-}
-
-
-#define PRECACHE_OTHER_ONCE
-// UNDONE: Do we need this to avoid doing too much of this? Measure startup times and see
-#if defined( PRECACHE_OTHER_ONCE )
-
-#include "utlsymbol.h"
-class CPrecacheOtherList : public CAutoGameSystem
-{
-public:
- CPrecacheOtherList( char const *name ) : CAutoGameSystem( name )
- {
- }
- virtual void LevelInitPreEntity();
- virtual void LevelShutdownPostEntity();
-
- bool AddOrMarkPrecached( const char *pClassname );
-
-private:
- CUtlSymbolTable m_list;
-};
-
-void CPrecacheOtherList::LevelInitPreEntity()
-{
- m_list.RemoveAll();
-}
-
-void CPrecacheOtherList::LevelShutdownPostEntity()
-{
- m_list.RemoveAll();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: mark or add
-// Input : *pEntity -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CPrecacheOtherList::AddOrMarkPrecached( const char *pClassname )
-{
- CUtlSymbol sym = m_list.Find( pClassname );
- if ( sym.IsValid() )
- return false;
-
- m_list.AddString( pClassname );
- return true;
-}
-
-CPrecacheOtherList g_PrecacheOtherList( "CPrecacheOtherList" );
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *szClassname -
-// *modelName -
-//-----------------------------------------------------------------------------
-void UTIL_PrecacheOther( const char *szClassname, const char *modelName )
-{
-#if defined( PRECACHE_OTHER_ONCE )
- // already done this one?, if not, mark as done
- if ( !g_PrecacheOtherList.AddOrMarkPrecached( szClassname ) )
- return;
-#endif
-
- CBaseEntity *pEntity = CreateEntityByName( szClassname );
- if ( !pEntity )
- {
- Warning( "NULL Ent in UTIL_PrecacheOther\n" );
- return;
- }
-
- // If we have a specified model, set it before calling precache
- if ( modelName && modelName[0] )
- {
- pEntity->SetModelName( AllocPooledString( modelName ) );
- }
-
- if (pEntity)
- pEntity->Precache( );
-
- UTIL_RemoveImmediate( pEntity );
-}
-
-//=========================================================
-// UTIL_LogPrintf - Prints a logged message to console.
-// Preceded by LOG: ( timestamp ) < message >
-//=========================================================
-void UTIL_LogPrintf( const char *fmt, ... )
-{
- va_list argptr;
- char tempString[1024];
-
- va_start ( argptr, fmt );
- Q_vsnprintf( tempString, sizeof(tempString), fmt, argptr );
- va_end ( argptr );
-
- // Print to server console
- engine->LogPrint( tempString );
-}
-
-//=========================================================
-// UTIL_DotPoints - returns the dot product of a line from
-// src to check and vecdir.
-//=========================================================
-float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir )
-{
- Vector2D vec2LOS;
-
- vec2LOS = ( vecCheck - vecSrc ).AsVector2D();
- Vector2DNormalize( vec2LOS );
-
- return DotProduct2D(vec2LOS, vecDir.AsVector2D());
-}
-
-
-//=========================================================
-// UTIL_StripToken - for redundant keynames
-//=========================================================
-void UTIL_StripToken( const char *pKey, char *pDest )
-{
- int i = 0;
-
- while ( pKey[i] && pKey[i] != '#' )
- {
- pDest[i] = pKey[i];
- i++;
- }
- pDest[i] = 0;
-}
-
-
-// computes gravity scale for an absolute gravity. Pass the result into CBaseEntity::SetGravity()
-float UTIL_ScaleForGravity( float desiredGravity )
-{
- float worldGravity = GetCurrentGravity();
- return worldGravity > 0 ? desiredGravity / worldGravity : 0;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Implemented for mathlib.c error handling
-// Input : *error -
-//-----------------------------------------------------------------------------
-extern "C" void Sys_Error( char *error, ... )
-{
- va_list argptr;
- char string[1024];
-
- va_start( argptr, error );
- Q_vsnprintf( string, sizeof(string), error, argptr );
- va_end( argptr );
-
- Warning( "%s", string );
- Assert(0);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Spawns an entity into the game, initializing it with the map ent data block
-// Input : *pEntity - the newly created entity
-// *mapData - pointer a block of entity map data
-// Output : -1 if the entity was not successfully created; 0 on success
-//-----------------------------------------------------------------------------
-int DispatchSpawn( CBaseEntity *pEntity )
-{
- if ( pEntity )
- {
- MDLCACHE_CRITICAL_SECTION();
-
- // keep a smart pointer that will now if the object gets deleted
- EHANDLE pEntSafe;
- pEntSafe = pEntity;
-
- // Initialize these or entities who don't link to the world won't have anything in here
- // is this necessary?
- //pEntity->SetAbsMins( pEntity->GetOrigin() - Vector(1,1,1) );
- //pEntity->SetAbsMaxs( pEntity->GetOrigin() + Vector(1,1,1) );
-
-#if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
- const char *pszClassname = NULL;
- int iClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.Find( pEntity->GetClassname() );
- if ( iClassname != ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.InvalidIndex() )
- pszClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.GetElementName( iClassname );
- if ( pszClassname )
- {
- MemAlloc_PushAllocDbgInfo( pszClassname, __LINE__ );
- }
-#endif
- bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false );
- CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
- if (!pAnimating)
- {
- pEntity->Spawn();
- }
- else
- {
- // Don't allow the PVS check to skip animation setup during spawning
- pAnimating->SetBoneCacheFlags( BCF_IS_IN_SPAWN );
- pEntity->Spawn();
- if ( pEntSafe != NULL )
- pAnimating->ClearBoneCacheFlags( BCF_IS_IN_SPAWN );
- }
- mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims );
-
-#if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG)
- if ( pszClassname )
- {
- MemAlloc_PopAllocDbgInfo();
- }
-#endif
- // Try to get the pointer again, in case the spawn function deleted the entity.
- // UNDONE: Spawn() should really return a code to ask that the entity be deleted, but
- // that would touch too much code for me to do that right now.
-
- if ( pEntSafe == NULL || pEntity->IsMarkedForDeletion() )
- return -1;
-
- if ( pEntity->m_iGlobalname != NULL_STRING )
- {
- // Handle global stuff here
- int globalIndex = GlobalEntity_GetIndex( pEntity->m_iGlobalname );
- if ( globalIndex >= 0 )
- {
- // Already dead? delete
- if ( GlobalEntity_GetState(globalIndex) == GLOBAL_DEAD )
- {
- pEntity->Remove();
- return -1;
- }
- else if ( !FStrEq(STRING(gpGlobals->mapname), GlobalEntity_GetMap(globalIndex)) )
- {
- pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive
- }
- // In this level & not dead, continue on as normal
- }
- else
- {
- // Spawned entities default to 'On'
- GlobalEntity_Add( pEntity->m_iGlobalname, gpGlobals->mapname, GLOBAL_ON );
-// Msg( "Added global entity %s (%s)\n", pEntity->GetClassname(), STRING(pEntity->m_iGlobalname) );
- }
- }
-
- gEntList.NotifySpawn( pEntity );
- }
-
- return 0;
-}
-
-// UNDONE: This could be a better test - can we run the absbox through the bsp and see
-// if it contains any solid space? or would that eliminate some entities we want to keep?
-int UTIL_EntityInSolid( CBaseEntity *ent )
-{
- Vector point;
-
- CBaseEntity *pParent = ent->GetMoveParent();
- // HACKHACK -- If you're attached to a client, always go through
- if ( pParent )
- {
- if ( pParent->IsPlayer() )
- return 0;
-
- ent = ent->GetRootMoveParent();
- }
-
- point = ent->WorldSpaceCenter();
- return ( enginetrace->GetPointContents( point ) & MASK_SOLID );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Initialize the matrix from an entity
-// Input : *pEntity -
-//-----------------------------------------------------------------------------
-void EntityMatrix::InitFromEntity( CBaseEntity *pEntity, int iAttachment )
-{
- if ( !pEntity )
- {
- Identity();
- return;
- }
-
- // Get an attachment's matrix?
- if ( iAttachment != 0 )
- {
- CBaseAnimating *pAnimating = pEntity->GetBaseAnimating();
- if ( pAnimating && pAnimating->GetModelPtr() )
- {
- Vector vOrigin;
- QAngle vAngles;
- if ( pAnimating->GetAttachment( iAttachment, vOrigin, vAngles ) )
- {
- ((VMatrix *)this)->SetupMatrixOrgAngles( vOrigin, vAngles );
- return;
- }
- }
- }
-
- ((VMatrix *)this)->SetupMatrixOrgAngles( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles() );
-}
-
-
-void EntityMatrix::InitFromEntityLocal( CBaseEntity *entity )
-{
- if ( !entity || !entity->edict() )
- {
- Identity();
- return;
- }
- ((VMatrix *)this)->SetupMatrixOrgAngles( entity->GetLocalOrigin(), entity->GetLocalAngles() );
-}
-
-//==================================================
-// Purpose:
-// Input:
-// Output:
-//==================================================
-
-void UTIL_ValidateSoundName( string_t &name, const char *defaultStr )
-{
- if ( ( !name ||
- strlen( (char*) STRING( name ) ) < 1 ) ||
- !Q_stricmp( (char *)STRING(name), "0" ) )
- {
- name = AllocPooledString( defaultStr );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Slightly modified strtok. Does not modify the input string. Does
-// not skip over more than one separator at a time. This allows parsing
-// strings where tokens between separators may or may not be present:
-//
-// Door01,,,0 would be parsed as "Door01" "" "" "0"
-// Door01,Open,,0 would be parsed as "Door01" "Open" "" "0"
-//
-// Input : token - Returns with a token, or zero length if the token was missing.
-// str - String to parse.
-// sep - Character to use as separator. UNDONE: allow multiple separator chars
-// Output : Returns a pointer to the next token to be parsed.
-//-----------------------------------------------------------------------------
-const char *nexttoken(char *token, const char *str, char sep)
-{
- if ((str == NULL) || (*str == '\0'))
- {
- *token = '\0';
- return(NULL);
- }
-
- //
- // Copy everything up to the first separator into the return buffer.
- // Do not include separators in the return buffer.
- //
- while ((*str != sep) && (*str != '\0'))
- {
- *token++ = *str++;
- }
- *token = '\0';
-
- //
- // Advance the pointer unless we hit the end of the input string.
- //
- if (*str == '\0')
- {
- return(str);
- }
-
- return(++str);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Helper for UTIL_FindClientInPVS
-// Input : check - last checked client
-// Output : static int UTIL_GetNewCheckClient
-//-----------------------------------------------------------------------------
-// FIXME: include bspfile.h here?
-class CCheckClient : public CAutoGameSystem
-{
-public:
- CCheckClient( char const *name ) : CAutoGameSystem( name )
- {
- }
-
- void LevelInitPreEntity()
- {
- m_checkCluster = -1;
- m_lastcheck = 1;
- m_lastchecktime = -1;
- m_bClientPVSIsExpanded = false;
- }
-
- byte m_checkPVS[MAX_MAP_LEAFS/8];
- byte m_checkVisibilityPVS[MAX_MAP_LEAFS/8];
- int m_checkCluster;
- int m_lastcheck;
- float m_lastchecktime;
- bool m_bClientPVSIsExpanded;
-};
-
-CCheckClient g_CheckClient( "CCheckClient" );
-
-
-static int UTIL_GetNewCheckClient( int check )
-{
- int i;
- edict_t *ent;
- Vector org;
-
-// cycle to the next one
-
- if (check < 1)
- check = 1;
- if (check > gpGlobals->maxClients)
- check = gpGlobals->maxClients;
-
- if (check == gpGlobals->maxClients)
- i = 1;
- else
- i = check + 1;
-
- for ( ; ; i++)
- {
- if ( i > gpGlobals->maxClients )
- {
- i = 1;
- }
-
- ent = engine->PEntityOfEntIndex( i );
- if ( !ent )
- continue;
-
- // Looped but didn't find anything else
- if ( i == check )
- break;
-
- if ( !ent->GetUnknown() )
- continue;
-
- CBaseEntity *entity = GetContainingEntity( ent );
- if ( !entity )
- continue;
-
- if ( entity->GetFlags() & FL_NOTARGET )
- continue;
-
- // anything that is a client, or has a client as an enemy
- break;
- }
-
- if ( i != check )
- {
- memset( g_CheckClient.m_checkVisibilityPVS, 0, sizeof(g_CheckClient.m_checkVisibilityPVS) );
- g_CheckClient.m_bClientPVSIsExpanded = false;
- }
-
- if ( ent )
- {
- // get the PVS for the entity
- CBaseEntity *pce = GetContainingEntity( ent );
- if ( !pce )
- return i;
-
- org = pce->EyePosition();
-
- int clusterIndex = engine->GetClusterForOrigin( org );
- if ( clusterIndex != g_CheckClient.m_checkCluster )
- {
- g_CheckClient.m_checkCluster = clusterIndex;
- engine->GetPVSForCluster( clusterIndex, sizeof(g_CheckClient.m_checkPVS), g_CheckClient.m_checkPVS );
- }
- }
-
- return i;
-}
-
-
-//-----------------------------------------------------------------------------
-// Gets the current check client....
-//-----------------------------------------------------------------------------
-static edict_t *UTIL_GetCurrentCheckClient()
-{
- edict_t *ent;
-
- // find a new check if on a new frame
- float delta = gpGlobals->curtime - g_CheckClient.m_lastchecktime;
- if ( delta >= 0.1 || delta < 0 )
- {
- g_CheckClient.m_lastcheck = UTIL_GetNewCheckClient( g_CheckClient.m_lastcheck );
- g_CheckClient.m_lastchecktime = gpGlobals->curtime;
- }
-
- // return check if it might be visible
- ent = engine->PEntityOfEntIndex( g_CheckClient.m_lastcheck );
-
- // Allow dead clients -- JAY
- // Our monsters know the difference, and this function gates alot of behavior
- // It's annoying to die and see monsters stop thinking because you're no longer
- // "in" their PVS
- if ( !ent || ent->IsFree() || !ent->GetUnknown())
- {
- return NULL;
- }
-
- return ent;
-}
-
-void UTIL_SetClientVisibilityPVS( edict_t *pClient, const unsigned char *pvs, int pvssize )
-{
- if ( pClient == UTIL_GetCurrentCheckClient() )
- {
- Assert( pvssize <= sizeof(g_CheckClient.m_checkVisibilityPVS) );
-
- g_CheckClient.m_bClientPVSIsExpanded = false;
-
- unsigned *pFrom = (unsigned *)pvs;
- unsigned *pMask = (unsigned *)g_CheckClient.m_checkPVS;
- unsigned *pTo = (unsigned *)g_CheckClient.m_checkVisibilityPVS;
-
- int limit = pvssize / 4;
- int i;
-
- for ( i = 0; i < limit; i++ )
- {
- pTo[i] = pFrom[i] & ~pMask[i];
-
- if ( pFrom[i] )
- {
- g_CheckClient.m_bClientPVSIsExpanded = true;
- }
- }
-
- int remainder = pvssize % 4;
- for ( i = 0; i < remainder; i++ )
- {
- ((unsigned char *)&pTo[limit])[i] = ((unsigned char *)&pFrom[limit])[i] & !((unsigned char *)&pMask[limit])[i];
-
- if ( ((unsigned char *)&pFrom[limit])[i] != 0)
- {
- g_CheckClient.m_bClientPVSIsExpanded = true;
- }
- }
- }
-}
-
-bool UTIL_ClientPVSIsExpanded()
-{
- return g_CheckClient.m_bClientPVSIsExpanded;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns a client (or object that has a client enemy) that would be a valid target.
-// If there are more than one valid options, they are cycled each frame
-// If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all.
-// Input : *pEdict -
-// Output : edict_t*
-//-----------------------------------------------------------------------------
-CBaseEntity *UTIL_FindClientInPVS( const Vector &vecBoxMins, const Vector &vecBoxMaxs )
-{
- edict_t *ent = UTIL_GetCurrentCheckClient();
- if ( !ent )
- {
- return NULL;
- }
-
- if ( !engine->CheckBoxInPVS( vecBoxMins, vecBoxMaxs, g_CheckClient.m_checkPVS, sizeof( g_CheckClient.m_checkPVS ) ) )
- {
- return NULL;
- }
-
- // might be able to see it
- return GetContainingEntity( ent );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns a client (or object that has a client enemy) that would be a valid target.
-// If there are more than one valid options, they are cycled each frame
-// If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all.
-// Input : *pEdict -
-// Output : edict_t*
-//-----------------------------------------------------------------------------
-ConVar sv_strict_notarget( "sv_strict_notarget", "0", 0, "If set, notarget will cause entities to never think they are in the pvs" );
-
-static edict_t *UTIL_FindClientInPVSGuts(edict_t *pEdict, unsigned char *pvs, unsigned pvssize )
-{
- Vector view;
-
- edict_t *ent = UTIL_GetCurrentCheckClient();
- if ( !ent )
- {
- return NULL;
- }
-
- CBaseEntity *pPlayerEntity = GetContainingEntity( ent );
- if( (!pPlayerEntity || (pPlayerEntity->GetFlags() & FL_NOTARGET)) && sv_strict_notarget.GetBool() )
- {
- return NULL;
- }
- // if current entity can't possibly see the check entity, return 0
- // UNDONE: Build a box for this and do it over that box
- // UNDONE: Use CM_BoxLeafnums()
- CBaseEntity *pe = GetContainingEntity( pEdict );
- if ( pe )
- {
- view = pe->EyePosition();
-
- if ( !engine->CheckOriginInPVS( view, pvs, pvssize ) )
- {
- return NULL;
- }
- }
-
- // might be able to see it
- return ent;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns a client that could see the entity directly
-//-----------------------------------------------------------------------------
-
-edict_t *UTIL_FindClientInPVS(edict_t *pEdict)
-{
- return UTIL_FindClientInPVSGuts( pEdict, g_CheckClient.m_checkPVS, sizeof( g_CheckClient.m_checkPVS ) );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns a client that could see the entity, including through a camera
-//-----------------------------------------------------------------------------
-edict_t *UTIL_FindClientInVisibilityPVS( edict_t *pEdict )
-{
- return UTIL_FindClientInPVSGuts( pEdict, g_CheckClient.m_checkVisibilityPVS, sizeof( g_CheckClient.m_checkVisibilityPVS ) );
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns a chain of entities within the PVS of another entity (client)
-// starting_ent is the ent currently at in the list
-// a starting_ent of NULL signifies the beginning of a search
-// Input : *pplayer -
-// *starting_ent -
-// Output : edict_t
-//-----------------------------------------------------------------------------
-CBaseEntity *UTIL_EntitiesInPVS( CBaseEntity *pPVSEntity, CBaseEntity *pStartingEntity )
-{
- Vector org;
- static byte pvs[ MAX_MAP_CLUSTERS/8 ];
- static Vector lastOrg( 0, 0, 0 );
- static int lastCluster = -1;
-
- if ( !pPVSEntity )
- return NULL;
-
- // NOTE: These used to be caching code here to prevent this from
- // being called over+over which breaks when you go back + forth
- // across level transitions
- // So, we'll always get the PVS each time we start a new EntitiesInPVS iteration.
- // Given that weapon_binocs + leveltransition code is the only current clients
- // of this, this seems safe.
- if ( !pStartingEntity )
- {
- org = pPVSEntity->EyePosition();
- int clusterIndex = engine->GetClusterForOrigin( org );
- Assert( clusterIndex >= 0 );
- engine->GetPVSForCluster( clusterIndex, sizeof(pvs), pvs );
- }
-
- for ( CBaseEntity *pEntity = gEntList.NextEnt(pStartingEntity); pEntity; pEntity = gEntList.NextEnt(pEntity) )
- {
- // Only return attached ents.
- if ( !pEntity->edict() )
- continue;
-
- CBaseEntity *pParent = pEntity->GetRootMoveParent();
-
- Vector vecSurroundMins, vecSurroundMaxs;
- pParent->CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
- if ( !engine->CheckBoxInPVS( vecSurroundMins, vecSurroundMaxs, pvs, sizeof( pvs ) ) )
- continue;
-
- return pEntity;
- }
-
- return NULL;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the predicted postion of an entity of a certain number of seconds
-// Use this function with caution, it has great potential for annoying the player, especially
-// if used for target firing predition
-// Input : *pTarget - target entity to predict
-// timeDelta - amount of time to predict ahead (in seconds)
-// &vecPredictedPosition - output
-//-----------------------------------------------------------------------------
-void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *vecPredictedPosition )
-{
- if ( ( pTarget == NULL ) || ( vecPredictedPosition == NULL ) )
- return;
-
- Vector vecPredictedVel;
-
- //FIXME: Should we look at groundspeed or velocity for non-clients??
-
- //Get the proper velocity to predict with
- CBasePlayer *pPlayer = ToBasePlayer( pTarget );
-
- //Player works differently than other entities
- if ( pPlayer != NULL )
- {
- if ( pPlayer->IsInAVehicle() )
- {
- //Calculate the predicted position in this vehicle
- vecPredictedVel = pPlayer->GetVehicleEntity()->GetSmoothedVelocity();
- }
- else
- {
- //Get the player's stored velocity
- vecPredictedVel = pPlayer->GetSmoothedVelocity();
- }
- }
- else
- {
- // See if we're a combat character in a vehicle
- CBaseCombatCharacter *pCCTarget = pTarget->MyCombatCharacterPointer();
- if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() )
- {
- //Calculate the predicted position in this vehicle
- vecPredictedVel = pCCTarget->GetVehicleEntity()->GetSmoothedVelocity();
- }
- else
- {
- // See if we're an animating entity
- CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(pTarget);
- if ( pAnimating != NULL )
- {
- vecPredictedVel = pAnimating->GetGroundSpeedVelocity();
- }
- else
- {
- // Otherwise we're a vanilla entity
- vecPredictedVel = pTarget->GetSmoothedVelocity();
- }
- }
- }
-
- //Get the result
- (*vecPredictedPosition) = pTarget->GetAbsOrigin() + ( vecPredictedVel * flTimeDelta );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Points the destination entity at the target entity
-// Input : *pDest - entity to be pointed at the target
-// *pTarget - target to point at
-//-----------------------------------------------------------------------------
-bool UTIL_PointAtEntity( CBaseEntity *pDest, CBaseEntity *pTarget )
-{
- if ( ( pDest == NULL ) || ( pTarget == NULL ) )
- {
- return false;
- }
-
- Vector dir = (pTarget->GetAbsOrigin() - pDest->GetAbsOrigin());
-
- VectorNormalize( dir );
-
- //Store off as angles
- QAngle angles;
- VectorAngles( dir, angles );
- pDest->SetLocalAngles( angles );
- pDest->SetAbsAngles( angles );
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Points the destination entity at the target entity by name
-// Input : *pDest - entity to be pointed at the target
-// strTarget - name of entity to target (will only choose the first!)
-//-----------------------------------------------------------------------------
-void UTIL_PointAtNamedEntity( CBaseEntity *pDest, string_t strTarget )
-{
- //Attempt to find the entity
- if ( !UTIL_PointAtEntity( pDest, gEntList.FindEntityByName( NULL, strTarget ) ) )
- {
- DevMsg( 1, "%s (%s) was unable to point at an entity named: %s\n", pDest->GetClassname(), pDest->GetDebugName(), STRING( strTarget ) );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Copy the pose parameter values from one entity to the other
-// Input : *pSourceEntity - entity to copy from
-// *pDestEntity - entity to copy to
-//-----------------------------------------------------------------------------
-bool UTIL_TransferPoseParameters( CBaseEntity *pSourceEntity, CBaseEntity *pDestEntity )
-{
- CBaseAnimating *pSourceBaseAnimating = dynamic_cast<CBaseAnimating*>( pSourceEntity );
- CBaseAnimating *pDestBaseAnimating = dynamic_cast<CBaseAnimating*>( pDestEntity );
-
- if ( !pSourceBaseAnimating || !pDestBaseAnimating )
- return false;
-
- for ( int iPose = 0; iPose < MAXSTUDIOPOSEPARAM; ++iPose )
- {
- pDestBaseAnimating->SetPoseParameter( iPose, pSourceBaseAnimating->GetPoseParameter( iPose ) );
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Make a muzzle flash appear
-// Input : &origin - position of the muzzle flash
-// &angles - angles of the fire direction
-// scale - scale of the muzzle flash
-// type - type of muzzle flash
-//-----------------------------------------------------------------------------
-void UTIL_MuzzleFlash( const Vector &origin, const QAngle &angles, int scale, int type )
-{
- CPASFilter filter( origin );
-
- te->MuzzleFlash( filter, 0.0f, origin, angles, scale, type );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : vStartPos - start of the line
-// vEndPos - end of the line
-// vPoint - point to find nearest point to on specified line
-// clampEnds - clamps returned points to being on the line segment specified
-// Output : Vector - nearest point on the specified line
-//-----------------------------------------------------------------------------
-Vector UTIL_PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint, bool clampEnds )
-{
- Vector vEndToStart = (vEndPos - vStartPos);
- Vector vOrgToStart = (vPoint - vStartPos);
- float fNumerator = DotProduct(vEndToStart,vOrgToStart);
- float fDenominator = vEndToStart.Length() * vOrgToStart.Length();
- float fIntersectDist = vOrgToStart.Length()*(fNumerator/fDenominator);
- float flLineLength = VectorNormalize( vEndToStart );
-
- if ( clampEnds )
- {
- fIntersectDist = clamp( fIntersectDist, 0.0f, flLineLength );
- }
-
- Vector vIntersectPos = vStartPos + vEndToStart * fIntersectDist;
-
- return vIntersectPos;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-AngularImpulse WorldToLocalRotation( const VMatrix &localToWorld, const Vector &worldAxis, float rotation )
-{
- // fix axes of rotation to match axes of vector
- Vector rot = worldAxis * rotation;
- // since the matrix maps local to world, do a transpose rotation to get world to local
- AngularImpulse ang = localToWorld.VMul3x3Transpose( rot );
-
- return ang;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *filename -
-// *pLength -
-// Output : byte
-//-----------------------------------------------------------------------------
-byte *UTIL_LoadFileForMe( const char *filename, int *pLength )
-{
- void *buffer = NULL;
-
- int length = filesystem->ReadFileEx( filename, "GAME", &buffer, true, true );
-
- if ( pLength )
- {
- *pLength = length;
- }
-
- return (byte *)buffer;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *buffer -
-//-----------------------------------------------------------------------------
-void UTIL_FreeFile( byte *buffer )
-{
- filesystem->FreeOptimalReadBuffer( buffer );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Determines whether an entity is within a certain angular tolerance to viewer
-// Input : *pEntity - entity which is the "viewer"
-// vecPosition - position to test against
-// flTolerance - tolerance (as dot-product)
-// *pflDot - if not NULL, holds the
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, const Vector &vecPosition, float flDotTolerance, float *pflDot /*= NULL*/ )
-{
- if ( pflDot )
- {
- *pflDot = 0.0f;
- }
-
- // Required elements
- if ( pViewer == NULL )
- return false;
-
- Vector forward;
- pViewer->GetVectors( &forward, NULL, NULL );
-
- Vector dir = vecPosition - pViewer->GetAbsOrigin();
- VectorNormalize( dir );
-
- // Larger dot product corresponds to a smaller angle
- float flDot = dir.Dot( forward );
-
- // Return the result
- if ( pflDot )
- {
- *pflDot = flDot;
- }
-
- // Within the goal tolerance
- if ( flDot >= flDotTolerance )
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Determines whether an entity is within a certain angular tolerance to viewer
-// Input : *pEntity - entity which is the "viewer"
-// *pTarget - entity to test against
-// flTolerance - tolerance (as dot-product)
-// *pflDot - if not NULL, holds the
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, CBaseEntity *pTarget, float flDotTolerance, float *pflDot /*= NULL*/ )
-{
- if ( pViewer == NULL || pTarget == NULL )
- return false;
-
- return UTIL_IsFacingWithinTolerance( pViewer, pTarget->GetAbsOrigin(), flDotTolerance, pflDot );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Fills in color for debug purposes based on a relationship
-// Input : nRelationship - relationship to test
-// *pR, *pG, *pB - colors to fill
-//-----------------------------------------------------------------------------
-void UTIL_GetDebugColorForRelationship( int nRelationship, int &r, int &g, int &b )
-{
- switch ( nRelationship )
- {
- case D_LI:
- r = 0;
- g = 255;
- b = 0;
- break;
- case D_NU:
- r = 0;
- g = 0;
- b = 255;
- break;
- case D_HT:
- r = 255;
- g = 0;
- b = 0;
- break;
- case D_FR:
- r = 255;
- g = 255;
- b = 0;
- break;
- default:
- r = 255;
- g = 255;
- b = 255;
- break;
- }
-}
-
-void LoadAndSpawnEntities_ParseEntKVBlockHelper( CBaseEntity *pNode, KeyValues *pkvNode )
-{
- KeyValues *pkvNodeData = pkvNode->GetFirstSubKey();
- while ( pkvNodeData )
- {
- // Handle the connections block
- if ( !Q_strcmp(pkvNodeData->GetName(), "connections") )
- {
- LoadAndSpawnEntities_ParseEntKVBlockHelper( pNode, pkvNodeData );
- }
- else
- {
- pNode->KeyValue( pkvNodeData->GetName(), pkvNodeData->GetString() );
- }
-
- pkvNodeData = pkvNodeData->GetNextKey();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Loads and parses a file and spawns entities defined in it.
-//-----------------------------------------------------------------------------
-bool UTIL_LoadAndSpawnEntitiesFromScript( CUtlVector <CBaseEntity*> &entities, const char *pScriptFile, const char *pBlock, bool bActivate )
-{
- KeyValues *pkvFile = new KeyValues( pBlock );
-
- if ( pkvFile->LoadFromFile( filesystem, pScriptFile, "MOD" ) )
- {
- // Load each block, and spawn the entities
- KeyValues *pkvNode = pkvFile->GetFirstSubKey();
- while ( pkvNode )
- {
- // Get name
- const char *pNodeName = pkvNode->GetName();
-
- if ( stricmp( pNodeName, "entity" ) )
- {
- pkvNode = pkvNode->GetNextKey();
- continue;
- }
-
- KeyValues *pClassname = pkvNode->FindKey( "classname" );
-
- if ( pClassname )
- {
- // Use the classname instead
- pNodeName = pClassname->GetString();
- }
-
- // Spawn the entity
- CBaseEntity *pNode = CreateEntityByName( pNodeName );
-
- if ( pNode )
- {
- LoadAndSpawnEntities_ParseEntKVBlockHelper( pNode, pkvNode );
- DispatchSpawn( pNode );
- entities.AddToTail( pNode );
- }
- else
- {
- Warning( "UTIL_LoadAndSpawnEntitiesFromScript: Failed to spawn entity, type: '%s'\n", pNodeName );
- }
-
- // Move to next entity
- pkvNode = pkvNode->GetNextKey();
- }
-
- if ( bActivate == true )
- {
- bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false );
- // Then activate all the entities
- for ( int i = 0; i < entities.Count(); i++ )
- {
- entities[i]->Activate();
- }
- mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims );
- }
- }
- else
- return false;
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Convert a vector an angle from worldspace to the entity's parent's local space
-// Input : *pEntity - Entity whose parent we're concerned with
-//-----------------------------------------------------------------------------
-void UTIL_ParentToWorldSpace( CBaseEntity *pEntity, Vector &vecPosition, QAngle &vecAngles )
-{
- if ( pEntity == NULL )
- return;
-
- // Construct the entity-to-world matrix
- // Start with making an entity-to-parent matrix
- matrix3x4_t matEntityToParent;
- AngleMatrix( vecAngles, matEntityToParent );
- MatrixSetColumn( vecPosition, 3, matEntityToParent );
-
- // concatenate with our parent's transform
- matrix3x4_t matScratch, matResult;
- matrix3x4_t matParentToWorld;
-
- if ( pEntity->GetParent() != NULL )
- {
- matParentToWorld = pEntity->GetParentToWorldTransform( matScratch );
- }
- else
- {
- matParentToWorld = pEntity->EntityToWorldTransform();
- }
-
- ConcatTransforms( matParentToWorld, matEntityToParent, matResult );
-
- // pull our absolute position out of the matrix
- MatrixGetColumn( matResult, 3, vecPosition );
- MatrixAngles( matResult, vecAngles );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space
-// Input : *pEntity - Entity whose parent we're concerned with
-//-----------------------------------------------------------------------------
-void UTIL_ParentToWorldSpace( CBaseEntity *pEntity, Vector &vecPosition, Quaternion &quat )
-{
- if ( pEntity == NULL )
- return;
-
- QAngle vecAngles;
- QuaternionAngles( quat, vecAngles );
- UTIL_ParentToWorldSpace( pEntity, vecPosition, vecAngles );
- AngleQuaternion( vecAngles, quat );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Convert a vector an angle from worldspace to the entity's parent's local space
-// Input : *pEntity - Entity whose parent we're concerned with
-//-----------------------------------------------------------------------------
-void UTIL_WorldToParentSpace( CBaseEntity *pEntity, Vector &vecPosition, QAngle &vecAngles )
-{
- if ( pEntity == NULL )
- return;
-
- // Construct the entity-to-world matrix
- // Start with making an entity-to-parent matrix
- matrix3x4_t matEntityToParent;
- AngleMatrix( vecAngles, matEntityToParent );
- MatrixSetColumn( vecPosition, 3, matEntityToParent );
-
- // concatenate with our parent's transform
- matrix3x4_t matScratch, matResult;
- matrix3x4_t matWorldToParent;
-
- if ( pEntity->GetParent() != NULL )
- {
- matScratch = pEntity->GetParentToWorldTransform( matScratch );
- }
- else
- {
- matScratch = pEntity->EntityToWorldTransform();
- }
-
- MatrixInvert( matScratch, matWorldToParent );
- ConcatTransforms( matWorldToParent, matEntityToParent, matResult );
-
- // pull our absolute position out of the matrix
- MatrixGetColumn( matResult, 3, vecPosition );
- MatrixAngles( matResult, vecAngles );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space
-// Input : *pEntity - Entity whose parent we're concerned with
-//-----------------------------------------------------------------------------
-void UTIL_WorldToParentSpace( CBaseEntity *pEntity, Vector &vecPosition, Quaternion &quat )
-{
- if ( pEntity == NULL )
- return;
-
- QAngle vecAngles;
- QuaternionAngles( quat, vecAngles );
- UTIL_WorldToParentSpace( pEntity, vecPosition, vecAngles );
- AngleQuaternion( vecAngles, quat );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Given a vector, clamps the scalar axes to MAX_COORD_FLOAT ranges from worldsize.h
-// Input : *pVecPos -
-//-----------------------------------------------------------------------------
-void UTIL_BoundToWorldSize( Vector *pVecPos )
-{
- Assert( pVecPos );
- for ( int i = 0; i < 3; ++i )
- {
- (*pVecPos)[ i ] = clamp( (*pVecPos)[ i ], MIN_COORD_FLOAT, MAX_COORD_FLOAT );
- }
-}
-
-//=============================================================================
-//
-// Tests!
-//
-
-#define NUM_KDTREE_TESTS 2500
-#define NUM_KDTREE_ENTITY_SIZE 256
-
-void CC_KDTreeTest( const CCommand &args )
-{
- Msg( "Testing kd-tree entity queries." );
-
- // Get the testing spot.
-// CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start" );
-// Vector vecStart = pSpot->GetAbsOrigin();
-
- CBasePlayer *pPlayer = static_cast<CBasePlayer*>( UTIL_GetLocalPlayer() );
- Vector vecStart = pPlayer->GetAbsOrigin();
-
- static Vector *vecTargets = NULL;
- static bool bFirst = true;
-
- // Generate the targets - rays (1K long).
- if ( bFirst )
- {
- vecTargets = new Vector [NUM_KDTREE_TESTS];
- double flRadius = 0;
- double flTheta = 0;
- double flPhi = 0;
- for ( int i = 0; i < NUM_KDTREE_TESTS; ++i )
- {
- flRadius += NUM_KDTREE_TESTS * 123.123;
- flRadius = fmod( flRadius, 128.0 );
- flRadius = fabs( flRadius );
-
- flTheta += NUM_KDTREE_TESTS * 76.76;
- flTheta = fmod( flTheta, (double) DEG2RAD( 360 ) );
- flTheta = fabs( flTheta );
-
- flPhi += NUM_KDTREE_TESTS * 1997.99;
- flPhi = fmod( flPhi, (double) DEG2RAD( 180 ) );
- flPhi = fabs( flPhi );
-
- float st, ct, sp, cp;
- SinCos( flTheta, &st, &ct );
- SinCos( flPhi, &sp, &cp );
-
- vecTargets[i].x = flRadius * ct * sp;
- vecTargets[i].y = flRadius * st * sp;
- vecTargets[i].z = flRadius * cp;
-
- // Make the trace 1024 units long.
- Vector vecDir = vecTargets[i] - vecStart;
- VectorNormalize( vecDir );
- vecTargets[i] = vecStart + vecDir * 1024;
- }
-
- bFirst = false;
- }
-
- int nTestType = 0;
- if ( args.ArgC() >= 2 )
- {
- nTestType = atoi( args[ 1 ] );
- }
-
- vtune( true );
-
-#ifdef VPROF_ENABLED
- g_VProfCurrentProfile.Resume();
- g_VProfCurrentProfile.Start();
- g_VProfCurrentProfile.Reset();
- g_VProfCurrentProfile.MarkFrame();
-#endif
-
- switch ( nTestType )
- {
- case 0:
- {
- VPROF( "TraceTotal" );
-
- trace_t trace;
- for ( int iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
- {
- UTIL_TraceLine( vecStart, vecTargets[iTest], MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
- }
- break;
- }
- case 1:
- {
- VPROF( "TraceTotal" );
-
- trace_t trace;
- for ( int iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
- {
- UTIL_TraceHull( vecStart, vecTargets[iTest], VEC_HULL_MIN_SCALED( pPlayer ), VEC_HULL_MAX_SCALED( pPlayer ), MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &trace );
- }
- break;
- }
- case 2:
- {
- Vector vecMins[NUM_KDTREE_TESTS];
- Vector vecMaxs[NUM_KDTREE_TESTS];
- int iTest;
- for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
- {
- vecMins[iTest] = vecStart;
- vecMaxs[iTest] = vecStart;
- for ( int iAxis = 0; iAxis < 3; ++iAxis )
- {
- if ( vecTargets[iTest].x < vecMins[iTest].x ) { vecMins[iTest].x = vecTargets[iTest].x; }
- if ( vecTargets[iTest].y < vecMins[iTest].y ) { vecMins[iTest].y = vecTargets[iTest].y; }
- if ( vecTargets[iTest].z < vecMins[iTest].z ) { vecMins[iTest].z = vecTargets[iTest].z; }
-
- if ( vecTargets[iTest].x > vecMaxs[iTest].x ) { vecMaxs[iTest].x = vecTargets[iTest].x; }
- if ( vecTargets[iTest].y > vecMaxs[iTest].y ) { vecMaxs[iTest].y = vecTargets[iTest].y; }
- if ( vecTargets[iTest].z > vecMaxs[iTest].z ) { vecMaxs[iTest].z = vecTargets[iTest].z; }
- }
- }
-
-
- VPROF( "TraceTotal" );
-
- int nCount = 0;
-
- Vector vecDelta;
- trace_t trace;
- CBaseEntity *pList[1024];
- for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
- {
- nCount += UTIL_EntitiesInBox( pList, 1024, vecMins[iTest], vecMaxs[iTest], 0 );
- }
-
- Msg( "Count = %d\n", nCount );
- break;
- }
- case 3:
- {
- Vector vecDelta;
- float flRadius[NUM_KDTREE_TESTS];
- int iTest;
- for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
- {
- VectorSubtract( vecTargets[iTest], vecStart, vecDelta );
- flRadius[iTest] = vecDelta.Length() * 0.5f;
- }
-
- VPROF( "TraceTotal" );
-
- int nCount = 0;
-
- trace_t trace;
- CBaseEntity *pList[1024];
- for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest )
- {
- nCount += UTIL_EntitiesInSphere( pList, 1024, vecStart, flRadius[iTest], 0 );
- }
-
- Msg( "Count = %d\n", nCount );
- break;
- }
- default:
- {
- break;
- }
- }
-
-#ifdef VPROF_ENABLED
- g_VProfCurrentProfile.MarkFrame();
- g_VProfCurrentProfile.Pause();
- g_VProfCurrentProfile.OutputReport( VPRT_FULL );
-#endif
-
- vtune( false );
-}
-
-static ConCommand kdtree_test( "kdtree_test", CC_KDTreeTest, "Tests spatial partition for entities queries.", FCVAR_CHEAT );
-
-void CC_VoxelTreeView( void )
-{
- Msg( "VoxelTreeView\n" );
- partition->RenderAllObjectsInTree( 10.0f );
-}
-
-static ConCommand voxeltree_view( "voxeltree_view", CC_VoxelTreeView, "View entities in the voxel-tree.", FCVAR_CHEAT );
-
-void CC_VoxelTreePlayerView( void )
-{
- Msg( "VoxelTreePlayerView\n" );
-
- CBasePlayer *pPlayer = static_cast<CBasePlayer*>( UTIL_GetLocalPlayer() );
- Vector vecStart = pPlayer->GetAbsOrigin();
- partition->RenderObjectsInPlayerLeafs( vecStart - VEC_HULL_MIN_SCALED( pPlayer ), vecStart + VEC_HULL_MAX_SCALED( pPlayer ), 3.0f );
-}
-
-static ConCommand voxeltree_playerview( "voxeltree_playerview", CC_VoxelTreePlayerView, "View entities in the voxel-tree at the player position.", FCVAR_CHEAT );
-
-void CC_VoxelTreeBox( const CCommand &args )
-{
- Vector vecMin, vecMax;
- if ( args.ArgC() >= 6 )
- {
- vecMin.x = atof( args[ 1 ] );
- vecMin.y = atof( args[ 2 ] );
- vecMin.z = atof( args[ 3 ] );
-
- vecMax.x = atof( args[ 4 ] );
- vecMax.y = atof( args[ 5 ] );
- vecMax.z = atof( args[ 6 ] );
- }
- else
- {
- return;
- }
-
- float flTime = 10.0f;
-
- Vector vecPoints[8];
- vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z );
- vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z );
- vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z );
- vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z );
- vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z );
- vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z );
- vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z );
- vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z );
-
- debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[1], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[2], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[3], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[0], 255, 0, 0, true, flTime );
-
- debugoverlay->AddLineOverlay( vecPoints[4], vecPoints[5], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[5], vecPoints[6], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[6], vecPoints[7], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[7], vecPoints[4], 255, 0, 0, true, flTime );
-
- debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[4], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[7], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[5], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[6], 255, 0, 0, true, flTime );
-
- Msg( "VoxelTreeBox - (%f %f %f) to (%f %f %f)\n", vecMin.x, vecMin.y, vecMin.z, vecMax.x, vecMax.y, vecMax.z );
- partition->RenderObjectsInBox( vecMin, vecMax, flTime );
-}
-
-static ConCommand voxeltree_box( "voxeltree_box", CC_VoxelTreeBox, "View entities in the voxel-tree inside box <Vector(min), Vector(max)>.", FCVAR_CHEAT );
-
-void CC_VoxelTreeSphere( const CCommand &args )
-{
- Vector vecCenter;
- float flRadius;
- if ( args.ArgC() >= 4 )
- {
- vecCenter.x = atof( args[ 1 ] );
- vecCenter.y = atof( args[ 2 ] );
- vecCenter.z = atof( args[ 3 ] );
-
- flRadius = atof( args[ 3 ] );
- }
- else
- {
- return;
- }
-
- float flTime = 3.0f;
-
- Vector vecMin, vecMax;
- vecMin.Init( vecCenter.x - flRadius, vecCenter.y - flRadius, vecCenter.z - flRadius );
- vecMax.Init( vecCenter.x + flRadius, vecCenter.y + flRadius, vecCenter.z + flRadius );
-
- Vector vecPoints[8];
- vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z );
- vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z );
- vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z );
- vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z );
- vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z );
- vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z );
- vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z );
- vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z );
-
- debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[1], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[2], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[3], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[0], 255, 0, 0, true, flTime );
-
- debugoverlay->AddLineOverlay( vecPoints[4], vecPoints[5], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[5], vecPoints[6], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[6], vecPoints[7], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[7], vecPoints[4], 255, 0, 0, true, flTime );
-
- debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[4], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[7], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[5], 255, 0, 0, true, flTime );
- debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[6], 255, 0, 0, true, flTime );
-
- Msg( "VoxelTreeSphere - (%f %f %f), %f\n", vecCenter.x, vecCenter.y, vecCenter.z, flRadius );
- partition->RenderObjectsInSphere( vecCenter, flRadius, flTime );
-}
-
-static ConCommand voxeltree_sphere( "voxeltree_sphere", CC_VoxelTreeSphere, "View entities in the voxel-tree inside sphere <Vector(center), float(radius)>.", FCVAR_CHEAT );
-
-
-
-#define NUM_COLLISION_TESTS 2500
-void CC_CollisionTest( const CCommand &args )
-{
- if ( !physenv )
- return;
-
- Msg( "Testing collision system\n" );
- partition->ReportStats( "" );
- int i;
- CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start");
- Vector start = pSpot->GetAbsOrigin();
- static Vector *targets = NULL;
- static bool first = true;
- static float test[2] = {1,1};
- if ( first )
- {
- targets = new Vector[NUM_COLLISION_TESTS];
- float radius = 0;
- float theta = 0;
- float phi = 0;
- for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
- {
- radius += NUM_COLLISION_TESTS * 123.123;
- radius = fabs(fmod(radius, 128));
- theta += NUM_COLLISION_TESTS * 76.76;
- theta = fabs(fmod(theta, DEG2RAD(360)));
- phi += NUM_COLLISION_TESTS * 1997.99;
- phi = fabs(fmod(phi, DEG2RAD(180)));
-
- float st, ct, sp, cp;
- SinCos( theta, &st, &ct );
- SinCos( phi, &sp, &cp );
-
- targets[i].x = radius * ct * sp;
- targets[i].y = radius * st * sp;
- targets[i].z = radius * cp;
-
- // make the trace 1024 units long
- Vector dir = targets[i] - start;
- VectorNormalize(dir);
- targets[i] = start + dir * 1024;
- }
- first = false;
- }
-
- //Vector results[NUM_COLLISION_TESTS];
-
- int testType = 0;
- if ( args.ArgC() >= 2 )
- {
- testType = atoi(args[1]);
- }
- float duration = 0;
- Vector size[2];
- size[0].Init(0,0,0);
- size[1].Init(16,16,16);
- unsigned int dots = 0;
- int nMask = MASK_ALL & ~(CONTENTS_MONSTER | CONTENTS_HITBOX );
- for ( int j = 0; j < 2; j++ )
- {
- float startTime = engine->Time();
- if ( testType == 1 )
- {
- trace_t tr;
- for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
- {
- UTIL_TraceHull( start, targets[i], -size[1], size[1], nMask, NULL, COLLISION_GROUP_NONE, &tr );
- }
- }
- else
- {
- testType = 0;
- trace_t tr;
-
- for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
- {
- if ( i == 0 )
- {
- partition->RenderLeafsForRayTraceStart( 10.0f );
- }
-
- UTIL_TraceLine( start, targets[i], nMask, NULL, COLLISION_GROUP_NONE, &tr );
-
- if ( i == 0 )
- {
- partition->RenderLeafsForRayTraceEnd( );
- }
- }
- }
-
- duration += engine->Time() - startTime;
- }
- test[testType] = duration;
- Msg("%d collisions in %.2f ms (%u dots)\n", NUM_COLLISION_TESTS, duration*1000, dots );
- partition->ReportStats( "" );
-#if 1
- int red = 255, green = 0, blue = 0;
- for ( i = 0; i < 1 /*NUM_COLLISION_TESTS*/; i++ )
- {
- NDebugOverlay::Line( start, targets[i], red, green, blue, false, 2 );
- }
-#endif
-}
-static ConCommand collision_test("collision_test", CC_CollisionTest, "Tests collision system", FCVAR_CHEAT );
-
-
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Utility code. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "saverestore.h" +#include "globalstate.h" +#include <stdarg.h> +#include "shake.h" +#include "decals.h" +#include "player.h" +#include "gamerules.h" +#include "entitylist.h" +#include "bspfile.h" +#include "mathlib/mathlib.h" +#include "IEffects.h" +#include "vstdlib/random.h" +#include "soundflags.h" +#include "ispatialpartition.h" +#include "igamesystem.h" +#include "saverestoretypes.h" +#include "checksum_crc.h" +#include "hierarchy.h" +#include "iservervehicle.h" +#include "te_effect_dispatch.h" +#include "utldict.h" +#include "collisionutils.h" +#include "movevars_shared.h" +#include "inetchannelinfo.h" +#include "tier0/vprof.h" +#include "ndebugoverlay.h" +#include "engine/ivdebugoverlay.h" +#include "datacache/imdlcache.h" +#include "util.h" +#include "cdll_int.h" + +#ifdef PORTAL +#include "PortalSimulation.h" +//#include "Portal_PhysicsEnvironmentMgr.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud +extern short g_sModelIndexBloodDrop; // (in combatweapon.cpp) holds the sprite index for the initial blood +extern short g_sModelIndexBloodSpray; // (in combatweapon.cpp) holds the sprite index for splattered blood + +#ifdef DEBUG +void DBG_AssertFunction( bool fExpr, const char *szExpr, const char *szFile, int szLine, const char *szMessage ) +{ + if (fExpr) + return; + char szOut[512]; + if (szMessage != NULL) + Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage); + else + Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine); + Warning( szOut); +} +#endif // DEBUG + + +//----------------------------------------------------------------------------- +// Entity creation factory +//----------------------------------------------------------------------------- +class CEntityFactoryDictionary : public IEntityFactoryDictionary +{ +public: + CEntityFactoryDictionary(); + + virtual void InstallFactory( IEntityFactory *pFactory, const char *pClassName ); + virtual IServerNetworkable *Create( const char *pClassName ); + virtual void Destroy( const char *pClassName, IServerNetworkable *pNetworkable ); + virtual const char *GetCannonicalName( const char *pClassName ); + void ReportEntitySizes(); + +private: + IEntityFactory *FindFactory( const char *pClassName ); +public: + CUtlDict< IEntityFactory *, unsigned short > m_Factories; +}; + +//----------------------------------------------------------------------------- +// Singleton accessor +//----------------------------------------------------------------------------- +IEntityFactoryDictionary *EntityFactoryDictionary() +{ + static CEntityFactoryDictionary s_EntityFactory; + return &s_EntityFactory; +} + +void DumpEntityFactories_f() +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + + CEntityFactoryDictionary *dict = ( CEntityFactoryDictionary * )EntityFactoryDictionary(); + if ( dict ) + { + for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) ) + { + Warning( "%s\n", dict->m_Factories.GetElementName( i ) ); + } + } +} + +static ConCommand dumpentityfactories( "dumpentityfactories", DumpEntityFactories_f, "Lists all entity factory names.", FCVAR_GAMEDLL ); + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CON_COMMAND( dump_entity_sizes, "Print sizeof(entclass)" ) +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + + ((CEntityFactoryDictionary*)EntityFactoryDictionary())->ReportEntitySizes(); +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CEntityFactoryDictionary::CEntityFactoryDictionary() : m_Factories( true, 0, 128 ) +{ +} + + +//----------------------------------------------------------------------------- +// Finds a new factory +//----------------------------------------------------------------------------- +IEntityFactory *CEntityFactoryDictionary::FindFactory( const char *pClassName ) +{ + unsigned short nIndex = m_Factories.Find( pClassName ); + if ( nIndex == m_Factories.InvalidIndex() ) + return NULL; + return m_Factories[nIndex]; +} + + +//----------------------------------------------------------------------------- +// Install a new factory +//----------------------------------------------------------------------------- +void CEntityFactoryDictionary::InstallFactory( IEntityFactory *pFactory, const char *pClassName ) +{ + Assert( FindFactory( pClassName ) == NULL ); + m_Factories.Insert( pClassName, pFactory ); +} + + +//----------------------------------------------------------------------------- +// Instantiate something using a factory +//----------------------------------------------------------------------------- +IServerNetworkable *CEntityFactoryDictionary::Create( const char *pClassName ) +{ + IEntityFactory *pFactory = FindFactory( pClassName ); + if ( !pFactory ) + { + Warning("Attempted to create unknown entity type %s!\n", pClassName ); + return NULL; + } +#if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG) + MEM_ALLOC_CREDIT_( m_Factories.GetElementName( m_Factories.Find( pClassName ) ) ); +#endif + return pFactory->Create( pClassName ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +const char *CEntityFactoryDictionary::GetCannonicalName( const char *pClassName ) +{ + return m_Factories.GetElementName( m_Factories.Find( pClassName ) ); +} + +//----------------------------------------------------------------------------- +// Destroy a networkable +//----------------------------------------------------------------------------- +void CEntityFactoryDictionary::Destroy( const char *pClassName, IServerNetworkable *pNetworkable ) +{ + IEntityFactory *pFactory = FindFactory( pClassName ); + if ( !pFactory ) + { + Warning("Attempted to destroy unknown entity type %s!\n", pClassName ); + return; + } + + pFactory->Destroy( pNetworkable ); +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CEntityFactoryDictionary::ReportEntitySizes() +{ + for ( int i = m_Factories.First(); i != m_Factories.InvalidIndex(); i = m_Factories.Next( i ) ) + { + Msg( " %s: %d", m_Factories.GetElementName( i ), m_Factories[i]->GetEntitySize() ); + } +} + + +//----------------------------------------------------------------------------- +// class CFlaggedEntitiesEnum +//----------------------------------------------------------------------------- + +CFlaggedEntitiesEnum::CFlaggedEntitiesEnum( CBaseEntity **pList, int listMax, int flagMask ) +{ + m_pList = pList; + m_listMax = listMax; + m_flagMask = flagMask; + m_count = 0; +} + +bool CFlaggedEntitiesEnum::AddToList( CBaseEntity *pEntity ) +{ + if ( m_count >= m_listMax ) + { + AssertMsgOnce( 0, "reached enumerated list limit. Increase limit, decrease radius, or make it so entity flags will work for you" ); + return false; + } + m_pList[m_count] = pEntity; + m_count++; + return true; +} + +IterationRetval_t CFlaggedEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity ) +{ + CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() ); + if ( pEntity ) + { + if ( m_flagMask && !(pEntity->GetFlags() & m_flagMask) ) // Does it meet the criteria? + return ITERATION_CONTINUE; + + if ( !AddToList( pEntity ) ) + return ITERATION_STOP; + } + + return ITERATION_CONTINUE; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int UTIL_PrecacheDecal( const char *name, bool preload ) +{ + // If this is out of order, make sure to warn. + if ( !CBaseEntity::IsPrecacheAllowed() ) + { + if ( !engine->IsDecalPrecached( name ) ) + { + Assert( !"UTIL_PrecacheDecal: too late" ); + + Warning( "Late precache of %s\n", name ); + } + } + + return engine->PrecacheDecal( name, preload ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float UTIL_GetSimulationInterval() +{ + if ( CBaseEntity::IsSimulatingOnAlternateTicks() ) + return ( TICK_INTERVAL * 2.0 ); + return TICK_INTERVAL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int UTIL_EntitiesInBox( const Vector &mins, const Vector &maxs, CFlaggedEntitiesEnum *pEnum ) +{ + partition->EnumerateElementsInBox( PARTITION_ENGINE_NON_STATIC_EDICTS, mins, maxs, false, pEnum ); + return pEnum->GetCount(); +} + +int UTIL_EntitiesAlongRay( const Ray_t &ray, CFlaggedEntitiesEnum *pEnum ) +{ + partition->EnumerateElementsAlongRay( PARTITION_ENGINE_NON_STATIC_EDICTS, ray, false, pEnum ); + return pEnum->GetCount(); +} + +int UTIL_EntitiesInSphere( const Vector ¢er, float radius, CFlaggedEntitiesEnum *pEnum ) +{ + partition->EnumerateElementsInSphere( PARTITION_ENGINE_NON_STATIC_EDICTS, center, radius, false, pEnum ); + return pEnum->GetCount(); +} + +CEntitySphereQuery::CEntitySphereQuery( const Vector ¢er, float radius, int flagMask ) +{ + m_listIndex = 0; + m_listCount = UTIL_EntitiesInSphere( m_pList, ARRAYSIZE(m_pList), center, radius, flagMask ); +} + +CBaseEntity *CEntitySphereQuery::GetCurrentEntity() +{ + if ( m_listIndex < m_listCount ) + return m_pList[m_listIndex]; + return NULL; +} + + +//----------------------------------------------------------------------------- +// Simple trace filter +//----------------------------------------------------------------------------- +class CTracePassFilter : public CTraceFilter +{ +public: + CTracePassFilter( IHandleEntity *pPassEnt ) : m_pPassEnt( pPassEnt ) {} + + bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + if ( !StandardFilterRules( pHandleEntity, contentsMask ) ) + return false; + + if (!PassServerEntityFilter( pHandleEntity, m_pPassEnt )) + return false; + + return true; + } + +private: + IHandleEntity *m_pPassEnt; +}; + + +//----------------------------------------------------------------------------- +// Drops an entity onto the floor +//----------------------------------------------------------------------------- +int UTIL_DropToFloor( CBaseEntity *pEntity, unsigned int mask, CBaseEntity *pIgnore ) +{ + // Assume no ground + pEntity->SetGroundEntity( NULL ); + + Assert( pEntity ); + + trace_t trace; + +#ifndef HL2MP + // HACK: is this really the only sure way to detect crossing a terrain boundry? + UTIL_TraceEntity( pEntity, pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin(), mask, pIgnore, pEntity->GetCollisionGroup(), &trace ); + if (trace.fraction == 0.0) + return -1; +#endif // HL2MP + + UTIL_TraceEntity( pEntity, pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() - Vector(0,0,256), mask, pIgnore, pEntity->GetCollisionGroup(), &trace ); + + if (trace.allsolid) + return -1; + + if (trace.fraction == 1) + return 0; + + pEntity->SetAbsOrigin( trace.endpos ); + pEntity->SetGroundEntity( trace.m_pEnt ); + + return 1; +} + +//----------------------------------------------------------------------------- +// Returns false if any part of the bottom of the entity is off an edge that +// is not a staircase. +//----------------------------------------------------------------------------- +bool UTIL_CheckBottom( CBaseEntity *pEntity, ITraceFilter *pTraceFilter, float flStepSize ) +{ + Vector mins, maxs, start, stop; + trace_t trace; + int x, y; + float mid, bottom; + + Assert( pEntity ); + + CTracePassFilter traceFilter(pEntity); + if ( !pTraceFilter ) + { + pTraceFilter = &traceFilter; + } + + unsigned int mask = pEntity->PhysicsSolidMaskForEntity(); + + VectorAdd (pEntity->GetAbsOrigin(), pEntity->WorldAlignMins(), mins); + VectorAdd (pEntity->GetAbsOrigin(), pEntity->WorldAlignMaxs(), maxs); + + // if all of the points under the corners are solid world, don't bother + // with the tougher checks + // the corners must be within 16 of the midpoint + start[2] = mins[2] - 1; + for (x=0 ; x<=1 ; x++) + { + for (y=0 ; y<=1 ; y++) + { + start[0] = x ? maxs[0] : mins[0]; + start[1] = y ? maxs[1] : mins[1]; + if (enginetrace->GetPointContents(start) != CONTENTS_SOLID) + goto realcheck; + } + } + return true; // we got out easy + +realcheck: + // check it for real... + start[2] = mins[2] + flStepSize; // seems to help going up/down slopes. + + // the midpoint must be within 16 of the bottom + start[0] = stop[0] = (mins[0] + maxs[0])*0.5; + start[1] = stop[1] = (mins[1] + maxs[1])*0.5; + stop[2] = start[2] - 2*flStepSize; + + UTIL_TraceLine( start, stop, mask, pTraceFilter, &trace ); + + if (trace.fraction == 1.0) + return false; + mid = bottom = trace.endpos[2]; + + // the corners must be within 16 of the midpoint + for (x=0 ; x<=1 ; x++) + { + for (y=0 ; y<=1 ; y++) + { + start[0] = stop[0] = x ? maxs[0] : mins[0]; + start[1] = stop[1] = y ? maxs[1] : mins[1]; + + UTIL_TraceLine( start, stop, mask, pTraceFilter, &trace ); + + if (trace.fraction != 1.0 && trace.endpos[2] > bottom) + bottom = trace.endpos[2]; + if (trace.fraction == 1.0 || mid - trace.endpos[2] > flStepSize) + return false; + } + } + return true; +} + + + +bool g_bDisableEhandleAccess = false; +bool g_bReceivedChainedUpdateOnRemove = false; +//----------------------------------------------------------------------------- +// Purpose: Sets the entity up for deletion. Entity will not actually be deleted +// until the next frame, so there can be no pointer errors. +// Input : *oldObj - object to delete +//----------------------------------------------------------------------------- +void UTIL_Remove( IServerNetworkable *oldObj ) +{ + CServerNetworkProperty* pProp = static_cast<CServerNetworkProperty*>( oldObj ); + if ( !pProp || pProp->IsMarkedForDeletion() ) + return; + + if ( PhysIsInCallback() ) + { + // This assert means that someone is deleting an entity inside a callback. That isn't supported so + // this code will defer the deletion of that object until the end of the current physics simulation frame + // Since this is hidden from the calling code it's preferred to call PhysCallbackRemove() directly from the caller + // in case the deferred delete will have unwanted results (like continuing to receive callbacks). That will make it + // obvious why the unwanted results are happening so the caller can handle them appropriately. (some callbacks can be masked + // or the calling entity can be flagged to filter them in most cases) + Assert(0); + PhysCallbackRemove(oldObj); + return; + } + + // mark it for deletion + pProp->MarkForDeletion( ); + + CBaseEntity *pBaseEnt = oldObj->GetBaseEntity(); + if ( pBaseEnt ) + { +#ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment + CPortalSimulator::Pre_UTIL_Remove( pBaseEnt ); +#endif + g_bReceivedChainedUpdateOnRemove = false; + pBaseEnt->UpdateOnRemove(); + + Assert( g_bReceivedChainedUpdateOnRemove ); + + // clear oldObj targetname / other flags now + pBaseEnt->SetName( NULL_STRING ); + +#ifdef PORTAL + CPortalSimulator::Post_UTIL_Remove( pBaseEnt ); +#endif + } + + gEntList.AddToDeleteList( oldObj ); +} + +void UTIL_Remove( CBaseEntity *oldObj ) +{ + if ( !oldObj ) + return; + UTIL_Remove( oldObj->NetworkProp() ); +} + +static int s_RemoveImmediateSemaphore = 0; +void UTIL_DisableRemoveImmediate() +{ + s_RemoveImmediateSemaphore++; +} +void UTIL_EnableRemoveImmediate() +{ + s_RemoveImmediateSemaphore--; + Assert(s_RemoveImmediateSemaphore>=0); +} +//----------------------------------------------------------------------------- +// Purpose: deletes an entity, without any delay. WARNING! Only use this when sure +// no pointers rely on this entity. +// Input : *oldObj - the entity to delete +//----------------------------------------------------------------------------- +void UTIL_RemoveImmediate( CBaseEntity *oldObj ) +{ + // valid pointer or already removed? + if ( !oldObj || oldObj->IsEFlagSet(EFL_KILLME) ) + return; + + if ( s_RemoveImmediateSemaphore ) + { + UTIL_Remove(oldObj); + return; + } + +#ifdef PORTAL //make sure entities are in the primary physics environment for the portal mod, this code should be safe even if the entity is in neither extra environment + CPortalSimulator::Pre_UTIL_Remove( oldObj ); +#endif + + oldObj->AddEFlags( EFL_KILLME ); // Make sure to ignore further calls into here or UTIL_Remove. + + g_bReceivedChainedUpdateOnRemove = false; + oldObj->UpdateOnRemove(); + Assert( g_bReceivedChainedUpdateOnRemove ); + + // Entities shouldn't reference other entities in their destructors + // that type of code should only occur in an UpdateOnRemove call + g_bDisableEhandleAccess = true; + delete oldObj; + g_bDisableEhandleAccess = false; + +#ifdef PORTAL + CPortalSimulator::Post_UTIL_Remove( oldObj ); +#endif +} + + +// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected +// otherwise returns NULL +// Index is 1 based +CBasePlayer *UTIL_PlayerByIndex( int playerIndex ) +{ + CBasePlayer *pPlayer = NULL; + + if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients ) + { + edict_t *pPlayerEdict = INDEXENT( playerIndex ); + if ( pPlayerEdict && !pPlayerEdict->IsFree() ) + { + pPlayer = (CBasePlayer*)GetContainingEntity( pPlayerEdict ); + } + } + + return pPlayer; +} + +CBasePlayer* UTIL_PlayerByName( const char *name ) +{ + if ( !name || !name[0] ) + return NULL; + + for (int i = 1; i<=gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + if ( !pPlayer->IsConnected() ) + continue; + + if ( Q_stricmp( pPlayer->GetPlayerName(), name ) == 0 ) + { + return pPlayer; + } + } + + return NULL; +} + +CBasePlayer* UTIL_PlayerByUserId( int userID ) +{ + for (int i = 1; i<=gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + + if ( !pPlayer ) + continue; + + if ( !pPlayer->IsConnected() ) + continue; + + if ( engine->GetPlayerUserId(pPlayer->edict()) == userID ) + { + return pPlayer; + } + } + + return NULL; +} + +// +// Return the local player. +// If this is a multiplayer game, return NULL. +// +CBasePlayer *UTIL_GetLocalPlayer( void ) +{ + if ( gpGlobals->maxClients > 1 ) + { + if ( developer.GetBool() ) + { + Assert( !"UTIL_GetLocalPlayer" ); + +#ifdef DEBUG + Warning( "UTIL_GetLocalPlayer() called in multiplayer game.\n" ); +#endif + } + + return NULL; + } + + return UTIL_PlayerByIndex( 1 ); +} + +// +// Get the local player on a listen server - this is for multiplayer use only +// +CBasePlayer *UTIL_GetListenServerHost( void ) +{ + // no "local player" if this is a dedicated server or a single player game + if (engine->IsDedicatedServer()) + { + Assert( !"UTIL_GetListenServerHost" ); + Warning( "UTIL_GetListenServerHost() called from a dedicated server or single-player game.\n" ); + return NULL; + } + + return UTIL_PlayerByIndex( 1 ); +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns true if the command was issued by the listenserver host, or by the dedicated server, via rcon or the server console. + * This is valid during ConCommand execution. + */ +bool UTIL_IsCommandIssuedByServerAdmin( void ) +{ + int issuingPlayerIndex = UTIL_GetCommandClientIndex(); + + if ( engine->IsDedicatedServer() && issuingPlayerIndex > 0 ) + return false; + +#if defined( REPLAY_ENABLED ) + // entity 1 is replay? + player_info_t pi; + bool bPlayerIsReplay = engine->GetPlayerInfo( 1, &pi ) && pi.isreplay; +#else + bool bPlayerIsReplay = false; +#endif + + if ( bPlayerIsReplay ) + { + if ( issuingPlayerIndex > 2 ) + return false; + } + else if ( issuingPlayerIndex > 1 ) + { + return false; + } + + return true; +} + + +//-------------------------------------------------------------------------------------------------------------- +/** + * Returns a CBaseEntity pointer by entindex. Index is 1 based. + */ +CBaseEntity *UTIL_EntityByIndex( int entityIndex ) +{ + CBaseEntity *entity = NULL; + + if ( entityIndex > 0 ) + { + edict_t *edict = INDEXENT( entityIndex ); + if ( edict && !edict->IsFree() ) + { + entity = GetContainingEntity( edict ); + } + } + + return entity; +} + + +int ENTINDEX( CBaseEntity *pEnt ) +{ + // This works just like ENTINDEX for edicts. + if ( pEnt ) + return pEnt->entindex(); + else + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : playerIndex - +// ping - +// packetloss - +//----------------------------------------------------------------------------- +void UTIL_GetPlayerConnectionInfo( int playerIndex, int& ping, int &packetloss ) +{ + CBasePlayer *player = UTIL_PlayerByIndex( playerIndex ); + + INetChannelInfo *nci = engine->GetPlayerNetInfo(playerIndex); + + if ( nci && player && !player->IsBot() ) + { + float latency = nci->GetAvgLatency( FLOW_OUTGOING ); // in seconds + + // that should be the correct latency, we assume that cmdrate is higher + // then updaterate, what is the case for default settings + const char * szCmdRate = engine->GetClientConVarValue( playerIndex, "cl_cmdrate" ); + + int nCmdRate = MAX( 1, Q_atoi( szCmdRate ) ); + latency -= (0.5f/nCmdRate) + TICKS_TO_TIME( 1.0f ); // correct latency + + // in GoldSrc we had a different, not fixed tickrate. so we have to adjust + // Source pings by half a tick to match the old GoldSrc pings. + latency -= TICKS_TO_TIME( 0.5f ); + + ping = latency * 1000.0f; // as msecs + ping = clamp( ping, 5, 1000 ); // set bounds, dont show pings under 5 msecs + + packetloss = 100.0f * nci->GetAvgLoss( FLOW_INCOMING ); // loss in percentage + packetloss = clamp( packetloss, 0, 100 ); + } + else + { + ping = 0; + packetloss = 0; + } +} + +static unsigned short FixedUnsigned16( float value, float scale ) +{ + int output; + + output = value * scale; + if ( output < 0 ) + output = 0; + if ( output > 0xFFFF ) + output = 0xFFFF; + + return (unsigned short)output; +} + + +//----------------------------------------------------------------------------- +// Compute shake amplitude +//----------------------------------------------------------------------------- +inline float ComputeShakeAmplitude( const Vector ¢er, const Vector &shakePt, float amplitude, float radius ) +{ + if ( radius <= 0 ) + return amplitude; + + float localAmplitude = -1; + Vector delta = center - shakePt; + float distance = delta.Length(); + + if ( distance <= radius ) + { + // Make the amplitude fall off over distance + float flPerc = 1.0 - (distance / radius); + localAmplitude = amplitude * flPerc; + } + + return localAmplitude; +} + + +//----------------------------------------------------------------------------- +// Transmits the actual shake event +//----------------------------------------------------------------------------- +inline void TransmitShakeEvent( CBasePlayer *pPlayer, float localAmplitude, float frequency, float duration, ShakeCommand_t eCommand ) +{ + if (( localAmplitude > 0 ) || ( eCommand == SHAKE_STOP )) + { + if ( eCommand == SHAKE_STOP ) + localAmplitude = 0; + + CSingleUserRecipientFilter user( pPlayer ); + user.MakeReliable(); + UserMessageBegin( user, "Shake" ); + WRITE_BYTE( eCommand ); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE) + WRITE_FLOAT( localAmplitude ); // shake magnitude/amplitude + WRITE_FLOAT( frequency ); // shake noise frequency + WRITE_FLOAT( duration ); // shake lasts this long + MessageEnd(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Shake the screen of all clients within radius. +// radius == 0, shake all clients +// UNDONE: Fix falloff model (disabled)? +// UNDONE: Affect user controls? +// Input : center - Center of screen shake, radius is measured from here. +// amplitude - Amplitude of shake +// frequency - +// duration - duration of shake in seconds. +// radius - Radius of effect, 0 shakes all clients. +// command - One of the following values: +// SHAKE_START - starts the screen shake for all players within the radius +// SHAKE_STOP - stops the screen shake for all players within the radius +// SHAKE_AMPLITUDE - modifies the amplitude of the screen shake +// for all players within the radius +// SHAKE_FREQUENCY - modifies the frequency of the screen shake +// for all players within the radius +// bAirShake - if this is false, then it will only shake players standing on the ground. +//----------------------------------------------------------------------------- +const float MAX_SHAKE_AMPLITUDE = 16.0f; +void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake ) +{ + int i; + float localAmplitude; + + if ( amplitude > MAX_SHAKE_AMPLITUDE ) + { + amplitude = MAX_SHAKE_AMPLITUDE; + } + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + + // + // Only start shakes for players that are on the ground unless doing an air shake. + // + if ( !pPlayer || (!bAirShake && (eCommand == SHAKE_START) && !(pPlayer->GetFlags() & FL_ONGROUND)) ) + { + continue; + } + + localAmplitude = ComputeShakeAmplitude( center, pPlayer->WorldSpaceCenter(), amplitude, radius ); + + // This happens if the player is outside the radius, in which case we should ignore + // all commands + if (localAmplitude < 0) + continue; + + TransmitShakeEvent( (CBasePlayer *)pPlayer, localAmplitude, frequency, duration, eCommand ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Shake an object and all players on or near it +//----------------------------------------------------------------------------- +void UTIL_ScreenShakeObject( CBaseEntity *pEnt, const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake ) +{ + int i; + float localAmplitude; + + CBaseEntity *pHighestParent = pEnt->GetRootMoveParent(); + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + if (!pPlayer) + continue; + + // Shake the object, or anything hierarchically attached to it at maximum amplitude + localAmplitude = 0; + if (pHighestParent == pPlayer->GetRootMoveParent()) + { + localAmplitude = amplitude; + } + else if ((pPlayer->GetFlags() & FL_ONGROUND) && (pPlayer->GetGroundEntity()->GetRootMoveParent() == pHighestParent)) + { + // If the player is standing on the object, use maximum amplitude + localAmplitude = amplitude; + } + else + { + // Only shake players that are on the ground. + if ( !bAirShake && !(pPlayer->GetFlags() & FL_ONGROUND) ) + { + continue; + } + + localAmplitude = ComputeShakeAmplitude( center, pPlayer->WorldSpaceCenter(), amplitude, radius ); + + // This happens if the player is outside the radius, + // in which case we should ignore all commands + if (localAmplitude < 0) + continue; + } + + TransmitShakeEvent( (CBasePlayer *)pPlayer, localAmplitude, frequency, duration, eCommand ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Punches the view of all clients within radius. +// If radius is 0, punches all clients. +// Input : center - Center of punch, radius is measured from here. +// radius - Radius of effect, 0 punches all clients. +// bInAir - if this is false, then it will only punch players standing on the ground. +//----------------------------------------------------------------------------- +void UTIL_ViewPunch( const Vector ¢er, QAngle angPunch, float radius, bool bInAir ) +{ + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + + // + // Only apply the punch to players that are on the ground unless doing an air punch. + // + if ( !pPlayer || (!bInAir && !(pPlayer->GetFlags() & FL_ONGROUND)) ) + { + continue; + } + + QAngle angTemp = angPunch; + + if ( radius > 0 ) + { + Vector delta = center - pPlayer->GetAbsOrigin(); + float distance = delta.Length(); + + if ( distance <= radius ) + { + // Make the punch amplitude fall off over distance. + float flPerc = 1.0 - (distance / radius); + angTemp *= flPerc; + } + else + { + continue; + } + } + + pPlayer->ViewPunch( angTemp ); + } +} + + +void UTIL_ScreenFadeBuild( ScreenFade_t &fade, const color32 &color, float fadeTime, float fadeHold, int flags ) +{ + fade.duration = FixedUnsigned16( fadeTime, 1<<SCREENFADE_FRACBITS ); // 7.9 fixed + fade.holdTime = FixedUnsigned16( fadeHold, 1<<SCREENFADE_FRACBITS ); // 7.9 fixed + fade.r = color.r; + fade.g = color.g; + fade.b = color.b; + fade.a = color.a; + fade.fadeFlags = flags; +} + + +void UTIL_ScreenFadeWrite( const ScreenFade_t &fade, CBaseEntity *pEntity ) +{ + if ( !pEntity || !pEntity->IsNetClient() ) + return; + + CSingleUserRecipientFilter user( (CBasePlayer *)pEntity ); + user.MakeReliable(); + + UserMessageBegin( user, "Fade" ); // use the magic #1 for "one client" + WRITE_SHORT( fade.duration ); // fade lasts this long + WRITE_SHORT( fade.holdTime ); // fade lasts this long + WRITE_SHORT( fade.fadeFlags ); // fade type (in / out) + WRITE_BYTE( fade.r ); // fade red + WRITE_BYTE( fade.g ); // fade green + WRITE_BYTE( fade.b ); // fade blue + WRITE_BYTE( fade.a ); // fade blue + MessageEnd(); +} + + +void UTIL_ScreenFadeAll( const color32 &color, float fadeTime, float fadeHold, int flags ) +{ + int i; + ScreenFade_t fade; + + + UTIL_ScreenFadeBuild( fade, color, fadeTime, fadeHold, flags ); + + for ( i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBaseEntity *pPlayer = UTIL_PlayerByIndex( i ); + + UTIL_ScreenFadeWrite( fade, pPlayer ); + } +} + + +void UTIL_ScreenFade( CBaseEntity *pEntity, const color32 &color, float fadeTime, float fadeHold, int flags ) +{ + ScreenFade_t fade; + + UTIL_ScreenFadeBuild( fade, color, fadeTime, fadeHold, flags ); + UTIL_ScreenFadeWrite( fade, pEntity ); +} + + +void UTIL_HudMessage( CBasePlayer *pToPlayer, const hudtextparms_t &textparms, const char *pMessage ) +{ + CRecipientFilter filter; + + if( pToPlayer ) + { + filter.AddRecipient( pToPlayer ); + } + else + { + filter.AddAllPlayers(); + } + + filter.MakeReliable(); + + UserMessageBegin( filter, "HudMsg" ); + WRITE_BYTE ( textparms.channel & 0xFF ); + WRITE_FLOAT( textparms.x ); + WRITE_FLOAT( textparms.y ); + WRITE_BYTE ( textparms.r1 ); + WRITE_BYTE ( textparms.g1 ); + WRITE_BYTE ( textparms.b1 ); + WRITE_BYTE ( textparms.a1 ); + WRITE_BYTE ( textparms.r2 ); + WRITE_BYTE ( textparms.g2 ); + WRITE_BYTE ( textparms.b2 ); + WRITE_BYTE ( textparms.a2 ); + WRITE_BYTE ( textparms.effect ); + WRITE_FLOAT( textparms.fadeinTime ); + WRITE_FLOAT( textparms.fadeoutTime ); + WRITE_FLOAT( textparms.holdTime ); + WRITE_FLOAT( textparms.fxTime ); + WRITE_STRING( pMessage ); + MessageEnd(); +} + +void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage ) +{ + UTIL_HudMessage( NULL, textparms, pMessage ); +} + +void UTIL_HudHintText( CBaseEntity *pEntity, const char *pMessage ) +{ + if ( !pEntity ) + return; + + CSingleUserRecipientFilter user( (CBasePlayer *)pEntity ); + user.MakeReliable(); + UserMessageBegin( user, "KeyHintText" ); + WRITE_BYTE( 1 ); // one string + WRITE_STRING( pMessage ); + MessageEnd(); +} + +void UTIL_ClientPrintFilter( IRecipientFilter& filter, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) +{ + UserMessageBegin( filter, "TextMsg" ); + WRITE_BYTE( msg_dest ); + WRITE_STRING( msg_name ); + + if ( param1 ) + WRITE_STRING( param1 ); + else + WRITE_STRING( "" ); + + if ( param2 ) + WRITE_STRING( param2 ); + else + WRITE_STRING( "" ); + + if ( param3 ) + WRITE_STRING( param3 ); + else + WRITE_STRING( "" ); + + if ( param4 ) + WRITE_STRING( param4 ); + else + WRITE_STRING( "" ); + + MessageEnd(); +} + +void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) +{ + CReliableBroadcastRecipientFilter filter; + + UTIL_ClientPrintFilter( filter, msg_dest, msg_name, param1, param2, param3, param4 ); +} + +void ClientPrint( CBasePlayer *player, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) +{ + if ( !player ) + return; + + CSingleUserRecipientFilter user( player ); + user.MakeReliable(); + + UTIL_ClientPrintFilter( user, msg_dest, msg_name, param1, param2, param3, param4 ); +} + +void UTIL_SayTextFilter( IRecipientFilter& filter, const char *pText, CBasePlayer *pPlayer, bool bChat ) +{ + UserMessageBegin( filter, "SayText" ); + if ( pPlayer ) + { + WRITE_BYTE( pPlayer->entindex() ); + } + else + { + WRITE_BYTE( 0 ); // world, dedicated server says + } + WRITE_STRING( pText ); + WRITE_BYTE( bChat ); + MessageEnd(); +} + +void UTIL_SayText2Filter( IRecipientFilter& filter, CBasePlayer *pEntity, bool bChat, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 ) +{ + UserMessageBegin( filter, "SayText2" ); + if ( pEntity ) + { + WRITE_BYTE( pEntity->entindex() ); + } + else + { + WRITE_BYTE( 0 ); // world, dedicated server says + } + + WRITE_BYTE( bChat ); + + WRITE_STRING( msg_name ); + + if ( param1 ) + WRITE_STRING( param1 ); + else + WRITE_STRING( "" ); + + if ( param2 ) + WRITE_STRING( param2 ); + else + WRITE_STRING( "" ); + + if ( param3 ) + WRITE_STRING( param3 ); + else + WRITE_STRING( "" ); + + if ( param4 ) + WRITE_STRING( param4 ); + else + WRITE_STRING( "" ); + + MessageEnd(); +} + +void UTIL_SayText( const char *pText, CBasePlayer *pToPlayer ) +{ + if ( !pToPlayer->IsNetClient() ) + return; + + CSingleUserRecipientFilter user( pToPlayer ); + user.MakeReliable(); + + UTIL_SayTextFilter( user, pText, pToPlayer, false ); +} + +void UTIL_SayTextAll( const char *pText, CBasePlayer *pPlayer, bool bChat ) +{ + CReliableBroadcastRecipientFilter filter; + UTIL_SayTextFilter( filter, pText, pPlayer, bChat ); +} + +void UTIL_ShowMessage( const char *pString, CBasePlayer *pPlayer ) +{ + CRecipientFilter filter; + + if ( pPlayer ) + { + filter.AddRecipient( pPlayer ); + } + else + { + filter.AddAllPlayers(); + } + + filter.MakeReliable(); + + UserMessageBegin( filter, "HudText" ); + WRITE_STRING( pString ); + MessageEnd(); +} + + +void UTIL_ShowMessageAll( const char *pString ) +{ + UTIL_ShowMessage( pString, NULL ); +} + +// So we always return a valid surface +static csurface_t g_NullSurface = { "**empty**", 0 }; + +void UTIL_SetTrace(trace_t& trace, const Ray_t &ray, edict_t *ent, float fraction, + int hitgroup, unsigned int contents, const Vector& normal, float intercept ) +{ + trace.startsolid = (fraction == 0.0f); + trace.fraction = fraction; + VectorCopy( ray.m_Start, trace.startpos ); + VectorMA( ray.m_Start, fraction, ray.m_Delta, trace.endpos ); + VectorCopy( normal, trace.plane.normal ); + trace.plane.dist = intercept; + trace.m_pEnt = CBaseEntity::Instance( ent ); + trace.hitgroup = hitgroup; + trace.surface = g_NullSurface; + trace.contents = contents; +} + +void UTIL_ClearTrace( trace_t &trace ) +{ + memset( &trace, 0, sizeof(trace)); + trace.fraction = 1.f; + trace.fractionleftsolid = 0; + trace.surface = g_NullSurface; +} + + + +//----------------------------------------------------------------------------- +// Sets the entity size +//----------------------------------------------------------------------------- +static void SetMinMaxSize (CBaseEntity *pEnt, const Vector& mins, const Vector& maxs ) +{ + for ( int i=0 ; i<3 ; i++ ) + { + if ( mins[i] > maxs[i] ) + { + Error( "%s: backwards mins/maxs", ( pEnt ) ? pEnt->GetDebugName() : "<NULL>" ); + } + } + + Assert( pEnt ); + + pEnt->SetCollisionBounds( mins, maxs ); +} + + +//----------------------------------------------------------------------------- +// Sets the model size +//----------------------------------------------------------------------------- +void UTIL_SetSize( CBaseEntity *pEnt, const Vector &vecMin, const Vector &vecMax ) +{ + SetMinMaxSize (pEnt, vecMin, vecMax); +} + + +//----------------------------------------------------------------------------- +// Sets the model to be associated with an entity +//----------------------------------------------------------------------------- +void UTIL_SetModel( CBaseEntity *pEntity, const char *pModelName ) +{ + // check to see if model was properly precached + int i = modelinfo->GetModelIndex( pModelName ); + if ( i == -1 ) + { + Error("%i/%s - %s: UTIL_SetModel: not precached: %s\n", pEntity->entindex(), + STRING( pEntity->GetEntityName() ), + pEntity->GetClassname(), pModelName); + } + + CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); + if ( pAnimating ) + { + pAnimating->m_nForceBone = 0; + } + + pEntity->SetModelName( AllocPooledString( pModelName ) ); + pEntity->SetModelIndex( i ) ; + SetMinMaxSize(pEntity, vec3_origin, vec3_origin); + pEntity->SetCollisionBoundsFromModel(); +} + + +void UTIL_SetOrigin( CBaseEntity *entity, const Vector &vecOrigin, bool bFireTriggers ) +{ + entity->SetLocalOrigin( vecOrigin ); + if ( bFireTriggers ) + { + entity->PhysicsTouchTriggers(); + } +} + + +void UTIL_ParticleEffect( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount ) +{ + Msg( "UTIL_ParticleEffect: Disabled\n" ); +} + +void UTIL_Smoke( const Vector &origin, const float scale, const float framerate ) +{ + g_pEffects->Smoke( origin, g_sModelIndexSmoke, scale, framerate ); +} + +// snaps a vector to the nearest axis vector (if within epsilon) +void UTIL_SnapDirectionToAxis( Vector &direction, float epsilon ) +{ + float proj = 1 - epsilon; + for ( int i = 0; i < 3; i ++ ) + { + if ( fabs(direction[i]) > proj ) + { + // snap to axis unit vector + if ( direction[i] < 0 ) + direction[i] = -1.0f; + else + direction[i] = 1.0f; + direction[(i+1)%3] = 0; + direction[(i+2)%3] = 0; + return; + } + } +} + +char *UTIL_VarArgs( const char *format, ... ) +{ + va_list argptr; + static char string[1024]; + + va_start (argptr, format); + Q_vsnprintf(string, sizeof(string), format,argptr); + va_end (argptr); + + return string; +} + +bool UTIL_IsMasterTriggered(string_t sMaster, CBaseEntity *pActivator) +{ + if (sMaster != NULL_STRING) + { + CBaseEntity *pMaster = gEntList.FindEntityByName( NULL, sMaster, NULL, pActivator ); + + if ( pMaster && (pMaster->ObjectCaps() & FCAP_MASTER) ) + { + return pMaster->IsTriggered( pActivator ); + } + + Warning( "Master was null or not a master!\n"); + } + + // if this isn't a master entity, just say yes. + return true; +} + +void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount ) +{ + if ( !UTIL_ShouldShowBlood( color ) ) + return; + + if ( g_Language.GetInt() == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED ) + color = 0; + + CPVSFilter filter( origin ); + te->BloodStream( filter, 0.0, &origin, &direction, 247, 63, 14, 255, MIN( amount, 255 ) ); +} + + +Vector UTIL_RandomBloodVector( void ) +{ + Vector direction; + + direction.x = random->RandomFloat ( -1, 1 ); + direction.y = random->RandomFloat ( -1, 1 ); + direction.z = random->RandomFloat ( 0, 1 ); + + return direction; +} + + +//------------------------------------------------------------------------------ +// Purpose : Creates both an decal and any associated impact effects (such +// as flecks) for the given iDamageType and the trace's end position +// Input : +// Output : +//------------------------------------------------------------------------------ +void UTIL_ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ) +{ + CBaseEntity *pEntity = pTrace->m_pEnt; + + // Is the entity valid, is the surface sky? + if ( !pEntity || !UTIL_IsValidEntity( pEntity ) || (pTrace->surface.flags & SURF_SKY) ) + return; + + if ( pTrace->fraction == 1.0 ) + return; + + pEntity->ImpactTrace( pTrace, iDamageType, pCustomImpactName ); +} + +/* +============== +UTIL_PlayerDecalTrace + +A player is trying to apply his custom decal for the spray can. +Tell connected clients to display it, or use the default spray can decal +if the custom can't be loaded. +============== +*/ +void UTIL_PlayerDecalTrace( trace_t *pTrace, int playernum ) +{ + if (pTrace->fraction == 1.0) + return; + + CBroadcastRecipientFilter filter; + + te->PlayerDecal( filter, 0.0, + &pTrace->endpos, playernum, pTrace->m_pEnt->entindex() ); +} + +bool UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 ) +{ + // Everyone matches unless it's teamplay + if ( !g_pGameRules->IsTeamplay() ) + return true; + + // Both on a team? + if ( *pTeamName1 != 0 && *pTeamName2 != 0 ) + { + if ( !stricmp( pTeamName1, pTeamName2 ) ) // Same Team? + return true; + } + + return false; +} + + +void UTIL_AxisStringToPointPoint( Vector &start, Vector &end, const char *pString ) +{ + char tmpstr[256]; + + Q_strncpy( tmpstr, pString, sizeof(tmpstr) ); + char *pVec = strtok( tmpstr, "," ); + int i = 0; + while ( pVec != NULL && *pVec ) + { + if ( i == 0 ) + { + UTIL_StringToVector( start.Base(), pVec ); + i++; + } + else + { + UTIL_StringToVector( end.Base(), pVec ); + } + pVec = strtok( NULL, "," ); + } +} + +void UTIL_AxisStringToPointDir( Vector &start, Vector &dir, const char *pString ) +{ + Vector end; + UTIL_AxisStringToPointPoint( start, end, pString ); + dir = end - start; + VectorNormalize(dir); +} + +void UTIL_AxisStringToUnitDir( Vector &dir, const char *pString ) +{ + Vector start; + UTIL_AxisStringToPointDir( start, dir, pString ); +} + +/* +================================================== +UTIL_ClipPunchAngleOffset +================================================== +*/ + +void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip ) +{ + QAngle final = in + punch; + + //Clip each component + for ( int i = 0; i < 3; i++ ) + { + if ( final[i] > clip[i] ) + { + final[i] = clip[i]; + } + else if ( final[i] < -clip[i] ) + { + final[i] = -clip[i]; + } + + //Return the result + in[i] = final[i] - punch[i]; + } +} + +float UTIL_WaterLevel( const Vector &position, float minz, float maxz ) +{ + Vector midUp = position; + midUp.z = minz; + + if ( !(UTIL_PointContents(midUp) & MASK_WATER) ) + return minz; + + midUp.z = maxz; + if ( UTIL_PointContents(midUp) & MASK_WATER ) + return maxz; + + float diff = maxz - minz; + while (diff > 1.0) + { + midUp.z = minz + diff/2.0; + if ( UTIL_PointContents(midUp) & MASK_WATER ) + { + minz = midUp.z; + } + else + { + maxz = midUp.z; + } + diff = maxz - minz; + } + + return midUp.z; +} + + +//----------------------------------------------------------------------------- +// Like UTIL_WaterLevel, but *way* less expensive. +// I didn't replace UTIL_WaterLevel everywhere to avoid breaking anything. +//----------------------------------------------------------------------------- +class CWaterTraceFilter : public CTraceFilter +{ +public: + bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) + { + CBaseEntity *pCollide = EntityFromEntityHandle( pHandleEntity ); + + // Static prop case... + if ( !pCollide ) + return false; + + // Only impact water stuff... + if ( pCollide->GetSolidFlags() & FSOLID_VOLUME_CONTENTS ) + return true; + + return false; + } +}; + +float UTIL_FindWaterSurface( const Vector &position, float minz, float maxz ) +{ + Vector vecStart, vecEnd; + vecStart.Init( position.x, position.y, maxz ); + vecEnd.Init( position.x, position.y, minz ); + + Ray_t ray; + trace_t tr; + CWaterTraceFilter waterTraceFilter; + ray.Init( vecStart, vecEnd ); + enginetrace->TraceRay( ray, MASK_WATER, &waterTraceFilter, &tr ); + + return tr.endpos.z; +} + + +extern short g_sModelIndexBubbles;// holds the index for the bubbles model + +void UTIL_Bubbles( const Vector& mins, const Vector& maxs, int count ) +{ + Vector mid = (mins + maxs) * 0.5; + + float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 ); + flHeight = flHeight - mins.z; + + CPASFilter filter( mid ); + + te->Bubbles( filter, 0.0, + &mins, &maxs, flHeight, g_sModelIndexBubbles, count, 8.0 ); +} + +void UTIL_BubbleTrail( const Vector& from, const Vector& to, int count ) +{ + // Find water surface will return from.z if the from point is above water + float flStartHeight = UTIL_FindWaterSurface( from, from.z, from.z + 256 ); + flStartHeight = flStartHeight - from.z; + + float flEndHeight = UTIL_FindWaterSurface( to, to.z, to.z + 256 ); + flEndHeight = flEndHeight - to.z; + + if ( ( flStartHeight == 0 ) && ( flEndHeight == 0 ) ) + return; + + float flWaterZ = flStartHeight + from.z; + + const Vector *pFrom = &from; + const Vector *pTo = &to; + Vector vecWaterPoint; + if ( ( flStartHeight == 0 ) || ( flEndHeight == 0 ) ) + { + if ( flStartHeight == 0 ) + { + flWaterZ = flEndHeight + to.z; + } + + float t = IntersectRayWithAAPlane( from, to, 2, 1.0f, flWaterZ ); + Assert( (t >= -1e-3f) && ( t <= 1.0f ) ); + VectorLerp( from, to, t, vecWaterPoint ); + if ( flStartHeight == 0 ) + { + pFrom = &vecWaterPoint; + + // Reduce the count by the actual length + count = (int)( count * ( 1.0f - t ) ); + } + else + { + pTo = &vecWaterPoint; + + // Reduce the count by the actual length + count = (int)( count * t ); + } + } + + CBroadcastRecipientFilter filter; + te->BubbleTrail( filter, 0.0, pFrom, pTo, flWaterZ, g_sModelIndexBubbles, count, 8.0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : Start - +// End - +// ModelIndex - +// FrameStart - +// FrameRate - +// Life - +// Width - +// Noise - +// Red - +// Green - +// Brightness - +// Speed - +//----------------------------------------------------------------------------- +void UTIL_Beam( Vector &Start, Vector &End, int nModelIndex, int nHaloIndex, unsigned char FrameStart, unsigned char FrameRate, + float Life, unsigned char Width, unsigned char EndWidth, unsigned char FadeLength, unsigned char Noise, unsigned char Red, unsigned char Green, + unsigned char Blue, unsigned char Brightness, unsigned char Speed) +{ + CBroadcastRecipientFilter filter; + + te->BeamPoints( filter, 0.0, + &Start, + &End, + nModelIndex, + nHaloIndex, + FrameStart, + FrameRate, + Life, + Width, + EndWidth, + FadeLength, + Noise, + Red, + Green, + Blue, + Brightness, + Speed ); +} + +bool UTIL_IsValidEntity( CBaseEntity *pEnt ) +{ + edict_t *pEdict = pEnt->edict(); + if ( !pEdict || pEdict->IsFree() ) + return false; + return true; +} + + +#define PRECACHE_OTHER_ONCE +// UNDONE: Do we need this to avoid doing too much of this? Measure startup times and see +#if defined( PRECACHE_OTHER_ONCE ) + +#include "utlsymbol.h" +class CPrecacheOtherList : public CAutoGameSystem +{ +public: + CPrecacheOtherList( char const *name ) : CAutoGameSystem( name ) + { + } + virtual void LevelInitPreEntity(); + virtual void LevelShutdownPostEntity(); + + bool AddOrMarkPrecached( const char *pClassname ); + +private: + CUtlSymbolTable m_list; +}; + +void CPrecacheOtherList::LevelInitPreEntity() +{ + m_list.RemoveAll(); +} + +void CPrecacheOtherList::LevelShutdownPostEntity() +{ + m_list.RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: mark or add +// Input : *pEntity - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPrecacheOtherList::AddOrMarkPrecached( const char *pClassname ) +{ + CUtlSymbol sym = m_list.Find( pClassname ); + if ( sym.IsValid() ) + return false; + + m_list.AddString( pClassname ); + return true; +} + +CPrecacheOtherList g_PrecacheOtherList( "CPrecacheOtherList" ); +#endif + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *szClassname - +// *modelName - +//----------------------------------------------------------------------------- +void UTIL_PrecacheOther( const char *szClassname, const char *modelName ) +{ +#if defined( PRECACHE_OTHER_ONCE ) + // already done this one?, if not, mark as done + if ( !g_PrecacheOtherList.AddOrMarkPrecached( szClassname ) ) + return; +#endif + + CBaseEntity *pEntity = CreateEntityByName( szClassname ); + if ( !pEntity ) + { + Warning( "NULL Ent in UTIL_PrecacheOther\n" ); + return; + } + + // If we have a specified model, set it before calling precache + if ( modelName && modelName[0] ) + { + pEntity->SetModelName( AllocPooledString( modelName ) ); + } + + if (pEntity) + pEntity->Precache( ); + + UTIL_RemoveImmediate( pEntity ); +} + +//========================================================= +// UTIL_LogPrintf - Prints a logged message to console. +// Preceded by LOG: ( timestamp ) < message > +//========================================================= +void UTIL_LogPrintf( const char *fmt, ... ) +{ + va_list argptr; + char tempString[1024]; + + va_start ( argptr, fmt ); + Q_vsnprintf( tempString, sizeof(tempString), fmt, argptr ); + va_end ( argptr ); + + // Print to server console + engine->LogPrint( tempString ); +} + +//========================================================= +// UTIL_DotPoints - returns the dot product of a line from +// src to check and vecdir. +//========================================================= +float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir ) +{ + Vector2D vec2LOS; + + vec2LOS = ( vecCheck - vecSrc ).AsVector2D(); + Vector2DNormalize( vec2LOS ); + + return DotProduct2D(vec2LOS, vecDir.AsVector2D()); +} + + +//========================================================= +// UTIL_StripToken - for redundant keynames +//========================================================= +void UTIL_StripToken( const char *pKey, char *pDest ) +{ + int i = 0; + + while ( pKey[i] && pKey[i] != '#' ) + { + pDest[i] = pKey[i]; + i++; + } + pDest[i] = 0; +} + + +// computes gravity scale for an absolute gravity. Pass the result into CBaseEntity::SetGravity() +float UTIL_ScaleForGravity( float desiredGravity ) +{ + float worldGravity = GetCurrentGravity(); + return worldGravity > 0 ? desiredGravity / worldGravity : 0; +} + + +//----------------------------------------------------------------------------- +// Purpose: Implemented for mathlib.c error handling +// Input : *error - +//----------------------------------------------------------------------------- +extern "C" void Sys_Error( char *error, ... ) +{ + va_list argptr; + char string[1024]; + + va_start( argptr, error ); + Q_vsnprintf( string, sizeof(string), error, argptr ); + va_end( argptr ); + + Warning( "%s", string ); + Assert(0); +} + + +//----------------------------------------------------------------------------- +// Purpose: Spawns an entity into the game, initializing it with the map ent data block +// Input : *pEntity - the newly created entity +// *mapData - pointer a block of entity map data +// Output : -1 if the entity was not successfully created; 0 on success +//----------------------------------------------------------------------------- +int DispatchSpawn( CBaseEntity *pEntity ) +{ + if ( pEntity ) + { + MDLCACHE_CRITICAL_SECTION(); + + // keep a smart pointer that will now if the object gets deleted + EHANDLE pEntSafe; + pEntSafe = pEntity; + + // Initialize these or entities who don't link to the world won't have anything in here + // is this necessary? + //pEntity->SetAbsMins( pEntity->GetOrigin() - Vector(1,1,1) ); + //pEntity->SetAbsMaxs( pEntity->GetOrigin() + Vector(1,1,1) ); + +#if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG) + const char *pszClassname = NULL; + int iClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.Find( pEntity->GetClassname() ); + if ( iClassname != ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.InvalidIndex() ) + pszClassname = ((CEntityFactoryDictionary*)EntityFactoryDictionary())->m_Factories.GetElementName( iClassname ); + if ( pszClassname ) + { + MemAlloc_PushAllocDbgInfo( pszClassname, __LINE__ ); + } +#endif + bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false ); + CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); + if (!pAnimating) + { + pEntity->Spawn(); + } + else + { + // Don't allow the PVS check to skip animation setup during spawning + pAnimating->SetBoneCacheFlags( BCF_IS_IN_SPAWN ); + pEntity->Spawn(); + if ( pEntSafe != NULL ) + pAnimating->ClearBoneCacheFlags( BCF_IS_IN_SPAWN ); + } + mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims ); + +#if defined(TRACK_ENTITY_MEMORY) && defined(USE_MEM_DEBUG) + if ( pszClassname ) + { + MemAlloc_PopAllocDbgInfo(); + } +#endif + // Try to get the pointer again, in case the spawn function deleted the entity. + // UNDONE: Spawn() should really return a code to ask that the entity be deleted, but + // that would touch too much code for me to do that right now. + + if ( pEntSafe == NULL || pEntity->IsMarkedForDeletion() ) + return -1; + + if ( pEntity->m_iGlobalname != NULL_STRING ) + { + // Handle global stuff here + int globalIndex = GlobalEntity_GetIndex( pEntity->m_iGlobalname ); + if ( globalIndex >= 0 ) + { + // Already dead? delete + if ( GlobalEntity_GetState(globalIndex) == GLOBAL_DEAD ) + { + pEntity->Remove(); + return -1; + } + else if ( !FStrEq(STRING(gpGlobals->mapname), GlobalEntity_GetMap(globalIndex)) ) + { + pEntity->MakeDormant(); // Hasn't been moved to this level yet, wait but stay alive + } + // In this level & not dead, continue on as normal + } + else + { + // Spawned entities default to 'On' + GlobalEntity_Add( pEntity->m_iGlobalname, gpGlobals->mapname, GLOBAL_ON ); +// Msg( "Added global entity %s (%s)\n", pEntity->GetClassname(), STRING(pEntity->m_iGlobalname) ); + } + } + + gEntList.NotifySpawn( pEntity ); + } + + return 0; +} + +// UNDONE: This could be a better test - can we run the absbox through the bsp and see +// if it contains any solid space? or would that eliminate some entities we want to keep? +int UTIL_EntityInSolid( CBaseEntity *ent ) +{ + Vector point; + + CBaseEntity *pParent = ent->GetMoveParent(); + // HACKHACK -- If you're attached to a client, always go through + if ( pParent ) + { + if ( pParent->IsPlayer() ) + return 0; + + ent = ent->GetRootMoveParent(); + } + + point = ent->WorldSpaceCenter(); + return ( enginetrace->GetPointContents( point ) & MASK_SOLID ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Initialize the matrix from an entity +// Input : *pEntity - +//----------------------------------------------------------------------------- +void EntityMatrix::InitFromEntity( CBaseEntity *pEntity, int iAttachment ) +{ + if ( !pEntity ) + { + Identity(); + return; + } + + // Get an attachment's matrix? + if ( iAttachment != 0 ) + { + CBaseAnimating *pAnimating = pEntity->GetBaseAnimating(); + if ( pAnimating && pAnimating->GetModelPtr() ) + { + Vector vOrigin; + QAngle vAngles; + if ( pAnimating->GetAttachment( iAttachment, vOrigin, vAngles ) ) + { + ((VMatrix *)this)->SetupMatrixOrgAngles( vOrigin, vAngles ); + return; + } + } + } + + ((VMatrix *)this)->SetupMatrixOrgAngles( pEntity->GetAbsOrigin(), pEntity->GetAbsAngles() ); +} + + +void EntityMatrix::InitFromEntityLocal( CBaseEntity *entity ) +{ + if ( !entity || !entity->edict() ) + { + Identity(); + return; + } + ((VMatrix *)this)->SetupMatrixOrgAngles( entity->GetLocalOrigin(), entity->GetLocalAngles() ); +} + +//================================================== +// Purpose: +// Input: +// Output: +//================================================== + +void UTIL_ValidateSoundName( string_t &name, const char *defaultStr ) +{ + if ( ( !name || + strlen( (char*) STRING( name ) ) < 1 ) || + !Q_stricmp( (char *)STRING(name), "0" ) ) + { + name = AllocPooledString( defaultStr ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Slightly modified strtok. Does not modify the input string. Does +// not skip over more than one separator at a time. This allows parsing +// strings where tokens between separators may or may not be present: +// +// Door01,,,0 would be parsed as "Door01" "" "" "0" +// Door01,Open,,0 would be parsed as "Door01" "Open" "" "0" +// +// Input : token - Returns with a token, or zero length if the token was missing. +// str - String to parse. +// sep - Character to use as separator. UNDONE: allow multiple separator chars +// Output : Returns a pointer to the next token to be parsed. +//----------------------------------------------------------------------------- +const char *nexttoken(char *token, const char *str, char sep) +{ + if ((str == NULL) || (*str == '\0')) + { + *token = '\0'; + return(NULL); + } + + // + // Copy everything up to the first separator into the return buffer. + // Do not include separators in the return buffer. + // + while ((*str != sep) && (*str != '\0')) + { + *token++ = *str++; + } + *token = '\0'; + + // + // Advance the pointer unless we hit the end of the input string. + // + if (*str == '\0') + { + return(str); + } + + return(++str); +} + +//----------------------------------------------------------------------------- +// Purpose: Helper for UTIL_FindClientInPVS +// Input : check - last checked client +// Output : static int UTIL_GetNewCheckClient +//----------------------------------------------------------------------------- +// FIXME: include bspfile.h here? +class CCheckClient : public CAutoGameSystem +{ +public: + CCheckClient( char const *name ) : CAutoGameSystem( name ) + { + } + + void LevelInitPreEntity() + { + m_checkCluster = -1; + m_lastcheck = 1; + m_lastchecktime = -1; + m_bClientPVSIsExpanded = false; + } + + byte m_checkPVS[MAX_MAP_LEAFS/8]; + byte m_checkVisibilityPVS[MAX_MAP_LEAFS/8]; + int m_checkCluster; + int m_lastcheck; + float m_lastchecktime; + bool m_bClientPVSIsExpanded; +}; + +CCheckClient g_CheckClient( "CCheckClient" ); + + +static int UTIL_GetNewCheckClient( int check ) +{ + int i; + edict_t *ent; + Vector org; + +// cycle to the next one + + if (check < 1) + check = 1; + if (check > gpGlobals->maxClients) + check = gpGlobals->maxClients; + + if (check == gpGlobals->maxClients) + i = 1; + else + i = check + 1; + + for ( ; ; i++) + { + if ( i > gpGlobals->maxClients ) + { + i = 1; + } + + ent = engine->PEntityOfEntIndex( i ); + if ( !ent ) + continue; + + // Looped but didn't find anything else + if ( i == check ) + break; + + if ( !ent->GetUnknown() ) + continue; + + CBaseEntity *entity = GetContainingEntity( ent ); + if ( !entity ) + continue; + + if ( entity->GetFlags() & FL_NOTARGET ) + continue; + + // anything that is a client, or has a client as an enemy + break; + } + + if ( i != check ) + { + memset( g_CheckClient.m_checkVisibilityPVS, 0, sizeof(g_CheckClient.m_checkVisibilityPVS) ); + g_CheckClient.m_bClientPVSIsExpanded = false; + } + + if ( ent ) + { + // get the PVS for the entity + CBaseEntity *pce = GetContainingEntity( ent ); + if ( !pce ) + return i; + + org = pce->EyePosition(); + + int clusterIndex = engine->GetClusterForOrigin( org ); + if ( clusterIndex != g_CheckClient.m_checkCluster ) + { + g_CheckClient.m_checkCluster = clusterIndex; + engine->GetPVSForCluster( clusterIndex, sizeof(g_CheckClient.m_checkPVS), g_CheckClient.m_checkPVS ); + } + } + + return i; +} + + +//----------------------------------------------------------------------------- +// Gets the current check client.... +//----------------------------------------------------------------------------- +static edict_t *UTIL_GetCurrentCheckClient() +{ + edict_t *ent; + + // find a new check if on a new frame + float delta = gpGlobals->curtime - g_CheckClient.m_lastchecktime; + if ( delta >= 0.1 || delta < 0 ) + { + g_CheckClient.m_lastcheck = UTIL_GetNewCheckClient( g_CheckClient.m_lastcheck ); + g_CheckClient.m_lastchecktime = gpGlobals->curtime; + } + + // return check if it might be visible + ent = engine->PEntityOfEntIndex( g_CheckClient.m_lastcheck ); + + // Allow dead clients -- JAY + // Our monsters know the difference, and this function gates alot of behavior + // It's annoying to die and see monsters stop thinking because you're no longer + // "in" their PVS + if ( !ent || ent->IsFree() || !ent->GetUnknown()) + { + return NULL; + } + + return ent; +} + +void UTIL_SetClientVisibilityPVS( edict_t *pClient, const unsigned char *pvs, int pvssize ) +{ + if ( pClient == UTIL_GetCurrentCheckClient() ) + { + Assert( pvssize <= sizeof(g_CheckClient.m_checkVisibilityPVS) ); + + g_CheckClient.m_bClientPVSIsExpanded = false; + + unsigned *pFrom = (unsigned *)pvs; + unsigned *pMask = (unsigned *)g_CheckClient.m_checkPVS; + unsigned *pTo = (unsigned *)g_CheckClient.m_checkVisibilityPVS; + + int limit = pvssize / 4; + int i; + + for ( i = 0; i < limit; i++ ) + { + pTo[i] = pFrom[i] & ~pMask[i]; + + if ( pFrom[i] ) + { + g_CheckClient.m_bClientPVSIsExpanded = true; + } + } + + int remainder = pvssize % 4; + for ( i = 0; i < remainder; i++ ) + { + ((unsigned char *)&pTo[limit])[i] = ((unsigned char *)&pFrom[limit])[i] & !((unsigned char *)&pMask[limit])[i]; + + if ( ((unsigned char *)&pFrom[limit])[i] != 0) + { + g_CheckClient.m_bClientPVSIsExpanded = true; + } + } + } +} + +bool UTIL_ClientPVSIsExpanded() +{ + return g_CheckClient.m_bClientPVSIsExpanded; +} + + +//----------------------------------------------------------------------------- +// Purpose: Returns a client (or object that has a client enemy) that would be a valid target. +// If there are more than one valid options, they are cycled each frame +// If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all. +// Input : *pEdict - +// Output : edict_t* +//----------------------------------------------------------------------------- +CBaseEntity *UTIL_FindClientInPVS( const Vector &vecBoxMins, const Vector &vecBoxMaxs ) +{ + edict_t *ent = UTIL_GetCurrentCheckClient(); + if ( !ent ) + { + return NULL; + } + + if ( !engine->CheckBoxInPVS( vecBoxMins, vecBoxMaxs, g_CheckClient.m_checkPVS, sizeof( g_CheckClient.m_checkPVS ) ) ) + { + return NULL; + } + + // might be able to see it + return GetContainingEntity( ent ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a client (or object that has a client enemy) that would be a valid target. +// If there are more than one valid options, they are cycled each frame +// If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all. +// Input : *pEdict - +// Output : edict_t* +//----------------------------------------------------------------------------- +ConVar sv_strict_notarget( "sv_strict_notarget", "0", 0, "If set, notarget will cause entities to never think they are in the pvs" ); + +static edict_t *UTIL_FindClientInPVSGuts(edict_t *pEdict, unsigned char *pvs, unsigned pvssize ) +{ + Vector view; + + edict_t *ent = UTIL_GetCurrentCheckClient(); + if ( !ent ) + { + return NULL; + } + + CBaseEntity *pPlayerEntity = GetContainingEntity( ent ); + if( (!pPlayerEntity || (pPlayerEntity->GetFlags() & FL_NOTARGET)) && sv_strict_notarget.GetBool() ) + { + return NULL; + } + // if current entity can't possibly see the check entity, return 0 + // UNDONE: Build a box for this and do it over that box + // UNDONE: Use CM_BoxLeafnums() + CBaseEntity *pe = GetContainingEntity( pEdict ); + if ( pe ) + { + view = pe->EyePosition(); + + if ( !engine->CheckOriginInPVS( view, pvs, pvssize ) ) + { + return NULL; + } + } + + // might be able to see it + return ent; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a client that could see the entity directly +//----------------------------------------------------------------------------- + +edict_t *UTIL_FindClientInPVS(edict_t *pEdict) +{ + return UTIL_FindClientInPVSGuts( pEdict, g_CheckClient.m_checkPVS, sizeof( g_CheckClient.m_checkPVS ) ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a client that could see the entity, including through a camera +//----------------------------------------------------------------------------- +edict_t *UTIL_FindClientInVisibilityPVS( edict_t *pEdict ) +{ + return UTIL_FindClientInPVSGuts( pEdict, g_CheckClient.m_checkVisibilityPVS, sizeof( g_CheckClient.m_checkVisibilityPVS ) ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: Returns a chain of entities within the PVS of another entity (client) +// starting_ent is the ent currently at in the list +// a starting_ent of NULL signifies the beginning of a search +// Input : *pplayer - +// *starting_ent - +// Output : edict_t +//----------------------------------------------------------------------------- +CBaseEntity *UTIL_EntitiesInPVS( CBaseEntity *pPVSEntity, CBaseEntity *pStartingEntity ) +{ + Vector org; + static byte pvs[ MAX_MAP_CLUSTERS/8 ]; + static Vector lastOrg( 0, 0, 0 ); + static int lastCluster = -1; + + if ( !pPVSEntity ) + return NULL; + + // NOTE: These used to be caching code here to prevent this from + // being called over+over which breaks when you go back + forth + // across level transitions + // So, we'll always get the PVS each time we start a new EntitiesInPVS iteration. + // Given that weapon_binocs + leveltransition code is the only current clients + // of this, this seems safe. + if ( !pStartingEntity ) + { + org = pPVSEntity->EyePosition(); + int clusterIndex = engine->GetClusterForOrigin( org ); + Assert( clusterIndex >= 0 ); + engine->GetPVSForCluster( clusterIndex, sizeof(pvs), pvs ); + } + + for ( CBaseEntity *pEntity = gEntList.NextEnt(pStartingEntity); pEntity; pEntity = gEntList.NextEnt(pEntity) ) + { + // Only return attached ents. + if ( !pEntity->edict() ) + continue; + + CBaseEntity *pParent = pEntity->GetRootMoveParent(); + + Vector vecSurroundMins, vecSurroundMaxs; + pParent->CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs ); + if ( !engine->CheckBoxInPVS( vecSurroundMins, vecSurroundMaxs, pvs, sizeof( pvs ) ) ) + continue; + + return pEntity; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Get the predicted postion of an entity of a certain number of seconds +// Use this function with caution, it has great potential for annoying the player, especially +// if used for target firing predition +// Input : *pTarget - target entity to predict +// timeDelta - amount of time to predict ahead (in seconds) +// &vecPredictedPosition - output +//----------------------------------------------------------------------------- +void UTIL_PredictedPosition( CBaseEntity *pTarget, float flTimeDelta, Vector *vecPredictedPosition ) +{ + if ( ( pTarget == NULL ) || ( vecPredictedPosition == NULL ) ) + return; + + Vector vecPredictedVel; + + //FIXME: Should we look at groundspeed or velocity for non-clients?? + + //Get the proper velocity to predict with + CBasePlayer *pPlayer = ToBasePlayer( pTarget ); + + //Player works differently than other entities + if ( pPlayer != NULL ) + { + if ( pPlayer->IsInAVehicle() ) + { + //Calculate the predicted position in this vehicle + vecPredictedVel = pPlayer->GetVehicleEntity()->GetSmoothedVelocity(); + } + else + { + //Get the player's stored velocity + vecPredictedVel = pPlayer->GetSmoothedVelocity(); + } + } + else + { + // See if we're a combat character in a vehicle + CBaseCombatCharacter *pCCTarget = pTarget->MyCombatCharacterPointer(); + if ( pCCTarget != NULL && pCCTarget->IsInAVehicle() ) + { + //Calculate the predicted position in this vehicle + vecPredictedVel = pCCTarget->GetVehicleEntity()->GetSmoothedVelocity(); + } + else + { + // See if we're an animating entity + CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(pTarget); + if ( pAnimating != NULL ) + { + vecPredictedVel = pAnimating->GetGroundSpeedVelocity(); + } + else + { + // Otherwise we're a vanilla entity + vecPredictedVel = pTarget->GetSmoothedVelocity(); + } + } + } + + //Get the result + (*vecPredictedPosition) = pTarget->GetAbsOrigin() + ( vecPredictedVel * flTimeDelta ); +} + +//----------------------------------------------------------------------------- +// Purpose: Points the destination entity at the target entity +// Input : *pDest - entity to be pointed at the target +// *pTarget - target to point at +//----------------------------------------------------------------------------- +bool UTIL_PointAtEntity( CBaseEntity *pDest, CBaseEntity *pTarget ) +{ + if ( ( pDest == NULL ) || ( pTarget == NULL ) ) + { + return false; + } + + Vector dir = (pTarget->GetAbsOrigin() - pDest->GetAbsOrigin()); + + VectorNormalize( dir ); + + //Store off as angles + QAngle angles; + VectorAngles( dir, angles ); + pDest->SetLocalAngles( angles ); + pDest->SetAbsAngles( angles ); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Points the destination entity at the target entity by name +// Input : *pDest - entity to be pointed at the target +// strTarget - name of entity to target (will only choose the first!) +//----------------------------------------------------------------------------- +void UTIL_PointAtNamedEntity( CBaseEntity *pDest, string_t strTarget ) +{ + //Attempt to find the entity + if ( !UTIL_PointAtEntity( pDest, gEntList.FindEntityByName( NULL, strTarget ) ) ) + { + DevMsg( 1, "%s (%s) was unable to point at an entity named: %s\n", pDest->GetClassname(), pDest->GetDebugName(), STRING( strTarget ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Copy the pose parameter values from one entity to the other +// Input : *pSourceEntity - entity to copy from +// *pDestEntity - entity to copy to +//----------------------------------------------------------------------------- +bool UTIL_TransferPoseParameters( CBaseEntity *pSourceEntity, CBaseEntity *pDestEntity ) +{ + CBaseAnimating *pSourceBaseAnimating = dynamic_cast<CBaseAnimating*>( pSourceEntity ); + CBaseAnimating *pDestBaseAnimating = dynamic_cast<CBaseAnimating*>( pDestEntity ); + + if ( !pSourceBaseAnimating || !pDestBaseAnimating ) + return false; + + for ( int iPose = 0; iPose < MAXSTUDIOPOSEPARAM; ++iPose ) + { + pDestBaseAnimating->SetPoseParameter( iPose, pSourceBaseAnimating->GetPoseParameter( iPose ) ); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Make a muzzle flash appear +// Input : &origin - position of the muzzle flash +// &angles - angles of the fire direction +// scale - scale of the muzzle flash +// type - type of muzzle flash +//----------------------------------------------------------------------------- +void UTIL_MuzzleFlash( const Vector &origin, const QAngle &angles, int scale, int type ) +{ + CPASFilter filter( origin ); + + te->MuzzleFlash( filter, 0.0f, origin, angles, scale, type ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : vStartPos - start of the line +// vEndPos - end of the line +// vPoint - point to find nearest point to on specified line +// clampEnds - clamps returned points to being on the line segment specified +// Output : Vector - nearest point on the specified line +//----------------------------------------------------------------------------- +Vector UTIL_PointOnLineNearestPoint(const Vector& vStartPos, const Vector& vEndPos, const Vector& vPoint, bool clampEnds ) +{ + Vector vEndToStart = (vEndPos - vStartPos); + Vector vOrgToStart = (vPoint - vStartPos); + float fNumerator = DotProduct(vEndToStart,vOrgToStart); + float fDenominator = vEndToStart.Length() * vOrgToStart.Length(); + float fIntersectDist = vOrgToStart.Length()*(fNumerator/fDenominator); + float flLineLength = VectorNormalize( vEndToStart ); + + if ( clampEnds ) + { + fIntersectDist = clamp( fIntersectDist, 0.0f, flLineLength ); + } + + Vector vIntersectPos = vStartPos + vEndToStart * fIntersectDist; + + return vIntersectPos; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +AngularImpulse WorldToLocalRotation( const VMatrix &localToWorld, const Vector &worldAxis, float rotation ) +{ + // fix axes of rotation to match axes of vector + Vector rot = worldAxis * rotation; + // since the matrix maps local to world, do a transpose rotation to get world to local + AngularImpulse ang = localToWorld.VMul3x3Transpose( rot ); + + return ang; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *filename - +// *pLength - +// Output : byte +//----------------------------------------------------------------------------- +byte *UTIL_LoadFileForMe( const char *filename, int *pLength ) +{ + void *buffer = NULL; + + int length = filesystem->ReadFileEx( filename, "GAME", &buffer, true, true ); + + if ( pLength ) + { + *pLength = length; + } + + return (byte *)buffer; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *buffer - +//----------------------------------------------------------------------------- +void UTIL_FreeFile( byte *buffer ) +{ + filesystem->FreeOptimalReadBuffer( buffer ); +} + +//----------------------------------------------------------------------------- +// Purpose: Determines whether an entity is within a certain angular tolerance to viewer +// Input : *pEntity - entity which is the "viewer" +// vecPosition - position to test against +// flTolerance - tolerance (as dot-product) +// *pflDot - if not NULL, holds the +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, const Vector &vecPosition, float flDotTolerance, float *pflDot /*= NULL*/ ) +{ + if ( pflDot ) + { + *pflDot = 0.0f; + } + + // Required elements + if ( pViewer == NULL ) + return false; + + Vector forward; + pViewer->GetVectors( &forward, NULL, NULL ); + + Vector dir = vecPosition - pViewer->GetAbsOrigin(); + VectorNormalize( dir ); + + // Larger dot product corresponds to a smaller angle + float flDot = dir.Dot( forward ); + + // Return the result + if ( pflDot ) + { + *pflDot = flDot; + } + + // Within the goal tolerance + if ( flDot >= flDotTolerance ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Determines whether an entity is within a certain angular tolerance to viewer +// Input : *pEntity - entity which is the "viewer" +// *pTarget - entity to test against +// flTolerance - tolerance (as dot-product) +// *pflDot - if not NULL, holds the +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool UTIL_IsFacingWithinTolerance( CBaseEntity *pViewer, CBaseEntity *pTarget, float flDotTolerance, float *pflDot /*= NULL*/ ) +{ + if ( pViewer == NULL || pTarget == NULL ) + return false; + + return UTIL_IsFacingWithinTolerance( pViewer, pTarget->GetAbsOrigin(), flDotTolerance, pflDot ); +} + +//----------------------------------------------------------------------------- +// Purpose: Fills in color for debug purposes based on a relationship +// Input : nRelationship - relationship to test +// *pR, *pG, *pB - colors to fill +//----------------------------------------------------------------------------- +void UTIL_GetDebugColorForRelationship( int nRelationship, int &r, int &g, int &b ) +{ + switch ( nRelationship ) + { + case D_LI: + r = 0; + g = 255; + b = 0; + break; + case D_NU: + r = 0; + g = 0; + b = 255; + break; + case D_HT: + r = 255; + g = 0; + b = 0; + break; + case D_FR: + r = 255; + g = 255; + b = 0; + break; + default: + r = 255; + g = 255; + b = 255; + break; + } +} + +void LoadAndSpawnEntities_ParseEntKVBlockHelper( CBaseEntity *pNode, KeyValues *pkvNode ) +{ + KeyValues *pkvNodeData = pkvNode->GetFirstSubKey(); + while ( pkvNodeData ) + { + // Handle the connections block + if ( !Q_strcmp(pkvNodeData->GetName(), "connections") ) + { + LoadAndSpawnEntities_ParseEntKVBlockHelper( pNode, pkvNodeData ); + } + else + { + pNode->KeyValue( pkvNodeData->GetName(), pkvNodeData->GetString() ); + } + + pkvNodeData = pkvNodeData->GetNextKey(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Loads and parses a file and spawns entities defined in it. +//----------------------------------------------------------------------------- +bool UTIL_LoadAndSpawnEntitiesFromScript( CUtlVector <CBaseEntity*> &entities, const char *pScriptFile, const char *pBlock, bool bActivate ) +{ + KeyValues *pkvFile = new KeyValues( pBlock ); + + if ( pkvFile->LoadFromFile( filesystem, pScriptFile, "MOD" ) ) + { + // Load each block, and spawn the entities + KeyValues *pkvNode = pkvFile->GetFirstSubKey(); + while ( pkvNode ) + { + // Get name + const char *pNodeName = pkvNode->GetName(); + + if ( stricmp( pNodeName, "entity" ) ) + { + pkvNode = pkvNode->GetNextKey(); + continue; + } + + KeyValues *pClassname = pkvNode->FindKey( "classname" ); + + if ( pClassname ) + { + // Use the classname instead + pNodeName = pClassname->GetString(); + } + + // Spawn the entity + CBaseEntity *pNode = CreateEntityByName( pNodeName ); + + if ( pNode ) + { + LoadAndSpawnEntities_ParseEntKVBlockHelper( pNode, pkvNode ); + DispatchSpawn( pNode ); + entities.AddToTail( pNode ); + } + else + { + Warning( "UTIL_LoadAndSpawnEntitiesFromScript: Failed to spawn entity, type: '%s'\n", pNodeName ); + } + + // Move to next entity + pkvNode = pkvNode->GetNextKey(); + } + + if ( bActivate == true ) + { + bool bAsyncAnims = mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, false ); + // Then activate all the entities + for ( int i = 0; i < entities.Count(); i++ ) + { + entities[i]->Activate(); + } + mdlcache->SetAsyncLoad( MDLCACHE_ANIMBLOCK, bAsyncAnims ); + } + } + else + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Convert a vector an angle from worldspace to the entity's parent's local space +// Input : *pEntity - Entity whose parent we're concerned with +//----------------------------------------------------------------------------- +void UTIL_ParentToWorldSpace( CBaseEntity *pEntity, Vector &vecPosition, QAngle &vecAngles ) +{ + if ( pEntity == NULL ) + return; + + // Construct the entity-to-world matrix + // Start with making an entity-to-parent matrix + matrix3x4_t matEntityToParent; + AngleMatrix( vecAngles, matEntityToParent ); + MatrixSetColumn( vecPosition, 3, matEntityToParent ); + + // concatenate with our parent's transform + matrix3x4_t matScratch, matResult; + matrix3x4_t matParentToWorld; + + if ( pEntity->GetParent() != NULL ) + { + matParentToWorld = pEntity->GetParentToWorldTransform( matScratch ); + } + else + { + matParentToWorld = pEntity->EntityToWorldTransform(); + } + + ConcatTransforms( matParentToWorld, matEntityToParent, matResult ); + + // pull our absolute position out of the matrix + MatrixGetColumn( matResult, 3, vecPosition ); + MatrixAngles( matResult, vecAngles ); +} + +//----------------------------------------------------------------------------- +// Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space +// Input : *pEntity - Entity whose parent we're concerned with +//----------------------------------------------------------------------------- +void UTIL_ParentToWorldSpace( CBaseEntity *pEntity, Vector &vecPosition, Quaternion &quat ) +{ + if ( pEntity == NULL ) + return; + + QAngle vecAngles; + QuaternionAngles( quat, vecAngles ); + UTIL_ParentToWorldSpace( pEntity, vecPosition, vecAngles ); + AngleQuaternion( vecAngles, quat ); +} + +//----------------------------------------------------------------------------- +// Purpose: Convert a vector an angle from worldspace to the entity's parent's local space +// Input : *pEntity - Entity whose parent we're concerned with +//----------------------------------------------------------------------------- +void UTIL_WorldToParentSpace( CBaseEntity *pEntity, Vector &vecPosition, QAngle &vecAngles ) +{ + if ( pEntity == NULL ) + return; + + // Construct the entity-to-world matrix + // Start with making an entity-to-parent matrix + matrix3x4_t matEntityToParent; + AngleMatrix( vecAngles, matEntityToParent ); + MatrixSetColumn( vecPosition, 3, matEntityToParent ); + + // concatenate with our parent's transform + matrix3x4_t matScratch, matResult; + matrix3x4_t matWorldToParent; + + if ( pEntity->GetParent() != NULL ) + { + matScratch = pEntity->GetParentToWorldTransform( matScratch ); + } + else + { + matScratch = pEntity->EntityToWorldTransform(); + } + + MatrixInvert( matScratch, matWorldToParent ); + ConcatTransforms( matWorldToParent, matEntityToParent, matResult ); + + // pull our absolute position out of the matrix + MatrixGetColumn( matResult, 3, vecPosition ); + MatrixAngles( matResult, vecAngles ); +} + +//----------------------------------------------------------------------------- +// Purpose: Convert a vector and quaternion from worldspace to the entity's parent's local space +// Input : *pEntity - Entity whose parent we're concerned with +//----------------------------------------------------------------------------- +void UTIL_WorldToParentSpace( CBaseEntity *pEntity, Vector &vecPosition, Quaternion &quat ) +{ + if ( pEntity == NULL ) + return; + + QAngle vecAngles; + QuaternionAngles( quat, vecAngles ); + UTIL_WorldToParentSpace( pEntity, vecPosition, vecAngles ); + AngleQuaternion( vecAngles, quat ); +} + +//----------------------------------------------------------------------------- +// Purpose: Given a vector, clamps the scalar axes to MAX_COORD_FLOAT ranges from worldsize.h +// Input : *pVecPos - +//----------------------------------------------------------------------------- +void UTIL_BoundToWorldSize( Vector *pVecPos ) +{ + Assert( pVecPos ); + for ( int i = 0; i < 3; ++i ) + { + (*pVecPos)[ i ] = clamp( (*pVecPos)[ i ], MIN_COORD_FLOAT, MAX_COORD_FLOAT ); + } +} + +//============================================================================= +// +// Tests! +// + +#define NUM_KDTREE_TESTS 2500 +#define NUM_KDTREE_ENTITY_SIZE 256 + +void CC_KDTreeTest( const CCommand &args ) +{ + Msg( "Testing kd-tree entity queries." ); + + // Get the testing spot. +// CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start" ); +// Vector vecStart = pSpot->GetAbsOrigin(); + + CBasePlayer *pPlayer = static_cast<CBasePlayer*>( UTIL_GetLocalPlayer() ); + Vector vecStart = pPlayer->GetAbsOrigin(); + + static Vector *vecTargets = NULL; + static bool bFirst = true; + + // Generate the targets - rays (1K long). + if ( bFirst ) + { + vecTargets = new Vector [NUM_KDTREE_TESTS]; + double flRadius = 0; + double flTheta = 0; + double flPhi = 0; + for ( int i = 0; i < NUM_KDTREE_TESTS; ++i ) + { + flRadius += NUM_KDTREE_TESTS * 123.123; + flRadius = fmod( flRadius, 128.0 ); + flRadius = fabs( flRadius ); + + flTheta += NUM_KDTREE_TESTS * 76.76; + flTheta = fmod( flTheta, (double) DEG2RAD( 360 ) ); + flTheta = fabs( flTheta ); + + flPhi += NUM_KDTREE_TESTS * 1997.99; + flPhi = fmod( flPhi, (double) DEG2RAD( 180 ) ); + flPhi = fabs( flPhi ); + + float st, ct, sp, cp; + SinCos( flTheta, &st, &ct ); + SinCos( flPhi, &sp, &cp ); + + vecTargets[i].x = flRadius * ct * sp; + vecTargets[i].y = flRadius * st * sp; + vecTargets[i].z = flRadius * cp; + + // Make the trace 1024 units long. + Vector vecDir = vecTargets[i] - vecStart; + VectorNormalize( vecDir ); + vecTargets[i] = vecStart + vecDir * 1024; + } + + bFirst = false; + } + + int nTestType = 0; + if ( args.ArgC() >= 2 ) + { + nTestType = atoi( args[ 1 ] ); + } + + vtune( true ); + +#ifdef VPROF_ENABLED + g_VProfCurrentProfile.Resume(); + g_VProfCurrentProfile.Start(); + g_VProfCurrentProfile.Reset(); + g_VProfCurrentProfile.MarkFrame(); +#endif + + switch ( nTestType ) + { + case 0: + { + VPROF( "TraceTotal" ); + + trace_t trace; + for ( int iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest ) + { + UTIL_TraceLine( vecStart, vecTargets[iTest], MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace ); + } + break; + } + case 1: + { + VPROF( "TraceTotal" ); + + trace_t trace; + for ( int iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest ) + { + UTIL_TraceHull( vecStart, vecTargets[iTest], VEC_HULL_MIN_SCALED( pPlayer ), VEC_HULL_MAX_SCALED( pPlayer ), MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &trace ); + } + break; + } + case 2: + { + Vector vecMins[NUM_KDTREE_TESTS]; + Vector vecMaxs[NUM_KDTREE_TESTS]; + int iTest; + for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest ) + { + vecMins[iTest] = vecStart; + vecMaxs[iTest] = vecStart; + for ( int iAxis = 0; iAxis < 3; ++iAxis ) + { + if ( vecTargets[iTest].x < vecMins[iTest].x ) { vecMins[iTest].x = vecTargets[iTest].x; } + if ( vecTargets[iTest].y < vecMins[iTest].y ) { vecMins[iTest].y = vecTargets[iTest].y; } + if ( vecTargets[iTest].z < vecMins[iTest].z ) { vecMins[iTest].z = vecTargets[iTest].z; } + + if ( vecTargets[iTest].x > vecMaxs[iTest].x ) { vecMaxs[iTest].x = vecTargets[iTest].x; } + if ( vecTargets[iTest].y > vecMaxs[iTest].y ) { vecMaxs[iTest].y = vecTargets[iTest].y; } + if ( vecTargets[iTest].z > vecMaxs[iTest].z ) { vecMaxs[iTest].z = vecTargets[iTest].z; } + } + } + + + VPROF( "TraceTotal" ); + + int nCount = 0; + + Vector vecDelta; + trace_t trace; + CBaseEntity *pList[1024]; + for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest ) + { + nCount += UTIL_EntitiesInBox( pList, 1024, vecMins[iTest], vecMaxs[iTest], 0 ); + } + + Msg( "Count = %d\n", nCount ); + break; + } + case 3: + { + Vector vecDelta; + float flRadius[NUM_KDTREE_TESTS]; + int iTest; + for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest ) + { + VectorSubtract( vecTargets[iTest], vecStart, vecDelta ); + flRadius[iTest] = vecDelta.Length() * 0.5f; + } + + VPROF( "TraceTotal" ); + + int nCount = 0; + + trace_t trace; + CBaseEntity *pList[1024]; + for ( iTest = 0; iTest < NUM_KDTREE_TESTS; ++iTest ) + { + nCount += UTIL_EntitiesInSphere( pList, 1024, vecStart, flRadius[iTest], 0 ); + } + + Msg( "Count = %d\n", nCount ); + break; + } + default: + { + break; + } + } + +#ifdef VPROF_ENABLED + g_VProfCurrentProfile.MarkFrame(); + g_VProfCurrentProfile.Pause(); + g_VProfCurrentProfile.OutputReport( VPRT_FULL ); +#endif + + vtune( false ); +} + +static ConCommand kdtree_test( "kdtree_test", CC_KDTreeTest, "Tests spatial partition for entities queries.", FCVAR_CHEAT ); + +void CC_VoxelTreeView( void ) +{ + Msg( "VoxelTreeView\n" ); + partition->RenderAllObjectsInTree( 10.0f ); +} + +static ConCommand voxeltree_view( "voxeltree_view", CC_VoxelTreeView, "View entities in the voxel-tree.", FCVAR_CHEAT ); + +void CC_VoxelTreePlayerView( void ) +{ + Msg( "VoxelTreePlayerView\n" ); + + CBasePlayer *pPlayer = static_cast<CBasePlayer*>( UTIL_GetLocalPlayer() ); + Vector vecStart = pPlayer->GetAbsOrigin(); + partition->RenderObjectsInPlayerLeafs( vecStart - VEC_HULL_MIN_SCALED( pPlayer ), vecStart + VEC_HULL_MAX_SCALED( pPlayer ), 3.0f ); +} + +static ConCommand voxeltree_playerview( "voxeltree_playerview", CC_VoxelTreePlayerView, "View entities in the voxel-tree at the player position.", FCVAR_CHEAT ); + +void CC_VoxelTreeBox( const CCommand &args ) +{ + Vector vecMin, vecMax; + if ( args.ArgC() >= 6 ) + { + vecMin.x = atof( args[ 1 ] ); + vecMin.y = atof( args[ 2 ] ); + vecMin.z = atof( args[ 3 ] ); + + vecMax.x = atof( args[ 4 ] ); + vecMax.y = atof( args[ 5 ] ); + vecMax.z = atof( args[ 6 ] ); + } + else + { + return; + } + + float flTime = 10.0f; + + Vector vecPoints[8]; + vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z ); + vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z ); + vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z ); + vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z ); + vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z ); + vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z ); + vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z ); + vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z ); + + debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[1], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[2], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[3], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[0], 255, 0, 0, true, flTime ); + + debugoverlay->AddLineOverlay( vecPoints[4], vecPoints[5], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[5], vecPoints[6], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[6], vecPoints[7], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[7], vecPoints[4], 255, 0, 0, true, flTime ); + + debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[4], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[7], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[5], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[6], 255, 0, 0, true, flTime ); + + Msg( "VoxelTreeBox - (%f %f %f) to (%f %f %f)\n", vecMin.x, vecMin.y, vecMin.z, vecMax.x, vecMax.y, vecMax.z ); + partition->RenderObjectsInBox( vecMin, vecMax, flTime ); +} + +static ConCommand voxeltree_box( "voxeltree_box", CC_VoxelTreeBox, "View entities in the voxel-tree inside box <Vector(min), Vector(max)>.", FCVAR_CHEAT ); + +void CC_VoxelTreeSphere( const CCommand &args ) +{ + Vector vecCenter; + float flRadius; + if ( args.ArgC() >= 4 ) + { + vecCenter.x = atof( args[ 1 ] ); + vecCenter.y = atof( args[ 2 ] ); + vecCenter.z = atof( args[ 3 ] ); + + flRadius = atof( args[ 3 ] ); + } + else + { + return; + } + + float flTime = 3.0f; + + Vector vecMin, vecMax; + vecMin.Init( vecCenter.x - flRadius, vecCenter.y - flRadius, vecCenter.z - flRadius ); + vecMax.Init( vecCenter.x + flRadius, vecCenter.y + flRadius, vecCenter.z + flRadius ); + + Vector vecPoints[8]; + vecPoints[0].Init( vecMin.x, vecMin.y, vecMin.z ); + vecPoints[1].Init( vecMin.x, vecMax.y, vecMin.z ); + vecPoints[2].Init( vecMax.x, vecMax.y, vecMin.z ); + vecPoints[3].Init( vecMax.x, vecMin.y, vecMin.z ); + vecPoints[4].Init( vecMin.x, vecMin.y, vecMax.z ); + vecPoints[5].Init( vecMin.x, vecMax.y, vecMax.z ); + vecPoints[6].Init( vecMax.x, vecMax.y, vecMax.z ); + vecPoints[7].Init( vecMax.x, vecMin.y, vecMax.z ); + + debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[1], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[2], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[3], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[0], 255, 0, 0, true, flTime ); + + debugoverlay->AddLineOverlay( vecPoints[4], vecPoints[5], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[5], vecPoints[6], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[6], vecPoints[7], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[7], vecPoints[4], 255, 0, 0, true, flTime ); + + debugoverlay->AddLineOverlay( vecPoints[0], vecPoints[4], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[3], vecPoints[7], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[1], vecPoints[5], 255, 0, 0, true, flTime ); + debugoverlay->AddLineOverlay( vecPoints[2], vecPoints[6], 255, 0, 0, true, flTime ); + + Msg( "VoxelTreeSphere - (%f %f %f), %f\n", vecCenter.x, vecCenter.y, vecCenter.z, flRadius ); + partition->RenderObjectsInSphere( vecCenter, flRadius, flTime ); +} + +static ConCommand voxeltree_sphere( "voxeltree_sphere", CC_VoxelTreeSphere, "View entities in the voxel-tree inside sphere <Vector(center), float(radius)>.", FCVAR_CHEAT ); + + + +#define NUM_COLLISION_TESTS 2500 +void CC_CollisionTest( const CCommand &args ) +{ + if ( !physenv ) + return; + + Msg( "Testing collision system\n" ); + partition->ReportStats( "" ); + int i; + CBaseEntity *pSpot = gEntList.FindEntityByClassname( NULL, "info_player_start"); + Vector start = pSpot->GetAbsOrigin(); + static Vector *targets = NULL; + static bool first = true; + static float test[2] = {1,1}; + if ( first ) + { + targets = new Vector[NUM_COLLISION_TESTS]; + float radius = 0; + float theta = 0; + float phi = 0; + for ( i = 0; i < NUM_COLLISION_TESTS; i++ ) + { + radius += NUM_COLLISION_TESTS * 123.123; + radius = fabs(fmod(radius, 128)); + theta += NUM_COLLISION_TESTS * 76.76; + theta = fabs(fmod(theta, DEG2RAD(360))); + phi += NUM_COLLISION_TESTS * 1997.99; + phi = fabs(fmod(phi, DEG2RAD(180))); + + float st, ct, sp, cp; + SinCos( theta, &st, &ct ); + SinCos( phi, &sp, &cp ); + + targets[i].x = radius * ct * sp; + targets[i].y = radius * st * sp; + targets[i].z = radius * cp; + + // make the trace 1024 units long + Vector dir = targets[i] - start; + VectorNormalize(dir); + targets[i] = start + dir * 1024; + } + first = false; + } + + //Vector results[NUM_COLLISION_TESTS]; + + int testType = 0; + if ( args.ArgC() >= 2 ) + { + testType = atoi(args[1]); + } + float duration = 0; + Vector size[2]; + size[0].Init(0,0,0); + size[1].Init(16,16,16); + unsigned int dots = 0; + int nMask = MASK_ALL & ~(CONTENTS_MONSTER | CONTENTS_HITBOX ); + for ( int j = 0; j < 2; j++ ) + { + float startTime = engine->Time(); + if ( testType == 1 ) + { + trace_t tr; + for ( i = 0; i < NUM_COLLISION_TESTS; i++ ) + { + UTIL_TraceHull( start, targets[i], -size[1], size[1], nMask, NULL, COLLISION_GROUP_NONE, &tr ); + } + } + else + { + testType = 0; + trace_t tr; + + for ( i = 0; i < NUM_COLLISION_TESTS; i++ ) + { + if ( i == 0 ) + { + partition->RenderLeafsForRayTraceStart( 10.0f ); + } + + UTIL_TraceLine( start, targets[i], nMask, NULL, COLLISION_GROUP_NONE, &tr ); + + if ( i == 0 ) + { + partition->RenderLeafsForRayTraceEnd( ); + } + } + } + + duration += engine->Time() - startTime; + } + test[testType] = duration; + Msg("%d collisions in %.2f ms (%u dots)\n", NUM_COLLISION_TESTS, duration*1000, dots ); + partition->ReportStats( "" ); +#if 1 + int red = 255, green = 0, blue = 0; + for ( i = 0; i < 1 /*NUM_COLLISION_TESTS*/; i++ ) + { + NDebugOverlay::Line( start, targets[i], red, green, blue, false, 2 ); + } +#endif +} +static ConCommand collision_test("collision_test", CC_CollisionTest, "Tests collision system", FCVAR_CHEAT ); + + + + |