source-engine/game/client/cdll_util.cpp
2022-03-01 23:00:42 +03:00

1322 lines
35 KiB
C++

//========= 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"
// 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
}
int GetLocalPlayerVisionFilterFlags( bool bWeaponsCheck /*= false */ )
{
C_BasePlayer * player = C_BasePlayer::GetLocalPlayer();
if ( player )
return player->GetVisionFilterFlags( bWeaponsCheck );
else
return 0;
}
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 &center, 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 -
// &center -
// radius -
// flagMask -
// Output : int
//-----------------------------------------------------------------------------
int UTIL_EntitiesInSphere( C_BaseEntity **pList, int listMax, const Vector &center, 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 &center, 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: 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:
// 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.
//-----------------------------------------------------------------------------
void UTIL_ReplaceKeyBindings( const wchar_t *inbuf, int inbufsizebytes, OUT_Z_BYTECAP(outbufsizebytes) wchar_t *outbuf, int outbufsizebytes )
{
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) );
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 );
wchar_t *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" );
}