2427 lines
62 KiB
C
2427 lines
62 KiB
C
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
#include "g_local.h"
|
|
|
|
#include "../../ui/menudef.h" // for the voice chats
|
|
|
|
//rww - for getting bot commands...
|
|
int AcceptBotCommand(char *cmd, gentity_t *pl);
|
|
//end rww
|
|
|
|
void BG_CycleInven(playerState_t *ps, int direction);
|
|
void BG_CycleForce(playerState_t *ps, int direction);
|
|
|
|
/*
|
|
==================
|
|
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;
|
|
|
|
if (numSorted > MAX_CLIENT_SCORE_SEND)
|
|
{
|
|
numSorted = MAX_CLIENT_SCORE_SEND;
|
|
}
|
|
|
|
for (i=0 ; i < numSorted ; i++) {
|
|
int ping;
|
|
|
|
cl = &level.clients[level.sortedClients[i]];
|
|
|
|
if ( cl->pers.connected == CON_CONNECTING ) {
|
|
ping = -1;
|
|
} else {
|
|
ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
|
|
}
|
|
|
|
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", 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]);
|
|
j = strlen(entry);
|
|
if (stringlength + j > 1022)
|
|
break;
|
|
strcpy (string + stringlength, entry);
|
|
stringlength += j;
|
|
}
|
|
|
|
//still want to know the total # of clients
|
|
i = level.numConnectedClients;
|
|
|
|
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 \"%s\n\"", G_GetStripEdString("SVINGAME", "NOCHEATS")));
|
|
return qfalse;
|
|
}
|
|
if ( ent->health <= 0 ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "MUSTBEALIVE")));
|
|
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[MAX_TOKEN_CHARS];
|
|
gitem_t *it;
|
|
int i;
|
|
qboolean give_all;
|
|
gentity_t *it_ent;
|
|
trace_t trace;
|
|
char arg[MAX_TOKEN_CHARS];
|
|
|
|
if ( !CheatsOk( ent ) ) {
|
|
return;
|
|
}
|
|
|
|
trap_Argv( 1, name, sizeof( name ) );
|
|
|
|
if (Q_stricmp(name, "all") == 0)
|
|
give_all = qtrue;
|
|
else
|
|
give_all = qfalse;
|
|
|
|
if (give_all)
|
|
{
|
|
i = 0;
|
|
while (i < HI_NUM_HOLDABLE)
|
|
{
|
|
ent->client->ps.stats[STAT_HOLDABLE_ITEMS] |= (1 << i);
|
|
i++;
|
|
}
|
|
i = 0;
|
|
}
|
|
|
|
if (give_all || Q_stricmp( name, "health") == 0)
|
|
{
|
|
if (trap_Argc() == 3) {
|
|
trap_Argv( 2, arg, sizeof( arg ) );
|
|
ent->health = atoi(arg);
|
|
if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH]) {
|
|
ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
|
|
}
|
|
}
|
|
else {
|
|
ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
|
|
}
|
|
if (!give_all)
|
|
return;
|
|
}
|
|
|
|
if (give_all || Q_stricmp(name, "weapons") == 0)
|
|
{
|
|
ent->client->ps.stats[STAT_WEAPONS] = (1 << (WP_DET_PACK+1)) - ( 1 << WP_NONE );
|
|
if (!give_all)
|
|
return;
|
|
}
|
|
|
|
if ( !give_all && Q_stricmp(name, "weaponnum") == 0 )
|
|
{
|
|
trap_Argv( 2, arg, sizeof( arg ) );
|
|
ent->client->ps.stats[STAT_WEAPONS] |= (1 << atoi(arg));
|
|
return;
|
|
}
|
|
|
|
if (give_all || Q_stricmp(name, "ammo") == 0)
|
|
{
|
|
int num = 999;
|
|
if (trap_Argc() == 3) {
|
|
trap_Argv( 2, arg, sizeof( arg ) );
|
|
num = atoi(arg);
|
|
}
|
|
for ( i = 0 ; i < MAX_WEAPONS ; i++ ) {
|
|
ent->client->ps.ammo[i] = num;
|
|
}
|
|
if (!give_all)
|
|
return;
|
|
}
|
|
|
|
if (give_all || Q_stricmp(name, "armor") == 0)
|
|
{
|
|
if (trap_Argc() == 3) {
|
|
trap_Argv( 2, arg, sizeof( arg ) );
|
|
ent->client->ps.stats[STAT_ARMOR] = atoi(arg);
|
|
} else {
|
|
ent->client->ps.stats[STAT_ARMOR] = ent->client->ps.stats[STAT_MAX_HEALTH];
|
|
}
|
|
|
|
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->health <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (g_gametype.integer == GT_TOURNAMENT && level.numPlayingClients > 1 && !level.warmupTime)
|
|
{
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "ATTEMPTDUELKILL")) );
|
|
return;
|
|
}
|
|
|
|
ent->flags &= ~FL_GODMODE;
|
|
ent->client->ps.stats[STAT_HEALTH] = ent->health = -999;
|
|
player_die (ent, ent, ent, 100000, MOD_SUICIDE);
|
|
}
|
|
|
|
gentity_t *G_GetDuelWinner(gclient_t *client)
|
|
{
|
|
gclient_t *wCl;
|
|
int i;
|
|
|
|
for ( i = 0 ; i < level.maxclients ; i++ ) {
|
|
wCl = &level.clients[i];
|
|
|
|
if (wCl && wCl != client && /*wCl->ps.clientNum != client->ps.clientNum &&*/
|
|
wCl->pers.connected == CON_CONNECTED && wCl->sess.sessionTeam != TEAM_SPECTATOR)
|
|
{
|
|
return &g_entities[wCl->ps.clientNum];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
BroadCastTeamChange
|
|
|
|
Let everyone know about a team change
|
|
=================
|
|
*/
|
|
void BroadcastTeamChange( gclient_t *client, int oldTeam )
|
|
{
|
|
client->ps.fd.forceDoInit = 1; //every time we change teams make sure our force powers are set right
|
|
|
|
if ( client->sess.sessionTeam == TEAM_RED ) {
|
|
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
|
|
client->pers.netname, G_GetStripEdString("SVINGAME", "JOINEDTHEREDTEAM")) );
|
|
} else if ( client->sess.sessionTeam == TEAM_BLUE ) {
|
|
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
|
|
client->pers.netname, G_GetStripEdString("SVINGAME", "JOINEDTHEBLUETEAM")));
|
|
} else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) {
|
|
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
|
|
client->pers.netname, G_GetStripEdString("SVINGAME", "JOINEDTHESPECTATORS")));
|
|
} else if ( client->sess.sessionTeam == TEAM_FREE ) {
|
|
if (g_gametype.integer == GT_TOURNAMENT)
|
|
{
|
|
/*
|
|
gentity_t *currentWinner = G_GetDuelWinner(client);
|
|
|
|
if (currentWinner && currentWinner->client)
|
|
{
|
|
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s %s\n\"",
|
|
currentWinner->client->pers.netname, G_GetStripEdString("SVINGAME", "VERSUS"), client->pers.netname));
|
|
}
|
|
else
|
|
{
|
|
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
|
|
client->pers.netname, G_GetStripEdString("SVINGAME", "JOINEDTHEBATTLE")));
|
|
}
|
|
*/
|
|
//NOTE: Just doing a vs. once it counts two players up
|
|
}
|
|
else
|
|
{
|
|
trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
|
|
client->pers.netname, G_GetStripEdString("SVINGAME", "JOINEDTHEBATTLE")));
|
|
}
|
|
}
|
|
|
|
G_LogPrintf ( "setteam: %i %s %s\n",
|
|
client - &level.clients[0],
|
|
TeamName ( oldTeam ),
|
|
TeamName ( client->sess.sessionTeam ) );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SetTeam
|
|
=================
|
|
*/
|
|
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;
|
|
} 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
|
|
//For now, don't do this. The legalize function will set powers properly now.
|
|
/*
|
|
if (g_forceBasedTeams.integer)
|
|
{
|
|
if (ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
|
|
{
|
|
team = TEAM_BLUE;
|
|
}
|
|
else
|
|
{
|
|
team = TEAM_RED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*/
|
|
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 ) {
|
|
//For now, don't do this. The legalize function will set powers properly now.
|
|
/*
|
|
if (g_forceBasedTeams.integer && ent->client->ps.fd.forceSide == FORCE_DARKSIDE)
|
|
{
|
|
trap_SendServerCommand( ent->client->ps.clientNum,
|
|
va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "TOOMANYRED_SWITCH")) );
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
trap_SendServerCommand( ent->client->ps.clientNum,
|
|
va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "TOOMANYRED")) );
|
|
}
|
|
return; // ignore the request
|
|
}
|
|
if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
|
|
//For now, don't do this. The legalize function will set powers properly now.
|
|
/*
|
|
if (g_forceBasedTeams.integer && ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
|
|
{
|
|
trap_SendServerCommand( ent->client->ps.clientNum,
|
|
va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "TOOMANYBLUE_SWITCH")) );
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
trap_SendServerCommand( ent->client->ps.clientNum,
|
|
va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "TOOMANYBLUE")) );
|
|
}
|
|
return; // ignore the request
|
|
}
|
|
|
|
// It's ok, the team we are switching to has less or same number of players
|
|
}
|
|
|
|
//For now, don't do this. The legalize function will set powers properly now.
|
|
/*
|
|
if (g_forceBasedTeams.integer)
|
|
{
|
|
if (team == TEAM_BLUE && ent->client->ps.fd.forceSide != FORCE_LIGHTSIDE)
|
|
{
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "MUSTBELIGHT")) );
|
|
return;
|
|
}
|
|
if (team == TEAM_RED && ent->client->ps.fd.forceSide != FORCE_DARKSIDE)
|
|
{
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "MUSTBEDARK")) );
|
|
return;
|
|
}
|
|
}
|
|
*/
|
|
|
|
} 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;
|
|
if ( oldTeam != TEAM_SPECTATOR ) {
|
|
// 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_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
|
|
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, qfalse );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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 ) {
|
|
ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
|
|
ent->client->sess.sessionTeam = TEAM_SPECTATOR;
|
|
ent->client->sess.spectatorState = SPECTATOR_FREE;
|
|
ent->client->ps.pm_flags &= ~PMF_FOLLOW;
|
|
ent->r.svFlags &= ~SVF_BOT;
|
|
ent->client->ps.clientNum = ent - g_entities;
|
|
ent->client->ps.weapon = WP_NONE;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "PRINTBLUETEAM")) );
|
|
break;
|
|
case TEAM_RED:
|
|
trap_SendServerCommand( ent-g_entities, va("print \"Red team\n\"", G_GetStripEdString("SVINGAME", "PRINTREDTEAM")) );
|
|
break;
|
|
case TEAM_FREE:
|
|
trap_SendServerCommand( ent-g_entities, va("print \"Free team\n\"", G_GetStripEdString("SVINGAME", "PRINTFREETEAM")) );
|
|
break;
|
|
case TEAM_SPECTATOR:
|
|
trap_SendServerCommand( ent-g_entities, va("print \"Spectator team\n\"", G_GetStripEdString("SVINGAME", "PRINTSPECTEAM")) );
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( ent->client->switchTeamTime > level.time ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOSWITCH")) );
|
|
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_Team_f
|
|
=================
|
|
*/
|
|
void Cmd_ForceChanged_f( gentity_t *ent )
|
|
{
|
|
char fpChStr[1024];
|
|
const char *buf;
|
|
// Cmd_Kill_f(ent);
|
|
if (ent->client->sess.sessionTeam == TEAM_SPECTATOR)
|
|
{ //if it's a spec, just make the changes now
|
|
//trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "FORCEAPPLIED")) );
|
|
//No longer print it, as the UI calls this a lot.
|
|
WP_InitForcePowers( ent );
|
|
goto argCheck;
|
|
}
|
|
|
|
buf = G_GetStripEdString("SVINGAME", "FORCEPOWERCHANGED");
|
|
|
|
strcpy(fpChStr, buf);
|
|
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s%s\n\"", S_COLOR_GREEN, fpChStr) );
|
|
|
|
ent->client->ps.fd.forceDoInit = 1;
|
|
argCheck:
|
|
if (trap_Argc() > 1)
|
|
{
|
|
char arg[MAX_TOKEN_CHARS];
|
|
|
|
trap_Argv( 1, arg, sizeof( arg ) );
|
|
|
|
if (arg && arg[0])
|
|
{ //if there's an arg, assume it's a combo team command from the UI.
|
|
Cmd_Team_f(ent);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
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;
|
|
}
|
|
|
|
// 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.sessionTeam != TEAM_SPECTATOR ) {
|
|
SetTeam( ent, "spectator" );
|
|
}
|
|
|
|
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;
|
|
|
|
// 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 );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// 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_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:
|
|
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:
|
|
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 );
|
|
|
|
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 );
|
|
|
|
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_STUN_BATON) {
|
|
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",
|
|
"Holocron FFA",
|
|
"Jedi Master",
|
|
"Duel",
|
|
"Single Player",
|
|
"Team FFA",
|
|
"N/A",
|
|
"Capture the Flag",
|
|
"Capture the Ysalamiri"
|
|
};
|
|
|
|
/*
|
|
==================
|
|
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, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOVOTE")) );
|
|
return;
|
|
}
|
|
|
|
if ( level.voteTime ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "VOTEINPROGRESS")) );
|
|
return;
|
|
}
|
|
if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "MAXVOTES")) );
|
|
return;
|
|
}
|
|
if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOSPECVOTE")) );
|
|
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;
|
|
}
|
|
|
|
level.votingGametype = qtrue;
|
|
level.votingGametypeTo = i;
|
|
|
|
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];
|
|
|
|
if (!G_DoesMapSupportGametype(arg2, trap_Cvar_VariableIntegerValue("g_gametype")))
|
|
{
|
|
//trap_SendServerCommand( ent-g_entities, "print \"You can't vote for this map, it isn't supported by the current gametype.\n\"" );
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOVOTE_MAPNOTSUPPORTEDBYGAME")) );
|
|
return;
|
|
}
|
|
|
|
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 %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLCALLEDVOTE") ) );
|
|
|
|
// 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, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOVOTEINPROG")) );
|
|
return;
|
|
}
|
|
if ( ent->client->ps.eFlags & EF_VOTED ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "VOTEALREADY")) );
|
|
return;
|
|
}
|
|
if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOVOTEASSPEC")) );
|
|
return;
|
|
}
|
|
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "PLVOTECAST")) );
|
|
|
|
ent->client->ps.eFlags |= EF_VOTED;
|
|
|
|
trap_Argv( 1, msg, sizeof( msg ) );
|
|
|
|
if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
|
|
level.voteYes++;
|
|
trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
|
|
} else {
|
|
level.voteNo++;
|
|
trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );
|
|
}
|
|
|
|
// a majority will be determined in CheckVote, which will also account
|
|
// for players entering or leaving
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Cmd_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, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOVOTE")) );
|
|
return;
|
|
}
|
|
|
|
if ( level.teamVoteTime[cs_offset] ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "TEAMVOTEALREADY")) );
|
|
return;
|
|
}
|
|
if ( ent->client->pers.teamVoteCount >= MAX_VOTE_COUNT ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "MAXTEAMVOTES")) );
|
|
return;
|
|
}
|
|
if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOSPECVOTE")) );
|
|
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, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOTEAMVOTEINPROG")) );
|
|
return;
|
|
}
|
|
if ( ent->client->ps.eFlags & EF_TEAMVOTED ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "TEAMVOTEALREADYCAST")) );
|
|
return;
|
|
}
|
|
if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NOVOTEASSPEC")) );
|
|
return;
|
|
}
|
|
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "PLTEAMVOTECAST")) );
|
|
|
|
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 \"%s\n\"", G_GetStripEdString("SVINGAME", "NOCHEATS")));
|
|
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));
|
|
*/
|
|
}
|
|
|
|
int G_ItemUsable(playerState_t *ps, int forcedUse)
|
|
{
|
|
vec3_t fwd, fwdorg, dest, pos;
|
|
vec3_t yawonly;
|
|
vec3_t mins, maxs;
|
|
vec3_t trtest;
|
|
trace_t tr;
|
|
|
|
if (ps->usingATST)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (ps->pm_flags & PMF_USE_ITEM_HELD)
|
|
{ //force to let go first
|
|
return 0;
|
|
}
|
|
|
|
if (!forcedUse)
|
|
{
|
|
forcedUse = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
|
|
}
|
|
|
|
switch (forcedUse)
|
|
{
|
|
case HI_MEDPAC:
|
|
if (ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH])
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (ps->stats[STAT_HEALTH] <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
case HI_SEEKER:
|
|
if (ps->eFlags & EF_SEEKERDRONE)
|
|
{
|
|
G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SEEKER_ALREADYDEPLOYED);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
case HI_SENTRY_GUN:
|
|
if (ps->fd.sentryDeployed)
|
|
{
|
|
G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SENTRY_ALREADYPLACED);
|
|
return 0;
|
|
}
|
|
|
|
yawonly[ROLL] = 0;
|
|
yawonly[PITCH] = 0;
|
|
yawonly[YAW] = ps->viewangles[YAW];
|
|
|
|
VectorSet( mins, -8, -8, 0 );
|
|
VectorSet( maxs, 8, 8, 24 );
|
|
|
|
AngleVectors(yawonly, fwd, NULL, NULL);
|
|
|
|
fwdorg[0] = ps->origin[0] + fwd[0]*64;
|
|
fwdorg[1] = ps->origin[1] + fwd[1]*64;
|
|
fwdorg[2] = ps->origin[2] + fwd[2]*64;
|
|
|
|
trtest[0] = fwdorg[0] + fwd[0]*16;
|
|
trtest[1] = fwdorg[1] + fwd[1]*16;
|
|
trtest[2] = fwdorg[2] + fwd[2]*16;
|
|
|
|
trap_Trace(&tr, ps->origin, mins, maxs, trtest, ps->clientNum, MASK_PLAYERSOLID);
|
|
|
|
if ((tr.fraction != 1 && tr.entityNum != ps->clientNum) || tr.startsolid || tr.allsolid)
|
|
{
|
|
G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SENTRY_NOROOM);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
case HI_SHIELD:
|
|
mins[0] = -8;
|
|
mins[1] = -8;
|
|
mins[2] = 0;
|
|
|
|
maxs[0] = 8;
|
|
maxs[1] = 8;
|
|
maxs[2] = 8;
|
|
|
|
AngleVectors (ps->viewangles, fwd, NULL, NULL);
|
|
fwd[2] = 0;
|
|
VectorMA(ps->origin, 64, fwd, dest);
|
|
trap_Trace(&tr, ps->origin, mins, maxs, dest, ps->clientNum, MASK_SHOT );
|
|
if (tr.fraction > 0.9 && !tr.startsolid && !tr.allsolid)
|
|
{
|
|
VectorCopy(tr.endpos, pos);
|
|
VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
|
|
trap_Trace( &tr, pos, mins, maxs, dest, ps->clientNum, MASK_SOLID );
|
|
if ( !tr.startsolid && !tr.allsolid )
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SHIELD_NOROOM);
|
|
return 0;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
extern int saberOffSound;
|
|
extern int saberOnSound;
|
|
|
|
void Cmd_ToggleSaber_f(gentity_t *ent)
|
|
{
|
|
if (!saberOffSound || !saberOnSound)
|
|
{
|
|
saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav");
|
|
saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav");
|
|
}
|
|
|
|
if (ent->client->ps.saberInFlight)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ent->client->ps.forceHandExtend != HANDEXTEND_NONE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ent->client->ps.weapon != WP_SABER)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if (ent->client->ps.duelInProgress && !ent->client->ps.saberHolstered)
|
|
// {
|
|
// return;
|
|
// }
|
|
|
|
if (ent->client->ps.duelTime >= level.time)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ent->client && ent->client->ps.weaponTime < 1)
|
|
{
|
|
if (ent->client->ps.saberHolstered)
|
|
{
|
|
ent->client->ps.saberHolstered = qfalse;
|
|
G_Sound(ent, CHAN_AUTO, saberOnSound);
|
|
}
|
|
else
|
|
{
|
|
ent->client->ps.saberHolstered = qtrue;
|
|
G_Sound(ent, CHAN_AUTO, saberOffSound);
|
|
|
|
//prevent anything from being done for 400ms after holster
|
|
ent->client->ps.weaponTime = 400;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Cmd_SaberAttackCycle_f(gentity_t *ent)
|
|
{
|
|
int selectLevel = 0;
|
|
|
|
if ( !ent || !ent->client )
|
|
{
|
|
return;
|
|
}
|
|
/*
|
|
if (ent->client->ps.weaponTime > 0)
|
|
{ //no switching attack level when busy
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (ent->client->saberCycleQueue)
|
|
{ //resume off of the queue if we haven't gotten a chance to update it yet
|
|
selectLevel = ent->client->saberCycleQueue;
|
|
}
|
|
else
|
|
{
|
|
selectLevel = ent->client->ps.fd.saberAnimLevel;
|
|
}
|
|
|
|
selectLevel++;
|
|
if ( selectLevel > ent->client->ps.fd.forcePowerLevel[FP_SABERATTACK] )
|
|
{
|
|
selectLevel = FORCE_LEVEL_1;
|
|
}
|
|
/*
|
|
#ifndef FINAL_BUILD
|
|
switch ( selectLevel )
|
|
{
|
|
case FORCE_LEVEL_1:
|
|
trap_SendServerCommand( ent-g_entities, va("print \"Lightsaber Combat Style: %sfast\n\"", S_COLOR_BLUE) );
|
|
break;
|
|
case FORCE_LEVEL_2:
|
|
trap_SendServerCommand( ent-g_entities, va("print \"Lightsaber Combat Style: %smedium\n\"", S_COLOR_YELLOW) );
|
|
break;
|
|
case FORCE_LEVEL_3:
|
|
trap_SendServerCommand( ent-g_entities, va("print \"Lightsaber Combat Style: %sstrong\n\"", S_COLOR_RED) );
|
|
break;
|
|
}
|
|
#endif
|
|
*/
|
|
if (ent->client->ps.weaponTime <= 0)
|
|
{ //not busy, set it now
|
|
ent->client->ps.fd.saberAnimLevel = selectLevel;
|
|
}
|
|
else
|
|
{ //can't set it now or we might cause unexpected chaining, so queue it
|
|
ent->client->saberCycleQueue = selectLevel;
|
|
}
|
|
}
|
|
|
|
qboolean G_OtherPlayersDueling(void)
|
|
{
|
|
int i = 0;
|
|
gentity_t *ent;
|
|
|
|
while (i < MAX_CLIENTS)
|
|
{
|
|
ent = &g_entities[i];
|
|
|
|
if (ent && ent->inuse && ent->client && ent->client->ps.duelInProgress)
|
|
{
|
|
return qtrue;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
void Cmd_EngageDuel_f(gentity_t *ent)
|
|
{
|
|
trace_t tr;
|
|
vec3_t forward, fwdOrg;
|
|
|
|
if (!g_privateDuel.integer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (g_gametype.integer == GT_TOURNAMENT)
|
|
{ //rather pointless in this mode..
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NODUEL_GAMETYPE")) );
|
|
return;
|
|
}
|
|
|
|
if (g_gametype.integer >= GT_TEAM)
|
|
{ //no private dueling in team modes
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "NODUEL_GAMETYPE")) );
|
|
return;
|
|
}
|
|
|
|
if (ent->client->ps.duelTime >= level.time)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ent->client->ps.weapon != WP_SABER)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
if (!ent->client->ps.saberHolstered)
|
|
{ //must have saber holstered at the start of the duel
|
|
return;
|
|
}
|
|
*/
|
|
//NOTE: No longer doing this..
|
|
|
|
if (ent->client->ps.saberInFlight)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ent->client->ps.duelInProgress)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//New: Don't let a player duel if he just did and hasn't waited 10 seconds yet (note: If someone challenges him, his duel timer will reset so he can accept)
|
|
if (ent->client->ps.fd.privateDuelTime > level.time)
|
|
{
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "CANTDUEL_JUSTDID")) );
|
|
return;
|
|
}
|
|
|
|
if (G_OtherPlayersDueling())
|
|
{
|
|
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "CANTDUEL_BUSY")) );
|
|
return;
|
|
}
|
|
|
|
AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );
|
|
|
|
fwdOrg[0] = ent->client->ps.origin[0] + forward[0]*256;
|
|
fwdOrg[1] = ent->client->ps.origin[1] + forward[1]*256;
|
|
fwdOrg[2] = (ent->client->ps.origin[2]+ent->client->ps.viewheight) + forward[2]*256;
|
|
|
|
trap_Trace(&tr, ent->client->ps.origin, NULL, NULL, fwdOrg, ent->s.number, MASK_PLAYERSOLID);
|
|
|
|
if (tr.fraction != 1 && tr.entityNum < MAX_CLIENTS)
|
|
{
|
|
gentity_t *challenged = &g_entities[tr.entityNum];
|
|
|
|
if (!challenged || !challenged->client || !challenged->inuse ||
|
|
challenged->health < 1 || challenged->client->ps.stats[STAT_HEALTH] < 1 ||
|
|
challenged->client->ps.weapon != WP_SABER || challenged->client->ps.duelInProgress ||
|
|
challenged->client->ps.saberInFlight)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (g_gametype.integer >= GT_TEAM && OnSameTeam(ent, challenged))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (challenged->client->ps.duelIndex == ent->s.number && challenged->client->ps.duelTime >= level.time)
|
|
{
|
|
trap_SendServerCommand( /*challenged-g_entities*/-1, va("print \"%s %s %s!\n\"", challenged->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELACCEPT"), ent->client->pers.netname) );
|
|
|
|
ent->client->ps.duelInProgress = qtrue;
|
|
challenged->client->ps.duelInProgress = qtrue;
|
|
|
|
ent->client->ps.duelTime = level.time + 2000;
|
|
challenged->client->ps.duelTime = level.time + 2000;
|
|
|
|
G_AddEvent(ent, EV_PRIVATE_DUEL, 1);
|
|
G_AddEvent(challenged, EV_PRIVATE_DUEL, 1);
|
|
|
|
//Holster their sabers now, until the duel starts (then they'll get auto-turned on to look cool)
|
|
|
|
if (!ent->client->ps.saberHolstered)
|
|
{
|
|
G_Sound(ent, CHAN_AUTO, saberOffSound);
|
|
ent->client->ps.weaponTime = 400;
|
|
ent->client->ps.saberHolstered = qtrue;
|
|
}
|
|
if (!challenged->client->ps.saberHolstered)
|
|
{
|
|
G_Sound(challenged, CHAN_AUTO, saberOffSound);
|
|
challenged->client->ps.weaponTime = 400;
|
|
challenged->client->ps.saberHolstered = qtrue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Print the message that a player has been challenged in private, only announce the actual duel initiation in private
|
|
trap_SendServerCommand( challenged-g_entities, va("cp \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELCHALLENGE")) );
|
|
trap_SendServerCommand( ent-g_entities, va("cp \"%s %s\n\"", G_GetStripEdString("SVINGAME", "PLDUELCHALLENGED"), challenged->client->pers.netname) );
|
|
}
|
|
|
|
challenged->client->ps.fd.privateDuelTime = 0; //reset the timer in case this player just got out of a duel. He should still be able to accept the challenge.
|
|
|
|
ent->client->ps.forceHandExtend = HANDEXTEND_DUELCHALLENGE;
|
|
ent->client->ps.forceHandExtendTime = level.time + 1000;
|
|
|
|
ent->client->ps.duelIndex = challenged->s.number;
|
|
ent->client->ps.duelTime = level.time + 5000;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
ClientCommand
|
|
=================
|
|
*/
|
|
void ClientCommand( int clientNum ) {
|
|
gentity_t *ent;
|
|
char cmd[MAX_TOKEN_CHARS];
|
|
|
|
ent = g_entities + clientNum;
|
|
if ( !ent->client ) {
|
|
return; // not fully in game yet
|
|
}
|
|
|
|
|
|
trap_Argv( 0, cmd, sizeof( cmd ) );
|
|
|
|
//rww - redirect bot commands
|
|
if (strstr(cmd, "bot_") && AcceptBotCommand(cmd, ent))
|
|
{
|
|
return;
|
|
}
|
|
//end rww
|
|
|
|
if (Q_stricmp (cmd, "say") == 0) {
|
|
Cmd_Say_f (ent, SAY_ALL, qfalse);
|
|
return;
|
|
}
|
|
if (Q_stricmp (cmd, "say_team") == 0) {
|
|
Cmd_Say_f (ent, SAY_TEAM, qfalse);
|
|
return;
|
|
}
|
|
if (Q_stricmp (cmd, "tell") == 0) {
|
|
Cmd_Tell_f ( ent );
|
|
return;
|
|
}
|
|
if (Q_stricmp (cmd, "vsay") == 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;
|
|
}
|
|
|
|
// ignore all other commands when at intermission
|
|
if (level.intermissiontime)
|
|
{
|
|
qboolean giveError = qfalse;
|
|
|
|
if (!Q_stricmp(cmd, "give"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "god"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "notarget"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "noclip"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "kill"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "teamtask"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "levelshot"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "follow"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "follownext"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "followprev"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "team"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "forcechanged"))
|
|
{ //special case: still update force change
|
|
Cmd_ForceChanged_f (ent);
|
|
return;
|
|
}
|
|
else if (!Q_stricmp(cmd, "where"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "callvote"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "vote"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "callteamvote"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "teamvote"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "gc"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "setviewpos"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
else if (!Q_stricmp(cmd, "stats"))
|
|
{
|
|
giveError = qtrue;
|
|
}
|
|
|
|
if (giveError)
|
|
{
|
|
trap_SendServerCommand( clientNum, va("print \"You cannot perform this task (%s) during the intermission.\n\"", cmd ) );
|
|
}
|
|
else
|
|
{
|
|
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)
|
|
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, "forcechanged") == 0)
|
|
Cmd_ForceChanged_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 );
|
|
else if (Q_stricmp(cmd, "#mm") == 0 && CheatsOk( ent ))
|
|
{
|
|
G_PlayerBecomeATST(ent);
|
|
}
|
|
else if (Q_stricmp(cmd, "thedestroyer") == 0 && CheatsOk( ent ) && ent && ent->client && ent->client->ps.saberHolstered && ent->client->ps.weapon == WP_SABER)
|
|
{
|
|
Cmd_ToggleSaber_f(ent);
|
|
|
|
if (!ent->client->ps.saberHolstered)
|
|
{
|
|
if (ent->client->ps.dualBlade)
|
|
{
|
|
ent->client->ps.dualBlade = qfalse;
|
|
//ent->client->ps.fd.saberAnimLevel = FORCE_LEVEL_1;
|
|
}
|
|
else
|
|
{
|
|
ent->client->ps.dualBlade = qtrue;
|
|
|
|
trap_SendServerCommand( -1, va("print \"%sTHE DESTROYER COMETH\n\"", S_COLOR_RED) );
|
|
G_ScreenShake(vec3_origin, NULL, 10.0f, 800, qtrue);
|
|
//ent->client->ps.fd.saberAnimLevel = FORCE_LEVEL_3;
|
|
}
|
|
}
|
|
}
|
|
#ifdef _DEBUG
|
|
else if (Q_stricmp(cmd, "gotocoord") == 0 && CheatsOk( ent ))
|
|
{
|
|
char x[64], y[64], z[64];
|
|
vec3_t xyz;
|
|
|
|
if (trap_Argc() < 3)
|
|
{
|
|
return;
|
|
}
|
|
|
|
trap_Argv( 1, x, sizeof( x ) );
|
|
trap_Argv( 2, y, sizeof( y ) );
|
|
trap_Argv( 3, z, sizeof( z ) );
|
|
|
|
xyz[0] = atof(x);
|
|
xyz[1] = atof(y);
|
|
xyz[2] = atof(z);
|
|
|
|
VectorCopy(xyz, ent->client->ps.origin);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
else if (Q_stricmp (cmd, "offwithmyhead") == 0)
|
|
{
|
|
DismembermentTest(ent);
|
|
}
|
|
*/
|
|
else
|
|
{
|
|
if (Q_stricmp(cmd, "addbot") == 0)
|
|
{ //because addbot isn't a recognized command unless you're the server, but it is in the menus regardless
|
|
trap_SendServerCommand( clientNum, va("print \"You can only add bots as the server.\n\"" ) );
|
|
}
|
|
else
|
|
{
|
|
trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
|
|
}
|
|
}
|
|
}
|