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