summaryrefslogtreecommitdiff
path: root/src/game/g_client.c.orig
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/g_client.c.orig')
-rw-r--r--src/game/g_client.c.orig1600
1 files changed, 0 insertions, 1600 deletions
diff --git a/src/game/g_client.c.orig b/src/game/g_client.c.orig
deleted file mode 100644
index 735c59d..0000000
--- a/src/game/g_client.c.orig
+++ /dev/null
@@ -1,1600 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-This file is part of Tremulous.
-
-Tremulous 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.
-
-Tremulous 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 Tremulous; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-
-#include "g_local.h"
-
-// g_client.c -- client functions that don't happen every frame
-
-static vec3_t playerMins = {-15, -15, -24};
-static vec3_t playerMaxs = {15, 15, 32};
-
-/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) initial
-potential spawning position for deathmatch games.
-The first time a player enters the game, they will be at an 'initial' spot.
-Targets will be fired when someone spawns in on them.
-"nobots" will prevent bots from using this spot.
-"nohumans" will prevent non-bots from using this spot.
-*/
-void SP_info_player_deathmatch( gentity_t *ent )
-{
- int i;
-
- G_SpawnInt( "nobots", "0", &i);
-
- if( i )
- ent->flags |= FL_NO_BOTS;
-
- G_SpawnInt( "nohumans", "0", &i );
- if( i )
- ent->flags |= FL_NO_HUMANS;
-}
-
-/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
-equivelant to info_player_deathmatch
-*/
-void SP_info_player_start( gentity_t *ent )
-{
- ent->classname = "info_player_deathmatch";
- SP_info_player_deathmatch( ent );
-}
-
-/*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
-The intermission will be viewed from this point. Target an info_notnull for the view direction.
-*/
-void SP_info_player_intermission( gentity_t *ent )
-{
-}
-
-/*QUAKED info_alien_intermission (1 0 1) (-16 -16 -24) (16 16 32)
-The intermission will be viewed from this point. Target an info_notnull for the view direction.
-*/
-void SP_info_alien_intermission( gentity_t *ent )
-{
-}
-
-/*QUAKED info_human_intermission (1 0 1) (-16 -16 -24) (16 16 32)
-The intermission will be viewed from this point. Target an info_notnull for the view direction.
-*/
-void SP_info_human_intermission( gentity_t *ent )
-{
-}
-
-/*
-===============
-G_AddCreditToClient
-===============
-*/
-void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap )
-{
- int capAmount;
-
- if( !client )
- return;
-
- if( cap && credit > 0 )
- {
- capAmount = client->pers.teamSelection == TEAM_ALIENS ?
- ALIEN_MAX_CREDITS : HUMAN_MAX_CREDITS;
- if( client->pers.credit < capAmount )
- {
- client->pers.credit += credit;
- if( client->pers.credit > capAmount )
- client->pers.credit = capAmount;
- }
- }
- else
- client->pers.credit += credit;
-
- if( client->pers.credit < 0 )
- client->pers.credit = 0;
-
- // Copy to ps so the client can access it
- client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
-}
-
-
-/*
-=======================================================================
-
- G_SelectSpawnPoint
-
-=======================================================================
-*/
-
-/*
-================
-SpotWouldTelefrag
-
-================
-*/
-qboolean SpotWouldTelefrag( gentity_t *spot )
-{
- int i, num;
- int touch[ MAX_GENTITIES ];
- gentity_t *hit;
- vec3_t mins, maxs;
-
- VectorAdd( spot->s.origin, playerMins, mins );
- VectorAdd( spot->s.origin, playerMaxs, maxs );
- num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
-
- for( i = 0; i < num; i++ )
- {
- hit = &g_entities[ touch[ i ] ];
- //if ( hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 ) {
- if( hit->client )
- return qtrue;
- }
-
- return qfalse;
-}
-
-
-/*
-===========
-G_SelectRandomFurthestSpawnPoint
-
-Chooses a player start, deathmatch start, etc
-============
-*/
-static gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
-{
- gentity_t *spot;
- vec3_t delta;
- float dist;
- float list_dist[ 64 ];
- gentity_t *list_spot[ 64 ];
- int numSpots, rnd, i, j;
-
- numSpots = 0;
- spot = NULL;
-
- while( ( spot = G_Find( spot, FOFS( classname ), "info_player_deathmatch" ) ) != NULL )
- {
- if( SpotWouldTelefrag( spot ) )
- continue;
-
- VectorSubtract( spot->s.origin, avoidPoint, delta );
- dist = VectorLength( delta );
-
- for( i = 0; i < numSpots; i++ )
- {
- if( dist > list_dist[ i ] )
- {
- if( numSpots >= 64 )
- numSpots = 64 - 1;
-
- for( j = numSpots; j > i; j-- )
- {
- list_dist[ j ] = list_dist[ j - 1 ];
- list_spot[ j ] = list_spot[ j - 1 ];
- }
-
- list_dist[ i ] = dist;
- list_spot[ i ] = spot;
- numSpots++;
-
- if( numSpots > 64 )
- numSpots = 64;
-
- break;
- }
- }
-
- if( i >= numSpots && numSpots < 64 )
- {
- list_dist[ numSpots ] = dist;
- list_spot[ numSpots ] = spot;
- numSpots++;
- }
- }
-
- if( !numSpots )
- {
- spot = G_Find( NULL, FOFS( classname ), "info_player_deathmatch" );
-
- if( !spot )
- G_Error( "Couldn't find a spawn point" );
-
- VectorCopy( spot->s.origin, origin );
- origin[ 2 ] += 9;
- VectorCopy( spot->s.angles, angles );
- return spot;
- }
-
- // select a random spot from the spawn points furthest away
- rnd = random( ) * ( numSpots / 2 );
-
- VectorCopy( list_spot[ rnd ]->s.origin, origin );
- origin[ 2 ] += 9;
- VectorCopy( list_spot[ rnd ]->s.angles, angles );
-
- return list_spot[ rnd ];
-}
-
-
-/*
-================
-G_SelectSpawnBuildable
-
-find the nearest buildable of the right type that is
-spawned/healthy/unblocked etc.
-================
-*/
-static gentity_t *G_SelectSpawnBuildable( vec3_t preference, buildable_t buildable )
-{
- gentity_t *search, *spot;
-
- search = spot = NULL;
-
- while( ( search = G_Find( search, FOFS( classname ),
- BG_Buildable( buildable, NULL )->entityName ) ) != NULL )
- {
- if( !search->spawned )
- continue;
-
- if( search->health <= 0 )
- continue;
-
- if( !search->s.groundEntityNum )
- continue;
-
- if( search->clientSpawnTime > 0 )
- continue;
-
- if( G_CheckSpawnPoint( search->s.number, search->s.origin,
- search->s.origin2, buildable, NULL ) != NULL )
- continue;
-
- if( !spot || DistanceSquared( preference, search->s.origin ) <
- DistanceSquared( preference, spot->s.origin ) )
- spot = search;
- }
-
- return spot;
-}
-
-/*
-===========
-G_SelectSpawnPoint
-
-Chooses a player start, deathmatch start, etc
-============
-*/
-gentity_t *G_SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
-{
- return G_SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles );
-}
-
-
-/*
-===========
-G_SelectTremulousSpawnPoint
-
-Chooses a player start, deathmatch start, etc
-============
-*/
-gentity_t *G_SelectTremulousSpawnPoint( team_t team, vec3_t preference, vec3_t origin, vec3_t angles )
-{
- gentity_t *spot = NULL;
-
- if( team == TEAM_ALIENS )
- {
- if( level.numAlienSpawns <= 0 )
- return NULL;
-
- spot = G_SelectSpawnBuildable( preference, BA_A_SPAWN );
- }
- else if( team == TEAM_HUMANS )
- {
- if( level.numHumanSpawns <= 0 )
- return NULL;
-
- spot = G_SelectSpawnBuildable( preference, BA_H_SPAWN );
- }
-
- //no available spots
- if( !spot )
- return NULL;
-
- if( team == TEAM_ALIENS )
- G_CheckSpawnPoint( spot->s.number, spot->s.origin, spot->s.origin2, BA_A_SPAWN, origin );
- else if( team == TEAM_HUMANS )
- G_CheckSpawnPoint( spot->s.number, spot->s.origin, spot->s.origin2, BA_H_SPAWN, origin );
-
- VectorCopy( spot->s.angles, angles );
- angles[ ROLL ] = 0;
-
- return spot;
-
-}
-
-
-/*
-===========
-G_SelectSpectatorSpawnPoint
-
-============
-*/
-static gentity_t *G_SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
-{
- FindIntermissionPoint( );
-
- VectorCopy( level.intermission_origin, origin );
- VectorCopy( level.intermission_angle, angles );
-
- return NULL;
-}
-
-
-/*
-===========
-G_SelectAlienLockSpawnPoint
-
-Try to find a spawn point for alien intermission otherwise
-use normal intermission spawn.
-============
-*/
-gentity_t *G_SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
-{
- gentity_t *spot;
-
- spot = NULL;
- spot = G_Find( spot, FOFS( classname ), "info_alien_intermission" );
-
- if( !spot )
- return G_SelectSpectatorSpawnPoint( origin, angles );
-
- VectorCopy( spot->s.origin, origin );
- VectorCopy( spot->s.angles, angles );
-
- return spot;
-}
-
-
-/*
-===========
-G_SelectHumanLockSpawnPoint
-
-Try to find a spawn point for human intermission otherwise
-use normal intermission spawn.
-============
-*/
-gentity_t *G_SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
-{
- gentity_t *spot;
-
- spot = NULL;
- spot = G_Find( spot, FOFS( classname ), "info_human_intermission" );
-
- if( !spot )
- return G_SelectSpectatorSpawnPoint( origin, angles );
-
- VectorCopy( spot->s.origin, origin );
- VectorCopy( spot->s.angles, angles );
-
- return spot;
-}
-
-
-/*
-=======================================================================
-
-BODYQUE
-
-=======================================================================
-*/
-
-
-/*
-=============
-BodySink
-
-After sitting around for five seconds, fall into the ground and dissapear
-=============
-*/
-static void BodySink( gentity_t *ent )
-{
- //run on first BodySink call
- if( !ent->active )
- {
- ent->active = qtrue;
-
- //sinking bodies can't be infested
- ent->killedBy = ent->s.misc = MAX_CLIENTS;
- ent->timestamp = level.time;
- }
-
- if( level.time - ent->timestamp > 6500 )
- {
- G_FreeEntity( ent );
- return;
- }
-
- ent->nextthink = level.time + 100;
- ent->s.pos.trBase[ 2 ] -= 1;
-}
-
-
-/*
-=============
-SpawnCorpse
-
-A player is respawning, so make an entity that looks
-just like the existing corpse to leave behind.
-=============
-*/
-static void SpawnCorpse( gentity_t *ent )
-{
- gentity_t *body;
- int contents;
- vec3_t origin, dest;
- trace_t tr;
- float vDiff;
-
- VectorCopy( ent->r.currentOrigin, origin );
-
- trap_UnlinkEntity( ent );
-
- // if client is in a nodrop area, don't leave the body
- contents = trap_PointContents( origin, -1 );
- if( contents & CONTENTS_NODROP )
- return;
-
- body = G_Spawn( );
-
- VectorCopy( ent->s.apos.trBase, body->s.angles );
- body->s.eFlags = EF_DEAD;
- body->s.eType = ET_CORPSE;
- body->s.number = body - g_entities;
- body->timestamp = level.time;
- body->s.event = 0;
- body->r.contents = CONTENTS_CORPSE;
- body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ];
- body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;
-
- if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
- body->classname = "humanCorpse";
- else
- body->classname = "alienCorpse";
-
- body->s.misc = MAX_CLIENTS;
-
- body->think = BodySink;
- body->nextthink = level.time + 20000;
-
- body->s.legsAnim = ent->s.legsAnim;
-
- if( !body->nonSegModel )
- {
- switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
- {
- case BOTH_DEATH1:
- case BOTH_DEAD1:
- body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
- break;
- case BOTH_DEATH2:
- case BOTH_DEAD2:
- body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
- break;
- case BOTH_DEATH3:
- case BOTH_DEAD3:
- default:
- body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
- break;
- }
- }
- else
- {
- switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
- {
- case NSPA_DEATH1:
- case NSPA_DEAD1:
- body->s.legsAnim = NSPA_DEAD1;
- break;
- case NSPA_DEATH2:
- case NSPA_DEAD2:
- body->s.legsAnim = NSPA_DEAD2;
- break;
- case NSPA_DEATH3:
- case NSPA_DEAD3:
- default:
- body->s.legsAnim = NSPA_DEAD3;
- break;
- }
- }
-
- body->takedamage = qfalse;
-
- body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ];
- ent->health = 0;
-
- //change body dimensions
- BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs );
- vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ];
-
- //drop down to match the *model* origins of ent and body
- VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff );
- trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask );
- VectorCopy( tr.endpos, origin );
-
- G_SetOrigin( body, origin );
- VectorCopy( origin, body->s.origin );
- body->s.pos.trType = TR_GRAVITY;
- body->s.pos.trTime = level.time;
- VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );
-
- VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
- trap_LinkEntity( body );
-}
-
-//======================================================================
-
-
-/*
-==================
-G_SetClientViewAngle
-
-==================
-*/
-void G_SetClientViewAngle( gentity_t *ent, vec3_t angle )
-{
- int i;
-
- // set the delta angle
- for( i = 0; i < 3; i++ )
- {
- int cmdAngle;
-
- cmdAngle = ANGLE2SHORT( angle[ i ] );
- ent->client->ps.delta_angles[ i ] = cmdAngle - ent->client->pers.cmd.angles[ i ];
- }
-
- VectorCopy( angle, ent->s.angles );
- VectorCopy( ent->s.angles, ent->client->ps.viewangles );
-}
-
-/*
-================
-respawn
-================
-*/
-void respawn( gentity_t *ent )
-{
- int i;
-
- SpawnCorpse( ent );
-
- // Clients can't respawn - they must go through the class cmd
- ent->client->pers.classSelection = PCL_NONE;
- ClientSpawn( ent, NULL, NULL, NULL );
-
- // stop any following clients that don't have sticky spec on
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW &&
- level.clients[ i ].sess.spectatorClient == ent - g_entities )
- {
- if( !( level.clients[ i ].pers.stickySpec ) )
- {
- if( !G_FollowNewClient( &g_entities[ i ], 1 ) )
- G_StopFollowing( &g_entities[ i ] );
- }
- else
- G_FollowLockView( &g_entities[ i ] );
- }
- }
-}
-
-static qboolean G_IsEmoticon( const char *s, qboolean *escaped )
-{
- int i, j;
- const char *p = s;
- char emoticon[ MAX_EMOTICON_NAME_LEN ] = {""};
- qboolean escape = qfalse;
-
- if( *p != '[' )
- return qfalse;
- p++;
- if( *p == '[' )
- {
- escape = qtrue;
- p++;
- }
- i = 0;
- while( *p && i < ( MAX_EMOTICON_NAME_LEN - 1 ) )
- {
- if( *p == ']' )
- {
- for( j = 0; j < level.emoticonCount; j++ )
- {
- if( !Q_stricmp( emoticon, level.emoticons[ j ].name ) )
- {
- *escaped = escape;
- return qtrue;
- }
- }
- return qfalse;
- }
- emoticon[ i++ ] = *p;
- emoticon[ i ] = '\0';
- p++;
- }
- return qfalse;
-}
-
-/*
-===========
-G_ClientCleanName
-============
-*/
-static void G_ClientCleanName( const char *in, char *out, int outSize )
-{
- int len, colorlessLen;
- char *p;
- int spaces;
- qboolean escaped;
- qboolean invalid = qfalse;
-
- //save room for trailing null byte
- outSize--;
-
- len = 0;
- colorlessLen = 0;
- p = out;
- *p = 0;
- spaces = 0;
-
- for( ; *in; in++ )
- {
- // don't allow leading spaces
- if( colorlessLen == 0 && *in == ' ' )
- continue;
-
- // don't allow nonprinting characters or (dead) console keys
- if( *in < ' ' || *in > '}' || *in == '`' )
- continue;
-
- // check colors
- if( Q_IsColorString( in ) )
- {
- in++;
-
- // make sure room in dest for both chars
- if( len > outSize - 2 )
- break;
-
- *out++ = Q_COLOR_ESCAPE;
-
- *out++ = *in;
-
- len += 2;
- continue;
- }
- else if( !g_emoticonsAllowedInNames.integer && G_IsEmoticon( in, &escaped ) )
- {
- // make sure room in dest for both chars
- if( len > outSize - 2 )
- break;
-
- *out++ = '[';
- *out++ = '[';
- len += 2;
- if( escaped )
- in++;
- continue;
- }
-
- // don't allow too many consecutive spaces
- if( *in == ' ' )
- {
- spaces++;
- if( spaces > 3 )
- continue;
- }
- else
- spaces = 0;
-
- if( len > outSize - 1 )
- break;
-
- *out++ = *in;
- colorlessLen++;
- len++;
- }
-
- *out = 0;
-
- // don't allow names beginning with "[skipnotify]" because it messes up /ignore-related code
- if( !Q_stricmpn( p, "[skipnotify]", 12 ) )
- invalid = qtrue;
-
- // don't allow comment-beginning strings because it messes up various parsers
- if( strstr( p, "//" ) || strstr( p, "/*" ) )
- invalid = qtrue;
-
- // don't allow empty names
- if( *p == 0 || colorlessLen == 0 )
- invalid = qtrue;
-
- // if something made the name bad, put them back to UnnamedPlayer
- if( invalid )
- Q_strncpyz( p, "UnnamedPlayer", outSize );
-}
-
-
-/*
-======================
-G_NonSegModel
-
-Reads an animation.cfg to check for nonsegmentation
-======================
-*/
-static qboolean G_NonSegModel( const char *filename )
-{
- char *text_p;
- int len;
- char *token;
- char text[ 20000 ];
- fileHandle_t f;
-
- // load the file
- len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if( !f )
- {
- G_Printf( "File not found: %s\n", filename );
- return qfalse;
- }
-
- if( len < 0 )
- return qfalse;
-
- if( len == 0 || len >= sizeof( text ) - 1 )
- {
- trap_FS_FCloseFile( f );
- G_Printf( "File %s is %s\n", filename, len == 0 ? "empty" : "too long" );
- return qfalse;
- }
-
- trap_FS_Read( text, len, f );
- text[ len ] = 0;
- trap_FS_FCloseFile( f );
-
- // parse the text
- text_p = text;
-
- // read optional parameters
- while( 1 )
- {
- token = COM_Parse( &text_p );
-
- //EOF
- if( !token[ 0 ] )
- break;
-
- if( !Q_stricmp( token, "nonsegmented" ) )
- return qtrue;
- }
-
- return qfalse;
-}
-
-/*
-===========
-ClientUserInfoChanged
-
-Called from ClientConnect when the player first connects and
-directly by the server system when the player updates a userinfo variable.
-
-The game can override any of the settings and call trap_SetUserinfo
-if desired.
-============
-*/
-char *ClientUserinfoChanged( int clientNum, qboolean forceName )
-{
- gentity_t *ent;
- char *s;
- char model[ MAX_QPATH ];
- char buffer[ MAX_QPATH ];
- char filename[ MAX_QPATH ];
- char oldname[ MAX_NAME_LENGTH ];
- char newname[ MAX_NAME_LENGTH ];
- char err[ MAX_STRING_CHARS ];
- qboolean revertName = qfalse;
- gclient_t *client;
- char userinfo[ MAX_INFO_STRING ];
-
- ent = g_entities + clientNum;
- client = ent->client;
-
- trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
-
- // check for malformed or illegal info strings
- if( !Info_Validate(userinfo) )
- {
- trap_SendServerCommand( ent - g_entities,
- "disconnect \"illegal or malformed userinfo\n\"" );
- trap_DropClient( ent - g_entities,
- "dropped: illegal or malformed userinfo");
- return "Illegal or malformed userinfo";
- }
- // If their userinfo overflowed, tremded is in the process of disconnecting them.
- // If we send our own disconnect, it won't work, so just return to prevent crashes later
- // in this function. This check must come after the Info_Validate call.
- else if( !userinfo[ 0 ] )
- return "Empty (overflowed) userinfo";
-
- // stickyspec toggle
- s = Info_ValueForKey( userinfo, "cg_stickySpec" );
- client->pers.stickySpec = atoi( s ) != 0;
-
- // set name
- Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
- s = Info_ValueForKey( userinfo, "name" );
- G_ClientCleanName( s, newname, sizeof( newname ) );
-
- if( strcmp( oldname, newname ) )
- {
- if( !forceName && client->pers.namelog->nameChangeTime &&
- level.time - client->pers.namelog->nameChangeTime <=
- g_minNameChangePeriod.value * 1000 )
- {
- trap_SendServerCommand( ent - g_entities, va(
- "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"",
- g_minNameChangePeriod.integer ) );
- revertName = qtrue;
- }
- else if( !forceName && g_maxNameChanges.integer > 0 &&
- client->pers.namelog->nameChanges >= g_maxNameChanges.integer )
- {
- trap_SendServerCommand( ent - g_entities, va(
- "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
- g_maxNameChanges.integer ) );
- revertName = qtrue;
- }
- else if( !forceName && client->pers.namelog->muted )
- {
- trap_SendServerCommand( ent - g_entities,
- "print \"You cannot change your name while you are muted\n\"" );
- revertName = qtrue;
- }
- else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
- {
- trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) );
- revertName = qtrue;
- }
-
- if( revertName )
- {
- Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer",
- sizeof( client->pers.netname ) );
- Info_SetValueForKey( userinfo, "name", oldname );
- trap_SetUserinfo( clientNum, userinfo );
- }
- else
- {
- G_CensorString( client->pers.netname, newname,
- sizeof( client->pers.netname ), ent );
- if( !forceName && client->pers.connected == CON_CONNECTED )
- {
- client->pers.namelog->nameChangeTime = level.time;
- client->pers.namelog->nameChanges++;
- }
- if( *oldname )
- {
- G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n",
- clientNum, client->pers.ip.str, client->pers.guid,
- oldname, client->pers.netname,
- DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
- }
- }
- G_namelog_update_name( client );
- }
-
- if( client->pers.classSelection == PCL_NONE )
- {
- //This looks hacky and frankly it is. The clientInfo string needs to hold different
- //model details to that of the spawning class or the info change will not be
- //registered and an axis appears instead of the player model. There is zero chance
- //the player can spawn with the battlesuit, hence this choice.
- Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName,
- BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName );
- }
- else
- {
- Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName,
- BG_ClassConfig( client->pers.classSelection )->skinName );
-
- //model segmentation
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg",
- BG_ClassConfig( client->pers.classSelection )->modelName );
-
- if( G_NonSegModel( filename ) )
- client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL;
- }
- Q_strncpyz( model, buffer, sizeof( model ) );
-
- // wallwalk follow
- s = Info_ValueForKey( userinfo, "cg_wwFollow" );
-
- if( atoi( s ) )
- client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW;
-
- // wallwalk toggle
- s = Info_ValueForKey( userinfo, "cg_wwToggle" );
-
- if( atoi( s ) )
- client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE;
-
- // always sprint
- s = Info_ValueForKey( userinfo, "cg_sprintToggle" );
-
- if( atoi( s ) )
- client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE;
-
- // fly speed
- s = Info_ValueForKey( userinfo, "cg_flySpeed" );
-
- if( *s )
- client->pers.flySpeed = atoi( s );
- else
- client->pers.flySpeed = BG_Class( PCL_NONE )->speed;
-
- // disable blueprint errors
- s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" );
-
- if( atoi( s ) )
- client->pers.disableBlueprintErrors = qtrue;
- else
- client->pers.disableBlueprintErrors = qfalse;
-
- // teamInfo
- s = Info_ValueForKey( userinfo, "teamoverlay" );
-
- if( atoi( s ) != 0 )
- client->pers.teamInfo = qtrue;
- else
- client->pers.teamInfo = qfalse;
-
- s = Info_ValueForKey( userinfo, "cg_unlagged" );
- if( !s[0] || atoi( s ) != 0 )
- client->pers.useUnlagged = qtrue;
- else
- client->pers.useUnlagged = qfalse;
-
- Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ),
- sizeof( client->pers.voice ) );
-
- // send over a subset of the userinfo keys so other clients can
- // print scoreboards, display models, and play custom sounds
-
- Com_sprintf( userinfo, sizeof( userinfo ),
- "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s",
- client->pers.netname, client->pers.teamSelection, model,
- Com_ClientListString( &client->sess.ignoreList ),
- client->pers.voice );
-
- trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );
-
- /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/
-
- return NULL;
-}
-
-
-/*
-===========
-ClientConnect
-
-Called when a player begins connecting to the server.
-Called again for every map change or tournement restart.
-
-The session information will be valid after exit.
-
-Return NULL if the client should be allowed, otherwise return
-a string with the reason for denial.
-
-Otherwise, the client will be sent the current gamestate
-and will eventually get to ClientBegin.
-
-firstTime will be qtrue the very first time a client connects
-to the server machine, but qfalse on map changes and tournement
-restarts.
-============
-*/
-char *ClientConnect( int clientNum, qboolean firstTime )
-{
- char *value;
- char *userInfoError;
- gclient_t *client;
- char userinfo[ MAX_INFO_STRING ];
- gentity_t *ent;
- char reason[ MAX_STRING_CHARS ] = {""};
- int i;
-
- ent = &g_entities[ clientNum ];
- client = &level.clients[ clientNum ];
-
- // ignore if client already connected
- if( client->pers.connected != CON_DISCONNECTED )
- return NULL;
-
- ent->client = client;
- memset( client, 0, sizeof( *client ) );
-
- trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
-
- value = Info_ValueForKey( userinfo, "cl_guid" );
- Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) );
-
- value = Info_ValueForKey( userinfo, "ip" );
- // check for local client
- if( !strcmp( value, "localhost" ) )
- client->pers.localClient = qtrue;
- G_AddressParse( value, &client->pers.ip );
-
- client->pers.admin = G_admin_admin( client->pers.guid );
-
- // check for admin ban
- if( G_admin_ban_check( ent, reason, sizeof( reason ) ) )
- {
- return va( "%s", reason );
- }
-
- // check for a password
- value = Info_ValueForKey( userinfo, "password" );
-
- if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) &&
- strcmp( g_password.string, value ) != 0 )
- return "Invalid password";
-
- // add guid to session so we don't have to keep parsing userinfo everywhere
- for( i = 0; i < sizeof( client->pers.guid ) - 1 &&
- isxdigit( client->pers.guid[ i ] ); i++ );
-
- if( i < sizeof( client->pers.guid ) - 1 )
- return "Invalid GUID";
-
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].pers.connected == CON_DISCONNECTED )
- continue;
-
- if( !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) )
- {
- if( !G_ClientIsLagging( level.clients + i ) )
- {
- trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" );
- return "Duplicate GUID";
- }
- trap_DropClient( i, "Ghost" );
- }
- }
-
- client->pers.connected = CON_CONNECTING;
-
- // read or initialize the session data
- if( firstTime || level.newSession )
- G_InitSessionData( client, userinfo );
-
- G_ReadSessionData( client );
-
- // get and distribute relevent paramters
- G_namelog_connect( client );
- userInfoError = ClientUserinfoChanged( clientNum, qfalse );
- if( userInfoError != NULL )
- return userInfoError;
-
- G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%c%s%c^7\"\n",
- clientNum, client->pers.ip.str, client->pers.guid,
- client->pers.netname,
- DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
-
- // don't do the "xxx connected" messages if they were caried over from previous level
- if( firstTime )
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"",
- client->pers.netname ) );
-
- if( client->pers.admin )
- G_admin_authlog( ent );
-
- // count current clients and rank for scoreboard
- CalculateRanks( );
-
-
- // if this is after !restart keepteams or !restart switchteams, apply said selection
- if ( client->sess.restartTeam != TEAM_NONE )
- {
- G_ChangeTeam( ent, client->sess.restartTeam );
- client->sess.restartTeam = TEAM_NONE;
- }
-
-
- return NULL;
-}
-
-/*
-===========
-ClientBegin
-
-called when a client has finished connecting, and is ready
-to be placed into the level. This will happen every level load,
-and on transition between teams, but doesn't happen on respawns
-============
-*/
-void ClientBegin( int clientNum )
-{
- gentity_t *ent;
- gclient_t *client;
- int flags;
-
- ent = g_entities + clientNum;
-
- client = level.clients + clientNum;
-
- // ignore if client already entered the game
- if( client->pers.connected != CON_CONNECTING )
- return;
-
- if( ent->r.linked )
- trap_UnlinkEntity( ent );
-
- G_InitGentity( ent );
- ent->touch = 0;
- ent->pain = 0;
- ent->client = client;
-
- client->pers.connected = CON_CONNECTED;
- client->pers.enterTime = level.time;
-
- // save eflags around this, because changing teams will
- // cause this to happen with a valid entity, and we
- // want to make sure the teleport bit is set right
- // so the viewpoint doesn't interpolate through the
- // world to the new position
- flags = client->ps.eFlags;
- memset( &client->ps, 0, sizeof( client->ps ) );
- memset( &client->pmext, 0, sizeof( client->pmext ) );
- client->ps.eFlags = flags;
-
- // locate ent at a spawn point
- ClientSpawn( ent, NULL, NULL, NULL );
-
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname ) );
-
- G_namelog_restore( client );
-
- G_LogPrintf( "ClientBegin: %i\n", clientNum );
-
- // count current clients and rank for scoreboard
- CalculateRanks( );
-
- // send the client a list of commands that can be used
- G_ListCommands( ent );
-
- // reset cuboidSelection
- client->cuboidSelection[ 0 ] =
- client->cuboidSelection[ 1 ] =
- client->cuboidSelection[ 2 ] = 32;
-}
-
-/*
-===========
-ClientSpawn
-
-Called every time a client is placed fresh in the world:
-after the first ClientBegin, and after each respawn
-Initializes all non-persistant parts of playerState
-============
-*/
-void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles )
-{
- int index;
- vec3_t spawn_origin, spawn_angles;
- gclient_t *client;
- int i;
- clientPersistant_t saved;
- clientSession_t savedSess;
- int persistant[ MAX_PERSISTANT ];
- gentity_t *spawnPoint = NULL;
- int flags;
- int savedPing;
- int teamLocal;
- int eventSequence;
- char userinfo[ MAX_INFO_STRING ];
- vec3_t up = { 0.0f, 0.0f, 1.0f };
- int maxAmmo, maxClips;
- weapon_t weapon;
-
- index = ent - g_entities;
- client = ent->client;
-
- teamLocal = client->pers.teamSelection;
-
- //if client is dead and following teammate, stop following before spawning
- if( client->sess.spectatorClient != -1 )
- {
- client->sess.spectatorClient = -1;
- client->sess.spectatorState = SPECTATOR_FREE;
- }
-
- // only start client if chosen a class and joined a team
- if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE )
- client->sess.spectatorState = SPECTATOR_FREE;
- else if( client->pers.classSelection == PCL_NONE )
- client->sess.spectatorState = SPECTATOR_LOCKED;
-
- // if client is dead and following teammate, stop following before spawning
- if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
- G_StopFollowing( ent );
-
- if( origin != NULL )
- VectorCopy( origin, spawn_origin );
-
- if( angles != NULL )
- VectorCopy( angles, spawn_angles );
-
- // find a spawn point
- // do it before setting health back up, so farthest
- // ranging doesn't count this client
- if( client->sess.spectatorState != SPECTATOR_NOT )
- {
- if( teamLocal == TEAM_NONE )
- spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
- else if( teamLocal == TEAM_ALIENS )
- spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
- else if( teamLocal == TEAM_HUMANS )
- spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
- }
- else
- {
- if( spawn == NULL )
- {
- G_Error( "ClientSpawn: spawn is NULL\n" );
- return;
- }
-
- spawnPoint = spawn;
-
- if( ent != spawn )
- {
- //start spawn animation on spawnPoint
- G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue );
-
- if( spawnPoint->buildableTeam == TEAM_ALIENS )
- spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
- else if( spawnPoint->buildableTeam == TEAM_HUMANS )
- spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
- }
- }
-
- // toggle the teleport bit so the client knows to not lerp
- flags = ( ent->client->ps.eFlags & EF_TELEPORT_BIT ) ^ EF_TELEPORT_BIT;
- G_UnlaggedClear( ent );
-
- // clear everything but the persistant data
-
- saved = client->pers;
- savedSess = client->sess;
- savedPing = client->ps.ping;
-
- for( i = 0; i < MAX_PERSISTANT; i++ )
- persistant[ i ] = client->ps.persistant[ i ];
-
- eventSequence = client->ps.eventSequence;
- memset( client, 0, sizeof( *client ) );
-
- client->pers = saved;
- client->sess = savedSess;
- client->ps.ping = savedPing;
- client->lastkilled_client = -1;
-
- for( i = 0; i < MAX_PERSISTANT; i++ )
- client->ps.persistant[ i ] = persistant[ i ];
-
- client->ps.eventSequence = eventSequence;
-
- // increment the spawncount so the client will detect the respawn
- client->ps.persistant[ PERS_SPAWN_COUNT ]++;
- client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState;
-
- client->airOutTime = level.time + 12000;
-
- trap_GetUserinfo( index, userinfo, sizeof( userinfo ) );
- client->ps.eFlags = flags;
-
- //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection );
-
- ent->s.groundEntityNum = ENTITYNUM_NONE;
- ent->client = &level.clients[ index ];
- ent->takedamage = qtrue;
- ent->inuse = qtrue;
- ent->classname = "player";
- ent->r.contents = CONTENTS_BODY;
- ent->clipmask = MASK_PLAYERSOLID;
- ent->die = player_die;
- ent->waterlevel = 0;
- ent->watertype = 0;
- ent->flags = 0;
-
- // calculate each client's acceleration
- ent->evaluateAcceleration = qtrue;
-
- client->ps.stats[ STAT_MISC ] = 0;
- client->buildTimer = 0;
-
- client->ps.eFlags = flags;
- client->ps.clientNum = index;
-
- BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );
-
- if( client->sess.spectatorState == SPECTATOR_NOT )
- client->ps.stats[ STAT_MAX_HEALTH ] =
- BG_Class( ent->client->pers.classSelection )->health;
- else
- client->ps.stats[ STAT_MAX_HEALTH ] = 100;
-
- // clear entity values
- if( ent->client->pers.classSelection == PCL_HUMAN )
- {
- BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats );
- weapon = client->pers.humanItemSelection;
- }
- else if( client->sess.spectatorState == SPECTATOR_NOT )
- weapon = BG_Class( ent->client->pers.classSelection )->startWeapon;
- else
- weapon = WP_NONE;
-
- maxAmmo = BG_Weapon( weapon )->maxAmmo;
- maxClips = BG_Weapon( weapon )->maxClips;
- client->ps.stats[ STAT_WEAPON ] = weapon;
- client->ps.ammo = maxAmmo;
- client->ps.clips = maxClips;
-
- // We just spawned, not changing weapons
- client->ps.persistant[ PERS_NEWWEAPON ] = 0;
-
- ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection;
- ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection;
-
- ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
- ent->client->ps.stats[ STAT_STATE ] = 0;
- VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f );
-
- // health will count down towards max_health
- ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25;
-
- //if evolving scale health
- if( ent == spawn )
- {
- ent->health *= ent->client->pers.evolveHealthFraction;
- client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction;
- }
-
- //clear the credits array
- for( i = 0; i < MAX_CLIENTS; i++ )
- ent->credits[ i ] = 0;
-
- client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
-
- G_SetOrigin( ent, spawn_origin );
- VectorCopy( spawn_origin, client->ps.origin );
-
-#define UP_VEL 150.0f
-#define F_VEL 50.0f
-
- //give aliens some spawn velocity
- if( client->sess.spectatorState == SPECTATOR_NOT &&
- client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
- {
- if( ent == spawn )
- {
- //evolution particle system
- G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) );
- }
- else
- {
- spawn_angles[ YAW ] += 180.0f;
- AngleNormalize360( spawn_angles[ YAW ] );
-
- if( spawnPoint->s.origin2[ 2 ] > 0.0f )
- {
- vec3_t forward, dir;
-
- AngleVectors( spawn_angles, forward, NULL, NULL );
- VectorScale( forward, F_VEL, forward );
- VectorAdd( spawnPoint->s.origin2, forward, dir );
- VectorNormalize( dir );
-
- VectorScale( dir, UP_VEL, client->ps.velocity );
- }
-
- G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
- }
- }
- else if( client->sess.spectatorState == SPECTATOR_NOT &&
- client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
- {
- spawn_angles[ YAW ] += 180.0f;
- AngleNormalize360( spawn_angles[ YAW ] );
- }
-
- // the respawned flag will be cleared after the attack and jump keys come up
- client->ps.pm_flags |= PMF_RESPAWNED;
-
- trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
- G_SetClientViewAngle( ent, spawn_angles );
-
- if( client->sess.spectatorState == SPECTATOR_NOT )
- {
- trap_LinkEntity( ent );
-
- // force the base weapon up
- if( client->pers.teamSelection == TEAM_HUMANS )
- G_ForceWeaponChange( ent, weapon );
-
- client->ps.weaponstate = WEAPON_READY;
- }
-
- // don't allow full run speed for a bit
- client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
- client->ps.pm_time = 100;
-
- client->respawnTime = level.time;
- ent->nextRegenTime = level.time;
-
- client->inactivityTime = level.time + g_inactivity.integer * 1000;
- client->latched_buttons = 0;
-
- // set default animations
- client->ps.torsoAnim = TORSO_STAND;
- client->ps.legsAnim = LEGS_IDLE;
-
- if( level.intermissiontime )
- MoveClientToIntermission( ent );
- else
- {
- // fire the targets of the spawn point
- if( !spawn )
- G_UseTargets( spawnPoint, ent );
-
- // select the highest weapon number available, after any
- // spawn given items have fired
- client->ps.weapon = 1;
-
- for( i = WP_NUM_WEAPONS - 1; i > 0 ; i-- )
- {
- if( BG_InventoryContainsWeapon( i, client->ps.stats ) )
- {
- client->ps.weapon = i;
- break;
- }
- }
- }
-
- client->lastRantBombTime = level.time;
-
- // run a client frame to drop exactly to the floor,
- // initialize animations and other things
- client->ps.commandTime = level.time - 100;
- ent->client->pers.cmd.serverTime = level.time;
- ClientThink( ent-g_entities );
-
- // positively link the client, even if the command times are weird
- if( client->sess.spectatorState == SPECTATOR_NOT )
- {
- BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
- VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
- trap_LinkEntity( ent );
- }
-
- // must do this here so the number of active clients is calculated
- CalculateRanks( );
-
- // run the presend to set anything else
- ClientEndFrame( ent );
-
- // clear entity state values
- BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
-}
-
-
-/*
-===========
-ClientDisconnect
-
-Called when a player drops from the server.
-Will not be called between levels.
-
-This should NOT be called directly by any game logic,
-call trap_DropClient(), which will call this and do
-server system housekeeping.
-============
-*/
-void ClientDisconnect( int clientNum )
-{
- gentity_t *ent;
- gentity_t *tent;
- int i;
-
- ent = g_entities + clientNum;
-
- if( !ent->client || ent->client->pers.connected == CON_DISCONNECTED )
- return;
-
- G_LeaveTeam( ent );
- G_namelog_disconnect( ent->client );
- G_Vote( ent, TEAM_NONE, qfalse );
-
- // stop any following clients
- for( i = 0; i < level.maxclients; i++ )
- {
- // remove any /ignore settings for this clientNum
- Com_ClientListRemove( &level.clients[ i ].sess.ignoreList, clientNum );
- }
-
- // send effect if they were completely connected
- if( ent->client->pers.connected == CON_CONNECTED &&
- ent->client->sess.spectatorState == SPECTATOR_NOT )
- {
- tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
- tent->s.clientNum = ent->s.clientNum;
- }
-
- G_LogPrintf( "ClientDisconnect: %i [%s] (%s) \"%s^7\"\n", clientNum,
- ent->client->pers.ip.str, ent->client->pers.guid, ent->client->pers.netname );
-
- trap_UnlinkEntity( ent );
- ent->s.modelindex = 0;
- ent->inuse = qfalse;
- ent->classname = "disconnected";
- ent->client->pers.connected = CON_DISCONNECTED;
- ent->client->sess.spectatorState =
- ent->client->ps.persistant[ PERS_SPECSTATE ] = SPECTATOR_NOT;
-
- trap_SetConfigstring( CS_PLAYERS + clientNum, "");
-
- CalculateRanks( );
-}
-
-/*
-===========
-G_RelayCuboidToSpectators
-
-Called everytime a player changes his cuboid size.
-A server command is issued to everyone spectating him
-so that their clients can know the cuboid size as well.
-============
-*/
-void G_RelayCuboidToSpectators(gentity_t *self)
-{
-}
-