1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "g_local.h"
30 /*
31 ==================
32 DeathmatchScoreboardMessage
33 
34 ==================
35 */
DeathmatchScoreboardMessage(gentity_t * ent)36 void DeathmatchScoreboardMessage( gentity_t *ent ) {
37 	char entry[1024];
38 	char string[1000];
39 	int stringlength;
40 	int i, j;
41 	gclient_t   *cl;
42 	int numSorted;
43 	int scoreFlags;
44 
45 	// don't send scores to bots, they don't parse it
46 	if ( ent->r.svFlags & SVF_BOT ) {
47 		return;
48 	}
49 
50 	// send the latest information on all clients
51 	string[0] = 0;
52 	stringlength = 0;
53 	scoreFlags = 0;
54 
55 	// don't send more than 32 scores (FIXME?)
56 	numSorted = level.numConnectedClients;
57 	if ( numSorted > 32 ) {
58 		numSorted = 32;
59 	}
60 
61 	for ( i = 0 ; i < numSorted ; i++ ) {
62 		int ping;
63 		int playerClass;
64 		int respawnsLeft;
65 
66 		cl = &level.clients[level.sortedClients[i]];
67 
68 		// NERVE - SMF - if on same team, send across player class
69 		if ( cl->ps.persistant[PERS_TEAM] == ent->client->ps.persistant[PERS_TEAM] ) {
70 			playerClass = cl->ps.stats[STAT_PLAYER_CLASS];
71 		} else {
72 			playerClass = 0;
73 		}
74 
75 		// NERVE - SMF - number of respawns left
76 		respawnsLeft = cl->ps.persistant[PERS_RESPAWNS_LEFT];
77 		if ( respawnsLeft == 0 && ( ( cl->ps.pm_flags & PMF_LIMBO ) || ( level.intermissiontime && g_entities[level.sortedClients[i]].health <= 0 ) ) ) {
78 			respawnsLeft = -2;
79 		}
80 
81 		if ( cl->pers.connected == CON_CONNECTING ) {
82 			ping = -1;
83 		} else {
84 			ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
85 		}
86 		Com_sprintf( entry, sizeof( entry ),
87 					 " %i %i %i %i %i %i %i %i", level.sortedClients[i],
88 					 cl->ps.persistant[PERS_SCORE], ping, ( level.time - cl->pers.enterTime ) / 60000,
89 					 scoreFlags, g_entities[level.sortedClients[i]].s.powerups, playerClass, respawnsLeft );
90 		j = strlen( entry );
91 		if ( stringlength + j >= sizeof(string) ) {
92 			break;
93 		}
94 		strcpy( string + stringlength, entry );
95 		stringlength += j;
96 	}
97 
98 	trap_SendServerCommand( ent - g_entities, va( "scores %i %i %i%s", i,
99 												  level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE],
100 												  string ) );
101 }
102 
103 
104 /*
105 ==================
106 Cmd_Score_f
107 
108 Request current scoreboard information
109 ==================
110 */
Cmd_Score_f(gentity_t * ent)111 void Cmd_Score_f( gentity_t *ent ) {
112 	DeathmatchScoreboardMessage( ent );
113 }
114 
115 
116 
117 /*
118 ==================
119 CheatsOk
120 ==================
121 */
CheatsOk(gentity_t * ent)122 qboolean    CheatsOk( gentity_t *ent ) {
123 	if ( !g_cheats.integer ) {
124 		trap_SendServerCommand( ent-g_entities, "print \"Cheats are not enabled on this server.\n\"");
125 		return qfalse;
126 	}
127 	if ( ent->health <= 0 ) {
128 		trap_SendServerCommand( ent-g_entities, "print \"You must be alive to use this command.\n\"");
129 		return qfalse;
130 	}
131 	return qtrue;
132 }
133 
134 
135 /*
136 ==================
137 ConcatArgs
138 ==================
139 */
ConcatArgs(int start)140 char    *ConcatArgs( int start ) {
141 	int i, c, tlen;
142 	static char line[MAX_STRING_CHARS];
143 	int len;
144 	char arg[MAX_STRING_CHARS];
145 
146 	len = 0;
147 	c = trap_Argc();
148 	for ( i = start ; i < c ; i++ ) {
149 		trap_Argv( i, arg, sizeof( arg ) );
150 		tlen = strlen( arg );
151 		if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
152 			break;
153 		}
154 		memcpy( line + len, arg, tlen );
155 		len += tlen;
156 		if ( i != c - 1 ) {
157 			line[len] = ' ';
158 			len++;
159 		}
160 	}
161 
162 	line[len] = 0;
163 
164 	return line;
165 }
166 
167 /*
168 ==================
169 StringIsInteger
170 ==================
171 */
StringIsInteger(const char * s)172 qboolean StringIsInteger( const char * s ) {
173 	int			i;
174 	int			len;
175 	qboolean	foundDigit;
176 
177 	len = strlen( s );
178 	foundDigit = qfalse;
179 
180 	for ( i=0 ; i < len ; i++ ) {
181 		if ( !isdigit( s[i] ) ) {
182 			return qfalse;
183 		}
184 
185 		foundDigit = qtrue;
186 	}
187 
188 	return foundDigit;
189 }
190 
191 /*
192 ==================
193 ClientNumberFromString
194 
195 Returns a player number for either a number or name string
196 Returns -1 if invalid
197 ==================
198 */
ClientNumberFromString(gentity_t * to,char * s,qboolean checkNums,qboolean checkNames)199 int ClientNumberFromString( gentity_t *to, char *s, qboolean checkNums, qboolean checkNames ) {
200 	gclient_t   *cl;
201 	int idnum;
202 	char cleanName[MAX_STRING_CHARS];
203 
204 	if ( checkNums ) {
205 		// numeric values could be slot numbers
206 		if ( StringIsInteger( s ) ) {
207 			idnum = atoi( s );
208 			if ( idnum >= 0 && idnum < level.maxclients ) {
209 				cl = &level.clients[idnum];
210 				if ( cl->pers.connected == CON_CONNECTED ) {
211 					return idnum;
212 				}
213 			}
214 		}
215 	}
216 
217 	if ( checkNames ) {
218 		// check for a name match
219 		for ( idnum = 0, cl = level.clients; idnum < level.maxclients; idnum++, cl++ ) {
220 			if ( cl->pers.connected != CON_CONNECTED ) {
221 				continue;
222 			}
223 			Q_strncpyz( cleanName, cl->pers.netname, sizeof( cleanName ) );
224 			Q_CleanStr( cleanName );
225 			if ( !Q_stricmp( cleanName, s ) ) {
226 				return idnum;
227 			}
228 		}
229 	}
230 
231 	trap_SendServerCommand( to - g_entities, va( "print \"User [lof]%s [lon]is not on the server\n\"", s ) );
232 	return -1;
233 }
234 
235 /*
236 ==================
237 Cmd_Give_f
238 
239 Give items to a client
240 ==================
241 */
Cmd_Give_f(gentity_t * ent)242 void Cmd_Give_f( gentity_t *ent ) {
243 	char        *name, *amt;
244 	gitem_t     *it;
245 	int i;
246 	qboolean give_all;
247 	gentity_t       *it_ent;
248 	trace_t trace;
249 	int amount;
250 
251 	if ( !CheatsOk( ent ) ) {
252 		return;
253 	}
254 
255 	//----(SA)	check for an amount (like "give health 30")
256 	amt = ConcatArgs( 2 );
257 	amount = atoi( amt );
258 	//----(SA)	end
259 
260 	name = ConcatArgs( 1 );
261 
262 	if ( Q_stricmp( name, "all" ) == 0 ) {
263 		give_all = qtrue;
264 	} else {
265 		give_all = qfalse;
266 	}
267 
268 
269 	if ( give_all || Q_stricmpn( name, "health", 6 ) == 0 ) {
270 		//----(SA)	modified
271 		if ( amount ) {
272 			ent->health += amount;
273 		} else {
274 			ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
275 		}
276 		if ( !give_all ) {
277 			return;
278 		}
279 	}
280 
281 	if ( give_all || Q_stricmp( name, "weapons" ) == 0 ) {
282 		for ( i = 0; i < WP_NUM_WEAPONS; i++ ) {
283 			if ( BG_WeaponInWolfMP( i ) ) {
284 				COM_BitSet( ent->client->ps.weapons, i );
285 			}
286 		}
287 
288 		if ( !give_all ) {
289 			return;
290 		}
291 	}
292 
293 	if ( give_all || Q_stricmp( name, "holdable" ) == 0 ) {
294 		ent->client->ps.stats[STAT_HOLDABLE_ITEM] = ( 1 << ( HI_BOOK3 - 1 ) ) - 1 - ( 1 << HI_NONE );
295 		for ( i = 1 ; i <= HI_BOOK3 ; i++ ) {
296 			ent->client->ps.holdable[i] = 10;
297 		}
298 
299 		if ( !give_all ) {
300 			return;
301 		}
302 	}
303 
304 	if ( give_all || Q_stricmpn( name, "ammo", 4 ) == 0 ) {
305 		if ( amount ) {
306 			if ( ent->client->ps.weapon ) {
307 				Add_Ammo( ent, ent->client->ps.weapon, amount, qtrue );
308 			}
309 		} else {
310 			for ( i = 1 ; i < WP_MONSTER_ATTACK1 ; i++ )
311 				Add_Ammo( ent, i, 9999, qtrue );
312 		}
313 
314 		if ( !give_all ) {
315 			return;
316 		}
317 	}
318 
319 	//	"give allammo <n>" allows you to give a specific amount of ammo to /all/ weapons while
320 	//	allowing "give ammo <n>" to only give to the selected weap.
321 	if ( Q_stricmpn( name, "allammo", 7 ) == 0 && amount ) {
322 		for ( i = 1 ; i < WP_MONSTER_ATTACK1 ; i++ )
323 			Add_Ammo( ent, i, amount, qtrue );
324 
325 		if ( !give_all ) {
326 			return;
327 		}
328 	}
329 
330 	if ( give_all || Q_stricmpn( name, "armor", 5 ) == 0 ) {
331 		if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // JPW NERVE -- no armor in multiplayer
332 			//----(SA)	modified
333 			if ( amount ) {
334 				ent->client->ps.stats[STAT_ARMOR] += amount;
335 			} else {
336 				ent->client->ps.stats[STAT_ARMOR] = 200;
337 			}
338 		} // jpw
339 		if ( !give_all ) {
340 			return;
341 		}
342 	}
343 
344 	//---- (SA) Wolf keys
345 	if ( give_all || Q_stricmp( name, "keys" ) == 0 ) {
346 		ent->client->ps.stats[STAT_KEYS] = ( 1 << KEY_NUM_KEYS ) - 2;
347 		if ( !give_all ) {
348 			return;
349 		}
350 	}
351 	//---- (SA) end
352 
353 	// spawn a specific item right on the player
354 	if ( !give_all ) {
355 		it = BG_FindItem( name );
356 		if ( !it ) {
357 			return;
358 		}
359 
360 		it_ent = G_Spawn();
361 		VectorCopy( ent->r.currentOrigin, it_ent->s.origin );
362 		it_ent->classname = it->classname;
363 		G_SpawnItem( it_ent, it );
364 		FinishSpawningItem( it_ent );
365 		memset( &trace, 0, sizeof( trace ) );
366 		it_ent->active = qtrue;
367 		Touch_Item( it_ent, ent, &trace );
368 		it_ent->active = qfalse;
369 		if ( it_ent->inuse ) {
370 			G_FreeEntity( it_ent );
371 		}
372 	}
373 }
374 
375 
376 /*
377 ==================
378 Cmd_God_f
379 
380 Sets client to godmode
381 
382 argv(0) god
383 ==================
384 */
Cmd_God_f(gentity_t * ent)385 void Cmd_God_f( gentity_t *ent ) {
386 	char    *msg;
387 
388 	if ( !CheatsOk( ent ) ) {
389 		return;
390 	}
391 
392 	ent->flags ^= FL_GODMODE;
393 	if ( !( ent->flags & FL_GODMODE ) ) {
394 		msg = "godmode OFF\n";
395 	} else {
396 		msg = "godmode ON\n";
397 	}
398 
399 	trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
400 }
401 
402 /*
403 ==================
404 Cmd_Nofatigue_f
405 
406 Sets client to nofatigue
407 
408 argv(0) nofatigue
409 ==================
410 */
411 
Cmd_Nofatigue_f(gentity_t * ent)412 void Cmd_Nofatigue_f( gentity_t *ent ) {
413 	char    *msg;
414 
415 	if ( !CheatsOk( ent ) ) {
416 		return;
417 	}
418 
419 	ent->flags ^= FL_NOFATIGUE;
420 	if ( !( ent->flags & FL_NOFATIGUE ) ) {
421 		msg = "nofatigue OFF\n";
422 	} else {
423 		msg = "nofatigue ON\n";
424 	}
425 
426 	trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
427 }
428 
429 /*
430 ==================
431 Cmd_Notarget_f
432 
433 Sets client to notarget
434 
435 argv(0) notarget
436 ==================
437 */
Cmd_Notarget_f(gentity_t * ent)438 void Cmd_Notarget_f( gentity_t *ent ) {
439 	char    *msg;
440 
441 	if ( !CheatsOk( ent ) ) {
442 		return;
443 	}
444 
445 	ent->flags ^= FL_NOTARGET;
446 	if ( !( ent->flags & FL_NOTARGET ) ) {
447 		msg = "notarget OFF\n";
448 	} else {
449 		msg = "notarget ON\n";
450 	}
451 
452 	trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
453 }
454 
455 
456 /*
457 ==================
458 Cmd_Noclip_f
459 
460 argv(0) noclip
461 ==================
462 */
Cmd_Noclip_f(gentity_t * ent)463 void Cmd_Noclip_f( gentity_t *ent ) {
464 	char    *msg;
465 
466 	if ( !CheatsOk( ent ) ) {
467 		return;
468 	}
469 
470 	if ( ent->client->noclip ) {
471 		msg = "noclip OFF\n";
472 	} else {
473 		msg = "noclip ON\n";
474 	}
475 	ent->client->noclip = !ent->client->noclip;
476 
477 	trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
478 }
479 
480 
481 /*
482 ==================
483 Cmd_LevelShot_f
484 
485 This is just to help generate the level pictures
486 for the menus.  It goes to the intermission immediately
487 and sends over a command to the client to resize the view,
488 hide the scoreboard, and take a special screenshot
489 ==================
490 */
Cmd_LevelShot_f(gentity_t * ent)491 void Cmd_LevelShot_f( gentity_t *ent ) {
492 	if(!ent->client->pers.localClient)
493 	{
494 		trap_SendServerCommand(ent-g_entities,
495 			"print \"The levelshot command must be executed by a local client\n\"");
496  		return;
497  	}
498 
499 	if ( !CheatsOk( ent ) ) {
500 		return;
501 	}
502 
503 	// doesn't work in single player
504 	if(g_gametype.integer == GT_SINGLE_PLAYER)
505 	{
506 		trap_SendServerCommand(ent-g_entities,
507 			"print \"Must not be in singleplayer mode for levelshot\n\"" );
508 		return;
509 	}
510 
511 	BeginIntermission();
512 	trap_SendServerCommand( ent - g_entities, "clientLevelShot" );
513 }
514 
515 
516 /*
517 =================
518 Cmd_Kill_f
519 =================
520 */
Cmd_Kill_f(gentity_t * ent)521 void Cmd_Kill_f( gentity_t *ent ) {
522 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
523 		return;
524 	}
525 	if ( g_gamestate.integer != GS_PLAYING ) {
526 		return;
527 	}
528 	if ( g_gametype.integer >= GT_WOLF && ent->client->ps.pm_flags & PMF_LIMBO ) {
529 		return;
530 	}
531 
532 	ent->flags &= ~FL_GODMODE;
533 	ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
534 	ent->client->ps.persistant[PERS_HWEAPON_USE] = 0; // TTimo - if using /kill while at MG42
535 	player_die( ent, ent, ent, 100000, MOD_SUICIDE );
536 }
537 
538 
539 /*
540 =================
541 SetTeam
542 =================
543 */
SetTeam(gentity_t * ent,const char * s)544 void SetTeam( gentity_t *ent, const char *s ) {
545 	int team, oldTeam;
546 	gclient_t           *client;
547 	int clientNum;
548 	spectatorState_t specState;
549 	int specClient;
550 
551 	//
552 	// see what change is requested
553 	//
554 	client = ent->client;
555 
556 	clientNum = client - level.clients;
557 	specClient = 0;
558 
559 	specState = SPECTATOR_NOT;
560 	if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
561 		team = TEAM_SPECTATOR;
562 		specState = SPECTATOR_SCOREBOARD;
563 	} else if ( !Q_stricmp( s, "follow1" ) ) {
564 		team = TEAM_SPECTATOR;
565 		specState = SPECTATOR_FOLLOW;
566 		specClient = -1;
567 	} else if ( !Q_stricmp( s, "follow2" ) ) {
568 		team = TEAM_SPECTATOR;
569 		specState = SPECTATOR_FOLLOW;
570 		specClient = -2;
571 	} else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) {
572 		team = TEAM_SPECTATOR;
573 		specState = SPECTATOR_FREE;
574 	} else if ( g_gametype.integer >= GT_TEAM ) {
575 		// if running a team game, assign player to one of the teams
576 		specState = SPECTATOR_NOT;
577 		if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
578 			team = TEAM_RED;
579 		} else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
580 			team = TEAM_BLUE;
581 		} else {
582 			// pick the team with the least number of players
583 			team = PickTeam( clientNum );
584 		}
585 
586 		// NERVE - SMF
587 		if ( g_noTeamSwitching.integer && team != ent->client->sess.sessionTeam && g_gamestate.integer == GS_PLAYING ) {
588 			trap_SendServerCommand( clientNum, "cp \"You cannot switch during a match, please wait until the round ends.\n\"" );
589 			return; // ignore the request
590 		}
591 
592 		// NERVE - SMF - merge from team arena
593 		if ( g_teamForceBalance.integer && !client->pers.localClient && !( ent->r.svFlags & SVF_BOT ) ) {
594 			int counts[TEAM_NUM_TEAMS];
595 
596 			counts[TEAM_BLUE] = TeamCount( clientNum, TEAM_BLUE );
597 			counts[TEAM_RED] = TeamCount( clientNum, TEAM_RED );
598 
599 			// We allow a spread of one
600 			if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] >= 1 ) {
601 				trap_SendServerCommand( clientNum,
602 										"cp \"The Axis has too many players.\n\"" );
603 				return; // ignore the request
604 			}
605 			if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] >= 1 ) {
606 				trap_SendServerCommand( clientNum,
607 										"cp \"The Allies have too many players.\n\"" );
608 				return; // ignore the request
609 			}
610 
611 			// It's ok, the team we are switching to has less or same number of players
612 		}
613 		// -NERVE - SMF
614 	} else {
615 		// force them to spectators if there aren't any spots free
616 		team = TEAM_FREE;
617 	}
618 
619 	// override decision if limiting the players
620 	if ( g_gametype.integer == GT_TOURNAMENT
621 		 && level.numNonSpectatorClients >= 2 ) {
622 		team = TEAM_SPECTATOR;
623 	} else if ( g_maxGameClients.integer > 0 &&
624 				level.numNonSpectatorClients >= g_maxGameClients.integer ) {
625 		team = TEAM_SPECTATOR;
626 	}
627 
628 	//
629 	// decide if we will allow the change
630 	//
631 	oldTeam = client->sess.sessionTeam;
632 	if ( team == oldTeam && team != TEAM_SPECTATOR ) {
633 		return;
634 	}
635 
636 	// NERVE - SMF - prevent players from switching to regain deployments
637 	if ( g_maxlives.integer > 0 && ent->client->ps.persistant[PERS_RESPAWNS_LEFT] == 0 &&
638 		 oldTeam != TEAM_SPECTATOR ) {
639 		trap_SendServerCommand( clientNum,
640 								"cp \"You can't switch teams because you are out of lives.\n\" 3" );
641 		return; // ignore the request
642 	}
643 
644 	// DHM - Nerve :: Force players to wait 30 seconds before they can join a new team.
645 	if ( g_gametype.integer >= GT_WOLF && team != oldTeam && level.warmupTime == 0 && !client->pers.initialSpawn
646 		 && ( ( level.time - client->pers.connectTime ) > 10000 ) && ( ( level.time - client->pers.enterTime ) < 30000 ) ) {
647 		trap_SendServerCommand( ent - g_entities,
648 								va( "cp \"^3You must wait %i seconds before joining ^3a new team.\n\" 3", (int)( 30 - ( ( level.time - client->pers.enterTime ) / 1000 ) ) ) );
649 		return;
650 	}
651 	// dhm
652 
653 	//
654 	// execute the team change
655 	//
656 
657 	// DHM - Nerve
658 	if ( client->pers.initialSpawn && team != TEAM_SPECTATOR ) {
659 		client->pers.initialSpawn = qfalse;
660 	}
661 
662 	// he starts at 'base'
663 	client->pers.teamState.state = TEAM_BEGIN;
664 	if ( oldTeam != TEAM_SPECTATOR ) {
665 		if ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) {
666 			// Kill him (makes sure he loses flags, etc)
667 			ent->flags &= ~FL_GODMODE;
668 			ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
669 			player_die( ent, ent, ent, 100000, MOD_SUICIDE );
670 		}
671 	}
672 	// they go to the end of the line for tournements
673 	if(team == TEAM_SPECTATOR && oldTeam != team)
674 		AddTournamentQueue(client);
675 
676 	client->sess.sessionTeam = team;
677 	client->sess.spectatorState = specState;
678 	client->sess.spectatorClient = specClient;
679 
680 	if ( team == TEAM_RED ) {
681 		trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the Axis team.\n\"",
682 										client->pers.netname ) );
683 	} else if ( team == TEAM_BLUE ) {
684 		trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the Allied team.\n\"",
685 										client->pers.netname ) );
686 	} else if ( team == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) {
687 		trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the spectators.\n\"",
688 										client->pers.netname ) );
689 	} else if ( team == TEAM_FREE ) {
690 		trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the battle.\n\"",
691 										client->pers.netname ) );
692 	}
693 
694 	// get and distribute relevent paramters
695 	ClientUserinfoChanged( clientNum );
696 
697 	// client hasn't spawned yet, they sent an early team command, teampref userinfo, or g_teamAutoJoin is enabled
698 	if ( client->pers.connected != CON_CONNECTED ) {
699 		return;
700 	}
701 
702 	ClientBegin( clientNum );
703 }
704 
705 // DHM - Nerve
706 /*
707 =================
708 SetWolfData
709 =================
710 */
SetWolfData(gentity_t * ent,char * ptype,char * weap,char * grenade,char * skinnum)711 void SetWolfData( gentity_t *ent, char *ptype, char *weap, char *grenade, char *skinnum ) { // DHM - Nerve
712 	gclient_t   *client;
713 
714 	client = ent->client;
715 
716 	client->sess.latchPlayerType = atoi( ptype );
717 	client->sess.latchPlayerWeapon = atoi( weap );
718 	client->sess.latchPlayerItem = atoi( grenade );
719 	client->sess.latchPlayerSkin = atoi( skinnum );
720 }
721 // dhm - end
722 
723 /*
724 =================
725 StopFollowing
726 
727 If the client being followed leaves the game, or you just want to drop
728 to free floating spectator mode
729 
730 =================
731 */
StopFollowing(gentity_t * ent)732 void StopFollowing( gentity_t *ent ) {
733 	if ( g_gametype.integer < GT_WOLF ) {       // NERVE - SMF - don't forcibly set this for multiplayer
734 		ent->client->sess.sessionTeam = TEAM_SPECTATOR;
735 		ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;
736 	}
737 
738 	// ATVI Wolfenstein Misc #474
739 	// divert behaviour if TEAM_SPECTATOR, moved the code from SpectatorThink to put back into free fly correctly
740 	// (I am not sure this can be called in non-TEAM_SPECTATOR situation, better be safe)
741 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
742 		// drop to free floating, somewhere above the current position (that's the client you were following)
743 		vec3_t pos, angle;
744 		int enterTime;
745 		gclient_t   *client = ent->client;
746 		VectorCopy( client->ps.origin, pos ); pos[2] += 16;
747 		VectorCopy( client->ps.viewangles, angle );
748 		// ATVI Wolfenstein Misc #414, backup enterTime
749 		enterTime = client->pers.enterTime;
750 		SetTeam( ent, "spectator" );
751 		client->pers.enterTime = enterTime;
752 		VectorCopy( pos, client->ps.origin );
753 		SetClientViewAngle( ent, angle );
754 	} else
755 	{
756 		// legacy code, FIXME: useless?
757 		ent->client->sess.spectatorState = SPECTATOR_FREE;
758 		ent->r.svFlags &= ~SVF_BOT;
759 		ent->client->ps.clientNum = ent - g_entities;
760 	}
761 }
762 
763 /*
764 =================
765 Cmd_Team_f
766 =================
767 */
Cmd_Team_f(gentity_t * ent)768 void Cmd_Team_f( gentity_t *ent ) {
769 	int oldTeam;
770 	char s[MAX_TOKEN_CHARS];
771 	char ptype[4], weap[4], pistol[4], grenade[4], skinnum[4];
772 
773 	if ( trap_Argc() < 2 ) {
774 		oldTeam = ent->client->sess.sessionTeam;
775 		switch ( oldTeam ) {
776 		case TEAM_BLUE:
777 			trap_SendServerCommand( ent - g_entities, "print \"Blue team\n\"" );
778 			break;
779 		case TEAM_RED:
780 			trap_SendServerCommand( ent - g_entities, "print \"Red team\n\"" );
781 			break;
782 		case TEAM_FREE:
783 			trap_SendServerCommand( ent - g_entities, "print \"Free team\n\"" );
784 			break;
785 		case TEAM_SPECTATOR:
786 			trap_SendServerCommand( ent - g_entities, "print \"Spectator team\n\"" );
787 			break;
788 		}
789 		return;
790 	}
791 
792 	// if they are playing a tournement game, count as a loss
793 	if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) {
794 		ent->client->sess.losses++;
795 	}
796 
797 	// DHM - Nerve
798 	if ( g_gametype.integer >= GT_WOLF ) {
799 		trap_Argv( 2, ptype, sizeof( ptype ) );
800 		trap_Argv( 3, weap, sizeof( weap ) );
801 		trap_Argv( 4, pistol, sizeof( pistol ) );
802 		trap_Argv( 5, grenade, sizeof( grenade ) );
803 		trap_Argv( 6, skinnum, sizeof( skinnum ) );
804 
805 		SetWolfData( ent, ptype, weap, grenade, skinnum );
806 	}
807 	// dhm - end
808 
809 	trap_Argv( 1, s, sizeof( s ) );
810 
811 	SetTeam( ent, s );
812 }
813 
814 /*
815 =================
816 Cmd_Follow_f
817 =================
818 */
Cmd_Follow_f(gentity_t * ent)819 void Cmd_Follow_f( gentity_t *ent ) {
820 	int i;
821 	char arg[MAX_TOKEN_CHARS];
822 
823 	if ( trap_Argc() != 2 ) {
824 		if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
825 			StopFollowing( ent );
826 		}
827 		return;
828 	}
829 
830 	trap_Argv( 1, arg, sizeof( arg ) );
831 	i = ClientNumberFromString( ent, arg, qtrue, qtrue );
832 	if ( i == -1 ) {
833 		return;
834 	}
835 
836 	// can't follow self
837 	if ( &level.clients[ i ] == ent->client ) {
838 		return;
839 	}
840 
841 	// can't follow another spectator
842 	if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) {
843 		return;
844 	}
845 
846 	if ( g_gametype.integer >= GT_WOLF ) {
847 		if ( level.clients[ i ].ps.pm_flags & PMF_LIMBO ) {
848 			return;
849 		}
850 	}
851 
852 	// if they are playing a tournement game, count as a loss
853 	if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) {
854 		ent->client->sess.losses++;
855 	}
856 
857 	// first set them to spectator
858 	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
859 		SetTeam( ent, "spectator" );
860 	}
861 
862 	ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
863 	ent->client->sess.spectatorClient = i;
864 }
865 
866 /*
867 =================
868 Cmd_FollowCycle_f
869 =================
870 */
Cmd_FollowCycle_f(gentity_t * ent,int dir)871 void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
872 	int clientnum;
873 	int original;
874 
875 	// if they are playing a tournement game, count as a loss
876 	if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) {
877 		ent->client->sess.losses++;
878 	}
879 	// first set them to spectator
880 	if ( ( ent->client->sess.spectatorState == SPECTATOR_NOT ) && ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) ) { // JPW NERVE for limbo state
881 		SetTeam( ent, "spectator" );
882 	}
883 
884 	if ( dir != 1 && dir != -1 ) {
885 		G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
886 	}
887 
888 	// if dedicated follow client, just switch between the two auto clients
889 	if (ent->client->sess.spectatorClient < 0) {
890 		if (ent->client->sess.spectatorClient == -1) {
891 			ent->client->sess.spectatorClient = -2;
892 		} else if (ent->client->sess.spectatorClient == -2) {
893 			ent->client->sess.spectatorClient = -1;
894 		}
895 		return;
896 	}
897 
898 	clientnum = ent->client->sess.spectatorClient;
899 	original = clientnum;
900 	do {
901 		clientnum += dir;
902 		if ( clientnum >= level.maxclients ) {
903 			clientnum = 0;
904 		}
905 		if ( clientnum < 0 ) {
906 			clientnum = level.maxclients - 1;
907 		}
908 
909 		// can only follow connected clients
910 		if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) {
911 			continue;
912 		}
913 
914 		// can't follow another spectator
915 		if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) {
916 			continue;
917 		}
918 
919 // JPW NERVE -- couple extra checks for limbo mode
920 		if ( ent->client->ps.pm_flags & PMF_LIMBO ) {
921 			if ( level.clients[clientnum].ps.pm_flags & PMF_LIMBO ) {
922 				continue;
923 			}
924 			if ( level.clients[clientnum].sess.sessionTeam != ent->client->sess.sessionTeam ) {
925 				continue;
926 			}
927 		}
928 // jpw
929 
930 		if ( g_gametype.integer >= GT_WOLF ) {
931 			if ( level.clients[clientnum].ps.pm_flags & PMF_LIMBO ) {
932 				continue;
933 			}
934 		}
935 
936 		// this is good, we can use it
937 		ent->client->sess.spectatorClient = clientnum;
938 		ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
939 		return;
940 	} while ( clientnum != original );
941 
942 	// leave it where it was
943 }
944 
945 
946 /*
947 ==================
948 G_Say
949 ==================
950 */
951 #define MAX_SAY_TEXT    150
952 
953 #define SAY_ALL     0
954 #define SAY_TEAM    1
955 #define SAY_TELL    2
956 #define SAY_LIMBO   3           // NERVE - SMF
957 
G_SayTo(gentity_t * ent,gentity_t * other,int mode,int color,const char * name,const char * message,qboolean localize)958 void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message, qboolean localize ) { // removed static so it would link
959 	if ( !other ) {
960 		return;
961 	}
962 	if ( !other->inuse ) {
963 		return;
964 	}
965 	if ( !other->client ) {
966 		return;
967 	}
968 	if ( mode == SAY_TEAM  && !OnSameTeam( ent, other ) ) {
969 		return;
970 	}
971 	// no chatting to players in tournements
972 	if ( g_gametype.integer == GT_TOURNAMENT
973 		 && other->client->sess.sessionTeam == TEAM_FREE
974 		 && ent->client->sess.sessionTeam != TEAM_FREE ) {
975 		return;
976 	}
977 
978 	// NERVE - SMF - if spectator, no chatting to players in WolfMP
979 	if ( g_gametype.integer >= GT_WOLF
980 		 && ( ( ent->client->sess.sessionTeam == TEAM_FREE && other->client->sess.sessionTeam != TEAM_FREE ) ||
981 			  ( ent->client->sess.sessionTeam == TEAM_SPECTATOR && other->client->sess.sessionTeam != TEAM_SPECTATOR ) ) ) {
982 		return;
983 	}
984 
985 	// NERVE - SMF
986 	if ( mode == SAY_LIMBO ) {
987 		trap_SendServerCommand( other - g_entities, va( "%s \"%s%c%c%s\"",
988 														"lchat", name, Q_COLOR_ESCAPE, color, message ) );
989 	}
990 	// -NERVE - SMF
991 	else {
992 		trap_SendServerCommand( other - g_entities, va( "%s \"%s%c%c%s\" %i",
993 														mode == SAY_TEAM ? "tchat" : "chat",
994 														name, Q_COLOR_ESCAPE, color, message, localize ) );
995 	}
996 }
997 
G_Say(gentity_t * ent,gentity_t * target,int mode,const char * chatText)998 void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) {
999 	int j;
1000 	gentity_t   *other;
1001 	int color;
1002 	char name[64];
1003 	// don't let text be too long for malicious reasons
1004 	char text[MAX_SAY_TEXT];
1005 	char location[64];
1006 	qboolean localize = qfalse;
1007 
1008 	if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
1009 		mode = SAY_ALL;
1010 	}
1011 
1012 	switch ( mode ) {
1013 	default:
1014 	case SAY_ALL:
1015 		G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
1016 		Com_sprintf( name, sizeof( name ), "%s%c%c: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
1017 		color = COLOR_GREEN;
1018 		break;
1019 	case SAY_TEAM:
1020 		localize = qtrue;
1021 		G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
1022 		if ( Team_GetLocationMsg( ent, location, sizeof( location ) ) ) {
1023 			Com_sprintf( name, sizeof( name ), "[lof](%s%c%c) (%s): ",
1024 						 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location );
1025 		} else {
1026 			Com_sprintf( name, sizeof( name ), "(%s%c%c): ",
1027 						 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
1028 		}
1029 		color = COLOR_CYAN;
1030 		break;
1031 	case SAY_TELL:
1032 		if (target && target->inuse && target->client && g_gametype.integer >= GT_TEAM &&
1033 			 target->client->sess.sessionTeam == ent->client->sess.sessionTeam &&
1034 			 Team_GetLocationMsg( ent, location, sizeof( location ) ) ) {
1035 			Com_sprintf( name, sizeof( name ), "[%s%c%c] (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location );
1036 		} else {
1037 			Com_sprintf( name, sizeof( name ), "[%s%c%c]: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
1038 		}
1039 		color = COLOR_MAGENTA;
1040 		break;
1041 		// NERVE - SMF
1042 	case SAY_LIMBO:
1043 		G_LogPrintf( "say_limbo: %s: %s\n", ent->client->pers.netname, chatText );
1044 		Com_sprintf( name, sizeof( name ), "%s%c%c: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
1045 		color = COLOR_GREEN;
1046 		break;
1047 		// -NERVE - SMF
1048 	}
1049 
1050 	Q_strncpyz( text, chatText, sizeof( text ) );
1051 
1052 	if ( target ) {
1053 		G_SayTo( ent, target, mode, color, name, text, localize );
1054 		return;
1055 	}
1056 
1057 	// echo the text to the console
1058 	if ( g_dedicated.integer ) {
1059 		G_Printf( "%s%s\n", name, text );
1060 	}
1061 
1062 	// send it to all the apropriate clients
1063 	for ( j = 0; j < level.maxclients; j++ ) {
1064 		other = &g_entities[j];
1065 		G_SayTo( ent, other, mode, color, name, text, localize );
1066 	}
1067 }
1068 
SanitizeChatText(char * text)1069 static void SanitizeChatText( char *text ) {
1070 	int i;
1071 
1072 	for ( i = 0; text[i]; i++ ) {
1073 		if ( text[i] == '\n' || text[i] == '\r' ) {
1074 			text[i] = ' ';
1075 		}
1076 	}
1077 }
1078 
1079 /*
1080 ==================
1081 Cmd_Say_f
1082 ==================
1083 */
Cmd_Say_f(gentity_t * ent,int mode,qboolean arg0)1084 static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
1085 	char        *p;
1086 
1087 	if ( trap_Argc() < 2 && !arg0 ) {
1088 		return;
1089 	}
1090 
1091 	if ( arg0 ) {
1092 		p = ConcatArgs( 0 );
1093 	} else
1094 	{
1095 		p = ConcatArgs( 1 );
1096 	}
1097 
1098 	SanitizeChatText( p );
1099 
1100 	G_Say( ent, NULL, mode, p );
1101 }
1102 
1103 /*
1104 ==================
1105 Cmd_Tell_f
1106 ==================
1107 */
Cmd_Tell_f(gentity_t * ent)1108 static void Cmd_Tell_f( gentity_t *ent ) {
1109 	int			targetNum;
1110 	gentity_t	*target;
1111 	char		*p;
1112 	char		arg[MAX_TOKEN_CHARS];
1113 
1114 	if ( trap_Argc () < 3 ) {
1115 		trap_SendServerCommand( ent-g_entities, "print \"Usage: tell <player id> <message>\n\"" );
1116 		return;
1117 	}
1118 
1119 	trap_Argv( 1, arg, sizeof( arg ) );
1120 	targetNum = ClientNumberFromString( ent, arg, qtrue, qtrue );
1121 	if ( targetNum == -1 ) {
1122 		return;
1123 	}
1124 
1125 	target = &g_entities[targetNum];
1126 	if ( !target->inuse || !target->client ) {
1127 		return;
1128 	}
1129 
1130 	p = ConcatArgs( 2 );
1131 
1132 	SanitizeChatText( p );
1133 
1134 	G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
1135 	G_Say( ent, target, SAY_TELL, p );
1136 	// don't tell to the player self if it was already directed to this player
1137 	// also don't send the chat back to a bot
1138 	if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
1139 		G_Say( ent, ent, SAY_TELL, p );
1140 	}
1141 }
1142 
1143 // NERVE - SMF
G_VoiceTo(gentity_t * ent,gentity_t * other,int mode,const char * id,qboolean voiceonly)1144 static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) {
1145 	int color;
1146 	char *cmd;
1147 
1148 	if ( !other ) {
1149 		return;
1150 	}
1151 	if ( !other->inuse ) {
1152 		return;
1153 	}
1154 	if ( !other->client ) {
1155 		return;
1156 	}
1157 	if ( mode == SAY_TEAM && !OnSameTeam( ent, other ) ) {
1158 		return;
1159 	}
1160 	// no chatting to players in tournements
1161 	if ( g_gametype.integer == GT_TOURNAMENT ) {
1162 		return;
1163 	}
1164 
1165 	if ( mode == SAY_TEAM ) {
1166 		color = COLOR_CYAN;
1167 		cmd = "vtchat";
1168 	} else if ( mode == SAY_TELL )     {
1169 		color = COLOR_MAGENTA;
1170 		cmd = "vtell";
1171 	} else {
1172 		color = COLOR_GREEN;
1173 		cmd = "vchat";
1174 	}
1175 
1176 	trap_SendServerCommand( other - g_entities, va( "%s %d %d %d %s %i %i %i", cmd, voiceonly, ent->s.number, color, id,
1177 													(int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) );
1178 }
1179 
G_Voice(gentity_t * ent,gentity_t * target,int mode,const char * id,qboolean voiceonly)1180 void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) {
1181 	int j;
1182 	gentity_t   *other;
1183 
1184 	if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
1185 		mode = SAY_ALL;
1186 	}
1187 
1188 	// DHM - Nerve :: Don't allow excessive spamming of voice chats
1189 	ent->voiceChatSquelch -= ( level.time - ent->voiceChatPreviousTime );
1190 	ent->voiceChatPreviousTime = level.time;
1191 
1192 	if ( ent->voiceChatSquelch < 0 ) {
1193 		ent->voiceChatSquelch = 0;
1194 	}
1195 
1196 	if ( ent->voiceChatSquelch >= 30000 ) {
1197 		trap_SendServerCommand( ent - g_entities, "print \"^1Spam Protection^7: VoiceChat ignored\n\"" );
1198 		return;
1199 	}
1200 
1201 	if ( g_voiceChatsAllowed.integer ) {
1202 		ent->voiceChatSquelch += ( 34000 / g_voiceChatsAllowed.integer );
1203 	} else {
1204 		return;
1205 	}
1206 	// dhm
1207 
1208 	if ( target ) {
1209 		G_VoiceTo( ent, target, mode, id, voiceonly );
1210 		return;
1211 	}
1212 
1213 	// echo the text to the console
1214 	if ( g_dedicated.integer ) {
1215 		G_Printf( "voice: %s %s\n", ent->client->pers.netname, id );
1216 	}
1217 
1218 	// send it to all the apropriate clients
1219 	for ( j = 0; j < level.maxclients; j++ ) {
1220 		other = &g_entities[j];
1221 		G_VoiceTo( ent, other, mode, id, voiceonly );
1222 	}
1223 }
1224 
1225 /*
1226 ==================
1227 Cmd_Voice_f
1228 ==================
1229 */
Cmd_Voice_f(gentity_t * ent,int mode,qboolean arg0,qboolean voiceonly)1230 static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) {
1231 	char        *p;
1232 
1233 	if ( trap_Argc() < 2 && !arg0 ) {
1234 		return;
1235 	}
1236 
1237 	if ( arg0 ) {
1238 		p = ConcatArgs( 0 );
1239 	} else
1240 	{
1241 		p = ConcatArgs( 1 );
1242 	}
1243 
1244 	SanitizeChatText( p );
1245 
1246 	G_Voice( ent, NULL, mode, p, voiceonly );
1247 }
1248 
1249 // TTimo gcc: defined but not used
1250 #if 0
1251 /*
1252 ==================
1253 Cmd_VoiceTell_f
1254 ==================
1255 */
1256 static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) {
1257 	int targetNum;
1258 	gentity_t   *target;
1259 	char        *id;
1260 	char arg[MAX_TOKEN_CHARS];
1261 
1262 	if ( trap_Argc() < 2 ) {
1263 		return;
1264 	}
1265 
1266 	trap_Argv( 1, arg, sizeof( arg ) );
1267 	targetNum = atoi( arg );
1268 	if ( targetNum < 0 || targetNum >= level.maxclients ) {
1269 		return;
1270 	}
1271 
1272 	target = &g_entities[targetNum];
1273 	if ( !target->inuse || !target->client ) {
1274 		return;
1275 	}
1276 
1277 	id = ConcatArgs( 2 );
1278 
1279 	SanitizeChatText( p );
1280 
1281 	G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id );
1282 	G_Voice( ent, target, SAY_TELL, id, voiceonly );
1283 	// don't tell to the player self if it was already directed to this player
1284 	// also don't send the chat back to a bot
1285 	if ( ent != target && !( ent->r.svFlags & SVF_BOT ) ) {
1286 		G_Voice( ent, ent, SAY_TELL, id, voiceonly );
1287 	}
1288 }
1289 #endif
1290 
1291 // TTimo gcc: defined but not used
1292 #if 0
1293 /*
1294 ==================
1295 Cmd_VoiceTaunt_f
1296 ==================
1297 */
1298 static void Cmd_VoiceTaunt_f( gentity_t *ent ) {
1299 	gentity_t *who;
1300 	int i;
1301 
1302 	if ( !ent->client ) {
1303 		return;
1304 	}
1305 
1306 	// insult someone who just killed you
1307 	if ( ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number ) {
1308 		// i am a dead corpse
1309 		if ( !( ent->enemy->r.svFlags & SVF_BOT ) ) {
1310 //			G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
1311 		}
1312 		if ( !( ent->r.svFlags & SVF_BOT ) ) {
1313 //			G_Voice( ent, ent,        SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
1314 		}
1315 		ent->enemy = NULL;
1316 		return;
1317 	}
1318 	// insult someone you just killed
1319 	if ( ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number ) {
1320 		who = g_entities + ent->client->lastkilled_client;
1321 		if ( who->client ) {
1322 			// who is the person I just killed
1323 			if ( who->client->lasthurt_mod == MOD_GAUNTLET ) {
1324 				if ( !( who->r.svFlags & SVF_BOT ) ) {
1325 //					G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );	// and I killed them with a gauntlet
1326 				}
1327 				if ( !( ent->r.svFlags & SVF_BOT ) ) {
1328 //					G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );
1329 				}
1330 			} else {
1331 				if ( !( who->r.svFlags & SVF_BOT ) ) {
1332 //					G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );	// and I killed them with something else
1333 				}
1334 				if ( !( ent->r.svFlags & SVF_BOT ) ) {
1335 //					G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );
1336 				}
1337 			}
1338 			ent->client->lastkilled_client = -1;
1339 			return;
1340 		}
1341 	}
1342 
1343 	if ( g_gametype.integer >= GT_TEAM ) {
1344 		// praise a team mate who just got a reward
1345 		for ( i = 0; i < MAX_CLIENTS; i++ ) {
1346 			who = g_entities + i;
1347 			if ( who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam ) {
1348 				if ( who->client->rewardTime > level.time ) {
1349 					if ( !( who->r.svFlags & SVF_BOT ) ) {
1350 //						G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse );
1351 					}
1352 					if ( !( ent->r.svFlags & SVF_BOT ) ) {
1353 //						G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse );
1354 					}
1355 					return;
1356 				}
1357 			}
1358 		}
1359 	}
1360 
1361 	// just say something
1362 //	G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse );
1363 }
1364 // -NERVE - SMF
1365 #endif
1366 
1367 static char *gc_orders[] = {
1368 	"hold your position",
1369 	"hold this position",
1370 	"come here",
1371 	"cover me",
1372 	"guard location",
1373 	"search and destroy",
1374 	"report"
1375 };
1376 
1377 static const int numgc_orders = ARRAY_LEN( gc_orders );
1378 
Cmd_GameCommand_f(gentity_t * ent)1379 void Cmd_GameCommand_f( gentity_t *ent ) {
1380 	int			targetNum;
1381 	gentity_t	*target;
1382 	int			order;
1383 	char		arg[MAX_TOKEN_CHARS];
1384 
1385 	if ( trap_Argc() != 3 ) {
1386 		trap_SendServerCommand( ent-g_entities, va( "print \"Usage: gc <player id> <order 0-%d>\n\"", numgc_orders - 1 ) );
1387 		return;
1388 	}
1389 
1390 	trap_Argv( 2, arg, sizeof( arg ) );
1391 	order = atoi( arg );
1392 
1393 	if ( order < 0 || order >= numgc_orders ) {
1394 		trap_SendServerCommand( ent-g_entities, va("print \"Bad order: %i\n\"", order));
1395 		return;
1396 	}
1397 
1398 	trap_Argv( 1, arg, sizeof( arg ) );
1399 	targetNum = ClientNumberFromString( ent, arg, qtrue, qtrue );
1400 	if ( targetNum == -1 ) {
1401 		return;
1402 	}
1403 
1404 	target = &g_entities[targetNum];
1405 	if ( !target->inuse || !target->client ) {
1406 		return;
1407 	}
1408 
1409 	G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, gc_orders[order] );
1410 	G_Say( ent, target, SAY_TELL, gc_orders[order] );
1411 	// don't tell to the player self if it was already directed to this player
1412 	// also don't send the chat back to a bot
1413 	if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
1414 		G_Say( ent, ent, SAY_TELL, gc_orders[order] );
1415 	}
1416 }
1417 
1418 /*
1419 ==================
1420 Cmd_Where_f
1421 ==================
1422 */
Cmd_Where_f(gentity_t * ent)1423 void Cmd_Where_f( gentity_t *ent ) {
1424 	trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos(ent->r.currentOrigin) ) );
1425 }
1426 
1427 
1428 static const char *gameNames[] = {
1429 	"Free For All",
1430 	"Tournament",
1431 	"Single Player",
1432 	"Team Deathmatch",
1433 	"Capture the Flag",
1434 	"Wolf Multiplayer",
1435 	"Wolf Stopwatch",
1436 	"Wolf Checkpoint"
1437 };
1438 
1439 
1440 /*
1441 ==================
1442 Cmd_CallVote_f
1443 ==================
1444 */
Cmd_CallVote_f(gentity_t * ent)1445 void Cmd_CallVote_f( gentity_t *ent ) {
1446 	char*	c;
1447 	int i;
1448 	char arg1[MAX_STRING_TOKENS];
1449 	char arg2[MAX_STRING_TOKENS];
1450 	int mask = 0;
1451 
1452 	if ( !g_voteFlags.integer ) {
1453 		trap_SendServerCommand( ent - g_entities, "print \"Voting not enabled on this server.\n\"" );
1454 		return;
1455 	}
1456 
1457 	if ( level.voteTime ) {
1458 		trap_SendServerCommand( ent - g_entities, "print \"A vote is already in progress.\n\"" );
1459 		return;
1460 	}
1461 	if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) {
1462 		trap_SendServerCommand( ent - g_entities, "print \"You have called the maximum number of votes.\n\"" );
1463 		return;
1464 	}
1465 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1466 		trap_SendServerCommand( ent - g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
1467 		return;
1468 	}
1469 
1470 	// make sure it is a valid command to vote on
1471 	trap_Argv( 1, arg1, sizeof( arg1 ) );
1472 	trap_Argv( 2, arg2, sizeof( arg2 ) );
1473 
1474 	// check for command separators in arg2
1475 	for( c = arg2; *c; ++c) {
1476 		switch(*c) {
1477 			case '\n':
1478 			case '\r':
1479 			case ';':
1480 				trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
1481 				return;
1482 			break;
1483 		}
1484 	}
1485 
1486 	if ( !Q_stricmp( arg1, "map_restart" ) ) {
1487 		mask = VOTEFLAGS_RESTART;
1488 	} else if ( !Q_stricmp( arg1, "nextmap" ) ) {
1489 		mask = VOTEFLAGS_NEXTMAP;
1490 	} else if ( !Q_stricmp( arg1, "map" ) ) {
1491 		mask = VOTEFLAGS_MAP;
1492 	} else if ( !Q_stricmp( arg1, "g_gametype" ) ) {
1493 		mask = VOTEFLAGS_TYPE;
1494 	} else if ( !Q_stricmp( arg1, "kick" ) ) {
1495 		mask = VOTEFLAGS_KICK;
1496 	} else if ( !Q_stricmp( arg1, "clientkick" ) ) {
1497 		mask = VOTEFLAGS_KICK;
1498 	} else if ( !Q_stricmp( arg1, "start_match" ) ) {        // NERVE - SMF
1499 		mask = VOTEFLAGS_STARTMATCH;
1500 	} else if ( !Q_stricmp( arg1, "reset_match" ) ) {        // NERVE - SMF
1501 		mask = VOTEFLAGS_RESETMATCH;
1502 	} else if ( !Q_stricmp( arg1, "swap_teams" ) ) {     // NERVE - SMF
1503 		mask = VOTEFLAGS_SWAP;
1504 // JPW NERVE
1505 #ifndef PRE_RELEASE_DEMO
1506 	} else if ( !Q_stricmp( arg1, testid1 ) ) {
1507 	} else if ( !Q_stricmp( arg1, testid2 ) ) {
1508 	} else if ( !Q_stricmp( arg1, testid3 ) ) {
1509 #endif
1510 // jpw
1511 	} else {
1512 		trap_SendServerCommand( ent - g_entities, "print \"Invalid vote string.\n\"" );
1513 		trap_SendServerCommand( ent - g_entities, "print \"Vote commands are: map_restart, nextmap, start_match, swap_teams, reset_match, map <mapname>, g_gametype <n>, kick <player>, clientkick <clientnum>\n\"" );
1514 		return;
1515 	}
1516 
1517 	if ( !( g_voteFlags.integer & mask ) ) {
1518 		trap_SendServerCommand( ent - g_entities, va( "print \"Voting for %s disabled on this server\n\"", arg1 ) );
1519 		return;
1520 	}
1521 
1522 	// if there is still a vote to be executed
1523 	if ( level.voteExecuteTime ) {
1524 		// don't start a vote when map change or restart is in progress
1525 		if ( !Q_stricmpn( level.voteString, "map", 3 )
1526 			|| !Q_stricmpn( level.voteString, "nextmap", 7 ) ) {
1527 			trap_SendServerCommand( ent-g_entities, "print \"Vote after map change.\n\"" );
1528 			return;
1529 		}
1530 
1531 		level.voteExecuteTime = 0;
1532 		trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.voteString ) );
1533 	}
1534 
1535 	// special case for g_gametype, check for bad values
1536 	if ( !Q_stricmp( arg1, "g_gametype" ) ) {
1537 		i = atoi( arg2 );
1538 		if ( i < GT_WOLF || i >= GT_MAX_GAME_TYPE ) {
1539 			trap_SendServerCommand( ent - g_entities, "print \"Invalid gametype.\n\"" );
1540 			return;
1541 		}
1542 
1543 		Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %d", arg1, i );
1544 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, gameNames[i] );
1545 	} else if ( !Q_stricmp( arg1, "map_restart" ) ) {
1546 		// NERVE - SMF - do a warmup when we restart maps
1547 		if ( strlen( arg2 ) ) {
1548 			Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
1549 		} else {
1550 			Com_sprintf( level.voteString, sizeof( level.voteString ), "%s", arg1 );
1551 		}
1552 
1553 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
1554 	} else if ( !Q_stricmp( arg1, "map" ) ) {
1555 		// special case for map changes, we want to reset the nextmap setting
1556 		// this allows a player to change maps, but not upset the map rotation
1557 		char s[MAX_STRING_CHARS];
1558 
1559 		trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof( s ) );
1560 		if ( *s ) {
1561 			Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s );
1562 		} else {
1563 			Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
1564 		}
1565 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
1566 	} else if ( !Q_stricmp( arg1, "nextmap" ) ) {
1567 		char s[MAX_STRING_CHARS];
1568 
1569 		trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof( s ) );
1570 		if ( !*s ) {
1571 			trap_SendServerCommand( ent - g_entities, "print \"nextmap not set.\n\"" );
1572 			return;
1573 		}
1574 		Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap" );
1575 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
1576 	} else if ( !Q_stricmp( arg1, "clientkick" ) || !Q_stricmp( arg1, "kick" ) ) {
1577 		i = ClientNumberFromString( ent, arg2, !Q_stricmp( arg1, "clientkick" ), !Q_stricmp( arg1, "kick" ) );
1578 		if ( i == -1 ) {
1579 			// if it can't do a client number match, don't allow kick (to prevent votekick text spam wars)
1580 			trap_SendServerCommand( ent - g_entities, "print \"Client not on server.\n\"" );
1581 			return;
1582 		}
1583 
1584 		if ( level.clients[i].pers.localClient ) {
1585 			trap_SendServerCommand( ent - g_entities, "print \"Cannot kick host player.\n\"" );
1586 			return;
1587 		}
1588 
1589 		// found a client number to kick, so override votestring with better one
1590 		if ( i != MAX_CLIENTS ) {
1591 			Com_sprintf( level.voteString, sizeof( level.voteString ), "clientkick \"%d\"", i );
1592 			Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "kick %s", level.clients[i].pers.netname );
1593 		} else {
1594 			return;
1595 		}
1596 #if 0
1597 // JPW NERVE
1598 	} else if ( !Q_stricmp( arg1,"kick" ) ) {
1599 		int i, kicknum = MAX_CLIENTS;
1600 		char cleanName[64];    // JPW NERVE
1601 
1602 		for ( i = 0; i < MAX_CLIENTS; i++ ) {
1603 			if ( level.clients[i].pers.connected != CON_CONNECTED ) {
1604 				continue;
1605 			}
1606 
1607 			// strip the color crap out
1608 			Q_strncpyz( cleanName, level.clients[i].pers.netname, sizeof( cleanName ) );
1609 			Q_CleanStr( cleanName );
1610 			if ( !Q_stricmp( cleanName, arg2 ) ) {
1611 				kicknum = i;
1612 			}
1613 		}
1614 
1615 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "kick %s", level.clients[kicknum].pers.netname );
1616 
1617 		if ( kicknum != MAX_CLIENTS ) { // found a client # to kick, so override votestring with better one
1618 			Com_sprintf( level.voteString, sizeof( level.voteString ),"clientkick \"%d\"",kicknum );
1619 		} else { // if it can't do a name match, don't allow kick (to prevent votekick text spam wars)
1620 			trap_SendServerCommand( ent - g_entities, "print \"Client not on server.\n\"" );
1621 			return;
1622 		}
1623 // jpw
1624 #endif
1625 	} else {
1626 		Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
1627 		Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
1628 	}
1629 
1630 	trap_SendServerCommand( -1, va( "print \"[lof]%s [lon]called a vote.\n\"", ent->client->pers.netname ) );
1631 
1632 	// start the voting, the caller automatically votes yes
1633 	level.voteTime = level.time;
1634 	level.voteYes = 1;
1635 	level.voteNo = 0;
1636 	ent->client->pers.voteCount++;
1637 
1638 	for ( i = 0 ; i < level.maxclients ; i++ ) {
1639 		level.clients[i].ps.eFlags &= ~EF_VOTED;
1640 	}
1641 	ent->client->ps.eFlags |= EF_VOTED;
1642 
1643 	trap_SetConfigstring( CS_VOTE_TIME, va( "%i", level.voteTime ) );
1644 	trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString );
1645 	trap_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteYes ) );
1646 	trap_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteNo ) );
1647 }
1648 
1649 /*
1650 ==================
1651 Cmd_Vote_f
1652 ==================
1653 */
Cmd_Vote_f(gentity_t * ent)1654 void Cmd_Vote_f( gentity_t *ent ) {
1655 	char msg[64];
1656 	int num;
1657 
1658 	// DHM - Nerve :: Complaints supercede voting (and share command)
1659 	if ( ent->client->pers.complaintEndTime > level.time ) {
1660 
1661 		gclient_t *cl = g_entities[ ent->client->pers.complaintClient ].client;
1662 		if ( !cl ) {
1663 			return;
1664 		}
1665 		if ( cl->pers.connected != CON_CONNECTED ) {
1666 			return;
1667 		}
1668 		if ( cl->pers.localClient ) {
1669 			trap_SendServerCommand( ent - g_entities, "complaint -3" );
1670 			return;
1671 		}
1672 
1673 		// Reset this ent's complainEndTime so they can't send multiple complaints
1674 		ent->client->pers.complaintEndTime = -1;
1675 		ent->client->pers.complaintClient = -1;
1676 
1677 		trap_Argv( 1, msg, sizeof( msg ) );
1678 
1679 		if ( tolower( msg[0] ) == 'y' || msg[0] == '1' ) {
1680 			// Increase their complaint counter
1681 			cl->pers.complaints++;
1682 
1683 			num = g_complaintlimit.integer - cl->pers.complaints;
1684 
1685 			if ( num <= 0 && !cl->pers.localClient ) {
1686 				trap_DropClient( cl - level.clients, "kicked after too many complaints." );
1687 				trap_SendServerCommand( ent - g_entities, "complaint -1" );
1688 				return;
1689 			}
1690 
1691 			trap_SendServerCommand( cl->ps.clientNum, va( "print \"^1Warning^7: Complaint filed against you. [lof](%d [lon]until kicked[lof])\n\"", num ) );
1692 			trap_SendServerCommand( ent - g_entities, "complaint -1" );
1693 		} else {
1694 			trap_SendServerCommand( ent - g_entities, "complaint -2" );
1695 		}
1696 
1697 		return;
1698 	}
1699 	// dhm
1700 
1701 	// Reset this ent's complainEndTime so they can't send multiple complaints
1702 	ent->client->pers.complaintEndTime = -1;
1703 	ent->client->pers.complaintClient = -1;
1704 
1705 	if ( !level.voteTime ) {
1706 		trap_SendServerCommand( ent - g_entities, "print \"No vote in progress.\n\"" );
1707 		return;
1708 	}
1709 	if ( ent->client->ps.eFlags & EF_VOTED ) {
1710 		trap_SendServerCommand( ent - g_entities, "print \"Vote already cast.\n\"" );
1711 		return;
1712 	}
1713 	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1714 		trap_SendServerCommand( ent - g_entities, "print \"Not allowed to vote as spectator.\n\"" );
1715 		return;
1716 	}
1717 
1718 	trap_SendServerCommand( ent - g_entities, "print \"Vote cast.\n\"" );
1719 
1720 	ent->client->ps.eFlags |= EF_VOTED;
1721 
1722 	trap_Argv( 1, msg, sizeof( msg ) );
1723 
1724 	if ( tolower( msg[0] ) == 'y' || msg[0] == '1' ) {
1725 		level.voteYes++;
1726 		trap_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteYes ) );
1727 	} else {
1728 		level.voteNo++;
1729 		trap_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteNo ) );
1730 	}
1731 
1732 	// a majority will be determined in G_CheckVote, which will also account
1733 	// for players entering or leaving
1734 }
1735 
1736 
G_canPickupMelee(gentity_t * ent)1737 qboolean G_canPickupMelee( gentity_t *ent ) {
1738 
1739 // JPW NERVE -- no "melee" weapons in net play
1740 	if ( g_gametype.integer >= GT_WOLF ) {
1741 		return qfalse;
1742 	}
1743 // jpw
1744 
1745 	if ( !( ent->client ) ) {
1746 		return qfalse;  // hmm, shouldn't be too likely...
1747 
1748 	}
1749 	if ( !( ent->s.weapon ) ) {  // no weap, go ahead
1750 		return qtrue;
1751 	}
1752 
1753 	if ( ent->client->ps.weaponstate == WEAPON_RELOADING ) {
1754 		return qfalse;
1755 	}
1756 
1757 	if ( WEAPS_ONE_HANDED & ( 1 << ( ent->s.weapon ) ) ) {
1758 		return qtrue;
1759 	}
1760 
1761 	return qfalse;
1762 }
1763 
1764 
1765 
1766 
1767 /*
1768 =================
1769 Cmd_SetViewpos_f
1770 =================
1771 */
Cmd_SetViewpos_f(gentity_t * ent)1772 void Cmd_SetViewpos_f( gentity_t *ent ) {
1773 	vec3_t origin, angles;
1774 	char buffer[MAX_TOKEN_CHARS];
1775 	int i;
1776 
1777 	if ( !g_cheats.integer ) {
1778 		trap_SendServerCommand( ent-g_entities, "print \"Cheats are not enabled on this server.\n\"");
1779 		return;
1780 	}
1781 	if ( trap_Argc() != 5 ) {
1782 		trap_SendServerCommand( ent-g_entities, "print \"usage: setviewpos x y z yaw\n\"");
1783 		return;
1784 	}
1785 
1786 	VectorClear( angles );
1787 	for ( i = 0 ; i < 3 ; i++ ) {
1788 		trap_Argv( i + 1, buffer, sizeof( buffer ) );
1789 		origin[i] = atof( buffer );
1790 	}
1791 
1792 	trap_Argv( 4, buffer, sizeof( buffer ) );
1793 	angles[YAW] = atof( buffer );
1794 
1795 	TeleportPlayer( ent, origin, angles );
1796 }
1797 
1798 /*
1799 =================
1800 Cmd_StartCamera_f
1801 =================
1802 */
Cmd_StartCamera_f(gentity_t * ent)1803 void Cmd_StartCamera_f( gentity_t *ent ) {
1804 
1805 	if ( !CheatsOk( ent ) ) {
1806 		return;
1807 	}
1808 
1809 	g_camEnt->r.svFlags |= SVF_PORTAL;
1810 	g_camEnt->r.svFlags &= ~SVF_NOCLIENT;
1811 	ent->client->cameraPortal = g_camEnt;
1812 	ent->client->ps.eFlags |= EF_VIEWING_CAMERA;
1813 	ent->s.eFlags |= EF_VIEWING_CAMERA;
1814 }
1815 
1816 /*
1817 =================
1818 Cmd_StopCamera_f
1819 =================
1820 */
Cmd_StopCamera_f(gentity_t * ent)1821 void Cmd_StopCamera_f( gentity_t *ent ) {
1822 
1823 	if ( !CheatsOk( ent ) ) {
1824 		return;
1825 	}
1826 
1827 	if ( ent->client->cameraPortal ) {
1828 		// send a script event
1829 		G_Script_ScriptEvent( ent->client->cameraPortal, "stopcam", "" );
1830 		// go back into noclient mode
1831 		ent->client->cameraPortal->r.svFlags |= SVF_NOCLIENT;
1832 		ent->client->cameraPortal = NULL;
1833 		ent->s.eFlags &= ~EF_VIEWING_CAMERA;
1834 		ent->client->ps.eFlags &= ~EF_VIEWING_CAMERA;
1835 	}
1836 }
1837 
1838 /*
1839 =================
1840 Cmd_SetCameraOrigin_f
1841 =================
1842 */
Cmd_SetCameraOrigin_f(gentity_t * ent)1843 void Cmd_SetCameraOrigin_f( gentity_t *ent ) {
1844 	char buffer[MAX_TOKEN_CHARS];
1845 	int i;
1846 
1847 	if ( trap_Argc() != 4 ) {
1848 		return;
1849 	}
1850 
1851 	VectorClear( ent->client->cameraOrigin );
1852 	for ( i = 0 ; i < 3 ; i++ ) {
1853 		trap_Argv( i + 1, buffer, sizeof( buffer ) );
1854 		ent->client->cameraOrigin[i] = atof( buffer );
1855 	}
1856 }
1857 
1858 
1859 // Rafael
1860 /*
1861 ==================
1862 Cmd_Activate_f
1863 ==================
1864 */
Cmd_Activate_f(gentity_t * ent)1865 void Cmd_Activate_f( gentity_t *ent ) {
1866 	trace_t tr;
1867 	vec3_t end;
1868 	gentity_t   *traceEnt;
1869 	vec3_t forward, right, up, offset;
1870 	static int oldactivatetime = 0;
1871 	int activatetime = level.time;
1872 	qboolean walking = qfalse;
1873 
1874 	if ( ent->active ) {
1875 		if ( ent->client->ps.persistant[PERS_HWEAPON_USE] ) {
1876 			// DHM - Nerve :: Restore original position if current position is bad
1877 			trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ent->s.number, MASK_PLAYERSOLID );
1878 			if ( tr.startsolid ) {
1879 				VectorCopy( ent->TargetAngles, ent->client->ps.origin );
1880 				VectorCopy( ent->TargetAngles, ent->r.currentOrigin );
1881 				ent->r.contents = CONTENTS_CORPSE;      // DHM - this will correct itself in ClientEndFrame
1882 			}
1883 			ent->client->ps.eFlags &= ~EF_MG42_ACTIVE;          // DHM - Nerve :: unset flag
1884 			ent->client->ps.persistant[PERS_HWEAPON_USE] = 0;
1885 			ent->active = qfalse;
1886 			return;
1887 		} else
1888 		{
1889 			ent->active = qfalse;
1890 		}
1891 	}
1892 
1893 	if ( ent->client->pers.cmd.buttons & BUTTON_WALKING ) {
1894 		walking = qtrue;
1895 	}
1896 
1897 	AngleVectors( ent->client->ps.viewangles, forward, right, up );
1898 	CalcMuzzlePointForActivate( ent, forward, right, up, offset );
1899 	VectorMA( offset, 96, forward, end );
1900 
1901 	trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) );
1902 
1903 	if ( tr.surfaceFlags & SURF_NOIMPACT ) {
1904 		return;
1905 	}
1906 
1907 	if ( tr.entityNum == ENTITYNUM_WORLD ) {
1908 		return;
1909 	}
1910 
1911 	traceEnt = &g_entities[ tr.entityNum ];
1912 
1913 	if ( traceEnt->classname ) {
1914 		traceEnt->flags &= ~FL_SOFTACTIVATE;    // FL_SOFTACTIVATE will be set if the user is holding 'walk' key
1915 
1916 		if ( traceEnt->s.eType == ET_ALARMBOX ) {
1917 			trace_t trace;
1918 
1919 			if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1920 				return;
1921 			}
1922 
1923 			memset( &trace, 0, sizeof( trace ) );
1924 
1925 			if ( traceEnt->use ) {
1926 				traceEnt->use( traceEnt, ent, 0 );
1927 			}
1928 		} else if ( traceEnt->s.eType == ET_ITEM )     {
1929 			trace_t trace;
1930 
1931 			if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
1932 				return;
1933 			}
1934 
1935 			memset( &trace, 0, sizeof( trace ) );
1936 
1937 			if ( traceEnt->touch ) {
1938 				if ( ent->client->pers.autoActivate == PICKUP_ACTIVATE ) {
1939 					ent->client->pers.autoActivate = PICKUP_FORCE;      //----(SA) force pickup
1940 				}
1941 				traceEnt->active = qtrue;
1942 				traceEnt->touch( traceEnt, ent, &trace );
1943 			}
1944 
1945 		} else if ( ( Q_stricmp( traceEnt->classname, "misc_mg42" ) == 0 ) && traceEnt->active == qfalse )         {
1946 			if (
1947 				( traceEnt->r.currentOrigin[2] - ent->r.currentOrigin[2] < 40 ) &&
1948 				( traceEnt->r.currentOrigin[2] - ent->r.currentOrigin[2] > 0 ) &&
1949 				!infront( traceEnt, ent ) ) {
1950 				//----(SA)	make sure the client isn't holding a hot potato
1951 				gclient_t   *cl;
1952 				cl = &level.clients[ ent->s.clientNum ];
1953 
1954 				if ( !( cl->ps.grenadeTimeLeft ) && !( cl->ps.pm_flags & PMF_DUCKED )
1955 					 && !( traceEnt->s.eFlags & EF_SMOKING ) && ( cl->ps.weapon != WP_SNIPERRIFLE ) ) { // JPW NERVE no mg42 while scoped in
1956 					// DHM - Remember initial gun position to restore later
1957 					vec3_t point;
1958 
1959 					AngleVectors( traceEnt->s.apos.trBase, forward, NULL, NULL );
1960 					VectorMA( traceEnt->r.currentOrigin, -36, forward, point );
1961 					point[2] = ent->r.currentOrigin[2];
1962 
1963 					// Save initial position
1964 					VectorCopy( point, ent->TargetAngles );
1965 
1966 					// Zero out velocity
1967 					VectorCopy( vec3_origin, ent->client->ps.velocity );
1968 					VectorCopy( vec3_origin, ent->s.pos.trDelta );
1969 
1970 					traceEnt->active = qtrue;
1971 					ent->active = qtrue;
1972 					traceEnt->r.ownerNum = ent->s.number;
1973 					VectorCopy( traceEnt->s.angles, traceEnt->TargetAngles );
1974 					traceEnt->s.otherEntityNum = ent->s.number;
1975 
1976 					cl->pmext.harc = traceEnt->harc;
1977 					cl->pmext.varc = traceEnt->varc;
1978 					VectorCopy( traceEnt->s.angles, cl->pmext.centerangles );
1979 					cl->pmext.centerangles[PITCH] = AngleNormalize180( cl->pmext.centerangles[PITCH] );
1980 					cl->pmext.centerangles[YAW] = AngleNormalize180( cl->pmext.centerangles[YAW] );
1981 					cl->pmext.centerangles[ROLL] = AngleNormalize180( cl->pmext.centerangles[ROLL] );
1982 
1983 					if ( !( ent->r.svFlags & SVF_CASTAI ) ) {
1984 						G_UseTargets( traceEnt, ent );   //----(SA)	added for Mike so mounting an MG42 can be a trigger event (let me know if there's any issues with this)
1985 					}
1986 				}
1987 			}
1988 		} else if ( ( ( Q_stricmp( traceEnt->classname, "func_door" ) == 0 ) || ( Q_stricmp( traceEnt->classname, "func_door_rotating" ) == 0 ) ) )           {
1989 			if ( walking ) {
1990 				traceEnt->flags |= FL_SOFTACTIVATE;     // no noise
1991 			}
1992 			G_TryDoor( traceEnt, ent, ent );      // (door,other,activator)
1993 		} else if ( ( Q_stricmp( traceEnt->classname, "team_WOLF_checkpoint" ) == 0 ) )       {
1994 			if ( traceEnt->count != ent->client->sess.sessionTeam ) {
1995 				traceEnt->health++;
1996 			}
1997 		} else if ( ( Q_stricmp( traceEnt->classname, "func_button" ) == 0 )
1998 					&& ( traceEnt->s.apos.trType == TR_STATIONARY && traceEnt->s.pos.trType == TR_STATIONARY )
1999 					&& traceEnt->active == qfalse ) {
2000 			Use_BinaryMover( traceEnt, ent, ent );
2001 			traceEnt->active = qtrue;
2002 		} else if ( !Q_stricmp( traceEnt->classname, "func_invisible_user" ) )     {
2003 			if ( walking ) {
2004 				traceEnt->flags |= FL_SOFTACTIVATE;     // no noise
2005 			}
2006 			traceEnt->use( traceEnt, ent, ent );
2007 		} else if ( !Q_stricmp( traceEnt->classname, "script_mover" ) )     {
2008 			G_Script_ScriptEvent( traceEnt, "activate", ent->aiName );
2009 		} else if ( !Q_stricmp( traceEnt->classname, "props_footlocker" ) )     {
2010 			traceEnt->use( traceEnt, ent, ent );
2011 		}
2012 	}
2013 
2014 	if ( activatetime > oldactivatetime + 1000 ) {
2015 		oldactivatetime = activatetime;
2016 	}
2017 }
2018 
2019 // Rafael WolfKick
2020 //===================
2021 //	Cmd_WolfKick
2022 //===================
2023 
2024 #define WOLFKICKDISTANCE    96
Cmd_WolfKick_f(gentity_t * ent)2025 int Cmd_WolfKick_f( gentity_t *ent ) {
2026 	trace_t tr;
2027 	vec3_t end;
2028 	gentity_t   *traceEnt;
2029 	vec3_t forward, right, up, offset;
2030 	gentity_t   *tent;
2031 	static int oldkicktime = 0;
2032 	int kicktime = level.time;
2033 	qboolean solidKick = qfalse;    // don't play "hit" sound on a trigger unless it's an func_invisible_user
2034 
2035 	int damage = 15;
2036 
2037 	// DHM - Nerve :: No kick in wolf multiplayer
2038 	if ( g_gametype.integer >= GT_WOLF ) {
2039 		return 0;
2040 	}
2041 
2042 	if ( ent->client->ps.leanf ) {
2043 		return 0;   // no kick when leaning
2044 
2045 	}
2046 	if ( oldkicktime > kicktime ) {
2047 		return ( 0 );
2048 	} else {
2049 		oldkicktime = kicktime + 1000;
2050 	}
2051 
2052 	// play the anim
2053 	BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_KICK, qfalse, qtrue );
2054 
2055 	ent->client->ps.persistant[PERS_WOLFKICK] = 1;
2056 
2057 	AngleVectors( ent->client->ps.viewangles, forward, right, up );
2058 
2059 	CalcMuzzlePointForActivate( ent, forward, right, up, offset );
2060 
2061 	// note to self: we need to determine the usable distance for wolf
2062 	VectorMA( offset, WOLFKICKDISTANCE, forward, end );
2063 
2064 	trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) );
2065 
2066 	if ( tr.surfaceFlags & SURF_NOIMPACT || tr.fraction == 1.0 ) {
2067 		tent = G_TempEntity( tr.endpos, EV_WOLFKICK_MISS );
2068 		tent->s.eventParm = ent->s.number;
2069 		return ( 1 );
2070 	}
2071 
2072 	traceEnt = &g_entities[ tr.entityNum ];
2073 
2074 	if ( !ent->melee ) { // because we dont want you to open a door with a prop
2075 		if ( ( Q_stricmp( traceEnt->classname, "func_door_rotating" ) == 0 )
2076 			 && ( traceEnt->s.apos.trType == TR_STATIONARY && traceEnt->s.pos.trType == TR_STATIONARY )
2077 			 && traceEnt->active == qfalse ) {
2078 			if ( traceEnt->key < 0 ) { // door force locked
2079 				//----(SA)	play kick "hit" sound
2080 				tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_WALL );
2081 				tent->s.otherEntityNum = ent->s.number;	\
2082 				//----(SA)	end
2083 
2084 				AICast_AudibleEvent( ent->s.clientNum, tr.endpos, HEAR_RANGE_DOOR_KICKLOCKED ); // "someone kicked a locked door near me!"
2085 
2086 				if ( traceEnt->soundPos3 ) {
2087 					G_AddEvent( traceEnt, EV_GENERAL_SOUND, traceEnt->soundPos3 );
2088 				} else {
2089 					G_AddEvent( traceEnt, EV_GENERAL_SOUND, G_SoundIndex( "sound/movers/doors/default_door_locked.wav" ) );
2090 				}
2091 				return 1;   //----(SA)	changed.  shows boot for locked doors
2092 			}
2093 
2094 			if ( traceEnt->key > 0 ) { // door requires key
2095 				gitem_t *item = BG_FindItemForKey( traceEnt->key, 0 );
2096 				if ( !( ent->client->ps.stats[STAT_KEYS] & ( 1 << item->giTag ) ) ) {
2097 					//----(SA)	play kick "hit" sound
2098 					tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_WALL );
2099 					tent->s.otherEntityNum = ent->s.number;	\
2100 					//----(SA)	end
2101 
2102 					AICast_AudibleEvent( ent->s.clientNum, tr.endpos, HEAR_RANGE_DOOR_KICKLOCKED ); // "someone kicked a locked door near me!"
2103 
2104 					// player does not have key
2105 					if ( traceEnt->soundPos3 ) {
2106 						G_AddEvent( traceEnt, EV_GENERAL_SOUND, traceEnt->soundPos3 );
2107 					} else {
2108 						G_AddEvent( traceEnt, EV_GENERAL_SOUND, G_SoundIndex( "sound/movers/doors/default_door_locked.wav" ) );
2109 					}
2110 					return 1;   //----(SA)	changed.  shows boot animation for locked doors
2111 				}
2112 			}
2113 
2114 			if ( traceEnt->teammaster && traceEnt->team && traceEnt != traceEnt->teammaster ) {
2115 				traceEnt->teammaster->active = qtrue;
2116 				traceEnt->teammaster->flags |= FL_KICKACTIVATE;
2117 				Use_BinaryMover( traceEnt->teammaster, ent, ent );
2118 				G_UseTargets( traceEnt->teammaster, ent );
2119 			} else
2120 			{
2121 				traceEnt->active = qtrue;
2122 				traceEnt->flags |= FL_KICKACTIVATE;
2123 				Use_BinaryMover( traceEnt, ent, ent );
2124 				G_UseTargets( traceEnt, ent );
2125 			}
2126 		} else if ( ( Q_stricmp( traceEnt->classname, "func_button" ) == 0 )
2127 					&& ( traceEnt->s.apos.trType == TR_STATIONARY && traceEnt->s.pos.trType == TR_STATIONARY )
2128 					&& traceEnt->active == qfalse ) {
2129 			Use_BinaryMover( traceEnt, ent, ent );
2130 			traceEnt->active = qtrue;
2131 
2132 		} else if ( !Q_stricmp( traceEnt->classname, "func_invisible_user" ) )     {
2133 			traceEnt->flags |= FL_KICKACTIVATE;     // so cell doors know they were kicked
2134 													// It doesn't hurt to pass this along since only ent use() funcs who care about it will check.
2135 													// However, it may become handy to put a "KICKABLE" or "NOTKICKABLE" flag on the invisible_user
2136 			traceEnt->use( traceEnt, ent, ent );
2137 			traceEnt->flags &= ~FL_KICKACTIVATE;    // reset
2138 
2139 			solidKick = qtrue;  //----(SA)
2140 		} else if ( !Q_stricmp( traceEnt->classname, "props_flippy_table" ) && traceEnt->use )       {
2141 			traceEnt->use( traceEnt, ent, ent );
2142 		}
2143 	}
2144 
2145 	// snap the endpos to integers, but nudged towards the line
2146 	SnapVectorTowards( tr.endpos, offset );
2147 
2148 	// send bullet impact
2149 	if ( traceEnt->takedamage && traceEnt->client ) {
2150 		tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_FLESH );
2151 		tent->s.eventParm = traceEnt->s.number;
2152 		if ( LogAccuracyHit( traceEnt, ent ) ) {
2153 			ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
2154 		}
2155 	} else {
2156 		// Ridah, bullet impact should reflect off surface
2157 		vec3_t reflect;
2158 		float dot;
2159 
2160 		if ( traceEnt->r.contents >= 0 && ( traceEnt->r.contents & CONTENTS_TRIGGER ) && !solidKick ) {
2161 			tent = G_TempEntity( tr.endpos, EV_WOLFKICK_MISS ); // (SA) don't play the "hit" sound if you kick most triggers
2162 		} else {
2163 			tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_WALL );
2164 		}
2165 
2166 
2167 		dot = DotProduct( forward, tr.plane.normal );
2168 		VectorMA( forward, -2 * dot, tr.plane.normal, reflect );
2169 		VectorNormalize( reflect );
2170 
2171 		tent->s.eventParm = DirToByte( reflect );
2172 		// done.
2173 
2174 		if ( ent->melee ) {
2175 			ent->active = qfalse;
2176 			ent->melee->health = 0;
2177 		}
2178 	}
2179 
2180 	tent->s.otherEntityNum = ent->s.number;
2181 
2182 	// try to swing chair
2183 	if ( traceEnt->takedamage ) {
2184 
2185 		if ( ent->melee ) {
2186 			ent->active = qfalse;
2187 			ent->melee->health = 0;
2188 			ent->client->ps.eFlags &= ~EF_MELEE_ACTIVE;
2189 
2190 		}
2191 
2192 		G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_KICKED );   //----(SA)	modified
2193 	}
2194 
2195 	return ( 1 );
2196 }
2197 // done
2198 
2199 /*
2200 ============================
2201 Cmd_ClientMonsterSlickAngle
2202 ============================
2203 */
2204 /*
2205 void Cmd_ClientMonsterSlickAngle (gentity_t *clent) {
2206 
2207 	char s[MAX_STRING_CHARS];
2208 	int	entnum;
2209 	int angle;
2210 	gentity_t *ent;
2211 	vec3_t	dir, kvel;
2212 	vec3_t	forward;
2213 
2214 	if (trap_Argc() != 3) {
2215 		G_Printf( "ClientDamage command issued with incorrect number of args\n" );
2216 	}
2217 
2218 	trap_Argv( 1, s, sizeof( s ) );
2219 	entnum = atoi(s);
2220 	ent = &g_entities[entnum];
2221 
2222 	trap_Argv( 2, s, sizeof( s ) );
2223 	angle = atoi(s);
2224 
2225 	// sanity check (also protect from cheaters)
2226 	if (g_gametype.integer != GT_SINGLE_PLAYER && entnum != clent->s.number) {
2227 		trap_DropClient( clent->s.number, "Dropped due to illegal ClientMonsterSlick command\n" );
2228 		return;
2229 	}
2230 
2231 	VectorClear (dir);
2232 	dir[YAW] = angle;
2233 	AngleVectors (dir, forward, NULL, NULL);
2234 
2235 	VectorScale (forward, 32, kvel);
2236 	VectorAdd (ent->client->ps.velocity, kvel, ent->client->ps.velocity);
2237 }
2238 */
2239 
2240 // NERVE - SMF
2241 /*
2242 ============
2243 ClientDamage
2244 ============
2245 */
ClientDamage(gentity_t * clent,int entnum,int enemynum,int id)2246 void ClientDamage( gentity_t *clent, int entnum, int enemynum, int id ) {
2247 	gentity_t *enemy, *ent;
2248 	vec3_t vec;
2249 
2250 	ent = &g_entities[entnum];
2251 
2252 	enemy = &g_entities[enemynum];
2253 
2254 	// NERVE - SMF - took this out, this is causing more problems than its helping
2255 	//  Either a new way has to be found, or this check needs to change.
2256 	// sanity check (also protect from cheaters)
2257 //	if (g_gametype.integer != GT_SINGLE_PLAYER && entnum != clent->s.number) {
2258 //		trap_DropClient( clent->s.number, "Dropped due to illegal ClientDamage command\n" );
2259 //		return;
2260 //	}
2261 	// -NERVE - SMF
2262 
2263 	// if the attacker can't see the target, then don't allow damage
2264 	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
2265 		// TTimo it can happen that enemy->client == NULL
2266 		// see Changelog 09/22/2001
2267 		if ( ( enemy->client ) && ( !CanDamage( ent, enemy->client->ps.origin ) ) ) {
2268 			return; // don't allow damage
2269 		}
2270 	}
2271 
2272 	switch ( id ) {
2273 	case CLDMG_SPIRIT:
2274 		if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
2275 			G_Damage( ent, enemy, enemy, vec3_origin, vec3_origin, 3, DAMAGE_NO_KNOCKBACK, MOD_ZOMBIESPIRIT );
2276 		}
2277 		break;
2278 	case CLDMG_BOSS1LIGHTNING:
2279 		if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
2280 			break;
2281 		}
2282 		if ( ent->takedamage ) {
2283 			VectorSubtract( ent->r.currentOrigin, enemy->r.currentOrigin, vec );
2284 			VectorNormalize( vec );
2285 			G_Damage( ent, enemy, enemy, vec, ent->r.currentOrigin, 6 + rand() % 3, 0, MOD_LIGHTNING );
2286 		}
2287 		break;
2288 	case CLDMG_TESLA:
2289 		// do some cheat protection
2290 		if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
2291 			if ( enemy->s.weapon != WP_TESLA ) {
2292 				break;
2293 			}
2294 			if ( enemy->client && !( enemy->client->buttons & BUTTON_ATTACK ) ) {
2295 				break;
2296 			}
2297 			//if ( AICast_GetCastState( enemy->s.number )->lastWeaponFiredWeaponNum != WP_TESLA )
2298 			//	break;
2299 			//if ( AICast_GetCastState( enemy->s.number )->lastWeaponFired < level.time - 400 )
2300 			//	break;
2301 		}
2302 
2303 		if (    ( ent->aiCharacter == AICHAR_PROTOSOLDIER ) ||
2304 				( ent->aiCharacter == AICHAR_SUPERSOLDIER ) ||
2305 				( ent->aiCharacter == AICHAR_LOPER ) ||
2306 				( ent->aiCharacter >= AICHAR_STIMSOLDIER1 && ent->aiCharacter <= AICHAR_STIMSOLDIER3 ) ) {
2307 			break;
2308 		}
2309 
2310 		if ( ent->takedamage /*&& !AICast_NoFlameDamage(ent->s.number)*/ ) {
2311 			VectorSubtract( ent->r.currentOrigin, enemy->r.currentOrigin, vec );
2312 			VectorNormalize( vec );
2313 			G_Damage( ent, enemy, enemy, vec, ent->r.currentOrigin, 3, 0, MOD_LIGHTNING );
2314 		}
2315 		break;
2316 	case CLDMG_FLAMETHROWER:
2317 		// do some cheat protection
2318 /*  JPW NERVE pulled flamethrower client damage completely
2319 		if (g_gametype.integer != GT_SINGLE_PLAYER) {
2320 			if ( enemy->s.weapon != WP_FLAMETHROWER )
2321 				break;
2322 //			if ( !(enemy->client->buttons & BUTTON_ATTACK) ) // JPW NERVE flames should be able to damage while puffs are active
2323 //				break;
2324 		} else {
2325 			// this is required for Zombie flame attack
2326 			//if ((enemy->aiCharacter == AICHAR_ZOMBIE) && !AICast_VisibleFromPos( enemy->r.currentOrigin, enemy->s.number, ent->r.currentOrigin, ent->s.number, qfalse ))
2327 			//	break;
2328 		}
2329 
2330 		if ( ent->takedamage && !AICast_NoFlameDamage(ent->s.number) ) {
2331 			#define	FLAME_THRESHOLD	50
2332 			int damage = 5;
2333 
2334 			// RF, only do damage once they start burning
2335 			//if (ent->health > 0)	// don't explode from flamethrower
2336 			//	G_Damage( traceEnt, ent, ent, forward, tr.endpos, 1, 0, MOD_LIGHTNING);
2337 
2338 			// now check the damageQuota to see if we should play a pain animation
2339 			// first reduce the current damageQuota with time
2340 			if (ent->flameQuotaTime && ent->flameQuota > 0) {
2341 				ent->flameQuota -= (int)(((float)(level.time - ent->flameQuotaTime)/1000) * (float)damage/2.0);
2342 				if (ent->flameQuota < 0)
2343 					ent->flameQuota = 0;
2344 			}
2345 
2346 			// add the new damage
2347 			ent->flameQuota += damage;
2348 			ent->flameQuotaTime = level.time;
2349 
2350 			// Ridah, make em burn
2351 			if (ent->client && ( !(ent->r.svFlags & SVF_CASTAI) || ent->health <= 0 || ent->flameQuota > FLAME_THRESHOLD)) {				if (ent->s.onFireEnd < level.time)
2352 					ent->s.onFireStart = level.time;
2353 				if (ent->health <= 0 || !(ent->r.svFlags & SVF_CASTAI) || (g_gametype.integer != GT_SINGLE_PLAYER)) {
2354 					if (ent->r.svFlags & SVF_CASTAI) {
2355 						ent->s.onFireEnd = level.time + 6000;
2356 					} else {
2357 						ent->s.onFireEnd = level.time + FIRE_FLASH_TIME;
2358 					}
2359 				} else {
2360 					ent->s.onFireEnd = level.time + 99999;	// make sure it goes for longer than they need to die
2361 				}
2362 				ent->flameBurnEnt = enemy->s.number;
2363 				// add to playerState for client-side effect
2364 				ent->client->ps.onFireStart = level.time;
2365 			}
2366 		}
2367 */
2368 		break;
2369 	}
2370 }
2371 // -NERVE - SMF
2372 
2373 /*
2374 ============
2375 Cmd_ClientDamage_f
2376 ============
2377 */
Cmd_ClientDamage_f(gentity_t * clent)2378 void Cmd_ClientDamage_f( gentity_t *clent ) {
2379 	char s[MAX_STRING_CHARS];
2380 	int entnum, id, enemynum;
2381 
2382 	if ( trap_Argc() != 4 ) {
2383 		G_Printf( "ClientDamage command issued with incorrect number of args\n" );
2384 	}
2385 
2386 	trap_Argv( 1, s, sizeof( s ) );
2387 	entnum = atoi( s );
2388 
2389 	trap_Argv( 2, s, sizeof( s ) );
2390 	enemynum = atoi( s );
2391 
2392 	trap_Argv( 3, s, sizeof( s ) );
2393 	id = atoi( s );
2394 
2395 	ClientDamage( clent, entnum, enemynum, id );
2396 }
2397 
2398 /*
2399 ==============
2400 Cmd_EntityCount_f
2401 ==============
2402 */
2403 #define AITEAM_NAZI     0
2404 #define AITEAM_ALLIES   1
2405 #define AITEAM_MONSTER  2
Cmd_EntityCount_f(gentity_t * ent)2406 void Cmd_EntityCount_f( gentity_t *ent ) {
2407 	if ( !g_cheats.integer ) {
2408 		return;
2409 	}
2410 
2411 	G_Printf( "entity count = %i\n", level.num_entities );
2412 
2413 	{
2414 		int kills[2];
2415 		int nazis[2];
2416 		int monsters[2];
2417 		int i;
2418 		gentity_t *ent;
2419 
2420 		// count kills
2421 		kills[0] = kills[1] = 0;
2422 		nazis[0] = nazis[1] = 0;
2423 		monsters[0] = monsters[1] = 0;
2424 		for ( i = 0; i < MAX_CLIENTS; i++ ) {
2425 			ent = &g_entities[i];
2426 
2427 			if ( !ent->inuse ) {
2428 				continue;
2429 			}
2430 
2431 			if ( !( ent->r.svFlags & SVF_CASTAI ) ) {
2432 				continue;
2433 			}
2434 
2435 			if ( ent->aiTeam == AITEAM_ALLIES ) {
2436 				continue;
2437 			}
2438 
2439 			kills[1]++;
2440 
2441 			if ( ent->health <= 0 ) {
2442 				kills[0]++;
2443 			}
2444 
2445 			if ( ent->aiTeam == AITEAM_NAZI ) {
2446 				nazis[1]++;
2447 				if ( ent->health <= 0 ) {
2448 					nazis[0]++;
2449 				}
2450 			} else {
2451 				monsters[1]++;
2452 				if ( ent->health <= 0 ) {
2453 					monsters[0]++;
2454 				}
2455 			}
2456 		}
2457 		G_Printf( "kills %i/%i nazis %i/%i monsters %i/%i \n",kills[0], kills[1], nazis[0], nazis[1], monsters[0], monsters[1] );
2458 
2459 	}
2460 }
2461 
2462 // NERVE - SMF
2463 /*
2464 ============
2465 Cmd_SetSpawnPoint_f
2466 ============
2467 */
Cmd_SetSpawnPoint_f(gentity_t * clent)2468 void Cmd_SetSpawnPoint_f( gentity_t *clent ) {
2469 	char arg[MAX_TOKEN_CHARS];
2470 //	int		spawnIndex;
2471 
2472 	if ( trap_Argc() != 2 ) {
2473 		return;
2474 	}
2475 
2476 	trap_Argv( 1, arg, sizeof( arg ) );
2477 	if ( clent->client ) { // JPW NERVE
2478 		clent->client->sess.spawnObjectiveIndex = atoi( arg ); // JPW NERVE
2479 	}
2480 }
2481 // -NERVE - SMF
2482 
2483 /*
2484 =================
2485 ClientCommand
2486 =================
2487 */
ClientCommand(int clientNum)2488 void ClientCommand( int clientNum ) {
2489 	gentity_t *ent;
2490 	char cmd[MAX_TOKEN_CHARS];
2491 
2492 	ent = g_entities + clientNum;
2493 	if (!ent->client || ent->client->pers.connected != CON_CONNECTED) {
2494 		return;     // not fully in game yet
2495 	}
2496 
2497 	trap_Argv( 0, cmd, sizeof( cmd ) );
2498 
2499 	if ( Q_stricmp( cmd, "say" ) == 0 ) {
2500 		Cmd_Say_f( ent, SAY_ALL, qfalse );
2501 		return;
2502 	}
2503 	if ( Q_stricmp( cmd, "say_team" ) == 0 ) {
2504 		Cmd_Say_f( ent, SAY_TEAM, qfalse );
2505 		return;
2506 	}
2507 	// NERVE - SMF
2508 	if ( Q_stricmp( cmd, "say_limbo" ) == 0 ) {
2509 		Cmd_Say_f( ent, SAY_LIMBO, qfalse );
2510 		return;
2511 	}
2512 	if ( Q_stricmp( cmd, "vsay" ) == 0 ) {
2513 		Cmd_Voice_f( ent, SAY_ALL, qfalse, qfalse );
2514 		return;
2515 	}
2516 	if ( Q_stricmp( cmd, "vsay_team" ) == 0 ) {
2517 		Cmd_Voice_f( ent, SAY_TEAM, qfalse, qfalse );
2518 		return;
2519 	}
2520 	// -NERVE - SMF
2521 
2522 	if ( Q_stricmp( cmd, "tell" ) == 0 ) {
2523 		Cmd_Tell_f( ent );
2524 		return;
2525 	}
2526 	if ( Q_stricmp( cmd, "score" ) == 0 ) {
2527 		Cmd_Score_f( ent );
2528 		return;
2529 	}
2530 
2531 	// NERVE - SMF - moved this here so current/new players can set team during scoreboard win
2532 	if ( Q_stricmp( cmd, "team" ) == 0 ) {
2533 		Cmd_Team_f( ent );
2534 		return;
2535 	}
2536 
2537 	// ignore all other commands when at intermission
2538 	if ( level.intermissiontime ) {
2539 //		Cmd_Say_f (ent, qfalse, qtrue);			// NERVE - SMF - we don't want to spam the clients with this.
2540 		return;
2541 	}
2542 
2543 	if ( Q_stricmp( cmd, "give" ) == 0 ) {
2544 		Cmd_Give_f( ent );
2545 	} else if ( Q_stricmp( cmd, "god" ) == 0 )  {
2546 		Cmd_God_f( ent );
2547 	} else if ( Q_stricmp( cmd, "nofatigue" ) == 0 )  {
2548 		Cmd_Nofatigue_f( ent );
2549 	} else if ( Q_stricmp( cmd, "notarget" ) == 0 )  {
2550 		Cmd_Notarget_f( ent );
2551 	} else if ( Q_stricmp( cmd, "noclip" ) == 0 )  {
2552 		Cmd_Noclip_f( ent );
2553 	} else if ( Q_stricmp( cmd, "kill" ) == 0 )  {
2554 		Cmd_Kill_f( ent );
2555 	} else if ( Q_stricmp( cmd, "levelshot" ) == 0 )  {
2556 		Cmd_LevelShot_f( ent );
2557 	} else if ( Q_stricmp( cmd, "follow" ) == 0 )  {
2558 		Cmd_Follow_f( ent );
2559 	} else if ( Q_stricmp( cmd, "follownext" ) == 0 )  {
2560 		Cmd_FollowCycle_f( ent, 1 );
2561 	} else if ( Q_stricmp( cmd, "followprev" ) == 0 )  {
2562 		Cmd_FollowCycle_f( ent, -1 );
2563 	}
2564 //	else if (Q_stricmp (cmd, "team") == 0)		// NERVE - SMF - moved above intermission check
2565 //		Cmd_Team_f (ent);
2566 	else if ( Q_stricmp( cmd, "where" ) == 0 ) {
2567 		Cmd_Where_f( ent );
2568 	} else if ( Q_stricmp( cmd, "callvote" ) == 0 )  {
2569 		Cmd_CallVote_f( ent );
2570 	} else if ( Q_stricmp( cmd, "vote" ) == 0 )  {
2571 		Cmd_Vote_f( ent );
2572 	} else if ( Q_stricmp( cmd, "gc" ) == 0 )  {
2573 		Cmd_GameCommand_f( ent );
2574 	}
2575 //	else if (Q_stricmp (cmd, "startCamera") == 0)
2576 //		Cmd_StartCamera_f( ent );
2577 //	else if (Q_stricmp (cmd, "stopCamera") == 0)
2578 //		Cmd_StopCamera_f( ent );
2579 //	else if (Q_stricmp (cmd, "setCameraOrigin") == 0)
2580 //		Cmd_SetCameraOrigin_f( ent );
2581 	else if ( Q_stricmp( cmd, "setviewpos" ) == 0 ) {
2582 		Cmd_SetViewpos_f( ent );
2583 	} else if ( Q_stricmp( cmd, "entitycount" ) == 0 )  {
2584 		Cmd_EntityCount_f( ent );
2585 	} else if ( Q_stricmp( cmd, "setspawnpt" ) == 0 )  {
2586 		Cmd_SetSpawnPoint_f( ent );
2587 	} else {
2588 		trap_SendServerCommand( clientNum, va( "print \"unknown cmd[lof] %s\n\"", cmd ) );
2589 	}
2590 }
2591