1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7
8 This file is part of the OpenJK source code.
9
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13
14 This program 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 this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23
24 #include "g_headers.h"
25
26 #include "g_local.h"
27 #include "objectives.h"
28 #include "wp_saber.h"
29
30 extern bool in_camera;
31
32 extern void ForceThrow( gentity_t *self, qboolean pull );
33 extern void ForceLevitation( gentity_t *self );
34 extern void ForceLightning( gentity_t *self );
35 extern void ForceHeal( gentity_t *self );
36 extern void ForceGrip( gentity_t *self );
37 extern void ForceTelepathy( gentity_t *self );
38 extern void G_ActivatePersonalShield( gentity_t *ent );
39 extern void G_ActivateSeeker( gentity_t *ent );
40 extern void G_PilotXWing( gentity_t *ent );
41 extern void G_DriveATST( gentity_t *ent, gentity_t *atst );
42 extern void G_StartMatrixEffect( gentity_t *ent, qboolean falling = qfalse, int length = 1000 );
43 extern void ItemUse_Bacta(gentity_t *ent);
44 extern gentity_t *G_GetSelfForPlayerCmd( void );
45
46 /*
47 ==================
48 CheatsOk
49 ==================
50 */
CheatsOk(gentity_t * ent)51 qboolean CheatsOk( gentity_t *ent ) {
52 if ( !g_cheats->integer ) {
53 gi.SendServerCommand( ent-g_entities, "print \"Cheats are not enabled on this server.\n\"");
54 return qfalse;
55 }
56 if ( ent->health <= 0 ) {
57 gi.SendServerCommand( ent-g_entities, "print \"You must be alive to use this command.\n\"");
58 return qfalse;
59 }
60 return qtrue;
61 }
62
63
64 /*
65 ==================
66 ConcatArgs
67 ==================
68 */
ConcatArgs(int start)69 char *ConcatArgs( int start ) {
70 int i, c, tlen;
71 static char line[MAX_STRING_CHARS];
72 int len;
73 const char *arg;
74
75 len = 0;
76 c = gi.argc();
77 for ( i = start ; i < c ; i++ ) {
78 arg = gi.argv( i );
79 tlen = strlen( arg );
80 if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
81 break;
82 }
83 memcpy( line + len, arg, tlen );
84 len += tlen;
85 if ( i != c - 1 ) {
86 line[len] = ' ';
87 len++;
88 }
89 }
90
91 line[len] = 0;
92
93 return line;
94 }
95
96 /*
97 ==================
98 SanitizeString
99
100 Remove case and control characters
101 ==================
102 */
SanitizeString(char * in,char * out)103 void SanitizeString( char *in, char *out ) {
104 while ( *in ) {
105 if ( *in == 94 ) {
106 in += 2; // skip color code
107 continue;
108 }
109 if ( *in < 32 ) {
110 in++;
111 continue;
112 }
113 *out++ = tolower( *in++ );
114 }
115
116 *out = 0;
117 }
118
119 /*
120 ==================
121 ClientNumberFromString
122
123 Returns a player number for either a number or name string
124 Returns -1 if invalid
125 ==================
126 */
ClientNumberFromString(gentity_t * to,char * s)127 int ClientNumberFromString( gentity_t *to, char *s ) {
128 gclient_t *cl;
129 int idnum;
130 char s2[MAX_STRING_CHARS];
131 char n2[MAX_STRING_CHARS];
132
133 // numeric values are just slot numbers
134 if (s[0] >= '0' && s[0] <= '9') {
135 idnum = atoi( s );
136 if ( idnum < 0 || idnum >= level.maxclients ) {
137 gi.SendServerCommand( to-g_entities, "print \"Bad client slot: %i\n\"", idnum);
138 return -1;
139 }
140
141 cl = &level.clients[idnum];
142 if ( cl->pers.connected != CON_CONNECTED ) {
143 gi.SendServerCommand( to-g_entities, "print \"Client %i is not active\n\"", idnum);
144 return -1;
145 }
146 return idnum;
147 }
148
149 // check for a name match
150 SanitizeString( s, s2 );
151 for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) {
152 if ( cl->pers.connected != CON_CONNECTED ) {
153 continue;
154 }
155 SanitizeString( cl->pers.netname, n2 );
156 if ( !strcmp( n2, s2 ) ) {
157 return idnum;
158 }
159 }
160
161 gi.SendServerCommand( to-g_entities, "print \"User %s is not on the server\n\"", s);
162 return -1;
163 }
164
G_Give(gentity_t * ent,const char * name,const char * args,int argc)165 void G_Give( gentity_t *ent, const char *name, const char *args, int argc )
166 {
167 gitem_t *it;
168 int i;
169 qboolean give_all = qfalse;
170
171 if ( !Q_stricmp( name, "all" ) )
172 give_all = qtrue;
173
174 if ( give_all || !Q_stricmp( name, "health") )
175 {
176 if ( argc == 3 )
177 ent->health = Com_Clampi( 1, ent->client->ps.stats[STAT_MAX_HEALTH], atoi( args ) );
178 else
179 ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
180 if ( !give_all )
181 return;
182 }
183
184 if ( give_all || !Q_stricmp( name, "armor" ) || !Q_stricmp( name, "shield" ) )
185 {
186 if ( argc == 3 )
187 ent->client->ps.stats[STAT_ARMOR] = Com_Clampi( 0, ent->client->ps.stats[STAT_MAX_HEALTH], atoi( args ) );
188 else
189 ent->client->ps.stats[STAT_ARMOR] = ent->client->ps.stats[STAT_MAX_HEALTH];
190
191 if ( ent->client->ps.stats[STAT_ARMOR] > 0 )
192 ent->client->ps.powerups[PW_BATTLESUIT] = Q3_INFINITE;
193 else
194 ent->client->ps.powerups[PW_BATTLESUIT] = 0;
195
196 if ( !give_all )
197 return;
198 }
199
200 if ( give_all || !Q_stricmp( name, "force" ) )
201 {
202 if ( argc == 3 )
203 ent->client->ps.forcePower = Com_Clampi( 0, FORCE_POWER_MAX, atoi( args ) );
204 else
205 ent->client->ps.forcePower = FORCE_POWER_MAX;
206
207 if ( !give_all )
208 return;
209 }
210
211 if ( give_all || !Q_stricmp( name, "weapons" ) )
212 {
213 ent->client->ps.stats[STAT_WEAPONS] = (1 << (MAX_PLAYER_WEAPONS+1)) - ( 1 << WP_NONE );
214 if ( !give_all )
215 return;
216 }
217
218 if ( !give_all && !Q_stricmp( name, "weaponnum" ) )
219 {
220 ent->client->ps.stats[STAT_WEAPONS] |= (1 << atoi( args ));
221 return;
222 }
223
224 if ( !give_all && !Q_stricmp( name, "eweaps" ) ) //for developing, gives you all the weapons, including enemy
225 {
226 ent->client->ps.stats[STAT_WEAPONS] = (unsigned)(1 << WP_NUM_WEAPONS) - ( 1 << WP_NONE ); // NOTE: this wasn't giving the last weapon in the list
227 return;
228 }
229
230 if ( give_all || !Q_stricmp( name, "ammo" ) )
231 {
232 int num = 999;
233 if ( argc == 3 )
234 num = Com_Clampi( -1, 999, atoi( args ) );
235 for ( i=AMMO_BLASTER; i<AMMO_MAX; i++ )
236 ent->client->ps.ammo[i] = num != -1 ? num : ammoData[i].max;
237 if ( !give_all )
238 return;
239 }
240
241 if ( give_all || !Q_stricmp( name, "batteries" ) )
242 {
243 if ( argc == 3 )
244 ent->client->ps.batteryCharge = Com_Clampi( 0, MAX_BATTERIES, atoi( args ) );
245 else
246 ent->client->ps.batteryCharge = MAX_BATTERIES;
247
248 if ( !give_all )
249 return;
250 }
251
252 if ( give_all || !Q_stricmp( name, "inventory" ) )
253 {
254 // Huh? Was doing a INV_MAX+1 which was wrong because then you'd actually have every inventory item including INV_MAX
255 ent->client->ps.stats[STAT_ITEMS] = (1 << (INV_MAX)) - ( 1 << INV_ELECTROBINOCULARS );
256
257 ent->client->ps.inventory[INV_ELECTROBINOCULARS] = 1;
258 ent->client->ps.inventory[INV_BACTA_CANISTER] = 5;
259 ent->client->ps.inventory[INV_SEEKER] = 5;
260 ent->client->ps.inventory[INV_LIGHTAMP_GOGGLES] = 1;
261 ent->client->ps.inventory[INV_SENTRY] = 5;
262 ent->client->ps.inventory[INV_GOODIE_KEY] = 5;
263 ent->client->ps.inventory[INV_SECURITY_KEY] = 5;
264
265 if ( !give_all )
266 return;
267 }
268
269 // spawn a specific item right on the player
270 if ( !give_all ) {
271 gentity_t *it_ent;
272 trace_t trace;
273 it = FindItem (args);
274 if (!it) {
275 it = FindItem (name);
276 if (!it) {
277 gi.SendServerCommand( ent-g_entities, "print \"unknown item\n\"");
278 return;
279 }
280 }
281
282 it_ent = G_Spawn();
283 VectorCopy( ent->currentOrigin, it_ent->s.origin );
284 it_ent->classname = G_NewString(it->classname);
285 G_SpawnItem (it_ent, it);
286 FinishSpawningItem(it_ent );
287 memset( &trace, 0, sizeof( trace ) );
288 Touch_Item (it_ent, ent, &trace);
289 if (it_ent->inuse) {
290 G_FreeEntity( it_ent );
291 }
292 }
293 }
294
Cmd_Give_f(gentity_t * ent)295 void Cmd_Give_f( gentity_t *ent )
296 {
297 if ( !CheatsOk( ent ) ) {
298 return;
299 }
300
301 G_Give( ent, gi.argv(1), ConcatArgs( 2 ), gi.argc() );
302 }
303
304 //------------------
Cmd_Fx(gentity_t * ent)305 void Cmd_Fx( gentity_t *ent )
306 {
307 vec3_t dir;
308 gentity_t *fx_ent = NULL;
309
310 if ( Q_stricmp( gi.argv(1), "play" ) == 0 )
311 {
312 if ( gi.argc() == 3 )
313 {
314 // I guess, only allow one active at a time
315 while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL )
316 {
317 G_FreeEntity( fx_ent );
318 }
319
320 fx_ent = G_Spawn();
321
322 fx_ent->fxFile = gi.argv( 2 );
323
324 // Move out in front of the person spawning the effect
325 AngleVectors( ent->currentAngles, dir, NULL, NULL );
326 VectorMA( ent->currentOrigin, 32, dir, fx_ent->s.origin );
327
328 extern void SP_fx_runner( gentity_t *ent );
329
330 SP_fx_runner( fx_ent );
331 fx_ent->delay = 2000; // adjusting delay
332 fx_ent->classname = "cmd_fx"; // and classname
333
334 return;
335 }
336 }
337 else if ( Q_stricmp( gi.argv(1), "stop" ) == 0 )
338 {
339 while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL )
340 {
341 G_FreeEntity( fx_ent );
342 }
343
344 return;
345 }
346 else if ( Q_stricmp( gi.argv(1), "delay" ) == 0 )
347 {
348 while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL )
349 {
350 if ( gi.argc() == 3 )
351 {
352 fx_ent->delay = atoi( gi.argv( 2 ));
353 }
354 else
355 {
356 gi.Printf( S_COLOR_GREEN"FX: current delay is: %i\n", fx_ent->delay );
357 }
358
359 return;
360 }
361 }
362 else if ( Q_stricmp( gi.argv(1), "random" ) == 0 )
363 {
364 while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL )
365 {
366 if ( gi.argc() == 3 )
367 {
368 fx_ent->random = atoi( gi.argv( 2 ));
369 }
370 else
371 {
372 gi.Printf( S_COLOR_GREEN"FX: current random is: %6.2f\n", fx_ent->random );
373 }
374
375 return;
376 }
377 }
378 else if ( Q_stricmp( gi.argv(1), "origin" ) == 0 )
379 {
380 while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL )
381 {
382 if ( gi.argc() == 5 )
383 {
384 fx_ent->s.origin[0] = atof( gi.argv( 2 ));
385 fx_ent->s.origin[1] = atof( gi.argv( 3 ));
386 fx_ent->s.origin[2] = atof( gi.argv( 4 ));
387
388 G_SetOrigin( fx_ent, fx_ent->s.origin );
389 }
390 else
391 {
392 gi.Printf( S_COLOR_GREEN"FX: current origin is: <%6.2f %6.2f %6.2f>\n",
393 fx_ent->currentOrigin[0], fx_ent->currentOrigin[1], fx_ent->currentOrigin[2] );
394 }
395
396 return;
397 }
398 }
399 else if ( Q_stricmp( gi.argv(1), "dir" ) == 0 )
400 {
401 while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL )
402 {
403 if ( gi.argc() == 5 )
404 {
405 fx_ent->s.angles[0] = atof( gi.argv( 2 ));
406 fx_ent->s.angles[1] = atof( gi.argv( 3 ));
407 fx_ent->s.angles[2] = atof( gi.argv( 4 ));
408
409 if ( !VectorNormalize( fx_ent->s.angles ))
410 {
411 // must have been zero length
412 fx_ent->s.angles[2] = 1;
413 }
414 }
415 else
416 {
417 gi.Printf( S_COLOR_GREEN"FX: current dir is: <%6.2f %6.2f %6.2f>\n",
418 fx_ent->s.angles[0], fx_ent->s.angles[1], fx_ent->s.angles[2] );
419 }
420
421 return;
422 }
423 }
424
425 gi.Printf( S_COLOR_CYAN"Fx--------------------------------------------------------\n" );
426 gi.Printf( S_COLOR_CYAN"commands: sample usage:\n" );
427 gi.Printf( S_COLOR_CYAN"----------------------------------------------------------\n" );
428 gi.Printf( S_COLOR_CYAN"fx play <filename> fx play sparks, fx play env/fire\n" );
429 gi.Printf( S_COLOR_CYAN"fx stop fx stop\n" );
430 gi.Printf( S_COLOR_CYAN"fx delay <#> fx delay 1000\n" );
431 gi.Printf( S_COLOR_CYAN"fx random <#> fx random 200\n" );
432 gi.Printf( S_COLOR_CYAN"fx origin <#><#><#> fx origin 10 20 30\n" );
433 gi.Printf( S_COLOR_CYAN"fx dir <#><#><#> fx dir 0 0 -1\n\n" );
434 }
435
436 /*
437 ==================
438 Cmd_God_f
439
440 Sets client to godmode
441
442 argv(0) god
443 ==================
444 */
Cmd_God_f(gentity_t * ent)445 void Cmd_God_f (gentity_t *ent)
446 {
447 const char *msg;
448
449 if ( !CheatsOk( ent ) ) {
450 return;
451 }
452
453 ent->flags ^= FL_GODMODE;
454 if (!(ent->flags & FL_GODMODE) )
455 msg = "godmode OFF\n";
456 else
457 msg = "godmode ON\n";
458
459 gi.SendServerCommand( ent-g_entities, "print \"%s\"", msg);
460 }
461
462 /*
463 ==================
464 Cmd_Undying_f
465
466 Sets client to undead mode
467
468 argv(0) undying
469 ==================
470 */
Cmd_Undying_f(gentity_t * ent)471 void Cmd_Undying_f (gentity_t *ent)
472 {
473 const char *msg;
474
475 if ( !CheatsOk( ent ) )
476 {
477 return;
478 }
479
480 ent->flags ^= FL_UNDYING;
481 if (!(ent->flags & FL_UNDYING) )
482 {
483 msg = "undead mode OFF\n";
484 }
485 else
486 {
487 int max;
488 const char *cmd;
489
490 cmd = gi.argv(1);
491 if ( cmd && atoi( cmd ) )
492 {
493 max = atoi( cmd );
494 }
495 else
496 {
497 max = 999;
498 }
499
500 ent->health = ent->max_health = max;
501
502 msg = "undead mode ON\n";
503
504 if ( ent->client )
505 {
506 ent->client->ps.stats[STAT_HEALTH] = ent->client->ps.stats[STAT_MAX_HEALTH] = 999;
507 }
508 }
509
510 gi.SendServerCommand( ent-g_entities, "print \"%s\"", msg);
511 }
512
513 /*
514 ==================
515 Cmd_Notarget_f
516
517 Sets client to notarget
518
519 argv(0) notarget
520 ==================
521 */
Cmd_Notarget_f(gentity_t * ent)522 void Cmd_Notarget_f( gentity_t *ent ) {
523 const char *msg;
524
525 if ( !CheatsOk( ent ) ) {
526 return;
527 }
528
529 ent->flags ^= FL_NOTARGET;
530 if (!(ent->flags & FL_NOTARGET) )
531 msg = "notarget OFF\n";
532 else
533 msg = "notarget ON\n";
534
535 gi.SendServerCommand( ent-g_entities, "print \"%s\"", msg);
536 }
537
538
539 /*
540 ==================
541 Cmd_Noclip_f
542
543 argv(0) noclip
544 ==================
545 */
Cmd_Noclip_f(gentity_t * ent)546 void Cmd_Noclip_f( gentity_t *ent ) {
547 const char *msg;
548
549 if ( !CheatsOk( ent ) ) {
550 return;
551 }
552
553 if ( ent->client->noclip ) {
554 msg = "noclip OFF\n";
555 } else {
556 msg = "noclip ON\n";
557 }
558 ent->client->noclip = (qboolean)!ent->client->noclip;
559
560 gi.SendServerCommand( ent-g_entities, "print \"%s\"", msg);
561 }
562
563
564 /*
565 ==================
566 Cmd_LevelShot_f
567
568 This is just to help generate the level pictures
569 for the menus. It goes to the intermission immediately
570 and sends over a command to the client to resize the view,
571 hide the scoreboard, and take a special screenshot
572 ==================
573 */
Cmd_LevelShot_f(gentity_t * ent)574 void Cmd_LevelShot_f( gentity_t *ent ) {
575 if ( !CheatsOk( ent ) ) {
576 return;
577 }
578
579 gi.SendServerCommand( ent-g_entities, "clientLevelShot" );
580 }
581
582
583 /*
584 =================
585 Cmd_Kill_f
586 =================
587 */
Cmd_Kill_f(gentity_t * ent)588 void Cmd_Kill_f( gentity_t *ent ) {
589 if( ( level.time - ent->client->respawnTime ) < 5000 ) {
590 gi.SendServerCommand( ent-g_entities, "cp @INGAME_ONE_KILL_PER_5_SECONDS");
591 return;
592 }
593 ent->flags &= ~FL_GODMODE;
594 ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
595 player_die (ent, ent, ent, 100000, MOD_SUICIDE);
596 }
597
598
599 /*
600 ==================
601 Cmd_Where_f
602 ==================
603 */
Cmd_Where_f(gentity_t * ent)604 void Cmd_Where_f( gentity_t *ent ) {
605 const char *s = gi.argv(1);
606 const int len = strlen(s);
607 gentity_t *check;
608
609 if ( gi.argc () < 2 ) {
610 gi.Printf("usage: where classname\n");
611 return;
612 }
613 for (int i = 0; i < globals.num_entities; i++)
614 {
615 if(!PInUse(i))
616 continue;
617 // if(!check || !check->inuse) {
618 // continue;
619 // }
620 check = &g_entities[i];
621 if (!Q_stricmpn(s, check->classname, len) ) {
622 gi.SendServerCommand( ent-g_entities, "print \"%s %s\n\"", check->classname, vtos( check->s.pos.trBase ) );
623 }
624 }
625 }
626
627
628 /*
629 -------------------------
630 UserSpawn
631 -------------------------
632 */
633
634 extern qboolean G_CallSpawn( gentity_t *ent );
635
UserSpawn(gentity_t * ent,const char * name)636 void UserSpawn( gentity_t *ent, const char *name )
637 {
638 vec3_t origin;
639 vec3_t vf;
640 vec3_t angles;
641 gentity_t *ent2;
642
643 //Spawn the ent
644 ent2 = G_Spawn();
645 ent2->classname = G_NewString( name ); //FIXME: This will leave floating memory...
646
647 //TODO: This should ultimately make sure this is a safe spawn!
648
649 //Spawn the entity and place it there
650 VectorSet( angles, 0, ent->s.apos.trBase[YAW], 0 );
651 AngleVectors( angles, vf, NULL, NULL );
652 VectorMA( ent->s.pos.trBase, 96, vf, origin ); //FIXME: Find the radius size of the object, and push out 32 + radius
653
654 origin[2] += 8;
655 VectorCopy( origin, ent2->s.pos.trBase );
656 VectorCopy( origin, ent2->s.origin );
657 VectorCopy( ent->s.apos.trBase, ent2->s.angles );
658
659 gi.linkentity( ent2 );
660
661 //Find a valid spawning spot
662 if ( G_CallSpawn( ent2 ) == qfalse )
663 {
664 gi.SendServerCommand( ent-g_entities, "print \"Failed to spawn '%s'\n\"", name );
665 G_FreeEntity( ent2 );
666 return;
667 }
668 }
669
670 /*
671 -------------------------
672 Cmd_Spawn
673 -------------------------
674 */
675
Cmd_Spawn(gentity_t * ent)676 void Cmd_Spawn( gentity_t *ent )
677 {
678 char *name;
679
680 name = ConcatArgs( 1 );
681
682 gi.SendServerCommand( ent-g_entities, "print \"Spawning '%s'\n\"", name );
683
684 UserSpawn( ent, name );
685 }
686
687 /*
688 =================
689 Cmd_SetViewpos_f
690 =================
691 */
Cmd_SetViewpos_f(gentity_t * ent)692 void Cmd_SetViewpos_f( gentity_t *ent ) {
693 vec3_t origin, angles;
694 int i;
695
696 if ( !g_cheats->integer ) {
697 gi.SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
698 return;
699 }
700 if ( gi.argc() != 5 ) {
701 gi.SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
702 return;
703 }
704
705 VectorClear( angles );
706 for ( i = 0 ; i < 3 ; i++ ) {
707 origin[i] = atof( gi.argv( i+1 ) );
708 }
709
710 angles[YAW] = atof( gi.argv( 4 ) );
711
712 TeleportPlayer( ent, origin, angles );
713 }
714
715
716
717 /*
718 =================
719 Cmd_SetObjective_f
720 =================
721 */
Cmd_SetObjective_f(gentity_t * ent)722 void Cmd_SetObjective_f( gentity_t *ent )
723 {
724 int objectiveI,status,displayStatus;
725
726 if ( gi.argc() == 2 ) {
727 objectiveI = atoi(gi.argv(1));
728 gi.Printf("objective #%d display status=%d, status=%d\n",objectiveI,
729 ent->client->sess.mission_objectives[objectiveI].display,
730 ent->client->sess.mission_objectives[objectiveI].status
731 );
732 return;
733 }
734 if ( gi.argc() != 4 ) {
735 gi.SendServerCommand( ent-g_entities, va("print \"usage: setobjective <objective #> <display status> <status>\n\""));
736 return;
737 }
738
739 if ( !CheatsOk( ent ) )
740 {
741 return;
742 }
743
744 objectiveI = atoi(gi.argv(1));
745 displayStatus = atoi(gi.argv(2));
746 status = atoi(gi.argv(3));
747
748 ent->client->sess.mission_objectives[objectiveI].display = displayStatus;
749 ent->client->sess.mission_objectives[objectiveI].status = status;
750 }
751
752 /*
753 =================
754 Cmd_ViewObjective_f
755 =================
756 */
Cmd_ViewObjective_f(gentity_t * ent)757 void Cmd_ViewObjective_f( gentity_t *ent )
758 {
759 int objectiveI;
760
761 if ( gi.argc() != 2 ) {
762 gi.SendServerCommand( ent-g_entities, va("print \"usage: viewobjective <objective #>\n\""));
763 return;
764 }
765
766 objectiveI = atoi(gi.argv(1));
767
768 gi.SendServerCommand( ent-g_entities, va("print \"Objective %d Display Status(1=show): %d Status:%d\n\"",objectiveI,ent->client->sess.mission_objectives[objectiveI].display,ent->client->sess.mission_objectives[objectiveI].status));
769 }
770
771
772 /*
773 ================
774 Cmd_UseElectrobinoculars_f
775 ================
776 */
Cmd_UseElectrobinoculars_f(gentity_t * ent)777 void Cmd_UseElectrobinoculars_f(gentity_t *ent)
778 {
779 if ( ent->health < 1 || in_camera )
780 {
781 return;
782 }
783
784 G_AddEvent( ent, EV_USE_INV_BINOCULARS, 0 );
785 }
786
787 /*
788 ================
789 Cmd_UseBacta_f
790 ================
791 */
Cmd_UseBacta_f(gentity_t * ent)792 void Cmd_UseBacta_f(gentity_t *ent)
793 {
794 if ( ent->health < 1 || in_camera )
795 {
796 return;
797 }
798
799 ItemUse_Bacta(ent);
800 }
801
802 //----------------------------------------------------------------------------------
PickSeekerSpawnPoint(vec3_t org,vec3_t fwd,vec3_t right,int skip,vec3_t spot)803 qboolean PickSeekerSpawnPoint( vec3_t org, vec3_t fwd, vec3_t right, int skip, vec3_t spot )
804 {
805 vec3_t mins, maxs, forward, end;
806 trace_t tr;
807
808 VectorSet( maxs, -8, -8, -24); // ?? size
809 VectorSet( maxs, 8, 8, 8 );
810
811 VectorCopy( fwd, forward );
812
813 // to the front and side a bit
814 forward[2] = 0.3f; // start up a bit
815
816 VectorMA( org, 48, forward, end );
817 VectorMA( end, -8, right, end );
818
819 gi.trace( &tr, org, mins, maxs, end, skip, MASK_PLAYERSOLID, G2_NOCOLLIDE, 0 );
820
821 if ( !tr.startsolid && !tr.allsolid && tr.fraction >= 1.0f )
822 {
823 VectorCopy( tr.endpos, spot );
824 return qtrue;
825 }
826
827 // side
828 VectorMA( org, 48, right, end );
829
830 gi.trace( &tr, org, mins, maxs, end, skip, MASK_PLAYERSOLID, G2_NOCOLLIDE, 0 );
831
832 if ( !tr.startsolid && !tr.allsolid && tr.fraction >= 1.0f )
833 {
834 VectorCopy( tr.endpos, spot );
835 return qtrue;
836 }
837
838 // other side
839 VectorMA( org, -48, right, end );
840
841 gi.trace( &tr, org, mins, maxs, end, skip, MASK_PLAYERSOLID, G2_NOCOLLIDE, 0 );
842
843 if ( !tr.startsolid && !tr.allsolid && tr.fraction >= 1.0f )
844 {
845 VectorCopy( tr.endpos, spot );
846 return qtrue;
847 }
848
849 // behind
850 VectorMA( org, -48, fwd, end );
851
852 gi.trace( &tr, org, mins, maxs, end, skip, MASK_PLAYERSOLID, G2_NOCOLLIDE, 0 );
853
854 if ( !tr.startsolid && !tr.allsolid && tr.fraction >= 1.0f )
855 {
856 VectorCopy( tr.endpos, spot );
857 return qtrue;
858 }
859
860 return qfalse;
861 }
862
863 /*
864 ================
865 Cmd_UseSeeker_f
866 ================
867 */
Cmd_UseSeeker_f(gentity_t * ent)868 void Cmd_UseSeeker_f( gentity_t *ent )
869 {
870 if ( ent->health < 1 || in_camera )
871 {
872 return;
873 }
874
875 // don't use them if we don't have any...also don't use them if one is already going
876 if ( ent->client && ent->client->ps.inventory[INV_SEEKER] > 0 && level.time > ent->client->ps.powerups[PW_SEEKER] )
877 {
878 gentity_t *tent = G_Spawn();
879
880 if ( tent )
881 {
882 vec3_t fwd, right, spot;
883
884 AngleVectors( ent->client->ps.viewangles, fwd, right, NULL );
885
886 VectorCopy( ent->currentOrigin, spot ); // does nothing really, just initialize the goods...
887
888 if ( PickSeekerSpawnPoint( ent->currentOrigin, fwd, right, ent->s.number, spot ))
889 {
890 VectorCopy( spot, tent->s.origin );
891 G_SetOrigin( tent, spot );
892 G_SetAngles( tent, ent->currentAngles );
893
894 extern void SP_NPC_Droid_Seeker( gentity_t *ent );
895
896 SP_NPC_Droid_Seeker( tent );
897 G_Sound( tent, G_SoundIndex( "sound/chars/seeker/misc/hiss" ));
898
899 // make sure that we even have some
900 ent->client->ps.inventory[INV_SEEKER]--;
901 ent->client->ps.powerups[PW_SEEKER] = level.time + 1000;// can only drop one every second..maybe this is annoying?
902
903 }
904 }
905 }
906 }
907
908 /*
909 ================
910 Cmd_UseGoggles_f
911 ================
912 */
Cmd_UseGoggles_f(gentity_t * ent)913 void Cmd_UseGoggles_f(gentity_t *ent)
914 {
915 if ( ent->health < 1 || in_camera )
916 {
917 return;
918 }
919
920 if ( ent->client && ent->client->ps.inventory[INV_LIGHTAMP_GOGGLES] > 0 )
921 {
922 G_AddEvent( ent, EV_USE_INV_LIGHTAMP_GOGGLES, 0 );
923 }
924 }
925
926 /*
927 ================
928 Cmd_UseSentry_f
929 ================
930 */
931 qboolean place_portable_assault_sentry( gentity_t *self, vec3_t origin, vec3_t dir );
Cmd_UseSentry_f(gentity_t * ent)932 void Cmd_UseSentry_f(gentity_t *ent)
933 {
934 if ( ent->health < 1 || in_camera )
935 {
936 return;
937 }
938
939 if ( ent->client->ps.inventory[INV_SENTRY] <= 0 )
940 {
941 // have none to place...play sound?
942 return;
943 }
944
945 if ( place_portable_assault_sentry( ent, ent->currentOrigin, ent->client->ps.viewangles ))
946 {
947 ent->client->ps.inventory[INV_SENTRY]--;
948 G_AddEvent( ent, EV_USE_INV_SENTRY, 0 );
949 }
950 else
951 {
952 // couldn't be placed....play a notification sound!!
953 }
954 }
955
956 /*
957 ================
958 Cmd_UseInventory_f
959 ================
960 */
Cmd_UseInventory_f(gentity_t * ent)961 void Cmd_UseInventory_f(gentity_t *ent)
962 {
963 switch (cg.inventorySelect)
964 {
965 case INV_ELECTROBINOCULARS :
966 Cmd_UseElectrobinoculars_f(ent);
967 return;
968 case INV_BACTA_CANISTER :
969 Cmd_UseBacta_f(ent);
970 return;
971 case INV_SEEKER :
972 Cmd_UseSeeker_f(ent);
973 return;
974 case INV_LIGHTAMP_GOGGLES :
975 Cmd_UseGoggles_f(ent);
976 return;
977 case INV_SENTRY :
978 Cmd_UseSentry_f(ent);
979 return;
980 default :
981 return;
982
983 }
984 }
985
G_Taunt(gentity_t * ent)986 void G_Taunt( gentity_t *ent )
987 {
988 if ( ent->client )
989 {
990 ent->client->ps.taunting = level.time + 100;
991 }
992 }
993
994 extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
G_Victory(gentity_t * ent)995 void G_Victory( gentity_t *ent )
996 {
997 if ( ent->health > 0 )
998 {//say something and put away saber
999 G_SoundOnEnt( ent, CHAN_VOICE, "sound/chars/kyle/misc/taunt1.wav" );
1000 if ( ent->client )
1001 {
1002 ent->client->ps.saberActive = qfalse;
1003 }
1004 }
1005 }
1006 /*
1007 =================
1008 ClientCommand
1009 =================
1010 */
ClientCommand(int clientNum)1011 void ClientCommand( int clientNum ) {
1012 gentity_t *ent;
1013 const char *cmd;
1014
1015 ent = g_entities + clientNum;
1016 if ( !ent->client ) {
1017 return; // not fully in game yet
1018 }
1019
1020 cmd = gi.argv(0);
1021
1022 if (Q_stricmp (cmd, "spawn") == 0)
1023 {
1024 Cmd_Spawn( ent );
1025 return;
1026 }
1027
1028 if (Q_stricmp (cmd, "give") == 0)
1029 Cmd_Give_f (ent);
1030 else if (Q_stricmp (cmd, "god") == 0)
1031 Cmd_God_f (ent);
1032 else if (Q_stricmp (cmd, "undying") == 0)
1033 Cmd_Undying_f (ent);
1034 else if (Q_stricmp (cmd, "notarget") == 0)
1035 Cmd_Notarget_f (ent);
1036 else if (Q_stricmp (cmd, "noclip") == 0)
1037 {
1038 Cmd_Noclip_f (ent);
1039 }
1040 else if (Q_stricmp (cmd, "kill") == 0)
1041 {
1042 if ( !CheatsOk( ent ) )
1043 {
1044 return;
1045 }
1046 Cmd_Kill_f (ent);
1047 }
1048 else if (Q_stricmp (cmd, "levelshot") == 0)
1049 Cmd_LevelShot_f (ent);
1050 else if (Q_stricmp (cmd, "where") == 0)
1051 Cmd_Where_f (ent);
1052 else if (Q_stricmp (cmd, "setviewpos") == 0)
1053 Cmd_SetViewpos_f( ent );
1054 else if (Q_stricmp (cmd, "setobjective") == 0)
1055 Cmd_SetObjective_f( ent );
1056 else if (Q_stricmp (cmd, "viewobjective") == 0)
1057 Cmd_ViewObjective_f( ent );
1058 else if (Q_stricmp (cmd, "force_throw") == 0)
1059 {
1060 ent = G_GetSelfForPlayerCmd();
1061 ForceThrow( ent, qfalse );
1062 }
1063 else if (Q_stricmp (cmd, "force_pull") == 0)
1064 {
1065 ent = G_GetSelfForPlayerCmd();
1066 ForceThrow( ent, qtrue );
1067 }
1068 else if (Q_stricmp (cmd, "force_speed") == 0)
1069 {
1070 ent = G_GetSelfForPlayerCmd();
1071 ForceSpeed( ent );
1072 }
1073 else if (Q_stricmp (cmd, "force_heal") == 0)
1074 {
1075 ent = G_GetSelfForPlayerCmd();
1076 ForceHeal( ent );
1077 }
1078 else if (Q_stricmp (cmd, "force_grip") == 0)
1079 {
1080 ent = G_GetSelfForPlayerCmd();
1081 ForceGrip( ent );
1082 }
1083 else if (Q_stricmp (cmd, "force_distract") == 0)
1084 {
1085 ent = G_GetSelfForPlayerCmd();
1086 ForceTelepathy( ent );
1087 }
1088 else if (Q_stricmp (cmd, "taunt") == 0)
1089 {
1090 ent = G_GetSelfForPlayerCmd();
1091 G_Taunt( ent );
1092 }
1093 else if (Q_stricmp (cmd, "victory") == 0)
1094 G_Victory( ent );
1095 // else if (Q_stricmp (cmd, "use_shield") == 0) // sounds like the design doc states that the shields will be a pickup and so the player never decides whether to use them or not.
1096 // G_ActivatePersonalShield( ent ); // If you want shields (armor), type "give all" or "give armor" or "give armor #amt#"
1097 else if (Q_stricmp (cmd, "fly_xwing") == 0)
1098 G_PilotXWing( ent );
1099 else if (Q_stricmp (cmd, "drive_atst") == 0)
1100 {
1101 if ( CheatsOk( ent ) )
1102 {
1103 G_DriveATST( ent, NULL );
1104 }
1105 }
1106 else if (Q_stricmp (cmd, "thereisnospoon") == 0)
1107 G_StartMatrixEffect( ent );
1108 else if (Q_stricmp (cmd, "use_electrobinoculars") == 0)
1109 Cmd_UseElectrobinoculars_f( ent );
1110 else if (Q_stricmp (cmd, "use_bacta") == 0)
1111 Cmd_UseBacta_f( ent );
1112 else if (Q_stricmp (cmd, "use_seeker") == 0)
1113 Cmd_UseSeeker_f( ent );
1114 else if (Q_stricmp (cmd, "use_lightamp_goggles") == 0)
1115 Cmd_UseGoggles_f( ent );
1116 else if (Q_stricmp (cmd, "use_sentry") == 0)
1117 Cmd_UseSentry_f( ent );
1118 else if (Q_stricmp (cmd, "fx") == 0)
1119 Cmd_Fx( ent );
1120 else if (Q_stricmp (cmd, "invuse") == 0)
1121 {
1122 Cmd_UseInventory_f( ent );
1123 }
1124 else if (Q_stricmp (cmd, "playmusic") == 0)
1125 {
1126 const char *cmd2 = gi.argv(1);
1127 if ( cmd2 )
1128 {
1129 gi.SetConfigstring( CS_MUSIC, cmd2 );
1130 }
1131 }
1132 else
1133 {
1134 gi.SendServerCommand( clientNum, va("print \"Unknown command %s\n\"", cmd ) );
1135 }
1136 }
1137