1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 //
23 #include "g_local.h"
24 
25 #include "../../ui/menudef.h"			// for the voice chats
26 
27 /*
28 ==================
29 DeathmatchScoreboardMessage
30 
31 ==================
32 */
DeathmatchScoreboardMessage(gentity_t * ent)33 void DeathmatchScoreboardMessage( gentity_t *ent ) {
34 	char		entry[1024];
35 	char		string[1400];
36 	int			stringlength;
37 	int			i, j;
38 	gclient_t	*cl;
39 	int			numSorted, scoreFlags, accuracy, perfect;
40 
41 	// send the latest information on all clients
42 	string[0] = 0;
43 	stringlength = 0;
44 	scoreFlags = 0;
45 
46 	numSorted = level.numConnectedClients;
47 
48 	for (i=0 ; i < numSorted ; i++) {
49 		int		ping;
50 
51 		cl = &level.clients[level.sortedClients[i]];
52 
53 		if ( cl->pers.connected == CON_CONNECTING ) {
54 			ping = -1;
55 		} else {
56 			ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
57 		}
58 
59 		if( cl->accuracy_shots ) {
60 			accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots;
61 		}
62 		else {
63 			accuracy = 0;
64 		}
65 		perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;
66 
67 		Com_sprintf (entry, sizeof(entry),
68 			" %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i],
69 			cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000,
70 			scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy,
71 			cl->ps.persistant[PERS_IMPRESSIVE_COUNT],
72 			cl->ps.persistant[PERS_EXCELLENT_COUNT],
73 			cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT],
74 			cl->ps.persistant[PERS_DEFEND_COUNT],
75 			cl->ps.persistant[PERS_ASSIST_COUNT],
76 			perfect,
77 			cl->ps.persistant[PERS_CAPTURES]);
78 		j = strlen(entry);
79 		if (stringlength + j > 1024)
80 			break;
81 		strcpy (string + stringlength, entry);
82 		stringlength += j;
83 	}
84 
85 	trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i,
86 		level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE],
87 		string ) );
88 }
89 
90 
91 /*
92 ==================
93 Cmd_Score_f
94 
95 Request current scoreboard information
96 ==================
97 */
Cmd_Score_f(gentity_t * ent)98 void Cmd_Score_f( gentity_t *ent ) {
99 	DeathmatchScoreboardMessage( ent );
100 }
101 
102 
103 
104 /*
105 ==================
106 CheatsOk
107 ==================
108 */
CheatsOk(gentity_t * ent)109 qboolean	CheatsOk( gentity_t *ent ) {
110 	if ( !g_cheats.integer ) {
111 		trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
112 		return qfalse;
113 	}
114 	if ( ent->health <= 0 ) {
115 		trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\""));
116 		return qfalse;
117 	}
118 	return qtrue;
119 }
120 
121 
122 /*
123 ==================
124 ConcatArgs
125 ==================
126 */
ConcatArgs(int start)127 char	*ConcatArgs( int start ) {
128 	int		i, c, tlen;
129 	static char	line[MAX_STRING_CHARS];
130 	int		len;
131 	char	arg[MAX_STRING_CHARS];
132 
133 	len = 0;
134 	c = trap_Argc();
135 	for ( i = start ; i < c ; i++ ) {
136 		trap_Argv( i, arg, sizeof( arg ) );
137 		tlen = strlen( arg );
138 		if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
139 			break;
140 		}
141 		memcpy( line + len, arg, tlen );
142 		len += tlen;
143 		if ( i != c - 1 ) {
144 			line[len] = ' ';
145 			len++;
146 		}
147 	}
148 
149 	line[len] = 0;
150 
151 	return line;
152 }
153 
154 /*
155 ==================
156 SanitizeString
157 
158 Remove case and control characters
159 ==================
160 */
SanitizeString(char * in,char * out)161 void SanitizeString( char *in, char *out ) {
162 	while ( *in ) {
163 		if ( *in == 27 ) {
164 			in += 2;		// skip color code
165 			continue;
166 		}
167 		if ( *in < 32 ) {
168 			in++;
169 			continue;
170 		}
171 		*out++ = tolower( *in++ );
172 	}
173 
174 	*out = 0;
175 }
176 
177 /*
178 ==================
179 ClientNumberFromString
180 
181 Returns a player number for either a number or name string
182 Returns -1 if invalid
183 ==================
184 */
ClientNumberFromString(gentity_t * to,char * s)185 int ClientNumberFromString( gentity_t *to, char *s ) {
186 	gclient_t	*cl;
187 	int			idnum;
188 	char		s2[MAX_STRING_CHARS];
189 	char		n2[MAX_STRING_CHARS];
190 
191 	// numeric values are just slot numbers
192 	if (s[0] >= '0' && s[0] <= '9') {
193 		idnum = atoi( s );
194 		if ( idnum < 0 || idnum >= level.maxclients ) {
195 			trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum));
196 			return -1;
197 		}
198 
199 		cl = &level.clients[idnum];
200 		if ( cl->pers.connected != CON_CONNECTED ) {
201 			trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum));
202 			return -1;
203 		}
204 		return idnum;
205 	}
206 
207 	// check for a name match
208 	SanitizeString( s, s2 );
209 	for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) {
210 		if ( cl->pers.connected != CON_CONNECTED ) {
211 			continue;
212 		}
213 		SanitizeString( cl->pers.netname, n2 );
214 		if ( !strcmp( n2, s2 ) ) {
215 			return idnum;
216 		}
217 	}
218 
219 	trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s));
220 	return -1;
221 }
222 
223 /*
224 ==================
225 Cmd_Give_f
226 
227 Give items to a client
228 ==================
229 */
Cmd_Give_f(gentity_t * ent)230 void Cmd_Give_f (gentity_t *ent)
231 {
232 	char		*name;
233 	gitem_t		*it;
234 	int			i;
235 	qboolean	give_all;
236 	gentity_t		*it_ent;
237 	trace_t		trace;
238 
239 	if ( !CheatsOk( ent ) ) {
240 		return;
241 	}
242 
243 	name = ConcatArgs( 1 );
244 
245 	if (Q_stricmp(name, "all") == 0)
246 		give_all = qtrue;
247 	else
248 		give_all = qfalse;
249 
250 	if (give_all || Q_stricmp( name, "health") == 0)
251 	{
252 		ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
253 		if (!give_all)
254 			return;
255 	}
256 
257 	if (give_all || Q_stricmp(name, "weapons") == 0)
258 	{
259 		ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 -
260 			( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE );
261 		if (!give_all)
262 			return;
263 	}
264 
265 	if (give_all || Q_stricmp(name, "ammo") == 0)
266 	{
267 		for ( i = 0 ; i < MAX_WEAPONS ; i++ ) {
268 			ent->client->ps.ammo[i] = 999;
269 		}
270 		if (!give_all)
271 			return;
272 	}
273 
274 	if (give_all || Q_stricmp(name, "armor") == 0)
275 	{
276 		ent->client->ps.stats[STAT_ARMOR] = 200;
277 
278 		if (!give_all)
279 			return;
280 	}
281 
282 	if (Q_stricmp(name, "excellent") == 0) {
283 		ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
284 		return;
285 	}
286 	if (Q_stricmp(name, "impressive") == 0) {
287 		ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
288 		return;
289 	}
290 	if (Q_stricmp(name, "gauntletaward") == 0) {
291 		ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
292 		return;
293 	}
294 	if (Q_stricmp(name, "defend") == 0) {
295 		ent->client->ps.persistant[PERS_DEFEND_COUNT]++;
296 		return;
297 	}
298 	if (Q_stricmp(name, "assist") == 0) {
299 		ent->client->ps.persistant[PERS_ASSIST_COUNT]++;
300 		return;
301 	}
302 
303 	// spawn a specific item right on the player
304 	if ( !give_all ) {
305 		it = BG_FindItem (name);
306 		if (!it) {
307 			return;
308 		}
309 
310 		it_ent = G_Spawn();
311 		VectorCopy( ent->r.currentOrigin, it_ent->s.origin );
312 		it_ent->classname = it->classname;
313 		G_SpawnItem (it_ent, it);
314 		FinishSpawningItem(it_ent );
315 		memset( &trace, 0, sizeof( trace ) );
316 		Touch_Item (it_ent, ent, &trace);
317 		if (it_ent->inuse) {
318 			G_FreeEntity( it_ent );
319 		}
320 	}
321 }
322 
323 
324 /*
325 ==================
326 Cmd_God_f
327 
328 Sets client to godmode
329 
330 argv(0) god
331 ==================
332 */
Cmd_God_f(gentity_t * ent)333 void Cmd_God_f (gentity_t *ent)
334 {
335 	char	*msg;
336 
337 	if ( !CheatsOk( ent ) ) {
338 		return;
339 	}
340 
341 	ent->flags ^= FL_GODMODE;
342 	if (!(ent->flags & FL_GODMODE) )
343 		msg = "godmode OFF\n";
344 	else
345 		msg = "godmode ON\n";
346 
347 	trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
348 }
349 
350 
351 /*
352 ==================
353 Cmd_Notarget_f
354 
355 Sets client to notarget
356 
357 argv(0) notarget
358 ==================
359 */
Cmd_Notarget_f(gentity_t * ent)360 void Cmd_Notarget_f( gentity_t *ent ) {
361 	char	*msg;
362 
363 	if ( !CheatsOk( ent ) ) {
364 		return;
365 	}
366 
367 	ent->flags ^= FL_NOTARGET;
368 	if (!(ent->flags & FL_NOTARGET) )
369 		msg = "notarget OFF\n";
370 	else
371 		msg = "notarget ON\n";
372 
373 	trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
374 }
375 
376 
377 /*
378 ==================
379 Cmd_Noclip_f
380 
381 argv(0) noclip
382 ==================
383 */
Cmd_Noclip_f(gentity_t * ent)384 void Cmd_Noclip_f( gentity_t *ent ) {
385 	char	*msg;
386 
387 	if ( !CheatsOk( ent ) ) {
388 		return;
389 	}
390 
391 	if ( ent->client->noclip ) {
392 		msg = "noclip OFF\n";
393 	} else {
394 		msg = "noclip ON\n";
395 	}
396 	ent->client->noclip = !ent->client->noclip;
397 
398 	trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
399 }
400 
401 
402 /*
403 ==================
404 Cmd_LevelShot_f
405 
406 This is just to help generate the level pictures
407 for the menus.  It goes to the intermission immediately
408 and sends over a command to the client to resize the view,
409 hide the scoreboard, and take a special screenshot
410 ==================
411 */
Cmd_LevelShot_f(gentity_t * ent)412 void Cmd_LevelShot_f( gentity_t *ent ) {
413 	if ( !CheatsOk( ent ) ) {
414 		return;
415 	}
416 
417 	// doesn't work in single player
418 	if ( g_gametype.integer != 0 ) {
419 		trap_SendServerCommand( ent-g_entities,
420 			"print \"Must be in g_gametype 0 for levelshot\n\"" );
421 		return;
422 	}
423 
424 	BeginIntermission();
425 	trap_SendServerCommand( ent-g_entities, "clientLevelShot" );
426 }
427 
428 
429 /*
430 ==================
431 Cmd_LevelShot_f
432 
433 This is just to help generate the level pictures
434 for the menus.  It goes to the intermission immediately
435 and sends over a command to the client to resize the view,
436 hide the scoreboard, and take a special screenshot
437 ==================
438 */
Cmd_TeamTask_f(gentity_t * ent)439 void Cmd_TeamTask_f( gentity_t *ent ) {
440 	char userinfo[MAX_INFO_STRING];
441 	char		arg[MAX_TOKEN_CHARS];
442 	int task;
443 	int client = ent->client - level.clients;
444 
445 	if ( trap_Argc() != 2 ) {
446 		return;
447 	}
448 	trap_Argv( 1, arg, sizeof( arg ) );
449 	task = atoi( arg );
450 
451 	trap_GetUserinfo(client, userinfo, sizeof(userinfo));
452 	Info_SetValueForKey(userinfo, "teamtask", va("%d", task));
453 	trap_SetUserinfo(client, userinfo);
454 	ClientUserinfoChanged(client);
455 }
456 
457 
458 
459 /*
460 =================
461 Cmd_Kill_f
462 =================
463 */
Cmd_Kill_f(gentity_t * ent)464 void Cmd_Kill_f( gentity_t *ent ) {
465 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
466 		return;
467 	}
468 	if (ent->health <= 0) {
469 		return;
470 	}
471 	ent->flags &= ~FL_GODMODE;
472 	ent->client->ps.stats[STAT_HEALTH] = ent->health = -999;
473 	player_die (ent, ent, ent, 100000, MOD_SUICIDE);
474 }
475 
476 /*
477 =================
478 BroadCastTeamChange
479 
480 Let everyone know about a team change
481 =================
482 */
BroadcastTeamChange(gclient_t * client,int oldTeam)483 void BroadcastTeamChange( gclient_t *client, int oldTeam )
484 {
485 	if ( client->sess.sessionTeam == TEAM_RED ) {
486 		trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"",
487 			client->pers.netname) );
488 	} else if ( client->sess.sessionTeam == TEAM_BLUE ) {
489 		trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"",
490 		client->pers.netname));
491 	} else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) {
492 		trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"",
493 		client->pers.netname));
494 	} else if ( client->sess.sessionTeam == TEAM_FREE ) {
495 		trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"",
496 		client->pers.netname));
497 	}
498 }
499 
500 /*
501 =================
502 SetTeam
503 =================
504 */
SetTeam(gentity_t * ent,char * s)505 void SetTeam( gentity_t *ent, char *s ) {
506 	int					team, oldTeam;
507 	gclient_t			*client;
508 	int					clientNum;
509 	spectatorState_t	specState;
510 	int					specClient;
511 	int					teamLeader;
512 
513 	//
514 	// see what change is requested
515 	//
516 	client = ent->client;
517 
518 	clientNum = client - level.clients;
519 	specClient = 0;
520 	specState = SPECTATOR_NOT;
521 	if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
522 		team = TEAM_SPECTATOR;
523 		specState = SPECTATOR_SCOREBOARD;
524 	} else if ( !Q_stricmp( s, "follow1" ) ) {
525 		team = TEAM_SPECTATOR;
526 		specState = SPECTATOR_FOLLOW;
527 		specClient = -1;
528 	} else if ( !Q_stricmp( s, "follow2" ) ) {
529 		team = TEAM_SPECTATOR;
530 		specState = SPECTATOR_FOLLOW;
531 		specClient = -2;
532 	} else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) {
533 		team = TEAM_SPECTATOR;
534 		specState = SPECTATOR_FREE;
535 	} else if ( g_gametype.integer >= GT_TEAM ) {
536 		// if running a team game, assign player to one of the teams
537 		specState = SPECTATOR_NOT;
538 		if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
539 			team = TEAM_RED;
540 		} else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
541 			team = TEAM_BLUE;
542 		} else {
543 			// pick the team with the least number of players
544 			team = PickTeam( clientNum );
545 		}
546 
547 		if ( g_teamForceBalance.integer  ) {
548 			int		counts[TEAM_NUM_TEAMS];
549 
550 			counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE );
551 			counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED );
552 
553 			// We allow a spread of two
554 			if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) {
555 				trap_SendServerCommand( ent->client->ps.clientNum,
556 					"cp \"Red team has too many players.\n\"" );
557 				return; // ignore the request
558 			}
559 			if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
560 				trap_SendServerCommand( ent->client->ps.clientNum,
561 					"cp \"Blue team has too many players.\n\"" );
562 				return; // ignore the request
563 			}
564 
565 			// It's ok, the team we are switching to has less or same number of players
566 		}
567 
568 	} else {
569 		// force them to spectators if there aren't any spots free
570 		team = TEAM_FREE;
571 	}
572 
573 	// override decision if limiting the players
574 	if ( (g_gametype.integer == GT_TOURNAMENT)
575 		&& level.numNonSpectatorClients >= 2 ) {
576 		team = TEAM_SPECTATOR;
577 	} else if ( g_maxGameClients.integer > 0 &&
578 		level.numNonSpectatorClients >= g_maxGameClients.integer ) {
579 		team = TEAM_SPECTATOR;
580 	}
581 
582 	//
583 	// decide if we will allow the change
584 	//
585 	oldTeam = client->sess.sessionTeam;
586 	if ( team == oldTeam && team != TEAM_SPECTATOR ) {
587 		return;
588 	}
589 
590 	//
591 	// execute the team change
592 	//
593 
594 	// if the player was dead leave the body
595 	if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
596 		CopyToBodyQue(ent);
597 	}
598 
599 	// he starts at 'base'
600 	client->pers.teamState.state = TEAM_BEGIN;
601 	if ( oldTeam != TEAM_SPECTATOR ) {
602 		// Kill him (makes sure he loses flags, etc)
603 		ent->flags &= ~FL_GODMODE;
604 		ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
605 		player_die (ent, ent, ent, 100000, MOD_SUICIDE);
606 
607 	}
608 	// they go to the end of the line for tournements
609 	if ( team == TEAM_SPECTATOR ) {
610 		client->sess.spectatorTime = level.time;
611 	}
612 
613 	client->sess.sessionTeam = team;
614 	client->sess.spectatorState = specState;
615 	client->sess.spectatorClient = specClient;
616 
617 	client->sess.teamLeader = qfalse;
618 	if ( team == TEAM_RED || team == TEAM_BLUE ) {
619 		teamLeader = TeamLeader( team );
620 		// if there is no team leader or the team leader is a bot and this client is not a bot
621 		if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) {
622 			SetLeader( team, clientNum );
623 		}
624 	}
625 	// make sure there is a team leader on the team the player came from
626 	if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) {
627 		CheckTeamLeader( oldTeam );
628 	}
629 
630 	BroadcastTeamChange( client, oldTeam );
631 
632 	// get and distribute relevent paramters
633 	ClientUserinfoChanged( clientNum );
634 
635 	ClientBegin( clientNum );
636 }
637 
638 /*
639 =================
640 StopFollowing
641 
642 If the client being followed leaves the game, or you just want to drop
643 to free floating spectator mode
644 =================
645 */
StopFollowing(gentity_t * ent)646 void StopFollowing( gentity_t *ent ) {
647 	ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
648 	ent->client->sess.sessionTeam = TEAM_SPECTATOR;
649 	ent->client->sess.spectatorState = SPECTATOR_FREE;
650 	ent->client->ps.pm_flags &= ~PMF_FOLLOW;
651 	ent->r.svFlags &= ~SVF_BOT;
652 	ent->client->ps.clientNum = ent - g_entities;
653 }
654 
655 /*
656 =================
657 Cmd_Team_f
658 =================
659 */
Cmd_Team_f(gentity_t * ent)660 void Cmd_Team_f( gentity_t *ent ) {
661 	int			oldTeam;
662 	char		s[MAX_TOKEN_CHARS];
663 
664 	if ( trap_Argc() != 2 ) {
665 		oldTeam = ent->client->sess.sessionTeam;
666 		switch ( oldTeam ) {
667 		case TEAM_BLUE:
668 			trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" );
669 			break;
670 		case TEAM_RED:
671 			trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" );
672 			break;
673 		case TEAM_FREE:
674 			trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" );
675 			break;
676 		case TEAM_SPECTATOR:
677 			trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" );
678 			break;
679 		}
680 		return;
681 	}
682 
683 	if ( ent->client->switchTeamTime > level.time ) {
684 		trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" );
685 		return;
686 	}
687 
688 	// if they are playing a tournement game, count as a loss
689 	if ( (g_gametype.integer == GT_TOURNAMENT )
690 		&& ent->client->sess.sessionTeam == TEAM_FREE ) {
691 		ent->client->sess.losses++;
692 	}
693 
694 	trap_Argv( 1, s, sizeof( s ) );
695 
696 	SetTeam( ent, s );
697 
698 	ent->client->switchTeamTime = level.time + 5000;
699 }
700 
701 
702 /*
703 =================
704 Cmd_Follow_f
705 =================
706 */
Cmd_Follow_f(gentity_t * ent)707 void Cmd_Follow_f( gentity_t *ent ) {
708 	int		i;
709 	char	arg[MAX_TOKEN_CHARS];
710 
711 	if ( trap_Argc() != 2 ) {
712 		if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
713 			StopFollowing( ent );
714 		}
715 		return;
716 	}
717 
718 	trap_Argv( 1, arg, sizeof( arg ) );
719 	i = ClientNumberFromString( ent, arg );
720 	if ( i == -1 ) {
721 		return;
722 	}
723 
724 	// can't follow self
725 	if ( &level.clients[ i ] == ent->client ) {
726 		return;
727 	}
728 
729 	// can't follow another spectator
730 	if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) {
731 		return;
732 	}
733 
734 	// if they are playing a tournement game, count as a loss
735 	if ( (g_gametype.integer == GT_TOURNAMENT )
736 		&& ent->client->sess.sessionTeam == TEAM_FREE ) {
737 		ent->client->sess.losses++;
738 	}
739 
740 	// first set them to spectator
741 	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
742 		SetTeam( ent, "spectator" );
743 	}
744 
745 	ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
746 	ent->client->sess.spectatorClient = i;
747 }
748 
749 /*
750 =================
751 Cmd_FollowCycle_f
752 =================
753 */
Cmd_FollowCycle_f(gentity_t * ent,int dir)754 void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
755 	int		clientnum;
756 	int		original;
757 
758 	// if they are playing a tournement game, count as a loss
759 	if ( (g_gametype.integer == GT_TOURNAMENT )
760 		&& ent->client->sess.sessionTeam == TEAM_FREE ) {
761 		ent->client->sess.losses++;
762 	}
763 	// first set them to spectator
764 	if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) {
765 		SetTeam( ent, "spectator" );
766 	}
767 
768 	if ( dir != 1 && dir != -1 ) {
769 		G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
770 	}
771 
772 	clientnum = ent->client->sess.spectatorClient;
773 	original = clientnum;
774 	do {
775 		clientnum += dir;
776 		if ( clientnum >= level.maxclients ) {
777 			clientnum = 0;
778 		}
779 		if ( clientnum < 0 ) {
780 			clientnum = level.maxclients - 1;
781 		}
782 
783 		// can only follow connected clients
784 		if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) {
785 			continue;
786 		}
787 
788 		// can't follow another spectator
789 		if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) {
790 			continue;
791 		}
792 
793 		// this is good, we can use it
794 		ent->client->sess.spectatorClient = clientnum;
795 		ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
796 		return;
797 	} while ( clientnum != original );
798 
799 	// leave it where it was
800 }
801 
802 
803 /*
804 ==================
805 G_Say
806 ==================
807 */
808 
G_SayTo(gentity_t * ent,gentity_t * other,int mode,int color,const char * name,const char * message)809 static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) {
810 	if (!other) {
811 		return;
812 	}
813 	if (!other->inuse) {
814 		return;
815 	}
816 	if (!other->client) {
817 		return;
818 	}
819 	if ( other->client->pers.connected != CON_CONNECTED ) {
820 		return;
821 	}
822 	if ( mode == SAY_TEAM  && !OnSameTeam(ent, other) ) {
823 		return;
824 	}
825 	// no chatting to players in tournements
826 	if ( (g_gametype.integer == GT_TOURNAMENT )
827 		&& other->client->sess.sessionTeam == TEAM_FREE
828 		&& ent->client->sess.sessionTeam != TEAM_FREE ) {
829 		return;
830 	}
831 
832 	trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"",
833 		mode == SAY_TEAM ? "tchat" : "chat",
834 		name, Q_COLOR_ESCAPE, color, message));
835 }
836 
837 #define EC		"\x19"
838 
G_Say(gentity_t * ent,gentity_t * target,int mode,const char * chatText)839 void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) {
840 	int			j;
841 	gentity_t	*other;
842 	int			color;
843 	char		name[64];
844 	// don't let text be too long for malicious reasons
845 	char		text[MAX_SAY_TEXT];
846 	char		location[64];
847 
848 	if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
849 		mode = SAY_ALL;
850 	}
851 
852 	switch ( mode ) {
853 	default:
854 	case SAY_ALL:
855 		G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
856 		Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
857 		color = COLOR_GREEN;
858 		break;
859 	case SAY_TEAM:
860 		G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
861 		if (Team_GetLocationMsg(ent, location, sizeof(location)))
862 			Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC") (%s)"EC": ",
863 				ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location);
864 		else
865 			Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ",
866 				ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
867 		color = COLOR_CYAN;
868 		break;
869 	case SAY_TELL:
870 		if (target && g_gametype.integer >= GT_TEAM &&
871 			target->client->sess.sessionTeam == ent->client->sess.sessionTeam &&
872 			Team_GetLocationMsg(ent, location, sizeof(location)))
873 			Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"] (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location );
874 		else
875 			Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
876 		color = COLOR_MAGENTA;
877 		break;
878 	}
879 
880 	Q_strncpyz( text, chatText, sizeof(text) );
881 
882 	if ( target ) {
883 		G_SayTo( ent, target, mode, color, name, text );
884 		return;
885 	}
886 
887 	// echo the text to the console
888 	if ( g_dedicated.integer ) {
889 		G_Printf( "%s%s\n", name, text);
890 	}
891 
892 	// send it to all the apropriate clients
893 	for (j = 0; j < level.maxclients; j++) {
894 		other = &g_entities[j];
895 		G_SayTo( ent, other, mode, color, name, text );
896 	}
897 }
898 
899 
900 /*
901 ==================
902 Cmd_Say_f
903 ==================
904 */
Cmd_Say_f(gentity_t * ent,int mode,qboolean arg0)905 static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
906 	char		*p;
907 
908 	if ( trap_Argc () < 2 && !arg0 ) {
909 		return;
910 	}
911 
912 	if (arg0)
913 	{
914 		p = ConcatArgs( 0 );
915 	}
916 	else
917 	{
918 		p = ConcatArgs( 1 );
919 	}
920 
921 	G_Say( ent, NULL, mode, p );
922 }
923 
924 /*
925 ==================
926 Cmd_Tell_f
927 ==================
928 */
Cmd_Tell_f(gentity_t * ent)929 static void Cmd_Tell_f( gentity_t *ent ) {
930 	int			targetNum;
931 	gentity_t	*target;
932 	char		*p;
933 	char		arg[MAX_TOKEN_CHARS];
934 
935 	if ( trap_Argc () < 2 ) {
936 		return;
937 	}
938 
939 	trap_Argv( 1, arg, sizeof( arg ) );
940 	targetNum = atoi( arg );
941 	if ( targetNum < 0 || targetNum >= level.maxclients ) {
942 		return;
943 	}
944 
945 	target = &g_entities[targetNum];
946 	if ( !target || !target->inuse || !target->client ) {
947 		return;
948 	}
949 
950 	p = ConcatArgs( 2 );
951 
952 	G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
953 	G_Say( ent, target, SAY_TELL, p );
954 	// don't tell to the player self if it was already directed to this player
955 	// also don't send the chat back to a bot
956 	if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
957 		G_Say( ent, ent, SAY_TELL, p );
958 	}
959 }
960 
961 
G_VoiceTo(gentity_t * ent,gentity_t * other,int mode,const char * id,qboolean voiceonly)962 static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) {
963 	int color;
964 	char *cmd;
965 
966 	if (!other) {
967 		return;
968 	}
969 	if (!other->inuse) {
970 		return;
971 	}
972 	if (!other->client) {
973 		return;
974 	}
975 	if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) {
976 		return;
977 	}
978 	// no chatting to players in tournements
979 	if ( (g_gametype.integer == GT_TOURNAMENT )) {
980 		return;
981 	}
982 
983 	if (mode == SAY_TEAM) {
984 		color = COLOR_CYAN;
985 		cmd = "vtchat";
986 	}
987 	else if (mode == SAY_TELL) {
988 		color = COLOR_MAGENTA;
989 		cmd = "vtell";
990 	}
991 	else {
992 		color = COLOR_GREEN;
993 		cmd = "vchat";
994 	}
995 
996 	trap_SendServerCommand( other-g_entities, va("%s %d %d %d %s", cmd, voiceonly, ent->s.number, color, id));
997 }
998 
G_Voice(gentity_t * ent,gentity_t * target,int mode,const char * id,qboolean voiceonly)999 void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) {
1000 	int			j;
1001 	gentity_t	*other;
1002 
1003 	if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
1004 		mode = SAY_ALL;
1005 	}
1006 
1007 	if ( target ) {
1008 		G_VoiceTo( ent, target, mode, id, voiceonly );
1009 		return;
1010 	}
1011 
1012 	// echo the text to the console
1013 	if ( g_dedicated.integer ) {
1014 		G_Printf( "voice: %s %s\n", ent->client->pers.netname, id);
1015 	}
1016 
1017 	// send it to all the apropriate clients
1018 	for (j = 0; j < level.maxclients; j++) {
1019 		other = &g_entities[j];
1020 		G_VoiceTo( ent, other, mode, id, voiceonly );
1021 	}
1022 }
1023 
1024 /*
1025 ==================
1026 Cmd_Voice_f
1027 ==================
1028 */
Cmd_Voice_f(gentity_t * ent,int mode,qboolean arg0,qboolean voiceonly)1029 static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) {
1030 	char		*p;
1031 
1032 	if ( trap_Argc () < 2 && !arg0 ) {
1033 		return;
1034 	}
1035 
1036 	if (arg0)
1037 	{
1038 		p = ConcatArgs( 0 );
1039 	}
1040 	else
1041 	{
1042 		p = ConcatArgs( 1 );
1043 	}
1044 
1045 	G_Voice( ent, NULL, mode, p, voiceonly );
1046 }
1047 
1048 /*
1049 ==================
1050 Cmd_VoiceTell_f
1051 ==================
1052 */
Cmd_VoiceTell_f(gentity_t * ent,qboolean voiceonly)1053 static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) {
1054 	int			targetNum;
1055 	gentity_t	*target;
1056 	char		*id;
1057 	char		arg[MAX_TOKEN_CHARS];
1058 
1059 	if ( trap_Argc () < 2 ) {
1060 		return;
1061 	}
1062 
1063 	trap_Argv( 1, arg, sizeof( arg ) );
1064 	targetNum = atoi( arg );
1065 	if ( targetNum < 0 || targetNum >= level.maxclients ) {
1066 		return;
1067 	}
1068 
1069 	target = &g_entities[targetNum];
1070 	if ( !target || !target->inuse || !target->client ) {
1071 		return;
1072 	}
1073 
1074 	id = ConcatArgs( 2 );
1075 
1076 	G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id );
1077 	G_Voice( ent, target, SAY_TELL, id, voiceonly );
1078 	// don't tell to the player self if it was already directed to this player
1079 	// also don't send the chat back to a bot
1080 	if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
1081 		G_Voice( ent, ent, SAY_TELL, id, voiceonly );
1082 	}
1083 }
1084 
1085 
1086 /*
1087 ==================
1088 Cmd_VoiceTaunt_f
1089 ==================
1090 */
Cmd_VoiceTaunt_f(gentity_t * ent)1091 static void Cmd_VoiceTaunt_f( gentity_t *ent ) {
1092 	gentity_t *who;
1093 	int i;
1094 
1095 	if (!ent->client) {
1096 		return;
1097 	}
1098 
1099 	// insult someone who just killed you
1100 	if (ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number) {
1101 		// i am a dead corpse
1102 		if (!(ent->enemy->r.svFlags & SVF_BOT)) {
1103 			G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
1104 		}
1105 		if (!(ent->r.svFlags & SVF_BOT)) {
1106 			G_Voice( ent, ent,        SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
1107 		}
1108 		ent->enemy = NULL;
1109 		return;
1110 	}
1111 	// insult someone you just killed
1112 	if (ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number) {
1113 		who = g_entities + ent->client->lastkilled_client;
1114 		if (who->client) {
1115 			// who is the person I just killed
1116 			if (who->client->lasthurt_mod == MOD_GAUNTLET) {
1117 				if (!(who->r.svFlags & SVF_BOT)) {
1118 					G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );	// and I killed them with a gauntlet
1119 				}
1120 				if (!(ent->r.svFlags & SVF_BOT)) {
1121 					G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );
1122 				}
1123 			} else {
1124 				if (!(who->r.svFlags & SVF_BOT)) {
1125 					G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );	// and I killed them with something else
1126 				}
1127 				if (!(ent->r.svFlags & SVF_BOT)) {
1128 					G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );
1129 				}
1130 			}
1131 			ent->client->lastkilled_client = -1;
1132 			return;
1133 		}
1134 	}
1135 
1136 	if (g_gametype.integer >= GT_TEAM) {
1137 		// praise a team mate who just got a reward
1138 		for(i = 0; i < MAX_CLIENTS; i++) {
1139 			who = g_entities + i;
1140 			if (who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam) {
1141 				if (who->client->rewardTime > level.time) {
1142 					if (!(who->r.svFlags & SVF_BOT)) {
1143 						G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse );
1144 					}
1145 					if (!(ent->r.svFlags & SVF_BOT)) {
1146 						G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse );
1147 					}
1148 					return;
1149 				}
1150 			}
1151 		}
1152 	}
1153 
1154 	// just say something
1155 	G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse );
1156 }
1157 
1158 
1159 
1160 static char	*gc_orders[] = {
1161 	"hold your position",
1162 	"hold this position",
1163 	"come here",
1164 	"cover me",
1165 	"guard location",
1166 	"search and destroy",
1167 	"report"
1168 };
1169 
Cmd_GameCommand_f(gentity_t * ent)1170 void Cmd_GameCommand_f( gentity_t *ent ) {
1171 	int		player;
1172 	int		order;
1173 	char	str[MAX_TOKEN_CHARS];
1174 
1175 	trap_Argv( 1, str, sizeof( str ) );
1176 	player = atoi( str );
1177 	trap_Argv( 2, str, sizeof( str ) );
1178 	order = atoi( str );
1179 
1180 	if ( player < 0 || player >= MAX_CLIENTS ) {
1181 		return;
1182 	}
1183 	if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) {
1184 		return;
1185 	}
1186 	G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] );
1187 	G_Say( ent, ent, SAY_TELL, gc_orders[order] );
1188 }
1189 
1190 /*
1191 ==================
1192 Cmd_Where_f
1193 ==================
1194 */
Cmd_Where_f(gentity_t * ent)1195 void Cmd_Where_f( gentity_t *ent ) {
1196 	trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
1197 }
1198 
1199 static const char *gameNames[] = {
1200 	"Free For All",
1201 	"Tournament",
1202 	"Single Player",
1203 	"Team Deathmatch",
1204 	"Capture the Flag",
1205 	"One Flag CTF",
1206 	"Overload",
1207 	"Harvester"
1208 };
1209 
1210 /*
1211 ==================
1212 Cmd_CallVote_f
1213 ==================
1214 */
Cmd_CallVote_f(gentity_t * ent)1215 void Cmd_CallVote_f( gentity_t *ent ) {
1216 	int		i;
1217 	char	arg1[MAX_STRING_TOKENS];
1218 	char	arg2[MAX_STRING_TOKENS];
1219 
1220 	if ( !g_allowVote.integer ) {
1221 		trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
1222 		return;
1223 	}
1224 
1225 	if ( level.voteTime ) {
1226 		trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" );
1227 		return;
1228 	}
1229 	if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) {
1230 		trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" );
1231 		return;
1232 	}
1233 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1234 		trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
1235 		return;
1236 	}
1237 
1238 	// make sure it is a valid command to vote on
1239 	trap_Argv( 1, arg1, sizeof( arg1 ) );
1240 	trap_Argv( 2, arg2, sizeof( arg2 ) );
1241 
1242 	if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
1243 		trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
1244 		return;
1245 	}
1246 
1247 	if ( !Q_stricmp( arg1, "map_restart" ) ) {
1248 	} else if ( !Q_stricmp( arg1, "nextmap" ) ) {
1249 	} else if ( !Q_stricmp( arg1, "map" ) ) {
1250 	} else if ( !Q_stricmp( arg1, "g_gametype" ) ) {
1251 	} else if ( !Q_stricmp( arg1, "kick" ) ) {
1252 	} else if ( !Q_stricmp( arg1, "clientkick" ) ) {
1253 	} else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {
1254 	} else if ( !Q_stricmp( arg1, "timelimit" ) ) {
1255 	} else if ( !Q_stricmp( arg1, "fraglimit" ) ) {
1256 	} else {
1257 		trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
1258 		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\"" );
1259 		return;
1260 	}
1261 
1262 	// if there is still a vote to be executed
1263 	if ( level.voteExecuteTime ) {
1264 		level.voteExecuteTime = 0;
1265 		trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
1266 	}
1267 
1268 	// special case for g_gametype, check for bad values
1269 	if ( !Q_stricmp( arg1, "g_gametype" ) ) {
1270 		i = atoi( arg2 );
1271 		if( i == GT_SINGLE_PLAYER || i < GT_FFA || i >= GT_MAX_GAME_TYPE) {
1272 			trap_SendServerCommand( ent-g_entities, "print \"Invalid gametype.\n\"" );
1273 			return;
1274 		}
1275 
1276 		Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %d", arg1, i );
1277 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, gameNames[i] );
1278 	} else if ( !Q_stricmp( arg1, "map" ) ) {
1279 		// special case for map changes, we want to reset the nextmap setting
1280 		// this allows a player to change maps, but not upset the map rotation
1281 		char	s[MAX_STRING_CHARS];
1282 
1283 		trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
1284 		if (*s) {
1285 			Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s );
1286 		} else {
1287 			Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
1288 		}
1289 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
1290 	} else if ( !Q_stricmp( arg1, "nextmap" ) ) {
1291 		char	s[MAX_STRING_CHARS];
1292 
1293 		trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
1294 		if (!*s) {
1295 			trap_SendServerCommand( ent-g_entities, "print \"nextmap not set.\n\"" );
1296 			return;
1297 		}
1298 		Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap");
1299 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
1300 	} else {
1301 		Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
1302 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
1303 	}
1304 
1305 	trap_SendServerCommand( -1, va("print \"%s called a vote.\n\"", ent->client->pers.netname ) );
1306 
1307 	// start the voting, the caller autoamtically votes yes
1308 	level.voteTime = level.time;
1309 	level.voteYes = 1;
1310 	level.voteNo = 0;
1311 
1312 	for ( i = 0 ; i < level.maxclients ; i++ ) {
1313 		level.clients[i].ps.eFlags &= ~EF_VOTED;
1314 	}
1315 	ent->client->ps.eFlags |= EF_VOTED;
1316 
1317 	trap_SetConfigstring( CS_VOTE_TIME, va("%i", level.voteTime ) );
1318 	trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString );
1319 	trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
1320 	trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );
1321 }
1322 
1323 /*
1324 ==================
1325 Cmd_Vote_f
1326 ==================
1327 */
Cmd_Vote_f(gentity_t * ent)1328 void Cmd_Vote_f( gentity_t *ent ) {
1329 	char		msg[64];
1330 
1331 	if ( !level.voteTime ) {
1332 		trap_SendServerCommand( ent-g_entities, "print \"No vote in progress.\n\"" );
1333 		return;
1334 	}
1335 	if ( ent->client->ps.eFlags & EF_VOTED ) {
1336 		trap_SendServerCommand( ent-g_entities, "print \"Vote already cast.\n\"" );
1337 		return;
1338 	}
1339 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1340 		trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
1341 		return;
1342 	}
1343 
1344 	trap_SendServerCommand( ent-g_entities, "print \"Vote cast.\n\"" );
1345 
1346 	ent->client->ps.eFlags |= EF_VOTED;
1347 
1348 	trap_Argv( 1, msg, sizeof( msg ) );
1349 
1350 	if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
1351 		level.voteYes++;
1352 		trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
1353 	} else {
1354 		level.voteNo++;
1355 		trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );
1356 	}
1357 
1358 	// a majority will be determined in CheckVote, which will also account
1359 	// for players entering or leaving
1360 }
1361 
1362 /*
1363 ==================
1364 Cmd_CallTeamVote_f
1365 ==================
1366 */
Cmd_CallTeamVote_f(gentity_t * ent)1367 void Cmd_CallTeamVote_f( gentity_t *ent ) {
1368 	int		i, team, cs_offset;
1369 	char	arg1[MAX_STRING_TOKENS];
1370 	char	arg2[MAX_STRING_TOKENS];
1371 
1372 	team = ent->client->sess.sessionTeam;
1373 	if ( team == TEAM_RED )
1374 		cs_offset = 0;
1375 	else if ( team == TEAM_BLUE )
1376 		cs_offset = 1;
1377 	else
1378 		return;
1379 
1380 	if ( !g_allowVote.integer ) {
1381 		trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
1382 		return;
1383 	}
1384 
1385 	if ( level.teamVoteTime[cs_offset] ) {
1386 		trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress.\n\"" );
1387 		return;
1388 	}
1389 	if ( ent->client->pers.teamVoteCount >= MAX_VOTE_COUNT ) {
1390 		trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of team votes.\n\"" );
1391 		return;
1392 	}
1393 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1394 		trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
1395 		return;
1396 	}
1397 
1398 	// make sure it is a valid command to vote on
1399 	trap_Argv( 1, arg1, sizeof( arg1 ) );
1400 	arg2[0] = '\0';
1401 	for ( i = 2; i < trap_Argc(); i++ ) {
1402 		if (i > 2)
1403 			strcat(arg2, " ");
1404 		trap_Argv( i, &arg2[strlen(arg2)], sizeof( arg2 ) - strlen(arg2) );
1405 	}
1406 
1407 	if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
1408 		trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
1409 		return;
1410 	}
1411 
1412 	if ( !Q_stricmp( arg1, "leader" ) ) {
1413 		char netname[MAX_NETNAME], leader[MAX_NETNAME];
1414 
1415 		if ( !arg2[0] ) {
1416 			i = ent->client->ps.clientNum;
1417 		}
1418 		else {
1419 			// numeric values are just slot numbers
1420 			for (i = 0; i < 3; i++) {
1421 				if ( !arg2[i] || arg2[i] < '0' || arg2[i] > '9' )
1422 					break;
1423 			}
1424 			if ( i >= 3 || !arg2[i]) {
1425 				i = atoi( arg2 );
1426 				if ( i < 0 || i >= level.maxclients ) {
1427 					trap_SendServerCommand( ent-g_entities, va("print \"Bad client slot: %i\n\"", i) );
1428 					return;
1429 				}
1430 
1431 				if ( !g_entities[i].inuse ) {
1432 					trap_SendServerCommand( ent-g_entities, va("print \"Client %i is not active\n\"", i) );
1433 					return;
1434 				}
1435 			}
1436 			else {
1437 				Q_strncpyz(leader, arg2, sizeof(leader));
1438 				Q_CleanStr(leader);
1439 				for ( i = 0 ; i < level.maxclients ; i++ ) {
1440 					if ( level.clients[i].pers.connected == CON_DISCONNECTED )
1441 						continue;
1442 					if (level.clients[i].sess.sessionTeam != team)
1443 						continue;
1444 					Q_strncpyz(netname, level.clients[i].pers.netname, sizeof(netname));
1445 					Q_CleanStr(netname);
1446 					if ( !Q_stricmp(netname, leader) ) {
1447 						break;
1448 					}
1449 				}
1450 				if ( i >= level.maxclients ) {
1451 					trap_SendServerCommand( ent-g_entities, va("print \"%s is not a valid player on your team.\n\"", arg2) );
1452 					return;
1453 				}
1454 			}
1455 		}
1456 		Com_sprintf(arg2, sizeof(arg2), "%d", i);
1457 	} else {
1458 		trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
1459 		trap_SendServerCommand( ent-g_entities, "print \"Team vote commands are: leader <player>.\n\"" );
1460 		return;
1461 	}
1462 
1463 	Com_sprintf( level.teamVoteString[cs_offset], sizeof( level.teamVoteString[cs_offset] ), "%s %s", arg1, arg2 );
1464 
1465 	for ( i = 0 ; i < level.maxclients ; i++ ) {
1466 		if ( level.clients[i].pers.connected == CON_DISCONNECTED )
1467 			continue;
1468 		if (level.clients[i].sess.sessionTeam == team)
1469 			trap_SendServerCommand( i, va("print \"%s called a team vote.\n\"", ent->client->pers.netname ) );
1470 	}
1471 
1472 	// start the voting, the caller autoamtically votes yes
1473 	level.teamVoteTime[cs_offset] = level.time;
1474 	level.teamVoteYes[cs_offset] = 1;
1475 	level.teamVoteNo[cs_offset] = 0;
1476 
1477 	for ( i = 0 ; i < level.maxclients ; i++ ) {
1478 		if (level.clients[i].sess.sessionTeam == team)
1479 			level.clients[i].ps.eFlags &= ~EF_TEAMVOTED;
1480 	}
1481 	ent->client->ps.eFlags |= EF_TEAMVOTED;
1482 
1483 	trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, va("%i", level.teamVoteTime[cs_offset] ) );
1484 	trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, level.teamVoteString[cs_offset] );
1485 	trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
1486 	trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) );
1487 }
1488 
1489 /*
1490 ==================
1491 Cmd_TeamVote_f
1492 ==================
1493 */
Cmd_TeamVote_f(gentity_t * ent)1494 void Cmd_TeamVote_f( gentity_t *ent ) {
1495 	int			team, cs_offset;
1496 	char		msg[64];
1497 
1498 	team = ent->client->sess.sessionTeam;
1499 	if ( team == TEAM_RED )
1500 		cs_offset = 0;
1501 	else if ( team == TEAM_BLUE )
1502 		cs_offset = 1;
1503 	else
1504 		return;
1505 
1506 	if ( !level.teamVoteTime[cs_offset] ) {
1507 		trap_SendServerCommand( ent-g_entities, "print \"No team vote in progress.\n\"" );
1508 		return;
1509 	}
1510 	if ( ent->client->ps.eFlags & EF_TEAMVOTED ) {
1511 		trap_SendServerCommand( ent-g_entities, "print \"Team vote already cast.\n\"" );
1512 		return;
1513 	}
1514 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1515 		trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
1516 		return;
1517 	}
1518 
1519 	trap_SendServerCommand( ent-g_entities, "print \"Team vote cast.\n\"" );
1520 
1521 	ent->client->ps.eFlags |= EF_TEAMVOTED;
1522 
1523 	trap_Argv( 1, msg, sizeof( msg ) );
1524 
1525 	if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
1526 		level.teamVoteYes[cs_offset]++;
1527 		trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
1528 	} else {
1529 		level.teamVoteNo[cs_offset]++;
1530 		trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) );
1531 	}
1532 
1533 	// a majority will be determined in TeamCheckVote, which will also account
1534 	// for players entering or leaving
1535 }
1536 
1537 
1538 /*
1539 =================
1540 Cmd_SetViewpos_f
1541 =================
1542 */
Cmd_SetViewpos_f(gentity_t * ent)1543 void Cmd_SetViewpos_f( gentity_t *ent ) {
1544 	vec3_t		origin, angles;
1545 	char		buffer[MAX_TOKEN_CHARS];
1546 	int			i;
1547 
1548 	if ( !g_cheats.integer ) {
1549 		trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
1550 		return;
1551 	}
1552 	if ( trap_Argc() != 5 ) {
1553 		trap_SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
1554 		return;
1555 	}
1556 
1557 	VectorClear( angles );
1558 	for ( i = 0 ; i < 3 ; i++ ) {
1559 		trap_Argv( i + 1, buffer, sizeof( buffer ) );
1560 		origin[i] = atof( buffer );
1561 	}
1562 
1563 	trap_Argv( 4, buffer, sizeof( buffer ) );
1564 	angles[YAW] = atof( buffer );
1565 
1566 	TeleportPlayer( ent, origin, angles );
1567 }
1568 
1569 
1570 
1571 /*
1572 =================
1573 Cmd_Stats_f
1574 =================
1575 */
Cmd_Stats_f(gentity_t * ent)1576 void Cmd_Stats_f( gentity_t *ent ) {
1577 /*
1578 	int max, n, i;
1579 
1580 	max = trap_AAS_PointReachabilityAreaIndex( NULL );
1581 
1582 	n = 0;
1583 	for ( i = 0; i < max; i++ ) {
1584 		if ( ent->client->areabits[i >> 3] & (1 << (i & 7)) )
1585 			n++;
1586 	}
1587 
1588 	//trap_SendServerCommand( ent-g_entities, va("print \"visited %d of %d areas\n\"", n, max));
1589 	trap_SendServerCommand( ent-g_entities, va("print \"%d%% level coverage\n\"", n * 100 / max));
1590 */
1591 }
1592 
1593 /*
1594 =================
1595 ClientCommand
1596 =================
1597 */
ClientCommand(int clientNum)1598 void ClientCommand( int clientNum ) {
1599 	gentity_t *ent;
1600 	char	cmd[MAX_TOKEN_CHARS];
1601 
1602 	ent = g_entities + clientNum;
1603 	if ( !ent->client ) {
1604 		return;		// not fully in game yet
1605 	}
1606 
1607 
1608 	trap_Argv( 0, cmd, sizeof( cmd ) );
1609 
1610 	if (Q_stricmp (cmd, "say") == 0) {
1611 		Cmd_Say_f (ent, SAY_ALL, qfalse);
1612 		return;
1613 	}
1614 	if (Q_stricmp (cmd, "say_team") == 0) {
1615 		Cmd_Say_f (ent, SAY_TEAM, qfalse);
1616 		return;
1617 	}
1618 	if (Q_stricmp (cmd, "tell") == 0) {
1619 		Cmd_Tell_f ( ent );
1620 		return;
1621 	}
1622 	if (Q_stricmp (cmd, "vsay") == 0) {
1623 		Cmd_Voice_f (ent, SAY_ALL, qfalse, qfalse);
1624 		return;
1625 	}
1626 	if (Q_stricmp (cmd, "vsay_team") == 0) {
1627 		Cmd_Voice_f (ent, SAY_TEAM, qfalse, qfalse);
1628 		return;
1629 	}
1630 	if (Q_stricmp (cmd, "vtell") == 0) {
1631 		Cmd_VoiceTell_f ( ent, qfalse );
1632 		return;
1633 	}
1634 	if (Q_stricmp (cmd, "vosay") == 0) {
1635 		Cmd_Voice_f (ent, SAY_ALL, qfalse, qtrue);
1636 		return;
1637 	}
1638 	if (Q_stricmp (cmd, "vosay_team") == 0) {
1639 		Cmd_Voice_f (ent, SAY_TEAM, qfalse, qtrue);
1640 		return;
1641 	}
1642 	if (Q_stricmp (cmd, "votell") == 0) {
1643 		Cmd_VoiceTell_f ( ent, qtrue );
1644 		return;
1645 	}
1646 	if (Q_stricmp (cmd, "vtaunt") == 0) {
1647 		Cmd_VoiceTaunt_f ( ent );
1648 		return;
1649 	}
1650 	if (Q_stricmp (cmd, "score") == 0) {
1651 		Cmd_Score_f (ent);
1652 		return;
1653 	}
1654 
1655 	// ignore all other commands when at intermission
1656 	if (level.intermissiontime) {
1657 		Cmd_Say_f (ent, qfalse, qtrue);
1658 		return;
1659 	}
1660 
1661 	if (Q_stricmp (cmd, "give") == 0)
1662 		Cmd_Give_f (ent);
1663 	else if (Q_stricmp (cmd, "god") == 0)
1664 		Cmd_God_f (ent);
1665 	else if (Q_stricmp (cmd, "notarget") == 0)
1666 		Cmd_Notarget_f (ent);
1667 	else if (Q_stricmp (cmd, "noclip") == 0)
1668 		Cmd_Noclip_f (ent);
1669 	else if (Q_stricmp (cmd, "kill") == 0)
1670 		Cmd_Kill_f (ent);
1671 	else if (Q_stricmp (cmd, "teamtask") == 0)
1672 		Cmd_TeamTask_f (ent);
1673 	else if (Q_stricmp (cmd, "levelshot") == 0)
1674 		Cmd_LevelShot_f (ent);
1675 	else if (Q_stricmp (cmd, "follow") == 0)
1676 		Cmd_Follow_f (ent);
1677 	else if (Q_stricmp (cmd, "follownext") == 0)
1678 		Cmd_FollowCycle_f (ent, 1);
1679 	else if (Q_stricmp (cmd, "followprev") == 0)
1680 		Cmd_FollowCycle_f (ent, -1);
1681 	else if (Q_stricmp (cmd, "team") == 0)
1682 		Cmd_Team_f (ent);
1683 	else if (Q_stricmp (cmd, "where") == 0)
1684 		Cmd_Where_f (ent);
1685 	else if (Q_stricmp (cmd, "callvote") == 0)
1686 		Cmd_CallVote_f (ent);
1687 	else if (Q_stricmp (cmd, "vote") == 0)
1688 		Cmd_Vote_f (ent);
1689 	else if (Q_stricmp (cmd, "callteamvote") == 0)
1690 		Cmd_CallTeamVote_f (ent);
1691 	else if (Q_stricmp (cmd, "teamvote") == 0)
1692 		Cmd_TeamVote_f (ent);
1693 	else if (Q_stricmp (cmd, "gc") == 0)
1694 		Cmd_GameCommand_f( ent );
1695 	else if (Q_stricmp (cmd, "setviewpos") == 0)
1696 		Cmd_SetViewpos_f( ent );
1697 	else if (Q_stricmp (cmd, "stats") == 0)
1698 		Cmd_Stats_f( ent );
1699 	else
1700 		trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
1701 }
1702