sof2-sdk/code/game/g_cmds.c

2042 lines
46 KiB
C

// Copyright (C) 2001-2002 Raven Software.
//
#include "g_local.h"
#include "../../ui/menudef.h"
int AcceptBotCommand(char *cmd, gentity_t *pl);
/*
==================
DeathmatchScoreboardMessage
==================
*/
void DeathmatchScoreboardMessage( gentity_t *ent )
{
char entry[1024];
char string[1400];
int stringlength;
int i, j;
gclient_t *cl;
int numSorted;
// send the latest information on all clients
string[0] = 0;
stringlength = 0;
numSorted = level.numConnectedClients;
for (i=0 ; i < numSorted ; i++)
{
int ping;
cl = &level.clients[level.sortedClients[i]];
if ( cl->pers.connected == CON_CONNECTING )
{
ping = -1;
}
else
{
ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
}
Com_sprintf (entry, sizeof(entry),
" %i %i %i %i %i %i %i %i %i",
level.sortedClients[i],
cl->sess.score,
cl->sess.kills,
cl->sess.deaths,
ping,
(level.time - cl->pers.enterTime)/60000,
(cl->sess.ghost || cl->ps.pm_type == PM_DEAD) ? qtrue : qfalse,
g_entities[level.sortedClients[i]].s.gametypeitems,
g_teamkillDamageMax.integer ? 100 * cl->sess.teamkillDamage / g_teamkillDamageMax.integer : 0
);
j = strlen(entry);
if (stringlength + j > 1022 )
{
break;
}
strcpy (string + stringlength, entry);
stringlength += j;
}
trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i,
level.teamScores[TEAM_RED],
level.teamScores[TEAM_BLUE],
string ) );
}
/*
==================
Cmd_Score_f
Request current scoreboard information
==================
*/
void Cmd_Score_f( gentity_t *ent )
{
DeathmatchScoreboardMessage( ent );
}
/*
==================
CheatsOk
==================
*/
qboolean CheatsOk( gentity_t *ent ) {
if ( !g_cheats.integer ) {
trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
return qfalse;
}
if ( ent->health <= 0 ) {
trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\""));
return qfalse;
}
return qtrue;
}
/*
==================
ConcatArgs
==================
*/
char *ConcatArgs( int start ) {
int i, c, tlen;
static char line[MAX_STRING_CHARS];
int len;
char arg[MAX_STRING_CHARS];
len = 0;
c = trap_Argc();
for ( i = start ; i < c ; i++ ) {
trap_Argv( i, arg, sizeof( arg ) );
tlen = strlen( arg );
if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
break;
}
memcpy( line + len, arg, tlen );
len += tlen;
if ( i != c - 1 ) {
line[len] = ' ';
len++;
}
}
line[len] = 0;
return line;
}
/*
==================
SanitizeString
Remove case and control characters
==================
*/
void SanitizeString( char *in, char *out ) {
while ( *in ) {
if ( *in == 27 ) {
in += 2; // skip color code
continue;
}
if ( *in < 32 ) {
in++;
continue;
}
*out++ = tolower( *in++ );
}
*out = 0;
}
/*
==================
G_ClientNumberFromName
Finds the client number of the client with the given name
==================
*/
int G_ClientNumberFromName ( const char* name )
{
char s2[MAX_STRING_CHARS];
char n2[MAX_STRING_CHARS];
int i;
gclient_t* cl;
// check for a name match
SanitizeString( (char*)name, s2 );
for ( i=0, cl=level.clients ; i < level.numConnectedClients ; i++, cl++ )
{
SanitizeString( cl->pers.netname, n2 );
if ( !strcmp( n2, s2 ) )
{
return i;
}
}
return -1;
}
/*
==================
ClientNumberFromString
Returns a player number for either a number or name string
Returns -1 if invalid
==================
*/
int ClientNumberFromString( gentity_t *to, char *s ) {
gclient_t *cl;
int idnum;
char s2[MAX_STRING_CHARS];
char n2[MAX_STRING_CHARS];
// numeric values are just slot numbers
if (s[0] >= '0' && s[0] <= '9') {
idnum = atoi( s );
if ( idnum < 0 || idnum >= level.maxclients ) {
trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum));
return -1;
}
cl = &level.clients[idnum];
if ( cl->pers.connected != CON_CONNECTED ) {
trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum));
return -1;
}
return idnum;
}
// check for a name match
SanitizeString( s, s2 );
for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) {
if ( cl->pers.connected != CON_CONNECTED ) {
continue;
}
SanitizeString( cl->pers.netname, n2 );
if ( !strcmp( n2, s2 ) ) {
return idnum;
}
}
trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s));
return -1;
}
/*
==================
Cmd_Drop_f
Drops the currenty selected weapon
==================
*/
void Cmd_Drop_f ( gentity_t* ent )
{
gentity_t* dropped;
// spectators cant drop anything since they dont have anything
if ( ent->client->sess.team == TEAM_SPECTATOR )
{
return;
}
// Ghosts and followers cant drop stuff
if ( ent->client->ps.pm_flags & (PMF_GHOST|PMF_FOLLOW) )
{
return;
}
// Drop the weapon the client wanted to drop
dropped = G_DropWeapon ( ent, atoi(ConcatArgs( 1 )), 3000 );
if ( !dropped )
{
return;
}
}
/*
==================
Cmd_DropItem_f
Drops the gametype items the player is carrying
==================
*/
void Cmd_DropItem_f ( gentity_t* ent )
{
// spectators cant drop anything since they dont have anything
if ( ent->client->sess.team == TEAM_SPECTATOR )
{
return;
}
// Ghosts and followers cant drop stuff
if ( ent->client->ps.pm_flags & (PMF_GHOST|PMF_FOLLOW) )
{
return;
}
// Nothing to drop
if ( !ent->client->ps.stats[STAT_GAMETYPE_ITEMS] )
{
return;
}
G_DropGametypeItems ( ent );
}
/*
==================
Cmd_Give_f
Give items to a client
==================
*/
void Cmd_Give_f (gentity_t *ent)
{
char *name;
gitem_t *it;
int i;
qboolean give_all;
gentity_t *it_ent;
trace_t trace;
char arg[MAX_QPATH];
int start;
int end;
int l;
trap_Argv( 1, arg, sizeof( arg ) );
if ( !Q_stricmp ( arg, "me" ) )
{
start = ent->s.number;
end = start + 1;
}
else if ( !Q_stricmp ( arg, "all" ) )
{
start = 0;
end = MAX_CLIENTS;
}
else
{
start = atoi ( arg );
end = start + 1;
}
for ( l = start; l < end; l ++ )
{
ent = &g_entities[l];
if ( !ent->inuse )
{
continue;
}
if ( G_IsClientDead ( ent->client ) )
{
continue;
}
if ( !CheatsOk( ent ) ) {
return;
}
name = ConcatArgs( 2 );
if (Q_stricmp(name, "all") == 0)
give_all = qtrue;
else
give_all = qfalse;
if (give_all || Q_stricmp( name, "health") == 0)
{
ent->health = MAX_HEALTH;
if (!give_all)
continue;
}
if (give_all || Q_stricmp(name, "weapons") == 0)
{
ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - ( 1 << WP_NONE );
if (!give_all)
continue;
}
if (give_all || Q_stricmp(name, "ammo") == 0)
{
for ( i = WP_NONE + 1 ; i < WP_NUM_WEAPONS ; i++ )
{
attackType_t a;
for ( a = ATTACK_NORMAL; a < ATTACK_MAX; a ++ )
{
ent->client->ps.clip[a][i] = weaponData[i].attack[a].clipSize;
ent->client->ps.ammo[weaponData[i].attack[a].ammoIndex] = ammoData[weaponData[i].attack[a].ammoIndex].max;
}
}
if (!give_all)
continue;
}
if (give_all || Q_stricmp(name, "armor") == 0)
{
ent->client->ps.stats[STAT_ARMOR] = MAX_ARMOR;
if (!give_all)
continue;
}
// spawn a specific item right on the player
if ( !give_all )
{
it = BG_FindItem (name);
if (!it)
{
continue;
}
if ( it->giType == IT_GAMETYPE )
{
continue;
}
it_ent = G_Spawn();
VectorCopy( ent->r.currentOrigin, it_ent->s.origin );
it_ent->classname = it->classname;
G_SpawnItem (it_ent, it);
FinishSpawningItem(it_ent );
memset( &trace, 0, sizeof( trace ) );
Touch_Item (it_ent, ent, &trace);
if (it_ent->inuse)
{
G_FreeEntity( it_ent );
}
}
}
}
/*
==================
Cmd_God_f
Sets client to godmode
argv(0) god
==================
*/
void Cmd_God_f (gentity_t *ent)
{
char *msg;
if ( !CheatsOk( ent ) ) {
return;
}
ent->flags ^= FL_GODMODE;
if (!(ent->flags & FL_GODMODE) )
msg = "godmode OFF\n";
else
msg = "godmode ON\n";
trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
}
/*
==================
Cmd_Notarget_f
Sets client to notarget
argv(0) notarget
==================
*/
void Cmd_Notarget_f( gentity_t *ent ) {
char *msg;
if ( !CheatsOk( ent ) ) {
return;
}
ent->flags ^= FL_NOTARGET;
if (!(ent->flags & FL_NOTARGET) )
msg = "notarget OFF\n";
else
msg = "notarget ON\n";
trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
}
/*
==================
Cmd_Noclip_f
argv(0) noclip
==================
*/
void Cmd_Noclip_f( gentity_t *ent ) {
char *msg;
if ( !CheatsOk( ent ) ) {
return;
}
if ( ent->client->noclip ) {
msg = "noclip OFF\n";
} else {
msg = "noclip ON\n";
}
ent->client->noclip = !ent->client->noclip;
trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
}
/*
==================
Cmd_LevelShot_f
This is just to help generate the level pictures
for the menus. It goes to the intermission immediately
and sends over a command to the client to resize the view,
hide the scoreboard, and take a special screenshot
==================
*/
void Cmd_LevelShot_f( gentity_t *ent )
{
if ( !CheatsOk( ent ) )
{
return;
}
BeginIntermission();
trap_SendServerCommand( ent-g_entities, "clientLevelShot" );
}
/*
=================
Cmd_Kill_f
=================
*/
void Cmd_Kill_f( gentity_t *ent )
{
// No killing yourself if your a spectator
if ( G_IsClientSpectating ( ent->client ) )
{
return;
}
// No killing yourself if your dead
if ( G_IsClientDead ( ent->client ) )
{
return;
}
ent->flags &= ~FL_GODMODE;
ent->client->ps.stats[STAT_HEALTH] = ent->health = -999;
player_die (ent, ent, ent, 100000, MOD_SUICIDE, HL_NONE, vec3_origin );
}
/*
=================
BroadCastTeamChange
Let everyone know about a team change
=================
*/
void BroadcastTeamChange( gclient_t *client, int oldTeam )
{
switch ( client->sess.team )
{
case TEAM_RED:
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"", client->pers.netname) );
break;
case TEAM_BLUE:
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"", client->pers.netname));
break;
case TEAM_SPECTATOR:
if ( oldTeam != TEAM_SPECTATOR )
{
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"", client->pers.netname));
}
break;
case TEAM_FREE:
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"", client->pers.netname));
break;
}
}
/*
=================
SetTeam
=================
*/
void SetTeam( gentity_t *ent, char *s, const char* identity )
{
int team;
int oldTeam;
gclient_t *client;
int clientNum;
spectatorState_t specState;
int specClient;
qboolean ghost;
qboolean noOutfittingChange = qfalse;
// see what change is requested
//
client = ent->client;
clientNum = client - level.clients;
specClient = 0;
specState = SPECTATOR_NOT;
// If an identity was specified then inject it into
// the clients userinfo
if ( identity )
{
char userinfo[MAX_INFO_STRING];
trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
if ( Q_stricmp ( identity, Info_ValueForKey ( userinfo, "identity" ) ) )
{
Info_SetValueForKey ( userinfo, "identity", identity );
Info_SetValueForKey ( userinfo, "team_identity", identity );
trap_SetUserinfo ( clientNum, userinfo );
}
else
{
identity = NULL;
}
}
if ( !Q_stricmp( s, "follow1" ) )
{
team = TEAM_SPECTATOR;
specState = SPECTATOR_FOLLOW;
specClient = -1;
}
else if ( !Q_stricmp( s, "follow2" ) )
{
team = TEAM_SPECTATOR;
specState = SPECTATOR_FOLLOW;
specClient = -2;
}
else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) )
{
team = TEAM_SPECTATOR;
specState = SPECTATOR_FREE;
}
else if ( level.gametypeData->teams )
{
// if running a team game, assign player to one of the teams
specState = SPECTATOR_NOT;
if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) )
{
team = TEAM_RED;
}
else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) )
{
team = TEAM_BLUE;
}
else
{
// pick the team with the least number of players
team = PickTeam( clientNum );
}
if ( g_teamForceBalance.integer )
{
int counts[TEAM_NUM_TEAMS];
counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE, NULL );
counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED, NULL );
// We allow a spread of two
if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 )
{
trap_SendServerCommand( ent->client->ps.clientNum,
"cp \"Red team has too many players.\n\"" );
// ignore the request
return;
}
if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 )
{
trap_SendServerCommand( ent->client->ps.clientNum,
"cp \"Blue team has too many players.\n\"" );
// ignore the request
return;
}
// It's ok, the team we are switching to has less or same number of players
}
}
else
{
// force them to spectators if there aren't any spots free
team = TEAM_FREE;
}
// override decision if limiting the players
if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer )
{
team = TEAM_SPECTATOR;
}
// decide if we will allow the change
oldTeam = client->sess.team;
ghost = client->sess.ghost;
if ( team == oldTeam && team != TEAM_SPECTATOR )
{
if ( identity )
{
// get and distribute relevent paramters
client->pers.identity = NULL;
ClientUserinfoChanged( clientNum );
}
return;
}
noOutfittingChange = ent->client->noOutfittingChange;
// he starts at 'base'
client->pers.teamState.state = TEAM_BEGIN;
if ( oldTeam != TEAM_SPECTATOR )
{
if ( ghost )
{
G_StopGhosting ( ent );
}
else if ( !G_IsClientDead ( client ) )
{
// Kill him (makes sure he loses flags, etc)
ent->flags &= ~FL_GODMODE;
ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
player_die (ent, ent, ent, 100000, MOD_TEAMCHANGE, HL_NONE, vec3_origin );
ent->client->sess.ghost = qfalse;
}
}
// If respawn interval start as a ghost
if ( level.gametypeRespawnTime[ team ] )
{
ghost = qtrue;
}
// they go to the end of the line
if ( team == TEAM_SPECTATOR )
{
client->sess.spectatorTime = level.time;
}
client->sess.team = team;
client->sess.spectatorState = specState;
client->sess.spectatorClient = specClient;
// Always spawn into a ctf game using a respawn timer.
if ( team != TEAM_SPECTATOR && level.gametypeData->respawnType == RT_INTERVAL )
{
G_SetRespawnTimer ( ent );
ghost = qtrue;
}
BroadcastTeamChange( client, oldTeam );
// See if we should spawn as a ghost
if ( team != TEAM_SPECTATOR && level.gametypeData->respawnType == RT_NONE )
{
// If there are ghosts already then spawn as a ghost because
// the game is already in progress.
if ( (level.gametypeJoinTime && (level.time - level.gametypeJoinTime) > (g_roundjointime.integer * 1000)) || noOutfittingChange || client->sess.noTeamChange )
{
ghost = qtrue;
}
// Spectator to a team doesnt count
if ( oldTeam != TEAM_SPECTATOR )
{
client->sess.noTeamChange = qtrue;
}
}
// If a ghost, enforce it
if ( ghost )
{
// Make them a ghost again
if ( team != TEAM_SPECTATOR )
{
G_StartGhosting ( ent );
// get and distribute relevent paramters
client->pers.identity = NULL;
ClientUserinfoChanged( clientNum );
CalculateRanks();
return;
}
}
// get and distribute relevent paramters
client->pers.identity = NULL;
ClientUserinfoChanged( clientNum );
CalculateRanks();
// Begin the clients new life on the their new team
ClientBegin( clientNum );
}
/*
=================
G_StartGhosting
Starts a client ghosting. This essentially will kill a player which is alive
=================
*/
void G_StartGhosting ( gentity_t* ent )
{
int i;
// Dont start ghosting if already ghosting
if ( ent->client->sess.ghost )
{
return;
}
ent->client->sess.ghost = qtrue;
ent->client->sess.spectatorState = SPECTATOR_FREE;
ent->client->sess.spectatorClient = -1;
ent->client->ps.pm_flags |= PMF_GHOST;
ent->client->ps.stats[STAT_HEALTH] = 100;
ent->client->ps.pm_type = PM_SPECTATOR;
ent->client->ps.pm_flags &= ~PMF_FOLLOW;
trap_UnlinkEntity (ent);
// stop any following clients
for ( i = 0 ; i < level.maxclients ; i++ )
{
if ( G_IsClientSpectating ( &level.clients[i] )
&& level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW
&& level.clients[i].sess.spectatorClient == ent->s.number )
{
G_StopFollowing( &g_entities[i] );
}
}
}
/*
=================
G_StopGhosting
Stops a client from ghosting. The client will be dead after this
call
=================
*/
void G_StopGhosting ( gentity_t* ent )
{
// Dont stop someone who isnt ghosting in the first place
if ( !ent->client->sess.ghost )
{
return;
}
ent->client->sess.ghost = qfalse;
ent->client->ps.pm_flags &= ~PMF_GHOST;
ent->client->ps.pm_flags &= ~PMF_FOLLOW;
if ( ent->client->sess.team == TEAM_SPECTATOR )
{
ent->client->ps.pm_type = PM_SPECTATOR;
}
else
{
ent->client->ps.pm_type = PM_DEAD;
ent->health = ent->client->ps.stats[STAT_HEALTH] = 0;
}
}
/*
=================
G_StopFollowing
If the client being followed leaves the game, or you just want to drop
to free floating spectator mode
=================
*/
void G_StopFollowing( gentity_t *ent )
{
// Cant stop following if not following in the first place
if ( !(ent->client->ps.pm_flags&PMF_FOLLOW) )
{
return;
}
// Clear the following variables
ent->client->ps.pm_flags &= ~PMF_FOLLOW;
ent->client->sess.spectatorState = SPECTATOR_FREE;
ent->client->ps.clientNum = ent - g_entities;
ent->client->ps.zoomFov = 0;
ent->client->ps.loopSound = 0;
ent->client->ps.pm_flags &= ~(PMF_GOGGLES_ON|PMF_ZOOM_FLAGS);
ent->client->ps.persistant[PERS_TEAM] = ent->client->sess.team;
ent->r.svFlags &= ~SVF_BOT;
// Ghots dont really become spectators, just psuedo spectators
if ( ent->client->sess.ghost )
{
// Do a start and stop to ensure the variables are all set properly
G_StopGhosting ( ent );
G_StartGhosting ( ent );
}
else
{
ent->client->sess.team = TEAM_SPECTATOR;
ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
}
// If we were in fact following someone, then make the angles and origin nice for
// when we stop
if ( ent->client->sess.spectatorClient != -1 )
{
gclient_t* cl = &level.clients[ent->client->sess.spectatorClient];
int i;
for ( i = 0; i < 3; i ++ )
{
ent->client->ps.delta_angles[i] = ANGLE2SHORT(cl->ps.viewangles[i] - SHORT2ANGLE(ent->client->pers.cmd.angles[i]));
}
VectorCopy ( cl->ps.viewangles, ent->client->ps.viewangles );
VectorCopy ( cl->ps.origin, ent->client->ps.origin );
VectorClear ( ent->client->ps.velocity );
ent->client->ps.movementDir = 0;
BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
}
ent->client->sess.spectatorClient = -1;
}
/*
=================
Cmd_Team_f
=================
*/
void Cmd_Team_f( gentity_t *ent )
{
char team[MAX_TOKEN_CHARS];
char identity[MAX_TOKEN_CHARS];
// Need at least the team specified in the arguments
if ( trap_Argc() < 2 )
{
int oldTeam = ent->client->sess.team;
switch ( oldTeam )
{
case TEAM_BLUE:
trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" );
break;
case TEAM_RED:
trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" );
break;
case TEAM_FREE:
trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" );
break;
case TEAM_SPECTATOR:
trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" );
break;
}
return;
}
// Limit how often one can switch team
if ( ent->client->switchTeamTime > level.time )
{
trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" );
return;
}
trap_Argv( 1, team, sizeof( team ) );
trap_Argv( 2, identity, sizeof( identity ) );
SetTeam( ent, team, identity[0]?identity:NULL );
// Remember the team switch time so they cant do it again really quick
ent->client->switchTeamTime = level.time + 5000;
}
/*
=================
Cmd_Follow_f
=================
*/
void Cmd_Follow_f( gentity_t *ent )
{
int i;
char arg[MAX_TOKEN_CHARS];
if ( trap_Argc() != 2 )
{
if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
{
G_StopFollowing( ent );
}
return;
}
trap_Argv( 1, arg, sizeof( arg ) );
i = ClientNumberFromString( ent, arg );
if ( i == -1 )
{
return;
}
// can't follow self
if ( &level.clients[ i ] == ent->client )
{
return;
}
// cant cycle to dead people
if ( level.clients[i].ps.pm_type == PM_DEAD )
{
return;
}
// can't follow another spectator
if ( G_IsClientSpectating ( &level.clients[ i ] ) )
{
return;
}
// Dissallow following of the enemy if the cvar is set
if ( level.gametypeData->teams && !g_followEnemy.integer && ent->client->sess.team != TEAM_SPECTATOR )
{
// Are they on the same team?
if ( level.clients[ i ].sess.team != ent->client->sess.team )
{
return;
}
}
// first set them to spectator as long as they arent a ghost
if ( !ent->client->sess.ghost && ent->client->sess.team != TEAM_SPECTATOR )
{
SetTeam( ent, "spectator", NULL );
}
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
ent->client->sess.spectatorClient = i;
}
/*
=================
Cmd_FollowCycle_f
=================
*/
void Cmd_FollowCycle_f( gentity_t *ent, int dir )
{
int clientnum;
int deadclient;
int original;
// first set them to spectator
if ( !ent->client->sess.ghost && ent->client->sess.team != TEAM_SPECTATOR )
{
SetTeam( ent, "spectator", NULL );
}
if ( dir != 1 && dir != -1 )
{
Com_Error( ERR_FATAL, "Cmd_FollowCycle_f: bad dir %i", dir );
}
if ( ent->client->sess.spectatorClient == -1 )
{
clientnum = original = ent->s.number;
}
else
{
clientnum = original = ent->client->sess.spectatorClient;
}
deadclient = -1;
do
{
clientnum += dir;
if ( clientnum >= level.maxclients )
{
clientnum = 0;
}
if ( clientnum < 0 )
{
clientnum = level.maxclients - 1;
}
// can only follow connected clients
if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED )
{
continue;
}
// can't follow another spectator
if ( G_IsClientSpectating ( &level.clients[ clientnum ] ) )
{
continue;
}
// Cant switch to dead people unless there is nobody else to switch to
if ( G_IsClientDead ( &level.clients[clientnum] ) )
{
deadclient = clientnum;
continue;
}
// Dissallow following of the enemy if the cvar is set
if ( level.gametypeData->teams && !g_followEnemy.integer && ent->client->sess.team != TEAM_SPECTATOR )
{
// Are they on the same team?
if ( level.clients[ clientnum ].sess.team != ent->client->sess.team )
{
continue;
}
}
// this is good, we can use it
ent->client->sess.spectatorClient = clientnum;
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
return;
} while ( clientnum != original );
// If being forced to follow and there is a dead client to jump to, then jump to them now
if ( deadclient != -1 && g_forceFollow.integer )
{
// this is good, we can use it
ent->client->sess.spectatorClient = deadclient;
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
return;
}
G_StopFollowing( ent );
// leave it where it was
}
/*
==================
G_SayTo
==================
*/
static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, const char *name, const char *message )
{
qboolean ghost = qfalse;
qboolean spec = qfalse;
const char* type;
if (!other)
{
return;
}
if (!other->inuse)
{
return;
}
if (!other->client)
{
return;
}
if ( other->client->pers.connected != CON_CONNECTED )
{
return;
}
if ( mode == SAY_TEAM && !OnSameTeam(ent, other) )
{
return;
}
if ( !level.intermissiontime && !level.intermissionQueued )
{
// Spectators cant talk to alive people
if ( ent->client->sess.team == TEAM_SPECTATOR )
{
spec = qtrue;
}
if ( level.gametypeData->respawnType == RT_NONE )
{
// Dead people cant talk to alive people
if ( !spec && G_IsClientDead ( ent->client ) )
{
ghost = qtrue;
}
// If the client we are talking to is alive then a check
// must be made to see if this talker is alowed to speak to this person
if ( ent->s.number != other->s.number && !G_IsClientDead ( other->client ) && !G_IsClientSpectating( other->client) && (ghost || spec))
{
return;
}
}
}
type = "";
if ( ghost )
{
type = "*ghost* ";
}
else if ( spec )
{
type = "*spec* ";
}
trap_SendServerCommand( other-g_entities, va("%s %d \"%s%s%s\"",
mode == SAY_TEAM ? "tchat" : "chat",
ent->s.number,
type, name, message));
}
/*
==================
G_GetChatPrefix
==================
*/
void G_GetChatPrefix ( gentity_t* ent, gentity_t* target, int mode, char* name, int nameSize )
{
const char* namecolor;
char location[64];
qboolean locationOk = qtrue;
// Spectators and ghosts dont show locations
if ( ent->client->ps.pm_type == PM_DEAD || G_IsClientSpectating ( ent->client ) )
{
locationOk = qfalse;
}
if ( !level.gametypeData->teams && mode == SAY_TEAM )
{
mode = SAY_ALL;
}
if ( level.gametypeData->teams )
{
switch ( ent->client->sess.team )
{
case TEAM_BLUE:
namecolor = S_COLOR_BLUE;
break;
case TEAM_RED:
namecolor = S_COLOR_RED;
break;
default:
namecolor = S_COLOR_WHITE;
break;
}
}
else
{
namecolor = S_COLOR_WHITE;
}
switch ( mode )
{
default:
case SAY_ALL:
Com_sprintf (name, nameSize, "%s%s%s: ", namecolor, ent->client->pers.netname, S_COLOR_WHITE );
break;
case SAY_TEAM:
if ( locationOk && Team_GetLocationMsg(ent, location, sizeof(location)))
{
Com_sprintf ( name, nameSize, "%s(%s%s) %s(%s): ",
namecolor,
ent->client->pers.netname,
namecolor,
S_COLOR_WHITE, location );
}
else
{
Com_sprintf ( name, nameSize, "%s(%s%s)%s: ",
namecolor,
ent->client->pers.netname,
namecolor,
S_COLOR_WHITE );
}
break;
case SAY_TELL:
if ( locationOk && target && level.gametypeData->teams &&
target->client->sess.team == ent->client->sess.team &&
Team_GetLocationMsg(ent, location, sizeof(location)) )
{
Com_sprintf ( name, nameSize, "%s[%s%s] %s(%s): ",
namecolor,
ent->client->pers.netname,
namecolor,
S_COLOR_WHITE, location );
}
else
{
Com_sprintf ( name, nameSize, "%s[%s%s]%s: ",
namecolor,
ent->client->pers.netname,
namecolor,
S_COLOR_WHITE );
}
break;
}
strcat ( name, S_COLOR_GREEN );
}
/*
==================
G_Say
==================
*/
void G_Say ( gentity_t *ent, gentity_t *target, int mode, const char *chatText )
{
int j;
gentity_t *other;
char text[MAX_SAY_TEXT];
char name[256];
// Logging stuff
switch ( mode )
{
case SAY_ALL:
G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
break;
case SAY_TEAM:
G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
break;
}
// Generate the chat prefix
G_GetChatPrefix ( ent, target, mode, name, sizeof(name) );
// Save off the chat text
Q_strncpyz( text, chatText, sizeof(text) );
if ( target )
{
G_SayTo( ent, target, mode, name, text );
return;
}
// echo the text to the console
if ( g_dedicated.integer )
{
Com_Printf( "%s%s\n", name, text);
}
// send it to all the apropriate clients
for (j = 0; j < level.numConnectedClients; j++)
{
other = &g_entities[level.sortedClients[j]];
G_SayTo( ent, other, mode, name, text );
}
}
/*
==================
Cmd_Say_f
==================
*/
static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
char *p;
if ( trap_Argc () < 2 && !arg0 ) {
return;
}
if (arg0)
{
p = ConcatArgs( 0 );
}
else
{
p = ConcatArgs( 1 );
}
G_Say( ent, NULL, mode, p );
}
/*
==================
Cmd_Tell_f
==================
*/
static void Cmd_Tell_f( gentity_t *ent ) {
int targetNum;
gentity_t *target;
char *p;
char arg[MAX_TOKEN_CHARS];
if ( trap_Argc () < 2 ) {
return;
}
trap_Argv( 1, arg, sizeof( arg ) );
targetNum = atoi( arg );
if ( targetNum < 0 || targetNum >= level.maxclients ) {
return;
}
target = &g_entities[targetNum];
if ( !target || !target->inuse || !target->client ) {
return;
}
p = ConcatArgs( 2 );
G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
G_Say( ent, target, SAY_TELL, p );
// don't tell to the player self if it was already directed to this player
// also don't send the chat back to a bot
if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
G_Say( ent, ent, SAY_TELL, p );
}
}
static void G_VoiceTo ( gentity_t *ent, gentity_t *other, int mode, const char* name, const char *id, qboolean voiceonly )
{
// Only team say is supported right now for voice chatting
if (mode != SAY_TEAM)
{
return;
}
if (!other || !other->inuse || !other->client)
{
return;
}
if ( !OnSameTeam(ent, other) )
{
return;
}
trap_SendServerCommand( other-g_entities, va("%s %d %d \"%s\" \"%s\"", "vtchat", voiceonly, ent->s.number, name, id));
}
/*
==================
G_CanVoiceGlobal
Can we globaly speak right now
==================
*/
qboolean G_CanVoiceGlobal ( void )
{
if ( level.gametypeData->teams && level.time - level.globalVoiceTime > 5000 )
{
return qtrue;
}
return qfalse;
}
/*
==================
G_VoiceGlobal
says something out loud that everyone in the radius can hear
==================
*/
void G_VoiceGlobal ( gentity_t* ent, const char* id, qboolean force )
{
if ( !ent )
{
return;
}
if ( !level.gametypeData->teams )
{
return;
}
if ( !force && level.time - level.globalVoiceTime < 5000 )
{
return;
}
level.globalVoiceTime = level.time;
trap_SendServerCommand( -1, va("vglobal %d \"%s\"", ent->s.number, id));
}
/*
==================
G_Voice
==================
*/
void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly )
{
int j;
gentity_t *other;
char name[MAX_SAY_TEXT];
// Spectators and ghosts dont talk
if ( ent->client->ps.pm_type == PM_DEAD || G_IsClientSpectating ( ent->client ) )
{
return;
}
// Voice flooding protection on?
if ( g_voiceFloodCount.integer )
{
// If this client has been penalized for voice chatting to much then dont allow the voice chat
if ( ent->client->voiceFloodPenalty )
{
if ( ent->client->voiceFloodPenalty > level.time )
{
return;
}
// No longer penalized
ent->client->voiceFloodPenalty = 0;
}
// See if this client flooded with voice chats
ent->client->voiceFloodCount++;
if ( ent->client->voiceFloodCount >= g_voiceFloodCount.integer )
{
ent->client->voiceFloodCount = 0;
ent->client->voiceFloodTimer = 0;
ent->client->voiceFloodPenalty = level.time + g_voiceFloodPenalty.integer * 1000;
trap_SendServerCommand( ent-g_entities, va("print \"Voice chat flooded, you will be able use voice chats again in (%d) seconds\n\"", g_voiceFloodPenalty.integer ) );
return;
}
}
G_GetChatPrefix ( ent, target, mode, name, sizeof(name) );
if ( target )
{
G_VoiceTo( ent, target, mode, name, id, voiceonly );
return;
}
// send it to all the apropriate clients
for (j = 0; j < level.maxclients; j++)
{
other = &g_entities[j];
G_VoiceTo( ent, other, mode, name, id, voiceonly );
}
}
/*
==================
Cmd_Voice_f
==================
*/
static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly )
{
char *p;
if ( trap_Argc () < 2 && !arg0 ) {
return;
}
if (arg0)
{
p = ConcatArgs( 0 );
}
else
{
p = ConcatArgs( 1 );
}
G_Voice( ent, NULL, mode, p, voiceonly );
}
/*
==================
Cmd_Where_f
==================
*/
void Cmd_Where_f( gentity_t *ent )
{
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
}
/*
============
G_VoteDisabled
determins if the given vote is disabled
============
*/
int G_VoteDisabled ( const char* callvote )
{
return trap_Cvar_VariableIntegerValue( va("novote_%s", callvote) );
}
/*
==================
Cmd_CallVote_f
==================
*/
void Cmd_CallVote_f( gentity_t *ent )
{
int i;
char arg1[MAX_STRING_TOKENS];
char arg2[MAX_STRING_TOKENS];
if ( !g_allowVote.integer )
{
trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
return;
}
if ( level.intermissiontime || level.intermissionQueued )
{
trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed during intermission.\n\"" );
return;
}
// No voting within the minute of a map change
if ( level.time - level.startTime < 1000 * 60 )
{
trap_SendServerCommand( ent-g_entities, "print \"Cannot vote within the first minute of a map change.\n\"" );
return;
}
if ( level.numConnectedClients > 1 && level.numVotingClients == 1 )
{
trap_SendServerCommand( ent-g_entities, "print \"You need at least 2 clients to call a vote.\n\"" );
return;
}
if ( level.voteTime )
{
trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" );
return;
}
if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT )
{
trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" );
return;
}
if ( ent->client->sess.team == TEAM_SPECTATOR )
{
trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
return;
}
if ( ent->client->voteDelayTime > level.time )
{
trap_SendServerCommand( ent-g_entities, va("print \"You are not allowed to vote within %d minute of a failed vote.\n\"", g_failedVoteDelay.integer ) );
return;
}
// Save the voting client id
level.voteClient = ent->s.number;
// make sure it is a valid command to vote on
trap_Argv( 1, arg1, sizeof( arg1 ) );
trap_Argv( 2, arg2, sizeof( arg2 ) );
if( strchr( arg1, ';' ) || strchr( arg2, ';' ) )
{
trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
return;
}
if ( !Q_stricmp( arg1, "map_restart" ) ) {
} else if ( !Q_stricmp( arg1, "mapcycle" ) ) {
} else if ( !Q_stricmp( arg1, "map" ) ) {
} else if ( !Q_stricmp( arg1, "rmgmap" ) ) {
} else if ( !Q_stricmp( arg1, "g_gametype" ) ) {
} else if ( !Q_stricmp( arg1, "kick" ) ) {
} else if ( !Q_stricmp( arg1, "clientkick" ) ) {
} else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {
} else if ( !Q_stricmp( arg1, "g_friendlyfire" ) ) {
} else if ( !Q_stricmp( arg1, "timelimit" ) ) {
} else if ( !Q_stricmp( arg1, "timeextension" ) ) {
} else if ( !Q_stricmp( arg1, "scorelimit" ) ) {
} else
{
trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map <mapname>, g_gametype <n>, kick <player>, clientkick <clientnum>, g_doWarmup, timelimit <time>, scorelimit <score>.\n\"" );
return;
}
// see if this particular vote is disabled
if ( G_VoteDisabled ( arg1 ) )
{
trap_SendServerCommand( ent-g_entities, va("print \"The '%s' vote has been disabled on this server.\n\"", arg1) );
return;
}
// if there is still a vote to be executed
if ( level.voteExecuteTime )
{
level.voteExecuteTime = 0;
trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
}
// special case for g_gametype, check for bad values
if ( !Q_stricmp( arg1, "g_gametype" ) )
{
// Verify the gametype
i = BG_FindGametype ( arg2 );
if ( i < 0 )
{
trap_SendServerCommand( ent-g_entities, "print \"Invalid gametype.\n\"" );
return;
}
Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, bg_gametypeData[i].name );
}
else if ( !Q_stricmp( arg1, "map" ) )
{
Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
}
else if ( !Q_stricmp( arg1, "rmgmap" ) )
{
char arg3[MAX_STRING_TOKENS];
char arg4[MAX_STRING_TOKENS];
trap_Argv( 3, arg3, sizeof( arg3 ) );
trap_Argv( 4, arg4, sizeof( arg4 ) );
Com_sprintf( level.voteString, sizeof( level.voteString ), "rmgmap 1 %s 2 %s 3 %s 4 \"%s\" 0", arg2, arg3, arg4, ConcatArgs ( 5 ) );
Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
}
else if ( !Q_stricmp( arg1, "mapcycle" ) )
{
if (!*g_mapcycle.string || !Q_stricmp ( g_mapcycle.string, "none" ) )
{
trap_SendServerCommand( ent-g_entities, "print \"there is no map cycle currently set up.\n\"" );
return;
}
Com_sprintf( level.voteString, sizeof( level.voteString ), "mapcycle");
Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "nextmap" );
}
else if ( !Q_stricmp ( arg1, "clientkick" ) )
{
int n = atoi ( arg2 );
if ( n < 0 || n >= MAX_CLIENTS )
{
trap_SendServerCommand( ent-g_entities, va("print \"invalid client number %d.\n\"", n ) );
return;
}
if ( g_entities[n].client->pers.connected == CON_DISCONNECTED )
{
trap_SendServerCommand( ent-g_entities, va("print \"there is no client with the client number %d.\n\"", n ) );
return;
}
if ( g_voteKickBanTime.integer )
{
Com_sprintf ( level.voteString, sizeof(level.voteString ), "banclient %s %d voted off server", arg2, g_voteKickBanTime.integer );
}
else
{
Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %s", arg2 );
}
Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", g_entities[n].client->pers.netname );
}
else if ( !Q_stricmp ( arg1, "kick" ) )
{
int clientid = G_ClientNumberFromName ( arg2 );
if ( clientid == -1 )
{
trap_SendServerCommand( ent-g_entities, va("print \"there is no client named '%s' currently on the server.\n\"", arg2 ) );
return;
}
if ( g_voteKickBanTime.integer )
{
Com_sprintf ( level.voteString, sizeof(level.voteString ), "banclient %d %d voted off server", clientid, g_voteKickBanTime.integer );
}
else
{
Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %d", clientid );
}
Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", g_entities[clientid].client->pers.netname );
}
else if ( !Q_stricmp ( arg1, "timeextension" ) )
{
if ( !g_timelimit.integer )
{
trap_SendServerCommand( ent-g_entities, va("print \"There is no timelimit to extend.\n\"") );
return;
}
if ( !g_timeextension.integer )
{
trap_SendServerCommand( ent-g_entities, va("print \"This server does not allow time extensions.\n\"") );
return;
}
Com_sprintf ( level.voteString, sizeof(level.voteString ), "extendtime %d", g_timeextension.integer );
Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "extend timelimit by %d minutes", g_timeextension.integer );
}
else
{
Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
}
trap_SendServerCommand( -1, va("print \"%s called a vote.\n\"", ent->client->pers.netname ) );
// start the voting, the caller autoamtically votes yes
level.voteTime = level.time;
level.voteYes = 1;
level.voteNo = 0;
for ( i = 0 ; i < level.maxclients ; i++ )
{
level.clients[i].ps.eFlags &= ~EF_VOTED;
}
ent->client->ps.eFlags |= EF_VOTED;
trap_SetConfigstring( CS_VOTE_TIME, va("%i,%i", level.voteTime, g_voteDuration.integer*1000 ) );
trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString );
trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );
trap_SetConfigstring( CS_VOTE_NEEDED, va("%i", level.numVotingClients / 2 ) );
}
/*
==================
Cmd_Vote_f
==================
*/
void Cmd_Vote_f( gentity_t *ent )
{
char msg[64];
if ( !level.voteTime )
{
trap_SendServerCommand( ent-g_entities, "print \"No vote in progress.\n\"" );
return;
}
if ( ent->client->ps.eFlags & EF_VOTED )
{
trap_SendServerCommand( ent-g_entities, "print \"Vote already cast.\n\"" );
return;
}
if ( ent->client->sess.team == TEAM_SPECTATOR )
{
trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
return;
}
trap_SendServerCommand( ent-g_entities, "print \"Vote cast.\n\"" );
ent->client->ps.eFlags |= EF_VOTED;
trap_Argv( 1, msg, sizeof( msg ) );
if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' )
{
level.voteYes++;
trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
}
else
{
level.voteNo++;
trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );
}
// a majority will be determined in CheckVote, which will also account
// for players entering or leaving
}
/*
=================
Cmd_SetViewpos_f
=================
*/
void Cmd_SetViewpos_f( gentity_t *ent )
{
vec3_t origin, angles;
char buffer[MAX_TOKEN_CHARS];
int i;
if ( !g_cheats.integer )
{
trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
return;
}
if ( trap_Argc() != 5 )
{
trap_SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
return;
}
VectorClear( angles );
for ( i = 0 ; i < 3 ; i++ )
{
trap_Argv( i + 1, buffer, sizeof( buffer ) );
origin[i] = atof( buffer );
}
trap_Argv( 4, buffer, sizeof( buffer ) );
angles[YAW] = atof( buffer );
TeleportPlayer( ent, origin, angles );
}
/*
=================
ClientCommand
=================
*/
void ClientCommand( int clientNum ) {
gentity_t *ent;
char cmd[MAX_TOKEN_CHARS];
ent = g_entities + clientNum;
if ( !ent->client ) {
return; // not fully in game yet
}
trap_Argv( 0, cmd, sizeof( cmd ) );
//rww - redirect bot commands
if (strstr(cmd, "bot_") && AcceptBotCommand(cmd, ent))
{
return;
}
//end rww
if (Q_stricmp (cmd, "say") == 0) {
Cmd_Say_f (ent, SAY_ALL, qfalse);
return;
}
if (Q_stricmp (cmd, "say_team") == 0) {
Cmd_Say_f (ent, SAY_TEAM, qfalse);
return;
}
if (Q_stricmp (cmd, "tell") == 0)
{
Cmd_Tell_f ( ent );
return;
}
if (Q_stricmp (cmd, "vsay_team") == 0)
{
Cmd_Voice_f (ent, SAY_TEAM, qfalse, qfalse);
return;
}
if (Q_stricmp (cmd, "score") == 0) {
Cmd_Score_f (ent);
return;
}
if (Q_stricmp (cmd, "team") == 0)
{
Cmd_Team_f (ent);
return;
}
// ignore all other commands when at intermission
if (level.intermissiontime)
{
// Cmd_Say_f (ent, qfalse, qtrue);
return;
}
if ( Q_stricmp ( cmd, "drop" ) == 0 )
Cmd_Drop_f ( ent );
else if (Q_stricmp (cmd, "dropitem" ) == 0 )
Cmd_DropItem_f ( ent );
else if (Q_stricmp (cmd, "give") == 0)
Cmd_Give_f (ent);
else if (Q_stricmp (cmd, "god") == 0)
Cmd_God_f (ent);
else if (Q_stricmp (cmd, "notarget") == 0)
Cmd_Notarget_f (ent);
else if (Q_stricmp (cmd, "noclip") == 0)
Cmd_Noclip_f (ent);
else if (Q_stricmp (cmd, "kill") == 0)
Cmd_Kill_f (ent);
else if (Q_stricmp (cmd, "levelshot") == 0)
Cmd_LevelShot_f (ent);
else if (Q_stricmp (cmd, "follow") == 0)
Cmd_Follow_f (ent);
else if (Q_stricmp (cmd, "follownext") == 0)
Cmd_FollowCycle_f (ent, 1);
else if (Q_stricmp (cmd, "followprev") == 0)
Cmd_FollowCycle_f (ent, -1);
else if (Q_stricmp (cmd, "where") == 0)
Cmd_Where_f (ent);
else if (Q_stricmp (cmd, "callvote") == 0)
Cmd_CallVote_f (ent);
else if (Q_stricmp (cmd, "vote") == 0)
Cmd_Vote_f (ent);
else if (Q_stricmp (cmd, "setviewpos") == 0)
Cmd_SetViewpos_f( ent );
#ifdef _SOF2_BOTS
else if (Q_stricmp (cmd, "addbot") == 0)
trap_SendServerCommand( clientNum, va("print \"ADDBOT command can only be used via RCON\n\"" ) );
#endif
else
trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
}