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