You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1317 lines
35 KiB
1317 lines
35 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $Workfile: $ |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include <stdarg.h> |
|
#include "hud.h" |
|
#include "itextmessage.h" |
|
#include "materialsystem/imaterial.h" |
|
#include "materialsystem/itexture.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "imovehelper.h" |
|
#include "checksum_crc.h" |
|
#include "decals.h" |
|
#include "iefx.h" |
|
#include "view_scene.h" |
|
#include "filesystem.h" |
|
#include "model_types.h" |
|
#include "engine/IEngineTrace.h" |
|
#include "engine/ivmodelinfo.h" |
|
#include "c_te_effect_dispatch.h" |
|
#include <vgui_controls/Controls.h> |
|
#include <vgui/ISurface.h> |
|
#include <vgui/ILocalize.h> |
|
#include "view.h" |
|
#include "ixboxsystem.h" |
|
#include "inputsystem/iinputsystem.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
ConVar localplayer_visionflags( "localplayer_visionflags", "0", FCVAR_DEVELOPMENTONLY ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// ConVars |
|
//----------------------------------------------------------------------------- |
|
#ifdef _DEBUG |
|
|
|
ConVar r_FadeProps( "r_FadeProps", "1" ); |
|
|
|
#endif |
|
bool g_MakingDevShots = false; |
|
extern ConVar cl_leveloverview; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Performs a var args printf into a static return buffer |
|
// Input : *format - |
|
// ... - |
|
// Output : char |
|
//----------------------------------------------------------------------------- |
|
char *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; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if the entity index corresponds to a player slot |
|
// Input : index - |
|
// Output : bool |
|
//----------------------------------------------------------------------------- |
|
bool IsPlayerIndex( int index ) |
|
{ |
|
return ( index >= 1 && index <= gpGlobals->maxClients ) ? true : false; |
|
} |
|
|
|
int GetLocalPlayerIndex( void ) |
|
{ |
|
C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); |
|
|
|
if ( player ) |
|
return player->entindex(); |
|
else |
|
return 0; // game not started yet |
|
} |
|
|
|
// NOTE: cache these because this gets executed hundreds of times per frame |
|
static int g_nLocalPlayerVisionFlagsWeaponsCheck = 0; |
|
static int g_nLocalPlayerVisionFlags = 0; |
|
int GetLocalPlayerVisionFilterFlags( bool bWeaponsCheck /*= false */ ) |
|
{ |
|
return bWeaponsCheck ? g_nLocalPlayerVisionFlagsWeaponsCheck : g_nLocalPlayerVisionFlags; |
|
} |
|
|
|
void UpdateLocalPlayerVisionFlags() |
|
{ |
|
g_nLocalPlayerVisionFlagsWeaponsCheck = 0; |
|
g_nLocalPlayerVisionFlags = 0; |
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
if ( pPlayer ) |
|
{ |
|
g_nLocalPlayerVisionFlagsWeaponsCheck = pPlayer->GetVisionFilterFlags( true ); |
|
g_nLocalPlayerVisionFlags = pPlayer->GetVisionFilterFlags( false ); |
|
} |
|
} |
|
|
|
bool IsLocalPlayerUsingVisionFilterFlags( int nFlags, bool bWeaponsCheck /* = false */ ) |
|
{ |
|
int nLocalPlayerFlags = GetLocalPlayerVisionFilterFlags( bWeaponsCheck ); |
|
|
|
if ( !bWeaponsCheck ) |
|
{ |
|
// We can only modify the RJ flags with normal checks that won't take the forced kill cam flags that can happen in weapon checks |
|
int nRJShaderFlags = nLocalPlayerFlags; |
|
if ( nRJShaderFlags != 0 && GameRules() && !GameRules()->AllowMapVisionFilterShaders() ) |
|
{ |
|
nRJShaderFlags = 0; |
|
} |
|
|
|
if ( nRJShaderFlags != localplayer_visionflags.GetInt() ) |
|
{ |
|
localplayer_visionflags.SetValue( nRJShaderFlags ); |
|
} |
|
} |
|
|
|
return ( nLocalPlayerFlags & nFlags ) == nFlags; |
|
} |
|
|
|
bool IsLocalPlayerSpectator( void ) |
|
{ |
|
C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); |
|
|
|
if ( player ) |
|
return player->IsObserver(); |
|
else |
|
return false; // game not started yet |
|
} |
|
|
|
int GetSpectatorMode( void ) |
|
{ |
|
C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); |
|
|
|
if ( player ) |
|
return player->GetObserverMode(); |
|
else |
|
return OBS_MODE_NONE; // game not started yet |
|
} |
|
|
|
int GetSpectatorTarget( void ) |
|
{ |
|
C_BasePlayer * player = C_BasePlayer::GetLocalPlayer(); |
|
|
|
if ( player ) |
|
{ |
|
CBaseEntity * target = player->GetObserverTarget(); |
|
|
|
if ( target ) |
|
return target->entindex(); |
|
else |
|
return 0; |
|
} |
|
else |
|
{ |
|
return 0; // game not started yet |
|
} |
|
} |
|
|
|
int GetLocalPlayerTeam( void ) |
|
{ |
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); |
|
|
|
if ( pPlayer ) |
|
return pPlayer->GetTeamNumber(); |
|
else |
|
return TEAM_UNASSIGNED; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Convert angles to -180 t 180 range |
|
// Input : angles - |
|
//----------------------------------------------------------------------------- |
|
void NormalizeAngles( QAngle& angles ) |
|
{ |
|
int i; |
|
|
|
// Normalize angles to -180 to 180 range |
|
for ( i = 0; i < 3; i++ ) |
|
{ |
|
if ( angles[i] > 180.0 ) |
|
{ |
|
angles[i] -= 360.0; |
|
} |
|
else if ( angles[i] < -180.0 ) |
|
{ |
|
angles[i] += 360.0; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Interpolate Euler angles using quaternions to avoid singularities |
|
// Input : start - |
|
// end - |
|
// output - |
|
// frac - |
|
//----------------------------------------------------------------------------- |
|
void InterpolateAngles( const QAngle& start, const QAngle& end, QAngle& output, float frac ) |
|
{ |
|
Quaternion src, dest; |
|
|
|
// Convert to quaternions |
|
AngleQuaternion( start, src ); |
|
AngleQuaternion( end, dest ); |
|
|
|
Quaternion result; |
|
|
|
// Slerp |
|
QuaternionSlerp( src, dest, frac, result ); |
|
|
|
// Convert to euler |
|
QuaternionAngles( result, output ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Simple linear interpolation |
|
// Input : frac - |
|
// src - |
|
// dest - |
|
// output - |
|
//----------------------------------------------------------------------------- |
|
void InterpolateVector( float frac, const Vector& src, const Vector& dest, Vector& output ) |
|
{ |
|
int i; |
|
|
|
for ( i = 0; i < 3; i++ ) |
|
{ |
|
output[ i ] = src[ i ] + frac * ( dest[ i ] - src[ i ] ); |
|
} |
|
} |
|
|
|
client_textmessage_t *TextMessageGet( const char *pName ) |
|
{ |
|
return engine->TextMessageGet( pName ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: ScreenHeight returns the height of the screen, in pixels |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int ScreenHeight( void ) |
|
{ |
|
int w, h; |
|
GetHudSize( w, h ); |
|
return h; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: ScreenWidth returns the width of the screen, in pixels |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int ScreenWidth( void ) |
|
{ |
|
int w, h; |
|
GetHudSize( w, h ); |
|
return w; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return the difference between two angles |
|
// Input : destAngle - |
|
// srcAngle - |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float UTIL_AngleDiff( float destAngle, float srcAngle ) |
|
{ |
|
float delta; |
|
|
|
delta = destAngle - srcAngle; |
|
if ( destAngle > srcAngle ) |
|
{ |
|
while ( delta >= 180 ) |
|
delta -= 360; |
|
} |
|
else |
|
{ |
|
while ( delta <= -180 ) |
|
delta += 360; |
|
} |
|
return delta; |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
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 ); |
|
|
|
int bubbles = modelinfo->GetModelIndex( "sprites/bubble.vmt" ); |
|
|
|
te->Bubbles( filter, 0.0, |
|
&mins, &maxs, flHeight, bubbles, count, 8.0 ); |
|
} |
|
|
|
void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, bool bAirShake ) |
|
{ |
|
// Nothing for now |
|
} |
|
|
|
char TEXTURETYPE_Find( trace_t *ptr ) |
|
{ |
|
surfacedata_t *psurfaceData = physprops->GetSurfaceData( ptr->surface.surfaceProps ); |
|
|
|
return psurfaceData->game.material; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Make a tracer effect |
|
//----------------------------------------------------------------------------- |
|
void UTIL_Tracer( const Vector &vecStart, const Vector &vecEnd, int iEntIndex, int iAttachment, float flVelocity, bool bWhiz, char *pCustomTracerName ) |
|
{ |
|
CEffectData data; |
|
data.m_vStart = vecStart; |
|
data.m_vOrigin = vecEnd; |
|
data.m_hEntity = ClientEntityList().EntIndexToHandle( iEntIndex ); |
|
data.m_flScale = flVelocity; |
|
|
|
// Flags |
|
if ( bWhiz ) |
|
{ |
|
data.m_fFlags |= TRACER_FLAG_WHIZ; |
|
} |
|
if ( iAttachment != TRACER_DONT_USE_ATTACHMENT ) |
|
{ |
|
data.m_fFlags |= TRACER_FLAG_USEATTACHMENT; |
|
// Stomp the start, since it's not going to be used anyway |
|
data.m_vStart[0] = iAttachment; |
|
} |
|
|
|
// Fire it off |
|
if ( pCustomTracerName ) |
|
{ |
|
DispatchEffect( pCustomTracerName, data ); |
|
} |
|
else |
|
{ |
|
DispatchEffect( "Tracer", data ); |
|
} |
|
} |
|
|
|
|
|
//------------------------------------------------------------------------------ |
|
// 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 ) |
|
{ |
|
C_BaseEntity *pEntity = pTrace->m_pEnt; |
|
|
|
// Is the entity valid, is the surface sky? |
|
if ( !pEntity || (pTrace->surface.flags & SURF_SKY) ) |
|
return; |
|
|
|
if (pTrace->fraction == 1.0) |
|
return; |
|
|
|
// don't decal nodraw surfaces |
|
if ( pTrace->surface.flags & SURF_NODRAW ) |
|
return; |
|
|
|
pEntity->ImpactTrace( pTrace, iDamageType, pCustomImpactName ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int UTIL_PrecacheDecal( const char *name, bool preload ) |
|
{ |
|
return effects->Draw_DecalIndexFromName( (char*)name ); |
|
} |
|
|
|
extern short g_sModelIndexSmoke; |
|
|
|
void UTIL_Smoke( const Vector &origin, const float scale, const float framerate ) |
|
{ |
|
CPVSFilter filter( origin ); |
|
te->Smoke( filter, 0.0f, &origin, g_sModelIndexSmoke, scale, framerate ); |
|
} |
|
|
|
void UTIL_SetOrigin( C_BaseEntity *entity, const Vector &vecOrigin ) |
|
{ |
|
entity->SetLocalOrigin( vecOrigin ); |
|
} |
|
|
|
//#define PRECACHE_OTHER_ONCE |
|
// UNDONE: Do we need this to avoid doing too much of this? Measure startup times and see |
|
#if PRECACHE_OTHER_ONCE |
|
|
|
#include "utlsymbol.h" |
|
class CPrecacheOtherList : public CAutoServerSystem |
|
{ |
|
public: |
|
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; |
|
#endif |
|
|
|
void UTIL_PrecacheOther( const char *szClassname ) |
|
{ |
|
#if PRECACHE_OTHER_ONCE |
|
// already done this one?, if not, mark as done |
|
if ( !g_PrecacheOtherList.AddOrMarkPrecached( szClassname ) ) |
|
return; |
|
#endif |
|
|
|
// Client should only do this once entities are coming down from server!!! |
|
// Assert( engine->IsConnected() ); |
|
|
|
C_BaseEntity *pEntity = CreateEntityByName( szClassname ); |
|
if ( !pEntity ) |
|
{ |
|
Warning( "NULL Ent in UTIL_PrecacheOther\n" ); |
|
return; |
|
} |
|
|
|
if (pEntity) |
|
{ |
|
pEntity->Precache( ); |
|
} |
|
|
|
// Bye bye |
|
pEntity->Release(); |
|
} |
|
|
|
static csurface_t g_NullSurface = { "**empty**", 0 }; |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void UTIL_SetTrace(trace_t& trace, const Ray_t& ray, C_BaseEntity *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 = C_BaseEntity::Instance( ent ); |
|
trace.hitgroup = hitgroup; |
|
trace.surface = g_NullSurface; |
|
trace.contents = contents; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get the x & y positions of a world position in screenspace |
|
// Returns true if it's onscreen |
|
//----------------------------------------------------------------------------- |
|
bool GetVectorInScreenSpace( Vector pos, int& iX, int& iY, Vector *vecOffset ) |
|
{ |
|
Vector screen; |
|
|
|
// Apply the offset, if one was specified |
|
if ( vecOffset != NULL ) |
|
pos += *vecOffset; |
|
|
|
// Transform to screen space |
|
int iFacing = ScreenTransform( pos, screen ); |
|
iX = 0.5f * ( 1.0f + screen[0] ) * ScreenWidth(); |
|
iY = 0.5f * ( 1.0f - screen[1] ) * ScreenHeight(); |
|
|
|
// Make sure the player's facing it |
|
if ( iFacing ) |
|
{ |
|
// We're actually facing away from the Target. Stomp the screen position. |
|
iX = -640; |
|
iY = -640; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get the x & y positions of a world position in HUD space |
|
// Returns true if it's onscreen |
|
//----------------------------------------------------------------------------- |
|
bool GetVectorInHudSpace( Vector pos, int& iX, int& iY, Vector *vecOffset ) |
|
{ |
|
Vector screen; |
|
|
|
// Apply the offset, if one was specified |
|
if ( vecOffset != NULL ) |
|
pos += *vecOffset; |
|
|
|
// Transform to HUD space |
|
int iFacing = HudTransform( pos, screen ); |
|
iX = 0.5f * ( 1.0f + screen[0] ) * ScreenWidth(); |
|
iY = 0.5f * ( 1.0f - screen[1] ) * ScreenHeight(); |
|
|
|
// Make sure the player's facing it |
|
if ( iFacing ) |
|
{ |
|
// We're actually facing away from the Target. Stomp the screen position. |
|
iX = -640; |
|
iY = -640; |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get the x & y positions of an entity in screenspace |
|
// Returns true if it's onscreen |
|
//----------------------------------------------------------------------------- |
|
bool GetTargetInScreenSpace( C_BaseEntity *pTargetEntity, int& iX, int& iY, Vector *vecOffset ) |
|
{ |
|
return GetVectorInScreenSpace( pTargetEntity->WorldSpaceCenter(), iX, iY, vecOffset ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get the x & y positions of an entity in Vgui space |
|
// Returns true if it's onscreen |
|
//----------------------------------------------------------------------------- |
|
bool GetTargetInHudSpace( C_BaseEntity *pTargetEntity, int& iX, int& iY, Vector *vecOffset ) |
|
{ |
|
return GetVectorInHudSpace( pTargetEntity->WorldSpaceCenter(), iX, iY, vecOffset ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *player - |
|
// msg_dest - |
|
// *msg_name - |
|
// *param1 - |
|
// *param2 - |
|
// *param3 - |
|
// *param4 - |
|
//----------------------------------------------------------------------------- |
|
void ClientPrint( C_BasePlayer *player, int msg_dest, const char *msg_name, const char *param1 /*= NULL*/, const char *param2 /*= NULL*/, const char *param3 /*= NULL*/, const char *param4 /*= NULL*/ ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// class CFlaggedEntitiesEnum |
|
//----------------------------------------------------------------------------- |
|
// enumerate entities that match a set of edict flags into a static array |
|
class CFlaggedEntitiesEnum : public IPartitionEnumerator |
|
{ |
|
public: |
|
CFlaggedEntitiesEnum( C_BaseEntity **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( C_BaseEntity *pEntity ); |
|
|
|
private: |
|
C_BaseEntity **m_pList; |
|
int m_listMax; |
|
int m_flagMask; |
|
int m_count; |
|
}; |
|
|
|
CFlaggedEntitiesEnum::CFlaggedEntitiesEnum( C_BaseEntity **pList, int listMax, int flagMask ) |
|
{ |
|
m_pList = pList; |
|
m_listMax = listMax; |
|
m_flagMask = flagMask; |
|
m_count = 0; |
|
} |
|
|
|
bool CFlaggedEntitiesEnum::AddToList( C_BaseEntity *pEntity ) |
|
{ |
|
if ( m_count >= m_listMax ) |
|
return false; |
|
m_pList[m_count] = pEntity; |
|
m_count++; |
|
return true; |
|
} |
|
|
|
IterationRetval_t CFlaggedEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity ) |
|
{ |
|
IClientEntity *pClientEntity = cl_entitylist->GetClientEntityFromHandle( pHandleEntity->GetRefEHandle() ); |
|
C_BaseEntity *pEntity = pClientEntity ? pClientEntity->GetBaseEntity() : NULL; |
|
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: Pass in an array of pointers and an array size, it fills the array and returns the number inserted |
|
// Input : **pList - |
|
// listMax - |
|
// &mins - |
|
// &maxs - |
|
// flagMask - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int UTIL_EntitiesInBox( C_BaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask, int partitionMask ) |
|
{ |
|
CFlaggedEntitiesEnum boxEnum( pList, listMax, flagMask ); |
|
::partition->EnumerateElementsInBox( partitionMask, mins, maxs, false, &boxEnum ); |
|
|
|
return boxEnum.GetCount(); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted |
|
// Input : **pList - |
|
// listMax - |
|
// ¢er - |
|
// radius - |
|
// flagMask - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int UTIL_EntitiesInSphere( C_BaseEntity **pList, int listMax, const Vector ¢er, float radius, int flagMask, int partitionMask ) |
|
{ |
|
CFlaggedEntitiesEnum sphereEnum( pList, listMax, flagMask ); |
|
::partition->EnumerateElementsInSphere( partitionMask, center, radius, false, &sphereEnum ); |
|
|
|
return sphereEnum.GetCount(); |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Pass in an array of pointers and an array size, it fills the array and returns the number inserted |
|
// Input : **pList - |
|
// listMax - |
|
// &ray - |
|
// flagMask - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int UTIL_EntitiesAlongRay( C_BaseEntity **pList, int listMax, const Ray_t &ray, int flagMask, int partitionMask ) |
|
{ |
|
CFlaggedEntitiesEnum rayEnum( pList, listMax, flagMask ); |
|
::partition->EnumerateElementsAlongRay( partitionMask, ray, false, &rayEnum ); |
|
|
|
return rayEnum.GetCount(); |
|
} |
|
|
|
CEntitySphereQuery::CEntitySphereQuery( const Vector ¢er, float radius, int flagMask, int partitionMask ) |
|
{ |
|
m_listIndex = 0; |
|
m_listCount = UTIL_EntitiesInSphere( m_pList, ARRAYSIZE(m_pList), center, radius, flagMask, partitionMask ); |
|
} |
|
|
|
CBaseEntity *CEntitySphereQuery::GetCurrentEntity() |
|
{ |
|
if ( m_listIndex < m_listCount ) |
|
return m_pList[m_listIndex]; |
|
return NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : font - |
|
// *str - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int UTIL_ComputeStringWidth( vgui::HFont& font, const char *str ) |
|
{ |
|
float pixels = 0; |
|
const char *p = str; |
|
const char *pAfter = p + 1; |
|
const char *pBefore = "\0"; |
|
while ( *p ) |
|
{ |
|
#if USE_GETKERNEDCHARWIDTH |
|
float wide, abcA; |
|
vgui::surface()->GetKernedCharWidth( font, *p, *pBefore, *pAfter, wide, abcA ); |
|
pixels += wide; |
|
#else |
|
pixels += vgui::surface()->GetCharacterWidth( font, *p ); |
|
#endif |
|
pBefore = p; |
|
p++; |
|
if ( *p ) |
|
pAfter = p + 1; |
|
else |
|
pAfter = "\0"; |
|
} |
|
return (int)ceil(pixels); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : font - |
|
// *str - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int UTIL_ComputeStringWidth( vgui::HFont& font, const wchar_t *str ) |
|
{ |
|
float pixels = 0; |
|
const wchar_t *p = str; |
|
const wchar_t *pAfter = p + 1; |
|
const wchar_t *pBefore = L"\0"; |
|
while ( *p ) |
|
{ |
|
#if USE_GETKERNEDCHARWIDTH |
|
float wide, abcA; |
|
vgui::surface()->GetKernedCharWidth( font, *p, *pBefore, *pAfter, wide, abcA ); |
|
pixels += wide; |
|
#else |
|
pixels += vgui::surface()->GetCharacterWidth( font, *p ); |
|
#endif |
|
pBefore = p; |
|
p++; |
|
if ( *p ) |
|
pAfter = p + 1; |
|
else |
|
pAfter = L"\0"; |
|
} |
|
return (int)ceil(pixels); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Scans player names |
|
//Passes the player name to be checked in a KeyValues pointer |
|
//with the keyname "name" |
|
// - replaces '&' with '&&' so they will draw in the scoreboard |
|
// - replaces '#' at the start of the name with '*' |
|
//----------------------------------------------------------------------------- |
|
|
|
void UTIL_MakeSafeName( const char *oldName, char *newName, int newNameBufSize ) |
|
{ |
|
Assert( newNameBufSize >= sizeof(newName[0]) ); |
|
|
|
int newpos = 0; |
|
|
|
for( const char *p=oldName; *p != 0 && newpos < newNameBufSize-1; p++ ) |
|
{ |
|
//check for a '#' char at the beginning |
|
if( p == oldName && *p == '#' ) |
|
{ |
|
newName[newpos] = '*'; |
|
newpos++; |
|
} |
|
else if( *p == '%' ) |
|
{ |
|
// remove % chars |
|
newName[newpos] = '*'; |
|
newpos++; |
|
} |
|
else if( *p == '&' ) |
|
{ |
|
//insert another & after this one |
|
if ( newpos+2 < newNameBufSize ) |
|
{ |
|
newName[newpos] = '&'; |
|
newName[newpos+1] = '&'; |
|
newpos+=2; |
|
} |
|
} |
|
else |
|
{ |
|
newName[newpos] = *p; |
|
newpos++; |
|
} |
|
} |
|
newName[newpos] = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Scans player names and replaces characters that vgui won't |
|
// display properly |
|
// Input : *oldName - player name to be fixed up |
|
// Output : *char - static buffer with the safe name |
|
//----------------------------------------------------------------------------- |
|
|
|
const char * UTIL_SafeName( const char *oldName ) |
|
{ |
|
static char safeName[ MAX_PLAYER_NAME_LENGTH * 2 + 1 ]; |
|
UTIL_MakeSafeName( oldName, safeName, sizeof( safeName ) ); |
|
|
|
return safeName; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Looks up key bindings for commands and replaces them in string. |
|
// %<commandname>% will get replaced with its bound control, e.g. %attack2% |
|
// Input buffer sizes are in bytes rather than unicode character count |
|
// for consistency with other APIs. If inbufsizebytes is 0 a NULL-terminated |
|
// input buffer is assumed, or you can pass the size of the input buffer if |
|
// not NULL-terminated. |
|
// |
|
// If actionset is other than GAME_ACTION_SET_NONE (the default), then a lookup is first |
|
// attempted for a Steam Controller binding in the given action set. If none if found, fallback |
|
// is to the usual keyboard binding path. |
|
//----------------------------------------------------------------------------- |
|
void UTIL_ReplaceKeyBindings( const wchar_t *inbuf, int inbufsizebytes, OUT_Z_BYTECAP(outbufsizebytes) wchar_t *outbuf, int outbufsizebytes, GameActionSet_t actionset ) |
|
{ |
|
Assert( outbufsizebytes >= sizeof(outbuf[0]) ); |
|
// copy to a new buf if there are vars |
|
outbuf[0]=0; |
|
|
|
if ( !inbuf || !inbuf[0] ) |
|
return; |
|
|
|
int pos = 0; |
|
const wchar_t *inbufend = NULL; |
|
if ( inbufsizebytes > 0 ) |
|
{ |
|
inbufend = inbuf + ( inbufsizebytes / 2 ); |
|
} |
|
|
|
while( inbuf != inbufend && *inbuf != 0 ) |
|
{ |
|
// check for variables |
|
if ( *inbuf == '%' ) |
|
{ |
|
++inbuf; |
|
|
|
const wchar_t *end = wcschr( inbuf, '%' ); |
|
if ( end && ( end != inbuf ) ) // make sure we handle %% in the string, which should be treated in the output as % |
|
{ |
|
wchar_t token[64]; |
|
wcsncpy( token, inbuf, end - inbuf ); |
|
token[end - inbuf] = 0; |
|
|
|
inbuf += end - inbuf; |
|
|
|
// lookup key names |
|
char binding[64]; |
|
g_pVGuiLocalize->ConvertUnicodeToANSI( token, binding, sizeof(binding) ); |
|
|
|
// Find a Steam Controller mapping, if an action set was specified. |
|
const wchar_t* sc_origin = nullptr; |
|
if ( actionset != GAME_ACTION_SET_NONE) |
|
{ |
|
auto origin = g_pInputSystem->GetSteamControllerActionOrigin( *binding == '+' ? binding + 1 : binding, actionset ); |
|
if ( origin != k_EControllerActionOrigin_None ) |
|
{ |
|
sc_origin = g_pInputSystem->GetSteamControllerDescriptionForActionOrigin( origin ); |
|
} |
|
} |
|
|
|
// Find also the keyboard mapping |
|
const char *key = engine->Key_LookupBinding( *binding == '+' ? binding + 1 : binding ); |
|
if ( !key ) |
|
{ |
|
key = IsX360() ? "" : "< not bound >"; |
|
} |
|
|
|
//!! change some key names into better names |
|
char friendlyName[64]; |
|
bool bAddBrackets = false; |
|
if ( IsX360() ) |
|
{ |
|
if ( !key || !key[0] ) |
|
{ |
|
Q_snprintf( friendlyName, sizeof(friendlyName), "#GameUI_None" ); |
|
bAddBrackets = true; |
|
} |
|
else |
|
{ |
|
Q_snprintf( friendlyName, sizeof(friendlyName), "#GameUI_KeyNames_%s", key ); |
|
} |
|
} |
|
else |
|
{ |
|
Q_snprintf( friendlyName, sizeof(friendlyName), "%s", key ); |
|
} |
|
Q_strupr( friendlyName ); |
|
|
|
const wchar_t* locName = nullptr; |
|
|
|
// If we got a Steam Controller key description, use that, otherwise use the (possibly localized) key name |
|
if ( sc_origin ) |
|
{ |
|
locName = sc_origin; |
|
} |
|
else |
|
{ |
|
locName = g_pVGuiLocalize->Find( friendlyName ); |
|
} |
|
|
|
if ( !locName || wcslen(locName) <= 0) |
|
{ |
|
g_pVGuiLocalize->ConvertANSIToUnicode( friendlyName, token, sizeof(token) ); |
|
|
|
outbuf[pos] = '\0'; |
|
wcscat( outbuf, token ); |
|
pos += wcslen(token); |
|
} |
|
else |
|
{ |
|
outbuf[pos] = '\0'; |
|
if ( bAddBrackets ) |
|
{ |
|
wcscat( outbuf, L"[" ); |
|
pos += 1; |
|
} |
|
wcscat( outbuf, locName ); |
|
pos += wcslen(locName); |
|
if ( bAddBrackets ) |
|
{ |
|
wcscat( outbuf, L"]" ); |
|
pos += 1; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
outbuf[pos] = *inbuf; |
|
++pos; |
|
} |
|
} |
|
else |
|
{ |
|
outbuf[pos] = *inbuf; |
|
++pos; |
|
} |
|
|
|
++inbuf; |
|
} |
|
|
|
outbuf[pos] = '\0'; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *filename - |
|
// *pLength - |
|
// Output : byte |
|
//----------------------------------------------------------------------------- |
|
byte *UTIL_LoadFileForMe( const char *filename, int *pLength ) |
|
{ |
|
byte *buffer; |
|
|
|
FileHandle_t file; |
|
file = filesystem->Open( filename, "rb", "GAME" ); |
|
if ( FILESYSTEM_INVALID_HANDLE == file ) |
|
{ |
|
if ( pLength ) *pLength = 0; |
|
return NULL; |
|
} |
|
|
|
int size = filesystem->Size( file ); |
|
buffer = new byte[ size + 1 ]; |
|
if ( !buffer ) |
|
{ |
|
Warning( "UTIL_LoadFileForMe: Couldn't allocate buffer of size %i for file %s\n", size + 1, filename ); |
|
filesystem->Close( file ); |
|
return NULL; |
|
} |
|
filesystem->Read( buffer, size, file ); |
|
filesystem->Close( file ); |
|
|
|
// Ensure null terminator |
|
buffer[ size ] =0; |
|
|
|
if ( pLength ) |
|
{ |
|
*pLength = size; |
|
} |
|
|
|
return buffer; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *buffer - |
|
//----------------------------------------------------------------------------- |
|
void UTIL_FreeFile( byte *buffer ) |
|
{ |
|
delete[] buffer; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Compute distance fade |
|
//----------------------------------------------------------------------------- |
|
static unsigned char ComputeDistanceFade( C_BaseEntity *pEntity, float flMinDist, float flMaxDist ) |
|
{ |
|
if ((flMinDist <= 0) && (flMaxDist <= 0)) |
|
return 255; |
|
|
|
if( flMinDist > flMaxDist ) |
|
{ |
|
::V_swap( flMinDist, flMaxDist ); |
|
} |
|
|
|
// If a negative value is provided for the min fade distance, then base it off the max. |
|
if( flMinDist < 0 ) |
|
{ |
|
flMinDist = flMaxDist - 400; |
|
if( flMinDist < 0 ) |
|
{ |
|
flMinDist = 0; |
|
} |
|
} |
|
|
|
flMinDist *= flMinDist; |
|
flMaxDist *= flMaxDist; |
|
|
|
float flCurrentDistanceSq = CurrentViewOrigin().DistToSqr( pEntity->WorldSpaceCenter() ); |
|
C_BasePlayer *pLocal = C_BasePlayer::GetLocalPlayer(); |
|
if ( pLocal ) |
|
{ |
|
float flDistFactor = pLocal->GetFOVDistanceAdjustFactor(); |
|
flCurrentDistanceSq *= flDistFactor * flDistFactor; |
|
} |
|
|
|
// If I'm inside the minimum range than don't resort to alpha trickery |
|
if ( flCurrentDistanceSq <= flMinDist ) |
|
return 255; |
|
|
|
if ( flCurrentDistanceSq >= flMaxDist ) |
|
return 0; |
|
|
|
// NOTE: Because of the if-checks above, flMinDist != flMinDist here |
|
float flFalloffFactor = 255.0f / (flMaxDist - flMinDist); |
|
int nAlpha = flFalloffFactor * (flMaxDist - flCurrentDistanceSq); |
|
return clamp( nAlpha, 0, 255 ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Compute fade amount |
|
//----------------------------------------------------------------------------- |
|
unsigned char UTIL_ComputeEntityFade( C_BaseEntity *pEntity, float flMinDist, float flMaxDist, float flFadeScale ) |
|
{ |
|
unsigned char nAlpha = 255; |
|
|
|
// If we're taking devshots, don't fade props at all |
|
if ( g_MakingDevShots || cl_leveloverview.GetFloat() > 0 ) |
|
return 255; |
|
|
|
#ifdef _DEBUG |
|
if ( r_FadeProps.GetBool() ) |
|
#endif |
|
{ |
|
nAlpha = ComputeDistanceFade( pEntity, flMinDist, flMaxDist ); |
|
|
|
// NOTE: This computation for the center + radius is invalid! |
|
// The center of the sphere is at the center of the OBB, which is not necessarily |
|
// at the render origin. But it should be close enough. |
|
Vector vecMins, vecMaxs; |
|
pEntity->GetRenderBounds( vecMins, vecMaxs ); |
|
float flRadius = vecMins.DistTo( vecMaxs ) * 0.5f; |
|
|
|
Vector vecAbsCenter; |
|
if ( modelinfo->GetModelType( pEntity->GetModel() ) == mod_brush ) |
|
{ |
|
Vector vecRenderMins, vecRenderMaxs; |
|
pEntity->GetRenderBoundsWorldspace( vecRenderMins, vecRenderMaxs ); |
|
VectorAdd( vecRenderMins, vecRenderMaxs, vecAbsCenter ); |
|
vecAbsCenter *= 0.5f; |
|
} |
|
else |
|
{ |
|
vecAbsCenter = pEntity->GetRenderOrigin(); |
|
} |
|
|
|
unsigned char nGlobalAlpha = IsXbox() ? 255 : modelinfo->ComputeLevelScreenFade( vecAbsCenter, flRadius, flFadeScale ); |
|
unsigned char nDistAlpha; |
|
|
|
if ( !engine->IsLevelMainMenuBackground() ) |
|
{ |
|
nDistAlpha = modelinfo->ComputeViewScreenFade( vecAbsCenter, flRadius, flFadeScale ); |
|
} |
|
else |
|
{ |
|
nDistAlpha = 255; |
|
} |
|
|
|
if ( nDistAlpha < nGlobalAlpha ) |
|
{ |
|
nGlobalAlpha = nDistAlpha; |
|
} |
|
|
|
if ( nGlobalAlpha < nAlpha ) |
|
{ |
|
nAlpha = nGlobalAlpha; |
|
} |
|
} |
|
|
|
return nAlpha; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// 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 ); |
|
} |
|
} |
|
|
|
#ifdef _X360 |
|
#define MAP_KEY_FILE_DIR "cfg" |
|
#else |
|
#define MAP_KEY_FILE_DIR "media" |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the filename to count map loads in |
|
//----------------------------------------------------------------------------- |
|
bool UTIL_GetMapLoadCountFileName( const char *pszFilePrependName, char *pszBuffer, int iBuflen ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
#ifdef _X360 |
|
if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED ) |
|
return false; |
|
#endif |
|
} |
|
|
|
if ( IsX360() ) |
|
{ |
|
Q_snprintf( pszBuffer, iBuflen, "%s:/%s", MAP_KEY_FILE_DIR, pszFilePrependName ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( pszBuffer, iBuflen, "%s/%s", MAP_KEY_FILE_DIR, pszFilePrependName ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
#ifdef TF_CLIENT_DLL |
|
#define MAP_KEY_FILE "viewed.res" |
|
#else |
|
#define MAP_KEY_FILE "mapkeys.res" |
|
#endif |
|
|
|
void UTIL_IncrementMapKey( const char *pszCustomKey ) |
|
{ |
|
if ( !pszCustomKey ) |
|
return; |
|
|
|
char szFilename[ _MAX_PATH ]; |
|
if ( !UTIL_GetMapLoadCountFileName( MAP_KEY_FILE, szFilename, _MAX_PATH ) ) |
|
return; |
|
|
|
int iCount = 1; |
|
|
|
KeyValues *kvMapLoadFile = new KeyValues( MAP_KEY_FILE ); |
|
if ( kvMapLoadFile ) |
|
{ |
|
kvMapLoadFile->LoadFromFile( g_pFullFileSystem, szFilename, "MOD" ); |
|
|
|
char mapname[MAX_MAP_NAME]; |
|
Q_FileBase( engine->GetLevelName(), mapname, sizeof( mapname) ); |
|
Q_strlower( mapname ); |
|
|
|
// Increment existing, or add a new one |
|
KeyValues *pMapKey = kvMapLoadFile->FindKey( mapname ); |
|
if ( pMapKey ) |
|
{ |
|
iCount = pMapKey->GetInt( pszCustomKey, 0 ) + 1; |
|
pMapKey->SetInt( pszCustomKey, iCount ); |
|
} |
|
else |
|
{ |
|
KeyValues *pNewKey = new KeyValues( mapname ); |
|
if ( pNewKey ) |
|
{ |
|
pNewKey->SetString( pszCustomKey, "1" ); |
|
kvMapLoadFile->AddSubKey( pNewKey ); |
|
} |
|
} |
|
|
|
// Write it out |
|
|
|
// force create this directory incase it doesn't exist |
|
filesystem->CreateDirHierarchy( MAP_KEY_FILE_DIR, "MOD"); |
|
|
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); |
|
kvMapLoadFile->RecursiveSaveToFile( buf, 0 ); |
|
g_pFullFileSystem->WriteFile( szFilename, "MOD", buf ); |
|
|
|
kvMapLoadFile->deleteThis(); |
|
} |
|
|
|
if ( IsX360() ) |
|
{ |
|
#ifdef _X360 |
|
xboxsystem->FinishContainerWrites(); |
|
#endif |
|
} |
|
} |
|
|
|
int UTIL_GetMapKeyCount( const char *pszCustomKey ) |
|
{ |
|
if ( !pszCustomKey ) |
|
return 0; |
|
|
|
char szFilename[ _MAX_PATH ]; |
|
if ( !UTIL_GetMapLoadCountFileName( MAP_KEY_FILE, szFilename, _MAX_PATH ) ) |
|
return 0; |
|
|
|
int iCount = 0; |
|
|
|
KeyValues *kvMapLoadFile = new KeyValues( MAP_KEY_FILE ); |
|
if ( kvMapLoadFile ) |
|
{ |
|
// create an empty file if none exists |
|
if ( !g_pFullFileSystem->FileExists( szFilename, "MOD" ) ) |
|
{ |
|
// force create this directory incase it doesn't exist |
|
filesystem->CreateDirHierarchy( MAP_KEY_FILE_DIR, "MOD"); |
|
|
|
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); |
|
g_pFullFileSystem->WriteFile( szFilename, "MOD", buf ); |
|
} |
|
|
|
kvMapLoadFile->LoadFromFile( g_pFullFileSystem, szFilename, "MOD" ); |
|
|
|
char mapname[MAX_MAP_NAME]; |
|
Q_FileBase( engine->GetLevelName(), mapname, sizeof( mapname) ); |
|
Q_strlower( mapname ); |
|
|
|
KeyValues *pMapKey = kvMapLoadFile->FindKey( mapname ); |
|
if ( pMapKey ) |
|
{ |
|
iCount = pMapKey->GetInt( pszCustomKey ); |
|
} |
|
|
|
kvMapLoadFile->deleteThis(); |
|
} |
|
|
|
return iCount; |
|
} |
|
|
|
bool UTIL_HasLoadedAnyMap() |
|
{ |
|
char szFilename[ _MAX_PATH ]; |
|
if ( !UTIL_GetMapLoadCountFileName( MAP_KEY_FILE, szFilename, _MAX_PATH ) ) |
|
return false; |
|
|
|
return g_pFullFileSystem->FileExists( szFilename, "MOD" ); |
|
}
|
|
|