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