3075 lines
82 KiB
C++
3075 lines
82 KiB
C++
//=======================================================================
|
|
// Copyright (C) Shambler Team 2005
|
|
// utils.cpp - Utility code.
|
|
// Really not optional after all.
|
|
//=======================================================================
|
|
|
|
#include "extdll.h"
|
|
#include "utils.h"
|
|
#include "cbase.h"
|
|
#include "defaults.h"
|
|
#include "saverestore.h"
|
|
#include <time.h>
|
|
#include "shake.h"
|
|
#include "decals.h"
|
|
#include "player.h"
|
|
#include "baseweapon.h"
|
|
#include "gamerules.h"
|
|
#include "client.h"
|
|
|
|
FILE_GLOBAL char st_szNextMap[MAP_MAX_NAME];
|
|
FILE_GLOBAL char st_szNextSpot[MAP_MAX_NAME];
|
|
extern DLL_GLOBAL BOOL NewLevel;
|
|
int giAmmoIndex = 0;
|
|
BOOL CanAffect;
|
|
|
|
//=========================================================
|
|
// COM_Functions (text files parsing)
|
|
//=========================================================
|
|
#define COM_Format(x) va_list szCommand; \
|
|
static char value[1024]; \
|
|
va_start( szCommand, x ); \
|
|
vsprintf( value, x, szCommand ); \
|
|
va_end( szCommand );
|
|
|
|
void Msg( char *message, ... )
|
|
{
|
|
COM_Format( message );
|
|
g_engfuncs.pfnAlertMessage( at_console, "%s", value );
|
|
}
|
|
|
|
void DevMsg( char *message, ... )
|
|
{
|
|
COM_Format( message );
|
|
g_engfuncs.pfnAlertMessage( at_aiconsole, "%s", value );
|
|
}
|
|
|
|
struct
|
|
{
|
|
char token[1024];
|
|
int length;
|
|
int symbol;
|
|
} parse;
|
|
|
|
char *COM_Parse( char *data )
|
|
{
|
|
parse.length = 0;
|
|
parse.token[0] = 0;
|
|
|
|
if( !data ) return NULL;
|
|
skip:
|
|
while((parse.symbol = *data) <= ' ')
|
|
{
|
|
if( parse.symbol == 0 ) return NULL;
|
|
data++;
|
|
}
|
|
if( parse.symbol == '/' && data[1] == '/' )
|
|
{
|
|
while( *data && *data != '\n' ) data++;
|
|
goto skip;
|
|
}
|
|
if( parse.symbol == '\"' )
|
|
{
|
|
data++;
|
|
while(true)
|
|
{
|
|
parse.symbol = *data++;
|
|
if( parse.symbol == '\"' || !parse.symbol )
|
|
{
|
|
parse.token[parse.length] = 0;
|
|
return data;
|
|
}
|
|
parse.token[parse.length] = parse.symbol;
|
|
parse.length++;
|
|
}
|
|
}
|
|
|
|
if( parse.symbol == '{' || parse.symbol == '}' || parse.symbol == ')' || parse.symbol == '(' || parse.symbol == '\'' || parse.symbol == ',' )
|
|
{
|
|
parse.token[parse.length] = parse.symbol;
|
|
parse.length++;
|
|
parse.token[parse.length] = 0;
|
|
return data+1;
|
|
}
|
|
do
|
|
{
|
|
parse.token[parse.length] = parse.symbol;
|
|
data++;
|
|
parse.length++;
|
|
parse.symbol = *data;
|
|
if( parse.symbol == '{' || parse.symbol == '}' || parse.symbol == ')' || parse.symbol == '(' || parse.symbol == '\'' || parse.symbol == ',' ) break;
|
|
} while( parse.symbol > 32 );
|
|
|
|
parse.token[parse.length] = 0;
|
|
return data;
|
|
}
|
|
|
|
char *COM_ParseFile( char *data, char *token )
|
|
{
|
|
char *return_data = COM_Parse( data );
|
|
strcpy( token, parse.token );
|
|
return return_data;
|
|
}
|
|
|
|
void COM_FreeFile( char *buffer )
|
|
{
|
|
if( buffer ) FREE_FILE( buffer );
|
|
}
|
|
|
|
//=========================================================
|
|
// check for null ents
|
|
//=========================================================
|
|
|
|
inline BOOL FNullEnt(EOFFSET eoffset) { return eoffset == 0; }
|
|
inline BOOL FNullEnt(const edict_t* pent) { return pent == NULL || FNullEnt(OFFSET(pent)); }
|
|
inline BOOL FNullEnt(entvars_t* pev) { return pev == NULL || FNullEnt(OFFSET(pev)); }
|
|
inline BOOL FNullEnt( CBaseEntity *ent ) { return ent == NULL || FNullEnt( ent->edict()); }
|
|
|
|
//=========================================================
|
|
// calculate brush model origin
|
|
//=========================================================
|
|
Vector VecBModelOrigin( entvars_t* pevBModel )
|
|
{
|
|
return (pevBModel->absmin + pevBModel->absmax) * 0.5;
|
|
}
|
|
|
|
BOOL FClassnameIs(CBaseEntity *pEnt, const char* szClassname)
|
|
{
|
|
return FStrEq(STRING(pEnt->pev->classname), szClassname);
|
|
}
|
|
|
|
//========================================================================
|
|
// UTIL_FireTargets - supported prefix "+", "-", "!", ">", "<", "?".
|
|
// supported also this and self pointers - e.g. "fadein(mywall)"
|
|
//========================================================================
|
|
void UTIL_FireTargets( int targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
|
{
|
|
//overload version UTIL_FireTargets
|
|
if (!targetName) return;//no execute code, if target blank
|
|
|
|
UTIL_FireTargets( STRING(targetName), pActivator, pCaller, useType, value);
|
|
}
|
|
|
|
void UTIL_FireTargets( const char *targetName, CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
|
|
{
|
|
|
|
const char *inputTargetName = targetName;
|
|
CBaseEntity *inputActivator = pActivator;
|
|
CBaseEntity *pTarget = NULL;
|
|
int i,j, found = false;
|
|
char szBuf[80];
|
|
|
|
if ( !targetName )return;
|
|
|
|
//HACKHACK
|
|
if(FStrEq(targetName, "tr_endchange" ))
|
|
{
|
|
SERVER_COMMAND("game_over\n");
|
|
return;
|
|
}
|
|
|
|
if (targetName[0] == '+')
|
|
{
|
|
targetName++;
|
|
useType = USE_ON;
|
|
}
|
|
else if (targetName[0] == '-')
|
|
{
|
|
targetName++;
|
|
useType = USE_OFF;
|
|
}
|
|
else if (targetName[0] == '!')
|
|
{
|
|
targetName++;
|
|
useType = USE_REMOVE;
|
|
}
|
|
else if (targetName[0] == '<')
|
|
{
|
|
targetName++;
|
|
useType = USE_SET;
|
|
}
|
|
else if (targetName[0] == '>')
|
|
{
|
|
targetName++;
|
|
useType = USE_RESET;
|
|
}
|
|
else if (targetName[0] == '?')
|
|
{
|
|
targetName++;
|
|
useType = USE_SHOWINFO;
|
|
}
|
|
|
|
pTarget = UTIL_FindEntityByTargetname(pTarget, targetName, pActivator);
|
|
|
|
if( !pTarget )//smart field name ?
|
|
{
|
|
//try to extract value from name (it's more usefully than "locus" specifier)
|
|
for (i = 0; targetName[i]; i++)
|
|
{
|
|
if (targetName[i] == '.')//value specifier
|
|
{
|
|
value = atof(&targetName[i+1]);
|
|
sprintf(szBuf, targetName);
|
|
szBuf[i] = 0;
|
|
targetName = szBuf;
|
|
pTarget = UTIL_FindEntityByTargetname(NULL, targetName, inputActivator);
|
|
break;
|
|
}
|
|
}
|
|
if( !pTarget )//try to extract activator specified
|
|
{
|
|
for (i = 0; targetName[i]; i++)
|
|
{
|
|
if (targetName[i] == '(')
|
|
{
|
|
i++;
|
|
for (j = i; targetName[j]; j++)
|
|
{
|
|
if (targetName[j] == ')')
|
|
{
|
|
strncpy(szBuf, targetName+i, j-i);
|
|
szBuf[j-i] = 0;
|
|
pActivator = UTIL_FindEntityByTargetname(NULL, szBuf, inputActivator);
|
|
if (!pActivator) return; //it's a locus specifier, but the locus is invalid.
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) ALERT(at_error, "Missing ')' in targetname: %s", inputTargetName);
|
|
break;
|
|
}
|
|
}
|
|
if (!found) return; // no, it's not a locus specifier.
|
|
|
|
strncpy(szBuf, targetName, i-1);
|
|
szBuf[i-1] = 0;
|
|
targetName = szBuf;
|
|
pTarget = UTIL_FindEntityByTargetname(NULL, targetName, inputActivator);
|
|
|
|
if (!pTarget)return; // it's a locus specifier all right, but the target's invalid.
|
|
}
|
|
}
|
|
|
|
DevMsg( "Firing: (%s) with %s and value %g\n", targetName, GetStringForUseType( useType ), value );
|
|
|
|
do //start firing targets
|
|
{
|
|
if ( !(pTarget->pev->flags & FL_KILLME) ) // Don't use dying ents
|
|
{
|
|
if (useType == USE_REMOVE) UTIL_Remove( pTarget );
|
|
else pTarget->Use( pActivator, pCaller, useType, value );
|
|
}
|
|
pTarget = UTIL_FindEntityByTargetname(pTarget, targetName, inputActivator);
|
|
|
|
} while (pTarget);
|
|
}
|
|
|
|
//=========================================================
|
|
// UTIL_StripToken - for redundant keynames
|
|
//=========================================================
|
|
void UTIL_StripToken( const char *pKey, char *pDest )
|
|
{
|
|
int i = 0;
|
|
|
|
while ( pKey[i] && pKey[i] != '#' )
|
|
{
|
|
pDest[i] = pKey[i];
|
|
i++;
|
|
}
|
|
pDest[i] = 0;
|
|
}
|
|
|
|
|
|
char* GetStringForUseType( USE_TYPE useType )
|
|
{
|
|
switch(useType)
|
|
{
|
|
case USE_ON: return "USE_ON";
|
|
case USE_OFF: return "USE_OFF";
|
|
case USE_TOGGLE: return "USE_TOGGLE";
|
|
case USE_REMOVE: return "USE_REMOVE";
|
|
case USE_SET: return "USE_SET";
|
|
case USE_RESET: return "USE_RESET";
|
|
case USE_SHOWINFO: return "USE_SHOWINFO";
|
|
default:
|
|
return "UNKNOWN USE_TYPE!";
|
|
}
|
|
}
|
|
|
|
char* GetStringForState( STATE state )
|
|
{
|
|
switch(state)
|
|
{
|
|
case STATE_ON: return "ON";
|
|
case STATE_OFF: return "OFF";
|
|
case STATE_TURN_ON: return "TURN ON";
|
|
case STATE_TURN_OFF: return "TURN OFF";
|
|
case STATE_IN_USE: return "IN USE";
|
|
case STATE_DEAD: return "DEAD";
|
|
default:
|
|
return "UNKNOWN STATE!";
|
|
}
|
|
}
|
|
|
|
char* GetStringForDecalName( int decalname )
|
|
{
|
|
char *name = "";
|
|
int m_decal = UTIL_LoadDecalPreset( decalname );
|
|
switch(m_decal)
|
|
{
|
|
case 1: name = gDecals[ DECAL_GUNSHOT1 + RANDOM_LONG(0,4)].name; break;
|
|
case 2: name = gDecals[ DECAL_BLOOD1 + RANDOM_LONG(0,5)].name; break;
|
|
case 3: name = gDecals[ DECAL_YBLOOD1 + RANDOM_LONG(0,5)].name; break;
|
|
case 4: name = gDecals[ DECAL_GLASSBREAK1 + RANDOM_LONG(0,2)].name; break;
|
|
case 5: name = gDecals[ DECAL_BIGSHOT1 + RANDOM_LONG(0,4)].name; break;
|
|
case 6: name = gDecals[ DECAL_SCORCH1 + RANDOM_LONG(0,1)].name; break;
|
|
case 7: name = gDecals[ DECAL_SPIT1 + RANDOM_LONG(0,1)].name; break;
|
|
default: name = (char *)STRING( decalname ); break;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
void PrintStringForDamage( int dmgbits )
|
|
{
|
|
char szDmgBits[256];
|
|
strcat(szDmgBits, "DAMAGE: " );
|
|
if( dmgbits & DMG_GENERIC) strcat(szDmgBits, "Generic " );
|
|
if( dmgbits & DMG_CRUSH) strcat(szDmgBits, "Crush " );
|
|
if( dmgbits & DMG_BULLET) strcat(szDmgBits, "Bullet " );
|
|
if( dmgbits & DMG_SLASH) strcat(szDmgBits, "Slash " );
|
|
if( dmgbits & DMG_BURN) strcat(szDmgBits, "Burn " );
|
|
if( dmgbits & DMG_FREEZE) strcat(szDmgBits, "Freeze " );
|
|
if( dmgbits & DMG_FALL) strcat(szDmgBits, "Fall " );
|
|
if( dmgbits & DMG_BLAST) strcat(szDmgBits, "Blast " );
|
|
if( dmgbits & DMG_CLUB) strcat(szDmgBits, "Club " );
|
|
if( dmgbits & DMG_SHOCK) strcat(szDmgBits, "Shock " );
|
|
if( dmgbits & DMG_SONIC) strcat(szDmgBits, "Sonic " );
|
|
if( dmgbits & DMG_ENERGYBEAM) strcat(szDmgBits, "Energy Beam " );
|
|
if( dmgbits & DMG_NEVERGIB) strcat(szDmgBits, "Never Gib " );
|
|
if( dmgbits & DMG_ALWAYSGIB) strcat(szDmgBits, "Always Gib " );
|
|
if( dmgbits & DMG_DROWN) strcat(szDmgBits, "Drown " );
|
|
if( dmgbits & DMG_PARALYZE) strcat(szDmgBits, "Paralyze Gas " );
|
|
if( dmgbits & DMG_NERVEGAS) strcat(szDmgBits, "Nerve Gas " );
|
|
if( dmgbits & DMG_POISON) strcat(szDmgBits, "Poison " );
|
|
if( dmgbits & DMG_RADIATION) strcat(szDmgBits, "Radiation " );
|
|
if( dmgbits & DMG_DROWNRECOVER) strcat(szDmgBits, "Drown Recover " );
|
|
if( dmgbits & DMG_ACID) strcat(szDmgBits, "Acid " );
|
|
if( dmgbits & DMG_SLOWBURN) strcat(szDmgBits, "Slow Burn " );
|
|
if( dmgbits & DMG_SLOWFREEZE) strcat(szDmgBits, "Slow Freeze " );
|
|
if( dmgbits & DMG_MORTAR) strcat(szDmgBits, "Mortar " );
|
|
if( dmgbits & DMG_NUCLEAR) strcat(szDmgBits, "Nuclear Explode " );
|
|
Msg("%s\n", szDmgBits );
|
|
}
|
|
char* GetContentsString( int contents )
|
|
{
|
|
switch(contents)
|
|
{
|
|
case -1: return "EMPTY";
|
|
case -2: return "SOLID";
|
|
case -3: return "WATER";
|
|
case -4: return "SLIME";
|
|
case -5: return "LAVA";
|
|
case -6: return "SKY";
|
|
case -16: return "LADDER";
|
|
case -17: return "FLYFIELD";
|
|
case -18: return "GRAVITY_FLYFIELD";
|
|
case -19: return "FOG";
|
|
case -20: return "SPECIAL 1";
|
|
case -21: return "SPECIAL 2";
|
|
case -22: return "SPECIAL 3";
|
|
default:
|
|
return "NO CONTENTS!";
|
|
}
|
|
}
|
|
|
|
char* GetStringForGlobalState( GLOBALESTATE state )
|
|
{
|
|
switch(state)
|
|
{
|
|
case GLOBAL_ON: return "GLOBAL ON";
|
|
case GLOBAL_OFF: return "GLOBAL OFF";
|
|
case GLOBAL_DEAD: return "GLOBAL DEAD";
|
|
default:
|
|
return "UNKNOWN STATE!";
|
|
}
|
|
}
|
|
|
|
void UTIL_Remove( CBaseEntity *pEntity )
|
|
{
|
|
if ( !pEntity ) return;
|
|
|
|
pEntity->UpdateOnRemove();
|
|
pEntity->pev->flags |= FL_KILLME;
|
|
pEntity->pev->targetname = 0;
|
|
pEntity->pev->health = 0;
|
|
}
|
|
|
|
void UTIL_AngularVector( CBaseEntity *pEnt )
|
|
{
|
|
Vector movedir;
|
|
|
|
UTIL_MakeVectors( pEnt->pev->angles );
|
|
movedir.z = fabs(gpGlobals->v_forward.x);
|
|
movedir.x = fabs(gpGlobals->v_forward.y);
|
|
movedir.y = fabs(gpGlobals->v_forward.z);
|
|
|
|
pEnt->pev->movedir = movedir;
|
|
pEnt->pev->angles = g_vecZero;
|
|
}
|
|
|
|
void UTIL_LinearVector( CBaseEntity *pEnt )
|
|
{
|
|
if( pEnt->pev->angles == Vector( 0, -1, 0 ))
|
|
{
|
|
pEnt->pev->movedir = Vector( 0, 0, 1 );
|
|
}
|
|
else if( pEnt->pev->angles == Vector( 0, -2, 0 ))
|
|
{
|
|
pEnt->pev->movedir = Vector( 0, 0, -1);
|
|
}
|
|
else
|
|
{
|
|
UTIL_MakeVectors( pEnt->pev->angles );
|
|
pEnt->pev->movedir = gpGlobals->v_forward;
|
|
}
|
|
pEnt->pev->angles = g_vecZero;
|
|
}
|
|
|
|
Vector UTIL_GetAngleDistance( Vector vecAngles, float distance )
|
|
{
|
|
//set one length vector
|
|
if(vecAngles.x != 0) vecAngles.x = 1;
|
|
if(vecAngles.y != 0) vecAngles.y = 1;
|
|
if(vecAngles.z != 0) vecAngles.z = 1;
|
|
return vecAngles * distance;
|
|
}
|
|
|
|
Vector UTIL_RandomVector(void)
|
|
{
|
|
Vector out;
|
|
out.x = RANDOM_FLOAT(-1.0, 1.0);
|
|
out.y = RANDOM_FLOAT(-1.0, 1.0);
|
|
out.z = RANDOM_FLOAT(-1.0, 1.0);
|
|
return out;
|
|
}
|
|
|
|
Vector UTIL_RandomVector( Vector vmin, Vector vmax )
|
|
{
|
|
Vector out;
|
|
out.x = RANDOM_FLOAT( vmin.x, vmax.x );
|
|
out.y = RANDOM_FLOAT( vmin.y, vmax.y );
|
|
out.z = RANDOM_FLOAT( vmin.z, vmax.z );
|
|
return out;
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindGlobalEntity( string_t classname, string_t globalname )
|
|
{
|
|
CBaseEntity *pReturn = UTIL_FindEntityByString( NULL, "globalname", STRING(globalname) );
|
|
if ( pReturn )
|
|
{
|
|
if( !FClassnameIs( pReturn->pev, STRING( classname )))
|
|
{
|
|
ALERT( at_console, "Global entity found %s, wrong class %s\n", STRING(globalname), STRING(pReturn->pev->classname) );
|
|
pReturn = NULL;
|
|
}
|
|
}
|
|
|
|
return pReturn;
|
|
}
|
|
|
|
void UTIL_FindBreakable( CBaseEntity *Brush )
|
|
{
|
|
Vector mins = Brush->pev->absmin;
|
|
Vector maxs = Brush->pev->absmax;
|
|
mins.z = Brush->pev->absmax.z;
|
|
maxs.z += 8;
|
|
|
|
CBaseEntity *pList[256];
|
|
int count = UTIL_EntitiesInBox( pList, 256, mins, maxs, FL_ONGROUND );
|
|
if ( count )
|
|
{
|
|
for ( int i = 0; i < count; i++ )
|
|
{
|
|
ClearBits( pList[i]->pev->flags, FL_ONGROUND );
|
|
pList[i]->pev->groundentity = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindEntityForward( CBaseEntity *pMe )
|
|
{
|
|
TraceResult tr;
|
|
|
|
UTIL_MakeVectors( pMe->pev->viewangles );
|
|
UTIL_TraceLine( pMe->pev->origin + pMe->pev->view_ofs, pMe->pev->origin + pMe->pev->view_ofs + gpGlobals->v_forward * 8192, dont_ignore_monsters, pMe->edict(), &tr );
|
|
if( tr.flFraction != 1.0 && !FNullEnt( tr.pHit ))
|
|
{
|
|
CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit );
|
|
return pHit;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void UTIL_FindHullIntersection( const Vector &vecSrc, TraceResult &tr, float *mins, float *maxs, edict_t *pEntity )
|
|
{
|
|
int i, j, k;
|
|
float distance;
|
|
float *minmaxs[2] = {mins, maxs};
|
|
TraceResult tmpTrace;
|
|
Vector vecHullEnd = tr.vecEndPos;
|
|
Vector vecEnd;
|
|
|
|
distance = 1e6f;
|
|
|
|
vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
|
|
UTIL_TraceLine( vecSrc, vecHullEnd, dont_ignore_monsters, pEntity, &tmpTrace );
|
|
if ( tmpTrace.flFraction < 1.0 )
|
|
{
|
|
tr = tmpTrace;
|
|
return;
|
|
}
|
|
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
for ( j = 0; j < 2; j++ )
|
|
{
|
|
for ( k = 0; k < 2; k++ )
|
|
{
|
|
vecEnd.x = vecHullEnd.x + minmaxs[i][0];
|
|
vecEnd.y = vecHullEnd.y + minmaxs[j][1];
|
|
vecEnd.z = vecHullEnd.z + minmaxs[k][2];
|
|
|
|
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, pEntity, &tmpTrace );
|
|
if ( tmpTrace.flFraction < 1.0 )
|
|
{
|
|
float thisDistance = (tmpTrace.vecEndPos - vecSrc).Length();
|
|
if ( thisDistance < distance )
|
|
{
|
|
tr = tmpTrace;
|
|
distance = thisDistance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
edict_t *UTIL_FindLandmark( string_t iLandmarkName )
|
|
{
|
|
return UTIL_FindLandmark( STRING( iLandmarkName));
|
|
}
|
|
|
|
edict_t *UTIL_FindLandmark( const char *pLandmarkName )
|
|
{
|
|
CBaseEntity *pLandmark;
|
|
|
|
pLandmark = UTIL_FindEntityByTargetname( NULL, pLandmarkName );
|
|
while ( pLandmark )
|
|
{
|
|
// Found the landmark
|
|
if ( FClassnameIs( pLandmark->pev, "info_landmark" ) )
|
|
return ENT(pLandmark->pev);
|
|
else pLandmark = UTIL_FindEntityByTargetname( pLandmark, pLandmarkName );
|
|
}
|
|
Msg("ERROR: Can't find landmark %s\n", pLandmarkName );
|
|
return NULL;
|
|
}
|
|
|
|
int UTIL_FindTransition( CBaseEntity *pEntity, string_t iVolumeName )
|
|
{
|
|
return UTIL_FindTransition( pEntity, (char *)STRING( iVolumeName ));
|
|
}
|
|
|
|
int UTIL_FindTransition( CBaseEntity *pEntity, char *pVolumeName )
|
|
{
|
|
CBaseEntity *pVolume;
|
|
|
|
if ( pEntity->ObjectCaps() & FCAP_FORCE_TRANSITION ) return 1;
|
|
|
|
// If you're following another entity, follow it through the transition (weapons follow the player)
|
|
if ( pEntity->pev->movetype == MOVETYPE_FOLLOW && pEntity->pev->aiment != NULL)
|
|
{
|
|
pEntity = CBaseEntity::Instance( pEntity->pev->aiment );
|
|
}
|
|
|
|
int inVolume = 1; // Unless we find a trigger_transition, everything is in the volume
|
|
|
|
pVolume = UTIL_FindEntityByTargetname( NULL, pVolumeName );
|
|
while ( pVolume )
|
|
{
|
|
if ( FClassnameIs( pVolume->pev, "trigger_transition" ) )
|
|
{
|
|
if ( pVolume->Intersects( pEntity ) ) // It touches one, it's in the volume
|
|
return 1;
|
|
else inVolume = 0; // Found a trigger_transition, but I don't intersect it
|
|
}
|
|
pVolume = UTIL_FindEntityByTargetname( pVolume, pVolumeName );
|
|
}
|
|
return inVolume;
|
|
}
|
|
|
|
//========================================================================
|
|
// UTIL_ClearPTR - clear all pointers before changelevel
|
|
//========================================================================
|
|
void UTIL_ClearPTR( void )
|
|
{
|
|
CBaseEntity *pEntity = NULL;
|
|
|
|
for ( int i = 1; i <= gpGlobals->maxEntities; i++ )
|
|
{
|
|
edict_t *pEntityEdict = INDEXENT( i );
|
|
if ( pEntityEdict && !pEntityEdict->free && !FStringNull(pEntityEdict->v.globalname) )
|
|
{
|
|
pEntity = CBaseEntity::Instance( pEntityEdict );
|
|
}
|
|
if (!pEntity) continue;
|
|
else pEntity->ClearPointers();
|
|
}
|
|
}
|
|
|
|
//========================================================================
|
|
// UTIL_ChangeLevel - used for loading next level
|
|
//========================================================================
|
|
void UTIL_ChangeLevel( string_t mapname, string_t spotname )
|
|
{
|
|
UTIL_ChangeLevel((char *)STRING( mapname ), (char *)STRING( spotname ));
|
|
}
|
|
|
|
void UTIL_ChangeLevel( const char *szNextMap, const char *szNextSpot )
|
|
{
|
|
edict_t *pentLandmark;
|
|
LEVELLIST levels[16];
|
|
|
|
ASSERT(!FStrEq(szNextMap, ""));
|
|
|
|
// Don't work in deathmatch
|
|
if ( IsMultiplayer()) return;
|
|
// Some people are firing these multiple times in a frame, disable
|
|
if ( NewLevel ) return;
|
|
|
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex( 1 );
|
|
if (!UTIL_FindTransition( pPlayer, (char *)szNextSpot ))
|
|
{
|
|
DevMsg( "Player isn't in the transition volume %s, aborting\n", szNextSpot );
|
|
return;
|
|
}
|
|
|
|
// This object will get removed in the call to CHANGE_LEVEL, copy the params into "safe" memory
|
|
strcpy(st_szNextMap, szNextMap);
|
|
st_szNextSpot[0] = 0; // Init landmark to NULL
|
|
|
|
// look for a landmark entity
|
|
pentLandmark = UTIL_FindLandmark( szNextSpot );
|
|
if ( !FNullEnt( pentLandmark ) )
|
|
{
|
|
strcpy(st_szNextSpot, szNextSpot);
|
|
gpGlobals->spotOffset = VARS(pentLandmark)->origin;
|
|
}
|
|
|
|
//try to found bsp file before loading nextlevel
|
|
char path[128];
|
|
|
|
sprintf(path, "maps/%s.bsp", st_szNextMap);
|
|
byte *data = LOAD_FILE(path, NULL);
|
|
if (data)
|
|
{
|
|
UTIL_ClearPTR();
|
|
FREE_FILE( data );
|
|
DevMsg( "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot );
|
|
CHANGE_LEVEL( st_szNextMap, st_szNextSpot );
|
|
}
|
|
else Msg("Warning! Map %s not found!\n", st_szNextMap );
|
|
NewLevel = TRUE;//bit who indiactes new level
|
|
}
|
|
|
|
//========================================================================
|
|
// Parent system utils
|
|
// Used for physics code too
|
|
//========================================================================
|
|
void UTIL_MarkChild ( CBaseEntity *pEnt, BOOL correctSpeed, BOOL desired )
|
|
{
|
|
if(desired) //post affect
|
|
{
|
|
SetBits(pEnt->pFlags, PF_DESIRED);
|
|
if (CanAffect)
|
|
{ //apply post affect now
|
|
PostFrameAffect( pEnt );
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetBits(pEnt->pFlags, PF_AFFECT);
|
|
|
|
if (correctSpeed)
|
|
SetBits (pEnt->pFlags, PF_CORECTSPEED);
|
|
else ClearBits (pEnt->pFlags, PF_CORECTSPEED);
|
|
}
|
|
LinkChild( pEnt );//link children
|
|
}
|
|
|
|
void UTIL_SetThink ( CBaseEntity *pEnt )
|
|
{
|
|
SetBits (pEnt->pFlags, PF_SETTHINK);
|
|
pEnt->DontThink();
|
|
UTIL_MarkChild( pEnt );
|
|
}
|
|
|
|
void UTIL_SetPostAffect( CBaseEntity *pEnt )
|
|
{
|
|
SetBits(pEnt->pFlags, PF_POSTAFFECT);
|
|
UTIL_MarkChild( pEnt );
|
|
}
|
|
|
|
void UTIL_SetAction( CBaseEntity *pEnt )
|
|
{
|
|
SetBits(pEnt->pFlags, PF_ACTION);
|
|
UTIL_MarkChild( pEnt );
|
|
}
|
|
//========================================================================
|
|
// Assign origin and angles
|
|
//========================================================================
|
|
void UTIL_AssignOrigin( CBaseEntity *pEntity, const Vector vecOrigin, BOOL bInitiator)
|
|
{
|
|
Vector vecDiff = vecOrigin - pEntity->pev->origin;
|
|
|
|
UTIL_SetOrigin(pEntity, vecOrigin );
|
|
|
|
if (bInitiator && pEntity->m_pParent)
|
|
{
|
|
pEntity->OffsetOrigin = pEntity->pev->origin - pEntity->m_pParent->pev->origin;
|
|
}
|
|
if (pEntity->m_pChild)
|
|
{
|
|
CBaseEntity* pChild = pEntity->m_pChild;
|
|
|
|
Vector vecTemp;
|
|
while (pChild)
|
|
{
|
|
if (pChild->pev->movetype != MOVETYPE_PUSH || pChild->pev->velocity == pEntity->pev->velocity)
|
|
{
|
|
UTIL_AssignOrigin( pChild, vecOrigin + pChild->OffsetOrigin, FALSE );
|
|
}
|
|
else
|
|
{
|
|
vecTemp = vecDiff + pChild->pev->origin;
|
|
UTIL_AssignOrigin( pChild, vecTemp, FALSE );
|
|
}
|
|
pChild = pChild->m_pNextChild;
|
|
}
|
|
}
|
|
}
|
|
void UTIL_AssignAngles( CBaseEntity *pEntity, const Vector vecAngles, BOOL bInitiator)
|
|
{
|
|
Vector vecDiff = vecAngles - pEntity->pev->angles;
|
|
|
|
UTIL_SetAngles(pEntity, vecAngles);
|
|
|
|
if (bInitiator && pEntity->m_pParent)
|
|
pEntity->OffsetAngles = vecAngles - pEntity->m_pParent->pev->angles;
|
|
|
|
if (pEntity->m_pChild) // now I've moved pEntity, does anything else have to move with it?
|
|
{
|
|
CBaseEntity* pChild = pEntity->m_pChild;
|
|
Vector vecTemp;
|
|
while (pChild)
|
|
{
|
|
if (pChild->pev->avelocity == pEntity->pev->avelocity)
|
|
UTIL_AssignAngles( pChild, vecAngles + pChild->OffsetAngles, FALSE );
|
|
else
|
|
{
|
|
vecTemp = vecDiff + pChild->pev->angles;
|
|
UTIL_AssignAngles( pChild, vecTemp, FALSE );
|
|
}
|
|
pChild = pChild->m_pNextChild;
|
|
}
|
|
}
|
|
}
|
|
|
|
//========================================================================
|
|
// Set origin and angles
|
|
//========================================================================
|
|
void UTIL_MergePos ( CBaseEntity *pEnt, int loopbreaker )
|
|
{
|
|
if (loopbreaker <= 0)return;
|
|
if (!pEnt->m_pParent)return;
|
|
|
|
Vector forward, right, up, vecOrg, vecAngles;
|
|
|
|
UTIL_MakeVectorsPrivate( pEnt->m_pParent->pev->angles, forward, right, up );
|
|
|
|
if(pEnt->m_pParent->pev->flags & FL_MONSTER)
|
|
vecOrg = pEnt->PostOrigin = pEnt->m_pParent->pev->origin + (forward * pEnt->OffsetOrigin.x) + ( right * pEnt->OffsetOrigin.y) + (up * pEnt->OffsetOrigin.z);
|
|
else vecOrg = pEnt->PostOrigin = pEnt->m_pParent->pev->origin + (forward * pEnt->OffsetOrigin.x) + (-right * pEnt->OffsetOrigin.y) + (up * pEnt->OffsetOrigin.z);
|
|
|
|
vecAngles = pEnt->PostAngles = pEnt->m_pParent->pev->angles + pEnt->OffsetAngles;
|
|
SetBits(pEnt->pFlags, PF_POSTAFFECT | PF_DESIRED );
|
|
|
|
if ( pEnt->m_pChild )
|
|
{
|
|
CBaseEntity *pMoving = pEnt->m_pChild;
|
|
int sloopbreaker = MAX_CHILDS;
|
|
while (pMoving)
|
|
{
|
|
UTIL_MergePos(pMoving, loopbreaker - 1 );
|
|
pMoving = pMoving->m_pNextChild;
|
|
sloopbreaker--;
|
|
if (sloopbreaker <= 0)break;
|
|
}
|
|
}
|
|
|
|
if(pEnt->pFlags & PF_MERGEPOS)
|
|
{
|
|
UTIL_AssignOrigin( pEnt, vecOrg );
|
|
UTIL_AssignAngles( pEnt, vecAngles );
|
|
ClearBits(pEnt->pFlags, PF_MERGEPOS);
|
|
}
|
|
if(pEnt->pFlags & PF_POSTORG)
|
|
{
|
|
pEnt->pev->origin = vecOrg;
|
|
pEnt->pev->angles = vecAngles;
|
|
}
|
|
}
|
|
|
|
void UTIL_ComplexRotate( CBaseEntity *pParent, CBaseEntity *pChild, const Vector &dest, float m_flTravelTime )
|
|
{
|
|
float time = m_flTravelTime;
|
|
Vector vel = pParent->pev->velocity;
|
|
|
|
// Attempt at getting the train to rotate properly around the origin of the trackchange
|
|
if ( time <= 0 ) return;
|
|
|
|
Vector offset = pChild->pev->origin - pParent->pev->origin;
|
|
Vector delta = dest - pParent->pev->angles;
|
|
// Transform offset into local coordinates
|
|
UTIL_MakeInvVectors( delta, gpGlobals );
|
|
Vector local;
|
|
local.x = DotProduct( offset, gpGlobals->v_forward );
|
|
local.y = DotProduct( offset, gpGlobals->v_right );
|
|
local.z = DotProduct( offset, gpGlobals->v_up );
|
|
|
|
local = local - offset;
|
|
pChild->pev->velocity = vel + (local * (1.0 / time));
|
|
pChild->pev->avelocity = pParent->pev->avelocity;
|
|
}
|
|
|
|
void UTIL_SetOrigin( CBaseEntity *pEntity, const Vector &vecOrigin )
|
|
{
|
|
SET_ORIGIN(ENT(pEntity->pev), vecOrigin );
|
|
}
|
|
|
|
void UTIL_SetAngles( CBaseEntity *pEntity, const Vector &vecAngles )
|
|
{
|
|
pEntity->pev->angles = vecAngles;
|
|
}
|
|
|
|
void UTIL_SynchDoors( CBaseEntity *pEntity )
|
|
{
|
|
CBaseDoor *pDoor = NULL;
|
|
|
|
if ( !FStringNull( pEntity->pev->targetname ) )
|
|
{
|
|
while(1)
|
|
{
|
|
pDoor = (CBaseDoor *)UTIL_FindEntityByTargetname (pDoor, STRING(pEntity->pev->targetname));
|
|
|
|
if ( pDoor != pEntity )
|
|
{
|
|
if (FNullEnt(pDoor)) break;
|
|
|
|
if ( FClassnameIs ( pDoor, "func_door" ) && pDoor->m_flWait >= 0 )
|
|
{
|
|
if (pDoor->pev->velocity == pEntity->pev->velocity)
|
|
{
|
|
UTIL_SetOrigin( pDoor, pEntity->pev->origin );
|
|
UTIL_SetVelocity( pDoor, g_vecZero );
|
|
}
|
|
if (pDoor->GetState() == STATE_TURN_ON) pDoor->DoorGoDown();
|
|
else if (pDoor->GetState() == STATE_TURN_OFF) pDoor->DoorGoUp();
|
|
}
|
|
if( FClassnameIs ( pDoor, "func_door_rotating" ) && pDoor->m_flWait >= 0 )
|
|
{
|
|
if(pDoor->pev->avelocity == pEntity->pev->avelocity)
|
|
{
|
|
UTIL_SetAngles( pDoor, pEntity->pev->angles );
|
|
UTIL_SetAvelocity( pDoor, g_vecZero );
|
|
}
|
|
if (pDoor->GetState() == STATE_TURN_ON) pDoor->DoorGoDown();
|
|
else if (pDoor->GetState() == STATE_TURN_OFF) pDoor->DoorGoUp();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//========================================================================
|
|
// Set childs velocity and avelocity
|
|
//========================================================================
|
|
void UTIL_SetChildVelocity ( CBaseEntity *pEnt, const Vector vecSet, int loopbreaker )
|
|
{
|
|
if (loopbreaker <= 0)return;
|
|
if (!pEnt->m_pParent)return;
|
|
|
|
Vector vecNew;
|
|
vecNew = (pEnt->pev->velocity - pEnt->m_pParent->pev->velocity) + vecSet;
|
|
|
|
if ( pEnt->m_pChild )
|
|
{
|
|
CBaseEntity *pMoving = pEnt->m_pChild;
|
|
int sloopbreaker = MAX_CHILDS;
|
|
while (pMoving)
|
|
{
|
|
UTIL_SetChildVelocity(pMoving, vecNew, loopbreaker - 1 );
|
|
pMoving = pMoving->m_pNextChild;
|
|
sloopbreaker--;
|
|
if (sloopbreaker <= 0)break;
|
|
}
|
|
}
|
|
pEnt->pev->velocity = vecNew;
|
|
}
|
|
|
|
void UTIL_SetVelocity ( CBaseEntity *pEnt, const Vector vecSet )
|
|
{
|
|
Vector vecNew;
|
|
if (pEnt->m_pParent)
|
|
vecNew = vecSet + pEnt->m_pParent->pev->velocity;
|
|
else vecNew = vecSet;
|
|
|
|
if ( pEnt->m_pChild )
|
|
{
|
|
CBaseEntity *pMoving = pEnt->m_pChild;
|
|
int sloopbreaker = MAX_CHILDS;
|
|
while (pMoving)
|
|
{
|
|
UTIL_SetChildVelocity(pMoving, vecNew, MAX_CHILDS );
|
|
if(vecSet != g_vecZero)SetBits(pMoving->pFlags, PF_PARENTMOVE);
|
|
else ClearBits(pMoving->pFlags, PF_PARENTMOVE);
|
|
|
|
pMoving = pMoving->m_pNextChild;
|
|
sloopbreaker--;
|
|
if (sloopbreaker <= 0)break;
|
|
}
|
|
}
|
|
pEnt->pev->velocity = vecNew;
|
|
}
|
|
|
|
void UTIL_SetChildAvelocity ( CBaseEntity *pEnt, const Vector vecSet, int loopbreaker )
|
|
{
|
|
if (loopbreaker <= 0)return;
|
|
if (!pEnt->m_pParent)return;
|
|
|
|
Vector vecNew = (pEnt->pev->avelocity - pEnt->m_pParent->pev->avelocity) + vecSet;
|
|
|
|
if ( pEnt->m_pChild )
|
|
{
|
|
CBaseEntity *pMoving = pEnt->m_pChild;
|
|
int sloopbreaker = MAX_CHILDS;
|
|
while (pMoving)
|
|
{
|
|
UTIL_SetChildAvelocity(pMoving, vecNew, loopbreaker - 1 );
|
|
pMoving = pMoving->m_pNextChild;
|
|
sloopbreaker--;
|
|
if (sloopbreaker <= 0)break;
|
|
}
|
|
}
|
|
|
|
pEnt->pev->avelocity = vecNew;
|
|
}
|
|
|
|
void UTIL_SetAvelocity ( CBaseEntity *pEnt, const Vector vecSet )
|
|
{
|
|
Vector vecNew;
|
|
|
|
if (pEnt->m_pParent)
|
|
vecNew = vecSet + pEnt->m_pParent->pev->avelocity;
|
|
else vecNew = vecSet;
|
|
|
|
if ( pEnt->m_pChild )
|
|
{
|
|
CBaseEntity *pMoving = pEnt->m_pChild;
|
|
int sloopbreaker = MAX_CHILDS;
|
|
while (pMoving)
|
|
{
|
|
UTIL_SetChildAvelocity(pMoving, vecNew, MAX_CHILDS );
|
|
UTIL_MergePos( pMoving );
|
|
if(vecSet != g_vecZero)SetBits(pMoving->pFlags, PF_PARENTMOVE);
|
|
else ClearBits(pMoving->pFlags, PF_PARENTMOVE);
|
|
|
|
pMoving = pMoving->m_pNextChild;
|
|
sloopbreaker--;
|
|
if (sloopbreaker <= 0)break;
|
|
}
|
|
}
|
|
pEnt->pev->avelocity = vecNew;
|
|
}
|
|
|
|
//========================================================================
|
|
// Precache and set resources - add check for present or invalid name
|
|
// NOTE: game will not crashed if model not specified, this code is legacy
|
|
//========================================================================
|
|
void UTIL_SetModel( edict_t *e, string_t s, const char *c ) // set default model if not found
|
|
{
|
|
if( FStringNull( s )) UTIL_SetModel( e, c );
|
|
else UTIL_SetModel( e, s );
|
|
}
|
|
|
|
void UTIL_SetModel( edict_t *e, string_t model )
|
|
{
|
|
UTIL_SetModel( e, STRING( model ));
|
|
}
|
|
|
|
void UTIL_SetModel( edict_t *e, const char *model )
|
|
{
|
|
if( !model || !*model )
|
|
{
|
|
g_engfuncs.pfnSetModel( e, "models/common/null.mdl" );
|
|
return;
|
|
}
|
|
|
|
// is this brush model?
|
|
if( model[0] == '*' )
|
|
{
|
|
g_engfuncs.pfnSetModel( e, model );
|
|
return;
|
|
}
|
|
|
|
// verify file exists
|
|
if( FILE_EXISTS( model ))
|
|
{
|
|
g_engfuncs.pfnSetModel( e, model );
|
|
return;
|
|
}
|
|
|
|
if( !strcmp( UTIL_FileExtension( model ), "mdl" ))
|
|
{
|
|
// this is model
|
|
g_engfuncs.pfnSetModel(e, "models/common/error.mdl" );
|
|
}
|
|
else if( !strcmp( UTIL_FileExtension( model ), "spr" ))
|
|
{
|
|
// this is sprite
|
|
g_engfuncs.pfnSetModel( e, "sprites/error.spr" );
|
|
}
|
|
else
|
|
{
|
|
// set null model
|
|
g_engfuncs.pfnSetModel( e, "models/common/null.mdl" );
|
|
}
|
|
}
|
|
|
|
int UTIL_PrecacheModel( string_t s, const char *e ) // precache default model if not found
|
|
{
|
|
if( FStringNull( s ))
|
|
return UTIL_PrecacheModel( e );
|
|
return UTIL_PrecacheModel( s );
|
|
}
|
|
|
|
int UTIL_PrecacheModel( string_t s ){ return UTIL_PrecacheModel( STRING( s )); }
|
|
int UTIL_PrecacheModel( const char* s )
|
|
{
|
|
if( !s || !*s )
|
|
{
|
|
ALERT( at_warning, "modelname not specified\n" );
|
|
return g_sModelIndexNullModel; // set null model
|
|
}
|
|
|
|
// no need to precache brush
|
|
if( s[0] == '*' ) return 0;
|
|
|
|
if( FILE_EXISTS( s ))
|
|
{
|
|
return g_engfuncs.pfnPrecacheModel( s );
|
|
}
|
|
|
|
if( !strcmp( UTIL_FileExtension( s ), "mdl" ))
|
|
{
|
|
ALERT( at_warning, "model \"%s\" not found!\n", s );
|
|
return g_sModelIndexErrorModel;
|
|
}
|
|
else if( !strcmp( UTIL_FileExtension( s ), "spr" ))
|
|
{
|
|
ALERT( at_warning, "sprite \"%s\" not found!\n", s );
|
|
return g_sModelIndexErrorSprite;
|
|
}
|
|
else
|
|
{
|
|
ALERT( at_error, "invalid name \"%s\"!\n", s );
|
|
return g_sModelIndexNullModel;
|
|
}
|
|
}
|
|
|
|
//========================================================================
|
|
// Precaches the ammo and queues the ammo info for sending to clients
|
|
//========================================================================
|
|
void AddAmmoName( string_t iAmmoName )
|
|
{
|
|
// make sure it's not already in the registry
|
|
for ( int i = 0; i < MAX_AMMO_SLOTS; i++ )
|
|
{
|
|
if( !CBasePlayerWeapon::AmmoInfoArray[i].iszName ) continue;
|
|
if( CBasePlayerWeapon::AmmoInfoArray[i].iszName == iAmmoName )
|
|
return; // ammo already in registry, just quite
|
|
}
|
|
|
|
giAmmoIndex++;
|
|
ASSERT( giAmmoIndex < MAX_AMMO_SLOTS );
|
|
if( giAmmoIndex >= MAX_AMMO_SLOTS )
|
|
giAmmoIndex = 0;
|
|
|
|
CBasePlayerWeapon::AmmoInfoArray[giAmmoIndex].iszName = iAmmoName;
|
|
CBasePlayerWeapon::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; // yes, this info is redundant
|
|
}
|
|
|
|
//========================================================================
|
|
// Precaches entity from other entity
|
|
//========================================================================
|
|
void UTIL_PrecacheEntity( string_t szClassname ) { UTIL_PrecacheEntity( STRING( szClassname )); }
|
|
void UTIL_PrecacheEntity( const char *szClassname )
|
|
{
|
|
edict_t *pent;
|
|
int istr = ALLOC_STRING( szClassname );
|
|
|
|
// check for virtual entities
|
|
if( FUNCTION_FROM_NAME( szClassname ))
|
|
{
|
|
pent = CREATE_NAMED_ENTITY( istr );
|
|
if( FNullEnt( pent )) return;
|
|
}
|
|
else if( !strncmp( szClassname, "weapon_", 7 ))
|
|
{
|
|
//may be this a weapon_generic entity?
|
|
pent = CREATE_NAMED_ENTITY( MAKE_STRING( "weapon_generic" ));
|
|
if ( FNullEnt( pent )) return; //this never gonna called anymore. just in case
|
|
pent->v.netname = istr;
|
|
}
|
|
else // unknown error
|
|
{
|
|
ALERT( at_error, "can't create %s\n", szClassname );
|
|
return;
|
|
}
|
|
|
|
CBaseEntity *pEntity = CBaseEntity::Instance( VARS( pent ));
|
|
if( pEntity ) pEntity->Precache();
|
|
REMOVE_ENTITY(pent);
|
|
}
|
|
|
|
//========================================================================
|
|
// Precaches aurora particle and set it
|
|
//========================================================================
|
|
int UTIL_PrecacheAurora( string_t s ) { return UTIL_PrecacheAurora( STRING( s )); }
|
|
int UTIL_PrecacheAurora( const char *s )
|
|
{
|
|
char path[256];
|
|
sprintf( path, "scripts/aurora/%s.aur", s );
|
|
|
|
if( FILE_EXISTS( path ))
|
|
{
|
|
return ALLOC_STRING( path );
|
|
}
|
|
else // otherwise
|
|
{
|
|
if( !s || !*s )Msg( "Warning: Aurora not specified!\n", s);
|
|
else ALERT( at_warning, "aurora %s not found!\n", s );
|
|
return MAKE_STRING( "scripts/aurora/error.aur" );
|
|
}
|
|
}
|
|
|
|
void UTIL_SetAurora( CBaseEntity *pAttach, int aur, int attachment )
|
|
{
|
|
MESSAGE_BEGIN( MSG_ALL, gmsg.Particle );
|
|
WRITE_BYTE( pAttach->entindex() );
|
|
WRITE_STRING( STRING(aur) );
|
|
MESSAGE_END();
|
|
}
|
|
//========================================================================
|
|
// Set client beams
|
|
//========================================================================
|
|
void UTIL_SetBeams( char *szFile, CBaseEntity *pStart, CBaseEntity *pEnd )
|
|
{
|
|
MESSAGE_BEGIN( MSG_ALL, gmsg.Beams );
|
|
WRITE_STRING( szFile );
|
|
WRITE_BYTE( pStart->entindex()); // beam start entity
|
|
WRITE_BYTE( pEnd->entindex() ); // beam end entity
|
|
MESSAGE_END();
|
|
}
|
|
|
|
//========================================================================
|
|
// Precaches and play sound
|
|
//========================================================================
|
|
int UTIL_PrecacheSound( string_t s, const char *e ) // precache default model if not found
|
|
{
|
|
if (FStringNull( s ))
|
|
return UTIL_PrecacheSound( e );
|
|
return UTIL_PrecacheSound( s );
|
|
}
|
|
int UTIL_PrecacheSound( string_t s ){ return UTIL_PrecacheSound( STRING( s )); }
|
|
int UTIL_PrecacheSound( const char* s )
|
|
{
|
|
if( !s || !*s ) return MAKE_STRING( "common/null.wav" ); // set null sound
|
|
if( *s == '!' ) return MAKE_STRING( s ); // sentence - just make string
|
|
|
|
char path[256];
|
|
sprintf( path, "sound/%s", s );
|
|
|
|
// check file for existing
|
|
if( FILE_EXISTS( path ))
|
|
{
|
|
g_engfuncs.pfnPrecacheSound( s );
|
|
return MAKE_STRING( s );
|
|
}
|
|
|
|
if( !strcmp( UTIL_FileExtension( s ), "wav" ))
|
|
{
|
|
// this is sound
|
|
ALERT( at_warning, "sound \"%s\" not found!\n", s );
|
|
}
|
|
else if( !strcmp( UTIL_FileExtension( s ), "ogg" ))
|
|
{
|
|
// this is sound
|
|
ALERT( at_warning, "sound \"%s\" not found!\n", s );
|
|
}
|
|
else
|
|
{
|
|
// unknown format
|
|
ALERT( at_error, "invalid name \"%s\"!\n", s );
|
|
}
|
|
|
|
g_engfuncs.pfnPrecacheSound("common/null.wav");
|
|
return MAKE_STRING( "common/null.wav" );
|
|
}
|
|
|
|
int UTIL_LoadSoundPreset( string_t pString ) { return UTIL_LoadSoundPreset( STRING( pString )); }
|
|
int UTIL_LoadSoundPreset( const char *pString )
|
|
{
|
|
// try to load direct sound path
|
|
// so we supported 99 presets...
|
|
int m_sound, namelen = strlen( pString ) - 1;
|
|
|
|
if( namelen > 2 ) // yes, it's sound path
|
|
m_sound = ALLOC_STRING( pString );
|
|
else if( pString[0] == '!' ) // sentence
|
|
m_sound = ALLOC_STRING( pString );
|
|
else m_sound = atoi( pString ); // no, it's preset
|
|
return m_sound;
|
|
}
|
|
|
|
int UTIL_LoadDecalPreset( string_t pString ) { return UTIL_LoadDecalPreset( STRING( pString )); }
|
|
int UTIL_LoadDecalPreset( const char *pString )
|
|
{
|
|
// try to load direct sound path
|
|
// so we supported 9 decal groups...
|
|
int m_decal, namelen = strlen(pString) - 1;
|
|
|
|
if( namelen > 1 ) // yes, it's decal name
|
|
m_decal = ALLOC_STRING( pString );
|
|
else m_decal = atoi( pString ); // no, it's preset
|
|
return m_decal;
|
|
}
|
|
|
|
float UTIL_CalcDistance( Vector vecAngles )
|
|
{
|
|
for(int i = 0; i < 3; i++ )
|
|
{
|
|
if(vecAngles[i] < -180) vecAngles[i] += 360;
|
|
else if(vecAngles[i] > 180) vecAngles[i] -= 360;
|
|
}
|
|
return vecAngles.Length();
|
|
}
|
|
|
|
//========================================================================
|
|
// Watches for pWatching enetity and rotate pWatcher entity angles
|
|
//========================================================================
|
|
void UTIL_WatchTarget( CBaseEntity *pWatcher, CBaseEntity *pTarget)
|
|
{
|
|
Vector vecGoal = UTIL_VecToAngles( (pTarget->pev->origin - pWatcher->pev->origin).Normalize() );
|
|
vecGoal.x = -vecGoal.x;
|
|
|
|
if (pWatcher->pev->angles.y > 360) pWatcher->pev->angles.y -= 360;
|
|
if (pWatcher->pev->angles.y < 0) pWatcher->pev->angles.y += 360;
|
|
|
|
float dx = vecGoal.x - pWatcher->pev->angles.x;
|
|
float dy = vecGoal.y - pWatcher->pev->angles.y;
|
|
|
|
if (dx < -180) dx += 360;
|
|
if (dx > 180) dx = dx - 360;
|
|
|
|
if (dy < -180) dy += 360;
|
|
if (dy > 180) dy = dy - 360;
|
|
|
|
pWatcher->pev->avelocity.x = dx * pWatcher->pev->speed * gpGlobals->frametime;
|
|
pWatcher->pev->avelocity.y = dy * pWatcher->pev->speed * gpGlobals->frametime;
|
|
}
|
|
|
|
float UTIL_Approach( float target, float value, float speed )
|
|
{
|
|
float delta = target - value;
|
|
|
|
if ( delta > speed )
|
|
value += speed;
|
|
else if ( delta < -speed )
|
|
value -= speed;
|
|
else
|
|
value = target;
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
float UTIL_ApproachAngle( float target, float value, float speed )
|
|
{
|
|
target = UTIL_AngleMod( target );
|
|
value = UTIL_AngleMod( target );
|
|
|
|
float delta = target - value;
|
|
|
|
// Speed is assumed to be positive
|
|
if ( speed < 0 )
|
|
speed = -speed;
|
|
|
|
if ( delta < -180 )
|
|
delta += 360;
|
|
else if ( delta > 180 )
|
|
delta -= 360;
|
|
|
|
if ( delta > speed )
|
|
value += speed;
|
|
else if ( delta < -speed )
|
|
value -= speed;
|
|
else
|
|
value = target;
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
float UTIL_AngleDistance( float next, float cur )
|
|
{
|
|
float delta = next - cur;
|
|
|
|
while ( delta < -180 )
|
|
delta += 360;
|
|
while ( delta > 180 )
|
|
delta -= 360;
|
|
|
|
return delta;
|
|
}
|
|
|
|
BOOL UTIL_EntIsVisible( entvars_t* pev, entvars_t* pevTarget)
|
|
{
|
|
Vector vecSpot1 = pev->origin + pev->view_ofs;
|
|
Vector vecSpot2 = pevTarget->origin + pevTarget->view_ofs;
|
|
TraceResult tr;
|
|
|
|
UTIL_TraceLine( vecSpot1, vecSpot2, ignore_monsters, ENT(pev), &tr );
|
|
|
|
// FIXME: rewrote this relationship
|
|
if( tr.iContents & MASK_WATER )
|
|
return FALSE; // sight line crossed contents
|
|
if( tr.flFraction == 1 ) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//========================================================================
|
|
// Place all system resourses here
|
|
//========================================================================
|
|
void UTIL_PrecacheResourse( void )
|
|
{
|
|
//constant precaches
|
|
|
|
//null and errors stuff
|
|
g_sModelIndexErrorModel = UTIL_PrecacheModel("models/common/error.mdl");//last crash point
|
|
g_sModelIndexErrorSprite = UTIL_PrecacheModel("sprites/error.spr");
|
|
g_sModelIndexNullModel = UTIL_PrecacheModel("models/common/null.mdl");
|
|
g_sModelIndexNullSprite = UTIL_PrecacheModel("sprites/null.spr");
|
|
|
|
//global sprites and models
|
|
g_sModelIndexFireball = UTIL_PrecacheModel ("sprites/explode.spr");// fireball
|
|
g_sModelIndexWExplosion = UTIL_PrecacheModel ("sprites/wxplode.spr");// underwater fireball
|
|
g_sModelIndexSmoke = UTIL_PrecacheModel ("sprites/steam1.spr");// smoke
|
|
g_sModelIndexBubbles = UTIL_PrecacheModel ("sprites/bubble.spr");//bubbles
|
|
g_sModelIndexLaser = UTIL_PrecacheModel( "sprites/laserbeam.spr" );
|
|
g_sModelIndexBloodSpray = UTIL_PrecacheModel ("sprites/bloodspray.spr");
|
|
g_sModelIndexBloodDrop = UTIL_PrecacheModel ("sprites/blood.spr");
|
|
|
|
//player items and weapons
|
|
memset( CBasePlayerWeapon::ItemInfoArray, 0, sizeof(CBasePlayerWeapon::ItemInfoArray) );
|
|
memset( CBasePlayerWeapon::AmmoInfoArray, 0, sizeof(CBasePlayerWeapon::AmmoInfoArray) );
|
|
giAmmoIndex = 0;
|
|
|
|
//custom precaches
|
|
char token[256];
|
|
char *pfile = (char *)LOAD_FILE( "scripts/precache.txt", NULL );
|
|
if(pfile)
|
|
{
|
|
while ( pfile )
|
|
{
|
|
if ( !stricmp( token, "entity" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
UTIL_PrecacheEntity( ALLOC_STRING(token) );
|
|
}
|
|
else if ( !stricmp( token, "dmentity" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
if( IsDeatchmatch()) UTIL_PrecacheEntity( ALLOC_STRING( token ));
|
|
}
|
|
else if ( !stricmp( token, "model" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
UTIL_PrecacheModel( ALLOC_STRING(token) );
|
|
}
|
|
else if ( !stricmp( token, "dmmodel" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
if( IsDeatchmatch()) UTIL_PrecacheModel( ALLOC_STRING( token ));
|
|
}
|
|
else if ( !stricmp( token, "sound" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
UTIL_PrecacheSound( ALLOC_STRING( token ));
|
|
}
|
|
else if ( !stricmp( token, "dmsound" ))
|
|
{
|
|
pfile = COM_ParseFile( pfile, token );
|
|
if( IsDeatchmatch()) UTIL_PrecacheSound( ALLOC_STRING( token ));
|
|
}
|
|
else if ( !stricmp( token, "aurora" ))
|
|
{
|
|
pfile = COM_ParseFile(pfile, token);
|
|
UTIL_PrecacheAurora( ALLOC_STRING( token ));
|
|
}
|
|
pfile = COM_ParseFile(pfile, token);
|
|
}
|
|
COM_FreeFile(pfile);
|
|
}
|
|
}
|
|
|
|
BOOL IsMultiplayer ( void )
|
|
{
|
|
if( g_pGameRules->IsMultiplayer() )
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL IsDeatchmatch ( void )
|
|
{
|
|
if( g_pGameRules->IsDeathmatch() )
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
float UTIL_WeaponTimeBase( void )
|
|
{
|
|
return gpGlobals->time;
|
|
}
|
|
|
|
static unsigned int glSeed = 0;
|
|
|
|
unsigned int seed_table[ 256 ] =
|
|
{
|
|
28985, 27138, 26457, 9451, 17764, 10909, 28790, 8716, 6361, 4853, 17798, 21977, 19643, 20662, 10834, 20103,
|
|
27067, 28634, 18623, 25849, 8576, 26234, 23887, 18228, 32587, 4836, 3306, 1811, 3035, 24559, 18399, 315,
|
|
26766, 907, 24102, 12370, 9674, 2972, 10472, 16492, 22683, 11529, 27968, 30406, 13213, 2319, 23620, 16823,
|
|
10013, 23772, 21567, 1251, 19579, 20313, 18241, 30130, 8402, 20807, 27354, 7169, 21211, 17293, 5410, 19223,
|
|
10255, 22480, 27388, 9946, 15628, 24389, 17308, 2370, 9530, 31683, 25927, 23567, 11694, 26397, 32602, 15031,
|
|
18255, 17582, 1422, 28835, 23607, 12597, 20602, 10138, 5212, 1252, 10074, 23166, 19823, 31667, 5902, 24630,
|
|
18948, 14330, 14950, 8939, 23540, 21311, 22428, 22391, 3583, 29004, 30498, 18714, 4278, 2437, 22430, 3439,
|
|
28313, 23161, 25396, 13471, 19324, 15287, 2563, 18901, 13103, 16867, 9714, 14322, 15197, 26889, 19372, 26241,
|
|
31925, 14640, 11497, 8941, 10056, 6451, 28656, 10737, 13874, 17356, 8281, 25937, 1661, 4850, 7448, 12744,
|
|
21826, 5477, 10167, 16705, 26897, 8839, 30947, 27978, 27283, 24685, 32298, 3525, 12398, 28726, 9475, 10208,
|
|
617, 13467, 22287, 2376, 6097, 26312, 2974, 9114, 21787, 28010, 4725, 15387, 3274, 10762, 31695, 17320,
|
|
18324, 12441, 16801, 27376, 22464, 7500, 5666, 18144, 15314, 31914, 31627, 6495, 5226, 31203, 2331, 4668,
|
|
12650, 18275, 351, 7268, 31319, 30119, 7600, 2905, 13826, 11343, 13053, 15583, 30055, 31093, 5067, 761,
|
|
9685, 11070, 21369, 27155, 3663, 26542, 20169, 12161, 15411, 30401, 7580, 31784, 8985, 29367, 20989, 14203,
|
|
29694, 21167, 10337, 1706, 28578, 887, 3373, 19477, 14382, 675, 7033, 15111, 26138, 12252, 30996, 21409,
|
|
25678, 18555, 13256, 23316, 22407, 16727, 991, 9236, 5373, 29402, 6117, 15241, 27715, 19291, 19888, 19847
|
|
};
|
|
|
|
unsigned int U_Random( void )
|
|
{
|
|
glSeed *= 69069;
|
|
glSeed += seed_table[ glSeed & 0xff ];
|
|
|
|
return ( ++glSeed & 0x0fffffff );
|
|
}
|
|
|
|
void U_Srand( unsigned int seed )
|
|
{
|
|
glSeed = seed_table[ seed & 0xff ];
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
UTIL_SharedRandomLong
|
|
=====================
|
|
*/
|
|
int UTIL_SharedRandomLong( unsigned int seed, int low, int high )
|
|
{
|
|
unsigned int range;
|
|
|
|
U_Srand( (int)seed + low + high );
|
|
|
|
range = high - low + 1;
|
|
if ( !(range - 1) )
|
|
{
|
|
return low;
|
|
}
|
|
else
|
|
{
|
|
int offset;
|
|
int rnum;
|
|
|
|
rnum = U_Random();
|
|
|
|
offset = rnum % range;
|
|
|
|
return (low + offset);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
UTIL_SharedRandomFloat
|
|
=====================
|
|
*/
|
|
float UTIL_SharedRandomFloat( unsigned int seed, float low, float high )
|
|
{
|
|
//
|
|
unsigned int range;
|
|
|
|
U_Srand( (int)seed + *(int *)&low + *(int *)&high );
|
|
|
|
U_Random();
|
|
U_Random();
|
|
|
|
range = high - low;
|
|
if ( !range )
|
|
{
|
|
return low;
|
|
}
|
|
else
|
|
{
|
|
int tensixrand;
|
|
float offset;
|
|
|
|
tensixrand = U_Random() & 65535;
|
|
|
|
offset = (float)tensixrand / 65536.0;
|
|
|
|
return (low + offset * range );
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
edict_t *DBG_EntOfVars( const entvars_t *pev )
|
|
{
|
|
if( pev->pContainingEntity != NULL )
|
|
return pev->pContainingEntity;
|
|
|
|
ALERT( at_console, "entvars_t pContainingEntity is NULL, calling into engine" );
|
|
edict_t* pent = (*g_engfuncs.pfnFindEntityByVars)((entvars_t*)pev);
|
|
|
|
if( pent == NULL ) ALERT( at_console, "DAMN! Even the engine couldn't FindEntityByVars!" );
|
|
((entvars_t *)pev)->pContainingEntity = pent;
|
|
return pent;
|
|
}
|
|
#endif //DEBUG
|
|
|
|
|
|
#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
|
|
|
|
BOOL UTIL_GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerWeapon *pCurrentWeapon )
|
|
{
|
|
return g_pGameRules->GetNextBestWeapon( pPlayer, pCurrentWeapon );
|
|
}
|
|
|
|
// ripped this out of the engine
|
|
float UTIL_AngleMod(float a)
|
|
{
|
|
if (a < 0)
|
|
{
|
|
a = a + 360 * ((int)(a / 360) + 1);
|
|
}
|
|
else if (a >= 360)
|
|
{
|
|
a = a - 360 * ((int)(a / 360));
|
|
}
|
|
// a = (360.0/65536) * ((int)(a*(65536/360.0)) & 65535);
|
|
return a;
|
|
}
|
|
|
|
float UTIL_AngleDiff( float destAngle, float srcAngle )
|
|
{
|
|
float delta;
|
|
|
|
delta = destAngle - srcAngle;
|
|
if ( destAngle > srcAngle )
|
|
{
|
|
if ( delta >= 180 )
|
|
delta -= 360;
|
|
}
|
|
else
|
|
{
|
|
if ( delta <= -180 )
|
|
delta += 360;
|
|
}
|
|
return delta;
|
|
}
|
|
|
|
Vector UTIL_VecToAngles( const Vector &vec )
|
|
{
|
|
float rgflVecOut[3];
|
|
VEC_TO_ANGLES(vec, rgflVecOut);
|
|
return Vector(rgflVecOut);
|
|
}
|
|
|
|
//LRC - pass in a normalised axis vector and a number of degrees, and this returns the corresponding
|
|
// angles value for an entity.
|
|
inline Vector UTIL_AxisRotationToAngles( const Vector &vecAxis, float flDegs )
|
|
{
|
|
Vector vecTemp = UTIL_AxisRotationToVec( vecAxis, flDegs );
|
|
float rgflVecOut[3];
|
|
//ugh, mathsy.
|
|
rgflVecOut[0] = asin(vecTemp.z) * (-180.0 / M_PI);
|
|
rgflVecOut[1] = acos(vecTemp.x) * (180.0 / M_PI);
|
|
if (vecTemp.y < 0)
|
|
rgflVecOut[1] = -rgflVecOut[1];
|
|
rgflVecOut[2] = 0; //for now
|
|
return Vector(rgflVecOut);
|
|
}
|
|
|
|
//LRC - as above, but returns the position of point 1 0 0 under the given rotation
|
|
Vector UTIL_AxisRotationToVec( const Vector &vecAxis, float flDegs )
|
|
{
|
|
float rgflVecOut[3];
|
|
float flRads = flDegs * (M_PI / 180.0);
|
|
float c = cos(flRads);
|
|
float s = sin(flRads);
|
|
float v = vecAxis.x * (1-c);
|
|
//ugh, more maths. Thank goodness for internet geometry sites...
|
|
rgflVecOut[0] = vecAxis.x*v + c;
|
|
rgflVecOut[1] = vecAxis.y*v + vecAxis.z*s;
|
|
rgflVecOut[2] = vecAxis.z*v - vecAxis.y*s;
|
|
return Vector(rgflVecOut);
|
|
}
|
|
|
|
// float UTIL_MoveToOrigin( edict_t *pent, const Vector vecGoal, float flDist, int iMoveType )
|
|
void UTIL_MoveToOrigin( edict_t *pent, const Vector &vecGoal, float flDist, int iMoveType )
|
|
{
|
|
float rgfl[3];
|
|
vecGoal.CopyToArray(rgfl);
|
|
// return MOVE_TO_ORIGIN ( pent, rgfl, flDist, iMoveType );
|
|
MOVE_TO_ORIGIN ( pent, rgfl, flDist, iMoveType );
|
|
}
|
|
|
|
|
|
int UTIL_EntitiesInBox( CBaseEntity **pList, int listMax, const Vector &mins, const Vector &maxs, int flagMask )
|
|
{
|
|
edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 );
|
|
CBaseEntity *pEntity;
|
|
int count;
|
|
|
|
count = 0;
|
|
|
|
if ( !pEdict )
|
|
return count;
|
|
|
|
for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ )
|
|
{
|
|
if ( pEdict->free ) // Not in use
|
|
continue;
|
|
|
|
if ( flagMask && !(pEdict->v.flags & flagMask) ) // Does it meet the criteria?
|
|
continue;
|
|
|
|
if ( mins.x > pEdict->v.absmax.x ||
|
|
mins.y > pEdict->v.absmax.y ||
|
|
mins.z > pEdict->v.absmax.z ||
|
|
maxs.x < pEdict->v.absmin.x ||
|
|
maxs.y < pEdict->v.absmin.y ||
|
|
maxs.z < pEdict->v.absmin.z )
|
|
continue;
|
|
|
|
pEntity = CBaseEntity::Instance(pEdict);
|
|
if ( !pEntity )
|
|
continue;
|
|
|
|
pList[ count ] = pEntity;
|
|
count++;
|
|
|
|
if ( count >= listMax )
|
|
return count;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
int UTIL_MonstersInSphere( CBaseEntity **pList, int listMax, const Vector ¢er, float radius )
|
|
{
|
|
edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 );
|
|
CBaseEntity *pEntity;
|
|
int count;
|
|
float distance, delta;
|
|
|
|
count = 0;
|
|
float radiusSquared = radius * radius;
|
|
|
|
if ( !pEdict )
|
|
return count;
|
|
|
|
for ( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ )
|
|
{
|
|
if ( pEdict->free ) // Not in use
|
|
continue;
|
|
|
|
if ( !(pEdict->v.flags & (FL_CLIENT|FL_MONSTER)) ) // Not a client/monster ?
|
|
continue;
|
|
|
|
// Use origin for X & Y since they are centered for all monsters
|
|
// Now X
|
|
delta = center.x - pEdict->v.origin.x;//(pEdict->v.absmin.x + pEdict->v.absmax.x)*0.5;
|
|
delta *= delta;
|
|
|
|
if ( delta > radiusSquared )
|
|
continue;
|
|
distance = delta;
|
|
|
|
// Now Y
|
|
delta = center.y - pEdict->v.origin.y;//(pEdict->v.absmin.y + pEdict->v.absmax.y)*0.5;
|
|
delta *= delta;
|
|
|
|
distance += delta;
|
|
if ( distance > radiusSquared )
|
|
continue;
|
|
|
|
// Now Z
|
|
delta = center.z - (pEdict->v.absmin.z + pEdict->v.absmax.z)*0.5;
|
|
delta *= delta;
|
|
|
|
distance += delta;
|
|
if ( distance > radiusSquared )
|
|
continue;
|
|
|
|
pEntity = CBaseEntity::Instance(pEdict);
|
|
if ( !pEntity )
|
|
continue;
|
|
|
|
pList[ count ] = pEntity;
|
|
count++;
|
|
|
|
if ( count >= listMax )
|
|
return count;
|
|
}
|
|
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
CBaseEntity *UTIL_FindEntityInSphere( CBaseEntity *pStartEntity, const Vector &vecCenter, float flRadius )
|
|
{
|
|
edict_t *pentEntity;
|
|
|
|
if (pStartEntity)
|
|
pentEntity = pStartEntity->edict();
|
|
else
|
|
pentEntity = NULL;
|
|
|
|
pentEntity = FIND_ENTITY_IN_SPHERE( pentEntity, vecCenter, flRadius);
|
|
|
|
if (!FNullEnt(pentEntity))
|
|
return CBaseEntity::Instance(pentEntity);
|
|
return NULL;
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindPlayerInSphere( const Vector &vecCenter, float flRadius )
|
|
{
|
|
edict_t *pentEntity = NULL;
|
|
|
|
pentEntity = FIND_ENTITY_IN_SPHERE( pentEntity, vecCenter, flRadius);
|
|
while(!FNullEnt(pentEntity))
|
|
{
|
|
if(pentEntity->v.flags & FL_CLIENT) break; //found player
|
|
pentEntity = FIND_ENTITY_IN_SPHERE( pentEntity, vecCenter, flRadius);
|
|
}
|
|
if (!FNullEnt(pentEntity))
|
|
return CBaseEntity::Instance(pentEntity);
|
|
return NULL;
|
|
}
|
|
|
|
CBasePlayer *UTIL_FindPlayerInPVS( edict_t *pent )
|
|
{
|
|
edict_t *pentPlayer = FIND_CLIENT_IN_PVS( pent );
|
|
CBasePlayer *pPlayer = NULL;
|
|
|
|
if(!FNullEnt(pentPlayer)) //get pointer to player
|
|
pPlayer = GetClassPtr((CBasePlayer *)VARS(pentPlayer));
|
|
return pPlayer;
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindEntityByString( CBaseEntity *pStartEntity, const char *szKeyword, const char *szValue )
|
|
{
|
|
edict_t *pentEntity;
|
|
CBaseEntity *pEntity;
|
|
|
|
if (pStartEntity)
|
|
pentEntity = pStartEntity->edict();
|
|
else
|
|
pentEntity = NULL;
|
|
|
|
for (;;)
|
|
{
|
|
// Don't change this to use UTIL_FindEntityByString!
|
|
pentEntity = FIND_ENTITY_BY_STRING( pentEntity, szKeyword, szValue );
|
|
|
|
// if pentEntity (the edict) is null, we're at the end of the entities. Give up.
|
|
if (FNullEnt(pentEntity))
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
// ...but if only pEntity (the classptr) is null, we've just got one dud, so we try again.
|
|
pEntity = CBaseEntity::Instance(pentEntity);
|
|
if (pEntity)
|
|
return pEntity;
|
|
}
|
|
}
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindEntityByClassname( CBaseEntity *pStartEntity, const char *szName )
|
|
{
|
|
return UTIL_FindEntityByString( pStartEntity, "classname", szName );
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindEntityByTargetname( CBaseEntity *pStartEntity, const char *szName )
|
|
{
|
|
return UTIL_FindEntityByString( pStartEntity, "targetname", szName );
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindEntityByTargetname( CBaseEntity *pStartEntity, const char *szName, CBaseEntity *pActivator )
|
|
{
|
|
return UTIL_FindEntityByTargetname( pStartEntity, szName );
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindEntityByTarget( CBaseEntity *pStartEntity, const char *szName )
|
|
{
|
|
return UTIL_FindEntityByString( pStartEntity, "target", szName );
|
|
}
|
|
|
|
CBaseEntity *UTIL_FindEntityGeneric( const char *szWhatever, Vector &vecSrc, float flRadius )
|
|
{
|
|
CBaseEntity *pEntity = NULL;
|
|
|
|
pEntity = UTIL_FindEntityByTargetname( NULL, szWhatever );
|
|
if (pEntity)
|
|
return pEntity;
|
|
|
|
CBaseEntity *pSearch = NULL;
|
|
float flMaxDist2 = flRadius * flRadius;
|
|
while ((pSearch = UTIL_FindEntityByClassname( pSearch, szWhatever )) != NULL)
|
|
{
|
|
float flDist2 = (pSearch->pev->origin - vecSrc).Length();
|
|
flDist2 = flDist2 * flDist2;
|
|
if (flMaxDist2 > flDist2)
|
|
{
|
|
pEntity = pSearch;
|
|
flMaxDist2 = flDist2;
|
|
}
|
|
}
|
|
return pEntity;
|
|
}
|
|
|
|
|
|
// returns a CBaseEntity pointer to a player by index. Only returns if the player is spawned and connected
|
|
// otherwise returns NULL
|
|
// Index is 1 based
|
|
CBaseEntity *UTIL_PlayerByIndex( int playerIndex )
|
|
{
|
|
CBaseEntity *pPlayer = NULL;
|
|
|
|
if ( playerIndex > 0 && playerIndex <= gpGlobals->maxClients )
|
|
{
|
|
edict_t *pPlayerEdict = INDEXENT( playerIndex );
|
|
if ( pPlayerEdict && !pPlayerEdict->free )
|
|
{
|
|
pPlayer = CBaseEntity::Instance( pPlayerEdict );
|
|
}
|
|
}
|
|
|
|
return pPlayer;
|
|
}
|
|
|
|
|
|
void UTIL_MakeVectors( const Vector &vecAngles )
|
|
{
|
|
MAKE_VECTORS( vecAngles );
|
|
}
|
|
|
|
|
|
void UTIL_MakeAimVectors( const Vector &vecAngles )
|
|
{
|
|
float rgflVec[3];
|
|
vecAngles.CopyToArray(rgflVec);
|
|
rgflVec[0] = -rgflVec[0];
|
|
MAKE_VECTORS(rgflVec);
|
|
}
|
|
|
|
|
|
#define SWAP(a,b,temp) ((temp)=(a),(a)=(b),(b)=(temp))
|
|
|
|
void UTIL_MakeInvVectors( const Vector &vec, globalvars_t *pgv )
|
|
{
|
|
MAKE_VECTORS(vec);
|
|
|
|
float tmp;
|
|
pgv->v_right = pgv->v_right * -1;
|
|
|
|
SWAP(pgv->v_forward.y, pgv->v_right.x, tmp);
|
|
SWAP(pgv->v_forward.z, pgv->v_up.x, tmp);
|
|
SWAP(pgv->v_right.z, pgv->v_up.y, tmp);
|
|
}
|
|
|
|
|
|
void UTIL_EmitAmbientSound( edict_t *entity, const Vector &vecOrigin, const char *samp, float vol, float attenuation, int fFlags, int pitch )
|
|
{
|
|
float rgfl[3];
|
|
vecOrigin.CopyToArray(rgfl);
|
|
|
|
if (samp && *samp == '!')
|
|
{
|
|
char name[32];
|
|
if (SENTENCEG_Lookup(samp, name) >= 0)
|
|
EMIT_AMBIENT_SOUND(entity, rgfl, name, vol, attenuation, fFlags, pitch);
|
|
}
|
|
else
|
|
EMIT_AMBIENT_SOUND(entity, rgfl, samp, vol, attenuation, fFlags, pitch);
|
|
}
|
|
|
|
// Shake the screen of all clients within radius
|
|
// radius == 0, shake all clients
|
|
// UNDONE: Allow caller to shake clients not ONGROUND?
|
|
// UNDONE: Fix falloff model (disabled)?
|
|
// UNDONE: Affect user controls?
|
|
//LRC UNDONE: Work during trigger_camera?
|
|
//-----------------------------------------------------------------------------
|
|
// Compute shake amplitude
|
|
//-----------------------------------------------------------------------------
|
|
float ComputeShakeAmplitude( const Vector ¢er, const Vector &shakePt, float amplitude, float radius )
|
|
{
|
|
if( radius <= 0 )
|
|
return amplitude;
|
|
|
|
float localAmplitude = -1;
|
|
Vector delta = center - shakePt;
|
|
float distance = delta.Length();
|
|
|
|
if( distance <= radius )
|
|
{
|
|
// make the amplitude fall off over distance
|
|
float flPerc = 1.0 - (distance / radius);
|
|
localAmplitude = amplitude * flPerc;
|
|
}
|
|
|
|
return localAmplitude;
|
|
}
|
|
|
|
void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius, ShakeCommand_t eCommand, BOOL bAirShake )
|
|
{
|
|
int i;
|
|
float localAmplitude;
|
|
|
|
for( i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
|
|
|
//
|
|
// Only shake players that are on the ground.
|
|
//
|
|
if( !pPlayer || (!bAirShake && !FBitSet( pPlayer->pev->flags, FL_ONGROUND )))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
localAmplitude = ComputeShakeAmplitude( center, pPlayer->pev->origin, amplitude, radius );
|
|
|
|
// This happens if the player is outside the radius, in which case we should ignore
|
|
// all commands
|
|
if( localAmplitude < 0 ) continue;
|
|
|
|
if (( localAmplitude > 0 ) || ( eCommand == SHAKE_STOP ))
|
|
{
|
|
if ( eCommand == SHAKE_STOP ) localAmplitude = 0;
|
|
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.Shake, NULL, pPlayer->edict() );
|
|
WRITE_BYTE( eCommand ); // shake command (SHAKE_START, STOP, FREQUENCY, AMPLITUDE)
|
|
WRITE_FLOAT( localAmplitude );// shake magnitude/amplitude
|
|
WRITE_FLOAT( frequency ); // shake noise frequency
|
|
WRITE_FLOAT( duration ); // shake lasts this long
|
|
MESSAGE_END();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UTIL_ScreenShake( const Vector ¢er, float amplitude, float frequency, float duration, float radius )
|
|
{
|
|
UTIL_ScreenShake( center, amplitude, frequency, duration, radius, SHAKE_START, FALSE );
|
|
}
|
|
|
|
void UTIL_ScreenShakeAll( const Vector ¢er, float amplitude, float frequency, float duration )
|
|
{
|
|
UTIL_ScreenShake( center, amplitude, frequency, duration, 0, SHAKE_START, FALSE );
|
|
}
|
|
|
|
void UTIL_ScreenFadeAll( const Vector &color, float fadeTime, float fadeHold, int alpha, int flags )
|
|
{
|
|
if(IsMultiplayer())
|
|
{
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
UTIL_ScreenFade( color, fadeTime, fadeHold, alpha, flags, i );
|
|
}
|
|
else UTIL_ScreenFade( color, fadeTime, fadeHold, alpha, flags );
|
|
}
|
|
|
|
|
|
void UTIL_ScreenFade( const Vector &color, float fadeTime, float fadeHold, int alpha, int flags, int playernum )
|
|
{
|
|
CBasePlayer *pPlayer = (CBasePlayer*)UTIL_PlayerByIndex( playernum );
|
|
if ( pPlayer )
|
|
{
|
|
if(flags & FFADE_CUSTOMVIEW)
|
|
{
|
|
if(pPlayer->viewFlags == 0) return;
|
|
ClearBits( flags, FFADE_CUSTOMVIEW ); //don't send this flag to engine!!!
|
|
}
|
|
if(flags & FFADE_OUT && fadeHold == 0) SetBits( flags, FFADE_STAYOUT );
|
|
else ClearBits( flags, FFADE_STAYOUT );
|
|
|
|
pPlayer->m_FadeColor = color;
|
|
pPlayer->m_FadeAlpha = alpha;
|
|
pPlayer->m_iFadeFlags = flags;
|
|
pPlayer->m_iFadeTime = fadeTime;
|
|
pPlayer->m_iFadeHold = fadeHold;
|
|
pPlayer->fadeNeedsUpdate = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
void UTIL_HudMessage( CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage )
|
|
{
|
|
if ( !pEntity || !pEntity->IsNetClient() )
|
|
return;
|
|
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.TempEntity, NULL, pEntity->edict() );
|
|
WRITE_BYTE( TE_TEXTMESSAGE );
|
|
WRITE_BYTE( textparms.channel & 0xFF );
|
|
|
|
WRITE_SHORT( FixedSigned16( textparms.x, 1<<13 ) );
|
|
WRITE_SHORT( FixedSigned16( textparms.y, 1<<13 ) );
|
|
WRITE_BYTE( textparms.effect );
|
|
|
|
WRITE_BYTE( textparms.r1 );
|
|
WRITE_BYTE( textparms.g1 );
|
|
WRITE_BYTE( textparms.b1 );
|
|
WRITE_BYTE( textparms.a1 );
|
|
|
|
WRITE_BYTE( textparms.r2 );
|
|
WRITE_BYTE( textparms.g2 );
|
|
WRITE_BYTE( textparms.b2 );
|
|
WRITE_BYTE( textparms.a2 );
|
|
|
|
WRITE_SHORT( FixedUnsigned16( textparms.fadeinTime, 1<<8 ) );
|
|
WRITE_SHORT( FixedUnsigned16( textparms.fadeoutTime, 1<<8 ) );
|
|
WRITE_SHORT( FixedUnsigned16( textparms.holdTime, 1<<8 ) );
|
|
|
|
if ( textparms.effect == 2 )
|
|
WRITE_SHORT( FixedUnsigned16( textparms.fxTime, 1<<8 ) );
|
|
|
|
if ( strlen( pMessage ) < 512 )
|
|
{
|
|
WRITE_STRING( pMessage );
|
|
}
|
|
else
|
|
{
|
|
char tmp[512];
|
|
strncpy( tmp, pMessage, 511 );
|
|
tmp[511] = 0;
|
|
WRITE_STRING( tmp );
|
|
}
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void UTIL_HudMessageAll( const hudtextparms_t &textparms, const char *pMessage )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
|
if ( pPlayer )
|
|
UTIL_HudMessage( pPlayer, textparms, pMessage );
|
|
}
|
|
}
|
|
|
|
void UTIL_ClientPrintAll( int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
|
|
{
|
|
MESSAGE_BEGIN( MSG_ALL, gmsg.TextMsg );
|
|
WRITE_BYTE( msg_dest );
|
|
WRITE_STRING( msg_name );
|
|
|
|
if ( param1 )
|
|
WRITE_STRING( param1 );
|
|
if ( param2 )
|
|
WRITE_STRING( param2 );
|
|
if ( param3 )
|
|
WRITE_STRING( param3 );
|
|
if ( param4 )
|
|
WRITE_STRING( param4 );
|
|
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void ClientPrint( entvars_t *client, int msg_dest, const char *msg_name, const char *param1, const char *param2, const char *param3, const char *param4 )
|
|
{
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.TextMsg, NULL, client );
|
|
WRITE_BYTE( msg_dest );
|
|
WRITE_STRING( msg_name );
|
|
|
|
if ( param1 )
|
|
WRITE_STRING( param1 );
|
|
if ( param2 )
|
|
WRITE_STRING( param2 );
|
|
if ( param3 )
|
|
WRITE_STRING( param3 );
|
|
if ( param4 )
|
|
WRITE_STRING( param4 );
|
|
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void UTIL_SayText( const char *pText, CBaseEntity *pEntity )
|
|
{
|
|
if ( !pEntity->IsNetClient() )
|
|
return;
|
|
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.SayText, NULL, pEntity->edict() );
|
|
WRITE_BYTE( pEntity->entindex() );
|
|
WRITE_STRING( pText );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void UTIL_SayTextAll( const char *pText, CBaseEntity *pEntity )
|
|
{
|
|
MESSAGE_BEGIN( MSG_ALL, gmsg.SayText, NULL );
|
|
WRITE_BYTE( pEntity->entindex() );
|
|
WRITE_STRING( pText );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
|
|
char *UTIL_dtos1( int d )
|
|
{
|
|
static char buf[8];
|
|
sprintf( buf, "%d", d );
|
|
return buf;
|
|
}
|
|
|
|
char *UTIL_dtos2( int d )
|
|
{
|
|
static char buf[8];
|
|
sprintf( buf, "%d", d );
|
|
return buf;
|
|
}
|
|
|
|
char *UTIL_dtos3( int d )
|
|
{
|
|
static char buf[8];
|
|
sprintf( buf, "%d", d );
|
|
return buf;
|
|
}
|
|
|
|
char *UTIL_dtos4( int d )
|
|
{
|
|
static char buf[8];
|
|
sprintf( buf, "%d", d );
|
|
return buf;
|
|
}
|
|
|
|
void UTIL_ShowMessage( const char *pString, CBaseEntity *pEntity )
|
|
{
|
|
if ( !pEntity || !pEntity->IsNetClient() )
|
|
return;
|
|
|
|
MESSAGE_BEGIN( MSG_ONE, gmsg.HudText, NULL, pEntity->edict() );
|
|
WRITE_STRING( pString );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
|
|
void UTIL_ShowMessageAll( const char *pString )
|
|
{
|
|
// loop through all players
|
|
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
|
if ( pPlayer ) UTIL_ShowMessage( pString, pPlayer );
|
|
}
|
|
}
|
|
void UTIL_SetFogAll( Vector color, int iFadeTime, int iStartDist, int iEndDist )
|
|
{
|
|
// loop through all players
|
|
|
|
if(IsMultiplayer())
|
|
{
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
UTIL_SetFog( color, iFadeTime, iStartDist, iEndDist, i );
|
|
}
|
|
else UTIL_SetFog( color, iFadeTime, iStartDist, iEndDist );
|
|
}
|
|
|
|
void UTIL_SetFog( Vector color, int iFadeTime, int iStartDist, int iEndDist, int playernum )
|
|
{
|
|
CBasePlayer *pPlayer = (CBasePlayer*)UTIL_PlayerByIndex( playernum );
|
|
if ( pPlayer )
|
|
{
|
|
if(pPlayer->m_FogFadeTime != 0) return;//fading in progress !!!TODO: make smooth re-fading
|
|
if(IsMultiplayer()) iFadeTime = 0; //disable fading in multiplayer
|
|
if( iFadeTime > 0 )
|
|
{
|
|
pPlayer->m_iFogEndDist = FOG_LIMIT;
|
|
pPlayer->m_iFogFinalEndDist = iEndDist;
|
|
}
|
|
else if( iFadeTime < 0 )
|
|
{
|
|
pPlayer->m_iFogEndDist = iEndDist;
|
|
pPlayer->m_iFogFinalEndDist = iEndDist;
|
|
}
|
|
else pPlayer->m_iFogEndDist = iEndDist;
|
|
pPlayer->m_iFogStartDist = iStartDist;
|
|
pPlayer->m_FogColor = color;
|
|
pPlayer->m_FogFadeTime = iFadeTime;
|
|
pPlayer->m_flStartTime = gpGlobals->time;
|
|
pPlayer->fogNeedsUpdate = TRUE;
|
|
}
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
Vector mins, maxs;
|
|
|
|
switch( hullNumber )
|
|
{
|
|
case human_hull:
|
|
mins = Vector( -16, -16, 0 );
|
|
maxs = Vector( 16, 16, 72 );
|
|
break;
|
|
case large_hull:
|
|
mins = Vector( -32, -32,-32 );
|
|
maxs = Vector( 32, 32, 32 );
|
|
break;
|
|
case head_hull: // ducked
|
|
mins = Vector( -16, -16,-18 );
|
|
maxs = Vector( 16, 16, 18 );
|
|
break;
|
|
case point_hull:
|
|
default:
|
|
mins = g_vecZero;
|
|
maxs = g_vecZero;
|
|
break;
|
|
}
|
|
|
|
TRACE_HULL( vecStart, mins, maxs, vecEnd, (igmon == ignore_monsters ? TRUE : FALSE), 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 );
|
|
}
|
|
|
|
|
|
TraceResult UTIL_GetGlobalTrace( void )
|
|
{
|
|
TraceResult tr;
|
|
|
|
tr.fAllSolid = gpGlobals->trace_allsolid;
|
|
tr.fStartSolid = gpGlobals->trace_startsolid;
|
|
tr.fStartStuck = gpGlobals->trace_startstuck;
|
|
tr.iContents = gpGlobals->trace_contents;
|
|
tr.iStartContents = gpGlobals->trace_start_contents;
|
|
tr.flFraction = gpGlobals->trace_fraction;
|
|
tr.flPlaneDist = gpGlobals->trace_plane_dist;
|
|
tr.pHit = gpGlobals->trace_ent;
|
|
tr.vecEndPos = gpGlobals->trace_endpos;
|
|
tr.vecPlaneNormal = gpGlobals->trace_plane_normal;
|
|
tr.pTexName = gpGlobals->trace_texture;
|
|
tr.iHitgroup = gpGlobals->trace_hitgroup;
|
|
return tr;
|
|
}
|
|
|
|
|
|
void UTIL_SetSize( entvars_t *pev, const Vector &vecMin, const Vector &vecMax )
|
|
{
|
|
SET_SIZE( ENT(pev), vecMin, vecMax );
|
|
}
|
|
|
|
|
|
float UTIL_VecToYaw( const Vector &vec )
|
|
{
|
|
return VEC_TO_YAW(vec);
|
|
}
|
|
|
|
void UTIL_SetEdictOrigin( edict_t *pEdict, const Vector &vecOrigin )
|
|
{
|
|
SET_ORIGIN(pEdict, vecOrigin );
|
|
}
|
|
|
|
// 'links' the entity into the world
|
|
|
|
|
|
void UTIL_ParticleEffect( const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount )
|
|
{
|
|
PARTICLE_EFFECT( vecOrigin, vecDirection, (float)ulColor, (float)ulCount );
|
|
}
|
|
|
|
|
|
float UTIL_SplineFraction( float value, float scale )
|
|
{
|
|
value = scale * value;
|
|
float valueSquared = value * value;
|
|
|
|
// Nice little ease-in, ease-out spline-like curve
|
|
return 3 * valueSquared - 2 * valueSquared * value;
|
|
}
|
|
|
|
|
|
char* UTIL_VarArgs( char *format, ... )
|
|
{
|
|
va_list argptr;
|
|
static char string[1024];
|
|
|
|
va_start (argptr, format);
|
|
vsprintf (string, format,argptr);
|
|
va_end (argptr);
|
|
|
|
return string;
|
|
}
|
|
|
|
Vector UTIL_GetAimVector( edict_t *pent, float flSpeed )
|
|
{
|
|
Vector tmp;
|
|
GET_AIM_VECTOR(pent, flSpeed, tmp);
|
|
return tmp;
|
|
}
|
|
|
|
BOOL UTIL_IsMasterTriggered(string_t iszMaster, CBaseEntity *pActivator)
|
|
{
|
|
int i, j, found = false;
|
|
const char *szMaster;
|
|
char szBuf[80];
|
|
CBaseEntity *pMaster;
|
|
int reverse = false;
|
|
|
|
|
|
if (iszMaster)
|
|
{
|
|
//Msg( "IsMasterTriggered(%s, %s \"%s\")\n", STRING(iszMaster), STRING(pActivator->pev->classname), STRING(pActivator->pev->targetname));
|
|
szMaster = STRING(iszMaster);
|
|
if (szMaster[0] == '~') //inverse master
|
|
{
|
|
reverse = true;
|
|
szMaster++;
|
|
}
|
|
|
|
pMaster = UTIL_FindEntityByTargetname( NULL, szMaster );
|
|
if ( !pMaster )
|
|
{
|
|
for (i = 0; szMaster[i]; i++)
|
|
{
|
|
if (szMaster[i] == '(')
|
|
{
|
|
for (j = i+1; szMaster[j]; j++)
|
|
{
|
|
if (szMaster[j] == ')')
|
|
{
|
|
strncpy(szBuf, szMaster+i+1, (j-i)-1);
|
|
szBuf[(j-i)-1] = 0;
|
|
pActivator = UTIL_FindEntityByTargetname( NULL, szBuf );
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) // no ) found
|
|
{
|
|
ALERT(at_error, "Missing ')' in master \"%s\"\n", szMaster);
|
|
return FALSE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !found ) // no ( found
|
|
{
|
|
ALERT( at_console, "Master \"%s\" not found!\n", szMaster );
|
|
return TRUE;
|
|
}
|
|
|
|
strncpy(szBuf, szMaster, i);
|
|
szBuf[i] = 0;
|
|
pMaster = UTIL_FindEntityByTargetname( NULL, szBuf );
|
|
}
|
|
|
|
if( pMaster )
|
|
{
|
|
if( reverse )
|
|
return (pMaster->GetState( pActivator ) != STATE_ON );
|
|
return (pMaster->GetState( pActivator ) == STATE_ON);
|
|
}
|
|
}
|
|
|
|
// if the entity has no master (or the master is missing), just say yes.
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL UTIL_ShouldShowBlood( int color )
|
|
{
|
|
if ( color != DONT_BLEED )
|
|
{
|
|
if ( color == BLOOD_COLOR_RED )
|
|
{
|
|
if ( CVAR_GET_FLOAT("violence_hblood") != 0 )
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
if ( CVAR_GET_FLOAT("violence_ablood") != 0 )
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int UTIL_PointContents( const Vector &vec )
|
|
{
|
|
return POINT_CONTENTS( vec );
|
|
}
|
|
|
|
void UTIL_BloodStream( const Vector &origin, const Vector &direction, int color, int amount )
|
|
{
|
|
if ( !UTIL_ShouldShowBlood( color ) )
|
|
return;
|
|
|
|
|
|
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, origin );
|
|
WRITE_BYTE( TE_BLOODSTREAM );
|
|
WRITE_COORD( origin.x );
|
|
WRITE_COORD( origin.y );
|
|
WRITE_COORD( origin.z );
|
|
WRITE_COORD( direction.x );
|
|
WRITE_COORD( direction.y );
|
|
WRITE_COORD( direction.z );
|
|
WRITE_BYTE( color );
|
|
WRITE_BYTE( min( amount, 255 ) );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void UTIL_BloodDrips( const Vector &origin, const Vector &direction, int color, int amount )
|
|
{
|
|
if ( !UTIL_ShouldShowBlood( color ) )
|
|
return;
|
|
|
|
if ( color == DONT_BLEED || amount == 0 )
|
|
return;
|
|
|
|
if ( g_Language == LANGUAGE_GERMAN && color == BLOOD_COLOR_RED )
|
|
color = 0;
|
|
|
|
if ( g_pGameRules->IsMultiplayer() )
|
|
{
|
|
// scale up blood effect in multiplayer for better visibility
|
|
amount *= 2;
|
|
}
|
|
|
|
if ( amount > 255 )
|
|
amount = 255;
|
|
|
|
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, origin );
|
|
WRITE_BYTE( TE_BLOODSPRITE );
|
|
WRITE_COORD( origin.x); // pos
|
|
WRITE_COORD( origin.y);
|
|
WRITE_COORD( origin.z);
|
|
WRITE_SHORT( g_sModelIndexBloodSpray ); // initial sprite model
|
|
WRITE_SHORT( g_sModelIndexBloodDrop ); // droplet sprite models
|
|
WRITE_BYTE( color ); // color index into host_basepal
|
|
WRITE_BYTE( min( max( 3, amount / 10 ), 16 ) ); // size
|
|
MESSAGE_END();
|
|
}
|
|
|
|
Vector UTIL_RandomBloodVector( void )
|
|
{
|
|
Vector direction;
|
|
|
|
direction.x = RANDOM_FLOAT ( -1, 1 );
|
|
direction.y = RANDOM_FLOAT ( -1, 1 );
|
|
direction.z = RANDOM_FLOAT ( 0, 1 );
|
|
|
|
return direction;
|
|
}
|
|
|
|
|
|
void UTIL_BloodDecalTrace( TraceResult *pTrace, int bloodColor )
|
|
{
|
|
if ( UTIL_ShouldShowBlood( bloodColor ) )
|
|
{
|
|
if ( bloodColor == BLOOD_COLOR_RED )
|
|
UTIL_DecalTrace( pTrace, DECAL_BLOOD1 + RANDOM_LONG(0,5) );
|
|
else
|
|
UTIL_DecalTrace( pTrace, DECAL_YBLOOD1 + RANDOM_LONG(0,5) );
|
|
}
|
|
}
|
|
|
|
|
|
void UTIL_DecalTrace( TraceResult *pTrace, int decalNumber )
|
|
{
|
|
short entityIndex;
|
|
int index;
|
|
int message;
|
|
|
|
if ( decalNumber < 0 )
|
|
return;
|
|
|
|
index = gDecals[ decalNumber ].index;
|
|
|
|
if ( index < 0 )
|
|
return;
|
|
|
|
if (pTrace->flFraction == 1.0)
|
|
return;
|
|
|
|
// Only decal BSP models
|
|
if ( pTrace->pHit )
|
|
{
|
|
CBaseEntity *pEntity = CBaseEntity::Instance( pTrace->pHit );
|
|
if ( pEntity && !pEntity->IsBSPModel() )
|
|
return;
|
|
entityIndex = ENTINDEX( pTrace->pHit );
|
|
}
|
|
else
|
|
entityIndex = 0;
|
|
|
|
message = TE_DECAL;
|
|
if ( entityIndex != 0 )
|
|
{
|
|
if ( index > 255 )
|
|
{
|
|
message = TE_DECALHIGH;
|
|
index -= 256;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
message = TE_WORLDDECAL;
|
|
if ( index > 255 )
|
|
{
|
|
message = TE_WORLDDECALHIGH;
|
|
index -= 256;
|
|
}
|
|
}
|
|
|
|
MESSAGE_BEGIN( MSG_BROADCAST, gmsg.TempEntity );
|
|
WRITE_BYTE( message );
|
|
WRITE_COORD( pTrace->vecEndPos.x );
|
|
WRITE_COORD( pTrace->vecEndPos.y );
|
|
WRITE_COORD( pTrace->vecEndPos.z );
|
|
WRITE_BYTE( index );
|
|
if ( entityIndex )
|
|
WRITE_SHORT( entityIndex );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
/*
|
|
==============
|
|
UTIL_PlayerDecalTrace
|
|
|
|
A player is trying to apply his custom decal for the spray can.
|
|
Tell connected clients to display it, or use the default spray can decal
|
|
if the custom can't be loaded.
|
|
==============
|
|
*/
|
|
void UTIL_PlayerDecalTrace( TraceResult *pTrace, int playernum, int decalNumber, BOOL bIsCustom )
|
|
{
|
|
int index;
|
|
|
|
if (!bIsCustom)
|
|
{
|
|
if ( decalNumber < 0 )
|
|
return;
|
|
|
|
index = gDecals[ decalNumber ].index;
|
|
if ( index < 0 )
|
|
return;
|
|
}
|
|
else
|
|
index = decalNumber;
|
|
|
|
if (pTrace->flFraction == 1.0)
|
|
return;
|
|
|
|
MESSAGE_BEGIN( MSG_BROADCAST, gmsg.TempEntity );
|
|
WRITE_BYTE( TE_PLAYERDECAL );
|
|
WRITE_BYTE ( playernum );
|
|
WRITE_COORD( pTrace->vecEndPos.x );
|
|
WRITE_COORD( pTrace->vecEndPos.y );
|
|
WRITE_COORD( pTrace->vecEndPos.z );
|
|
WRITE_SHORT( (short)ENTINDEX(pTrace->pHit) );
|
|
WRITE_BYTE( index );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void UTIL_GunshotDecalTrace( TraceResult *pTrace, int decalNumber )
|
|
{
|
|
if ( decalNumber < 0 )
|
|
return;
|
|
|
|
int index = gDecals[ decalNumber ].index;
|
|
if ( index < 0 )
|
|
return;
|
|
|
|
if (pTrace->flFraction == 1.0)
|
|
return;
|
|
|
|
MESSAGE_BEGIN( MSG_PAS, gmsg.TempEntity, pTrace->vecEndPos );
|
|
WRITE_BYTE( TE_GUNSHOTDECAL );
|
|
WRITE_COORD( pTrace->vecEndPos.x );
|
|
WRITE_COORD( pTrace->vecEndPos.y );
|
|
WRITE_COORD( pTrace->vecEndPos.z );
|
|
WRITE_SHORT( (short)ENTINDEX(pTrace->pHit) );
|
|
WRITE_BYTE( index );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
|
|
void UTIL_Sparks( const Vector &position )
|
|
{
|
|
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, position );
|
|
WRITE_BYTE( TE_SPARKS );
|
|
WRITE_COORD( position.x );
|
|
WRITE_COORD( position.y );
|
|
WRITE_COORD( position.z );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void UTIL_Explode( const Vector ¢er, edict_t *pOwner, int radius, int name )
|
|
{
|
|
CBaseEntity *pExplosion = CBaseEntity::Create( "env_explosion", center, g_vecZero, pOwner );
|
|
if(pExplosion)
|
|
{
|
|
if(name) pExplosion->pev->classname = name;
|
|
pExplosion->pev->dmg = radius;
|
|
pExplosion->pev->owner = pOwner;
|
|
pExplosion->pev->spawnflags |= SF_FIREONCE; //remove entity after explode
|
|
pExplosion->Use( NULL, NULL, USE_ON, 0 );
|
|
}
|
|
}
|
|
|
|
void UTIL_Ricochet( const Vector &position, float scale )
|
|
{
|
|
MESSAGE_BEGIN( MSG_PVS, gmsg.TempEntity, position );
|
|
WRITE_BYTE( TE_ARMOR_RICOCHET );
|
|
WRITE_COORD( position.x );
|
|
WRITE_COORD( position.y );
|
|
WRITE_COORD( position.z );
|
|
WRITE_BYTE( (int)(scale*10) );
|
|
MESSAGE_END();
|
|
}
|
|
|
|
|
|
BOOL UTIL_TeamsMatch( const char *pTeamName1, const char *pTeamName2 )
|
|
{
|
|
// Everyone matches unless it's teamplay
|
|
if ( !g_pGameRules->IsTeamplay() )
|
|
return TRUE;
|
|
|
|
// Both on a team?
|
|
if ( *pTeamName1 != 0 && *pTeamName2 != 0 )
|
|
{
|
|
if ( !stricmp( pTeamName1, pTeamName2 ) ) // Same Team?
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL UTIL_IsFacing( entvars_t *pevTest, const Vector &reference )
|
|
{
|
|
Vector vecDir = (reference - pevTest->origin);
|
|
vecDir.z = 0;
|
|
vecDir = vecDir.Normalize();
|
|
Vector forward, angle;
|
|
angle = pevTest->viewangles;
|
|
angle.x = 0;
|
|
|
|
UTIL_MakeVectorsPrivate( angle, forward, NULL, NULL );
|
|
// He's facing me, he meant it
|
|
if( DotProduct( forward, vecDir ) > 0.96 ) // +/- 15 degrees or so
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void UTIL_StringToVector( float *pVector, const char *pString )
|
|
{
|
|
char *pstr, *pfront, tempString[128];
|
|
int j;
|
|
|
|
strcpy( tempString, pString );
|
|
pstr = pfront = tempString;
|
|
|
|
for ( j = 0; j < 3; j++ ) // lifted from pr_edict.c
|
|
{
|
|
pVector[j] = atof( pfront );
|
|
|
|
while ( *pstr && *pstr != ' ' )
|
|
pstr++;
|
|
if (!*pstr)
|
|
break;
|
|
pstr++;
|
|
pfront = pstr;
|
|
}
|
|
if (j < 2)
|
|
{
|
|
/*
|
|
ALERT( at_error, "Bad field in entity!! %s:%s == \"%s\"\n",
|
|
pkvd->szClassName, pkvd->szKeyName, pkvd->szValue );
|
|
*/
|
|
for (j = j+1;j < 3; j++)
|
|
pVector[j] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//LRC - randomized vectors of the form "0 0 0 .. 1 0 0"
|
|
void UTIL_StringToRandomVector( float *pVector, const char *pString )
|
|
{
|
|
char *pstr, *pfront, tempString[128];
|
|
int j;
|
|
float pAltVec[3];
|
|
|
|
strcpy( tempString, pString );
|
|
pstr = pfront = tempString;
|
|
|
|
for ( j = 0; j < 3; j++ ) // lifted from pr_edict.c
|
|
{
|
|
pVector[j] = atof( pfront );
|
|
|
|
while ( *pstr && *pstr != ' ' ) pstr++;
|
|
if (!*pstr) break;
|
|
pstr++;
|
|
pfront = pstr;
|
|
}
|
|
if (j < 2)
|
|
{
|
|
/*
|
|
ALERT( at_error, "Bad field in entity!! %s:%s == \"%s\"\n",
|
|
pkvd->szClassName, pkvd->szKeyName, pkvd->szValue );
|
|
*/
|
|
for (j = j+1;j < 3; j++)
|
|
pVector[j] = 0;
|
|
}
|
|
else if (*pstr == '.')
|
|
{
|
|
pstr++;
|
|
if (*pstr != '.') return;
|
|
pstr++;
|
|
if (*pstr != ' ') return;
|
|
|
|
UTIL_StringToVector(pAltVec, pstr);
|
|
|
|
pVector[0] = RANDOM_FLOAT( pVector[0], pAltVec[0] );
|
|
pVector[1] = RANDOM_FLOAT( pVector[1], pAltVec[1] );
|
|
pVector[2] = RANDOM_FLOAT( pVector[2], pAltVec[2] );
|
|
}
|
|
}
|
|
|
|
|
|
void UTIL_StringToIntArray( int *pVector, int count, const char *pString )
|
|
{
|
|
char *pstr, *pfront, tempString[128];
|
|
int j;
|
|
|
|
strcpy( tempString, pString );
|
|
pstr = pfront = tempString;
|
|
|
|
for ( j = 0; j < count; j++ ) // lifted from pr_edict.c
|
|
{
|
|
pVector[j] = atoi( pfront );
|
|
|
|
while ( *pstr && *pstr != ' ' )
|
|
pstr++;
|
|
if (!*pstr)
|
|
break;
|
|
pstr++;
|
|
pfront = pstr;
|
|
}
|
|
|
|
for ( j++; j < count; j++ )
|
|
{
|
|
pVector[j] = 0;
|
|
}
|
|
}
|
|
|
|
Vector UTIL_ClampVectorToBox( const Vector &input, const Vector &clampSize )
|
|
{
|
|
Vector sourceVector = input;
|
|
|
|
if ( sourceVector.x > clampSize.x )
|
|
sourceVector.x -= clampSize.x;
|
|
else if ( sourceVector.x < -clampSize.x )
|
|
sourceVector.x += clampSize.x;
|
|
else
|
|
sourceVector.x = 0;
|
|
|
|
if ( sourceVector.y > clampSize.y )
|
|
sourceVector.y -= clampSize.y;
|
|
else if ( sourceVector.y < -clampSize.y )
|
|
sourceVector.y += clampSize.y;
|
|
else
|
|
sourceVector.y = 0;
|
|
|
|
if ( sourceVector.z > clampSize.z )
|
|
sourceVector.z -= clampSize.z;
|
|
else if ( sourceVector.z < -clampSize.z )
|
|
sourceVector.z += clampSize.z;
|
|
else
|
|
sourceVector.z = 0;
|
|
|
|
return sourceVector.Normalize();
|
|
}
|
|
|
|
|
|
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( Vector mins, Vector maxs, int count )
|
|
{
|
|
Vector mid = (mins + maxs) * 0.5;
|
|
|
|
float flHeight = UTIL_WaterLevel( mid, mid.z, mid.z + 1024 );
|
|
flHeight = flHeight - mins.z;
|
|
|
|
MESSAGE_BEGIN( MSG_PAS, gmsg.TempEntity, mid );
|
|
WRITE_BYTE( TE_BUBBLES );
|
|
WRITE_COORD( mins.x ); // mins
|
|
WRITE_COORD( mins.y );
|
|
WRITE_COORD( mins.z );
|
|
WRITE_COORD( maxs.x ); // maxz
|
|
WRITE_COORD( maxs.y );
|
|
WRITE_COORD( maxs.z );
|
|
WRITE_COORD( flHeight ); // height
|
|
WRITE_SHORT( g_sModelIndexBubbles );
|
|
WRITE_BYTE( count ); // count
|
|
WRITE_COORD( 8 ); // speed
|
|
MESSAGE_END();
|
|
}
|
|
|
|
void UTIL_BubbleTrail( Vector from, Vector to, int count )
|
|
{
|
|
float flHeight = UTIL_WaterLevel( from, from.z, from.z + 256 );
|
|
flHeight = flHeight - from.z;
|
|
|
|
if (flHeight < 8)
|
|
{
|
|
flHeight = UTIL_WaterLevel( to, to.z, to.z + 256 );
|
|
flHeight = flHeight - to.z;
|
|
if (flHeight < 8)
|
|
return;
|
|
|
|
// UNDONE: do a ploink sound
|
|
flHeight = flHeight + to.z - from.z;
|
|
}
|
|
|
|
if (count > 255)
|
|
count = 255;
|
|
|
|
MESSAGE_BEGIN( MSG_BROADCAST, gmsg.TempEntity );
|
|
WRITE_BYTE( TE_BUBBLETRAIL );
|
|
WRITE_COORD( from.x ); // mins
|
|
WRITE_COORD( from.y );
|
|
WRITE_COORD( from.z );
|
|
WRITE_COORD( to.x ); // maxz
|
|
WRITE_COORD( to.y );
|
|
WRITE_COORD( to.z );
|
|
WRITE_COORD( flHeight ); // height
|
|
WRITE_SHORT( g_sModelIndexBubbles );
|
|
WRITE_BYTE( count ); // count
|
|
WRITE_COORD( 8 ); // speed
|
|
MESSAGE_END();
|
|
}
|
|
|
|
BOOL UTIL_IsValidEntity( edict_t *pent )
|
|
{
|
|
if ( !pent || pent->free || (pent->v.flags & FL_KILLME) )
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
//=========================================================
|
|
// UTIL_LogPrintf - Prints a logged message to console.
|
|
// Preceded by LOG: ( timestamp ) < message >
|
|
//=========================================================
|
|
void UTIL_LogPrintf( char *fmt, ... )
|
|
{
|
|
va_list argptr;
|
|
static char string[1024];
|
|
|
|
va_start ( argptr, fmt );
|
|
vsprintf ( string, fmt, argptr );
|
|
va_end ( argptr );
|
|
|
|
// Print to server console
|
|
ALERT( at_logged, "%s", string );
|
|
}
|
|
|
|
//============
|
|
// 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;
|
|
}
|
|
|
|
//=========================================================
|
|
// UTIL_DotPoints - returns the dot product of a line from
|
|
// src to check and vecdir.
|
|
//=========================================================
|
|
float UTIL_DotPoints ( const Vector &vecSrc, const Vector &vecCheck, const Vector &vecDir )
|
|
{
|
|
Vector2D vec2LOS;
|
|
|
|
vec2LOS = ( vecCheck - vecSrc ).Make2D();
|
|
vec2LOS = vec2LOS.Normalize();
|
|
|
|
return DotProduct (vec2LOS , ( vecDir.Make2D() ) );
|
|
}
|
|
|
|
//for trigger_viewset
|
|
int HaveCamerasInPVS( edict_t* edict )
|
|
{
|
|
CBaseEntity *pViewEnt = NULL;
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBaseEntity *pEntity = UTIL_PlayerByIndex( i );
|
|
if (!pEntity) continue;
|
|
CBasePlayer *pPlayer = (CBasePlayer *)pEntity;
|
|
if (pPlayer && pPlayer->viewFlags & 1) // custom view active
|
|
{
|
|
pViewEnt = pPlayer->pViewEnt;
|
|
if (!pViewEnt->edict())return 0;
|
|
|
|
edict_t *view = pViewEnt->edict();
|
|
edict_t *pent = UTIL_EntitiesInPVS( edict );
|
|
|
|
while ( !FNullEnt( pent ) )
|
|
{
|
|
if (pent == view)return TRUE;
|
|
pent = pent->v.chain;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
void UTIL_SetView( int ViewEntity, int flags )
|
|
{
|
|
//Light version SetView
|
|
//Please don't use this in multiplayer
|
|
CBaseEntity *m_pPlayer = UTIL_PlayerByIndex( 1 );
|
|
UTIL_SetView( m_pPlayer, ViewEntity, flags );
|
|
}
|
|
|
|
void UTIL_SetView( CBaseEntity *pActivator, int ViewEntity, int flags )
|
|
{
|
|
CBaseEntity *pViewEnt = 0;
|
|
|
|
if(ViewEntity)
|
|
{
|
|
//try to find by targetname
|
|
pViewEnt = UTIL_FindEntityByString( NULL, "targetname", STRING(ViewEntity));
|
|
//try to find by classname
|
|
if(FNullEnt(pViewEnt))
|
|
pViewEnt = UTIL_FindEntityByString( NULL, "classname", STRING(ViewEntity));
|
|
if(pViewEnt && pViewEnt->pev->flags & FL_MONSTER) flags |= MONSTER_VIEW;//detect monster view
|
|
}
|
|
UTIL_SetView( pActivator, pViewEnt, flags );
|
|
}
|
|
|
|
void UTIL_SetView( CBaseEntity *pActivator, CBaseEntity *pViewEnt, int flags )
|
|
{
|
|
((CBasePlayer *)pActivator)->pViewEnt = pViewEnt;
|
|
((CBasePlayer *)pActivator)->viewFlags = flags;
|
|
((CBasePlayer *)pActivator)->viewNeedsUpdate = 1;
|
|
} |