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.
1454 lines
37 KiB
1454 lines
37 KiB
//===== Copyright 1996-2005, 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 <ctype.h>
|
|
#include <vgui_controls/EditablePanel.h>
|
|
#include "vgui_int.h"
|
|
#include "cdll_client_int.h"
|
|
#include "c_cs_playerresource.h"
|
|
#include "c_cs_player.h"
|
|
#include "cs_gamerules.h"
|
|
#include "weapon_c4.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
// this is meant to be called on a bot character
|
|
// note: This is quite similar to CCSPlayer::CanControlBot. This is here since calling into the CCSPlayer was unrealistic in some code paths
|
|
// TODO: Fix this so it ACTUALLY calls CanControlBot, there are bugs otherwise (and will be more) do to them being different functions
|
|
bool CanControlSpectatedTarget( void )
|
|
{
|
|
C_CSPlayer * player = ( C_CSPlayer* )C_BasePlayer::GetLocalPlayer();
|
|
|
|
if ( !player || ( player->GetPendingTeamNumber() != player->GetTeamNumber() ) )
|
|
return false;
|
|
|
|
C_CSPlayer * target = dynamic_cast<C_CSPlayer*>(player->GetObserverTarget());
|
|
if ( !target )
|
|
return false;
|
|
|
|
if ( !target->IsAlive() )
|
|
return false;
|
|
|
|
if ( !target->IsOtherSameTeam( GetLocalPlayerTeam() ) || target->IsOtherEnemy( player ) )
|
|
return false;
|
|
|
|
C_CS_PlayerResource *pCSPR = ( C_CS_PlayerResource* )GameResources();
|
|
int entIndex = target->entindex();
|
|
|
|
bool targetIsFakePlayer = pCSPR->IsFakePlayer( entIndex );
|
|
if ( !targetIsFakePlayer )
|
|
return false;
|
|
|
|
AssertMsg(pCSPR, "Expected PlayerResources to exsist");
|
|
AssertMsg(entIndex > 0 && entIndex <= MAX_PLAYERS, "Bad entity index for player");
|
|
|
|
|
|
// need to duplicate some of the checks that CanControlBot already does so they match up....
|
|
// Can't control a bot that is setting a bomb
|
|
const CC4 *pC4 = dynamic_cast<CC4*>( target->GetActiveWeapon() );
|
|
if ( pC4 && pC4->m_bStartedArming )
|
|
return false;
|
|
|
|
if ( CSGameRules() && CSGameRules()->IsRoundOver() )
|
|
return false;
|
|
|
|
if ( CSGameRules() && CSGameRules()->IsFreezePeriod() )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CanSeeSpectatorOnlyTools( void )
|
|
{
|
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
|
|
if ( !pPlayer )
|
|
return false;
|
|
|
|
if ( pPlayer->IsHLTV() )
|
|
return true;
|
|
|
|
if ( pPlayer->IsSpectator() )
|
|
{
|
|
if ( sv_competitive_official_5v5.GetBool() )
|
|
return true;
|
|
|
|
if ( CSGameRules() && CSGameRules()->IsQueuedMatchmaking() )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CanToggleXRayView( void )
|
|
{
|
|
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
|
|
if ( !pPlayer )
|
|
return false;
|
|
|
|
if ( CanSeeSpectatorOnlyTools() )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
}
|
|
|
|
// Tried to include this in the actual trace function, but ran into
|
|
// linker issues. Just dropped it here instead. Michael Dorgan
|
|
|
|
static bool HasValidDirection(trace_t *pTrace)
|
|
{
|
|
if(pTrace->fraction <= 0.0001f)
|
|
{
|
|
// Ok, there is a very, very strong chance our vectors are out of wack
|
|
// Add a direct check against the actual start and end points to be sure.
|
|
if( VectorsAreEqual( pTrace->startpos, pTrace->endpos, 0.0001f ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// 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, 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.0f)
|
|
return;
|
|
|
|
// Very, very, very close range shots cause all sorts of issues with normal and correct
|
|
// decal orientation. Just skip them to prevent heartache.
|
|
if ( !HasValidDirection(pTrace) )
|
|
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 int 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
|
|
UTIL_Remove( pEntity );
|
|
}
|
|
|
|
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 x, y, screenWidth, screenHeight;
|
|
int insetX, insetY;
|
|
VGui_GetEngineRenderBounds( GET_ACTIVE_SPLITSCREEN_SLOT(), x, y, screenWidth, screenHeight, insetX, insetY );
|
|
|
|
// Transform to screen space
|
|
int iFacing = ScreenTransform( pos, screen );
|
|
|
|
iX = 0.5 * screen[0] * screenWidth;
|
|
iY = -0.5 * screen[1] * screenHeight;
|
|
iX += 0.5 * screenWidth;
|
|
iY += 0.5 * screenHeight;
|
|
|
|
iX += insetX;
|
|
iY += insetY;
|
|
|
|
// 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:
|
|
// 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*/ )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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();
|
|
|
|
}
|
|
|
|
|
|
int UTIL_RenderablesInBox( C_BaseEntity** pList, int listMax, const Vector &mins, const Vector &maxs )
|
|
{
|
|
return g_pClientLeafSystem->GetEntitiesInBox( pList, listMax, mins, maxs );
|
|
}
|
|
|
|
|
|
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;
|
|
char *p = (char *)str;
|
|
char *pAfter = p + 1;
|
|
char *pBefore = "\0";
|
|
while ( *p )
|
|
{
|
|
#ifdef OSX
|
|
float wide, abcA, abcC;
|
|
vgui::surface()->GetKernedCharWidth( font, *p, *pBefore, *pAfter, wide, abcA, abcC );
|
|
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;
|
|
wchar_t *p = (wchar_t *)str;
|
|
wchar_t *pAfter = p + 1;
|
|
wchar_t *pBefore = L"\0";
|
|
while ( *p )
|
|
{
|
|
#ifdef OSX
|
|
float wide, abcA, abcC;
|
|
vgui::surface()->GetKernedCharWidth( font, *p, *pBefore, *pAfter, wide, abcA, abcC );
|
|
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 )
|
|
{
|
|
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, wchar_t *outbuf, int outbufsizebytes )
|
|
{
|
|
if ( !inbuf || !inbuf[0] )
|
|
return;
|
|
|
|
ASSERT_LOCAL_PLAYER_RESOLVABLE();
|
|
int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
|
|
|
|
// copy to a new buf if there are vars
|
|
outbuf[0]=0;
|
|
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_LookupBindingEx( *binding == '+' ? binding + 1 : binding, nSlot );
|
|
|
|
//!! change some key names into better names
|
|
char friendlyName[64];
|
|
bool bAddBrackets = false;
|
|
if ( IsGameConsole() )
|
|
{
|
|
if ( !key || !key[0] )
|
|
{
|
|
Q_snprintf( friendlyName, sizeof(friendlyName), "%s", binding );
|
|
bAddBrackets = true;
|
|
}
|
|
else
|
|
{
|
|
Q_snprintf( friendlyName, sizeof(friendlyName), "#GameUI_KeyNames_%s", key );
|
|
Q_strupr( friendlyName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !key || !key[0] )
|
|
{
|
|
Q_snprintf( friendlyName, sizeof(friendlyName), "%s", binding );
|
|
}
|
|
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:
|
|
//-----------------------------------------------------------------------------
|
|
void UTIL_SetControlStringWithKeybindings( vgui::EditablePanel *panel, const char *controlName, const char *str )
|
|
{
|
|
if ( !panel || !controlName || !str )
|
|
return;
|
|
|
|
const wchar_t *unicodeStr = g_pVGuiLocalize->Find( str );
|
|
if ( unicodeStr )
|
|
{
|
|
wchar_t buf[512];
|
|
UTIL_ReplaceKeyBindings( unicodeStr, 0, buf, sizeof( buf ) );
|
|
panel->SetControlString( controlName, buf );
|
|
}
|
|
else
|
|
{
|
|
panel->SetControlString( controlName, str );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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.GetInt() != 0 || input->CAM_IsThirdPersonOverview() )
|
|
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 = 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 _GAMECONSOLE
|
|
#define MAP_KEY_FILE_DIR "cfg"
|
|
#else
|
|
#define MAP_KEY_FILE_DIR "media"
|
|
#endif
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
C_BasePlayer* UTIL_PlayerByUserId( int userID )
|
|
{
|
|
for (int i = 1; i<=gpGlobals->maxClients; i++ )
|
|
{
|
|
C_BasePlayer *pPlayer = UTIL_PlayerByIndex( i );
|
|
|
|
if ( !pPlayer )
|
|
continue;
|
|
|
|
if ( pPlayer->GetUserID() == userID )
|
|
{
|
|
return pPlayer;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
C_BaseEntity* UTIL_EntityFromUserMessageEHandle( long nEncodedEHandle )
|
|
{
|
|
int nEntity, nSerialNum;
|
|
if( nEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE )
|
|
return NULL;
|
|
|
|
nEntity = nEncodedEHandle & ((1 << MAX_EDICT_BITS) - 1);
|
|
nSerialNum = nEncodedEHandle >> MAX_EDICT_BITS;
|
|
|
|
EHANDLE hEntity( nEntity, nSerialNum );
|
|
return hEntity.Get();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void UTIL_ApproachTarget( float target, float increaseSpeed, float decreaseSpeed, float *val )
|
|
{
|
|
if ( *val < target )
|
|
{
|
|
*val += gpGlobals->frametime*increaseSpeed;
|
|
*val = MIN( *val, target );
|
|
}
|
|
else if ( *val > target )
|
|
{
|
|
*val -= gpGlobals->frametime*decreaseSpeed;
|
|
*val = MAX( *val, target );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void UTIL_ApproachTarget( const Vector &target, float increaseSpeed, float decreaseSpeed, Vector *val )
|
|
{
|
|
UTIL_ApproachTarget( target.x, increaseSpeed, decreaseSpeed, &val->x );
|
|
UTIL_ApproachTarget( target.y, increaseSpeed, decreaseSpeed, &val->y );
|
|
UTIL_ApproachTarget( target.z, increaseSpeed, decreaseSpeed, &val->z );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the filename to count map loads in
|
|
//-----------------------------------------------------------------------------
|
|
bool UTIL_GetMapLoadCountFileName( int iController, const char *pszFilePrependName, char *pszBuffer, int iBuflen )
|
|
{
|
|
if ( IsX360() )
|
|
{
|
|
#ifdef _X360
|
|
if ( iController < 0 || iController >= XUSER_MAX_COUNT )
|
|
return false;
|
|
|
|
int iSlot = -1;
|
|
for ( unsigned int k = 0; k < XBX_GetNumGameUsers(); ++ k )
|
|
{
|
|
if ( XBX_GetUserId( k ) == iController )
|
|
{
|
|
iSlot = k;
|
|
if ( XBX_GetUserIsGuest( k ) )
|
|
return false;
|
|
}
|
|
}
|
|
if ( iSlot < 0 )
|
|
return false;
|
|
|
|
DWORD nStorageDevice = XBX_GetStorageDeviceId( iController );
|
|
if ( !XBX_DescribeStorageDevice( nStorageDevice ) )
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#ifdef _X360
|
|
if ( IsX360() )
|
|
{
|
|
XBX_MakeStorageContainerRoot( iController, XBX_USER_SETTINGS_CONTAINER_DRIVE, pszBuffer, iBuflen );
|
|
int nLen = strlen( pszBuffer );
|
|
Q_snprintf( pszBuffer + nLen, iBuflen - nLen, ":/%s", pszFilePrependName );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
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 )
|
|
{
|
|
#ifdef _X360
|
|
// TODO: controller-specific code required
|
|
return;
|
|
#endif
|
|
int iController = -1;
|
|
|
|
if ( !pszCustomKey )
|
|
return;
|
|
|
|
char szFilename[ _MAX_PATH ];
|
|
if ( !UTIL_GetMapLoadCountFileName( iController, 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();
|
|
}
|
|
|
|
#ifdef _X360
|
|
if ( xboxsystem )
|
|
{
|
|
xboxsystem->FinishContainerWrites( iController );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int UTIL_GetMapKeyCount( const char *pszCustomKey )
|
|
{
|
|
#ifdef _X360
|
|
// TODO: controller-specific code required
|
|
return 0;
|
|
#endif
|
|
int iController = -1;
|
|
|
|
if ( !pszCustomKey )
|
|
return 0;
|
|
|
|
char szFilename[ _MAX_PATH ];
|
|
if ( !UTIL_GetMapLoadCountFileName( iController, 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()
|
|
{
|
|
#ifdef _X360
|
|
// TODO: controller-specific code required
|
|
return 0;
|
|
#endif
|
|
int iController = -1;
|
|
|
|
char szFilename[ _MAX_PATH ];
|
|
if ( !UTIL_GetMapLoadCountFileName( iController, MAP_KEY_FILE, szFilename, _MAX_PATH ) )
|
|
return false;
|
|
|
|
return g_pFullFileSystem->FileExists( szFilename, "MOD" );
|
|
}
|
|
|
|
wchar_t *UTIL_GetLocalizedKeyString( const char *command, const char *fmt, const wchar_t *arg1, const wchar_t *arg2, const wchar_t *arg3 )
|
|
{
|
|
static wchar_t useString[4][256];
|
|
static int index = 0;
|
|
|
|
index = index + 1;
|
|
if ( index > 3 )
|
|
index = 0;
|
|
|
|
const char *lowercaseKey = engine->Key_LookupBinding( command );
|
|
if ( !lowercaseKey )
|
|
{
|
|
lowercaseKey = "<NOT BOUND>";
|
|
}
|
|
|
|
char szKey[64];
|
|
V_strncpy( szKey, lowercaseKey, sizeof( szKey ) );
|
|
for ( char *tmp = szKey; *tmp; ++tmp )
|
|
{
|
|
*tmp = toupper( *tmp );
|
|
}
|
|
|
|
wchar_t wszKey[64];
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( szKey, wszKey, sizeof(wszKey) );
|
|
|
|
int argCount = 1;
|
|
if ( arg1 )
|
|
{
|
|
++argCount;
|
|
if ( arg2 )
|
|
{
|
|
++argCount;
|
|
if ( arg3 )
|
|
{
|
|
++argCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_pVGuiLocalize->ConstructString( useString[index], sizeof( useString[index] ), g_pVGuiLocalize->Find( fmt ), argCount, wszKey, arg1, arg2, arg3 );
|
|
return useString[index];
|
|
}
|
|
|
|
void UTIL_GetClientStatusText( char *buffer, int nSize )
|
|
{
|
|
if ( !buffer || nSize==0 ) {return;}
|
|
buffer[0] = 0;
|
|
|
|
#if defined ( CSTRIKE15 )
|
|
extern float g_flReadyToCheckForPCBootInvite;
|
|
bool bStartupFinished = g_flReadyToCheckForPCBootInvite && ( ( Plat_FloatTime() - g_flReadyToCheckForPCBootInvite ) > 1.5f );
|
|
if ( bStartupFinished )
|
|
Q_snprintf( buffer, nSize, "+" );
|
|
if ( nSize <= 2 )
|
|
return;
|
|
|
|
C_CS_PlayerResource *pCSPR = ( C_CS_PlayerResource* )GameResources();
|
|
if (pCSPR)
|
|
{
|
|
Q_snprintf( buffer, nSize, "%sPlayers: ", ( bStartupFinished ? "+" : "" ) );
|
|
for ( int i = 1; i <= MAX_PLAYERS; i++ )
|
|
{
|
|
if ( pCSPR->IsConnected(i) )
|
|
{
|
|
const char *name = pCSPR->GetPlayerName(i);
|
|
if (name && name[0])
|
|
{
|
|
V_strncat( buffer, name, nSize );
|
|
V_strncat( buffer, ", ", nSize );
|
|
}
|
|
}
|
|
}
|
|
buffer[nSize-1]=0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UTIL_ClearTrace( trace_t &trace )
|
|
{
|
|
memset( &trace, 0, sizeof(trace));
|
|
trace.fraction = 1.f;
|
|
trace.fractionleftsolid = 0;
|
|
trace.surface = g_NullSurface;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Also in cdll so linked libs can extern it
|
|
//-----------------------------------------------------------------------------
|
|
bool UTIL_IsDedicatedServer( void )
|
|
{
|
|
return false;
|
|
}
|
|
|