openmohaa/code/game/g_utils.cpp
2016-03-27 11:49:47 +02:00

2790 lines
48 KiB
C++

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
// g_utils.c -- misc utility functions for game module
#include "g_local.h"
#include "actor.h"
#include "player.h"
#include "playerbot.h"
typedef struct {
char oldShader[MAX_QPATH];
char newShader[MAX_QPATH];
float timeOffset;
} shaderRemap_t;
#define MAX_SHADER_REMAPS 128
int remapCount = 0;
shaderRemap_t remappedShaders[MAX_SHADER_REMAPS];
const char *means_of_death_strings[ MOD_TOTAL_NUMBER ] =
{
"none",
"suicide",
"crush",
"crush_every_frame",
"telefrag",
"lava",
"slime",
"falling",
"last_self_inflicted",
"explosion",
"explodewall",
"electric",
"electric_water",
"thrown_object",
"grenade",
"beam",
"rocket",
"impact",
"bullet",
"fast_bullet",
"vehicle",
"fire",
"flashbang",
"on_fire",
"gib",
"impale",
"bash",
"shotgun",
};
// FIXME: OLD Q3 CODE
#if 0
/*
=============
G_PickTarget
Selects a random entity from among the targets
=============
*/
#define MAXCHOICES 32
gentity_t *G_PickTarget (char *targetname)
{
gentity_t *ent = NULL;
int num_choices = 0;
gentity_t *choice[MAXCHOICES];
if (!targetname)
{
G_Printf("G_PickTarget called with NULL targetname\n");
return NULL;
}
while(1)
{
ent = G_Find (ent, FOFS(targetname), targetname);
if (!ent)
break;
choice[num_choices++] = ent;
if (num_choices == MAXCHOICES)
break;
}
if (!num_choices)
{
G_Printf("G_PickTarget: target %s not found\n", targetname);
return NULL;
}
return choice[rand() % num_choices];
}
/*
==============================
G_UseTargets
"activator" should be set to the entity that initiated the firing.
Search for (string)targetname in all entities that
match (string)self.target and call their .use function
==============================
*/
void G_UseTargets( gentity_t *ent, gentity_t *activator ) {
gentity_t *t;
if ( !ent ) {
return;
}
//if (ent->targetShaderName && ent->targetShaderNewName) {
// float f = level.time * 0.001;
// AddRemap(ent->targetShaderName, ent->targetShaderNewName, f);
// gi.SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
//}
if ( !ent->target ) {
return;
}
t = NULL;
while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) {
if ( t == ent ) {
G_Printf ("WARNING: Entity used itself.\n");
} else {
if ( t->use ) {
t->use (t, ent, activator);
}
}
if ( !ent->inuse ) {
G_Printf("entity was removed while using targets\n");
return;
}
}
}
#endif
int MOD_NameToNum( const str &meansOfDeath )
{
int i;
for ( i = 0 ; i < MOD_TOTAL_NUMBER ; i++ )
{
if ( !meansOfDeath.icmp( means_of_death_strings[ i ] ) )
return i;
}
gi.DPrintf( "Unknown means of death - %s\n", meansOfDeath.c_str() );
return -1;
}
const char *MOD_NumToName( int meansOfDeath )
{
if ( ( meansOfDeath > MOD_TOTAL_NUMBER ) || ( meansOfDeath < 0 ) )
{
gi.DPrintf( "Unknown means of death num - %d\n", meansOfDeath );
return "";
}
return means_of_death_strings[ meansOfDeath ];
}
qboolean MOD_matches( int incoming_damage, int damage_type )
{
if ( damage_type == -1 )
{
return true;
}
return false;
}
void AddRemap(const char *oldShader, const char *newShader, float timeOffset) {
int i;
for (i = 0; i < remapCount; i++) {
if (Q_stricmp(oldShader, remappedShaders[i].oldShader) == 0) {
// found it, just update this one
strcpy(remappedShaders[i].newShader,newShader);
remappedShaders[i].timeOffset = timeOffset;
return;
}
}
if (remapCount < MAX_SHADER_REMAPS) {
strcpy(remappedShaders[remapCount].newShader,newShader);
strcpy(remappedShaders[remapCount].oldShader,oldShader);
remappedShaders[remapCount].timeOffset = timeOffset;
remapCount++;
}
}
const char *BuildShaderStateConfig(void) {
static char buff[MAX_STRING_CHARS*4];
char out[(MAX_QPATH * 2) + 5];
int i;
memset(buff, 0, MAX_STRING_CHARS);
for (i = 0; i < remapCount; i++) {
Com_sprintf(out, (MAX_QPATH * 2) + 5, "%s=%s:%5.2f@", remappedShaders[i].oldShader, remappedShaders[i].newShader, remappedShaders[i].timeOffset);
Q_strcat( buff, sizeof( buff ), out);
}
return buff;
}
/*
=================
G_Random
Returns a number from 0<= num < 1
random()
=================
*/
float G_Random
(
void
)
{
return ( ( float )( rand() & 0x7fff ) ) / ( ( float )0x8000 );
}
/*
=================
G_Random
Returns a number from 0 <= num < n
random()
=================
*/
float G_Random
(
float n
)
{
return G_Random() * n;
}
/*
=================
G_CRandom
Returns a number from -1 <= num < 1
crandom()
=================
*/
float G_CRandom
(
void
)
{
return G_Random( 2 ) - 1;
}
/*
=================
G_CRandom
Returns a number from -n <= num < n
crandom()
=================
*/
float G_CRandom
(
float n
)
{
return G_CRandom() * n;
}
/*
=========================================================================
model / sound configstring indexes
=========================================================================
*/
/*
================
G_FindConfigstringIndex
================
*/
int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create ) {
int i;
char *s;
if ( !name || !name[0] ) {
return 0;
}
for ( i=1 ; i<max ; i++ ) {
s = gi.GetConfigstring( start + i );
if ( !s || !s[0] ) {
break;
}
if ( !strcmp( s, name ) ) {
return i;
}
}
if ( !create ) {
return 0;
}
if ( i == max ) {
G_Error( "G_FindConfigstringIndex: overflow" );
}
gi.SetConfigstring( start + i, name );
return i;
}
int G_ModelIndex( const char *name ) {
return G_FindConfigstringIndex( name, CS_MODELS, MAX_MODELS, qtrue );
}
int G_SoundIndex( const char *name ) {
return G_FindConfigstringIndex( name, CS_SOUNDS, MAX_SOUNDS, qtrue );
}
int G_WeaponIndex( const char *name ) {
return G_FindConfigstringIndex( name, CS_WEAPONS, MAX_WEAPONS, qtrue );
}
/*
===============
G_SetTrajectory
Sets the pos trajectory for a fixed position
===============
*/
void G_SetTrajectory
(
gentity_t *ent,
vec3_t org
)
{
ent->s.pos.trTime = 0;
VectorClear( ent->s.pos.trDelta );
VectorCopy( org, ent->s.origin );
}
/*
===============
G_SetConstantLight
Sets the encoded constant light parameter for entities
===============
*/
void G_SetConstantLight
(
int * constantlight,
float * red,
float * green,
float * blue,
float * radius,
int * lightStyle
)
{
int ir, ig, ib, iradius;
if( !constantlight )
return;
ir = ( *constantlight ) & 255;
ig = ( ( *constantlight ) >> 8 ) & 255;
ib = ( ( *constantlight ) >> 16 ) & 255;
iradius = ( ( *constantlight ) >> 24 ) & 255;
if( red )
{
ir = *red * 255;
if( ir > 255 )
ir = 255;
}
if( green )
{
ig = *green * 255;
if( ig > 255 )
ig = 255;
}
if( blue )
{
ib = *blue * 255;
if( ib > 255 )
ib = 255;
}
if( radius )
{
iradius = *radius / CONSTANTLIGHT_RADIUS_SCALE;
if( iradius > 255 )
iradius = 255;
}
if( lightStyle )
{
ir = *lightStyle;
if( ir > 255 )
ir = 255;
}
*constantlight = ( ir )+( ig << 8 ) + ( ib << 16 ) + ( iradius << 24 );
}
//=====================================================================
/*
================
G_TeamCommand
Broadcasts a command to only a specific team
================
*/
void G_TeamCommand( teamtype_t team, char *cmd ) {
int i;
for ( i = 0 ; i < game.maxclients ; i++ )
{
if ( game.clients[i].pers.enterTime != 0.0f )
{
if ( game.clients[i].pers.team == team ) {
gi.SendServerCommand( i, va("%s", cmd ));
}
}
}
}
/*
=============
M_CheckBottom
Returns false if any part of the bottom of the entity is off an edge that
is not a staircase.
=============
*/
int c_yes, c_no;
qboolean M_CheckBottom( Entity *ent )
{
Vector mins, maxs, start, stop;
trace_t trace;
int x, y;
float mid, bottom;
mins = ent->origin + ent->mins * 0.5;
maxs = ent->origin + ent->maxs * 0.5;
// if all of the points under the corners are solid world, don't bother
// with the tougher checks
// the corners must be within 16 of the midpoint
start[ 2 ] = mins[ 2 ] - 1;
for( x = 0; x <= 1; x++ )
{
for( y = 0; y <= 1; y++ )
{
start[ 0 ] = x ? maxs[ 0 ] : mins[ 0 ];
start[ 1 ] = y ? maxs[ 1 ] : mins[ 1 ];
if( gi.PointContents( start, 0 ) != CONTENTS_SOLID )
{
goto realcheck;
}
}
}
c_yes++;
return true; // we got out easy
realcheck:
c_no++;
//
// check it for real...
//
start[ 2 ] = mins[ 2 ];
// the midpoint must be within 16 of the bottom
start[ 0 ] = stop[ 0 ] = ( mins[ 0 ] + maxs[ 0 ] ) * 0.5;
start[ 1 ] = stop[ 1 ] = ( mins[ 1 ] + maxs[ 1 ] ) * 0.5;
stop[ 2 ] = start[ 2 ] - 3 * STEPSIZE;//2 * STEPSIZE;
trace = G_Trace( start, vec_zero, vec_zero, stop, ent, MASK_MONSTERSOLID, false, "M_CheckBottom 1" );
if( trace.fraction == 1.0 )
{
return false;
}
mid = bottom = trace.endpos[ 2 ];
// the corners must be within 16 of the midpoint
for( x = 0; x <= 1; x++ )
{
for( y = 0; y <= 1; y++ )
{
start[ 0 ] = stop[ 0 ] = x ? maxs[ 0 ] : mins[ 0 ];
start[ 1 ] = stop[ 1 ] = y ? maxs[ 1 ] : mins[ 1 ];
trace = G_Trace( start, vec_zero, vec_zero, stop, ent, MASK_MONSTERSOLID, false, "M_CheckBottom 2" );
if( trace.fraction != 1.0 && trace.endpos[ 2 ] > bottom )
{
bottom = trace.endpos[ 2 ];
}
if( trace.fraction == 1.0 || mid - trace.endpos[ 2 ] > STEPSIZE )
{
return false;
}
}
}
c_yes++;
return true;
}
/*
=============
G_Find
Searches all active entities for the next one that holds
the matching string at fieldofs (use the FOFS() macro) in the structure.
Searches beginning at the entity after from, or the beginning if NULL
NULL will be returned if the end of the list is reached.
=============
*/
gentity_t *G_Find( gentity_t *from, int fieldofs, const char *match )
{
char *s;
if( !from )
from = g_entities;
else
from++;
for( ; from < &g_entities[ globals.num_entities ]; from++ )
{
if( !from->inuse )
continue;
s = *( char ** )( ( byte * )from + fieldofs );
if( !s )
continue;
if( !Q_stricmp( s, match ) )
return from;
}
return NULL;
}
/*
=============
TempVector
This is just a convenience function
for making temporary vectors for function calls
=============
*/
float *tv( float x, float y, float z ) {
static int index;
static vec3_t vecs[8];
float *v;
// use an array so that multiple tempvectors won't collide
// for a while
v = vecs[index];
index = (index + 1)&7;
v[0] = x;
v[1] = y;
v[2] = z;
return v;
}
/*
=============
VectorToString
This is just a convenience function
for printing vectors
=============
*/
char *vtos( const vec3_t v ) {
static int index;
static char str[8][32];
char *s;
// use an array so that multiple vtos won't collide
s = str[index];
index = (index + 1)&7;
Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
return s;
}
Entity *G_NextEntity
(
Entity *ent
)
{
gentity_t *from;
if( !g_entities )
{
return NULL;
}
if( !ent )
{
from = g_entities;
} else
{
from = ent->edict + 1;
}
if( !from )
{
return NULL;
}
for( ; from < &g_entities[ globals.num_entities ]; from++ )
{
if( !from->inuse || !from->entity )
{
continue;
}
return from->entity;
}
return NULL;
}
/*
===============
G_GetMovedir
QuakeEd only writes a single float for angles (bad idea), so up and down are
just constant angles.
===============
*/
Vector G_GetMovedir( float angle )
{
if( angle == -1.0f )
{
return Vector( 0.0f, 0.0f, 1.0f );
}
else if( angle == -2.0f )
{
return Vector( 0.0f, 0.0f, -1.0f );
}
angle *= ( M_PI * 2.0f / 360.0f );
return Vector( cos( angle ), sin( angle ), 0.0f );
}
/*
===============
G_SetMovedir
The editor only specifies a single value for angles (yaw),
but we have special constants to generate an up or down direction.
Angles will be cleared, because it is being used to represent a direction
instead of an orientation.
===============
*/
void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
static vec3_t VEC_UP = {0, -1, 0};
static vec3_t MOVEDIR_UP = {0, 0, 1};
static vec3_t VEC_DOWN = {0, -2, 0};
static vec3_t MOVEDIR_DOWN = {0, 0, -1};
if ( VectorCompare (angles, VEC_UP) ) {
VectorCopy (MOVEDIR_UP, movedir);
} else if ( VectorCompare (angles, VEC_DOWN) ) {
VectorCopy (MOVEDIR_DOWN, movedir);
} else {
AngleVectors (angles, movedir, NULL, NULL);
}
VectorClear( angles );
}
float vectoyaw( const vec3_t vec ) {
float yaw;
if (vec[YAW] == 0 && vec[PITCH] == 0) {
yaw = 0;
} else {
if (vec[PITCH]) {
yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
} else if (vec[YAW] > 0) {
yaw = 90;
} else {
yaw = 270;
}
if (yaw < 0) {
yaw += 360;
}
}
return yaw;
}
/*
=================
g_entitiesFree
=================
*/
qboolean g_entitiesFree( void ) {
int i;
gentity_t *e;
e = &g_entities[MAX_CLIENTS];
for ( i = MAX_CLIENTS; i < globals.num_entities; i++, e++) {
if ( e->inuse ) {
continue;
}
// slot available
return qtrue;
}
return qfalse;
}
/*
=================
G_FreeEntity
Marks the entity as free
=================
*/
void G_FreeEntity( gentity_t *ed )
{
gi.UnlinkEntity( ed ); // unlink from world
memset( ed, 0, sizeof( *ed ) );
ed->inuse = qfalse;
}
/*
==============================================================================
Kill box
==============================================================================
*/
/*
=================
G_KillBox
Kills all entities that would touch the proposed new positioning
of ent. Ent should be unlinked before calling this!
=================
*/
void G_KillBox (gentity_t *ent) {
int i, num;
int touch[MAX_GENTITIES];
gentity_t *hit;
vec3_t mins, maxs;
VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
num = gi.AreaEntities( mins, maxs, touch, MAX_GENTITIES );
for (i=0 ; i<num ; i++) {
hit = &g_entities[touch[i]];
if ( !hit->client ) {
continue;
}
// nail it
ent->entity->Damage( hit->entity, ent->entity, 100000, vec_zero, vec_zero,
vec_zero, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
}
}
//==============================================================================
/*
=============
G_Sound
=============
*/
void G_Sound( gentity_t *ent, int channel, int soundIndex ) {
}
//==============================================================================
/*
=================
KillBox
Kills all entities that would touch the proposed new positioning
of ent. Ent should be unlinked before calling this!
=================
*/
qboolean KillBox
(
Entity *ent
)
{
int i;
int num;
int touch[ MAX_GENTITIES ];
gentity_t *hit;
Vector min;
Vector max;
int fail;
fail = 0;
min = ent->origin + ent->mins;
max = ent->origin + ent->maxs;
num = gi.AreaEntities( min, max, touch, MAX_GENTITIES );
for( i = 0; i < num; i++ )
{
hit = &g_entities[ touch[ i ] ];
if( !hit->inuse || ( hit->entity == ent ) || !hit->entity || ( hit->entity == world ) || ( !hit->entity->edict->solid ) )
{
continue;
}
hit->entity->Damage( ent, ent, hit->entity->health + 100000, ent->origin, vec_zero, vec_zero,
0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
//
// if we didn't kill it, fail
//
if( hit->entity->getSolidType() != SOLID_NOT )
{
fail++;
}
}
//
// all clear
//
return !fail;
}
/*
============
G_TouchSolids
Call after linking a new trigger in during gameplay
to force all entities it covers to immediately touch it
============
*/
void G_TouchSolids
(
Entity *ent
)
{
int i;
int num;
int touch[ MAX_GENTITIES ];
gentity_t *hit;
Event *ev;
num = gi.AreaEntities( ent->absmin, ent->absmax, touch, MAX_GENTITIES );
// be careful, it is possible to have an entity in this
// list removed before we get to it (killtriggered)
for( i = 0; i < num; i++ )
{
hit = &g_entities[ touch[ i ] ];
if( !hit->inuse )
{
continue;
}
assert( hit->entity );
//FIXME
// should we post the events so that we don't have to worry about any entities going away
ev = new Event( EV_Touch );
ev->AddEntity( ent );
hit->entity->ProcessEvent( ev );
hit->entity->Unregister( "touch" );
}
}
void G_ShowSightTrace
(
gentity_t *passent1,
gentity_t *passent2,
const char *reason
)
{
str text;
str pass1;
str pass2;
assert( reason );
if( passent1 )
{
pass1 = va( "'%s'(%d)", passent1->entname, passent1->s.number );
}
else
{
pass1 = "NULL";
}
if( passent2 )
{
pass2 = va( "'%s'(%d)", passent2->entname, passent2->s.number );
}
else
{
pass2 = "NULL";
}
text = va( "%0.2f : Pass1 %s Pass2 %s : '%s'\n",
level.time, pass1.c_str(), pass2.c_str(), reason ? reason : "" );
if( sv_traceinfo->integer == 3 )
{
gi.DebugPrintf( text.c_str() );
}
else
{
gi.DPrintf( "%s", text.c_str() );
}
}
void G_ShowTrace
(
trace_t *trace,
gentity_t *passent,
const char *reason
)
{
str text;
str pass;
str hit;
assert( reason );
assert( trace );
if( passent )
{
pass = va( "'%s'(%d)", passent->entname, passent->s.number );
}
else
{
pass = "NULL";
}
if( trace->ent )
{
hit = va( "'%s'(%d)", trace->ent->entname, trace->ent->s.number );
}
else
{
hit = "NULL";
}
text = va( "%0.2f : Pass %s Frac %f Hit %s : '%s'\n",
level.time, pass.c_str(), trace->fraction, hit.c_str(), reason ? reason : "" );
if( sv_traceinfo->integer == 3 )
{
gi.DebugPrintf( text.c_str() );
}
else
{
gi.DPrintf( "%s", text.c_str() );
}
}
void G_CalcBoundsOfMove
(
Vector &start,
Vector &end,
Vector &mins,
Vector &maxs,
Vector *minbounds,
Vector *maxbounds
)
{
Vector bmin;
Vector bmax;
ClearBounds( bmin, bmax );
AddPointToBounds( start, bmin, bmax );
AddPointToBounds( end, bmin, bmax );
bmin += mins;
bmax += maxs;
if( minbounds )
{
*minbounds = bmin;
}
if( maxbounds )
{
*maxbounds = bmax;
}
}
bool G_SightTrace
(
Vector &start,
Vector &mins,
Vector &maxs,
Vector &end,
gentity_t *passent,
gentity_t *passent2,
int contentmask,
qboolean cylinder,
const char *reason
)
{
int entnum, entnum2;
bool result;
assert( reason );
if( passent == NULL )
{
entnum = ENTITYNUM_NONE;
}
else
{
entnum = passent->s.number;
}
if( passent2 == NULL )
{
entnum2 = ENTITYNUM_NONE;
}
else
{
entnum2 = passent2->s.number;
}
result = gi.SightTrace( start, mins, maxs, end, entnum, entnum2, contentmask, cylinder ) ? true : false;
if( sv_traceinfo->integer > 1 )
{
G_ShowSightTrace( passent, passent2, reason );
}
sv_numtraces++;
if( sv_drawtrace->integer )
{
G_DebugLine( start, end, 1, 1, 0, 1 );
}
return result;
}
bool G_SightTrace
(
Vector &start,
Vector &mins,
Vector &maxs,
Vector &end,
Entity *passent,
Entity *passent2,
int contentmask,
qboolean cylinder,
const char *reason
)
{
gentity_t *ent, *ent2;
int entnum, entnum2;
bool result;
assert( reason );
if( passent == NULL || !passent->isSubclassOf( Entity ) )
{
ent = NULL;
entnum = ENTITYNUM_NONE;
}
else
{
ent = passent->edict;
entnum = ent->s.number;
}
if( passent2 == NULL || !passent2->isSubclassOf( Entity ) )
{
ent2 = NULL;
entnum2 = ENTITYNUM_NONE;
}
else
{
ent2 = passent2->edict;
entnum2 = ent2->s.number;
}
result = gi.SightTrace( start, mins, maxs, end, entnum, entnum2, contentmask, cylinder ) ? true : false;
if( sv_traceinfo->integer > 1 )
{
G_ShowSightTrace( ent, ent2, reason );
}
sv_numtraces++;
if( sv_drawtrace->integer )
{
G_DebugLine( start, end, 1, 1, 0, 1 );
}
return result;
}
trace_t G_Trace
(
vec3_t start,
vec3_t mins,
vec3_t maxs,
vec3_t end,
gentity_t *passent,
int contentmask,
qboolean cylinder,
const char *reason,
qboolean tracedeep
)
{
int entnum;
trace_t trace;
if( passent )
{
entnum = passent->s.number;
}
else
{
entnum = ENTITYNUM_NONE;
}
gi.Trace( &trace, start, mins, maxs, end, entnum, contentmask, cylinder, tracedeep );
if( trace.entityNum == ENTITYNUM_NONE )
{
trace.ent = NULL;
}
else
{
trace.ent = &g_entities[ trace.entityNum ];
}
if( sv_traceinfo->integer > 1 )
{
G_ShowTrace( &trace, passent, reason );
}
sv_numtraces++;
if( sv_drawtrace->integer )
{
G_DebugLine( Vector( start ), Vector( end ), 1, 1, 0, 1 );
}
return trace;
}
trace_t G_Trace
(
Vector &start,
Vector &mins,
Vector &maxs,
Vector &end,
Entity *passent,
int contentmask,
qboolean cylinder,
const char *reason,
qboolean tracedeep
)
{
gentity_t *ent;
int entnum;
trace_t trace;
assert( reason );
if( passent == NULL || !passent->isSubclassOf( Entity ) )
{
ent = NULL;
entnum = ENTITYNUM_NONE;
}
else
{
ent = passent->edict;
entnum = ent->s.number;
}
gi.Trace( &trace, start, mins, maxs, end, entnum, contentmask, cylinder, tracedeep );
if( trace.entityNum == ENTITYNUM_NONE )
{
trace.ent = NULL;
}
else
{
trace.ent = &g_entities[ trace.entityNum ];
}
if( sv_traceinfo->integer > 1 )
{
G_ShowTrace( &trace, ent, reason );
}
sv_numtraces++;
if( sv_drawtrace->integer )
{
G_DebugLine( start, end, 1, 1, 0, 1 );
}
return trace;
}
void G_TraceEntities
(
Vector &start,
Vector &mins,
Vector &maxs,
Vector &end,
Container<Entity *>*victimlist,
int contentmask,
qboolean bIncludeTriggers
)
{
trace_t trace;
vec3_t boxmins;
vec3_t boxmaxs;
int num;
int touchlist[ MAX_GENTITIES ];
gentity_t *touch;
int i;
// Find the bounding box
for( i = 0; i<3; i++ )
{
if( end[ i ] > start[ i ] )
{
boxmins[ i ] = start[ i ] + mins[ i ] - 1;
boxmaxs[ i ] = end[ i ] + maxs[ i ] + 1;
}
else
{
boxmins[ i ] = end[ i ] + mins[ i ] - 1;
boxmaxs[ i ] = start[ i ] + maxs[ i ] + 1;
}
}
// Find the list of entites
num = gi.AreaEntities( boxmins, boxmaxs, touchlist, MAX_GENTITIES );
for( i = 0; i<num; i++ )
{
touch = &g_entities[ touchlist[ i ] ];
// see if we should ignore this entity
if( touch->solid == SOLID_NOT )
continue;
if( touch->solid == SOLID_TRIGGER && !bIncludeTriggers )
continue;
gi.ClipToEntity( &trace, start, mins, maxs, end, touchlist[ i ], contentmask );
if( trace.entityNum == touchlist[ i ] )
victimlist->AddObject( touch->entity );
}
}
float PlayersRangeFromSpot
(
Entity *ent
)
{
float bestplayerdistance = 9999999.0f;
Vector vDist;
int i;
gentity_t *e;
Player *player;
if( g_gametype->integer > GT_FFA ) {
return bestplayerdistance;
}
// find the nearest player from the post
for( i = 0, e = g_entities; i < game.maxclients; i++, e++ )
{
if( !e->client || !e->entity ) {
continue;
}
player = ( Player * )e->entity;
if( !player->IsDead() && !player->IsSpectator() )
{
vDist = ent->origin - player->origin;
if( bestplayerdistance > vDist.length() )
bestplayerdistance = vDist.length();
}
}
return bestplayerdistance;
}
/*
================
G_SetOrigin
Sets the pos trajectory for a fixed position
================
*/
void G_SetOrigin( gentity_t *ent, vec3_t origin ) {
VectorCopy( origin, ent->s.origin );
ent->s.pos.trTime = 0;
VectorClear( ent->s.pos.trDelta );
VectorCopy( origin, ent->r.currentOrigin );
}
/*
=================
findradius
Returns entities that have origins within a spherical area
findradius (org, radius)
=================
*/
Entity *findradius
(
Entity *startent,
Vector org,
float rad
)
{
Vector eorg;
gentity_t *from;
float r2, distance;
if( !startent )
{
from = active_edicts.next;
}
else
{
from = startent->edict->next;
}
assert( from );
if( !from )
{
return NULL;
}
assert( ( from == &active_edicts ) || ( from->inuse ) );
// square the radius so that we don't have to do a square root
r2 = rad * rad;
for( ; from != &active_edicts; from = from->next )
{
assert( from->inuse );
assert( from->entity );
eorg = org - from->entity->centroid;
// dot product returns length squared
distance = eorg * eorg;
if( distance <= r2 )
{
return from->entity;
}
else
{
// subtract the object's own radius from this distance
distance -= from->radius2;
if( distance <= r2 )
{
return from->entity;
}
}
}
return NULL;
}
/*
=================
findclientinradius
Returns clients that have origins within a spherical area
findclientinradius (org, radius)
=================
*/
Entity *findclientsinradius
(
Entity *startent,
Vector org,
float rad
)
{
Vector eorg;
gentity_t *ed;
float r2;
int i;
// square the radius so that we don't have to do a square root
r2 = rad * rad;
if( !startent )
{
i = 0;
} else
{
i = startent->entnum + 1;
}
for( ; i < game.maxclients; i++ )
{
ed = &g_entities[ i ];
if( !ed->inuse || !ed->entity )
{
continue;
}
eorg = org - ed->entity->centroid;
// dot product returns length squared
if( ( eorg * eorg ) <= r2 )
{
return ed->entity;
}
}
return NULL;
}
Vector G_CalculateImpulse( const Vector &start, const Vector &end, float speed, float gravity )
{
float traveltime, vertical_speed;
Vector dir, xydir, velocity;
dir = end - start;
xydir = dir;
xydir.z = 0;
traveltime = xydir.length() / speed;
vertical_speed = ( dir.z / traveltime ) + ( 0.5f * gravity * sv_gravity->value * traveltime );
xydir.normalize();
velocity = speed * xydir;
velocity.z = vertical_speed;
return velocity;
}
/*
================
DebugLine
debug polygons only work when running a local game
with r_debugSurface set to 2
================
*/
int DebugLine(vec3_t start, vec3_t end, int color) {
vec3_t points[4], dir, cross, up = {0, 0, 1};
float dot;
VectorCopy(start, points[0]);
VectorCopy(start, points[1]);
//points[1][2] -= 2;
VectorCopy(end, points[2]);
//points[2][2] -= 2;
VectorCopy(end, points[3]);
VectorSubtract(end, start, dir);
VectorNormalize(dir);
dot = DotProduct(dir, up);
if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
else CrossProduct(dir, up, cross);
VectorNormalize(cross);
VectorMA(points[0], 2, cross, points[0]);
VectorMA(points[1], -2, cross, points[1]);
VectorMA(points[2], -2, cross, points[2]);
VectorMA(points[3], 2, cross, points[3]);
//return gi.DebugPolygonCreate(color, 4, points);
// FIXME
return 0;
}
void G_DebugDamage
(
float damage,
Entity *victim,
Entity *attacker,
Entity *inflictor
)
{
gi.DPrintf( "Victim:%s Attacker:%s Inflictor:%s Damage:%f\n", victim->getClassname(), attacker->getClassname(), inflictor->getClassname(), damage );
}
void G_DebugString
(
Vector pos,
float scale,
float r,
float g,
float b,
const char *pszText,
...
)
{
debugstring_t *string;
va_list va;
char szTemp[ 32768 ];
if( !g_numdebugstrings ) {
return;
}
if( *gi.numDebugStrings < g_numdebugstrings->integer )
{
string = ( debugstring_t * )&DebugStrings[ *gi.numDebugStrings ];
( *gi.numDebugStrings )++;
va_start( va, pszText );
vsprintf( szTemp, pszText, va );
va_end( va );
VectorCopy( pos, string->pos );
string->scale = scale;
string->color[ 0 ] = r;
string->color[ 1 ] = g;
string->color[ 2 ] = b;
string->color[ 3 ] = 1.0f;
strncpy( string->szText, szTemp, sizeof( string->szText ) );
string->szText[ sizeof( string->szText ) - 1 ] = 0;
}
else
{
gi.DPrintf( "G_DebugString: Exceeded g_numdebugstrings\n" );
}
}
void G_FadeOut
(
float delaytime
)
{
// Fade the screen out
level.m_fade_color = Vector( 0, 0, 0 );
level.m_fade_alpha = 1.0f;
level.m_fade_time = delaytime;
level.m_fade_time_start = delaytime;
level.m_fade_type = fadeout;
level.m_fade_style = alphablend;
}
void G_AutoFadeIn
(
void
)
{
level.m_fade_time_start = 1;
level.m_fade_time = 1;
level.m_fade_color[ 0 ] = 0;
level.m_fade_color[ 1 ] = 0;
level.m_fade_color[ 2 ] = 0;
level.m_fade_alpha = 1;
level.m_fade_type = fadein;
level.m_fade_style = alphablend;
}
void G_ClearFade
(
void
)
{
level.m_fade_time = -1;
level.m_fade_type = fadein;
}
void G_FadeSound
(
float delaytime
)
{
float time;
// Fade the screen out
time = delaytime * 1000;
gi.SendServerCommand( NULL, va( "fadesound %0.2f", time ) );
}
void G_MissionFailed
(
void
)
{
// Make the music system play the failure music for this level
ChangeMusic( "failure", "normal", true );
//G_PlayerDied( 3 );
// tell the player they f'd up
gi.centerprintf( &g_entities[ 0 ], "@textures/menu/mission.tga" );
level.mission_failed = true;
}
void G_StartCinematic
(
void
)
{
level.cinematic = true;
gi.Cvar_Set( "sv_cinematic", "1" );
}
void G_StopCinematic
(
void
)
{
// clear out the skip thread
level.cinematic = false;
gi.Cvar_Set( "sv_cinematic", "0" );
}
void G_CenterPrintToAllClients( const char *pszString )
{
gentity_t *ent;
int i;
for( i = 0, ent = g_entities; i < game.maxclients; ent++, i++ )
{
if( !ent->inuse || !ent->entity ) {
continue;
}
gi.centerprintf( ent, va( "%s\n", pszString ) );
}
}
void G_PrintToAllClients( const char *pszString, qboolean bBold )
{
if( bBold )
{
gi.SendServerCommand( -1, "print \"" HUD_MESSAGE_WHITE "%s\n\"", pszString );
}
else
{
gi.SendServerCommand( -1, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", pszString );
}
}
void G_PrintDeathMessage( const char *pszString )
{
gentity_t *ent;
int i;
for( i = 0, ent = g_entities; i < game.maxclients; i++, ent++ )
{
if( !ent->inuse || !ent->entity ) {
continue;
}
gi.SendServerCommand( ent - g_entities, "print \"" HUD_MESSAGE_CHAT_RED "%s\"", pszString );
}
}
void G_WarnPlayer( Player *player, const char *format, ... )
{
char buffer[ 4100 ];
va_list va;
va_start( va, format );
vsprintf( buffer, format, va );
va_end( va );
gi.SendServerCommand( player->client->ps.clientNum, "print \"%s\"\n", buffer );
gi.SendServerCommand( player->client->ps.clientNum, "print \"%c%s\"", HUD_MESSAGE_YELLOW, buffer );
}
dtiki_t *modelTiki( const char *mdl )
{
str name;
assert( mdl );
if( !mdl )
{
return 0;
}
// Prepend 'models/' to make things easier
if( !strchr( mdl, '*' ) && !strchr( mdl, '\\' ) && !strchr( mdl, '/' ) )
{
name = "models/";
}
name += mdl;
return gi.modeltiki( name.c_str() );
}
void G_ProcessCacheInitCommands( dtiki_t *tiki )
{
dtikicmd_t *pcmd;
if( tiki->a->num_server_initcmds )
{
int i, j;
Event *event;
for( i = 0; i < tiki->a->num_server_initcmds; i++ )
{
pcmd = &tiki->a->server_initcmds[ i ];
event = new Event( pcmd->args[ 0 ] );
if( Director.GetFlags( event ) & EV_CACHE )
{
for( j = 1; j < pcmd->num_args; j++ )
{
event->AddToken( pcmd->args[ j ] );
}
if( !Director.ProcessEvent( event ) )
{
Com_Printf( "^~^~^ Entity::G_ProcessCacheInitCommands: Bad init server command '%s' in '%s'\n", pcmd->args[ 0 ], tiki->name );
}
}
else
{
delete event;
}
}
}
}
void CacheResource( const char *stuff )
{
AliasListNode_t *ret;
qboolean streamed = qfalse;
char filename[ MAX_STRING_TOKENS ];
if( !stuff )
return;
if( !strchr( stuff, '.' ) )
{
// must be a global alias
stuff = gi.GlobalAlias_FindRandom( stuff, &ret );
if( !stuff )
{
if( gi.fsDebug->integer )
Com_Printf( "alias not found\n" );
return;
}
streamed = ret->streamed;
}
strcpy( filename, stuff );
gi.FS_CanonicalFilename( filename );
if( strstr( filename, ".wav" ) )
{
gi.soundindex( filename, streamed );
}
else if( strstr( filename, ".mp3" ) )
{
gi.soundindex( filename, streamed );
}
else if( strstr( filename, ".tik" ) )
{
dtiki_t *tiki;
if( *stuff && strncmp( "models/", stuff, 7 ) )
{
sprintf( filename, "models/%s", stuff );
}
else
{
strcpy( filename, stuff );
}
gi.FS_CanonicalFilename( filename );
tiki = gi.TIKI_RegisterModel( filename );
if( tiki )
G_ProcessCacheInitCommands( tiki );
}
else if( strstr( filename, ".scr" ) )
{
Director.GetScript( filename );
}
}
void ChangeMusic( const char *current, const char *fallback, qboolean force )
{
int j;
gentity_t *other;
if( current || fallback )
{
for( j = 0; j < game.maxclients; j++ )
{
other = &g_entities[ j ];
if( other->inuse && other->client )
{
Player *client;
client = ( Player * )other->entity;
client->ChangeMusic( current, fallback, force );
}
}
if( current && fallback )
{
gi.DPrintf( "music set to %s with fallback %s\n", current, fallback );
}
}
}
void ChangeMusicVolume( float volume, float fade_time )
{
int j;
gentity_t *other;
for( j = 0; j < game.maxclients; j++ )
{
other = &g_entities[ j ];
if( other->inuse && other->client )
{
Player *client;
client = ( Player * )other->entity;
client->ChangeMusicVolume( volume, fade_time );
}
}
gi.DPrintf( "music volume set to %.2f, fade time %.2f\n", volume, fade_time );
}
void RestoreMusicVolume( float fade_time )
{
int j;
gentity_t *other;
for( j = 0; j < game.maxclients; j++ )
{
other = &g_entities[ j ];
if( other->inuse && other->client )
{
Player *client;
client = ( Player * )other->entity;
client->RestoreMusicVolume( fade_time );
}
}
}
void ChangeSoundtrack( const char * soundtrack )
{
level.saved_soundtrack = level.current_soundtrack;
level.current_soundtrack = soundtrack;
gi.SetConfigstring( CS_MUSIC, soundtrack );
gi.DPrintf( "soundtrack switched to %s.\n", soundtrack );
}
void RestoreSoundtrack( void )
{
if( level.saved_soundtrack.length() )
{
level.current_soundtrack = level.saved_soundtrack;
level.saved_soundtrack = "";
gi.SetConfigstring( CS_MUSIC, level.current_soundtrack.c_str() );
gi.DPrintf( "soundtrack restored %s.\n", level.current_soundtrack.c_str() );
}
}
int G_AIEventTypeFromString
(
const char *pszType
)
{
if( !Q_stricmp( pszType, "weapon_fire" ) )
return AI_EVENT_WEAPON_FIRE;
else if( !Q_stricmp( pszType, "weapon_impact" ) )
return AI_EVENT_WEAPON_IMPACT;
else if( !Q_stricmp( pszType, "explosion" ) )
return AI_EVENT_EXPLOSION;
else if( !Q_stricmp( pszType, "american_voice" ) )
return AI_EVENT_AMERICAN_VOICE;
else if( !Q_stricmp( pszType, "german_voice" ) )
return AI_EVENT_GERMAN_VOICE;
else if( !Q_stricmp( pszType, "american_urgent" ) )
return AI_EVENT_AMERICAN_URGENT;
else if( !Q_stricmp( pszType, "german_urgent" ) )
return AI_EVENT_GERMAN_URGENT;
else if( !Q_stricmp( pszType, "misc" ) )
return AI_EVENT_MISC;
else if( !Q_stricmp( pszType, "misc_loud" ) )
return AI_EVENT_MISC_LOUD;
else if( !Q_stricmp( pszType, "footstep" ) )
return AI_EVENT_FOOTSTEP;
else if( !Q_stricmp( pszType, "grenade" ) )
return AI_EVENT_GRENADE;
else
return AI_EVENT_NONE;
}
float G_AIEventRadius
(
int iType
)
{
static float fRadius[] =
{
2048.0f,
384.0f,
4096.0f,
1024.0f,
1024.0f,
1536.0f,
1536.0f,
1500.0f,
2250.0f,
512.0f,
384.0f,
0,
0,
0,
0
};
if( iType <= 11 )
{
return fRadius[ iType ];
}
else
{
Com_Printf( "G_AIEventRadius: invalid event type\n" );
return 1500.0f;
}
}
void G_BroadcastAIEvent
(
Entity *originator,
Vector origin,
char *pszType
)
{
G_BroadcastAIEvent( originator, origin, G_AIEventTypeFromString( pszType ), -1.0f );
}
void G_BroadcastAIEvent
(
Entity *originator,
Vector origin,
int iType,
float radius
)
{
Sentient *ent;
Vector delta;
str name;
float r2;
float dist2;
int i;
int iNumSentients;
int iAreaNum;
if( iType < 10 )
{
ent = ( Sentient * )G_GetEntity( 0 );
if( ent && ent->m_bIsDisguised )
return;
}
if( radius <= 0.0f )
radius = G_AIEventRadius( iType );
assert( originator );
if( originator && !( originator->flags & FL_NOTARGET ) )
{
r2 = radius * radius;
iNumSentients = SentientList.NumObjects();
for( i = 1; i <= iNumSentients; i++ )
{
ent = SentientList.ObjectAt( i );
if( ( ent == originator ) || ent->deadflag )
{
continue;
}
delta = origin - ent->centroid;
// dot product returns length squared
dist2 = delta * delta;
if( originator )
iAreaNum = originator->edict->r.areanum;
else
iAreaNum = gi.AreaForPoint( origin );
if (
( dist2 <= r2 ) &&
(
( iAreaNum == ent->edict->r.areanum ) ||
( gi.AreasConnected( iAreaNum, ent->edict->r.areanum ) )
)
)
{
if( ent->IsSubclassOfActor() )
{
Actor *act = ( Actor * )ent;
if( !act->IgnoreSound( iType ) )
{
act->ReceiveAIEvent( origin, iType, originator, dist2, r2 );
}
}
else if( ent->IsSubclassOfBot() )
{
PlayerBot *bot = ( PlayerBot * )ent;
bot->NoticeEvent( origin, iType, originator, dist2, r2 );
}
}
}
#if 0
gi.DPrintf( "Broadcast event %s to %d entities\n", ev->getName(), count );
#endif
}
}
void CloneEntity
(
Entity *dest,
Entity *src
)
{
int i, num;
#if 0
dest->setModel( src->model );
// don't process our init commands
//dest->CancelEventsOfType( EV_ProcessInitCommands );
dest->setOrigin( src->origin );
dest->setAngles( src->angles );
#endif
dest->setScale( src->edict->s.scale );
dest->setAlpha( src->edict->s.alpha );
dest->health = src->health;
// copy the surfaces
memcpy( dest->edict->s.surfaces, src->edict->s.surfaces, sizeof( src->edict->s.surfaces ) );
dest->edict->s.constantLight = src->edict->s.constantLight;
//dest->edict->s.eFlags = src->edict->s.eFlags;
dest->edict->s.renderfx = src->edict->s.renderfx;
num = src->numchildren;
for( i = 0; ( i < MAX_MODEL_CHILDREN ) && num; i++ )
{
Entity * clone;
Entity * child;
// duplicate the children
if( !src->children[ i ] )
{
continue;
}
child = G_GetEntity( src->children[ i ] );
if( child )
{
clone = new Animate;
CloneEntity( clone, child );
clone->attach( dest->entnum, child->edict->s.tag_num );
}
num--;
}
}
weaponhand_t WeaponHandNameToNum
(
str side
)
{
if( !side.length() )
{
gi.DPrintf( "WeaponHandNameToNum : Weapon hand not specified\n" );
return WEAPON_ERROR;
}
if( !side.icmp( "mainhand" ) || !side.icmp( "main" ) )
{
return WEAPON_MAIN;
}
else if( !side.icmp( "offhand" ) || !side.icmp( "off" ) )
{
return WEAPON_OFFHAND;
}
else
{
return ( weaponhand_t )atoi( side );
}
}
const char *WeaponHandNumToName
(
weaponhand_t hand
)
{
switch( hand )
{
case WEAPON_MAIN:
return "mainhand";
case WEAPON_OFFHAND:
return "offhand";
default:
return "Invalid Hand";
}
}
firemode_t WeaponModeNameToNum
(
str mode
)
{
if( !mode.length() )
{
gi.DPrintf( "WeaponModeNameToNum : Weapon mode not specified\n" );
assert( 0 );
return FIRE_ERROR;
}
if( !mode.icmp( "primary" ) )
{
return FIRE_PRIMARY;
}
if( !mode.icmp( "secondary" ) )
{
return FIRE_SECONDARY;
}
else
{
return ( firemode_t )atoi( mode );
}
}
// Used to tell the server about the edict pose, such as the player pose
// so that G_Trace with tracedeep will set the location
void G_UpdatePoseInternal( gentity_t *edict )
{
if( edict->s.number == ENTITYNUM_NONE ||
level.frame_skel_index != level.skel_index[ edict->s.number ] )
{
gi.TIKI_SetPoseInternal( edict->tiki, edict->s.number, edict->s.frameInfo, edict->s.bone_tag, edict->s.bone_quat, edict->s.actionWeight );
}
}
orientation_t G_TIKI_Orientation( gentity_t *edict, int num )
{
orientation_t orient;
G_UpdatePoseInternal( edict );
orient = gi.TIKI_OrientationInternal( edict->tiki, edict->s.number, num, edict->s.scale );
return orient;
}
SkelMat4 *G_TIKI_Transform( gentity_t *edict, int num )
{
G_UpdatePoseInternal( edict );
return ( SkelMat4 *)gi.TIKI_TransformInternal( edict->tiki, edict->s.number, num );
}
qboolean G_TIKI_IsOnGround( gentity_t *edict, int num, float threshold )
{
G_UpdatePoseInternal( edict );
return gi.TIKI_IsOnGroundInternal( edict->tiki, edict->s.number, num, threshold );
}
char *G_TimeString( float fTime )
{
float fTmp;
static char szTime[ 32 ];
fTmp = fTime / 3600.0f;
if( fTmp >= 1.0f )
{
Com_sprintf( szTime, sizeof( szTime ), "%i:%02i:%02i",
( int )( fTmp ),
( int )( fmod( fTime / 60.0f, 60.0f ) ),
( int )( fmod( fTime, 60.0f ) )
);
}
else
{
Com_sprintf( szTime, sizeof( szTime ), "%i:%02i",
( int )( fTime / 60.0f ),
( int )( fmod( fTime, 60.0f ) )
);
}
return szTime;
}
char *CanonicalTikiName( const char *szInName )
{
static char filename[ 1024 ];
if( *szInName && Q_stricmpn( "models/", szInName, 7 ) )
{
sprintf( filename, "models/%s", szInName );
}
else
{
strcpy( filename, szInName );
}
gi.FS_CanonicalFilename( filename );
return filename;
}
qboolean G_Command_ProcessFile( const char * filename, qboolean quiet )
{
char *buffer;
const char *bufstart;
char com_token[ MAX_STRING_CHARS ];
if( gi.FS_ReadFile( filename, ( void ** )&buffer, quiet ) == -1 )
{
return qfalse;
}
if( !quiet )
gi.DPrintf( "G_Command_ProcessFile: %s\n", filename );
bufstart = buffer;
while( 1 )
{
Event *ev;
// grab each line as we go
strcpy( com_token, COM_ParseExt( &buffer, qtrue ) );
if( !com_token[ 0 ] )
break;
if(
!Q_stricmp( com_token, "end" ) ||
!Q_stricmp( com_token, "server" )
)
{
// skip the line
while( 1 )
{
strcpy( com_token, COM_ParseExt( &buffer, qfalse ) );
if( !com_token[ 0 ] )
break;
}
continue;
}
// Create the event
ev = new Event( com_token );
// get the rest of the line
while( 1 )
{
strcpy( com_token, COM_ParseExt( &buffer, qfalse ) );
if( !com_token[ 0 ] )
break;
ev->AddToken( com_token );
}
Director.ProcessEvent( ev );
}
gi.FS_FreeFile( ( void * )bufstart );
return qtrue;
}
/*
==============
G_ArchiveEdict
==============
*/
void G_ArchiveEdict
(
Archiver &arc,
gentity_t *edict
)
{
int i;
str tempStr;
assert( edict );
//
// this is written funny because it is used for both saving and loading
//
if( edict->client )
{
arc.ArchiveRaw( edict->client, sizeof( *edict->client ) );
}
arc.ArchiveInteger( &edict->s.beam_entnum );
for( i = 0; i < MAX_FRAMEINFOS; i++ )
{
arc.ArchiveInteger( &edict->s.frameInfo[ i ].index );
arc.ArchiveFloat( &edict->s.frameInfo[ i ].time );
arc.ArchiveFloat( &edict->s.frameInfo[ i ].weight );
}
arc.ArchiveFloat( &edict->s.actionWeight );
arc.ArchiveFloat( &edict->s.shader_data[ 0 ] );
arc.ArchiveFloat( &edict->s.shader_data[ 1 ] );
arc.ArchiveFloat( &edict->s.shader_time );
arc.ArchiveVec3( edict->s.eyeVector );
arc.ArchiveInteger( &edict->s.eType );
arc.ArchiveInteger( &edict->s.eFlags );
arc.ArchiveVec3( edict->s.netorigin );
arc.ArchiveVec3( edict->s.origin );
arc.ArchiveVec3( edict->s.origin2 );
arc.ArchiveVec3( edict->s.netangles );
arc.ArchiveVec3( edict->s.angles );
arc.ArchiveInteger( &edict->s.constantLight );
if( arc.Saving() )
{
if( edict->s.loopSound )
tempStr = gi.GetConfigstring( CS_SOUNDS + edict->s.loopSound );
else
tempStr = "";
arc.ArchiveString( &tempStr );
}
else
{
arc.ArchiveString( &tempStr );
if( tempStr.length() )
edict->s.loopSound = gi.soundindex( tempStr.c_str(), true );
else
edict->s.loopSound = 0;
}
arc.ArchiveFloat( &edict->s.loopSoundVolume );
arc.ArchiveFloat( &edict->s.loopSoundMinDist );
arc.ArchiveFloat( &edict->s.loopSoundMaxDist );
arc.ArchiveFloat( &edict->s.loopSoundPitch );
arc.ArchiveInteger( &edict->s.loopSoundFlags );
arc.ArchiveInteger( &edict->s.parent );
arc.ArchiveInteger( &edict->s.tag_num );
arc.ArchiveBoolean( &edict->s.attach_use_angles );
arc.ArchiveVec3( edict->s.attach_offset );
arc.ArchiveInteger( &edict->s.skinNum );
arc.ArchiveInteger( &edict->s.wasframe );
for( i = 0; i < NUM_BONE_CONTROLLERS; i++ )
{
arc.ArchiveInteger( &edict->s.bone_tag[ i ] );
arc.ArchiveVec3( edict->s.bone_angles[ i ] );
arc.ArchiveVec4( edict->s.bone_quat[ i ] );
}
arc.ArchiveRaw( &edict->s.surfaces, sizeof( edict->s.surfaces ) );
arc.ArchiveInteger( &edict->s.clientNum );
arc.ArchiveInteger( &edict->s.groundEntityNum );
arc.ArchiveInteger( &edict->s.solid );
arc.ArchiveFloat( &edict->s.scale );
arc.ArchiveFloat( &edict->s.alpha );
arc.ArchiveInteger( &edict->s.renderfx );
arc.ArchiveVec4( edict->s.quat );
arc.ArchiveRaw( &edict->mat, sizeof( edict->mat ) );
arc.ArchiveInteger( &edict->r.svFlags );
arc.ArchiveVec3( edict->r.mins );
arc.ArchiveVec3( edict->r.maxs );
arc.ArchiveInteger( &edict->r.contents );
arc.ArchiveVec3( edict->r.absmin );
arc.ArchiveVec3( edict->r.absmax );
arc.ArchiveFloat( &edict->r.radius );
if( !arc.Saving() ) {
edict->radius2 = edict->r.radius * edict->r.radius;
}
arc.ArchiveVec3( edict->r.currentOrigin );
arc.ArchiveVec3( edict->r.currentAngles );
arc.ArchiveInteger( &edict->r.ownerNum );
ArchiveEnum( edict->solid, solid_t );
arc.ArchiveFloat( &edict->freetime );
arc.ArchiveFloat( &edict->spawntime );
tempStr = str( edict->entname );
arc.ArchiveString( &tempStr );
strncpy( edict->entname, tempStr.c_str(), sizeof( edict->entname ) - 1 );
arc.ArchiveInteger( &edict->clipmask );
arc.ArchiveBoolean( &edict->r.bmodel );
if( arc.Loading() )
{
gi.LinkEntity( edict );
}
arc.ArchiveInteger( &edict->r.lastNetTime );
}
const char *G_LocationNumToDispString( int iLocation )
{
switch( iLocation )
{
case -2:
case -1:
return "";
case 0:
return "head";
case 1:
return "helmet";
case 2:
return "neck";
case 3:
return "upper torso";
case 4:
return "middle torso";
case 5:
return "lower torso";
case 6:
return "pelvis";
case 7:
return "upper right arm";
case 8:
return "upper left arm";
case 9:
return "upper right leg";
case 10:
return "upper left leg";
case 11:
return "lower right arm";
case 12:
return "lower left arm";
case 13:
return "lower right leg";
case 14:
return "lower left leg";
case 15:
return "right hand";
case 16:
return "left hand";
case 17:
return "right foot";
case 18:
return "left foot";
default:
return "";
}
}
str G_WeaponClassNumToName
(
int num
)
{
if( num & WEAPON_CLASS_PISTOL )
{
return "pistol";
}
else if( num & WEAPON_CLASS_RIFLE )
{
return "rifle";
}
else if( num & WEAPON_CLASS_SMG )
{
return "smg";
}
else if( num & WEAPON_CLASS_MG )
{
return "mg";
}
else if( num & WEAPON_CLASS_GRENADE )
{
return "grenade";
}
else if( num & WEAPON_CLASS_HEAVY )
{
return "heavy";
}
else if( num & WEAPON_CLASS_CANNON )
{
return "cannon";
}
else if( num & WEAPON_CLASS_ITEM )
{
return "item";
}
else if( num & WEAPON_CLASS_ITEM1 )
{
return "item1";
}
else if( num & WEAPON_CLASS_ITEM2 )
{
return "item2";
}
else if( num & WEAPON_CLASS_ITEM3 )
{
return "item3";
}
else if( num & WEAPON_CLASS_ITEM4 )
{
return "item4";
}
else
{
assert( 0 );
return "";
}
}
int G_WeaponClassNameToNum
(
str name
)
{
int weaponindex = 0;
if( !name.length() )
{
gi.DPrintf( "WeaponClassNameToNum: Weapon class not specified\n" );
return 0;
}
if( !str::icmp( name, "pistol" ) )
{
weaponindex = WEAPON_CLASS_PISTOL;
}
else if( !str::icmp( name, "rifle" ) )
{
weaponindex = WEAPON_CLASS_RIFLE;
}
else if( !str::icmp( name, "smg" ) )
{
weaponindex = WEAPON_CLASS_SMG;
}
else if( !str::icmp( name, "mg" ) )
{
weaponindex = WEAPON_CLASS_MG;
}
else if( !str::icmp( name, "grenade" ) )
{
weaponindex = WEAPON_CLASS_GRENADE;
}
else if( !str::icmp( name, "heavy" ) )
{
weaponindex = WEAPON_CLASS_HEAVY;
}
else if( !str::icmp( name, "cannon" ) )
{
weaponindex = WEAPON_CLASS_CANNON;
}
else if( !str::icmp( name, "item" ) )
{
weaponindex = WEAPON_CLASS_ITEM;
}
else if( !str::icmp( name, "item1" ) )
{
weaponindex = WEAPON_CLASS_ITEM1;
}
else if( !str::icmp( name, "item2" ) )
{
weaponindex = WEAPON_CLASS_ITEM2;
}
else if( !str::icmp( name, "item3" ) )
{
weaponindex = WEAPON_CLASS_ITEM3;
}
else if( !str::icmp( name, "item4" ) )
{
weaponindex = WEAPON_CLASS_ITEM4;
}
else
{
gi.DPrintf( "WeaponClassNameToNum: Unknown Weapon class %s\n", name.c_str() );
return 0;
}
return weaponindex;
}
void G_MoveClientToIntermission
(
Entity *ent
)
{
G_DisplayScores( ent );
ent->flags |= FL_IMMOBILE;
}
void G_DisplayScores
(
Entity *ent
)
{
ent->client->ps.pm_flags |= PMF_INTERMISSION;
}
void G_HideScores
(
Entity *ent
)
{
ent->client->ps.pm_flags &= ~PMF_INTERMISSION;
}
void G_DisplayScoresToAllClients
(
void
)
{
gentity_t *ent;
int i;
for( i = 0, ent = g_entities; i < game.maxclients; ent++, i++ )
{
if( !ent->inuse || !ent->entity ) {
continue;
}
G_DisplayScores( ent->entity );
}
}
void G_HideScoresToAllClients
(
void
)
{
gentity_t *ent;
int i;
for( i = 0, ent = g_entities; i < game.maxclients; ent++, i++ )
{
if( !ent->inuse || !ent->entity ) {
continue;
}
G_HideScores( ent->entity );
}
}