From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/util.cpp | 6582 +++++++++++++++++++++---------------------- 1 file changed, 3291 insertions(+), 3291 deletions(-) (limited to 'mp/src/game/server/util.cpp') diff --git a/mp/src/game/server/util.cpp b/mp/src/game/server/util.cpp index 431c8561..b273520a 100644 --- a/mp/src/game/server/util.cpp +++ b/mp/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 -#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( 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<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() : "" ); - } - } - - 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(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( pSourceEntity ); - CBaseAnimating *pDestBaseAnimating = dynamic_cast( 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 &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( 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( 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 .", 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 .", 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 +#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( 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<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() : "" ); + } + } + + 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(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( pSourceEntity ); + CBaseAnimating *pDestBaseAnimating = dynamic_cast( 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 &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( 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( 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 .", 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 .", 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 ); + + + + -- cgit v1.2.3