This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/client/global/utils.cpp

902 lines
19 KiB
C++

//=======================================================================
// Copyright XashXT Group 2008 ©
// utils.cpp - client game utilities code
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "hud.h"
static const float bytedirs[NUMVERTEXNORMALS][3] =
{
#include "anorms.h"
};
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() );
}
//
// Sprites draw stuff
//
typedef struct
{
// temp handle
HSPRITE hSprite;
HSPRITE hPause; // pause pic
// crosshair members
HSPRITE hCrosshair;
wrect_t rcCrosshair;
byte rgbCrosshair[3];
bool noCrosshair;
} draw_stuff_t;
static draw_stuff_t ds;
int SPR_Frames( HSPRITE hPic )
{
int Frames;
GetParms( NULL, NULL, &Frames, 0, hPic );
return Frames;
}
int SPR_Height( HSPRITE hPic, int frame )
{
int Height;
GetParms( NULL, &Height, NULL, frame, hPic );
return Height;
}
int SPR_Width( HSPRITE hPic, int frame )
{
int Width;
GetParms( &Width, NULL, NULL, frame, hPic );
return Width;
}
void Draw_VidInit( void )
{
memset( &ds, 0, sizeof( ds ));
}
/*
====================
SPRITE_GetList
====================
*/
void ParseHudSprite( const char **pfile, char *psz, client_sprite_t *result )
{
int x = 0, y = 0, width = 0, height = 0;
client_sprite_t p;
int section = 0;
char *token;
memset( &p, 0, sizeof( client_sprite_t ));
while(( token = COM_ParseToken( pfile )) != NULL )
{
if( !stricmp( token, psz ))
{
token = COM_ParseToken( pfile );
if( !stricmp( token, "{" )) section = 1;
}
if( section ) // parse section
{
if( !stricmp( token, "}" )) break; // end section
if( !stricmp( token, "file" ))
{
token = COM_ParseToken( pfile );
strncpy( p.szSprite, token, 64 );
// fill structure at default
p.hSprite = SPR_Load( p.szSprite );
width = SPR_Width( p.hSprite, 0 );
height = SPR_Height( p.hSprite, 0 );
x = y = 0;
}
else if ( !stricmp( token, "name" ))
{
token = COM_ParseToken( pfile );
strncpy( p.szName, token, 64 );
}
else if ( !stricmp( token, "x" ))
{
token = COM_ParseToken( pfile );
x = atoi( token );
}
else if ( !stricmp( token, "y" ))
{
token = COM_ParseToken( pfile );
y = atoi( token );
}
else if ( !stricmp( token, "width" ))
{
token = COM_ParseToken( pfile );
width = atoi( token );
}
else if ( !stricmp( token, "height" ))
{
token = COM_ParseToken( pfile );
height = atoi( token );
}
}
}
if( !section ) return; // data not found
// calculate sprite position
p.rc.left = x;
p.rc.right = x + width;
p.rc.top = y;
p.rc.bottom = y + height;
memcpy( result, &p, sizeof( client_sprite_t ));
}
client_sprite_t *SPR_GetList( const char *psz, int *piCount )
{
char *pfile = (char *)LOAD_FILE( psz, NULL );
int iSprCount = 0;
if( !pfile )
{
*piCount = iSprCount;
return NULL;
}
char *token;
const char *plist = pfile;
int depth = 0;
while(( token = COM_ParseToken( &plist )) != NULL ) // calculate count of sprites
{
if( !stricmp( token, "{" )) depth++;
else if( !stricmp( token, "}" )) depth--;
else if( depth == 0 && !strcmp( token, "hudsprite" ))
iSprCount++;
}
client_sprite_t *phud;
plist = pfile;
phud = new client_sprite_t[iSprCount];
if( depth != 0 ) ALERT( at_console, "%s EOF without closing brace\n", psz );
for( int i = 0; i < iSprCount; i++ ) //parse structures
{
ParseHudSprite( &plist, "hudsprite", &phud[i] );
}
if( !iSprCount ) ALERT( at_console, "SPR_GetList: %s doesn't have sprites\n", psz );
FREE_FILE( pfile );
*piCount = iSprCount;
return phud;
}
void SPR_Set( HSPRITE hPic, int r, int g, int b )
{
ds.hSprite = hPic;
SetColor( r, g, b, 255 );
}
void SPR_Set( HSPRITE hPic, int r, int g, int b, int a )
{
ds.hSprite = hPic;
SetColor( r, g, b, a );
}
inline static void SPR_AdjustSize( float *x, float *y, float *w, float *h )
{
if( !x && !y && !w && !h ) return;
// scale for screen sizes
float xscale = gHUD.m_scrinfo.iRealWidth / (float)gHUD.m_scrinfo.iWidth;
float yscale = gHUD.m_scrinfo.iRealHeight / (float)gHUD.m_scrinfo.iHeight;
if( x ) *x *= xscale;
if( y ) *y *= yscale;
if( w ) *w *= xscale;
if( h ) *h *= yscale;
}
inline static void SPR_DrawChar( HSPRITE hFont, int xpos, int ypos, int width, int height, int ch )
{
float size, frow, fcol;
float ax, ay, aw, ah;
int fontWidth, fontHeight;
ch &= 255;
if( ch == ' ' ) return;
if( ypos < -height ) return;
ax = xpos;
ay = ypos;
aw = width;
ah = height;
SPR_AdjustSize( &ax, &ay, &aw, &ah );
GetParms( &fontWidth, &fontHeight, NULL, 0, hFont );
frow = (ch >> 4) * 0.0625f + (0.5f / (float)fontWidth);
fcol = (ch & 15) * 0.0625f + (0.5f / (float)fontHeight);
size = 0.0625f - (1.0f / (float)fontWidth);
DrawImageExt( hFont, ax, ay, aw, ah, fcol, frow, fcol + size, frow + size );
}
inline static void SPR_DrawGeneric( int frame, float x, float y, float width, float height, const wrect_t *prc )
{
float s1, s2, t1, t2;
if( width == -1 && height == -1 )
{
int w, h;
GetParms( &w, &h, NULL, frame, ds.hSprite );
width = w;
height = h;
}
if( prc )
{
// calc rectangle
s1 = (float)prc->left / width;
t1 = (float)prc->top / height;
s2 = (float)prc->right / width;
t2 = (float)prc->bottom / height;
width = prc->right - prc->left;
height = prc->bottom - prc->top;
}
else
{
s1 = t1 = 0.0f;
s2 = t2 = 1.0f;
}
// scale for screen sizes
SPR_AdjustSize( &x, &y, &width, &height );
DrawImageExt( ds.hSprite, x, y, width, height, s1, t1, s2, t2 );
}
void TextMessageDrawChar( int xpos, int ypos, int number, int r, int g, int b )
{
// tune char size by taste
SetColor( r, g, b, 255 );
SPR_DrawChar( gHUD.m_hHudFont, xpos, ypos, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, number );
}
void FillRGBA( float x, float y, float width, float height, int r, int g, int b, int a )
{
SPR_AdjustSize( &x, &y, &width, &height );
g_engfuncs.pfnFillRGBA( x, y, width, height, r, g, b, a );
}
void SPR_Draw( int frame, int x, int y, const wrect_t *prc )
{
SetParms( ds.hSprite, kRenderNormal, frame );
SPR_DrawGeneric( frame, x, y, -1, -1, prc );
}
void SPR_Draw( int frame, int x, int y, int width, int height )
{
SetParms( ds.hSprite, kRenderNormal, frame );
SPR_DrawGeneric( frame, x, y, width, height, NULL );
}
void SPR_DrawTransColor( int frame, int x, int y, const wrect_t *prc )
{
SetParms( ds.hSprite, kRenderTransColor, frame );
SPR_DrawGeneric( frame, x, y, -1, -1, prc );
}
void SPR_DrawTransColor( int frame, int x, int y, int width, int height )
{
SetParms( ds.hSprite, kRenderTransColor, frame );
SPR_DrawGeneric( frame, x, y, width, height, NULL );
}
void SPR_DrawHoles( int frame, int x, int y, const wrect_t *prc )
{
SetParms( ds.hSprite, kRenderTransAlpha, frame );
SPR_DrawGeneric( frame, x, y, -1, -1, prc );
}
void SPR_DrawHoles( int frame, int x, int y, int width, int height )
{
SetParms( ds.hSprite, kRenderTransAlpha, frame );
SPR_DrawGeneric( frame, x, y, width, height, NULL );
}
void SPR_DrawAdditive( int frame, int x, int y, const wrect_t *prc )
{
SetParms( ds.hSprite, kRenderTransAdd, frame );
SPR_DrawGeneric( frame, x, y, -1, -1, prc );
}
void SPR_DrawAdditive( int frame, int x, int y, int width, int height )
{
SetParms( ds.hSprite, kRenderTransAdd, frame );
SPR_DrawGeneric( frame, x, y, width, height, NULL );
}
void SetCrosshair( HSPRITE hspr, wrect_t rc, int r, int g, int b )
{
ds.rgbCrosshair[0] = (byte)r;
ds.rgbCrosshair[1] = (byte)g;
ds.rgbCrosshair[2] = (byte)b;
ds.hCrosshair = hspr;
ds.rcCrosshair = rc;
}
void HideCrosshair( bool hide )
{
ds.noCrosshair = hide;
}
void DrawCrosshair( void )
{
if( ds.hCrosshair == 0 || ds.noCrosshair || !CVAR_GET_FLOAT( "cl_crosshair" ))
return;
int x = (ScreenWidth - (ds.rcCrosshair.right - ds.rcCrosshair.left)) / 2;
int y = (ScreenHeight - (ds.rcCrosshair.bottom - ds.rcCrosshair.top)) / 2;
// FIXME: apply crosshair angles properly
x += gHUD.m_CrosshairAngles.x;
y += gHUD.m_CrosshairAngles.y;
ds.hSprite = ds.hCrosshair;
SetParms( ds.hCrosshair, kRenderTransAlpha, 0 );
SetColor( ds.rgbCrosshair[0], ds.rgbCrosshair[1], ds.rgbCrosshair[2], 255 );
SPR_DrawGeneric( 0, x, y, -1, -1, &ds.rcCrosshair );
}
void DrawPause( void )
{
// pause image
if( !v_paused ) return;
DrawImageBar( 100, "m_pause" ); // HACKHACK
}
void DrawImageRectangle( HSPRITE hImage )
{
DrawImageExt( hImage, 0, 0, ActualWidth, ActualHeight, 0, 0, 1, 1 );
}
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
}
//
// 27/12/08 moved here from cl_view.c
//
void DrawProgressBar( void )
{
if( !gHUD.m_iDrawPlaque ) return;
DrawImageBar( CVAR_GET_FLOAT( "scr_loading" ), "m_loading" );
if( !CVAR_GET_FLOAT( "scr_download" )) return;
DrawImageBar( CVAR_GET_FLOAT( "scr_download" ), "m_download", (ScreenWidth-128)/2, ScreenHeight-60 );
}
//
// 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 )
{
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-- )
{
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( ScreenFade ));
}
}
gHUD.m_bModulate = false;
gHUD.m_vFadeColor = Vector( 0, 0, 0 );
gHUD.m_fFadeAlpha = 0.0f;
// Cycle through all fades in the list and calculate the overall color/alpha
for ( i = 0; i < HUD_MAX_FADES; i++ )
{
ScreenFade *pFade = &gHUD.m_FadeList[i];
if( pFade->fadeFlags == 0 ) continue; // free slot
// Color
gHUD.m_vFadeColor += 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_fFadeAlpha )
{
gHUD.m_fFadeAlpha = iFadeAlpha;
}
// Modulate?
if( pFade->fadeFlags & FFADE_MODULATE )
{
gHUD.m_bModulate = true;
}
numFades++;
}
// Divide colors
if( numFades ) gHUD.m_vFadeColor /= numFades;
if( gHUD.m_vFadeColor == Vector( 0, 0, 0 )) return;
const float *RGB = gHUD.m_vFadeColor;
FillRGBA( 0, 0, ScreenWidth, ScreenHeight, RGB[0], RGB[1], RGB[2], gHUD.m_fFadeAlpha );
}
void ClearPermanentFades( void )
{
for( int i = HUD_MAX_FADES - 1; i >= 0; i-- )
{
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( ScreenFade ));
}
}
}
void ClearAllFades( void )
{
memset( gHUD.m_FadeList, 0, sizeof( gHUD.m_FadeList ));
}
/*
====================
Sys LoadGameDLL
====================
*/
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 );
}
/*
============
va
does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
FIXME: make this buffer size safe someday
============
*/
char *va( const char *format, ... )
{
va_list argptr;
static char string[32][1024], *s;
static int stringindex = 0;
s = string[stringindex];
stringindex = (stringindex + 1) & 31;
va_start( argptr, format );
_vsnprintf( s, sizeof( string[0] ), format, argptr );
va_end( argptr );
return s;
}
/*
==============
COM_ParseToken
Parse a token out of a string
==============
*/
char *COM_ParseToken( const char **data_p )
{
int c;
int len = 0;
const char *data;
static char token[512];
token[0] = 0;
data = *data_p;
if( !data )
{
*data_p = NULL;
return NULL;
}
// skip whitespace
skipwhite:
while(( c = *data) <= ' ' )
{
if( c == 0 )
{
*data_p = NULL;
return NULL; // end of file;
}
data++;
}
// skip // comments
if( c=='/' && data[1] == '/' )
{
while( *data && *data != '\n' )
data++;
goto skipwhite;
}
// skip /* comments
if( c=='/' && data[1] == '*' )
{
while( data[1] && (data[0] != '*' || data[1] != '/' ))
data++;
data += 2;
goto skipwhite;
}
// handle quoted strings specially
if( *data == '\"' || *data == '\'' )
{
data++;
while( 1 )
{
c = *data++;
if( c=='\"' || c=='\0' )
{
token[len] = 0;
*data_p = data;
return token;
}
token[len] = c;
len++;
}
}
// parse single characters
if( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':' || c == ',' )
{
token[len] = c;
data++;
len++;
token[len] = 0;
*data_p = data;
return token;
}
// parse a regular word
do
{
token[len] = c;
data++;
len++;
c = *data;
if( c == '{' || c == '}'|| c == ')'|| c == '(' || c == '\'' || c == ':' || c == ',' )
break;
} while( c > 32 );
token[len] = 0;
*data_p = data;
return token;
}