// Copyright (C) 1999-2000 Id Software, Inc.
//
#include "g_local.h"

//#ifdef __ZCAM__
#include "zcam.h"
//#endif /* __ZACM__ */

#include "../../ui/menudef.h"			// for the voice chats

//#include "botlib.h"

/*
==================
DeathmatchScoreboardMessage
 
==================
*/
void DeathmatchScoreboardMessage( gentity_t *ent ) {
    char		entry[1024];
    char		string[1400];
    int			stringlength;
    int			i, j;
    gclient_t	*cl;
    int			numSorted, scoreFlags, accuracy, perfect;

    // send the latest information on all clients
    string[0] = 0;
    stringlength = 0;
    scoreFlags = 0;

    numSorted = level.numConnectedClients;

    for (i=0 ; i < numSorted ; i++) {
        int		ping;

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

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

        if( cl->accuracy_shots ) {
            accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots;
        } else {
            accuracy = 0;
        }
        perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;

        Com_sprintf (entry, sizeof(entry),
                     " %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", 
					 level.sortedClients[i],
                     cl->ps.persistant[PERS_SCORE], 
					 ping, 
					 (level.time - cl->pers.enterTime)/60000,
                     scoreFlags,
					 g_entities[level.sortedClients[i]].s.powerups, 
					 accuracy,
                     cl->ps.persistant[PERS_IMPRESSIVE_COUNT],
                     cl->ps.persistant[PERS_EXCELLENT_COUNT],
                     cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT],
                     cl->ps.persistant[PERS_DEFEND_COUNT],
                     cl->ps.persistant[PERS_ASSIST_COUNT],
                     perfect,
                     cl->ps.persistant[PERS_CAPTURES],
					 cl->ps.persistant[PERS_RETURNS]);
        j = strlen(entry);
        if (stringlength + j > 1024)
            break;
        strcpy (string + stringlength, entry);
        stringlength += j;
    }

    trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i,
                            level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE],
                            string ) );
}


/*
==================
Cmd_Score_f
 
Request current scoreboard information
==================
*/
void Cmd_Score_f( gentity_t *ent ) {
    DeathmatchScoreboardMessage( ent );
}



/*
==================
CheatsOk
==================
*/
qboolean	CheatsOk( gentity_t *ent ) {
    if ( !g_cheats.integer ) {
        trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
        return qfalse;
    }
    if ( ent->health <= 0 ) {
        trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\""));
        return qfalse;
    }
    return qtrue;
}


/*
==================
ConcatArgs
==================
*/
char	*ConcatArgs( int start ) {
    int		i, c, tlen;
    static char	line[MAX_STRING_CHARS];
    int		len;
    char	arg[MAX_STRING_CHARS];

    len = 0;
    c = trap_Argc();
    for ( i = start ; i < c ; i++ ) {
        trap_Argv( i, arg, sizeof( arg ) );
        tlen = strlen( arg );
        if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
            break;
        }
        memcpy( line + len, arg, tlen );
        len += tlen;
        if ( i != c - 1 ) {
            line[len] = ' ';
            len++;
        }
    }

    line[len] = 0;

    return line;
}

/*
==================
SanitizeString
 
Remove case and control characters
==================
*/
void SanitizeString( char *in, char *out ) {
    while ( *in ) {
        if ( *in == 27 ) {
            in += 2;		// skip color code
            continue;
        }
        if ( *in < 32 ) {
            in++;
            continue;
        }
        *out++ = tolower( *in++ );
    }

    *out = 0;
}

/*
==================
ClientNumberFromString
 
Returns a player number for either a number or name string
Returns -1 if invalid
==================
*/
int ClientNumberFromString( gentity_t *to, char *s ) {
    gclient_t	*cl;
    int			idnum;
    char		s2[MAX_STRING_CHARS];
    char		n2[MAX_STRING_CHARS];

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

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

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

    trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s));
    return -1;
}

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

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

    name = ConcatArgs( 1 );

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

    if (give_all || Q_stricmp( name, "health") == 0) {
        ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
        ent->armor = ent->client->ps.stats[STAT_MAX_ARMOR];
        if (!give_all)
            return;
    }


    //can we just let them have this  one using cvar grappleGlobal?
    // Alliance: grapple isn't a weapon
    // Joe - so? all this does is avoid giving you the grapple
	//default Grapple __ INCIN --should work for both now..
    if (give_all || Q_stricmp(name, "weapons") == 0) {
        ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 -
                                              ( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE );
        if (!give_all)
            return;
    }


    if (give_all || Q_stricmp(name, "ammo") == 0) {
        for ( i = 0 ; i < MAX_WEAPONS ; i++ ) {
            ent->client->ps.ammo[i] = 999;
        }
        if (!give_all)
            return;
    }

    if (give_all || Q_stricmp(name, "armor") == 0) {
        ent->client->ps.stats[STAT_ARMOR] = 150;

        if (!give_all)
            return;
    }

    if (Q_stricmp(name, "excellent") == 0) {
        ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
        return;
    }
    if (Q_stricmp(name, "impressive") == 0) {
        ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
        return;
    }
    if (Q_stricmp(name, "gauntletaward") == 0) {
        ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
        return;
    }
    if (Q_stricmp(name, "defend") == 0) {
        ent->client->ps.persistant[PERS_DEFEND_COUNT]++;
        return;
    }
    if (Q_stricmp(name, "assist") == 0) {
        ent->client->ps.persistant[PERS_ASSIST_COUNT]++;
        return;
    }

    // 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 ) );
        Touch_Item (it_ent, ent, &trace);
        if (it_ent->inuse) {
            G_FreeEntity( it_ent );
        }
    }
}


/*
==================
Cmd_God_f
 
Sets client to godmode
 
argv(0) god
==================
*/
void Cmd_God_f (gentity_t *ent) {
    char	*msg;

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

    ent->flags ^= FL_GODMODE;
    if (!(ent->flags & FL_GODMODE) )
        msg = "godmode OFF\n";
    else
        msg = "godmode ON\n";

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


/*
==================
Cmd_Notarget_f
 
Sets client to notarget
 
argv(0) notarget
==================
*/
void Cmd_Notarget_f( gentity_t *ent ) {
    char	*msg;

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

    ent->flags ^= FL_NOTARGET;
    if (!(ent->flags & FL_NOTARGET) )
        msg = "notarget OFF\n";
    else
        msg = "notarget ON\n";

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


/*
==================
Cmd_Noclip_f
 
argv(0) noclip
==================
*/
void Cmd_Noclip_f( gentity_t *ent ) {
    char	*msg;

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

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

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


/*
==================
Cmd_LevelShot_f
 
This is just to help generate the level pictures
for the menus.  It goes to the intermission immediately
and sends over a command to the client to resize the view,
hide the scoreboard, and take a special screenshot
==================
*/
void Cmd_LevelShot_f( gentity_t *ent ) {
    if ( !CheatsOk( ent ) ) {
        return;
    }

    // doesn't work in single player
    if ( g_gametype.integer != 0 ) {
        trap_SendServerCommand( ent-g_entities,
                                "print \"Must be in g_gametype 0 for levelshot\n\"" );
        return;
    }

    BeginIntermission();
    trap_SendServerCommand( ent-g_entities, "clientLevelShot" );
}


/*
==================
Cmd_LevelShot_f
 
This is just to help generate the level pictures
for the menus.  It goes to the intermission immediately
and sends over a command to the client to resize the view,
hide the scoreboard, and take a special screenshot
==================
*/
void Cmd_TeamTask_f( gentity_t *ent ) {
    char userinfo[MAX_INFO_STRING];
    char		arg[MAX_TOKEN_CHARS];
    int task;
    int client = ent->client - level.clients;

    if ( trap_Argc() != 2 ) {
        return;
    }
    trap_Argv( 1, arg, sizeof( arg ) );
    task = atoi( arg );

    trap_GetUserinfo(client, userinfo, sizeof(userinfo));
    Info_SetValueForKey(userinfo, "teamtask", va("%d", task));
    trap_SetUserinfo(client, userinfo);
    ClientUserinfoChanged(client);
}



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

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

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

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

    if (ent->health <= 0) {
        return;
    }
    ent->flags &= ~FL_GODMODE;
    ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
    player_die (ent, ent, ent, 100000, MOD_SUICIDE);
}

/*
=================
BroadCastTeamChange
 
Let everyone know about a team change
=================
*/
void BroadcastTeamChange( gclient_t *client, int oldTeam ) {


    if ( client->sess.sessionTeam == TEAM_RED ) {
        trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the RED team.\n\"",
                                       client->pers.netname) );
    } else if ( client->sess.sessionTeam == TEAM_BLUE ) {
        trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the BLUE team.\n\"",
                                       client->pers.netname));
    } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) {
        trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"",
                                       client->pers.netname));
        // Hugo
	} else if ( client->sess.sessionTeam == TEAM_SPECTATOR_RED && oldTeam != TEAM_SPECTATOR_RED ) {
        trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined ^1spectators.\n\"",
                                       client->pers.netname));
    } else if ( client->sess.sessionTeam == TEAM_SPECTATOR_BLUE && oldTeam != TEAM_SPECTATOR_BLUE ) {
        trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined ^4spectators.\n\"",
                                       client->pers.netname));
        //
    } else if ( client->sess.sessionTeam == TEAM_FREE ) {
        trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"",
                                       client->pers.netname));
    }
}

/*
=================
SetTeam
=================
*/
/*
void Cmd_Follow_f( gentity_t *ent );
 
void SetTeam( gentity_t *ent, char *s ) {
    int					team, oldTeam;
    gclient_t			*client;
    int					clientNum;
    spectatorState_t	specState;
    int					specClient;
    int					teamLeader;
	
    //
    // see what change is requested
    //
    client = ent->client;
 
    clientNum = client - level.clients;
    specClient = 0;
    specState = SPECTATOR_NOT;
    if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_SCOREBOARD;
    } else if ( !Q_stricmp( s, "follow1" ) ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_FOLLOW;
        specClient = -1;
    } else if ( !Q_stricmp( s, "follow2" ) ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_FOLLOW;
        specClient = -2;
    } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_FREE;
		specClient = -1;
	// alliance - Hugo
	} else if ( !Q_stricmp( s, "spectator red" ) ) {
		if ( TeamCount( ent->client->ps.clientNum, TEAM_RED ) > 0 ) {
			team = TEAM_SPECTATOR_RED;
			specState = SPECTATOR_FOLLOW;
			specClient = -1;
		} else {
			team = TEAM_SPECTATOR;
			specState = SPECTATOR_FREE;
			//specState = SPECTATOR_FOLLOW;
			specClient = 0;	//INCIN Test
		}
	} else if ( !Q_stricmp( s, "spectator blue" ) ) {
		if ( TeamCount( ent->client->ps.clientNum, TEAM_BLUE ) > 0 ) {
			team = TEAM_SPECTATOR_BLUE;
			specState = SPECTATOR_FOLLOW;
			specClient = -2;
		} else {
			team = TEAM_SPECTATOR;
			specState = SPECTATOR_FREE;
			//specState = SPECTATOR_FOLLOW;
			specClient = 0;	//INCIN Test
		}
	// alliance
    } else if ( g_gametype.integer >= GT_TEAM ) {
        // if running a team game, assign player to one of the teams
        specState = SPECTATOR_NOT;
        if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
            team = TEAM_RED;
        } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
            team = TEAM_BLUE;
        } else {
            // pick the team with the least number of players
            team = PickTeam( clientNum );
        }
 
        if ( g_teamForceBalance.integer  ) {
            int		counts[TEAM_NUM_TEAMS];
 
            counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE );
            counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED );
 
            // We allow a spread of two
            if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) {
                trap_SendServerCommand( ent->client->ps.clientNum,
                                        "cp \"Red team has too many players.\n\"" );
                return; // ignore the request
            }
            if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
                trap_SendServerCommand( ent->client->ps.clientNum,
                                        "cp \"Blue team has too many players.\n\"" );
                return; // ignore the request
            }
 
            // It's ok, the team we are switching to has less or same number of players
        }
 
	} else {
        // force them to spectators if there aren't any spots free
        team = TEAM_FREE;
		specState = SPECTATOR_FREE;
		//specState = SPECTATOR_FOLLOW;
		specClient = -1;	//INCIN Test
 
    }
 
    // override decision if limiting the players
    if ( (g_gametype.integer == GT_TOURNAMENT)
            && level.numNonSpectatorClients >= 2 ) {
        team = TEAM_SPECTATOR;
    } else 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;
    }
 
    //
    // execute the team change
    //
 
    // if the player was dead leave the body
    if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
        CopyToBodyQue(ent);
    }
 
    // he starts at 'base'
    client->pers.teamState.state = TEAM_BEGIN;
 
	// BB - Added TEAM_SPECTATOR red and blue in the condition
	if ( ( oldTeam != TEAM_SPECTATOR ) && ( oldTeam != TEAM_SPECTATOR_RED ) &&
		 ( oldTeam != TEAM_SPECTATOR_BLUE ) ) {
		// Kill him (makes sure he loses flags, etc)
		ent->flags &= ~FL_GODMODE;
		ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
		// BB - losing a point on team change sucks
		//player_die (ent, ent, ent, 100000, MOD_SUICIDE);
		player_die (ent, ent, ent, 100000, MOD_TEAMCHANGE);
 
 
	}
	// they go to the end of the line for tournements
	// alliance - Hugo - Added TEAM_SPECTATOR red and blue in the condition
	if ( ( team == TEAM_SPECTATOR ) || ( team == TEAM_SPECTATOR_RED ) ||
		 ( team == TEAM_SPECTATOR_BLUE ) ){
		client->sess.spectatorTime = level.time;
	}
 
    if ( oldTeam != TEAM_SPECTATOR ) {
        // Kill him (makes sure he loses flags, etc)
        ent->flags &= ~FL_GODMODE;
        // BB begin LOG BUGFIX SUICIDE
        //    Don't want to log a suicide...
        //ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
        //player_die (ent, ent, ent, 100000, MOD_SUICIDE);
        TossClientItems (ent);
        // BB end LOG BUGFIX SUICIDE
 
    }
    // they go to the end of the line for tournements
 
 
//    if ( team == TEAM_SPECTATOR ) {
//        client->sess.spectatorTime = level.time;
//    }
 
    client->sess.sessionTeam = team;
    client->sess.spectatorState = specState;
    client->sess.spectatorClient = specClient;
 
    client->sess.teamLeader = qfalse;
    if ( team == TEAM_RED || team == TEAM_BLUE ) {
        teamLeader = TeamLeader( team );
        // if there is no team leader or the team leader is a bot and this client is not a bot
        //BB
		//if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) {
            //SetLeader( team, clientNum );
        //}
    }
    // make sure there is a team leader on the team the player came from
 
    if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) {
        CheckTeamLeader( oldTeam );
    }
 
    BroadcastTeamChange( client, oldTeam );
 
    // get and distribute relevent paramters
    ClientUserinfoChanged( clientNum );
 
    ClientBegin( clientNum );
	
}
*/

/*
=================
SetTeam
=================
*/
void SetTeam( gentity_t *ent, char *s ) {
    int					team, oldTeam;
    gclient_t			*client;
    int					clientNum;
    spectatorState_t	specState;
    int					specClient;
    int					teamLeader;

    // alliance
    float oldFloatScore;

    //
    // see what change is requested
    //

    client = ent->client;

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

    specState = SPECTATOR_NOT;
    if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_SCOREBOARD;
    } else if ( !Q_stricmp( s, "follow1" ) ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_FOLLOW;
        specClient = -1;
    } else if ( !Q_stricmp( s, "follow2" ) ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_FOLLOW;
        specClient = -2;
    } else if ( !Q_stricmp( s, "spectator" )) { //|| !Q_stricmp( s, "s" ) ) {
        team = TEAM_SPECTATOR;
        specState = SPECTATOR_FREE;
        // alliance - Hugo
	} else if ( !Q_stricmp( s, "spectator red" ) ) {
        if ( TeamCount( ent->client->ps.clientNum, TEAM_RED ) > 0 ) {
            team = TEAM_SPECTATOR_RED;
            specState = SPECTATOR_FOLLOW;
            specClient = -1;
        } else {
            team = TEAM_SPECTATOR;
            specState = SPECTATOR_FREE;
        }
    } else if ( !Q_stricmp( s, "spectator blue" ) ) {
        if ( TeamCount( ent->client->ps.clientNum, TEAM_BLUE ) > 0 ) {
            team = TEAM_SPECTATOR_BLUE;
            specState = SPECTATOR_FOLLOW;
            specClient = -1;
        } else {
            team = TEAM_SPECTATOR;
            specState = SPECTATOR_FREE;
        }
        // alliance
	} else if ( g_gametype.integer >= GT_TEAM ) {
        // if running a team game, assign player to one of the teams
        specState = SPECTATOR_NOT;
        if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
            team = TEAM_RED;
        } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {

            team = TEAM_BLUE;
        } else {
            // pick the team with the least number of players
            team = PickTeam( clientNum );
        }

        if ( g_teamForceBalance.integer  ) {
            int		counts[TEAM_NUM_TEAMS];

            counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE );
            counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED );

            // We allow a spread of two
            if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) {
                trap_SendServerCommand( ent->client->ps.clientNum,
                                        "cp \"Red team has too many players.\n\"" );
                return; // ignore the request
            }
            if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
                trap_SendServerCommand( ent->client->ps.clientNum,
                                        "cp \"Blue team has too many players.\n\"" );
                return; // ignore the request
            }

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

	} else {
        // force them to spectators if there aren't any spots free
        team = TEAM_FREE;
    }

    // override decision if limiting the players
    if ( (g_gametype.integer == GT_TOURNAMENT)
            && level.numNonSpectatorClients >= 2 ) {
        team = TEAM_SPECTATOR;
    } else 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;
    }

    //
    // execute the team change
    //

    // if the player was dead leave the body
    if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
        CopyToBodyQue(ent);
    }

    // he starts at 'base'
    client->pers.teamState.state = TEAM_BEGIN;

    // alliance - Hugo - Added TEAM_SPECTATOR red and blue in the condition
    if ( ( oldTeam != TEAM_SPECTATOR ) && ( oldTeam != TEAM_SPECTATOR_RED ) &&
            ( oldTeam != TEAM_SPECTATOR_BLUE ) ) {
        // Kill him (makes sure he loses flags, etc)
        ent->flags &= ~FL_GODMODE;
        ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
        // Hugo - losing a point on team change sucks
        //player_die (ent, ent, ent, 100000, MOD_SUICIDE);
        player_die (ent, ent, ent, 100000, MOD_TEAMCHANGE);


    }
    // they go to the end of the line for tournements
    // alliance - Hugo - Added TEAM_SPECTATOR red and blue in the condition
    if ( ( team == TEAM_SPECTATOR ) || ( team == TEAM_SPECTATOR_RED ) ||
            ( team == TEAM_SPECTATOR_BLUE ) ) {
        client->sess.spectatorTime = level.time;
    }


    // Alliance: Nacho: If switching teams, reset declared pos.
    if ( oldTeam != team ) {
        client->pers.position = POS_UNDEFINED;
    }

    client->sess.sessionTeam = team;
    client->sess.spectatorState = specState;
    client->sess.spectatorClient = specClient;

    client->sess.teamLeader = qfalse;
    if ( team == TEAM_RED || team == TEAM_BLUE ) {
        teamLeader = TeamLeader( team );

        //SheBitch
        //Removed this to make bots be team leaders
        /*
        // if there is no team leader or the team leader is a bot and this client is not a bot
        if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) {
        	SetLeader( team, clientNum );
        }
        */
        //SheBitch end
    }
    // make sure there is a team leader on the team the player came from
    if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) {
        CheckTeamLeader( oldTeam );
    }

    BroadcastTeamChange( client, oldTeam );

    // get and distribute relevent paramters
    ClientUserinfoChanged( clientNum );

    // alliance
    // kom wants to save his score when switching teams
    //banshee 2.2 fix for teamswitch scoring. hm, seems we're coping FLOATSCORE  when PERS_SCORE is the value we wanna do
    //we'll keep their POINTS but all other team stuff stays with the team.
    //oldFloatScore = client->pers.floatScore;
    oldFloatScore = client->ps.persistant[PERS_SCORE];
    ClientBegin( clientNum );     // ps gets zeroed here
    // alliance

}

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

    // alliance - Hugo
    if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) ||
            ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) ) {
        Cmd_FollowCycle_f( ent, 1 );
        return;
    }
    // alliance

    ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
    ent->client->sess.sessionTeam = TEAM_SPECTATOR;
    ent->client->sess.spectatorState = SPECTATOR_FREE;
    //ent->client->sess.spectatorClient = 0;
    ent->client->ps.pm_flags &= ~PMF_FOLLOW;
    ent->r.svFlags &= ~SVF_BOT;
    ent->client->ps.clientNum = ent - g_entities;
}

/*
=================
Cmd_Team_f
=================
*/
void Cmd_Team_f( gentity_t *ent ) {
    int			oldTeam;
    char		s[MAX_TOKEN_CHARS];

    if ( trap_Argc() != 2 ) {
        oldTeam = ent->client->sess.sessionTeam;
        switch ( oldTeam ) {
        case TEAM_BLUE:
            trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" );
            break;
        case TEAM_RED:
            trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" );
            break;
        case TEAM_FREE:
            trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" );
            break;
        case TEAM_SPECTATOR:
            trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" );
            break;
            // alliance - Hugo
        case TEAM_SPECTATOR_RED:
            trap_SendServerCommand( ent-g_entities, "print \"Red Spectator team\n\"" );
            break;
        case TEAM_SPECTATOR_BLUE:
            trap_SendServerCommand( ent-g_entities, "print \"Blue Spectator team\n\"" );
            break;
            // alliance
        }
        return;
    }
    /*
       if ( ent->client->switchTeamTime > level.time ) {
           trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" );
           return;
       }
    */
    // if they are playing a tournement game, count as a loss
    if ( (g_gametype.integer == GT_TOURNAMENT )
            && ent->client->sess.sessionTeam == TEAM_FREE ) {
        ent->client->sess.losses++;
    }

    trap_Argv( 1, s, sizeof( s ) );

    SetTeam( ent, s );

    ent->client->switchTeamTime = level.time + 5000;
}


/*
=================
Cmd_Follow_f
=================
*/
void Cmd_Follow_f( gentity_t *ent ) {
    int		i;
    char	arg[MAX_TOKEN_CHARS];

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

    trap_Argv( 1, arg, sizeof( arg ) );
    i = ClientNumberFromString( ent, arg );
    if ( i == -1) {
        return;
    }

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

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

    // alliance - Hugo
    // can't follow another red spectator
    if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR_RED ) {
        return;
    }
    // can't follow another blue spectator
    if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR_BLUE ) {
        return;
    }
    // alliance

    // can't follow other team
    if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) &&
            ( level.clients[ i ].sess.sessionTeam == TEAM_BLUE ) ) {
        return;
    }

    if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) &&
            ( level.clients[ i ].sess.sessionTeam == TEAM_RED ) ) {
        return;
    }

    // if they are playing a tournement game, count as a loss
    if ( (g_gametype.integer == GT_TOURNAMENT )
            && ent->client->sess.sessionTeam == TEAM_FREE ) {
        ent->client->sess.losses++;
    }

    // first set them to spectator
    // alliance - Hugo - Added spec red & blue in condition
    if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR )
        SetTeam( ent, "spectator" );
    else if( ent->client->sess.sessionTeam != TEAM_SPECTATOR_RED )
        SetTeam( ent, "spectator red" );
    else if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR_BLUE )
        SetTeam( ent, "spectator blue" );


    ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
    ent->client->sess.spectatorClient = i;
}

/*
=================
Cmd_FollowCycle_f
=================
*/
/* INCIN -- Our old cycle command
void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
    int		clientnum;
    int		original;
	qboolean looped;
	
	looped = qfalse;
    // if they are playing a tournement game, count as a loss
    if ( (g_gametype.integer == GT_TOURNAMENT )
            && ent->client->sess.sessionTeam == TEAM_FREE ) {
        ent->client->sess.losses++;
    }
    // first set them to spectator
    if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) {
        SetTeam( ent, "spectator" );
    }
 
    if ( dir != 1 && dir != -1 ) {
        G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
    }
 
	//if in follow mode this will be -1 or -2 and not a player client id
	//if coming thru for the first time a negative value is set
    if(ent->client->sess.spectatorClient < 0 && ent->client->ps.clientNum >= 0) {
		original = ent->client->ps.clientNum; 
		clientnum = original;
	}else{
		clientnum = ent->client->sess.spectatorClient;
		original = clientnum;
	}
	//if(original < 0)
	//	return;
 
	
    do {
        clientnum += dir;
        
		if ( clientnum >= level.maxclients ) {
            clientnum = 0;
        }
        
		if ( clientnum == original ) {
			looped = qtrue;
		}
 
        // 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;
        }
 
        // this is good, we can use it
        ent->client->sess.spectatorClient = clientnum;
        ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
        return;
    } while ( clientnum != original && looped == qfalse);
	//INCIN --- this would get caught in a cyclic loop which caused the server to hang. 
	//it doesn't crash, just overflows due to not leaving the loop
 
    // leave it where it was
}
*/

/*
=================
Cmd_FollowCycle_f
=================
*/
//INCIN -- merged ours with alliance for team colors specs
void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
    int		clientnum;
    int		original;
    // alliance - Hugo
    int		counts[TEAM_NUM_TEAMS];
    qboolean looped;

    looped = qfalse;

    counts[TEAM_BLUE] = TeamCount( -1 , TEAM_BLUE );
    counts[TEAM_RED] = TeamCount( -1 , TEAM_RED );
    // alliance


    // if they are playing a tournement game, count as a loss
    if ( (g_gametype.integer == GT_TOURNAMENT )
            && ent->client->sess.sessionTeam == TEAM_FREE ) {
        ent->client->sess.losses++;
    }
    // first set them to spectator
    if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) {
        SetTeam( ent, "spectator" );
    }

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

    // alliance - Hugo
    if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) && ( counts[TEAM_RED] == 0 ) ) {
        SetTeam( ent, "spectator" );
        return;
    }

    if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) && ( counts[TEAM_BLUE] == 0 ) ) {
        SetTeam( ent, "spectator" );
        return;
    }
    // alliance
    //if in follow mode this will be -1 or -2 and not a player client id
    //if coming thru for the first time a negative value is set
    if(ent->client->sess.spectatorClient < 0 && ent->client->ps.clientNum >= 0) {
        original = ent->client->ps.clientNum;
        clientnum = original;
    } else {
        clientnum = ent->client->sess.spectatorClient;
        original = clientnum;
    }
    //if(original < 0)
    //	return;


    do {
        clientnum += dir;

        if ( clientnum == original ) {
            looped = qtrue;
        }

        if ( clientnum >= level.maxclients ) {
            clientnum = 0;
        } else if(clientnum == 0) {
            clientnum = level.maxclients;
        }



        // 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;
        }

        // alliance - Hugo
        if ( ( level.clients[ clientnum ].sess.sessionTeam == TEAM_RED ) &&
                ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) ) {
            continue;
        }

        if ( ( level.clients[ clientnum ].sess.sessionTeam == TEAM_BLUE ) &&
                ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) ) {
            continue;
        }
        // alliance

        // this is good, we can use it
        ent->client->sess.spectatorClient = clientnum;
        ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
        return;

    } while ( clientnum != original && looped == qfalse);



    // leave it where it was
}




/*
==================
G_Say
==================
*/

static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) {
    if (!other) {
        return;
    }
    if (!other->inuse) {
        return;
    }
    if (!other->client) {
        return;
    }
    if ( other->client->pers.connected != CON_CONNECTED ) {
        return;
    }
    if ( mode == SAY_TEAM  && !OnSameTeam(ent, other) ) {
        return;
    }
    // no chatting to players in tournements
    if ( (g_gametype.integer == GT_TOURNAMENT )
            && other->client->sess.sessionTeam == TEAM_FREE
            && ent->client->sess.sessionTeam != TEAM_FREE ) {
        return;
    }

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

#define EC		"\x19"

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];
    char		location[64];

    if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
        mode = SAY_ALL;
    }

    switch ( mode ) {
    default:
    case SAY_ALL:
        //BattleBots
        //G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );

        Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
        color = COLOR_GREEN;
        break;
    case SAY_TEAM:
        //Battlebots
        //G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );

        if (Team_GetLocationMsg(ent, location, sizeof(location)))
            Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC") (%s)"EC": ",
                         ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location);
        else
            Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ",
                         ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
        color = COLOR_CYAN;
        break;
    case SAY_TELL:
        if (target && g_gametype.integer >= GT_TEAM &&
                target->client->sess.sessionTeam == ent->client->sess.sessionTeam &&
                Team_GetLocationMsg(ent, location, sizeof(location)))
            Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"] (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location );
        else
            Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
        color = COLOR_MAGENTA;
        break;
    }

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

    if ( target ) {
        G_SayTo( ent, target, mode, color, name, text );
        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.maxclients; j++) {
        other = &g_entities[j];
        G_SayTo( ent, other, mode, color, name, text );
    }
}


/*
==================
Cmd_Say_f
==================
*/
static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
    char		*p;

    if ( trap_Argc () < 2 && !arg0 ) {
        return;
    }

    if (arg0) {
        p = ConcatArgs( 0 );
    } else {
        p = ConcatArgs( 1 );
    }

    G_Say( ent, NULL, mode, p );
}

/*
==================
Cmd_Tell_f
==================
*/
static void Cmd_Tell_f( gentity_t *ent ) {
    int			targetNum;
    gentity_t	*target;
    char		*p;
    char		arg[MAX_TOKEN_CHARS];

    if ( trap_Argc () < 2 ) {
        return;
    }

    trap_Argv( 1, arg, sizeof( arg ) );
    targetNum = atoi( arg );
    if ( targetNum < 0 || targetNum >= level.maxclients ) {
        return;
    }

    target = &g_entities[targetNum];
    if ( !target || !target->inuse || !target->client ) {
        return;
    }

    p = ConcatArgs( 2 );

    //BattleBots
    //G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );

    G_Say( ent, target, SAY_TELL, p );
    // don't tell to the player self if it was already directed to this player
    // also don't send the chat back to a bot
    if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
        G_Say( ent, ent, SAY_TELL, p );
    }
}


static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *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;
    }
    // no chatting to players in tournements
    if ( (g_gametype.integer == GT_TOURNAMENT )) {
        return;
    }

    if (mode == SAY_TEAM) {
        color = COLOR_CYAN;
        cmd = "vtchat";
    } else if (mode == SAY_TELL) {
        color = COLOR_MAGENTA;
        cmd = "vtell";
    } else {
        color = COLOR_GREEN;
        cmd = "vchat";
    }

    trap_SendServerCommand( other-g_entities, va("%s %d %d %d %s", cmd, voiceonly, ent->s.number, color, id));
}

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

    if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
        mode = SAY_ALL;
    }

    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);
    }

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

/*
==================
Cmd_Voice_f
==================
*/
static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) {
    char		*p;

    if ( trap_Argc () < 2 && !arg0 ) {
        return;
    }

    if (arg0) {
        p = ConcatArgs( 0 );
    } else {
        p = ConcatArgs( 1 );
    }

    G_Voice( ent, NULL, mode, p, voiceonly );
}

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

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


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



static char	*gc_orders[] = {
                               "hold your position",
                               "hold this position",
                               "come here",
                               "cover me",
                               "guard location",
                               "search and destroy",
                               "report"
                           };

void Cmd_GameCommand_f( gentity_t *ent ) {
    int		player;
    int		order;
    char	str[MAX_TOKEN_CHARS];

    trap_Argv( 1, str, sizeof( str ) );
    player = atoi( str );
    trap_Argv( 2, str, sizeof( str ) );
    order = atoi( str );

    if ( player < 0 || player >= MAX_CLIENTS ) {
        return;
    }
    if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) {
        return;
    }
    G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] );
    G_Say( ent, ent, SAY_TELL, gc_orders[order] );
}

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

static const char *gameNames[] = {
                                     "Free For All",
                                     "Tournament",
                                     "Single Player",
                                     "Team Deathmatch",
                                     "Capture the Flag",
                                     "Return the Flag",
                                     "One Flag CTF",
                                     "Overload",
                                     "Harvester"
                                 };

/*
==================
Cmd_CallVote_f
==================
*/
void Cmd_CallVote_f( gentity_t *ent ) {
    int		i;
    char	arg1[MAX_STRING_TOKENS];
    char	arg2[MAX_STRING_TOKENS];

    if ( !g_allowVote.integer ) {
        trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
        return;
    }

    if ( level.voteTime ) {
        trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" );
        return;
    }
    if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) {
        trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" );
        return;
    }
    if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
        trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
        return;
    }

    // make sure it is a valid command to vote on
    trap_Argv( 1, arg1, sizeof( arg1 ) );
    trap_Argv( 2, arg2, sizeof( arg2 ) );

    if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
        trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
        return;
    }

    if ( !Q_stricmp( arg1, "map_restart" ) ) {}
    else if ( !Q_stricmp( arg1, "nextmap" ) ) {}
    else if ( !Q_stricmp( arg1, "map" ) ) {}
    else if ( !Q_stricmp( arg1, "g_gametype" ) ) {}
    else if ( !Q_stricmp( arg1, "kick" ) ) {}
    else if ( !Q_stricmp( arg1, "clientkick" ) ) {}
    else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {}
    else if ( !Q_stricmp( arg1, "timelimit" ) ) {}
    else if ( !Q_stricmp( arg1, "fraglimit" ) ) {}
    else {
        trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
        trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map <mapname>, g_gametype <n>, kick <player>, clientkick <clientnum>, g_doWarmup, timelimit <time>, fraglimit <frags>.\n\"" );
        return;
    }

    // if there is still a vote to be executed
    if ( level.voteExecuteTime ) {
        level.voteExecuteTime = 0;
        trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
    }

    // special case for g_gametype, check for bad values
    if ( !Q_stricmp( arg1, "g_gametype" ) ) {
        i = atoi( arg2 );
        if( i == GT_SINGLE_PLAYER || i < GT_FFA || i >= GT_MAX_GAME_TYPE) {
            trap_SendServerCommand( ent-g_entities, "print \"Invalid gametype.\n\"" );
            return;
        }

        Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %d", arg1, i );
        Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, gameNames[i] );
    } else if ( !Q_stricmp( arg1, "map" ) ) {
        // special case for map changes, we want to reset the nextmap setting
        // this allows a player to change maps, but not upset the map rotation
        char	s[MAX_STRING_CHARS];

        trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
        if (*s) {
            Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s );
        } else {
            Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
        }
        Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
    } else if ( !Q_stricmp( arg1, "nextmap" ) ) {
        char	s[MAX_STRING_CHARS];

        trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
        if (!*s) {
            trap_SendServerCommand( ent-g_entities, "print \"nextmap not set.\n\"" );
            return;
        }
        Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap");
        Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
    } else {
        Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
        Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
    }

    trap_SendServerCommand( -1, va("print \"%s called a vote.\n\"", ent->client->pers.netname ) );

    // start the voting, the caller autoamtically votes yes
    level.voteTime = level.time;
    level.voteYes = 1;
    level.voteNo = 0;

    for ( i = 0 ; i < level.maxclients ; i++ ) {
        level.clients[i].ps.eFlags &= ~EF_VOTED;
    }
    ent->client->ps.eFlags |= EF_VOTED;

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

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

    if ( !level.voteTime ) {
        trap_SendServerCommand( ent-g_entities, "print \"No vote in progress.\n\"" );
        return;
    }
    if ( ent->client->ps.eFlags & EF_VOTED ) {
        trap_SendServerCommand( ent-g_entities, "print \"Vote already cast.\n\"" );
        return;
    }
    if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
        trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
        return;
    }

    trap_SendServerCommand( ent-g_entities, "print \"Vote cast.\n\"" );

    ent->client->ps.eFlags |= EF_VOTED;

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

    if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
        level.voteYes++;
        trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
    } else {
        level.voteNo++;
        trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );
    }

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

// alliance hugo drop code
/*
==================
Cmd_Drop_f
 
Drop items in game
==================
*/
void Cmd_Drop_f (gentity_t *ent) {
    char		*name;
    int			i, contents;
    gentity_t	*dropped;
    gitem_t		*item;
    vec3_t		muzzlePoint;
    vec3_t		forward, right, up;
    trace_t		tr;



    // Yeah spectators can't drop stuff right
    if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED )  &&
            ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) &&
            ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) ) {
        return;
    }
	
	

	//ent->client->dropflagtime = level.time;
    AngleVectors (ent->client->ps.viewangles, forward, right, up);

    // drop item from muzzle point to avoid player touching item b4 dropping it
    VectorCopy( ent->s.pos.trBase, muzzlePoint );
    muzzlePoint[2] += ent->client->ps.viewheight;
    //	forward[2] = 0;
    VectorMA( muzzlePoint, 60, forward, muzzlePoint );
    // snap to integer coordinates for more efficient network bandwidth usage
    SnapVector( muzzlePoint );

    // don't drop in walls
    contents = trap_PointContents( muzzlePoint, -1 );
    if ( contents & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP|CONTENTS_DONOTENTER|CONTENTS_NODROP))
        return;
    trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, muzzlePoint, ent->r.ownerNum, ent->clipmask );
    if ( tr.fraction != 1 )
        return;



    name = ConcatArgs( 1 );
    i = 0;
    item = NULL;

    if (Q_stricmp(name, "flag") == 0) {
				//spamming drops to pickup points sucks
			if(!ent->client->ps.powerups[ PW_REDFLAG ] &&
				!ent->client->ps.powerups[ PW_BLUEFLAG ] 
#ifdef MISSIONPACK
			&& !ent->client->ps.powerups[ PW_NEUTRALFLAG ]
#endif
			){
				return;
			}
			//Com_Printf("dropflag time:%i, level.time:%i\n", ent->client->dropflagtime, level.time);
			//initialize first time
			if(ent->client->dropflagtime == 0 && ent->client->dropflagcount == 0){
				ent->client->dropflagtime = level.time + 15000;
				ent->client->dropflagcount = 0;
			}
			else if(ent->client->dropflagtime < level.time + 15000 && ent->client->dropflagcount == 0) {
				//ent->client->dropflagtime = level.time + 15000;
				ent->client->dropflagcount++;
			}
			else if(ent->client->dropflagtime > level.time && ent->client->dropflagcount > 0){
				//Com_Printf("You can't drop the flag for %i more seconds\n", (ent->client->dropflagtime - level.time) / 1000  );
				//BG_Printf("You can't drop the flag for %i more seconds\n", (ent->client->dropflagtime - level.time) / 1000  );
				 trap_SendServerCommand( ent-g_entities, va("print \"You can't drop the flag for %i more seconds\n\"",
					 (ent->client->dropflagtime - level.time) / 1000) );
				ent->client->dropflagcount++;
				return;
			}
			else
			{
				ent->client->dropflagcount = 1;
				ent->client->dropflagtime = level.time + 15000;
			}

			//Com_Printf("dropflag time:%i, level.time:%i\n", ent->client->dropflagtime, level.time);

        // drop flags in CTF
        if ( ent->client->ps.powerups[ PW_REDFLAG ] ) {
            item = BG_FindItemForPowerup( PW_REDFLAG );
            i = PW_REDFLAG;
        } else if ( ent->client->ps.powerups[ PW_BLUEFLAG ] ) {
            item = BG_FindItemForPowerup( PW_BLUEFLAG );
            i = PW_BLUEFLAG;
        }
#ifdef MISSIONPACK
		else if ( ent->client->ps.powerups[ PW_NEUTRALFLAG ] ) {
			item = BG_FindItemForPowerup( PW_NEUTRALFLAG );
			i = PW_NEUTRALFLAG;
		}
#endif
        if ( item ) {
            ent->client->ps.powerups[ i ] = 0;
        }
    }

    if (Q_stricmp(name, "ammo") == 0 && ! g_ammoRestore.integer) {

        if ( G_TouchWeapon( ent ) )
            return;

        // drop current weapon ammo if not a gauntlet

		if ( ent->s.weapon >= WP_MACHINEGUN && ent->s.weapon != WP_HANDGRENADE && ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET &&
                ent->client->ps.ammo[ ent->s.weapon ] ) {
            // find the item type for this weapon's ammo
            item = BG_FindItemAmmoForWeapon( ent->s.weapon );

        }

        if ( item ) {
            if ( ent->client->ps.ammo[ ent->s.weapon ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ ent->s.weapon ];
            }
            ent->client->ps.ammo[ ent->s.weapon ] -= item->quantity;
        }
    }

    if (Q_stricmp(name, "armor") == 0 && ! g_healthRestore.integer) {

        // drop 50 armor

        if ( ent->client->ps.stats[STAT_ARMOR] >= 50 ) {
            // find the item type the yellow armor
            item = BG_FindItem( "Armor" );
        }

        if ( item ) {
            ent->client->ps.stats[STAT_ARMOR] -= 50;
        }
    }


    if (Q_stricmp(name, "item") == 0) {

        // drop holdable item

        if ( ent->client->ps.stats[STAT_HOLDABLE_ITEM] ) {
            // find the item for the holdable item
            item = BG_FindItemForHoldable( bg_itemlist[ent->client->ps.stats[STAT_HOLDABLE_ITEM]].giTag );
        }

        if ( item ) {
            ent->client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
        }
    }

    if ( Q_stricmp(name, "rune") == 0) {
		// drop rune
		for ( i = 1 ; i < PW_NUM_POWERUPS ; i++ ) {
			if ( ent->client->ps.powerups[ i ] > level.time ) {
				item = BG_FindItemForRune( i );
				if ( !item )  {
					continue;
				} else {
					ent->client->ps.powerups[ item->giTag ] = 0;
					ent->client->ps.stats[STAT_PERSISTANT_POWERUP] = 0;
					ent->client->persistantPowerup = NULL;
					break;
				}
			}
		}
		if ( item ) {
			ent->client->ps.powerups[ item->giTag ] = 0;
		}
	}

	if((Q_stricmp(name, "tech") == 0) ) {
		for ( i = 1 ; i < PW_NUM_POWERUPS ; i++ ) {
            if ( ent->client->ps.powerups[ i ] > level.time ) {
                item = BG_FindItemForRune( i );
                if ( !item )  {
                    continue;
                } else {
                    ent->client->ps.powerups[ item->giTag ] = 0;
                    ent->client->ps.stats[STAT_PERSISTANT_POWERUP] = 0;
                    ent->client->persistantPowerup = NULL;
                    break;
                }
            }
        }
        if ( item ) {
            ent->client->ps.powerups[ item->giTag ] = 0;
        }
    }
    //banshee 2.2 quad drop
    if ( (Q_stricmp(name, "quad") == 0) ) {

        // drop powerups
        if ( ent->client->ps.powerups[ PW_QUAD ] > level.time ) {
            item = BG_FindItemForPowerup( PW_QUAD );
        }
        //Banshee
        // drop quad if possessed when using wizard eye
        item = NULL;

        if ( ent->client->ps.powerups[ PW_QUAD ] ) {
            item = BG_FindItemForPowerup( PW_QUAD );
        }

        if ( item ) {
            dropped = Drop_Item( ent, item, 0 );
            // decide how many seconds it has left
            dropped->count = ( ent->client->ps.powerups[ PW_QUAD ] - level.time ) / 1000;
            if ( dropped->count < 1 ) {
                dropped->count = 1;
            }
            //throw it !
            dropped->s.pos.trType = TR_GRAVITY;
            dropped->s.pos.trTime = level.time - 50;	// move a bit on the very first frame
            VectorCopy( muzzlePoint, dropped->s.pos.trBase );
            VectorScale( forward, 500, dropped->s.pos.trDelta );
            SnapVector( dropped->s.pos.trDelta );		// save net bandwidth
            VectorCopy (muzzlePoint, dropped->r.currentOrigin);
            dropped->s.eFlags |= EF_BOUNCE_HALF;
            ent->client->ps.powerups[ PW_QUAD ] = 0;
            if ( g_gametype.integer >= GT_TEAM ) {
                play_team(ent, "available tech");
                G_Say( ent, NULL, SAY_TEAM, "Dropped QUAD" );
            }
            dropped->flags = FL_DROPPED_ITEM;

            trap_LinkEntity (dropped);

            return;
        }
    }




    if (Q_stricmp(name, "bullets") == 0 && ! g_ammoRestore.integer) {
        if ( G_TouchWeapon( ent ) )
            return;

        item = BG_FindItem( "Bullets" );

        if ( item ) {
            if ( ent->client->ps.ammo[ WP_MACHINEGUN ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ WP_MACHINEGUN ];
            }
            ent->client->ps.ammo[ WP_MACHINEGUN ] -= item->quantity;
            // infinite drop fix
            if ( !item->quantity ) {
                item = NULL;
            }
        }
    }

    if (Q_stricmp(name, "grenades") == 0 && ! g_ammoRestore.integer) {
        if ( G_TouchWeapon( ent ) )
            return;

        item = BG_FindItem( "Grenades" );

        if ( item ) {
            if ( ent->client->ps.ammo[ WP_GRENADE_LAUNCHER ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ WP_GRENADE_LAUNCHER ];
            }
            ent->client->ps.ammo[ WP_GRENADE_LAUNCHER ] -= item->quantity;
            // infinite drop fix
            if ( !item->quantity ) {
                item = NULL;
            }
        }
    }

    if (Q_stricmp(name, "cells") == 0 && ! g_ammoRestore.integer) {
        if ( G_TouchWeapon( ent ) )
            return;

        item = BG_FindItem( "Cells" );

        if ( item ) {
            if ( ent->client->ps.ammo[ WP_PLASMAGUN ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ WP_PLASMAGUN ];
            }
            ent->client->ps.ammo[ WP_PLASMAGUN ] -= item->quantity;
            // infinite drop fix
            if ( !item->quantity ) {
                item = NULL;
            }
        }
    }

    if (Q_stricmp(name, "lightning") == 0 && ! g_ammoRestore.integer) {
        if ( G_TouchWeapon( ent ) )
            return;

        item = BG_FindItem( "Lightning" );

        if ( item ) {
            if ( ent->client->ps.ammo[ WP_LIGHTNING ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ WP_LIGHTNING ];
            }
            ent->client->ps.ammo[ WP_LIGHTNING ] -= item->quantity;
            // infinite drop fix
            if ( !item->quantity ) {
                item = NULL;
            }
        }
    }

    if (Q_stricmp(name, "rockets") == 0 && ! g_ammoRestore.integer) {
        if ( G_TouchWeapon( ent ) )
            return;

        item = BG_FindItem( "Rockets" );

        if ( item ) {
            if ( ent->client->ps.ammo[ WP_ROCKET_LAUNCHER ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ WP_ROCKET_LAUNCHER ];
            }
            ent->client->ps.ammo[ WP_ROCKET_LAUNCHER ] -= item->quantity;
            // infinite drop fix
            if ( !item->quantity ) {
                item = NULL;
            }
        }
    }

    if (Q_stricmp(name, "slugs") == 0 && ! g_ammoRestore.integer) {

        if ( G_TouchWeapon( ent ) )
            return;

        item = BG_FindItem( "Slugs" );

        if ( item ) {
            if ( ent->client->ps.ammo[ WP_RAILGUN ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ WP_RAILGUN ];
            }
            ent->client->ps.ammo[ WP_RAILGUN ] -= item->quantity;
            // infinite drop fix
            if ( !item->quantity ) {
                item = NULL;
            }
        }
    }

    if (Q_stricmp(name, "bfg ammo") == 0 && ! g_ammoRestore.integer) {
        if ( G_TouchWeapon( ent ) )
            return;

        item = BG_FindItem( "Bfg Ammo" );

        if ( item ) {
            if ( ent->client->ps.ammo[ WP_BFG ] < item->quantity ) {
                item->quantity = ent->client->ps.ammo[ WP_BFG ];
            }
            ent->client->ps.ammo[ WP_BFG ] -= item->quantity;
            // infinite drop fix
            if ( !item->quantity ) {
                item = NULL;
            }
        }
    }

    if ( item ) {
        //banshee 2.2 below g_chatThrottle WAS 2000(2 seconds)
        // Hugo 2.5
#ifndef MISSIONPACK
        if ( g_gametype.integer >= GT_TEAM ) {
            if (!(level.time - ent->lastChatTime < g_chatThrottle.integer)) {
                switch (item->giTag) {
                case PW_RUNEDAMAGE:
                    play_team(ent, "available tech");
                    G_Say( ent, NULL, SAY_TEAM, "Dropped DAMAGE" );
                    break;
                case PW_RUNERESIST:
                    play_team(ent, "available tech");
                    G_Say( ent, NULL, SAY_TEAM, "Dropped RESIST" );
                    break;
                case PW_RUNEREGEN:
                    play_team(ent, "available tech");
                    G_Say( ent, NULL, SAY_TEAM, "Dropped REGEN" );
                    break;
                case PW_RUNEHASTE:
                    play_team(ent, "available tech");
                    G_Say( ent, NULL, SAY_TEAM, "Dropped HASTE" );
                    break;
                case PW_RUNEAMMOREGEN:
                    play_team(ent, "available tech");
                    G_Say( ent, NULL, SAY_TEAM, "Dropped AMMOREGEN" );
                    break;
                case PW_RUNEVAMPIRE:
                    play_team(ent, "available tech");
                    G_Say( ent, NULL, SAY_TEAM, "Dropped VAMPIRE" );
                    break;
                default:
                    break;
                }
                ent->lastChatTime = level.time;
            }
        }
#endif
        dropped = G_Spawn();

        dropped->s.eType = ET_ITEM;
        dropped->s.modelindex = item - bg_itemlist;
        dropped->s.modelindex2 = 1;

        dropped->classname = item->classname;
        dropped->item = item;
        dropped->count = item->quantity;

        VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
        VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
        dropped->r.contents = CONTENTS_TRIGGER;


        //throw it !
        dropped->s.pos.trType = TR_GRAVITY;
        dropped->s.pos.trTime = level.time - 50;	// move a bit on the very first frame
        VectorCopy( muzzlePoint, dropped->s.pos.trBase );
        VectorScale( forward, 500, dropped->s.pos.trDelta );
        SnapVector( dropped->s.pos.trDelta );		// save net bandwidth
        VectorCopy (muzzlePoint, dropped->r.currentOrigin);


        dropped->s.eFlags |= EF_BOUNCE_HALF;

        if (item->giType == IT_TEAM) { // Special case for CTF flags
            dropped->touch = Touch_Item;
            dropped->think = Team_DroppedFlagThink;
            dropped->nextthink = level.time + 30000;
            Team_CheckDroppedItem( dropped );
#ifdef MISSIONPACK
		//add code for dropping ta powerups
		//} else if ( item->giType == IT_POWERUP ) {
			//dropped->touch = Pickup_Rune;
			//dropped->takedamage = qtrue;
			//dropped->health = INT_MAX;
			//dropped->think = RespawnRunes;
			//dropped->nextthink = level.time + 30000;
#else
        } else if ( item->giType == IT_PERSISTANT_POWERUP ) {
            dropped->touch = Pickup_Rune;
            dropped->takedamage = qtrue;
            dropped->health = INT_MAX;
            dropped->think = RespawnRunes;
            dropped->nextthink = level.time + 10000;
#endif

        } else {
            dropped->touch = Touch_Item;
            dropped->nextthink = 0;
        }

        dropped->flags = FL_DROPPED_ITEM;

        trap_LinkEntity (dropped);

        return;
    }
}

/*
==================
Cmd_CallTeamVote_f
==================
*/
void Cmd_CallTeamVote_f( gentity_t *ent ) {
    int		i, team, cs_offset;
    char	arg1[MAX_STRING_TOKENS];
    char	arg2[MAX_STRING_TOKENS];

    team = ent->client->sess.sessionTeam;
    if ( team == TEAM_RED )
        cs_offset = 0;
    else if ( team == TEAM_BLUE )
        cs_offset = 1;
    else
        return;

    if ( !g_allowVote.integer ) {
        trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
        return;
    }

    if ( level.teamVoteTime[cs_offset] ) {
        trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress.\n\"" );
        return;
    }
    if ( ent->client->pers.teamVoteCount >= MAX_VOTE_COUNT ) {
        trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of team votes.\n\"" );
        return;
    }
    if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
        trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
        return;
    }

    if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) {
        trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
        return;
    }

    if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_BLUE ) {
        trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
        return;
    }

    // make sure it is a valid command to vote on
    trap_Argv( 1, arg1, sizeof( arg1 ) );
    arg2[0] = '\0';
    for ( i = 2; i < trap_Argc(); i++ ) {
        if (i > 2)
            strcat(arg2, " ");
        trap_Argv( i, &arg2[strlen(arg2)], sizeof( arg2 ) - strlen(arg2) );
    }

    if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
        trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
        return;
    }

    if ( !Q_stricmp( arg1, "leader" ) ) {
        char netname[MAX_NETNAME], leader[MAX_NETNAME];

        if ( !arg2[0] ) {
            i = ent->client->ps.clientNum;
        } else {
            // numeric values are just slot numbers
            for (i = 0; i < 3; i++) {
                if ( !arg2[i] || arg2[i] < '0' || arg2[i] > '9' )
                    break;
            }
            if ( i >= 3 || !arg2[i]) {
                i = atoi( arg2 );
                if ( i < 0 || i >= level.maxclients ) {
                    trap_SendServerCommand( ent-g_entities, va("print \"Bad client slot: %i\n\"", i) );
                    return;
                }

                if ( !g_entities[i].inuse ) {
                    trap_SendServerCommand( ent-g_entities, va("print \"Client %i is not active\n\"", i) );
                    return;
                }
            } else {
                Q_strncpyz(leader, arg2, sizeof(leader));
                Q_CleanStr(leader);
                for ( i = 0 ; i < level.maxclients ; i++ ) {
                    if ( level.clients[i].pers.connected == CON_DISCONNECTED )
                        continue;
                    if (level.clients[i].sess.sessionTeam != team)
                        continue;
                    Q_strncpyz(netname, level.clients[i].pers.netname, sizeof(netname));
                    Q_CleanStr(netname);
                    if ( !Q_stricmp(netname, leader) ) {
                        break;
                    }
                }
                if ( i >= level.maxclients ) {
                    trap_SendServerCommand( ent-g_entities, va("print \"%s is not a valid player on your team.\n\"", arg2) );
                    return;
                }
            }
        }
        Com_sprintf(arg2, sizeof(arg2), "%d", i);
    } else {
        trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
        trap_SendServerCommand( ent-g_entities, "print \"Team vote commands are: leader <player>.\n\"" );
        return;
    }

    Com_sprintf( level.teamVoteString[cs_offset], sizeof( level.teamVoteString[cs_offset] ), "%s %s", arg1, arg2 );

    for ( i = 0 ; i < level.maxclients ; i++ ) {
        if ( level.clients[i].pers.connected == CON_DISCONNECTED )
            continue;
        if (level.clients[i].sess.sessionTeam == team)
            trap_SendServerCommand( i, va("print \"%s called a team vote.\n\"", ent->client->pers.netname ) );
    }

    // start the voting, the caller autoamtically votes yes
    level.teamVoteTime[cs_offset] = level.time;
    level.teamVoteYes[cs_offset] = 1;
    level.teamVoteNo[cs_offset] = 0;

    for ( i = 0 ; i < level.maxclients ; i++ ) {
        if (level.clients[i].sess.sessionTeam == team)
            level.clients[i].ps.eFlags &= ~EF_TEAMVOTED;
    }
    ent->client->ps.eFlags |= EF_TEAMVOTED;

    trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, va("%i", level.teamVoteTime[cs_offset] ) );
    trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, level.teamVoteString[cs_offset] );
    trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
    trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) );
}

/*
==================
Cmd_TeamVote_f
==================
*/
void Cmd_TeamVote_f( gentity_t *ent ) {
    int			team, cs_offset;
    char		msg[64];

    team = ent->client->sess.sessionTeam;
    if ( team == TEAM_RED )
        cs_offset = 0;
    else if ( team == TEAM_BLUE )
        cs_offset = 1;
    else
        return;

    if ( !level.teamVoteTime[cs_offset] ) {
        trap_SendServerCommand( ent-g_entities, "print \"No team vote in progress.\n\"" );
        return;
    }
    if ( ent->client->ps.eFlags & EF_TEAMVOTED ) {
        trap_SendServerCommand( ent-g_entities, "print \"Team 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 ( ent->client->sess.sessionTeam == TEAM_SPECTATOR_RED ) {
        trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
        return;
    }

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

    trap_SendServerCommand( ent-g_entities, "print \"Team vote cast.\n\"" );

    ent->client->ps.eFlags |= EF_TEAMVOTED;

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

    if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
        level.teamVoteYes[cs_offset]++;
        trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
    } else {
        level.teamVoteNo[cs_offset]++;
        trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) );
    }

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


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

    if ( !g_cheats.integer ) {
        trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
        return;
    }
    if ( trap_Argc() != 5 ) {
        trap_SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
        return;
    }

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

    trap_Argv( 4, buffer, sizeof( buffer ) );
    angles[YAW] = atof( buffer );

    TeleportPlayer( ent, origin, angles );
}



/*
=================
Cmd_Stats_f
=================
*/
void Cmd_Stats_f( gentity_t *ent ) {
    /*
    	int max, n, i;
     
    	max = trap_AAS_PointReachabilityAreaIndex( NULL );
     
    	n = 0;
    	for ( i = 0; i < max; i++ ) {
    		if ( ent->client->areabits[i >> 3] & (1 << (i & 7)) )
    			n++;
    	}
     
    	//trap_SendServerCommand( ent-g_entities, va("print \"visited %d of %d areas\n\"", n, max));
    	trap_SendServerCommand( ent-g_entities, va("print \"%d%% level coverage\n\"", n * 100 / max));
    */
}



/*
==================
Cmd_GrappleOn_f - Hugo
==================
*/
/*
void Cmd_GrappleOn_f (int clientNum) {
    trap_EA_Action(clientNum, ACTION_GRAPPLEON);
}
*/
/*
==================
Cmd_GrappleOff_f - Hugo
==================
*/
/*
void Cmd_GrappleOff_f (int clientNum) {
    trap_EA_Action(clientNum, ACTION_GRAPPLEOFF);
}
*/
/*
=================
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 (level.time - ent->lastChatTime < g_chatThrottle.integer)
            return;

        ent->lastChatTime = level.time;

        // Joe
        addSoundtoMessage(ent);

        Cmd_Say_f (ent, SAY_ALL, qfalse);
        return;
    }
    if (Q_stricmp (cmd, "say_team") == 0) {
        if (level.time - ent->lastChatTime < g_chatThrottle.integer)
            return;

        ent->lastChatTime = level.time;

        // Joe - try to play audio
        addSoundtoTeamMessage(ent);

        Cmd_Say_f (ent, SAY_TEAM, qfalse);
        return;
    }
    if (Q_stricmp (cmd, "tell") == 0) {
        Cmd_Tell_f ( ent );
        return;
    }
    if (Q_stricmp (cmd, "vsay") == 0) {
        Cmd_Voice_f (ent, SAY_ALL, qfalse, qfalse);
        return;
    }
    if (Q_stricmp (cmd, "vsay_team") == 0) {
        Cmd_Voice_f (ent, SAY_TEAM, qfalse, qfalse);
        return;
    }
    if (Q_stricmp (cmd, "vtell") == 0) {
        Cmd_VoiceTell_f ( ent, qfalse );
        return;
    }
    if (Q_stricmp (cmd, "vosay") == 0) {
        Cmd_Voice_f (ent, SAY_ALL, qfalse, qtrue);
        return;
    }
    if (Q_stricmp (cmd, "vosay_team") == 0) {
        Cmd_Voice_f (ent, SAY_TEAM, qfalse, qtrue);
        return;
    }
    if (Q_stricmp (cmd, "votell") == 0) {
        Cmd_VoiceTell_f ( ent, qtrue );
        return;
    }
    if (Q_stricmp (cmd, "vtaunt") == 0) {
        Cmd_VoiceTaunt_f ( ent );
        return;
    }
    if (Q_stricmp (cmd, "score") == 0) {
        Cmd_Score_f (ent);
        return;
    }

    // Hugo
    if (Q_stricmp (cmd, "play_voice") == 0) {
        //Cmd_Play_Voice_f( ent );
        play_voice( ent, ConcatArgs( 1 ) );
        return;
    }

    // Hugo
    if (Q_stricmp (cmd, "play_team") == 0) {
        play_team( ent, ConcatArgs( 1 ) );
        return;
    }


    // ignore all other commands when at intermission
    if (level.intermissiontime) {
        Cmd_Say_f (ent, qfalse, qtrue);
        return;
    }

    if (Q_stricmp (cmd, "give") == 0)
        Cmd_Give_f (ent);
    else if (Q_stricmp (cmd, "god") == 0)
        Cmd_God_f (ent);
    else if (Q_stricmp (cmd, "notarget") == 0)
        Cmd_Notarget_f (ent);
    else if (Q_stricmp (cmd, "noclip") == 0)
        Cmd_Noclip_f (ent);
    else if (Q_stricmp (cmd, "kill") == 0)
        Cmd_Kill_f (ent);
    else if (Q_stricmp (cmd, "teamtask") == 0)
        Cmd_TeamTask_f (ent);
    else if (Q_stricmp (cmd, "levelshot") == 0)
        Cmd_LevelShot_f (ent);
    else if (Q_stricmp (cmd, "follow") == 0)	//This mode for zcam
        Cmd_Follow_f (ent);
    else if (Q_stricmp (cmd, "follownext") == 0)
        Cmd_FollowCycle_f (ent, 1);
    else if (Q_stricmp (cmd, "followprev") == 0)
        Cmd_FollowCycle_f (ent, -1);
    else if (Q_stricmp (cmd, "team") == 0)
        Cmd_Team_f (ent);
    else if (Q_stricmp (cmd, "where") == 0)
        Cmd_Where_f (ent);
    else if (Q_stricmp (cmd, "callvote") == 0)
        Cmd_CallVote_f (ent);
    else if (Q_stricmp (cmd, "vote") == 0)
        Cmd_Vote_f (ent);
    else if (Q_stricmp (cmd, "callteamvote") == 0)
        Cmd_CallTeamVote_f (ent);
    else if (Q_stricmp (cmd, "teamvote") == 0)
        Cmd_TeamVote_f (ent);
    else if (Q_stricmp (cmd, "gc") == 0)
        Cmd_GameCommand_f( ent );
    else if (Q_stricmp (cmd, "setviewpos") == 0)
        Cmd_SetViewpos_f( ent );
    else if (Q_stricmp (cmd, "stats") == 0)
        Cmd_Stats_f( ent );
    // alliance hugo drop code
    else if (Q_stricmp (cmd, "drop") == 0)
        Cmd_Drop_f( ent );
    else if (Q_stricmp (cmd, "observe") == 0)//this mode spectator mode(not zcam)//can type observe playername
        SetTeam( ent, "spectator" );
    else if (Q_stricmp (cmd, "observe_red") == 0)//this mode spectator mode(not zcam)
        SetTeam( ent, "spectator red");
    else if (Q_stricmp (cmd, "observe_blue") == 0)//this mode spectator mode(not zcam)
        SetTeam( ent, "spectator blue");
    //else if (Q_stricmp (cmd, "gamemap") == 0)
    //	Cmd_GameMap_f( ent );
    /*
       else if (Q_stricmp (cmd, "grappleon") == 0)
           Cmd_GrappleOn_f( clientNum );
       else if (Q_stricmp (cmd, "grappleoff") == 0)
           Cmd_GrappleOff_f( clientNum );
    */
    //#ifdef __ZCAM__
    else if (Q_stricmp (cmd, "camera") == 0)
        camera_cmd ( ent );
    //endif /* __ZACM__ */
    else
        trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
}
