/*
===========================================================================

Wolfenstein: Enemy Territory GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).  

Wolf ET 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 3 of the License, or
(at your option) any later version.

Wolf ET 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 Wolf ET Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "g_local.h"

void BotDebug( int clientNum );
void GetBotAutonomies( int clientNum, int *weapAutonomy, int *moveAutonomy );
qboolean G_IsOnFireteam( int entityNum, fireteamData_t** teamNum );

/*
==================
G_SendScore

Sends current scoreboard information
==================
*/
void G_SendScore( gentity_t *ent ) {
	char entry[128];
	int i;
	gclient_t   *cl;
	int numSorted;
	int team, size, count;
	char buffer[1024];
	char startbuffer[32];

	// send the latest information on all clients
	numSorted = level.numConnectedClients;
	if ( numSorted > 64 ) {
		numSorted = 64;
	}

	i = 0;
	// Gordon: team doesnt actually mean team, ignore...
	for ( team = 0; team < 2; team++ ) {
		*buffer = '\0';
		*startbuffer = '\0';
		if ( team == 0 ) {
			Q_strncpyz( startbuffer, va( "sc0 %i %i", level.teamScores[TEAM_AXIS], level.teamScores[TEAM_ALLIES] ), 32 );
		} else {
			Q_strncpyz( startbuffer, "sc1", 32 );
		}
		size = strlen( startbuffer ) + 1;
		count = 0;

		for (; i < numSorted ; i++ ) {
			int ping, playerClass, respawnsLeft;

			cl = &level.clients[level.sortedClients[i]];

			if ( g_entities[level.sortedClients[i]].r.svFlags & SVF_POW ) {
				continue;
			}

			// NERVE - SMF - if on same team, send across player class
			// Gordon: FIXME: remove/move elsewhere?
			if ( cl->ps.persistant[PERS_TEAM] == ent->client->ps.persistant[PERS_TEAM] || G_smvLocateEntityInMVList( ent, level.sortedClients[i], qfalse ) ) {
				playerClass = cl->ps.stats[STAT_PLAYER_CLASS];
			} else {
				playerClass = 0;
			}

			// NERVE - SMF - number of respawns left
			respawnsLeft = cl->ps.persistant[PERS_RESPAWNS_LEFT];
			if ( g_gametype.integer == GT_WOLF_LMS ) {
				if ( g_entities[level.sortedClients[i]].health <= 0 ) {
					respawnsLeft = -2;
				}
			} else {
				if ( ( respawnsLeft == 0 && ( ( cl->ps.pm_flags & PMF_LIMBO ) || ( ( level.intermissiontime ) && g_entities[level.sortedClients[i]].health <= 0 ) ) ) ) {
					respawnsLeft = -2;
				}
			}

			if ( cl->pers.connected == CON_CONNECTING ) {
				ping = -1;
			} else {
				ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
			}

			if ( g_gametype.integer == GT_WOLF_LMS ) {
				Com_sprintf( entry, sizeof( entry ), " %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping,
							 ( level.time - cl->pers.enterTime ) / 60000, g_entities[level.sortedClients[i]].s.powerups, playerClass, respawnsLeft );
			} else {
				int j, totalXP;

				for ( totalXP = 0, j = 0; j < SK_NUM_SKILLS; j++ ) {
					totalXP += cl->sess.skillpoints[j];
				}

				Com_sprintf( entry, sizeof( entry ), " %i %i %i %i %i %i %i", level.sortedClients[i], totalXP, ping,
							 ( level.time - cl->pers.enterTime ) / 60000, g_entities[level.sortedClients[i]].s.powerups, playerClass, respawnsLeft );
			}

			if ( size + strlen( entry ) > 1000 ) {
				i--; // we need to redo this client in the next buffer (if we can)
				break;
			}
			size += strlen( entry );

			Q_strcat( buffer, 1024, entry );
			if ( ++count >= 32 ) {
				i--; // we need to redo this client in the next buffer (if we can)
				break;
			}
		}

		if ( count > 0 || team == 0 ) {
			trap_SendServerCommand( ent - g_entities, va( "%s %i%s", startbuffer, count, buffer ) );
		}
	}
}

/*
==================
Cmd_Score_f

Request current scoreboard information
==================
*/
void Cmd_Score_f( gentity_t *ent ) {
	ent->client->wantsscore = qtrue;
//	G_SendScore( 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, qboolean fToLower ) {
	while ( *in ) {
		if ( *in == 27 || *in == '^' ) {
			in++;       // skip color code
			if ( *in ) {
				in++;
			}
			continue;
		}

		if ( *in < 32 ) {
			in++;
			continue;
		}

		*out++ = ( fToLower ) ? tolower( *in++ ) : *in++;
	}

	*out = 0;
}

/*
==================
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];
	qboolean fIsNumber = qtrue;

	// See if its a number or string
	for ( idnum = 0; idnum < strlen( s ) && s[idnum] != 0; idnum++ ) {
		if ( s[idnum] < '0' || s[idnum] > '9' ) {
			fIsNumber = qfalse;
			break;
		}
	}

	// check for a name match
	SanitizeString( s, s2, qtrue );
	for ( idnum = 0, cl = level.clients; idnum < level.maxclients; idnum++, cl++ ) {
		if ( cl->pers.connected != CON_CONNECTED ) {
			continue;
		}

		SanitizeString( cl->pers.netname, n2, qtrue );
		if ( !strcmp( n2, s2 ) ) {
			return( idnum );
		}
	}

	// numeric values are just slot numbers
	if ( fIsNumber ) {
		idnum = atoi( s );
		if ( idnum < 0 || idnum >= level.maxclients ) {
			CPx( to - g_entities, va( "print \"Bad client slot: [lof]%i\n\"", idnum ) );
			return -1;
		}

		cl = &level.clients[idnum];
		if ( cl->pers.connected != CON_CONNECTED ) {
			CPx( to - g_entities, va( "print \"Client[lof] %i [lon]is not active\n\"", idnum ) );
			return -1;
		}
		return( idnum );
	}

	CPx( to - g_entities, va( "print \"User [lof]%s [lon]is not on the server\n\"", s ) );
	return( -1 );
}

/*
=================
Cmd_ListBotGoals_f

=================
*/

void Cmd_ListBotGoals_f( gentity_t* ent ) {
	int i;
	team_t t;

	if ( !CheatsOk( ent ) ) {
		return;
	}

	for ( t = TEAM_AXIS; t <= TEAM_ALLIES; t++ ) {
		gentity_t* list = g_entities, *targ;

		G_Printf( "\n%s bot goals\n=====================\n", ( t == TEAM_AXIS ? "Axis" : "Allies" ) );


		for ( i = 0; i < level.num_entities; i++, list++ ) {
			if ( !list->inuse ) {
				continue;
			}

			if ( !( list->aiInactive & ( 1 << t ) ) ) {
				G_Printf( "%s (%s)", ( list->scriptName ? list->scriptName : ( list->targetname ? list->targetname : "NONE" ) ), list->classname );
				if ( list->target_ent ) {
					targ = list->target_ent;
					G_Printf( " -> " );
					G_Printf( "%s (%s)", ( targ->scriptName ? targ->scriptName : ( targ->targetname ? targ->targetname : "NONE" ) ), targ->classname );
				}
				G_Printf( "\n" );
			}
		}
	}
}

/*
==================
Cmd_Give_f

Give items to a client
==================
*/
void Cmd_Give_f( gentity_t *ent ) {
	char        *name, *amt;
//	gitem_t		*it;
	int i;
	qboolean give_all;
//	gentity_t		*it_ent;
//	trace_t		trace;
	int amount;
	qboolean hasAmount = qfalse;

	if ( !CheatsOk( ent ) ) {
		return;
	}

	//----(SA)	check for an amount (like "give health 30")
	amt = ConcatArgs( 2 );
	if ( *amt ) {
		hasAmount = qtrue;
	}
	amount = atoi( amt );
	//----(SA)	end

	name = ConcatArgs( 1 );

	if ( Q_stricmp( name, "all" ) == 0 ) {
		give_all = qtrue;
	} else {
		give_all = qfalse;
	}

	if ( Q_stricmpn( name, "skill", 5 ) == 0 ) {
		if ( hasAmount ) {
			if ( amount >= 0 && amount < SK_NUM_SKILLS ) {
				G_AddSkillPoints( ent, amount, 20 );
				G_DebugAddSkillPoints( ent, amount, 20, "give skill" );
			}
		} else {
			// bumps all skills with 1 level
			for ( i = 0; i < SK_NUM_SKILLS; i++ ) {
				G_AddSkillPoints( ent, i, 20 );
				G_DebugAddSkillPoints( ent, i, 20, "give skill" );
			}
		}
		return;
	}

	if ( Q_stricmpn( name, "medal", 5 ) == 0 ) {
		for ( i = 0; i < SK_NUM_SKILLS; i++ ) {
			if ( !ent->client->sess.medals[i] ) {
				ent->client->sess.medals[i] = 1;
			}
		}
		ClientUserinfoChanged( ent - g_entities );
		return;
	}

	if ( give_all || Q_stricmpn( name, "health", 6 ) == 0 ) {
		//----(SA)	modified
		if ( amount ) {
			ent->health += amount;
		} else {
			ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
		}
		if ( !give_all ) {
			return;
		}
	}

	/*if ( Q_stricmpn( name, "damage", 6) == 0)
	{
		if(amount) {
			name = ConcatArgs( 3 );

			if( *name ) {
				int client = ClientNumberFromString( ent, name );
				if( client >= 0 ) {
					G_Damage( &g_entities[client], ent, ent, NULL, NULL, amount, DAMAGE_NO_PROTECTION, MOD_UNKNOWN );
				}
			} else {
				G_Damage( ent, ent, ent, NULL, NULL, amount, DAMAGE_NO_PROTECTION, MOD_UNKNOWN );
			}
		}

		return;
	}*/

	if ( give_all || Q_stricmp( name, "weapons" ) == 0 ) {
		for ( i = 0; i < WP_NUM_WEAPONS; i++ ) {
			if ( BG_WeaponInWolfMP( i ) ) {
				COM_BitSet( ent->client->ps.weapons, i );
			}
		}

		if ( !give_all ) {
			return;
		}
	}

	if ( give_all || Q_stricmpn( name, "ammo", 4 ) == 0 ) {
		if ( amount ) {
			if ( ent->client->ps.weapon
				 && ent->client->ps.weapon != WP_SATCHEL && ent->client->ps.weapon != WP_SATCHEL_DET
				 ) {
				Add_Ammo( ent, ent->client->ps.weapon, amount, qtrue );
			}
		} else {
			for ( i = 1 ; i < WP_NUM_WEAPONS ; i++ ) {
				if ( COM_BitCheck( ent->client->ps.weapons, i ) && i != WP_SATCHEL && i != WP_SATCHEL_DET ) {
					Add_Ammo( ent, i, 9999, qtrue );
				}
			}
		}

		if ( !give_all ) {
			return;
		}
	}

	//	"give allammo <n>" allows you to give a specific amount of ammo to /all/ weapons while
	//	allowing "give ammo <n>" to only give to the selected weap.
	if ( Q_stricmpn( name, "allammo", 7 ) == 0 && amount ) {
		for ( i = 1 ; i < WP_NUM_WEAPONS; i++ )
			Add_Ammo( ent, i, amount, qtrue );

		if ( !give_all ) {
			return;
		}
	}

	//---- (SA) Wolf keys
	if ( give_all || Q_stricmp( name, "keys" ) == 0 ) {
		ent->client->ps.stats[STAT_KEYS] = ( 1 << KEY_NUM_KEYS ) - 2;
		if ( !give_all ) {
			return;
		}
	}
	//---- (SA) end

	// spawn a specific item right on the player
	/*if ( !give_all ) {
		it = BG_FindItem (name);
		if (!it) {
			return;
		}

		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 ) );
		it_ent->active = qtrue;
		Touch_Item (it_ent, ent, &trace);
		it_ent->active = qfalse;
		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;
	char    *name;
	qboolean godAll = qfalse;

	if ( !CheatsOk( ent ) ) {
		return;
	}

	name = ConcatArgs( 1 );

	// are we supposed to make all our teammates gods too?
	if ( Q_stricmp( name, "all" ) == 0 ) {
		godAll = qtrue;
	}

	// can only use this cheat in single player
	if ( godAll && g_gametype.integer == GT_SINGLE_PLAYER ) {
		int j;
		qboolean settingFlag = qtrue;
		gentity_t *other;

		// are we turning it on or off?
		if ( ent->flags & FL_GODMODE ) {
			settingFlag = qfalse;
		}

		// loop through all players
		for ( j = 0; j < level.maxclients; j++ )
		{
			other = &g_entities[j];
			// if they're on the same team
			if ( OnSameTeam( other, ent ) ) {
				// set or clear the flag
				if ( settingFlag ) {
					other->flags |= FL_GODMODE;
				} else {
					other->flags &= ~FL_GODMODE;
				}
			}
		}
		if ( settingFlag ) {
			msg = "godmode all ON\n";
		} else {
			msg = "godmode all OFF\n";
		}
	} else
	{
		if ( !Q_stricmp( name, "on" ) || atoi( name ) ) {
			ent->flags |= FL_GODMODE;
		} else if ( !Q_stricmp( name, "off" ) || !Q_stricmp( name, "0" ) ) {
			ent->flags &= ~FL_GODMODE;
		} else {
			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_Nofatigue_f

Sets client to nofatigue

argv(0) nofatigue
==================
*/

void Cmd_Nofatigue_f( gentity_t *ent ) {
	char    *msg;

	char    *name = ConcatArgs( 1 );

	if ( !CheatsOk( ent ) ) {
		return;
	}

	if ( !Q_stricmp( name, "on" ) || atoi( name ) ) {
		ent->flags |= FL_NOFATIGUE;
	} else if ( !Q_stricmp( name, "off" ) || !Q_stricmp( name, "0" ) ) {
		ent->flags &= ~FL_NOFATIGUE;
	} else {
		ent->flags ^= FL_NOFATIGUE;
	}

	if ( !( ent->flags & FL_NOFATIGUE ) ) {
		msg = "nofatigue OFF\n";
	} else {
		msg = "nofatigue 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;

	char    *name = ConcatArgs( 1 );

	if ( !CheatsOk( ent ) ) {
		return;
	}

	if ( !Q_stricmp( name, "on" ) || atoi( name ) ) {
		ent->client->noclip = qtrue;
	} else if ( !Q_stricmp( name, "off" ) || !Q_stricmp( name, "0" ) ) {
		ent->client->noclip = qfalse;
	} else {
		ent->client->noclip = !ent->client->noclip;
	}

	if ( ent->client->noclip ) {
		msg = "noclip ON\n";
	} else {
		msg = "noclip OFF\n";
	}

	trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
}

/*
=================
Cmd_Kill_f
=================
*/
void Cmd_Kill_f( gentity_t *ent ) {
	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ||
		 ( ent->client->ps.pm_flags & PMF_LIMBO ) ||
		 ent->health <= 0 || level.match_pause != PAUSE_NONE ) {
		return;
	}

#ifdef SAVEGAME_SUPPORT
	if ( g_gametype.integer == GT_SINGLE_PLAYER && g_reloading.integer ) {
		return;
	}
#endif // SAVEGAME_SUPPORT

	ent->flags &= ~FL_GODMODE;
	ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
	ent->client->ps.persistant[PERS_HWEAPON_USE] = 0; // TTimo - if using /kill while at MG42
	player_die( ent, ent, ent, ( g_gamestate.integer == GS_PLAYING ) ? 100000 : 135, MOD_SUICIDE );
}

void BotRecordTeamChange( int client );

void G_TeamDataForString( const char* teamstr, int clientNum, team_t* team, spectatorState_t* sState, int* specClient ) {
	*sState = SPECTATOR_NOT;
	if ( !Q_stricmp( teamstr, "follow1" ) ) {
		*team =     TEAM_SPECTATOR;
		*sState =   SPECTATOR_FOLLOW;
		if ( specClient ) {
			*specClient = -1;
		}
	} else if ( !Q_stricmp( teamstr, "follow2" ) ) {
		*team =     TEAM_SPECTATOR;
		*sState =   SPECTATOR_FOLLOW;
		if ( specClient ) {
			*specClient = -2;
		}
	} else if ( !Q_stricmp( teamstr, "spectator" ) || !Q_stricmp( teamstr, "s" ) ) {
		*team =     TEAM_SPECTATOR;
		*sState =   SPECTATOR_FREE;
	} else if ( !Q_stricmp( teamstr, "red" ) || !Q_stricmp( teamstr, "r" ) || !Q_stricmp( teamstr, "axis" ) ) {
		*team =     TEAM_AXIS;
	} else if ( !Q_stricmp( teamstr, "blue" ) || !Q_stricmp( teamstr, "b" ) || !Q_stricmp( teamstr, "allies" ) ) {
		*team =     TEAM_ALLIES;
	} else {
		*team = PickTeam( clientNum );
		if ( !G_teamJoinCheck( *team, &g_entities[clientNum] ) ) {
			*team = ( ( TEAM_AXIS | TEAM_ALLIES ) & ~*team );
		}
	}
}

/*
=================
SetTeam
=================
*/
qboolean SetTeam( gentity_t *ent, char *s, qboolean force, weapon_t w1, weapon_t w2, qboolean setweapons ) {
	team_t team, oldTeam;
	gclient_t           *client;
	int clientNum;
	spectatorState_t specState;
	int specClient;
	int respawnsLeft;

	//
	// see what change is requested
	//
	client = ent->client;

	clientNum = client - level.clients;
	specClient = 0;

	// ydnar: preserve respawn count
	respawnsLeft = client->ps.persistant[ PERS_RESPAWNS_LEFT ];

	G_TeamDataForString( s, client - level.clients, &team, &specState, &specClient );

	if ( team != TEAM_SPECTATOR ) {
		// Ensure the player can join
		if ( !G_teamJoinCheck( team, ent ) ) {
			// Leave them where they were before the command was issued
			return( qfalse );
		}

		if ( g_noTeamSwitching.integer && ( team != ent->client->sess.sessionTeam && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) && g_gamestate.integer == GS_PLAYING && !force ) {
			trap_SendServerCommand( clientNum, "cp \"You cannot switch during a match, please wait until the round ends.\n\"" );
			return qfalse;  // ignore the request
		}

		if ( ( ( g_gametype.integer == GT_WOLF_LMS && g_lms_teamForceBalance.integer ) || g_teamForceBalance.integer ) && !force ) {
			int counts[TEAM_NUM_TEAMS];

			counts[TEAM_ALLIES] = TeamCount( ent - g_entities, TEAM_ALLIES );
			counts[TEAM_AXIS] = TeamCount( ent - g_entities, TEAM_AXIS );

			// We allow a spread of one
			if ( team == TEAM_AXIS && counts[TEAM_AXIS] - counts[TEAM_ALLIES] >= 1 ) {
				CP( "cp \"The Axis has too many players.\n\"" );
				return qfalse; // ignore the request
			}
			if ( team == TEAM_ALLIES && counts[TEAM_ALLIES] - counts[TEAM_AXIS] >= 1 ) {
				CP( "cp \"The Allies have too many players.\n\"" );
				return qfalse; // ignore the request
			}

			// It's ok, the team we are switching to has less or same number of players
		}
	}

	if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) {
		team = TEAM_SPECTATOR;
	}

	//
	// decide if we will allow the change
	//
	oldTeam = client->sess.sessionTeam;
	if ( team == oldTeam && team != TEAM_SPECTATOR ) {
		return qfalse;
	}

	// NERVE - SMF - prevent players from switching to regain deployments
	if ( g_gametype.integer != GT_WOLF_LMS ) {
		if ( ( g_maxlives.integer > 0 ||
			   ( g_alliedmaxlives.integer > 0 && ent->client->sess.sessionTeam == TEAM_ALLIES ) ||
			   ( g_axismaxlives.integer > 0 && ent->client->sess.sessionTeam == TEAM_AXIS ) )

			 && ent->client->ps.persistant[PERS_RESPAWNS_LEFT] == 0 && oldTeam != TEAM_SPECTATOR ) {
			CP( "cp \"You can't switch teams because you are out of lives.\n\" 3" );
			return qfalse;  // ignore the request
		}
	}

	// DHM - Nerve :: Force players to wait 30 seconds before they can join a new team.
	// OSP - changed to 5 seconds
	// Gordon: disabling if in dev mode: cuz it sucks
	// Gordon: bleh, half of these variables don't mean what they used to, so this doesn't work
/*	if ( !g_cheats.integer ) {
		 if ( team != oldTeam && level.warmupTime == 0 && !client->pers.initialSpawn && ( (level.time - client->pers.connectTime) > 10000 ) && ( (level.time - client->pers.enterTime) < 5000 ) && !force ) {
			CP(va("cp \"^3You must wait %i seconds before joining ^3a new team.\n\" 3", (int)(5 - ((level.time - client->pers.enterTime)/1000))));
			return qfalse;
		}
	}*/
	// dhm

	//
	// execute the team change
	//


	// DHM - Nerve
	// OSP
	if ( team != TEAM_SPECTATOR ) {
		client->pers.initialSpawn = qfalse;
		// no MV in-game
		if ( client->pers.mvCount > 0 ) {
			G_smvRemoveInvalidClients( ent, TEAM_AXIS );
			G_smvRemoveInvalidClients( ent, TEAM_ALLIES );
		}
	}

	// he starts at 'base'
	// RF, in single player, bots always use regular spawn points
	if ( !( ( g_gametype.integer == GT_SINGLE_PLAYER || g_gametype.integer == GT_COOP ) && ( ent->r.svFlags & SVF_BOT ) ) ) {
		client->pers.teamState.state = TEAM_BEGIN;
	}

	if ( oldTeam != TEAM_SPECTATOR ) {
		if ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) {
			// 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_SWITCHTEAM );
		}
	}
	// they go to the end of the line for tournements
	if ( team == TEAM_SPECTATOR ) {
		client->sess.spectatorTime = level.time;
		if ( !client->sess.referee ) {
			client->pers.invite = 0;
		}
		if ( team != oldTeam ) {
			G_smvAllRemoveSingleClient( ent - g_entities );
		}
	}

	G_LeaveTank( ent, qfalse );
	G_RemoveClientFromFireteams( clientNum, qtrue, qfalse );
	if ( g_landminetimeout.integer ) {
		G_ExplodeMines( ent );
	}
	G_FadeItems( ent, MOD_SATCHEL );

	// remove ourself from teamlists
	{
		int i;
		mapEntityData_t *mEnt;
		mapEntityData_Team_t *teamList;

		for ( i = 0; i < 2; i++ ) {
			teamList = &mapEntityData[i];

			if ( ( mEnt = G_FindMapEntityData( &mapEntityData[0], ent - g_entities ) ) != NULL ) {
				G_FreeMapEntityData( teamList, mEnt );
			}

			mEnt = G_FindMapEntityDataSingleClient( teamList, NULL, ent->s.number, -1 );

			while ( mEnt ) {
				mapEntityData_t *mEntFree = mEnt;

				mEnt = G_FindMapEntityDataSingleClient( teamList, mEnt, ent->s.number, -1 );

				G_FreeMapEntityData( teamList, mEntFree );
			}
		}
	}
	client->sess.spec_team = 0;
	client->sess.sessionTeam = team;
	client->sess.spectatorState = specState;
	client->sess.spectatorClient = specClient;
	client->pers.ready = qfalse;

	// (l)users will spam spec messages... honest!
	if ( team != oldTeam ) {
		gentity_t* tent = G_PopupMessage( PM_TEAM );
		tent->s.effect2Time = team;
		tent->s.effect3Time = clientNum;
		tent->s.density = 0;
	}

	if ( setweapons ) {
		G_SetClientWeapons( ent, w1, w2, qfalse );
	}

	// get and distribute relevent paramters
	G_UpdateCharacter( client );            // FIXME : doesn't ClientBegin take care of this already?
	ClientUserinfoChanged( clientNum );

	ClientBegin( clientNum );

	// ydnar: restore old respawn count (players cannot jump from team to team to regain lives)
	if ( respawnsLeft >= 0 && oldTeam != TEAM_SPECTATOR ) {
		client->ps.persistant[ PERS_RESPAWNS_LEFT ] = respawnsLeft;
	}

	G_verifyMatchState( oldTeam );
	BotRecordTeamChange( clientNum );

	// Reset stats when changing teams
	if ( team != oldTeam ) {
		G_deleteStats( clientNum );
	}

	G_UpdateSpawnCounts();

	if ( g_gamestate.integer == GS_PLAYING && ( client->sess.sessionTeam == TEAM_AXIS || client->sess.sessionTeam == TEAM_ALLIES ) ) {
		if ( g_gametype.integer == GT_WOLF_LMS && level.numTeamClients[0] > 0 && level.numTeamClients[1] > 0 ) {
			trap_SendServerCommand( clientNum, "cp \"Will spawn next round, please wait.\n\"" );
			limbo( ent, qfalse );
			return( qfalse );
		} else {
			int i;
			int x = client->sess.sessionTeam - TEAM_AXIS;

			for ( i = 0; i < MAX_COMMANDER_TEAM_SOUNDS; i++ ) {
				if ( level.commanderSounds[ x ][ i ].index ) {
					gentity_t* tent = G_TempEntity( client->ps.origin, EV_GLOBAL_CLIENT_SOUND );
					tent->s.eventParm = level.commanderSounds[ x ][ i ].index - 1;
					tent->s.teamNum = clientNum;
				}
			}
		}
	}

	ent->client->pers.autofireteamCreateEndTime = 0;
	ent->client->pers.autofireteamJoinEndTime = 0;

	if ( client->sess.sessionTeam == TEAM_AXIS || client->sess.sessionTeam == TEAM_ALLIES ) {
		if ( g_autoFireteams.integer ) {
			fireteamData_t* ft = G_FindFreePublicFireteam( client->sess.sessionTeam );

			if ( ft ) {
				trap_SendServerCommand( ent - g_entities, "aftj -1" );
				ent->client->pers.autofireteamJoinEndTime = level.time + 20500;

//				G_AddClientToFireteam( ent-g_entities, ft->joinOrder[0] );
			} else {
				trap_SendServerCommand( ent - g_entities, "aftc -1" );
				ent->client->pers.autofireteamCreateEndTime = level.time + 20500;
			}
		}
	}

	return qtrue;
}

/*
=================
StopFollowing

If the client being followed leaves the game, or you just want to drop
to free floating spectator mode
=================
*/
void StopFollowing( gentity_t *ent ) {
	// ATVI Wolfenstein Misc #474
	// divert behaviour if TEAM_SPECTATOR, moved the code from SpectatorThink to put back into free fly correctly
	// (I am not sure this can be called in non-TEAM_SPECTATOR situation, better be safe)
	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
		// drop to free floating, somewhere above the current position (that's the client you were following)
		vec3_t pos, angle;
		gclient_t   *client = ent->client;
		VectorCopy( client->ps.origin, pos );
//		pos[2] += 16; // Gordon: removing for now
		VectorCopy( client->ps.viewangles, angle );
		// Need this as it gets spec mode reset properly
		SetTeam( ent, "s", qtrue, -1, -1, qfalse );
		VectorCopy( pos, client->ps.origin );
		SetClientViewAngle( ent, angle );
	} else {
		// legacy code, FIXME: useless?
		// Gordon: no this is for limbo i'd guess
		ent->client->sess.spectatorState = SPECTATOR_FREE;
		ent->client->ps.clientNum = ent - g_entities;
	}
}

int G_NumPlayersWithWeapon( weapon_t weap, team_t team ) {
	int i, j, cnt = 0;

	for ( i = 0; i < level.numConnectedClients; i++ ) {
		j = level.sortedClients[i];

		if ( level.clients[j].sess.playerType != PC_SOLDIER ) {
			continue;
		}

		if ( level.clients[j].sess.sessionTeam != team ) {
			continue;
		}

		if ( level.clients[j].sess.latchPlayerWeapon != weap && level.clients[j].sess.playerWeapon != weap ) {
			continue;
		}

		cnt++;
	}

	return cnt;
}

int G_NumPlayersOnTeam( team_t team ) {
	int i, j, cnt = 0;

	for ( i = 0; i < level.numConnectedClients; i++ ) {
		j = level.sortedClients[i];

		if ( level.clients[j].sess.sessionTeam != team ) {
			continue;
		}

		cnt++;
	}

	return cnt;
}

qboolean G_IsHeavyWeapon( weapon_t weap ) {
	int i;

	for ( i = 0; i < NUM_HEAVY_WEAPONS; i++ ) {
		if ( bg_heavyWeapons[i] == weap ) {
			return qtrue;
		}
	}

	return qfalse;
}

int G_TeamCount( gentity_t* ent, weapon_t weap ) {
	int i, j, cnt;

	if ( weap == -1 ) { // we aint checking for a weapon, so always include ourselves
		cnt = 1;
	} else { // we ARE checking for a weapon, so ignore ourselves
		cnt = 0;
	}

	for ( i = 0; i < level.numConnectedClients; i++ ) {
		j = level.sortedClients[i];

		if ( j == ent - g_entities ) {
			continue;
		}

		if ( level.clients[j].sess.sessionTeam != ent->client->sess.sessionTeam ) {
			continue;
		}

		if ( weap != -1 ) {
			if ( level.clients[j].sess.playerWeapon != weap && level.clients[j].sess.latchPlayerWeapon != weap ) {
				continue;
			}
		}

		cnt++;
	}

	return cnt;
}

qboolean G_IsWeaponDisabled( gentity_t* ent, weapon_t weapon ) {
	int count, wcount;

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
		return qtrue;
	}

	if ( !G_IsHeavyWeapon( weapon ) ) {
		return qfalse;
	}

	count =     G_TeamCount( ent, -1 );
	wcount =    G_TeamCount( ent, weapon );

	if ( wcount >= ceil( count * g_heavyWeaponRestriction.integer * 0.01f ) ) {
		return qtrue;
	}

	return qfalse;
}

void G_SetClientWeapons( gentity_t* ent, weapon_t w1, weapon_t w2, qboolean updateclient ) {
	qboolean changed = qfalse;

	if ( ent->client->sess.latchPlayerWeapon2 != w2 ) {
		ent->client->sess.latchPlayerWeapon2 = w2;
		changed = qtrue;
	}

	if ( !G_IsWeaponDisabled( ent, w1 ) ) {
		if ( ent->client->sess.latchPlayerWeapon != w1 ) {
			ent->client->sess.latchPlayerWeapon = w1;
			changed = qtrue;
		}
	} else {
		if ( ent->client->sess.latchPlayerWeapon != 0 ) {
			ent->client->sess.latchPlayerWeapon = 0;
			changed = qtrue;
		}
	}

	if ( updateclient && changed ) {
		ClientUserinfoChanged( ent - g_entities );
	}
}


/*
=================
Cmd_Team_f
=================
*/
void Cmd_Team_f( gentity_t *ent, unsigned int dwCommand, qboolean fValue ) {
	char s[MAX_TOKEN_CHARS];
	char ptype[4];
	char weap[4], weap2[4];
	weapon_t w, w2;

	if ( trap_Argc() < 2 ) {
		char *pszTeamName;

		switch ( ent->client->sess.sessionTeam ) {
		case TEAM_ALLIES:
			pszTeamName = "Allies";
			break;
		case TEAM_AXIS:
			pszTeamName = "Axis";
			break;
		case TEAM_SPECTATOR:
			pszTeamName = "Spectator";
			break;
		case TEAM_FREE:
		default:
			pszTeamName = "Free";
			break;
		}

		CP( va( "print \"%s team\n\"", pszTeamName ) );
		return;
	}

	trap_Argv( 1, s,        sizeof( s       ) );
	trap_Argv( 2, ptype,    sizeof( ptype   ) );
	trap_Argv( 3, weap,     sizeof( weap    ) );
	trap_Argv( 4, weap2,    sizeof( weap2   ) );

	w =     atoi( weap );
	w2 =    atoi( weap2 );

	ent->client->sess.latchPlayerType = atoi( ptype );
	if ( ent->client->sess.latchPlayerType < PC_SOLDIER || ent->client->sess.latchPlayerType > PC_COVERTOPS ) {
		ent->client->sess.latchPlayerType = PC_SOLDIER;
	}

	if ( ent->client->sess.latchPlayerType < PC_SOLDIER || ent->client->sess.latchPlayerType > PC_COVERTOPS ) {
		ent->client->sess.latchPlayerType = PC_SOLDIER;
	}

	if ( !SetTeam( ent, s, qfalse, w, w2, qtrue ) ) {
		G_SetClientWeapons( ent, w, w2, qtrue );
	}
}

void Cmd_ResetSetup_f( gentity_t* ent ) {
	qboolean changed = qfalse;

	if ( !ent || !ent->client ) {
		return;
	}

	ent->client->sess.latchPlayerType =     ent->client->sess.playerType;

	if ( ent->client->sess.latchPlayerWeapon != ent->client->sess.playerWeapon ) {
		ent->client->sess.latchPlayerWeapon = ent->client->sess.playerWeapon;
		changed = qtrue;
	}

	if ( ent->client->sess.latchPlayerWeapon2 != ent->client->sess.playerWeapon2 ) {
		ent->client->sess.latchPlayerWeapon2 =  ent->client->sess.playerWeapon2;
		changed = qtrue;
	}

	if ( changed ) {
		ClientUserinfoChanged( ent - g_entities );
	}
}

void Cmd_SetClass_f( gentity_t* ent, unsigned int dwCommand, qboolean fValue ) {
}

void Cmd_SetWeapons_f( gentity_t* ent, unsigned int dwCommand, qboolean fValue ) {
}



// START Mad Doc - TDF
/*
=================
Cmd_TeamBot_f
=================
*/
void Cmd_TeamBot_f( gentity_t *foo ) {
	char ptype[4], weap[4], fireteam[4];
	char entNumStr[4];
	int entNum;
	char *weapon;
	char weaponBuf[MAX_INFO_STRING];
	char userinfo[MAX_INFO_STRING];

	gentity_t *ent;

	trap_Argv( 1, entNumStr, sizeof( entNumStr ) );
	entNum = atoi( entNumStr );

	ent = g_entities + entNum;

	trap_Argv( 3, ptype, sizeof( ptype ) );
	trap_Argv( 4, weap, sizeof( weap ) );
	trap_Argv( 5, fireteam, sizeof( fireteam ) );



	ent->client->sess.latchPlayerType = atoi( ptype );
	ent->client->sess.latchPlayerWeapon = atoi( weap );
	ent->client->sess.latchPlayerWeapon2 = 0;
	ent->client->sess.playerType = atoi( ptype );
	ent->client->sess.playerWeapon = atoi( weap );

	// remove any weapon info from the userinfo, so SetWolfSpawnWeapons() doesn't reset the weapon as that
	trap_GetUserinfo( entNum, userinfo, sizeof( userinfo ) );

	weapon = Info_ValueForKey( userinfo, "pWeapon" );
	if ( weapon[0] ) {
		Q_strncpyz( weaponBuf, weapon, sizeof( weaponBuf ) );
		Info_RemoveKey( userinfo, "pWeapon" );
		trap_SetUserinfo( entNum, userinfo );
	}

	SetWolfSpawnWeapons( ent->client );
}

// END Mad Doc - TDF
/*
=================
Cmd_Follow_f
=================
*/
void Cmd_Follow_f( gentity_t *ent, unsigned int dwCommand, qboolean fValue ) {
	int i;
	char arg[MAX_TOKEN_CHARS];

	if ( trap_Argc() != 2 ) {
		if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
			StopFollowing( ent );
		}
		return;
	}

	if ( ent->client->ps.pm_flags & PMF_LIMBO ) {
		CP( "cpm \"Can't issue a follow command while in limbo.\n\"" );
		CP( "cpm \"Hit FIRE to switch between teammates.\n\"" );
		return;
	}

	trap_Argv( 1, arg, sizeof( arg ) );
	i = ClientNumberFromString( ent, arg );
	if ( i == -1 ) {
		if ( !Q_stricmp( arg, "allies" ) ) {
			i = TEAM_ALLIES;
		} else if ( !Q_stricmp( arg, "axis" ) ) {
			i = TEAM_AXIS;
		} else { return;}

		if ( !TeamCount( ent - g_entities, i ) ) {
			CP( va( "print \"The %s team %s empty!  Follow command ignored.\n\"", aTeams[i],
					( ( ent->client->sess.sessionTeam != i ) ? "is" : "would be" ) ) );
			return;
		}

		// Allow for simple toggle
		if ( ent->client->sess.spec_team != i ) {
			if ( teamInfo[i].spec_lock && !( ent->client->sess.spec_invite & i ) ) {
				CP( va( "print \"Sorry, the %s team is locked from spectators.\n\"", aTeams[i] ) );
			} else {
				ent->client->sess.spec_team = i;
				CP( va( "print \"Spectator follow is now locked on the %s team.\n\"", aTeams[i] ) );
				Cmd_FollowCycle_f( ent, 1 );
			}
		} else {
			ent->client->sess.spec_team = 0;
			CP( va( "print \"%s team spectating is now disabled.\n\"", aTeams[i] ) );
		}

		return;
	}

	// can't follow self
	if ( &level.clients[ i ] == ent->client ) {
		return;
	}

	// can't follow another spectator
	if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) {
		return;
	}
	if ( level.clients[ i ].ps.pm_flags & PMF_LIMBO ) {
		return;
	}

	// OSP - can't follow a player on a speclocked team, unless allowed
	if ( !G_allowFollow( ent, level.clients[i].sess.sessionTeam ) ) {
		CP( va( "print \"Sorry, the %s team is locked from spectators.\n\"", aTeams[level.clients[i].sess.sessionTeam] ) );
		return;
	}

	// first set them to spectator
	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
		SetTeam( ent, "spectator", qfalse, -1, -1, qfalse );
	}

	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 original;

	// first set them to spectator
	if ( ( ent->client->sess.spectatorState == SPECTATOR_NOT ) && ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) ) { // JPW NERVE for limbo state
		SetTeam( ent, "spectator", qfalse, -1, -1, qfalse );
	}

	if ( dir != 1 && dir != -1 ) {
		G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
	}

	clientnum = ent->client->sess.spectatorClient;
	original = clientnum;
	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 ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) {
			continue;
		}

		// JPW NERVE -- couple extra checks for limbo mode
		if ( ent->client->ps.pm_flags & PMF_LIMBO ) {
			if ( level.clients[clientnum].ps.pm_flags & PMF_LIMBO ) {
				continue;
			}
			if ( level.clients[clientnum].sess.sessionTeam != ent->client->sess.sessionTeam ) {
				continue;
			}
		}

		if ( level.clients[clientnum].ps.pm_flags & PMF_LIMBO ) {
			continue;
		}

		// OSP
		if ( !G_desiredFollow( ent, level.clients[clientnum].sess.sessionTeam ) ) {
			continue;
		}

		// this is good, we can use it
		ent->client->sess.spectatorClient = clientnum;
		ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
		return;
	} while ( clientnum != original );

	// leave it where it was
}


/*======================
G_EntitySound
	Mad Doc xkan, 11/06/2002 -

	Plays a sound (wav file or sound script) on this entity

	Note that calling G_AddEvent(..., EV_GENERAL_SOUND, ...) has the danger of
	the event never getting through to the client because the entity might not
	be visible (unless it has the SVF_BROADCAST flag), so if you want to make sure
	the sound is heard, call this function instead.
======================*/
void G_EntitySound(
	gentity_t *ent,         // entity to play the sound on
	const char *soundId,    // sound file name or sound script ID
	int volume ) {           // sound volume, only applies to sound file name call
							 //   for sound script, volume is currently always 127.
	trap_SendServerCommand( -1, va( "entitySound %d %s %d %i %i %i normal", ent->s.number, soundId, volume,
									(int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) );
}

/*======================
G_EntitySoundNoCut
	Mad Doc xkan, 1/16/2003 -

	Similar to G_EntitySound, but do not cut this sound off

======================*/
void G_EntitySoundNoCut(
	gentity_t *ent,         // entity to play the sound on
	const char *soundId,    // sound file name or sound script ID
	int volume ) {           // sound volume, only applies to sound file name call
							 //   for sound script, volume is currently always 127.
	trap_SendServerCommand( -1, va( "entitySound %d %s %d %i %i %i noCut", ent->s.number, soundId, volume,
									(int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) );
}


/*
==================
G_Say
==================
*/
#define MAX_SAY_TEXT    150

void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message, qboolean localize ) {
	if ( !other || !other->inuse || !other->client ) {
		return;
	}
	if ( ( mode == SAY_TEAM || mode == SAY_TEAMNL ) && !OnSameTeam( ent, other ) ) {
		return;
	}

	// NERVE - SMF - if spectator, no chatting to players in WolfMP
	if ( match_mutespecs.integer > 0 && ent->client->sess.referee == 0 &&   // OSP
		 ( ( ent->client->sess.sessionTeam == TEAM_FREE && other->client->sess.sessionTeam != TEAM_FREE ) ||
		   ( ent->client->sess.sessionTeam == TEAM_SPECTATOR && other->client->sess.sessionTeam != TEAM_SPECTATOR ) ) ) {
		return;
	} else {
		if ( mode == SAY_BUDDY ) {  // send only to people who have the sender on their buddy list
			if ( ent->s.clientNum != other->s.clientNum ) {
				fireteamData_t *ft1, *ft2;
				if ( !G_IsOnFireteam( other - g_entities, &ft1 ) ) {
					return;
				}
				if ( !G_IsOnFireteam( ent - g_entities, &ft2 ) ) {
					return;
				}
				if ( ft1 != ft2 ) {
					return;
				}
			}
		}

		trap_SendServerCommand( other - g_entities, va( "%s \"%s%c%c%s\" %i %i", mode == SAY_TEAM || mode == SAY_BUDDY ? "tchat" : "chat", name, Q_COLOR_ESCAPE, color, message, ent - g_entities, localize ) );
	}
}

void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) {
	int j;
	gentity_t   *other;
	int color;
	char name[64];
	// don't let text be too long for malicious reasons
	char text[MAX_SAY_TEXT];
	qboolean localize = qfalse;
	char        *loc;

	switch ( mode ) {
	default:
	case SAY_ALL:
		G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
		Com_sprintf( name, sizeof( name ), "%s%c%c: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
		color = COLOR_GREEN;
		break;
	case SAY_BUDDY:
		localize = qtrue;
		G_LogPrintf( "saybuddy: %s: %s\n", ent->client->pers.netname, chatText );
		loc = BG_GetLocationString( ent->r.currentOrigin );
		Com_sprintf( name, sizeof( name ), "[lof](%s%c%c) (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, loc );
		color = COLOR_YELLOW;
		break;
	case SAY_TEAM:
		localize = qtrue;
		G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
		loc = BG_GetLocationString( ent->r.currentOrigin );
		Com_sprintf( name, sizeof( name ), "[lof](%s%c%c) (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, loc );
		color = COLOR_CYAN;
		break;
	case SAY_TEAMNL:
		G_LogPrintf( "sayteamnl: %s: %s\n", ent->client->pers.netname, chatText );
		Com_sprintf( name, sizeof( name ), "(%s^7): ", ent->client->pers.netname );
		color = COLOR_CYAN;
		break;
	}

	Q_strncpyz( text, chatText, sizeof( text ) );

	if ( target ) {
		if ( !COM_BitCheck( target->client->sess.ignoreClients, ent - g_entities ) ) {
			G_SayTo( ent, target, mode, color, name, text, localize );
		}
		return;
	}

	// echo the text to the console
	if ( g_dedicated.integer ) {
		G_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]];
		if ( !COM_BitCheck( other->client->sess.ignoreClients, ent - g_entities ) ) {
			G_SayTo( ent, other, mode, color, name, text, localize );
		}
	}
}


/*
==================
Cmd_Say_f
==================
*/
void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
	if ( trap_Argc() < 2 && !arg0 ) {
		return;
	}
	G_Say( ent, NULL, mode, ConcatArgs( ( ( arg0 ) ? 0 : 1 ) ) );
}

extern void BotRecordVoiceChat( int client, int destclient, const char *id, int mode, qboolean noResponse );

// NERVE - SMF
void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) {
	int color;
	char *cmd;

	if ( !other ) {
		return;
	}
	if ( !other->inuse ) {
		return;
	}
	if ( !other->client ) {
		return;
	}
	if ( mode == SAY_TEAM && !OnSameTeam( ent, other ) ) {
		return;
	}

	// OSP - spec vchat rules follow the same as normal chatting rules
	if ( match_mutespecs.integer > 0 && ent->client->sess.referee == 0 &&
		 ent->client->sess.sessionTeam == TEAM_SPECTATOR && other->client->sess.sessionTeam != TEAM_SPECTATOR ) {
		return;
	}

	// send only to people who have the sender on their buddy list
	if ( mode == SAY_BUDDY ) {
		if ( ent->s.clientNum != other->s.clientNum ) {
			fireteamData_t *ft1, *ft2;
			if ( !G_IsOnFireteam( other - g_entities, &ft1 ) ) {
				return;
			}
			if ( !G_IsOnFireteam( ent - g_entities, &ft2 ) ) {
				return;
			}
			if ( ft1 != ft2 ) {
				return;
			}
		}
	}

	if ( mode == SAY_TEAM ) {
		color = COLOR_CYAN;
		cmd = "vtchat";
	} else if ( mode == SAY_BUDDY ) {
		color = COLOR_YELLOW;
		cmd = "vbchat";
	} else {
		color = COLOR_GREEN;
		cmd = "vchat";
	}

	// RF, record this chat so bots can parse them
	// bots respond with voiceonly, so we check for this so they dont keep responding to responses
	BotRecordVoiceChat( ent->s.number, other->s.number, id, mode, voiceonly == 2 );

	if ( voiceonly == 2 ) {
		voiceonly = qfalse;
	}

	if ( mode == SAY_TEAM || mode == SAY_BUDDY ) {
		CPx( other - g_entities, va( "%s %d %d %d %s %i %i %i", cmd, voiceonly, ent - g_entities, color, id, (int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) );
	} else {
		CPx( other - g_entities, va( "%s %d %d %d %s", cmd, voiceonly, ent - g_entities, color, id ) );
	}
}

void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) {
	int j;

	// DHM - Nerve :: Don't allow excessive spamming of voice chats
	ent->voiceChatSquelch -= ( level.time - ent->voiceChatPreviousTime );
	ent->voiceChatPreviousTime = level.time;

	if ( ent->voiceChatSquelch < 0 ) {
		ent->voiceChatSquelch = 0;
	}

	// Only do the spam check for MP
	if ( ent->voiceChatSquelch >= 30000 ) {
		trap_SendServerCommand( ent - g_entities, "cpm \"^1Spam Protection^7: VoiceChat ignored\n\"" );
		return;
	}

	if ( g_voiceChatsAllowed.integer ) {
		ent->voiceChatSquelch += ( 34000 / g_voiceChatsAllowed.integer );
	} else {
		return;
	}
	// dhm

	// OSP - Charge for the lame spam!
	/*if(mode == SAY_ALL && (!Q_stricmp(id, "DynamiteDefused") || !Q_stricmp(id, "DynamitePlanted"))) {
		return;
	}*/

	if ( target ) {
		G_VoiceTo( ent, target, mode, id, voiceonly );
		return;
	}

	// echo the text to the console
	if ( g_dedicated.integer ) {
		G_Printf( "voice: %s %s\n", ent->client->pers.netname, id );
	}

	if ( mode == SAY_BUDDY ) {
		char buffer[32];
		int cls = -1, i, cnt, num;
		qboolean allowclients[MAX_CLIENTS];

		memset( allowclients, 0, sizeof( allowclients ) );

		trap_Argv( 1, buffer, 32 );

		cls = atoi( buffer );

		trap_Argv( 2, buffer, 32 );
		cnt = atoi( buffer );
		if ( cnt > MAX_CLIENTS ) {
			cnt = MAX_CLIENTS;
		}

		for ( i = 0; i < cnt; i++ ) {
			trap_Argv( 3 + i, buffer, 32 );

			num = atoi( buffer );
			if ( num < 0 ) {
				continue;
			}
			if ( num >= MAX_CLIENTS ) {
				continue;
			}

			allowclients[ num ] = qtrue;
		}

		for ( j = 0; j < level.numConnectedClients; j++ ) {

			if ( level.sortedClients[j] != ent->s.clientNum ) {
				if ( cls != -1 && cls != level.clients[level.sortedClients[j]].sess.playerType ) {
					continue;
				}
			}

			if ( cnt ) {
				if ( !allowclients[ level.sortedClients[j] ] ) {
					continue;
				}
			}

			G_VoiceTo( ent, &g_entities[level.sortedClients[j]], mode, id, voiceonly );
		}
	} else {

		// send it to all the apropriate clients
		for ( j = 0; j < level.numConnectedClients; j++ ) {
			G_VoiceTo( ent, &g_entities[level.sortedClients[j]], mode, id, voiceonly );
		}
	}
}

/*
==================
Cmd_Voice_f
==================
*/
static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) {
	if ( mode != SAY_BUDDY ) {
		if ( trap_Argc() < 2 && !arg0 ) {
			return;
		}
		G_Voice( ent, NULL, mode, ConcatArgs( ( ( arg0 ) ? 0 : 1 ) ), voiceonly );
	} else {
		char buffer[16];
		int index;

		trap_Argv( 2, buffer, sizeof( buffer ) );
		index = atoi( buffer );
		if ( index < 0 ) {
			index = 0;
		}

		if ( trap_Argc() < 3 + index && !arg0 ) {
			return;
		}
		G_Voice( ent, NULL, mode, ConcatArgs( ( ( arg0 ) ? 2 + index : 3 + index ) ), voiceonly );
	}
}

// TTimo gcc: defined but not used
#if 0
/*
==================
Cmd_VoiceTell_f
==================
*/
static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) {
	int targetNum;
	gentity_t   *target;
	char        *id;
	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;
	}

	id = ConcatArgs( 2 );

	G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id );
	G_Voice( ent, target, SAY_TELL, id, voiceonly );
	// 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_Voice( ent, ent, SAY_TELL, id, voiceonly );
	}
}
#endif

// TTimo gcc: defined but not used
#if 0
/*
==================
Cmd_VoiceTaunt_f
==================
*/
static void Cmd_VoiceTaunt_f( gentity_t *ent ) {
	gentity_t *who;
	int i;

	if ( !ent->client ) {
		return;
	}

	// insult someone who just killed you
	if ( ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number ) {
		// i am a dead corpse
		if ( !( ent->enemy->r.svFlags & SVF_BOT ) ) {
//			G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
		}
		if ( !( ent->r.svFlags & SVF_BOT ) ) {
//			G_Voice( ent, ent,        SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
		}
		ent->enemy = NULL;
		return;
	}
	// insult someone you just killed
	if ( ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number ) {
		who = g_entities + ent->client->lastkilled_client;
		if ( who->client ) {
			// who is the person I just killed
			if ( who->client->lasthurt_mod == MOD_GAUNTLET ) {
				if ( !( who->r.svFlags & SVF_BOT ) ) {
//					G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );	// and I killed them with a gauntlet
				}
				if ( !( ent->r.svFlags & SVF_BOT ) ) {
//					G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );
				}
			} else {
				if ( !( who->r.svFlags & SVF_BOT ) ) {
//					G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );	// and I killed them with something else
				}
				if ( !( ent->r.svFlags & SVF_BOT ) ) {
//					G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );
				}
			}
			ent->client->lastkilled_client = -1;
			return;
		}
	}

	if ( g_gametype.integer >= GT_TEAM ) {
		// praise a team mate who just got a reward
		for ( i = 0; i < MAX_CLIENTS; i++ ) {
			who = g_entities + i;
			if ( who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam ) {
				if ( who->client->rewardTime > level.time ) {
					if ( !( who->r.svFlags & SVF_BOT ) ) {
//						G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse );
					}
					if ( !( ent->r.svFlags & SVF_BOT ) ) {
//						G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse );
					}
					return;
				}
			}
		}
	}

	// just say something
//	G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse );
}
// -NERVE - SMF
#endif

/*
==================
Cmd_Where_f
==================
*/
void Cmd_Where_f( gentity_t *ent ) {
	trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", vtos( ent->s.origin ) ) );
}

/*
==================
Cmd_CallVote_f
==================
*/
qboolean Cmd_CallVote_f( gentity_t *ent, unsigned int dwCommand, qboolean fRefCommand ) {
	int i;
	char arg1[MAX_STRING_TOKENS];
	char arg2[MAX_STRING_TOKENS];

	// Normal checks, if its not being issued as a referee command
	if ( !fRefCommand ) {
		if ( level.voteInfo.voteTime ) {
			CP( "cpm \"A vote is already in progress.\n\"" );
			return qfalse;
		} else if ( level.intermissiontime ) {
			CP( "cpm \"Cannot callvote during intermission.\n\"" );
			return qfalse;
		} else if ( !ent->client->sess.referee ) {
			if ( voteFlags.integer == VOTING_DISABLED ) {
				CP( "cpm \"Voting not enabled on this server.\n\"" );
				return qfalse;
			} else if ( vote_limit.integer > 0 && ent->client->pers.voteCount >= vote_limit.integer ) {
				CP( va( "cpm \"You have already called the maximum number of votes (%d).\n\"", vote_limit.integer ) );
				return qfalse;
			} else if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
				CP( "cpm \"Not allowed to call a vote as a spectator.\n\"" );
				return qfalse;
			}
		}
	}

	// 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, ';' ) ) {
		char *strCmdBase = ( !fRefCommand ) ? "vote" : "ref command";

		G_refPrintf( ent, "Invalid %s string.", strCmdBase );
		return( qfalse );
	}


	if ( trap_Argc() > 1 && ( i = G_voteCmdCheck( ent, arg1, arg2, fRefCommand ) ) != G_NOTFOUND ) {   //  --OSP
		if ( i != G_OK ) {
			if ( i == G_NOTFOUND ) {
				return( qfalse );               // Command error
			} else { return( qtrue );}
		}
	} else {
		if ( !fRefCommand ) {
			CP( va( "print \"\n^3>>> Unknown vote command: ^7%s %s\n\"", arg1, arg2 ) );
			G_voteHelp( ent, qtrue );
		}
		return( qfalse );
	}

	Com_sprintf( level.voteInfo.voteString, sizeof( level.voteInfo.voteString ), "%s %s", arg1, arg2 );

	// start the voting, the caller automatically votes yes
	// If a referee, vote automatically passes.	// OSP
	if ( fRefCommand ) {
//		level.voteInfo.voteYes = level.voteInfo.numVotingClients + 10;	// JIC :)
		// Don't announce some votes, as in comp mode, it is generally a ref
		// who is policing people who shouldn't be joining and players don't want
		// this sort of spam in the console
		if ( level.voteInfo.vote_fn != G_Kick_v && level.voteInfo.vote_fn != G_Mute_v ) {
			AP( "cp \"^1** Referee Server Setting Change **\n\"" );
		}

		// Gordon: just call the stupid thing.... don't bother with the voting faff
		level.voteInfo.vote_fn( NULL, 0, NULL, NULL, qfalse );

		G_globalSound( "sound/misc/referee.wav" );
	} else {
		level.voteInfo.voteYes = 1;
		AP( va( "print \"[lof]%s^7 [lon]called a vote.[lof]  Voting for: %s\n\"", ent->client->pers.netname, level.voteInfo.voteString ) );
		AP( va( "cp \"[lof]%s\n^7[lon]called a vote.\n\"", ent->client->pers.netname ) );
		G_globalSound( "sound/misc/vote.wav" );
	}

	level.voteInfo.voteTime = level.time;
	level.voteInfo.voteNo = 0;

	// Don't send the vote info if a ref initiates (as it will automatically pass)
	if ( !fRefCommand ) {
		for ( i = 0; i < level.numConnectedClients; i++ ) {
			level.clients[level.sortedClients[i]].ps.eFlags &= ~EF_VOTED;
		}

		ent->client->pers.voteCount++;
		ent->client->ps.eFlags |= EF_VOTED;

		trap_SetConfigstring( CS_VOTE_YES,    va( "%i", level.voteInfo.voteYes ) );
		trap_SetConfigstring( CS_VOTE_NO,     va( "%i", level.voteInfo.voteNo ) );
		trap_SetConfigstring( CS_VOTE_STRING, level.voteInfo.voteString );
		trap_SetConfigstring( CS_VOTE_TIME,   va( "%i", level.voteInfo.voteTime ) );
	}

	return( qtrue );
}

qboolean StringToFilter( const char *s, ipFilter_t *f );

qboolean G_FindFreeComplainIP( gclient_t* cl, ipFilter_t* ip ) {
	int i = 0;

	if ( !g_ipcomplaintlimit.integer ) {
		return qtrue;
	}

	for ( i = 0; i < MAX_COMPLAINTIPS && i < g_ipcomplaintlimit.integer; i++ ) {
		if ( !cl->pers.complaintips[i].compare && !cl->pers.complaintips[i].mask ) {
			cl->pers.complaintips[i].compare = ip->compare;
			cl->pers.complaintips[i].mask = ip->mask;
			return qtrue;
		}
		if ( ( cl->pers.complaintips[i].compare & cl->pers.complaintips[i].mask ) == ( ip->compare & ip->mask ) ) {
			return qtrue;
		}
	}
	return qfalse;
}

/*
==================
Cmd_Vote_f
==================
*/
void Cmd_Vote_f( gentity_t *ent ) {
	char msg[64];
	int num;

	// DHM - Nerve :: Complaints supercede voting (and share command)
	if ( ent->client->pers.complaintEndTime > level.time && g_gamestate.integer == GS_PLAYING && g_complaintlimit.integer ) {

		gentity_t* other = &g_entities[ ent->client->pers.complaintClient ];
		gclient_t *cl = other->client;
		if ( !cl ) {
			return;
		}
		if ( cl->pers.connected != CON_CONNECTED ) {
			return;
		}
		if ( cl->pers.localClient ) {
			trap_SendServerCommand( ent - g_entities, "complaint -3" );
			return;
		}

		trap_Argv( 1, msg, sizeof( msg ) );

		if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
			// Increase their complaint counter
			cl->pers.complaints++;

			num = g_complaintlimit.integer - cl->pers.complaints;

			if ( !cl->pers.localClient ) {

				const char* value;
				char userinfo[MAX_INFO_STRING];
				ipFilter_t ip;

				trap_GetUserinfo( ent - g_entities, userinfo, sizeof( userinfo ) );
				value = Info_ValueForKey( userinfo, "ip" );

				StringToFilter( value, &ip );

				if ( num <= 0 || !G_FindFreeComplainIP( cl, &ip ) ) {
					trap_DropClient( cl - level.clients, "kicked after too many complaints.", cl->sess.referee ? 0 : 300 );
					trap_SendServerCommand( ent - g_entities, "complaint -1" );
					return;
				}
			}

			trap_SendServerCommand( ent->client->pers.complaintClient, va( "cpm \"^1Warning^7: Complaint filed against you by %s^* You have Lost XP.\n\"", ent->client->pers.netname ) );
			trap_SendServerCommand( ent - g_entities, "complaint -1" );

			AddScore( other, WOLF_FRIENDLY_PENALTY );

			G_LoseKillSkillPoints( other, ent->sound2to1, ent->sound1to2, ent->sound2to3 ? qtrue : qfalse );
		} else {
			trap_SendServerCommand( ent->client->pers.complaintClient, "cpm \"No complaint filed against you.\n\"" );
			trap_SendServerCommand( ent - g_entities, "complaint -2" );
		}

		// Reset this ent's complainEndTime so they can't send multiple complaints
		ent->client->pers.complaintEndTime = -1;
		ent->client->pers.complaintClient = -1;

		return;
	}
	// dhm

	if ( ent->client->pers.applicationEndTime > level.time ) {

		gclient_t *cl = g_entities[ ent->client->pers.applicationClient ].client;
		if ( !cl ) {
			return;
		}
		if ( cl->pers.connected != CON_CONNECTED ) {
			return;
		}

		trap_Argv( 1, msg, sizeof( msg ) );

		if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
			trap_SendServerCommand( ent - g_entities, "application -4" );
			trap_SendServerCommand( ent->client->pers.applicationClient, "application -3" );

			G_AddClientToFireteam( ent->client->pers.applicationClient, ent - g_entities );
		} else {
			trap_SendServerCommand( ent - g_entities, "application -4" );
			trap_SendServerCommand( ent->client->pers.applicationClient, "application -2" );
		}

		ent->client->pers.applicationEndTime = 0;
		ent->client->pers.applicationClient = -1;

		return;
	}

	ent->client->pers.applicationEndTime = 0;
	ent->client->pers.applicationClient = -1;

	if ( ent->client->pers.invitationEndTime > level.time ) {

		gclient_t *cl = g_entities[ ent->client->pers.invitationClient ].client;
		if ( !cl ) {
			return;
		}
		if ( cl->pers.connected != CON_CONNECTED ) {
			return;
		}

		trap_Argv( 1, msg, sizeof( msg ) );

		if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
			trap_SendServerCommand( ent - g_entities, "invitation -4" );
			trap_SendServerCommand( ent->client->pers.invitationClient, "invitation -3" );

			G_AddClientToFireteam( ent - g_entities, ent->client->pers.invitationClient );
		} else {
			trap_SendServerCommand( ent - g_entities, "invitation -4" );
			trap_SendServerCommand( ent->client->pers.invitationClient, "invitation -2" );
		}

		ent->client->pers.invitationEndTime = 0;
		ent->client->pers.invitationClient = -1;

		return;
	}

	ent->client->pers.invitationEndTime = 0;
	ent->client->pers.invitationClient = -1;

	if ( ent->client->pers.propositionEndTime > level.time ) {
		gclient_t *cl = g_entities[ ent->client->pers.propositionClient ].client;
		if ( !cl ) {
			return;
		}
		if ( cl->pers.connected != CON_CONNECTED ) {
			return;
		}

		trap_Argv( 1, msg, sizeof( msg ) );

		if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
			trap_SendServerCommand( ent - g_entities, "proposition -4" );
			trap_SendServerCommand( ent->client->pers.propositionClient2, "proposition -3" );

			G_InviteToFireTeam( ent - g_entities, ent->client->pers.propositionClient );
		} else {
			trap_SendServerCommand( ent - g_entities, "proposition -4" );
			trap_SendServerCommand( ent->client->pers.propositionClient2, "proposition -2" );
		}

		ent->client->pers.propositionEndTime = 0;
		ent->client->pers.propositionClient = -1;
		ent->client->pers.propositionClient2 = -1;

		return;
	}

	if ( ent->client->pers.autofireteamEndTime > level.time ) {
		fireteamData_t* ft;

		trap_Argv( 1, msg, sizeof( msg ) );

		if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
			trap_SendServerCommand( ent - g_entities, "aft -2" );

			if ( G_IsFireteamLeader( ent - g_entities, &ft ) ) {
				ft->priv = qtrue;
			}
		} else {
			trap_SendServerCommand( ent - g_entities, "aft -2" );
		}

		ent->client->pers.autofireteamEndTime = 0;

		return;
	}

	if ( ent->client->pers.autofireteamCreateEndTime > level.time ) {
		trap_Argv( 1, msg, sizeof( msg ) );

		if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
			trap_SendServerCommand( ent - g_entities, "aftc -2" );

			G_RegisterFireteam( ent - g_entities );
		} else {
			trap_SendServerCommand( ent - g_entities, "aftc -2" );
		}

		ent->client->pers.autofireteamCreateEndTime = 0;

		return;
	}

	if ( ent->client->pers.autofireteamJoinEndTime > level.time ) {
		trap_Argv( 1, msg, sizeof( msg ) );

		if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
			fireteamData_t* ft;

			trap_SendServerCommand( ent - g_entities, "aftj -2" );


			ft = G_FindFreePublicFireteam( ent->client->sess.sessionTeam );
			if ( ft ) {
				G_AddClientToFireteam( ent - g_entities, ft->joinOrder[0] );
			}
		} else {
			trap_SendServerCommand( ent - g_entities, "aftj -2" );
		}

		ent->client->pers.autofireteamCreateEndTime = 0;

		return;
	}

	ent->client->pers.propositionEndTime = 0;
	ent->client->pers.propositionClient = -1;
	ent->client->pers.propositionClient2 = -1;

	// dhm
	// Reset this ent's complainEndTime so they can't send multiple complaints
	ent->client->pers.complaintEndTime = -1;
	ent->client->pers.complaintClient = -1;

	if ( !level.voteInfo.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.sessionTeam == TEAM_SPECTATOR ) {
		trap_SendServerCommand( ent - g_entities, "print \"Not allowed to vote as spectator.\n\"" );
		return;
	}

	if ( level.voteInfo.vote_fn == G_Kick_v ) {
		int pid = atoi( level.voteInfo.vote_value );
		if ( !g_entities[ pid ].client ) {
			return;
		}

		if ( g_entities[ pid ].client->sess.sessionTeam != TEAM_SPECTATOR && ent->client->sess.sessionTeam != g_entities[ pid ].client->sess.sessionTeam ) {
			trap_SendServerCommand( ent - g_entities, "print \"Cannot vote to kick player on opposing team.\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.voteInfo.voteYes++;
		trap_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteInfo.voteYes ) );
	} else {
		level.voteInfo.voteNo++;
		trap_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteInfo.voteNo ) );
	}

	// a majority will be determined in G_CheckVote, which will also account
	// for players entering or leaving
}


qboolean G_canPickupMelee( gentity_t *ent ) {
// JPW NERVE -- no "melee" weapons in net play
	return qfalse;
}
// jpw




/*
=================
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 );
}

/*
=================
Cmd_StartCamera_f
=================
*/
void Cmd_StartCamera_f( gentity_t *ent ) {

	if ( ent->client->cameraPortal  ) {
		G_FreeEntity( ent->client->cameraPortal );
	}
	ent->client->cameraPortal = G_Spawn();

	ent->client->cameraPortal->s.eType = ET_CAMERA;
	ent->client->cameraPortal->s.apos.trType = TR_STATIONARY;
	ent->client->cameraPortal->s.apos.trTime = 0;
	ent->client->cameraPortal->s.apos.trDuration = 0;
	VectorClear( ent->client->cameraPortal->s.angles );
	VectorClear( ent->client->cameraPortal->s.apos.trDelta );
	G_SetOrigin( ent->client->cameraPortal, ent->r.currentOrigin );
	VectorCopy( ent->r.currentOrigin, ent->client->cameraPortal->s.origin2 );

	ent->client->cameraPortal->s.frame = 0;

	ent->client->cameraPortal->r.svFlags |= ( SVF_PORTAL | SVF_SINGLECLIENT );
	ent->client->cameraPortal->r.singleClient = ent->client->ps.clientNum;

	ent->client->ps.eFlags |= EF_VIEWING_CAMERA;
	ent->s.eFlags |= EF_VIEWING_CAMERA;

	VectorCopy( ent->r.currentOrigin, ent->client->cameraOrigin );  // backup our origin

// (SA) trying this in client to avoid 1 frame of player drawing
//	ent->client->ps.eFlags |= EF_NODRAW;
//	ent->s.eFlags |= EF_NODRAW;
}

/*
=================
Cmd_StopCamera_f
=================
*/
void Cmd_StopCamera_f( gentity_t *ent ) {
//	gentity_t *sp;

	if ( ent->client->cameraPortal && ( ent->client->ps.eFlags & EF_VIEWING_CAMERA ) ) {
		// send a script event
//		G_Script_ScriptEvent( ent->client->cameraPortal, "stopcam", "" );

		// go back into noclient mode
		G_FreeEntity( ent->client->cameraPortal );
		ent->client->cameraPortal = NULL;

		ent->s.eFlags &= ~EF_VIEWING_CAMERA;
		ent->client->ps.eFlags &= ~EF_VIEWING_CAMERA;

		//G_SetOrigin( ent, ent->client->cameraOrigin );	// restore our origin
		//VectorCopy( ent->client->cameraOrigin, ent->client->ps.origin );

// (SA) trying this in client to avoid 1 frame of player drawing
//		ent->s.eFlags &= ~EF_NODRAW;
//		ent->client->ps.eFlags &= ~EF_NODRAW;

		// RF, if we are near the spawn point, save the "current" game, for reloading after death
//		sp = NULL;
		// gcc: suggests () around assignment used as truth value
//		while ((sp = G_Find( sp, FOFS(classname), "info_player_deathmatch" ))) {	// info_player_start becomes info_player_deathmatch in it's spawn functon
//			if (Distance( ent->s.pos.trBase, sp->s.origin ) < 256 && trap_InPVS( ent->s.pos.trBase, sp->s.origin )) {
//				G_SaveGame( NULL );
//				break;
//			}
//		}
	}
}

/*
=================
Cmd_SetCameraOrigin_f
=================
*/
void Cmd_SetCameraOrigin_f( gentity_t *ent ) {
	char buffer[MAX_TOKEN_CHARS];
	int i;
	vec3_t origin;

	if ( trap_Argc() != 4 ) {
		return;
	}

	for ( i = 0 ; i < 3 ; i++ ) {
		trap_Argv( i + 1, buffer, sizeof( buffer ) );
		origin[i] = atof( buffer );
	}

	if ( ent->client->cameraPortal ) {
		//G_SetOrigin( ent->client->cameraPortal, origin );	// set our origin
		VectorCopy( origin, ent->client->cameraPortal->s.origin2 );
		trap_LinkEntity( ent->client->cameraPortal );
		//	G_SetOrigin( ent, origin );	// set our origin
		//	VectorCopy( origin, ent->client->ps.origin );
	}
}

extern gentity_t *BotFindEntityForName( char *name );

/*
==============
Cmd_InterruptCamera_f
==============
*/
void Cmd_InterruptCamera_f( gentity_t *ent ) {
	gentity_t *player;

	if ( g_gametype.integer != GT_SINGLE_PLAYER && g_gametype.integer != GT_COOP ) {
		return;
	}

	player = BotFindEntityForName( "player" );

	if ( !player ) {
		return;
	}

	G_Script_ScriptEvent( player, "trigger", "cameraInterrupt" );
}

extern vec3_t playerMins;
extern vec3_t playerMaxs;

qboolean G_TankIsOccupied( gentity_t* ent ) {
	if ( !ent->tankLink ) {
		return qfalse;
	}

	return qtrue;
}

qboolean G_TankIsMountable( gentity_t* ent, gentity_t* other ) {
	if ( !( ent->spawnflags & 128 ) ) {
		return qfalse;
	}

	if ( level.disableTankEnter ) {
		return qfalse;
	}

	if ( G_TankIsOccupied( ent ) ) {
		return qfalse;
	}

	if ( ent->health <= 0 ) {
		return qfalse;
	}

	if ( other->client->ps.weaponDelay ) {
		return qfalse;
	}

	return qtrue;
}

// Rafael
/*
==================
Cmd_Activate_f
==================
*/
qboolean Do_Activate2_f( gentity_t *ent, gentity_t *traceEnt ) {
	qboolean found = qfalse;

	if ( ent->client->sess.playerType == PC_COVERTOPS && !ent->client->ps.powerups[PW_OPS_DISGUISED] && ent->health > 0 ) {
		if ( !ent->client->ps.powerups[PW_BLUEFLAG] && !ent->client->ps.powerups[PW_REDFLAG] ) {
			if ( traceEnt->s.eType == ET_CORPSE ) {
				if ( BODY_TEAM( traceEnt ) < 4 && BODY_TEAM( traceEnt ) != ent->client->sess.sessionTeam ) {
					found = qtrue;

					if ( BODY_VALUE( traceEnt ) >= 250 ) {

						traceEnt->nextthink = traceEnt->timestamp + BODY_TIME( BODY_TEAM( traceEnt ) );

//						BG_AnimScriptEvent( &ent->client->ps, ent->client->pers.character->animModelInfo, ANIM_ET_PICKUPGRENADE, qfalse, qtrue );
//						ent->client->ps.pm_flags |= PMF_TIME_LOCKPLAYER;
//						ent->client->ps.pm_time = 2100;

						ent->client->ps.powerups[PW_OPS_DISGUISED] = 1;
						ent->client->ps.powerups[PW_OPS_CLASS_1] = BODY_CLASS( traceEnt ) & 1;
						ent->client->ps.powerups[PW_OPS_CLASS_2] = BODY_CLASS( traceEnt ) & 2;
						ent->client->ps.powerups[PW_OPS_CLASS_3] = BODY_CLASS( traceEnt ) & 4;

						BODY_TEAM( traceEnt ) += 4;
						traceEnt->activator = ent;

						traceEnt->s.time2 = 1;

						// sound effect
						G_AddEvent( ent, EV_DISGUISE_SOUND, 0 );

						G_AddSkillPoints( ent, SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS, 5.f );
						G_DebugAddSkillPoints( ent, SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS, 5, "stealing uniform" );

						Q_strncpyz( ent->client->disguiseNetname, g_entities[traceEnt->s.clientNum].client->pers.netname, sizeof( ent->client->disguiseNetname ) );
						ent->client->disguiseRank = g_entities[traceEnt->s.clientNum].client ? g_entities[traceEnt->s.clientNum].client->sess.rank : 0;

						ClientUserinfoChanged( ent->s.clientNum );
					} else {
						BODY_VALUE( traceEnt ) += 5;
					}
				}
			}
		}
	}

	return found;
}

// TAT 1/14/2003 - extracted out the functionality of Cmd_Activate_f from finding the object to use
//		so we can force bots to use items, without worrying that they are looking EXACTLY at the target
qboolean Do_Activate_f( gentity_t *ent, gentity_t *traceEnt ) {
	qboolean found = qfalse;
	qboolean walking = qfalse;
	vec3_t forward;         //, offset, end;
	//trace_t		tr;

	// Arnout: invisible entities can't be used

	if ( traceEnt->entstate == STATE_INVISIBLE || traceEnt->entstate == STATE_UNDERCONSTRUCTION ) {
		return qfalse;
	}

	if ( ent->client->pers.cmd.buttons & BUTTON_WALKING ) {
		walking = qtrue;
	}

	if ( traceEnt->classname ) {
		traceEnt->flags &= ~FL_SOFTACTIVATE;    // FL_SOFTACTIVATE will be set if the user is holding 'walk' key

		if ( traceEnt->s.eType == ET_ALARMBOX ) {
			trace_t trace;

			if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
				return qfalse;
			}

			memset( &trace, 0, sizeof( trace ) );

			if ( traceEnt->use ) {
				G_UseEntity( traceEnt, ent, 0 );
			}
			found = qtrue;
		} else if ( traceEnt->s.eType == ET_ITEM )     {
			trace_t trace;

			if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
				return qfalse;
			}

			memset( &trace, 0, sizeof( trace ) );

			if ( traceEnt->touch ) {
				if ( ent->client->pers.autoActivate == PICKUP_ACTIVATE ) {
					ent->client->pers.autoActivate = PICKUP_FORCE;      //----(SA) force pickup
				}
				traceEnt->active = qtrue;
				traceEnt->touch( traceEnt, ent, &trace );
			}

			found = qtrue;
		} else if ( traceEnt->s.eType == ET_MOVER && G_TankIsMountable( traceEnt, ent ) ) {
			G_Script_ScriptEvent( traceEnt, "mg42", "mount" );
			ent->tagParent = traceEnt->nextTrain;
			Q_strncpyz( ent->tagName, "tag_player", MAX_QPATH );
			ent->backupWeaponTime = ent->client->ps.weaponTime;
			ent->client->ps.weaponTime = traceEnt->backupWeaponTime;
			ent->client->ps.weapHeat[WP_DUMMY_MG42] = traceEnt->mg42weapHeat;

			ent->tankLink = traceEnt;
			traceEnt->tankLink = ent;

			G_ProcessTagConnect( ent, qtrue );
			found = qtrue;
		} else if ( G_EmplacedGunIsMountable( traceEnt, ent ) ) {
			gclient_t* cl = &level.clients[ ent->s.clientNum ];
			vec3_t point;

			AngleVectors( traceEnt->s.apos.trBase, forward, NULL, NULL );
			VectorMA( traceEnt->r.currentOrigin, -36, forward, point );
			point[2] = ent->r.currentOrigin[2];

			// Save initial position
			VectorCopy( point, ent->TargetAngles );

			// Zero out velocity
			VectorCopy( vec3_origin, ent->client->ps.velocity );
			VectorCopy( vec3_origin, ent->s.pos.trDelta );

			traceEnt->active = qtrue;
			ent->active = qtrue;
			traceEnt->r.ownerNum = ent->s.number;
			VectorCopy( traceEnt->s.angles, traceEnt->TargetAngles );
			traceEnt->s.otherEntityNum = ent->s.number;

			cl->pmext.harc = traceEnt->harc;
			cl->pmext.varc = traceEnt->varc;
			VectorCopy( traceEnt->s.angles, cl->pmext.centerangles );
			cl->pmext.centerangles[PITCH] = AngleNormalize180( cl->pmext.centerangles[PITCH] );
			cl->pmext.centerangles[YAW] = AngleNormalize180( cl->pmext.centerangles[YAW] );
			cl->pmext.centerangles[ROLL] = AngleNormalize180( cl->pmext.centerangles[ROLL] );

			ent->backupWeaponTime = ent->client->ps.weaponTime;
			ent->client->ps.weaponTime = traceEnt->backupWeaponTime;
			ent->client->ps.weapHeat[WP_DUMMY_MG42] = traceEnt->mg42weapHeat;

			G_UseTargets( traceEnt, ent );   //----(SA)	added for Mike so mounting an MG42 can be a trigger event (let me know if there's any issues with this)
			found = qtrue;
		} else if ( ( ( Q_stricmp( traceEnt->classname, "func_door" ) == 0 ) || ( Q_stricmp( traceEnt->classname, "func_door_rotating" ) == 0 ) ) ) {
			if ( walking ) {
				traceEnt->flags |= FL_SOFTACTIVATE;     // no noise
			}
			G_TryDoor( traceEnt, ent, ent );      // (door,other,activator)
			found = qtrue;
		} else if ( ( Q_stricmp( traceEnt->classname, "team_WOLF_checkpoint" ) == 0 ) ) {
			if ( traceEnt->count != ent->client->sess.sessionTeam ) {
				traceEnt->health++;
			}
			found = qtrue;
		} else if ( ( Q_stricmp( traceEnt->classname, "func_button" ) == 0 ) && ( traceEnt->s.apos.trType == TR_STATIONARY && traceEnt->s.pos.trType == TR_STATIONARY ) && traceEnt->active == qfalse ) {
			Use_BinaryMover( traceEnt, ent, ent );
			traceEnt->active = qtrue;
			found = qtrue;
		} else if ( !Q_stricmp( traceEnt->classname, "func_invisible_user" ) ) {
			if ( walking ) {
				traceEnt->flags |= FL_SOFTACTIVATE;     // no noise
			}
			G_UseEntity( traceEnt, ent, ent );
			found = qtrue;
		} else if ( !Q_stricmp( traceEnt->classname, "props_footlocker" ) ) {
			G_UseEntity( traceEnt, ent, ent );
			found = qtrue;
		}
	}

	return found;
}

void G_LeaveTank( gentity_t* ent, qboolean position ) {
	gentity_t* tank;

	// found our tank (or whatever)
	vec3_t axis[3];
	vec3_t pos;
	trace_t tr;

	tank = ent->tankLink;
	if ( !tank ) {
		return;
	}

	if ( position ) {

		AnglesToAxis( tank->s.angles, axis );

		VectorMA( ent->client->ps.origin, 128, axis[1], pos );
		trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID );

		if ( tr.startsolid ) {
			// try right
			VectorMA( ent->client->ps.origin, -128, axis[1], pos );
			trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID );

			if ( tr.startsolid ) {
				// try back
				VectorMA( ent->client->ps.origin, -224, axis[0], pos );
				trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID );

				if ( tr.startsolid ) {
					// try front
					VectorMA( ent->client->ps.origin, 224, axis[0], pos );
					trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID );

					if ( tr.startsolid ) {
						// give up
						return;
					}
				}
			}
		}

		VectorClear( ent->client->ps.velocity ); // Gordon: dont want them to fly away ;D
		TeleportPlayer( ent, pos, ent->client->ps.viewangles );
	}


	tank->mg42weapHeat = ent->client->ps.weapHeat[WP_DUMMY_MG42];
	tank->backupWeaponTime = ent->client->ps.weaponTime;
	ent->client->ps.weaponTime = ent->backupWeaponTime;

	G_Script_ScriptEvent( tank, "mg42", "unmount" );
	ent->tagParent = NULL;
	*ent->tagName = '\0';
	ent->s.eFlags &= ~EF_MOUNTEDTANK;
	ent->client->ps.eFlags &= ~EF_MOUNTEDTANK;
	tank->s.powerups = -1;

	tank->tankLink = NULL;
	ent->tankLink = NULL;
}

void Cmd_Activate_f( gentity_t *ent ) {
	trace_t tr;
	vec3_t end;
	gentity_t   *traceEnt;
	vec3_t forward, right, up, offset;
//	int			activatetime = level.time;
	qboolean found = qfalse;
	qboolean pass2 = qfalse;
	int i;

	if ( ent->health <= 0 ) {
		return;
	}

	if ( ent->s.weapon == WP_MORTAR_SET || ent->s.weapon == WP_MOBILE_MG42_SET ) {
		return;
	}

	if ( ent->active ) {
		if ( ent->client->ps.persistant[PERS_HWEAPON_USE] ) {
			// DHM - Nerve :: Restore original position if current position is bad
			trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ent->s.number, MASK_PLAYERSOLID );
			if ( tr.startsolid ) {
				VectorCopy( ent->TargetAngles, ent->client->ps.origin );
				VectorCopy( ent->TargetAngles, ent->r.currentOrigin );
				ent->r.contents = CONTENTS_CORPSE;      // DHM - this will correct itself in ClientEndFrame
			}

			ent->client->ps.eFlags &= ~EF_MG42_ACTIVE;          // DHM - Nerve :: unset flag
			ent->client->ps.eFlags &= ~EF_AAGUN_ACTIVE;

			ent->client->ps.persistant[PERS_HWEAPON_USE] = 0;
			ent->active = qfalse;

			for ( i = 0; i < level.num_entities; i++ ) {
				if ( g_entities[i].s.eType == ET_MG42_BARREL && g_entities[i].r.ownerNum == ent->s.number ) {
					g_entities[i].mg42weapHeat = ent->client->ps.weapHeat[WP_DUMMY_MG42];
					g_entities[i].backupWeaponTime = ent->client->ps.weaponTime;
					break;
				}
			}
			ent->client->ps.weaponTime = ent->backupWeaponTime;
		} else {
			ent->active = qfalse;
		}
		return;
	} else if ( ent->client->ps.eFlags & EF_MOUNTEDTANK && ent->s.eFlags & EF_MOUNTEDTANK && !level.disableTankExit ) {
		G_LeaveTank( ent, qtrue );
		return;
	}

	AngleVectors( ent->client->ps.viewangles, forward, right, up );

	VectorCopy( ent->client->ps.origin, offset );
	offset[2] += ent->client->ps.viewheight;

	// lean
	if ( ent->client->ps.leanf ) {
		VectorMA( offset, ent->client->ps.leanf, right, offset );
	}

	//VectorMA( offset, 256, forward, end );
	VectorMA( offset, 96, forward, end );

	trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_MISSILECLIP | CONTENTS_BODY | CONTENTS_CORPSE )  );

	if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) {
		trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MISSILECLIP | CONTENTS_TRIGGER ) );
		pass2 = qtrue;
	}

tryagain:

	if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) {
		return;
	}

	traceEnt = &g_entities[ tr.entityNum ];

	found = Do_Activate_f( ent, traceEnt );

	if ( !found && !pass2 ) {
		pass2 = qtrue;
		trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_MISSILECLIP | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) );
		goto tryagain;
	}
}


void Cmd_Activate2_f( gentity_t *ent ) {
	trace_t tr;
	vec3_t end;
	gentity_t   *traceEnt;
	vec3_t forward, right, up, offset;
//	int			activatetime = level.time;
	qboolean found = qfalse;
	qboolean pass2 = qfalse;

	if ( ent->client->sess.playerType != PC_COVERTOPS ) {
		return;
	}

	AngleVectors( ent->client->ps.viewangles, forward, right, up );
	CalcMuzzlePointForActivate( ent, forward, right, up, offset );
	VectorMA( offset, 96, forward, end );

	trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE ) );

	if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) {
		trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) );
		pass2 = qtrue;
	}

tryagain:

	if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) {
		return;
	}

	traceEnt = &g_entities[ tr.entityNum ];

	found = Do_Activate2_f( ent, traceEnt );

	if ( !found && !pass2 ) {
		pass2 = qtrue;
		trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) );
		goto tryagain;
	}
}

/*
============================
Cmd_ClientMonsterSlickAngle
============================
*/
/*
void Cmd_ClientMonsterSlickAngle (gentity_t *clent) {

	char s[MAX_STRING_CHARS];
	int	entnum;
	int angle;
	gentity_t *ent;
	vec3_t	dir, kvel;
	vec3_t	forward;

	if (trap_Argc() != 3) {
		G_Printf( "ClientDamage command issued with incorrect number of args\n" );
	}

	trap_Argv( 1, s, sizeof( s ) );
	entnum = atoi(s);
	ent = &g_entities[entnum];

	trap_Argv( 2, s, sizeof( s ) );
	angle = atoi(s);

	// sanity check (also protect from cheaters)
	if (g_gametype.integer != GT_SINGLE_PLAYER && entnum != clent->s.number) {
		trap_DropClient( clent->s.number, "Dropped due to illegal ClientMonsterSlick command\n" );
		return;
	}

	VectorClear (dir);
	dir[YAW] = angle;
	AngleVectors (dir, forward, NULL, NULL);

	VectorScale (forward, 32, kvel);
	VectorAdd (ent->client->ps.velocity, kvel, ent->client->ps.velocity);
}
*/

void G_UpdateSpawnCounts( void ) {
	int i, j;
	char cs[MAX_STRING_CHARS];
	int current, count, team;

	for ( i = 0; i < level.numspawntargets; i++ ) {
		trap_GetConfigstring( CS_MULTI_SPAWNTARGETS + i, cs, sizeof( cs ) );

		current = atoi( Info_ValueForKey( cs, "c" ) );
		team = atoi( Info_ValueForKey( cs, "t" ) ) & ~256;

		count = 0;
		for ( j = 0; j < level.numConnectedClients; j++ ) {
			gclient_t* client = &level.clients[ level.sortedClients[ j ] ];

			if ( client->sess.sessionTeam != TEAM_AXIS && client->sess.sessionTeam != TEAM_ALLIES ) {
				continue;
			}

			if ( client->sess.sessionTeam == team && client->sess.spawnObjectiveIndex == i + 1 ) {
				count++;
				continue;
			}

			if ( client->sess.spawnObjectiveIndex == 0 ) {
				if ( client->sess.sessionTeam == TEAM_AXIS ) {
					if ( level.axisAutoSpawn == i ) {
						count++;
						continue;
					}
				} else {
					if ( level.alliesAutoSpawn == i ) {
						count++;
						continue;
					}
				}
			}
		}

		if ( count == current ) {
			continue;
		}

		Info_SetValueForKey( cs, "c", va( "%i", count ) );
		trap_SetConfigstring( CS_MULTI_SPAWNTARGETS + i, cs );
	}
}

/*
============
Cmd_SetSpawnPoint_f
============
*/
void SetPlayerSpawn( gentity_t* ent, int spawn, qboolean update ) {
	ent->client->sess.spawnObjectiveIndex = spawn;
	if ( ent->client->sess.spawnObjectiveIndex >= MAX_MULTI_SPAWNTARGETS || ent->client->sess.spawnObjectiveIndex < 0 ) {
		ent->client->sess.spawnObjectiveIndex = 0;
	}

	if ( update ) {
		G_UpdateSpawnCounts();
	}
}

void Cmd_SetSpawnPoint_f( gentity_t* ent ) {
	char arg[MAX_TOKEN_CHARS];
	int val, i;

	if ( trap_Argc() != 2 ) {
		return;
	}

	trap_Argv( 1, arg, sizeof( arg ) );
	val = atoi( arg );

	if ( ent->client ) {
		SetPlayerSpawn( ent, val, qtrue );
	}

//	if( ent->client->sess.sessionTeam != TEAM_SPECTATOR && !(ent->client->ps.pm_flags & PMF_LIMBO) ) {
//		return;
//	}

	for ( i = 0; i < level.numLimboCams; i++ ) {
		int x = ( g_entities[level.limboCams[i].targetEnt].count - CS_MULTI_SPAWNTARGETS ) + 1;
		if ( level.limboCams[i].spawn && x == val ) {
			VectorCopy( level.limboCams[i].origin, ent->s.origin2 );
			ent->r.svFlags |= SVF_SELF_PORTAL_EXCLUSIVE;
			trap_SendServerCommand( ent - g_entities, va( "portalcampos %i %i %i %i %i %i %i %i", val - 1, (int)level.limboCams[i].origin[0], (int)level.limboCams[i].origin[1], (int)level.limboCams[i].origin[2], (int)level.limboCams[i].angles[0], (int)level.limboCams[i].angles[1], (int)level.limboCams[i].angles[2], level.limboCams[i].hasEnt ? level.limboCams[i].targetEnt : -1 ) );
			break;
		}
	}
}

/*
============
Cmd_SetSniperSpot_f
============
*/
void Cmd_SetSniperSpot_f( gentity_t *clent ) {
	gentity_t *spot;

	vmCvar_t cvar_mapname;
	char filename[MAX_QPATH];
	fileHandle_t f;
	char buf[1024];

	if ( !g_cheats.integer ) {
		return;
	}
	if ( !trap_Cvar_VariableIntegerValue( "cl_running" ) ) {
		return;                                                 // only allow locally playing client
	}
	if ( clent->s.number != 0 ) {
		return;                         // only allow locally playing client

	}
	// drop a sniper spot here
	spot = G_Spawn();
	spot->classname = "bot_sniper_spot";
	VectorCopy( clent->r.currentOrigin, spot->s.origin );
	VectorCopy( clent->client->ps.viewangles, spot->s.angles );
	spot->aiTeam = clent->client->sess.sessionTeam;

	// output to text file
	trap_Cvar_Register( &cvar_mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );

	Com_sprintf( filename, sizeof( filename ), "maps/%s.botents", cvar_mapname.string );
	if ( trap_FS_FOpenFile( filename, &f, FS_APPEND ) < 0 ) {
		G_Error( "Cmd_SetSniperSpot_f: cannot open %s for writing", filename );
	}

	Com_sprintf( buf, sizeof( buf ), "{\n\"classname\" \"%s\"\n\"origin\" \"%.3f %.3f %.3f\"\n\"angles\" \"%.2f %.2f %.2f\"\n\"aiTeam\" \"%i\"\n}\n\n", spot->classname, spot->s.origin[0], spot->s.origin[1], spot->s.origin[2], spot->s.angles[0], spot->s.angles[1], spot->s.angles[2], spot->aiTeam );
	trap_FS_Write( buf, strlen( buf ), f );

	trap_FS_FCloseFile( f );

	G_Printf( "dropped sniper spot\n" );

	return;
}

void G_PrintAccuracyLog( gentity_t *ent );

/*
============
Cmd_SetWayPoint_f
============
*/
/*void Cmd_SetWayPoint_f( gentity_t *ent ) {
	char	arg[MAX_TOKEN_CHARS];
	vec3_t	forward, muzzlePoint, end, loc;
	trace_t	trace;

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
		trap_SendServerCommand( ent-g_entities, "print \"Not allowed to set waypoints as spectator.\n\"" );
		return;
	}

	if ( trap_Argc() != 2 ) {
		return;
	}

	trap_Argv( 1, arg, sizeof( arg ) );

	AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );

	VectorCopy( ent->r.currentOrigin, muzzlePoint );
	muzzlePoint[2] += ent->client->ps.viewheight;

	VectorMA( muzzlePoint, 8192, forward, end );
	trap_Trace( &trace, muzzlePoint, NULL, NULL, end, ent->s.number, MASK_SHOT );

	if( trace.surfaceFlags & SURF_NOIMPACT )
		return;

	VectorCopy( trace.endpos, loc );

	G_SetWayPoint( ent, atoi(arg), loc );
}*/

/*
============
Cmd_ClearWayPoint_f
============
*/
/*void Cmd_ClearWayPoint_f( gentity_t *ent ) {

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
		trap_SendServerCommand( ent-g_entities, "print \"Not allowed to clear waypoints as spectator.\n\"" );
		return;
	}

	G_RemoveWayPoint( ent->client );
}*/

void Cmd_WeaponStat_f( gentity_t* ent ) {
	char buffer[16];
	extWeaponStats_t stat;

	if ( !ent || !ent->client ) {
		return;
	}

	if ( trap_Argc() != 2 ) {
		return;
	}
	trap_Argv( 1, buffer, 16 );
	stat = atoi( buffer );

	trap_SendServerCommand( ent - g_entities, va( "rws %i %i", ent->client->sess.aWeaponStats[stat].atts, ent->client->sess.aWeaponStats[stat].hits ) );
}

void Cmd_IntermissionWeaponStats_f( gentity_t* ent ) {
	char buffer[1024];
	int i, clientNum;

	if ( !ent || !ent->client ) {
		return;
	}

	trap_Argv( 1, buffer, sizeof( buffer ) );

	clientNum = atoi( buffer );
	if ( clientNum < 0 || clientNum > MAX_CLIENTS ) {
		return;
	}

	Q_strncpyz( buffer, "imws ", sizeof( buffer ) );
	for ( i = 0; i < WS_MAX; i++ ) {
		Q_strcat( buffer, sizeof( buffer ), va( "%i %i %i ", level.clients[clientNum].sess.aWeaponStats[i].atts, level.clients[clientNum].sess.aWeaponStats[i].hits, level.clients[clientNum].sess.aWeaponStats[i].kills ) );
	}

	trap_SendServerCommand( ent - g_entities, buffer );
}

void G_MakeReady( gentity_t* ent ) {
	ent->client->ps.eFlags |= EF_READY;
	ent->s.eFlags |= EF_READY;
	// rain - #105 - moved this set here
	ent->client->pers.ready = qtrue;
}

void G_MakeUnready( gentity_t* ent ) {
	ent->client->ps.eFlags &= ~EF_READY;
	ent->s.eFlags &= ~EF_READY;
	// rain - #105 - moved this set here
	ent->client->pers.ready = qfalse;
}

void Cmd_IntermissionReady_f( gentity_t* ent ) {
	if ( !ent || !ent->client ) {
		return;
	}

	G_MakeReady( ent );
}

void Cmd_IntermissionPlayerKillsDeaths_f( gentity_t* ent ) {
	char buffer[1024];
	int i;

	if ( !ent || !ent->client ) {
		return;
	}

	Q_strncpyz( buffer, "impkd ", sizeof( buffer ) );
	for ( i = 0; i < MAX_CLIENTS; i++ ) {
		if ( g_entities[i].inuse ) {
			Q_strcat( buffer, sizeof( buffer ), va( "%i %i ", level.clients[i].sess.kills, level.clients[i].sess.deaths ) );
		} else {
			Q_strcat( buffer, sizeof( buffer ), "0 0 " );
		}
	}

	trap_SendServerCommand( ent - g_entities, buffer );
}

void G_CalcClientAccuracies( void ) {
	int i, j;
	int shots, hits;

	for ( i = 0; i < MAX_CLIENTS; i++ ) {
		shots = 0;
		hits = 0;

		if ( g_entities[i].inuse ) {
			for ( j = 0; j < WS_MAX; j++ ) {
				shots += level.clients[i].sess.aWeaponStats[j].atts;
				hits += level.clients[i].sess.aWeaponStats[j].hits;
			}

			level.clients[ i ].acc = shots ? ( 100 * hits ) / (float)shots : 0;
		} else {
			level.clients[ i ].acc = 0;
		}
	}
}


void Cmd_IntermissionWeaponAccuracies_f( gentity_t* ent ) {
	char buffer[1024];
	int i;

	if ( !ent || !ent->client ) {
		return;
	}

	G_CalcClientAccuracies();

	Q_strncpyz( buffer, "imwa ", sizeof( buffer ) );
	for ( i = 0; i < MAX_CLIENTS; i++ ) {
		Q_strcat( buffer, sizeof( buffer ), va( "%i ", (int)level.clients[ i ].acc ) );
	}

	trap_SendServerCommand( ent - g_entities, buffer );
}

void Cmd_SelectedObjective_f( gentity_t* ent ) {
	int i, val;
	char buffer[16];
	vec_t dist, neardist = 0;
	int nearest = -1;


	if ( !ent || !ent->client ) {
		return;
	}

//	if( ent->client->sess.sessionTeam != TEAM_SPECTATOR && !(ent->client->ps.pm_flags & PMF_LIMBO) ) {
//		return;
//	}

	if ( trap_Argc() != 2 ) {
		return;
	}
	trap_Argv( 1, buffer, 16 );
	val = atoi( buffer ) + 1;


	for ( i = 0; i < level.numLimboCams; i++ ) {
		if ( !level.limboCams[i].spawn && level.limboCams[i].info == val ) {
			if ( !level.limboCams[i].hasEnt ) {
				VectorCopy( level.limboCams[i].origin, ent->s.origin2 );
				ent->r.svFlags |= SVF_SELF_PORTAL_EXCLUSIVE;
				trap_SendServerCommand( ent - g_entities, va( "portalcampos %i %i %i %i %i %i %i %i", val - 1, (int)level.limboCams[i].origin[0], (int)level.limboCams[i].origin[1], (int)level.limboCams[i].origin[2], (int)level.limboCams[i].angles[0], (int)level.limboCams[i].angles[1], (int)level.limboCams[i].angles[2], level.limboCams[i].hasEnt ? level.limboCams[i].targetEnt : -1 ) );

				break;
			} else {
				dist = VectorDistanceSquared( level.limboCams[i].origin, g_entities[level.limboCams[i].targetEnt].r.currentOrigin );
				if ( nearest == -1 || dist < neardist ) {
					nearest = i;
					neardist = dist;
				}
			}
		}
	}

	if ( nearest != -1 ) {
		i = nearest;

		VectorCopy( level.limboCams[i].origin, ent->s.origin2 );
		ent->r.svFlags |= SVF_SELF_PORTAL_EXCLUSIVE;
		trap_SendServerCommand( ent - g_entities, va( "portalcampos %i %i %i %i %i %i %i %i", val - 1, (int)level.limboCams[i].origin[0], (int)level.limboCams[i].origin[1], (int)level.limboCams[i].origin[2], (int)level.limboCams[i].angles[0], (int)level.limboCams[i].angles[1], (int)level.limboCams[i].angles[2], level.limboCams[i].hasEnt ? level.limboCams[i].targetEnt : -1 ) );
	}
}

void Cmd_Ignore_f( gentity_t* ent ) {
	char cmd[MAX_TOKEN_CHARS];
	int cnum;

	trap_Argv( 1, cmd, sizeof( cmd ) );

	if ( !*cmd ) {
		trap_SendServerCommand( ent - g_entities, "print \"usage: Ignore <clientname>.\n\"\n" );
		return;
	}

	cnum = G_refClientnumForName( ent, cmd );

	if ( cnum != MAX_CLIENTS ) {
		COM_BitSet( ent->client->sess.ignoreClients, cnum );
	}
}

void Cmd_TicketTape_f( void ) {
/*	char	cmd[MAX_TOKEN_CHARS];

	trap_Argv( 1, cmd, sizeof( cmd ) );

	trap_SendServerCommand( -1, va( "tt \"LANDMINES SPOTTED <STOP> CHECK COMMAND MAP FOR DETAILS <STOP>\"\n", cmd ));*/
}

void Cmd_UnIgnore_f( gentity_t* ent ) {
	char cmd[MAX_TOKEN_CHARS];
	int cnum;

	trap_Argv( 1, cmd, sizeof( cmd ) );

	if ( !*cmd ) {
		trap_SendServerCommand( ent - g_entities, "print \"usage: Unignore <clientname>.\n\"\n" );
		return;
	}

	cnum = G_refClientnumForName( ent, cmd );

	if ( cnum != MAX_CLIENTS ) {
		COM_BitClear( ent->client->sess.ignoreClients, cnum );
	}
}

/*
=================
Cmd_SwapPlacesWithBot_f
=================
*/
void Cmd_SwapPlacesWithBot_f( gentity_t *ent, int botNum ) {
	gentity_t *botent;
	gclient_t cl, *client;
	clientPersistant_t saved;
	clientSession_t sess;
	int persistant[MAX_PERSISTANT];
	//
	client = ent->client;
	//
	botent = &g_entities[botNum];
	if ( !botent->client ) {
		return;
	}
	// if this bot is dead
	if ( botent->health <= 0 && ( botent->client->ps.pm_flags & PMF_LIMBO ) ) {
		trap_SendServerCommand( ent - g_entities, "print \"Bot is in limbo mode, cannot swap places.\n\"" );
		return;
	}
	//
	if ( client->sess.sessionTeam != botent->client->sess.sessionTeam ) {
		trap_SendServerCommand( ent - g_entities, "print \"Bot is on different team, cannot swap places.\n\"" );
		return;
	}
	//
	// copy the client information
	cl = *botent->client;
	//
	G_DPrintf( "Swapping places: %s in for %s\n", ent->client->pers.netname, botent->client->pers.netname );
	// kill the bot
	botent->flags &= ~FL_GODMODE;
	botent->client->ps.stats[STAT_HEALTH] = botent->health = 0;
	player_die( botent, ent, ent, 100000, MOD_SWAP_PLACES );
	// make sure they go into limbo mode right away, and dont show a corpse
	limbo( botent, qfalse );
	// respawn the player
	ent->client->ps.pm_flags &= ~PMF_LIMBO; // JPW NERVE turns off limbo
	// copy the location
	VectorCopy( cl.ps.origin, ent->s.origin );
	VectorCopy( cl.ps.viewangles, ent->s.angles );
	// copy session data, so we spawn in as the same class
	// save items
	saved = client->pers;
	sess = client->sess;
	memcpy( persistant, ent->client->ps.persistant, sizeof( persistant ) );
	// give them the right weapons/etc
	*client = cl;
	client->sess = sess;
	client->sess.playerType = ent->client->sess.latchPlayerType = cl.sess.playerType;
	client->sess.playerWeapon = ent->client->sess.latchPlayerWeapon = cl.sess.playerWeapon;
	client->sess.playerWeapon2 = ent->client->sess.latchPlayerWeapon2 = cl.sess.playerWeapon2;
	// spawn them in
	ClientSpawn( ent, qtrue );
	// restore items
	client->pers = saved;
	memcpy( ent->client->ps.persistant, persistant, sizeof( persistant ) );
	client->ps = cl.ps;
	client->ps.clientNum = ent->s.number;
	ent->health = client->ps.stats[STAT_HEALTH];
	SetClientViewAngle( ent, cl.ps.viewangles );
	// make sure they dont respawn immediately after they die
	client->pers.lastReinforceTime = 0;
}


/*
=================
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 ) );

	if ( Q_stricmp( cmd, "say" ) == 0 ) {
		if ( !ent->client->sess.muted ) {
			Cmd_Say_f( ent, SAY_ALL, qfalse );
		}
		return;
	}

	if ( Q_stricmp( cmd, "say_team" ) == 0 ) {
		if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->sess.sessionTeam == TEAM_FREE ) {
			trap_SendServerCommand( ent - g_entities, "print \"Can't team chat as spectator\n\"\n" );
			return;
		}

		if ( !ent->client->sess.muted ) {
			Cmd_Say_f( ent, SAY_TEAM, qfalse );
		}
		return;
	} else if ( Q_stricmp( cmd, "vsay" ) == 0 ) {
		if ( !ent->client->sess.muted ) {
			Cmd_Voice_f( ent, SAY_ALL, qfalse, qfalse );
		}
		return;
	} else if ( Q_stricmp( cmd, "vsay_team" ) == 0 ) {
		if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->sess.sessionTeam == TEAM_FREE ) {
			trap_SendServerCommand( ent - g_entities, "print \"Can't team chat as spectator\n\"\n" );
			return;
		}

		if ( !ent->client->sess.muted ) {
			Cmd_Voice_f( ent, SAY_TEAM, qfalse, qfalse );
		}
		return;
	} else if ( Q_stricmp( cmd, "say_buddy" ) == 0 ) {
		if ( !ent->client->sess.muted ) {
			Cmd_Say_f( ent, SAY_BUDDY, qfalse );
		}
		return;
	} else if ( Q_stricmp( cmd, "vsay_buddy" ) == 0 ) {
		if ( !ent->client->sess.muted ) {
			Cmd_Voice_f( ent, SAY_BUDDY, qfalse, qfalse );
		}
		return;
	} else if ( Q_stricmp( cmd, "score" ) == 0 ) {
		Cmd_Score_f( ent );
		return;
	} else if ( Q_stricmp( cmd, "vote" ) == 0 ) {
		Cmd_Vote_f( ent );
		return;
	} else if ( Q_stricmp( cmd, "fireteam" ) == 0 ) {
		Cmd_FireTeam_MP_f( ent );
		return;
	} else if ( Q_stricmp( cmd, "showstats" ) == 0 ) {
		G_PrintAccuracyLog( ent );
		return;
	} else if ( Q_stricmp( cmd, "rconAuth" ) == 0 ) {
		Cmd_AuthRcon_f( ent );
		return;
	} else if ( Q_stricmp( cmd, "ignore" ) == 0 ) {
		Cmd_Ignore_f( ent );
		return;
	} else if ( Q_stricmp( cmd, "unignore" ) == 0 ) {
		Cmd_UnIgnore_f( ent );
		return;
	} else if ( Q_stricmp( cmd, "obj" ) == 0 ) {
		Cmd_SelectedObjective_f( ent );
		return;
	} else if ( !Q_stricmp( cmd, "impkd" ) ) {
		Cmd_IntermissionPlayerKillsDeaths_f( ent );
		return;
	} else if ( !Q_stricmp( cmd, "imwa" ) ) {
		Cmd_IntermissionWeaponAccuracies_f( ent );
		return;
	} else if ( !Q_stricmp( cmd, "imws" ) ) {
		Cmd_IntermissionWeaponStats_f( ent );
		return;
	} else if ( !Q_stricmp( cmd, "imready" ) ) {
		Cmd_IntermissionReady_f( ent );
		return;
	} else if ( Q_stricmp( cmd, "ws" ) == 0 ) {
		Cmd_WeaponStat_f( ent );
		return;
	} else if ( !Q_stricmp( cmd, "forcetapout" ) ) {
		if ( !ent || !ent->client ) {
			return;
		}

		if ( ent->client->ps.stats[STAT_HEALTH] <= 0 && ( ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES ) ) {
			limbo( ent, qtrue );
		}

		return;
	}

	// OSP
	// Do these outside as we don't want to advertise it in the help screen
	if ( !Q_stricmp( cmd, "wstats" ) ) {
		G_statsPrint( ent, 1 );
		return;
	}
	if ( !Q_stricmp( cmd, "sgstats" ) ) { // Player game stats
		G_statsPrint( ent, 2 );
		return;
	}
	if ( !Q_stricmp( cmd, "stshots" ) ) { // "Topshots" accuracy rankings
		G_weaponStatsLeaders_cmd( ent, qtrue, qtrue );
		return;
	}
	if ( !Q_stricmp( cmd, "rs" ) ) {
		Cmd_ResetSetup_f( ent );
		return;
	}

	if ( G_commandCheck( ent, cmd, qtrue ) ) {
		return;
	}
	// OSP

	// ignore all other commands when at intermission
	if ( level.intermissiontime ) {
//		Cmd_Say_f (ent, qfalse, qtrue);			// NERVE - SMF - we don't want to spam the clients with this.
		CPx( clientNum, va( "print \"^3%s^7 not allowed during intermission.\n\"", cmd ) );
		return;
	}

	if ( Q_stricmp( cmd, "give" ) == 0 ) {
		Cmd_Give_f( ent );
	} else if ( Q_stricmp( cmd, "listbotgoals" ) == 0 ) {
		Cmd_ListBotGoals_f( ent );
	} else if ( Q_stricmp( cmd, "god" ) == 0 ) {
		Cmd_God_f( ent );
	} else if ( Q_stricmp( cmd, "nofatigue" ) == 0 ) {
		Cmd_Nofatigue_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, "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, "startCamera") == 0) {
//		Cmd_StartCamera_f( ent );
	} else if ( Q_stricmp( cmd, "stopCamera" ) == 0 ) {
		Cmd_StopCamera_f( ent );
	} else if ( Q_stricmp( cmd, "setCameraOrigin" ) == 0 ) {
		Cmd_SetCameraOrigin_f( ent );
	} else if ( Q_stricmp( cmd, "cameraInterrupt" ) == 0 ) {
		Cmd_InterruptCamera_f( ent );
	} else if ( Q_stricmp( cmd, "setviewpos" ) == 0 ) {
		Cmd_SetViewpos_f( ent );
	} else if ( Q_stricmp( cmd, "setspawnpt" ) == 0 ) {
		Cmd_SetSpawnPoint_f( ent );
	} else if ( Q_stricmp( cmd, "setsniperspot" ) == 0 ) {
		Cmd_SetSniperSpot_f( ent );
//	} else if (Q_stricmp (cmd, "waypoint") == 0) {
//		Cmd_SetWayPoint_f( ent );
//	} else if (Q_stricmp (cmd, "clearwaypoint") == 0) {
//		Cmd_ClearWayPoint_f( ent );
		// OSP
	} else if ( G_commandCheck( ent, cmd, qfalse ) ) {
		return;

	} else {
		trap_SendServerCommand( clientNum, va( "print \"unknown cmd[lof] %s\n\"", cmd ) );
	}
}


