635 lines
14 KiB
C++
635 lines
14 KiB
C++
//=======================================================================
|
|
// Copyright XashXT Group 2008 ©
|
|
// utils.cpp - client game utilities code
|
|
//=======================================================================
|
|
|
|
#include "extdll.h"
|
|
#include "utils.h"
|
|
#include "hud.h"
|
|
|
|
DLL_GLOBAL const Vector g_vecZero = Vector( 0.0f, 0.0f, 0.0f );
|
|
|
|
static const float bytedirs[NUMVERTEXNORMALS][3] =
|
|
{
|
|
#include "anorms.h"
|
|
};
|
|
|
|
#ifdef _DEBUG
|
|
void DBG_AssertFunction( BOOL fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage )
|
|
{
|
|
if( fExpr ) return;
|
|
|
|
char szOut[512];
|
|
if( szMessage != NULL )
|
|
sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage );
|
|
else sprintf( szOut, "ASSERT FAILED:\n %s \n(%s@%d)", szExpr, szFile, szLine );
|
|
HOST_ERROR( szOut );
|
|
}
|
|
#endif // DEBUG
|
|
|
|
|
|
Vector BitsToDir( int bits )
|
|
{
|
|
Vector dir;
|
|
|
|
if( bits < 0 || bits >= NUMVERTEXNORMALS )
|
|
return Vector( 0, 0, 0 );
|
|
|
|
dir.x = bytedirs[bits][0];
|
|
dir.y = bytedirs[bits][1];
|
|
dir.z = bytedirs[bits][2];
|
|
|
|
return dir;
|
|
}
|
|
|
|
// NOTE: modify these functions with caution
|
|
|
|
typedef struct
|
|
{
|
|
char *name;
|
|
byte *buf;
|
|
int size;
|
|
int read;
|
|
BOOL badRead;
|
|
char string[2048]; // using for store strings
|
|
} user_message_t;
|
|
|
|
static user_message_t gMsg;
|
|
|
|
void BEGIN_READ( const char *pszName, int iSize, void *pBuf )
|
|
{
|
|
memset( &gMsg, 0, sizeof( gMsg ));
|
|
|
|
gMsg.size = iSize;
|
|
gMsg.buf = (byte *)pBuf;
|
|
}
|
|
|
|
void END_READ( void )
|
|
{
|
|
if( gMsg.badRead )
|
|
{
|
|
ALERT( at_console, "%s was received with errors\n", gMsg.name );
|
|
}
|
|
}
|
|
|
|
int READ_CHAR( void )
|
|
{
|
|
int c;
|
|
|
|
if( gMsg.read + 1 > gMsg.size )
|
|
{
|
|
gMsg.badRead = true;
|
|
return -1;
|
|
}
|
|
|
|
c = (signed char)gMsg.buf[gMsg.read];
|
|
gMsg.read++;
|
|
|
|
return c;
|
|
}
|
|
|
|
int READ_BYTE( void )
|
|
{
|
|
int c;
|
|
|
|
if( gMsg.read+1 > gMsg.size )
|
|
{
|
|
gMsg.badRead = true;
|
|
return -1;
|
|
}
|
|
|
|
c = (unsigned char)gMsg.buf[gMsg.read];
|
|
gMsg.read++;
|
|
|
|
return c;
|
|
}
|
|
|
|
int READ_SHORT( void )
|
|
{
|
|
int c;
|
|
|
|
if( gMsg.read + 2 > gMsg.size )
|
|
{
|
|
gMsg.badRead = true;
|
|
return -1;
|
|
}
|
|
|
|
c = (short)( gMsg.buf[gMsg.read] + ( gMsg.buf[gMsg.read+1] << 8 ));
|
|
gMsg.read += 2;
|
|
|
|
return c;
|
|
}
|
|
|
|
int READ_WORD( void ) { return READ_SHORT(); }
|
|
|
|
int READ_LONG( void )
|
|
{
|
|
int c;
|
|
|
|
if( gMsg.read + 4 > gMsg.size )
|
|
{
|
|
gMsg.badRead = true;
|
|
return -1;
|
|
}
|
|
c = gMsg.buf[gMsg.read]+(gMsg.buf[gMsg.read+1]<<8)+(gMsg.buf[gMsg.read+2]<<16)+(gMsg.buf[gMsg.read+3]<<24);
|
|
gMsg.read += 4;
|
|
|
|
return c;
|
|
}
|
|
|
|
float READ_FLOAT( void )
|
|
{
|
|
union { float f; int l; } dat;
|
|
|
|
dat.l = READ_LONG();
|
|
return dat.f;
|
|
}
|
|
|
|
char* READ_STRING( void )
|
|
{
|
|
int l, c;
|
|
|
|
gMsg.string[0] = 0;
|
|
|
|
l = 0;
|
|
do
|
|
{
|
|
if( gMsg.read+1 > gMsg.size ) break; // no more characters
|
|
|
|
c = READ_CHAR();
|
|
if( c == -1 || c == '\0' )
|
|
break;
|
|
|
|
// translate all fmt spec to avoid crash bugs
|
|
if( c == '%' ) c = '.';
|
|
|
|
gMsg.string[l] = c;
|
|
l++;
|
|
} while( l < sizeof( gMsg.string ) - 1 );
|
|
|
|
gMsg.string[l] = 0; // terminator
|
|
|
|
return gMsg.string;
|
|
}
|
|
|
|
//
|
|
// Xash3D network specs. Don't modify!
|
|
//
|
|
float READ_COORD( void )
|
|
{
|
|
return READ_FLOAT();
|
|
}
|
|
|
|
float READ_ANGLE( void )
|
|
{
|
|
return (float)(READ_SHORT() * (360.0 / 65536));
|
|
}
|
|
|
|
Vector READ_DIR( void )
|
|
{
|
|
return BitsToDir( READ_BYTE() );
|
|
}
|
|
|
|
// Overloaded to add IGNORE_GLASS
|
|
void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, IGNORE_GLASS ignoreGlass, edict_t *pentIgnore, TraceResult *ptr )
|
|
{
|
|
TRACE_LINE( vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE) | (ignoreGlass?0x100:0), pentIgnore, ptr );
|
|
}
|
|
|
|
|
|
void UTIL_TraceLine( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, edict_t *pentIgnore, TraceResult *ptr )
|
|
{
|
|
TRACE_LINE( vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), pentIgnore, ptr );
|
|
}
|
|
|
|
|
|
void UTIL_TraceHull( const Vector &vecStart, const Vector &vecEnd, IGNORE_MONSTERS igmon, int hullNumber, edict_t *pentIgnore, TraceResult *ptr )
|
|
{
|
|
TRACE_HULL( vecStart, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), hullNumber, pentIgnore, ptr );
|
|
}
|
|
|
|
void UTIL_TraceModel( const Vector &vecStart, const Vector &vecEnd, int hullNumber, edict_t *pentModel, TraceResult *ptr )
|
|
{
|
|
g_engfuncs.pfnTraceModel( vecStart, vecEnd, pentModel, ptr );
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
DRAW HELPERS
|
|
|
|
==============================================================================
|
|
*/
|
|
void DrawImageBar( float percent, const char *szSpriteName )
|
|
{
|
|
int m_loading = gHUD.GetSpriteIndex( szSpriteName );
|
|
wrect_t rcSize = gHUD.GetSpriteRect( m_loading );
|
|
|
|
int w = rcSize.right - rcSize.left;
|
|
int h = rcSize.bottom - rcSize.top;
|
|
DrawImageBar( percent, szSpriteName, (ScreenWidth - w)/2, (ScreenHeight - h)/2 );
|
|
}
|
|
|
|
void DrawImageBar( float percent, const char *szSpriteName, int x, int y )
|
|
{
|
|
int m_loading = gHUD.GetSpriteIndex( szSpriteName );
|
|
HSPRITE hLoading = gHUD.GetSprite( m_loading );
|
|
wrect_t rcBar, rcBack;
|
|
float step;
|
|
|
|
rcBar = rcBack = gHUD.GetSpriteRect( m_loading );
|
|
step = (float)(rcBack.right - rcBack.left) / 100;
|
|
rcBar.right = rcBar.left + (int)ceil(percent * step);
|
|
|
|
SPR_Set( hLoading, 128, 128, 128 );
|
|
SPR_DrawAdditive( 0, x, y, &rcBack ); // background
|
|
|
|
SPR_Set( hLoading, 255, 160, 0 );
|
|
SPR_DrawAdditive( 0, x, y, &rcBar ); // progress bar
|
|
}
|
|
|
|
void AngleMatrix( const vec3_t angles, float (*matrix)[4] )
|
|
{
|
|
float angle;
|
|
float sr, sp, sy, cr, cp, cy;
|
|
|
|
angle = angles[YAW] * (M_PI*2 / 360);
|
|
sy = sin( angle );
|
|
cy = cos( angle );
|
|
angle = angles[PITCH] * (M_PI*2 / 360);
|
|
sp = sin( angle );
|
|
cp = cos( angle );
|
|
angle = angles[ROLL] * (M_PI*2 / 360);
|
|
sr = sin( angle );
|
|
cr = cos( angle );
|
|
|
|
// matrix = (YAW * PITCH) * ROLL
|
|
matrix[0][0] = cp*cy;
|
|
matrix[1][0] = cp*sy;
|
|
matrix[2][0] = -sp;
|
|
matrix[0][1] = sr*sp*cy+cr*-sy;
|
|
matrix[1][1] = sr*sp*sy+cr*cy;
|
|
matrix[2][1] = sr*cp;
|
|
matrix[0][2] = (cr*sp*cy+-sr*-sy);
|
|
matrix[1][2] = (cr*sp*sy+-sr*cy);
|
|
matrix[2][2] = cr*cp;
|
|
matrix[0][3] = 0.0;
|
|
matrix[1][3] = 0.0;
|
|
matrix[2][3] = 0.0;
|
|
}
|
|
|
|
//
|
|
// hl2 fade - this supports multiple fading
|
|
// FIXME: make Class CHudFade instead of C-style code?
|
|
//
|
|
void SetScreenFade( Vector fadeColor, float alpha, float duration, float holdTime, int fadeFlags )
|
|
{
|
|
CL_ScreenFade *sf = NULL;
|
|
|
|
for( int i = 0; i < HUD_MAX_FADES; i++ )
|
|
{
|
|
// search for free spot
|
|
if( gHUD.m_FadeList[i].fadeFlags == 0 )
|
|
{
|
|
sf = &gHUD.m_FadeList[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !sf ) return; // no free fades found
|
|
|
|
sf->fadeEnd = duration;
|
|
sf->fadeReset = holdTime;
|
|
sf->fadeColor = fadeColor;
|
|
sf->fadeAlpha = alpha;
|
|
sf->fadeFlags = fadeFlags;
|
|
sf->fadeSpeed = 0;
|
|
|
|
// calc fade speed
|
|
if( duration > 0 )
|
|
{
|
|
if( fadeFlags & FFADE_OUT )
|
|
{
|
|
if( sf->fadeEnd )
|
|
{
|
|
sf->fadeSpeed = -(float)sf->fadeAlpha / sf->fadeEnd;
|
|
}
|
|
|
|
sf->fadeEnd += gHUD.m_flTime;
|
|
sf->fadeReset += sf->fadeEnd;
|
|
}
|
|
else
|
|
{
|
|
if( sf->fadeEnd )
|
|
{
|
|
sf->fadeSpeed = (float)sf->fadeAlpha / sf->fadeEnd;
|
|
}
|
|
|
|
sf->fadeReset += gHUD.m_flTime;
|
|
sf->fadeEnd += sf->fadeReset;
|
|
}
|
|
}
|
|
|
|
if( fadeFlags & FFADE_PURGE )
|
|
{
|
|
ClearAllFades();
|
|
}
|
|
}
|
|
|
|
void DrawScreenFade( void )
|
|
{
|
|
int i, numFades = 0;
|
|
|
|
// Cycle through all fades and remove any that have finished (work backwards)
|
|
for( i = HUD_MAX_FADES - 1; i >= 0; i-- )
|
|
{
|
|
CL_ScreenFade *pFade = &gHUD.m_FadeList[i];
|
|
|
|
if( pFade->fadeFlags == 0 ) continue; // free slot
|
|
|
|
// Keep pushing reset time out indefinitely
|
|
if( pFade->fadeFlags & FFADE_STAYOUT )
|
|
{
|
|
pFade->fadeReset = gHUD.m_flTime + 0.1f;
|
|
}
|
|
|
|
// All done?
|
|
if(( gHUD.m_flTime > pFade->fadeReset ) && ( gHUD.m_flTime > pFade->fadeEnd ))
|
|
{
|
|
// remove this Fade from the list
|
|
memset( pFade, 0, sizeof( CL_ScreenFade ));
|
|
}
|
|
}
|
|
|
|
gHUD.m_bModulate = false;
|
|
gHUD.m_vecFadeColor = Vector( 0, 0, 0 );
|
|
gHUD.m_flFadeAlpha = 0.0f;
|
|
|
|
// Cycle through all fades in the list and calculate the overall color/alpha
|
|
for ( i = 0; i < HUD_MAX_FADES; i++ )
|
|
{
|
|
CL_ScreenFade *pFade = &gHUD.m_FadeList[i];
|
|
|
|
if( pFade->fadeFlags == 0 ) continue; // free slot
|
|
|
|
// Color
|
|
gHUD.m_vecFadeColor += pFade->fadeColor;
|
|
|
|
// Fading...
|
|
int iFadeAlpha;
|
|
if( pFade->fadeFlags & ( FFADE_OUT|FFADE_IN ))
|
|
{
|
|
iFadeAlpha = pFade->fadeSpeed * ( pFade->fadeEnd - gHUD.m_flTime );
|
|
if( pFade->fadeFlags & FFADE_OUT )
|
|
{
|
|
iFadeAlpha += pFade->fadeAlpha;
|
|
}
|
|
iFadeAlpha = min( iFadeAlpha, pFade->fadeAlpha );
|
|
iFadeAlpha = max( 0, iFadeAlpha );
|
|
}
|
|
else
|
|
{
|
|
iFadeAlpha = pFade->fadeAlpha;
|
|
}
|
|
|
|
// Use highest alpha
|
|
if( iFadeAlpha > gHUD.m_flFadeAlpha )
|
|
{
|
|
gHUD.m_flFadeAlpha = iFadeAlpha;
|
|
}
|
|
|
|
// Modulate?
|
|
if( pFade->fadeFlags & FFADE_MODULATE )
|
|
{
|
|
gHUD.m_bModulate = true;
|
|
}
|
|
numFades++;
|
|
}
|
|
|
|
// divide colors
|
|
if( numFades ) gHUD.m_vecFadeColor /= numFades;
|
|
else return;
|
|
|
|
const float *RGB = gHUD.m_vecFadeColor;
|
|
FillRGBA( 0, 0, ScreenWidth, ScreenHeight, RGB[0], RGB[1], RGB[2], gHUD.m_flFadeAlpha );
|
|
}
|
|
|
|
void ClearPermanentFades( void )
|
|
{
|
|
for( int i = HUD_MAX_FADES - 1; i >= 0; i-- )
|
|
{
|
|
CL_ScreenFade *pFade = &gHUD.m_FadeList[i];
|
|
if( pFade->fadeFlags == 0 ) continue; // free slot
|
|
|
|
if( pFade->fadeFlags & FFADE_STAYOUT )
|
|
{
|
|
// remove this Fade from the list
|
|
memset( pFade, 0, sizeof( CL_ScreenFade ));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClearAllFades( void )
|
|
{
|
|
memset( gHUD.m_FadeList, 0, sizeof( gHUD.m_FadeList ));
|
|
}
|
|
|
|
/*
|
|
====================
|
|
RotatePointAroundVector
|
|
====================
|
|
*/
|
|
void RotatePointAroundVector( Vector &dst, const Vector &dir, const Vector &point, float degrees )
|
|
{
|
|
float t0, t1;
|
|
float angle, c, s, d;
|
|
Vector vr, vu, vf;
|
|
|
|
angle = DEG2RAD( degrees );
|
|
s = sin( angle );
|
|
c = cos( angle );
|
|
|
|
vf.Init( dir.x, dir.y, dir.z );
|
|
vr.Init( vf.z, -vf.x, vf.y );
|
|
|
|
// find a three vectors
|
|
d = DotProduct( vf, vr );
|
|
vr = (vr + ( vf * -d )).Normalize();
|
|
vu = CrossProduct( vr, vf );
|
|
|
|
t0 = vr[0] * c + vu[0] * -s;
|
|
t1 = vr[0] * s + vu[0] * c;
|
|
dst[0] = (t0 * vr[0] + t1 * vu[0] + vf[0] * vf[0]) * point[0]
|
|
+ (t0 * vr[1] + t1 * vu[1] + vf[0] * vf[1]) * point[1]
|
|
+ (t0 * vr[2] + t1 * vu[2] + vf[0] * vf[2]) * point[2];
|
|
|
|
t0 = vr[1] * c + vu[1] * -s;
|
|
t1 = vr[1] * s + vu[1] * c;
|
|
dst[1] = (t0 * vr[0] + t1 * vu[0] + vf[1] * vf[0]) * point[0]
|
|
+ (t0 * vr[1] + t1 * vu[1] + vf[1] * vf[1]) * point[1]
|
|
+ (t0 * vr[2] + t1 * vu[2] + vf[1] * vf[2]) * point[2];
|
|
|
|
t0 = vr[2] * c + vu[2] * -s;
|
|
t1 = vr[2] * s + vu[2] * c;
|
|
dst[2] = (t0 * vr[0] + t1 * vu[0] + vf[2] * vf[0]) * point[0]
|
|
+ (t0 * vr[1] + t1 * vu[1] + vf[2] * vf[1]) * point[1]
|
|
+ (t0 * vr[2] + t1 * vu[2] + vf[2] * vf[2]) * point[2];
|
|
}
|
|
|
|
/*
|
|
====================
|
|
UTIL_Probe
|
|
|
|
client explosion utility
|
|
====================
|
|
*/
|
|
float UTIL_Probe( const Vector &origin, Vector *vecDirection, float strength )
|
|
{
|
|
// press out
|
|
Vector endpos = origin + (( *vecDirection ) * strength );
|
|
|
|
//Trace into the world
|
|
TraceResult tr;
|
|
UTIL_TraceLine( origin, endpos, dont_ignore_monsters, NULL, &tr );
|
|
|
|
// push back a proportional amount to the probe
|
|
(*vecDirection) = -(*vecDirection) * (1.0f - tr.flFraction);
|
|
|
|
ASSERT(( 1.0f - tr.flFraction ) >= 0.0f );
|
|
|
|
// return the impacted proportion of the probe
|
|
return (1.0f - tr.flFraction);
|
|
}
|
|
|
|
void UTIL_GetForceDirection( const Vector &origin, float magnitude, Vector *resultDirection, float *resultForce )
|
|
{
|
|
Vector d[6];
|
|
|
|
// all cardinal directions
|
|
d[0] = Vector( 1, 0, 0 );
|
|
d[1] = Vector( -1, 0, 0 );
|
|
d[2] = Vector( 0, 1, 0 );
|
|
d[3] = Vector( 0, -1, 0 );
|
|
d[4] = Vector( 0, 0, 1 );
|
|
d[5] = Vector( 0, 0, -1 );
|
|
|
|
//Init the results
|
|
(*resultDirection).Init();
|
|
(*resultForce) = 1.0f;
|
|
|
|
// Get the aggregate force vector
|
|
for ( int i = 0; i < 6; i++ )
|
|
{
|
|
(*resultForce) += UTIL_Probe( origin, &d[i], magnitude );
|
|
(*resultDirection) += d[i];
|
|
}
|
|
|
|
// If we've hit nothing, then point up
|
|
if (( *resultDirection ) == g_vecZero )
|
|
{
|
|
(*resultDirection) = Vector( 0, 0, 1 );
|
|
(*resultForce) = 2.0f; // default value
|
|
}
|
|
|
|
// Just return the direction
|
|
(*resultDirection) = (*resultDirection).Normalize();
|
|
}
|
|
|
|
/*
|
|
====================
|
|
Sys LoadGameDLL
|
|
|
|
some code from Darkplaces
|
|
====================
|
|
*/
|
|
BOOL Sys_LoadLibrary( const char* dllname, dllhandle_t* handle, const dllfunction_t *fcts )
|
|
{
|
|
const dllfunction_t *gamefunc;
|
|
dllhandle_t dllhandle = 0;
|
|
|
|
if( handle == NULL ) return false;
|
|
|
|
// Initializations
|
|
for( gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++ )
|
|
*gamefunc->funcvariable = NULL;
|
|
|
|
dllhandle = (dllhandle_t)LOAD_LIBRARY( dllname );
|
|
|
|
// No DLL found
|
|
if( !dllhandle ) return false;
|
|
|
|
// Get the function adresses
|
|
for( gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++ )
|
|
{
|
|
if(!( *gamefunc->funcvariable = (void *) Sys_GetProcAddress( dllhandle, gamefunc->name )))
|
|
{
|
|
Sys_UnloadLibrary( &dllhandle );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ALERT( at_loading, "%s loaded succesfully!\n", dllname );
|
|
*handle = dllhandle;
|
|
return true;
|
|
}
|
|
|
|
void Sys_UnloadLibrary( dllhandle_t *handle )
|
|
{
|
|
if( handle == NULL || *handle == NULL )
|
|
return;
|
|
|
|
FREE_LIBRARY( *handle );
|
|
*handle = NULL;
|
|
}
|
|
|
|
void* Sys_GetProcAddress( dllhandle_t handle, const char* name )
|
|
{
|
|
return (void *)GET_PROC_ADDRESS( handle, name );
|
|
}
|
|
|
|
//============
|
|
// UTIL_FileExtension
|
|
// returns file extension
|
|
//============
|
|
const char *UTIL_FileExtension( const char *in )
|
|
{
|
|
const char *separator, *backslash, *colon, *dot;
|
|
|
|
separator = strrchr( in, '/' );
|
|
backslash = strrchr( in, '\\' );
|
|
if( !separator || separator < backslash )
|
|
separator = backslash;
|
|
colon = strrchr( in, ':' );
|
|
if( !separator || separator < colon )
|
|
separator = colon;
|
|
dot = strrchr( in, '.' );
|
|
if( dot == NULL || (separator && ( dot < separator )))
|
|
return "";
|
|
return dot + 1;
|
|
}
|
|
|
|
HSPRITE LoadSprite( const char *pszName )
|
|
{
|
|
int i;
|
|
char sz[256];
|
|
|
|
if ( ActualWidth < 640 )
|
|
i = 320;
|
|
else
|
|
i = 640;
|
|
|
|
sprintf( sz, pszName, i );
|
|
|
|
return SPR_Load( sz );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
VGui_ConsolePrint
|
|
|
|
VGUI not implemented, wait for version 0.75
|
|
====================
|
|
*/
|
|
void VGui_ConsolePrint( const char *text )
|
|
{
|
|
} |