//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Shared util code between client and server. // //=============================================================================// #ifndef UTIL_SHARED_H #define UTIL_SHARED_H #ifdef _WIN32 #pragma once #endif #include "mathlib/vector.h" #include "cmodel.h" #include "utlvector.h" #include "networkvar.h" #include "engine/IEngineTrace.h" #include "engine/IStaticPropMgr.h" #include "shared_classnames.h" #ifdef CLIENT_DLL #include "cdll_client_int.h" #endif #ifdef PORTAL #include "portal_util_shared.h" #endif //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- class CGameTrace; class CBasePlayer; typedef CGameTrace trace_t; extern ConVar developer; // developer mode //----------------------------------------------------------------------------- // Language IDs. //----------------------------------------------------------------------------- #define LANGUAGE_ENGLISH 0 #define LANGUAGE_GERMAN 1 #define LANGUAGE_FRENCH 2 #define LANGUAGE_BRITISH 3 //----------------------------------------------------------------------------- // Pitch + yaw //----------------------------------------------------------------------------- float UTIL_VecToYaw (const Vector &vec); float UTIL_VecToPitch (const Vector &vec); float UTIL_VecToYaw (const matrix3x4_t& matrix, const Vector &vec); float UTIL_VecToPitch (const matrix3x4_t& matrix, const Vector &vec); Vector UTIL_YawToVector ( float yaw ); //----------------------------------------------------------------------------- // Shared random number generators for shared/predicted code: // whenever generating random numbers in shared/predicted code, these functions // have to be used. Each call should specify a unique "sharedname" string that // seeds the random number generator. In loops make sure the "additionalSeed" // is increased with the loop counter, otherwise it will always return the // same random number //----------------------------------------------------------------------------- float SharedRandomFloat( const char *sharedname, float flMinVal, float flMaxVal, int additionalSeed = 0 ); int SharedRandomInt( const char *sharedname, int iMinVal, int iMaxVal, int additionalSeed = 0 ); Vector SharedRandomVector( const char *sharedname, float minVal, float maxVal, int additionalSeed = 0 ); QAngle SharedRandomAngle( const char *sharedname, float minVal, float maxVal, int additionalSeed = 0 ); //----------------------------------------------------------------------------- // Standard collision filters... //----------------------------------------------------------------------------- bool PassServerEntityFilter( const IHandleEntity *pTouch, const IHandleEntity *pPass ); bool StandardFilterRules( IHandleEntity *pHandleEntity, int fContentsMask ); // "weapon_" #define WEAPON_CLASSNAME_PREFIX_LENGTH 7 bool IsWeaponClassname( const char *pszClassName ); //----------------------------------------------------------------------------- // Converts an IHandleEntity to an CBaseEntity //----------------------------------------------------------------------------- inline const CBaseEntity *EntityFromEntityHandle( const IHandleEntity *pConstHandleEntity ) { IHandleEntity *pHandleEntity = const_cast(pConstHandleEntity); #ifdef CLIENT_DLL IClientUnknown *pUnk = (IClientUnknown*)pHandleEntity; return pUnk->GetBaseEntity(); #else if ( staticpropmgr->IsStaticProp( pHandleEntity ) ) return NULL; IServerUnknown *pUnk = (IServerUnknown*)pHandleEntity; return pUnk->GetBaseEntity(); #endif } inline CBaseEntity *EntityFromEntityHandle( IHandleEntity *pHandleEntity ) { #ifdef CLIENT_DLL IClientUnknown *pUnk = (IClientUnknown*)pHandleEntity; return pUnk->GetBaseEntity(); #else #ifndef _GAMECONSOLE if ( staticpropmgr->IsStaticProp( pHandleEntity ) ) return NULL; #else if ( !pHandleEntity || pHandleEntity->m_bIsStaticProp ) return NULL; #endif IServerUnknown *pUnk = (IServerUnknown*)pHandleEntity; Assert( !pUnk || pUnk->GetBaseEntity() ); return pUnk->GetBaseEntity(); #endif } typedef bool (*ShouldHitFunc_t)( IHandleEntity *pHandleEntity, int contentsMask ); //----------------------------------------------------------------------------- // traceline methods //----------------------------------------------------------------------------- class CTraceFilterSimple : public CTraceFilter { public: // It does have a base, but we'll never network anything below here.. DECLARE_CLASS_NOBASE( CTraceFilterSimple ); CTraceFilterSimple( const IHandleEntity *passentity, int collisionGroup, ShouldHitFunc_t pExtraShouldHitCheckFn = NULL ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); virtual void SetPassEntity( const IHandleEntity *pPassEntity ) { m_pPassEnt = pPassEntity; } virtual void SetCollisionGroup( int iCollisionGroup ) { m_collisionGroup = iCollisionGroup; } const IHandleEntity *GetPassEntity( void ){ return m_pPassEnt;} int GetCollisionGroup( void ) const { return m_collisionGroup; } private: const IHandleEntity *m_pPassEnt; int m_collisionGroup; ShouldHitFunc_t m_pExtraShouldHitCheckFunction; }; class CTraceFilterSkipTwoEntities : public CTraceFilterSimple { public: // It does have a base, but we'll never network anything below here.. DECLARE_CLASS( CTraceFilterSkipTwoEntities, CTraceFilterSimple ); CTraceFilterSkipTwoEntities( const IHandleEntity *passentity = NULL, const IHandleEntity *passentity2 = NULL, int collisionGroup = COLLISION_GROUP_NONE ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); virtual void SetPassEntity2( const IHandleEntity *pPassEntity2 ) { m_pPassEnt2 = pPassEntity2; } private: const IHandleEntity *m_pPassEnt2; }; class CTraceFilterSimpleList : public CTraceFilterSimple { public: CTraceFilterSimpleList( int collisionGroup ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); void AddEntityToIgnore( IHandleEntity *pEntity ); void AddEntitiesToIgnore( int nCount, IHandleEntity **ppEntities ); protected: CUtlVector m_PassEntities; }; class CTraceFilterOnlyHitThis : public CTraceFilterEntitiesOnly { public: // It does have a base, but we'll never network anything below here.. DECLARE_CLASS_NOBASE( CTraceFilterOnlyHitThis ); CTraceFilterOnlyHitThis( const IHandleEntity *hitentity ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); private: const IHandleEntity *m_pHitEnt; }; class CTraceFilterOnlyNPCsAndPlayer : public CTraceFilterSimple { public: CTraceFilterOnlyNPCsAndPlayer( const IHandleEntity *passentity, int collisionGroup ) : CTraceFilterSimple( passentity, collisionGroup ) { } virtual TraceType_t GetTraceType() const { return TRACE_ENTITIES_ONLY; } virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); }; class CTraceFilterNoNPCsOrPlayer : public CTraceFilterSimple { public: CTraceFilterNoNPCsOrPlayer( const IHandleEntity *passentity = NULL, int collisionGroup = COLLISION_GROUP_NONE ) : CTraceFilterSimple( passentity, collisionGroup ) { } virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); }; class CTraceFilterNoPlayers : public CTraceFilterSimple { public: CTraceFilterNoPlayers( const IHandleEntity *passentity = NULL, int collisionGroup = COLLISION_GROUP_NONE ) : CTraceFilterSimple( passentity, collisionGroup ) { } virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); }; //----------------------------------------------------------------------------- // Purpose: Custom trace filter used for NPC LOS traces //----------------------------------------------------------------------------- class CTraceFilterLOS : public CTraceFilterSkipTwoEntities { public: CTraceFilterLOS( IHandleEntity *pHandleEntity, int collisionGroup, IHandleEntity *pHandleEntity2 = NULL ); bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); }; class CTraceFilterSkipClassname : public CTraceFilterSimple { public: CTraceFilterSkipClassname( const IHandleEntity *passentity, const char *pchClassname, int collisionGroup ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); private: const char *m_pchClassname; }; class CTraceFilterSkipTwoClassnames : public CTraceFilterSkipClassname { public: // It does have a base, but we'll never network anything below here.. DECLARE_CLASS( CTraceFilterSkipTwoClassnames, CTraceFilterSkipClassname ); CTraceFilterSkipTwoClassnames( const IHandleEntity *passentity, const char *pchClassname, const char *pchClassname2, int collisionGroup ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); private: const char *m_pchClassname2; }; class CTraceFilterSimpleClassnameList : public CTraceFilterSimple { public: CTraceFilterSimpleClassnameList( const IHandleEntity *passentity, int collisionGroup ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); void AddClassnameToIgnore( const char *pchClassname ); private: CUtlVector m_PassClassnames; }; class CTraceFilterChain : public CTraceFilter { public: CTraceFilterChain( ITraceFilter *pTraceFilter1, ITraceFilter *pTraceFilter2 ); virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ); private: ITraceFilter *m_pTraceFilter1; ITraceFilter *m_pTraceFilter2; }; // helper void DebugDrawLine( const Vector& vecAbsStart, const Vector& vecAbsEnd, int r, int g, int b, bool test, float duration ); extern ConVar r_visualizetraces; #ifdef DETECT_TRACE_SPIKES #define BeginDetectTraceSpike() \ extern void DoReportExpensiveTrace( bool repeat, float time ); \ extern float g_TraceSpikeTolerance; \ CFastTimer spikeTimer; \ spikeTimer.Start() #define EndDetectTraceSpike() \ spikeTimer.End() #define DidTraceSpike() \ ( spikeTimer.GetDuration().GetMillisecondsF() > g_TraceSpikeTolerance ) #define ReportExpensiveTrace( repeat ) if ( DidTraceSpike() ) DoReportExpensiveTrace( repeat, spikeTimer.GetDuration().GetMillisecondsF() ) #else #define BeginDetectTraceSpike() ((void)0) #define EndDetectTraceSpike() ((void)0) #define DidTraceSpike() false #define ReportExpensiveTrace( repeat ) ((void)0) #endif inline void UTIL_TraceLine( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr ) { BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd ); CTraceFilterSimple traceFilter( ignore, collisionGroup ); enginetrace->TraceRay( ray, mask, &traceFilter, ptr ); EndDetectTraceSpike(); if( r_visualizetraces.GetBool() || DidTraceSpike() ) { DebugDrawLine( ptr->startpos, ptr->endpos, 255, 0, 0, true, ( r_visualizetraces.GetBool() ) ? -1.0f : .5 ); ReportExpensiveTrace( false ); if ( DidTraceSpike() ) // Opimizer will remove this block { ReportExpensiveTrace( false ); BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd ); CTraceFilterSimple traceFilter( ignore, collisionGroup ); enginetrace->TraceRay( ray, mask, &traceFilter, ptr ); EndDetectTraceSpike(); if ( DidTraceSpike() ) { ReportExpensiveTrace( true ); } } } } inline void UTIL_TraceLine( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, ITraceFilter *pFilter, trace_t *ptr ) { BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd ); enginetrace->TraceRay( ray, mask, pFilter, ptr ); EndDetectTraceSpike(); if( r_visualizetraces.GetBool() || DidTraceSpike() ) { DebugDrawLine( ptr->startpos, ptr->endpos, 255, 0, 0, true, ( r_visualizetraces.GetBool() ) ? -1.0f : .5 ); ReportExpensiveTrace( false ); if ( DidTraceSpike() ) // Opimizer will remove this block { BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd ); enginetrace->TraceRay( ray, mask, pFilter, ptr ); EndDetectTraceSpike(); if ( DidTraceSpike() ) { ReportExpensiveTrace( true ); } } } } inline void UTIL_TraceHull( const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &hullMin, const Vector &hullMax, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr ) { BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd, hullMin, hullMax ); CTraceFilterSimple traceFilter( ignore, collisionGroup ); enginetrace->TraceRay( ray, mask, &traceFilter, ptr ); EndDetectTraceSpike(); if( r_visualizetraces.GetBool() || DidTraceSpike() ) { DebugDrawLine( ptr->startpos, ptr->endpos, 255, 255, 0, true, ( r_visualizetraces.GetBool() ) ? -1.0f : .5 ); ReportExpensiveTrace( false ); if ( DidTraceSpike() ) // Opimizer will remove this block { BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd, hullMin, hullMax ); CTraceFilterSimple traceFilter( ignore, collisionGroup ); enginetrace->TraceRay( ray, mask, &traceFilter, ptr ); EndDetectTraceSpike(); if ( DidTraceSpike() ) { ReportExpensiveTrace( true ); } } } } inline void UTIL_TraceHull( const Vector &vecAbsStart, const Vector &vecAbsEnd, const Vector &hullMin, const Vector &hullMax, unsigned int mask, ITraceFilter *pFilter, trace_t *ptr ) { BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd, hullMin, hullMax ); enginetrace->TraceRay( ray, mask, pFilter, ptr ); EndDetectTraceSpike(); if( r_visualizetraces.GetBool() || DidTraceSpike() ) { DebugDrawLine( ptr->startpos, ptr->endpos, 255, 255, 0, true, ( r_visualizetraces.GetBool() ) ? -1.0f : .5 ); ReportExpensiveTrace( false ); if ( DidTraceSpike() ) // Opimizer will remove this block { BeginDetectTraceSpike(); Ray_t ray; ray.Init( vecAbsStart, vecAbsEnd, hullMin, hullMax ); enginetrace->TraceRay( ray, mask, pFilter, ptr ); EndDetectTraceSpike(); if( DidTraceSpike() ) { ReportExpensiveTrace( true ); } } } } inline void UTIL_TraceRay( const Ray_t &ray, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr ) { CTraceFilterSimple traceFilter( ignore, collisionGroup ); enginetrace->TraceRay( ray, mask, &traceFilter, ptr ); if( r_visualizetraces.GetBool() ) { DebugDrawLine( ptr->startpos, ptr->endpos, 255, 0, 0, true, -1.0f ); } } inline void UTIL_TraceRay( const Ray_t &ray, unsigned int mask, ITraceFilter *pFilter, trace_t *ptr ) { enginetrace->TraceRay( ray, mask, pFilter, ptr ); if( r_visualizetraces.GetBool() ) { DebugDrawLine( ptr->startpos, ptr->endpos, 255, 0, 0, true, -1.0f ); } } // Sweeps a particular entity through the world void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, unsigned int mask, trace_t *ptr ); void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, unsigned int mask, ITraceFilter *pFilter, trace_t *ptr ); void UTIL_TraceEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd, unsigned int mask, const IHandleEntity *ignore, int collisionGroup, trace_t *ptr ); bool UTIL_EntityHasMatchingRootParent( CBaseEntity *pRootParent, CBaseEntity *pEntity ); inline int UTIL_PointContents( const Vector &vec, int contentsMask ) { return enginetrace->GetPointContents( vec, contentsMask ); } // Sweeps against a particular model, using collision rules void UTIL_TraceModel( const Vector &vecStart, const Vector &vecEnd, const Vector &hullMin, const Vector &hullMax, CBaseEntity *pentModel, int collisionGroup, trace_t *ptr ); void UTIL_ClipTraceToPlayers( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, ITraceFilter *filter, trace_t *tr ); // Particle effect tracer void UTIL_ParticleTracer( const char *pszTracerEffectName, const Vector &vecStart, const Vector &vecEnd, int iEntIndex = 0, int iAttachment = 0, bool bWhiz = false ); // Old style, non-particle system, tracers void UTIL_Tracer( const Vector &vecStart, const Vector &vecEnd, int iEntIndex = 0, int iAttachment = TRACER_DONT_USE_ATTACHMENT, float flVelocity = 0, bool bWhiz = false, const char *pCustomTracerName = NULL, int iParticleID = 0 ); bool UTIL_IsLowViolence( void ); bool UTIL_ShouldShowBlood( int bloodColor ); void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount ); void UTIL_BloodImpact( const Vector &pos, const Vector &dir, int color, int amount ); void UTIL_BloodDecalTrace( trace_t *pTrace, int bloodColor ); void UTIL_DecalTrace( trace_t *pTrace, char const *decalName ); bool UTIL_IsSpaceEmpty( CBaseEntity *pMainEnt, const Vector &vMin, const Vector &vMax ); bool UTIL_IsSpaceEmpty( CBaseEntity *pMainEnt, const Vector &vMin, const Vector &vMax, unsigned int mask, ITraceFilter *pFilter ); // Search for water transition along a vertical line float UTIL_WaterLevel( const Vector &position, float minz, float maxz ); // Like UTIL_WaterLevel, but *way* less expensive. // I didn't replace UTIL_WaterLevel everywhere to avoid breaking anything. float UTIL_FindWaterSurface( const Vector &position, float minz, float maxz ); void UTIL_StringToVector( float *pVector, const char *pString ); void UTIL_StringToFloatArray( float *pVector, int count, const char *pString ); CBasePlayer *UTIL_PlayerByIndex( int entindex ); // decodes/encodes a buffer using a 64bit ICE key (inplace) void UTIL_DecodeICE( unsigned char * buffer, int size, const unsigned char *key ); void UTIL_EncodeICE( unsigned char * buffer, unsigned int size, const unsigned char *key ); unsigned short UTIL_GetAchievementEventMask( void ); //assumes the object is already in a mostly passable space #define FL_AXIS_DIRECTION_NONE ( 0 ) #define FL_AXIS_DIRECTION_X ( 1 << 0 ) #define FL_AXIS_DIRECTION_NX ( 1 << 1 ) #define FL_AXIS_DIRECTION_Y ( 1 << 2 ) #define FL_AXIS_DIRECTION_NY ( 1 << 3 ) #define FL_AXIS_DIRECTION_Z ( 1 << 4 ) #define FL_AXIS_DIRECTION_NZ ( 1 << 5 ) struct FindClosestPassableSpace_TraceAdapter_t; typedef void (*FN_RayTraceAdapterFunc)( const Ray_t &ray, trace_t *pResult, FindClosestPassableSpace_TraceAdapter_t *pTraceAdapter ); typedef bool (*FN_PointIsOutsideWorld)( const Vector &vTest, FindClosestPassableSpace_TraceAdapter_t *pTraceAdapter ); //derive from this to tack on additional data to your adapted functions struct FindClosestPassableSpace_TraceAdapter_t { FN_RayTraceAdapterFunc pTraceFunc; FN_PointIsOutsideWorld pPointOutsideWorldFunc; ITraceFilter *pTraceFilter; unsigned int fMask; }; bool UTIL_FindClosestPassableSpace( const Vector &vCenter, const Vector &vExtents, const Vector &vIndecisivePush, unsigned int iIterations, Vector &vCenterOut, int nAxisRestrictionFlags, FindClosestPassableSpace_TraceAdapter_t *pTraceAdapter ); bool UTIL_FindClosestPassableSpace( const Vector &vCenter, const Vector &vExtents, const Vector &vIndecisivePush, ITraceFilter *pTraceFilter, unsigned int fMask, unsigned int iIterations, Vector &vCenterOut, int nAxisRestrictionFlags = FL_AXIS_DIRECTION_NONE ); bool UTIL_FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask, unsigned int iIterations, Vector &vOriginOut, Vector *pStartingPosition = NULL, int nAxisRestrictionFlags = FL_AXIS_DIRECTION_NONE ); bool UTIL_FindClosestPassableSpace( CBaseEntity *pEntity, const Vector &vIndecisivePush, unsigned int fMask, Vector *pStartingPosition = NULL, int nAxisRestrictionFlags = FL_AXIS_DIRECTION_NONE ); //-------------------------------------------------------------------------------------------------------------- /** * Given a position and a ray, return the shortest distance between the two. * If 'pos' is beyond either end of the ray, the returned distance is negated. */ inline float DistanceToRay( const Vector &pos, const Vector &rayStart, const Vector &rayEnd, float *along = NULL, Vector *pointOnRay = NULL ) { Vector to = pos - rayStart; Vector dir = rayEnd - rayStart; float length = dir.NormalizeInPlace(); float rangeAlong = DotProduct( dir, to ); if (along) { *along = rangeAlong; } float range; if (rangeAlong < 0.0f) { // off start point range = -(pos - rayStart).Length(); if (pointOnRay) { *pointOnRay = rayStart; } } else if (rangeAlong > length) { // off end point range = -(pos - rayEnd).Length(); if (pointOnRay) { *pointOnRay = rayEnd; } } else // within ray bounds { Vector onRay = rayStart + rangeAlong * dir; range = (pos - onRay).Length(); if (pointOnRay) { *pointOnRay = onRay; } } return range; } //-------------------------------------------------------------------------------------------------------------- /** * Macro for creating an interface that when inherited from automatically maintains a list of instances * that inherit from that interface. */ // interface for entities that want to a auto maintained global list #define DECLARE_AUTO_LIST( interfaceName ) \ class interfaceName; \ abstract_class interfaceName \ { \ public: \ interfaceName( bool bAutoAdd = true ); \ virtual ~interfaceName(); \ virtual CBaseEntity* GetEntity( void ) = 0; \ static void Add( interfaceName *pElement ) { m_##interfaceName##AutoList.AddToTail( pElement ); } \ static void Remove( interfaceName *pElement ) { m_##interfaceName##AutoList.FindAndFastRemove( pElement ); } \ static const CUtlVector< interfaceName* >& AutoList( void ) { return m_##interfaceName##AutoList; } \ private: \ static CUtlVector< interfaceName* > m_##interfaceName##AutoList; \ }; // Creates a simple function for accessing the higher level entity #define IMPLEMENT_AUTO_LIST_GET() \ virtual CBaseEntity* GetEntity( void ) { return this; } // Creates the auto add/remove constructor/destructor... // Pass false to the constructor to not auto add #define IMPLEMENT_AUTO_LIST( interfaceName ) \ CUtlVector< class interfaceName* > interfaceName::m_##interfaceName##AutoList; \ interfaceName::interfaceName( bool bAutoAdd ) \ { \ if ( bAutoAdd ) \ { \ Add( this ); \ } \ } \ interfaceName::~interfaceName() \ { \ Remove( this ); \ } //-------------------------------------------------------------------------------------------------------------- /** * Simple class for tracking intervals of game time. * Upon creation, the timer is invalidated. To measure time intervals, start the timer via Start(). */ class IntervalTimer { public: #ifdef CLIENT_DLL DECLARE_PREDICTABLE(); #endif DECLARE_DATADESC(); DECLARE_CLASS_NOBASE( IntervalTimer ); DECLARE_EMBEDDED_NETWORKVAR(); IntervalTimer( void ) : m_timestamp( -1.0f ) { } void Reset( void ) { m_timestamp = Now(); } void Start( void ) { m_timestamp = Now(); } void StartFromTime( float startTime ) { m_timestamp = startTime; } void Invalidate( void ) { m_timestamp = -1.0f; } bool HasStarted( void ) const { return (m_timestamp > 0.0f); } /// if not started, elapsed time is very large float GetElapsedTime( void ) const { return (HasStarted()) ? (Now() - m_timestamp) : 99999.9f; } bool IsLessThen( float duration ) const { return (Now() - m_timestamp < duration) ? true : false; } bool IsGreaterThen( float duration ) const { return (Now() - m_timestamp > duration) ? true : false; } float GetStartTime( void ) const { return m_timestamp; } protected: CNetworkVar( float, m_timestamp ); float Now( void ) const; // work-around since client header doesn't like inlined gpGlobals->curtime }; #ifdef CLIENT_DLL EXTERN_RECV_TABLE(DT_IntervalTimer); #else EXTERN_SEND_TABLE(DT_IntervalTimer); #endif //-------------------------------------------------------------------------------------------------------------- /** * Simple class for counting down a short interval of time. * Upon creation, the timer is invalidated. Invalidated countdown timers are considered to have elapsed. */ class CountdownTimer { public: #ifdef CLIENT_DLL DECLARE_PREDICTABLE(); #endif DECLARE_CLASS_NOBASE( CountdownTimer ); DECLARE_EMBEDDED_NETWORKVAR(); CountdownTimer( void ) : m_timestamp( -1.0f), m_duration( 0.0f ) { } void Reset( void ) { m_timestamp = Now() + m_duration; } void Start( float duration ) { m_timestamp = Now() + duration; m_duration = duration; } void StartFromTime( float startTime, float duration ) { m_timestamp = startTime + duration; m_duration = duration; } void Invalidate( void ) { m_timestamp = -1.0f; } bool HasStarted( void ) const { return (m_timestamp > 0.0f); } bool IsElapsed( void ) const { return (Now() > m_timestamp); } float GetElapsedTime( void ) const { return Now() - m_timestamp + m_duration; } float GetRemainingTime( void ) const { return (m_timestamp - Now()); } float GetTargetTime() const { return m_timestamp; } /// return original countdown time float GetCountdownDuration( void ) const { return (m_timestamp > 0.0f) ? m_duration : 0.0f; } /// 1.0 for newly started, 0.0 for elapsed float GetRemainingRatio( void ) const { if ( HasStarted() ) { float left = GetRemainingTime() / m_duration; if ( left < 0.0f ) return 0.0f; if ( left > 1.0f ) return 1.0f; return left; } return 0.0f; } float GetElapsedRatio() const { if ( HasStarted() ) { float elapsed = GetElapsedTime() / m_duration; if ( elapsed < 0.0f ) return 0.0f; if ( elapsed > 1.0f ) return 1.0f; return elapsed; } return 1.0f; } // Usage: // Declaration: CountdownTimer mTimer; // Think function: // while(mTimer.RunEvery( timerInterval )) // { // do fixed-rate stuff // } // // nextThinkTime = min(nextThinkTime, mTimer.GetTargetTime()); // // This avoids 'losing' ticks on a repeating timer when // the think rate is not a multiple of the timer duration, // especially since SetNextThink rounds ticks up/down, causing // even a timer that is running exactly at the think rate of // the underlying class to not elapse correctly. // // It also makes sure that ticks are never lost bool RunEvery( float amount = -1.0f ) { // First call starts the timer if(!HasStarted()) { if(amount > 0.0f) Start( amount ); return false; } if( IsElapsed() ) { if ( amount > 0.0f ) m_duration = amount; m_timestamp += m_duration; return true; } return false; } // Same as RunEvery() but only returns true once per 'tick', then guarantees being non-elapsed. // Useful when "do fixed rate stuff" is idempotent, like updating something to match // the current time. bool Interval( float amount = -1.0f ) { // First call starts the timer if ( !HasStarted() ) { if ( amount > 0.0f ) Start( amount ); return false; } if ( IsElapsed() ) { if ( amount > 0.0f ) m_duration = amount; m_timestamp += m_duration; // If we are still expired, add a multiple of the interval // until we become non-elapsed float remaining = GetRemainingTime(); if ( remaining < 0.0f) { float numIntervalsRequired = -floorf( remaining / m_duration ); m_timestamp += m_duration * numIntervalsRequired; } // We should no longer be elapsed Assert( !IsElapsed() ); return true; } return false; } private: CNetworkVar( float, m_duration ); CNetworkVar( float, m_timestamp ); float Now( void ) const; // work-around since client header doesn't like inlined gpGlobals->curtime }; #ifdef CLIENT_DLL EXTERN_RECV_TABLE(DT_CountdownTimer); #else EXTERN_SEND_TABLE(DT_CountdownTimer); #endif //-------------------------------------------------------------------------------------------------------------- /** * Simple class for tracking change in values over time. */ #define TIMELINE_ARRAY_SIZE 64 #define TIMELINE_INTERVAL_START 0.25f enum TimelineCompression_t { TIMELINE_COMPRESSION_SUM, TIMELINE_COMPRESSION_COUNT_PER_INTERVAL, TIMELINE_COMPRESSION_AVERAGE, TIMELINE_COMPRESSION_AVERAGE_BLEND, TIMELINE_COMPRESSION_TOTAL }; class CTimeline : public IntervalTimer { public: DECLARE_DATADESC(); DECLARE_CLASS( CTimeline, IntervalTimer ); DECLARE_EMBEDDED_NETWORKVAR(); CTimeline( void ) { ClearValues(); } void ClearValues( void ); void ClearAndStart( void ) { ClearValues(); Start(); } void StopRecording( void ) { m_bStopped = true; } void RecordValue( float flValue ); void RecordFinalValue( float flValue ) { RecordValue( flValue ); StopRecording(); } int Count( void ) const { return m_nBucketCount; } float GetValue( int i ) const; float GetValueAtInterp( float fInterp ) const; float GetValueTime( int i ) const { Assert( i >= 0 && i < m_nBucketCount ); return static_cast( i ) * m_flInterval; } float GetInterval( void ) const { return m_flInterval; } void SetCompressionType( TimelineCompression_t nCompressionType ) { m_nCompressionType = nCompressionType; } TimelineCompression_t GetCompressionType( void ) const { return m_nCompressionType; } private: int GetCurrentBucket( void ) { return static_cast( Now() - m_timestamp ) / m_flInterval; } void Compress( void ); CNetworkArray( float, m_flValues, TIMELINE_ARRAY_SIZE ); CNetworkArray( int, m_nValueCounts, TIMELINE_ARRAY_SIZE ); CNetworkVar( int, m_nBucketCount ); CNetworkVar( float, m_flInterval ); CNetworkVar( float, m_flFinalValue ); CNetworkVar( TimelineCompression_t, m_nCompressionType ); CNetworkVar( bool, m_bStopped ); }; #ifdef CLIENT_DLL EXTERN_RECV_TABLE(DT_Timeline); #else EXTERN_SEND_TABLE(DT_Timeline); #endif char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename = NULL ); int UTIL_StringFieldToInt( const char *szValue, const char **pValueStrings, int iNumStrings ); int UTIL_CountNumBitsSet( unsigned int nVar ); int UTIL_CountNumBitsSet( uint64 nVar ); bool UTIL_GetModDir( char *lpszTextOut, unsigned int nSize ); /*UTIL_CalcFrustumThroughPolygon - Given a frustum and a polygon, calculate how the current frustum would clip the polygon, then generate a new frustum that runs along the edge of the clipped polygon. -returns number of planes in the output frustum, 0 if the polygon was completely clipped by the input frustum -vFrustumOrigin can be thought of as the camera origin if your frustum is a view frustum -planes should face inward -iPreserveCount will preserve N planes at the end of your input frustum and ensure they're at the end of your output frustum. Assuming your input frustum is of type "Frustum", a value of 2 would preserve your near and far planes -to ensure that your output frustum can hold the entire complex frustum we generate. Make it of size (iPolyVertCount + iCurrentFrustumPlanes + iPreserveCount). Otherwise the output frustum will be simplified to fit your maximum output by eliminating bounding planes with the clipped area. -a lack of input frustum is considered valid input*/ int UTIL_CalcFrustumThroughConvexPolygon( const Vector *pPolyVertices, int iPolyVertCount, const Vector &vFrustumOrigin, const VPlane *pInputFrustumPlanes, int iInputFrustumPlanes, VPlane *pOutputFrustumPlanes, int iMaxOutputPlanes, int iPreserveCount ); //----------------------------------------------------------------------------- // class CFlaggedEntitiesEnum //----------------------------------------------------------------------------- // enumerate entities that match a set of edict flags into a static array class CFlaggedEntitiesEnum : public IPartitionEnumerator { public: CFlaggedEntitiesEnum( CBaseEntity **pList, int listMax, int flagMask ); // This gets called by the enumeration methods with each element // that passes the test. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); int GetCount() { return m_count; } bool AddToList( CBaseEntity *pEntity ); private: CBaseEntity **m_pList; int m_listMax; int m_flagMask; int m_count; }; class CHurtableEntitiesEnum : public IPartitionEnumerator { public: CHurtableEntitiesEnum( CBaseEntity **pList, int listMax ); // This gets called by the enumeration methods with each element // that passes the test. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ); int GetCount() { return m_count; } bool AddToList( CBaseEntity *pEntity ); private: CBaseEntity **m_pList; int m_listMax; int m_count; }; int UTIL_EntitiesAlongRay( const Ray_t &ray, CFlaggedEntitiesEnum *pEnum ); inline int UTIL_EntitiesAlongRay( CBaseEntity **pList, int listMax, const Ray_t &ray, int flagMask ) { CFlaggedEntitiesEnum rayEnum( pList, listMax, flagMask ); return UTIL_EntitiesAlongRay( ray, &rayEnum ); } #include "shareddefs.h" #if (PREDICTION_ERROR_CHECK_LEVEL > 0) extern void _Easy_DiffPrint( CBaseEntity *pEntity, PRINTF_FORMAT_STRING const char *szFormatSTring, ... ); #if defined( CLIENT_DLL ) extern bool _Easy_DiffPrint_InternalConditions( C_BaseEntity *pEntity ); #define _EASY_DIFFPRINT_INTERNALCONDITIONS(entity) _Easy_DiffPrint_InternalConditions( entity ) #else #define _EASY_DIFFPRINT_INTERNALCONDITIONS(entity) true #endif #define EASY_DIFFPRINT_CONDITIONAL( conditions, entity, szFormatString, ... ) if( (conditions) && _EASY_DIFFPRINT_INTERNALCONDITIONS( entity ) ) { _Easy_DiffPrint( entity, szFormatString, __VA_ARGS__ ); } #define EASY_DIFFPRINT( entity, szFormatString, ... ) EASY_DIFFPRINT_CONDITIONAL( true, entity, szFormatString, __VA_ARGS__ ) #else //#if (PREDICTION_ERROR_CHECK_LEVEL > 0) #define EASY_DIFFPRINT_CONDITIONAL( conditions, entity, szFormatString, ... ) #define EASY_DIFFPRINT( entity, szFormatString, ... ) #endif //#if (PREDICTION_ERROR_CHECK_LEVEL > 0) void UTIL_GetNextCommandLength( const char *pText, int nMaxLen, int *pCommandLength, int *pNextCommandOffset ); //-------------------------------------------------------------------------------------------------------------- /** * remove double spaces and empty bold/italic HTML tags from a string */ void UTIL_TrimEmptyWhitespaceFromHTML( OUT_Z_BYTECAP( descWriterByteSize ) wchar_t* pszDescWriter, size_t descWriterByteSize, const wchar_t* pszDescReader ); void UTIL_TrimEmptyWhitespaceFromHTML( OUT_Z_BYTECAP( descWriterByteSize ) char* pszDescWriter, size_t descWriterByteSize, const char* pszDescReader ); #endif // UTIL_SHARED_H