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 "../cgame/cg_local.h"
25 #include "Q3_Interface.h"
26
27 #include "g_local.h"
28 #include "wp_saber.h"
29 #include "g_functions.h"
30
31 extern void G_NextTestAxes( void );
32 extern void G_ChangePlayerModel( gentity_t *ent, const char *newModel );
33 extern void G_InitPlayerFromCvars( gentity_t *ent );
34 extern void Q3_SetViewEntity(int entID, const char *name);
35 extern qboolean G_ClearViewEntity( gentity_t *ent );
36 extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
37
38 extern void WP_SetSaber( gentity_t *ent, int saberNum, const char *saberName );
39 extern void WP_RemoveSaber( gentity_t *ent, int saberNum );
40 extern saber_colors_t TranslateSaberColor( const char *name );
41 extern qboolean WP_SaberBladeUseSecondBladeStyle( saberInfo_t *saber, int bladeNum );
42 extern qboolean WP_UseFirstValidSaberStyle( gentity_t *ent, int *saberAnimLevel );
43
44 extern void G_SetWeapon( gentity_t *self, int wp );
45 extern stringID_table_t WPTable[];
46
47 extern cvar_t *g_char_model;
48 extern cvar_t *g_char_skin_head;
49 extern cvar_t *g_char_skin_torso;
50 extern cvar_t *g_char_skin_legs;
51 extern cvar_t *g_char_color_red;
52 extern cvar_t *g_char_color_green;
53 extern cvar_t *g_char_color_blue;
54 extern cvar_t *g_saber;
55 extern cvar_t *g_saber2;
56 extern cvar_t *g_saber_color;
57 extern cvar_t *g_saber2_color;
58
59 /*
60 ===================
61 Svcmd_EntityList_f
62 ===================
63 */
Svcmd_EntityList_f(void)64 void Svcmd_EntityList_f (void) {
65 int e;
66 gentity_t *check;
67
68 check = g_entities;
69 for (e = 0; e < globals.num_entities ; e++, check++) {
70 if ( !check->inuse ) {
71 continue;
72 }
73 gi.Printf("%3i:", e);
74 switch ( check->s.eType ) {
75 case ET_GENERAL:
76 gi.Printf( "ET_GENERAL " );
77 break;
78 case ET_PLAYER:
79 gi.Printf( "ET_PLAYER " );
80 break;
81 case ET_ITEM:
82 gi.Printf( "ET_ITEM " );
83 break;
84 case ET_MISSILE:
85 gi.Printf( "ET_MISSILE " );
86 break;
87 case ET_MOVER:
88 gi.Printf( "ET_MOVER " );
89 break;
90 case ET_BEAM:
91 gi.Printf( "ET_BEAM " );
92 break;
93 case ET_PORTAL:
94 gi.Printf( "ET_PORTAL " );
95 break;
96 case ET_SPEAKER:
97 gi.Printf( "ET_SPEAKER " );
98 break;
99 case ET_PUSH_TRIGGER:
100 gi.Printf( "ET_PUSH_TRIGGER " );
101 break;
102 case ET_TELEPORT_TRIGGER:
103 gi.Printf( "ET_TELEPORT_TRIGGER " );
104 break;
105 case ET_INVISIBLE:
106 gi.Printf( "ET_INVISIBLE " );
107 break;
108 case ET_THINKER:
109 gi.Printf( "ET_THINKER " );
110 break;
111 case ET_CLOUD:
112 gi.Printf( "ET_CLOUD " );
113 break;
114 case ET_TERRAIN:
115 gi.Printf( "ET_TERRAIN " );
116 break;
117 default:
118 gi.Printf( "%-3i ", check->s.eType );
119 break;
120 }
121
122 if ( check->classname ) {
123 gi.Printf("%s", check->classname);
124 }
125 gi.Printf("\n");
126 }
127 }
128
129 //---------------------------
130 extern void G_StopCinematicSkip( void );
131 extern void G_StartCinematicSkip( void );
132 extern void ExitEmplacedWeapon( gentity_t *ent );
Svcmd_ExitView_f(void)133 static void Svcmd_ExitView_f( void )
134 {
135 extern cvar_t *g_skippingcin;
136 static int exitViewDebounce = 0;
137 if ( exitViewDebounce > level.time )
138 {
139 return;
140 }
141 exitViewDebounce = level.time + 500;
142 if ( in_camera )
143 {//see if we need to exit an in-game cinematic
144 if ( g_skippingcin->integer ) // already doing cinematic skip?
145 {// yes... so stop skipping...
146 G_StopCinematicSkip();
147 }
148 else
149 {// no... so start skipping...
150 G_StartCinematicSkip();
151 }
152 }
153 else if ( !G_ClearViewEntity( player ) )
154 {//didn't exit control of a droid or turret
155 //okay, now try exiting emplaced guns or AT-ST's
156 if ( player->s.eFlags & EF_LOCKED_TO_WEAPON )
157 {//get out of emplaced gun
158 ExitEmplacedWeapon( player );
159 }
160 else if ( player->client && player->client->NPC_class == CLASS_ATST )
161 {//a player trying to get out of his ATST
162 GEntity_UseFunc( player->activator, player, player );
163 }
164 }
165 }
166
G_GetSelfForPlayerCmd(void)167 gentity_t *G_GetSelfForPlayerCmd( void )
168 {
169 if ( g_entities[0].client->ps.viewEntity > 0
170 && g_entities[0].client->ps.viewEntity < ENTITYNUM_WORLD
171 && g_entities[g_entities[0].client->ps.viewEntity].client
172 && g_entities[g_entities[0].client->ps.viewEntity].s.weapon == WP_SABER )
173 {//you're controlling another NPC
174 return (&g_entities[g_entities[0].client->ps.viewEntity]);
175 }
176 else
177 {
178 return (&g_entities[0]);
179 }
180 }
181
Svcmd_Saber_f()182 static void Svcmd_Saber_f()
183 {
184 const char *saber = gi.argv(1);
185 const char *saber2 = gi.argv(2);
186 char name[MAX_CVAR_VALUE_STRING] = {0};
187
188 if ( gi.argc() < 2 )
189 {
190 gi.Printf( "Usage: saber <saber1> <saber2>\n" );
191 gi.Cvar_VariableStringBuffer( "g_saber", name, sizeof(name) );
192 gi.Printf("g_saber is set to %s\n", name);
193 gi.Cvar_VariableStringBuffer( "g_saber2", name, sizeof(name) );
194 if ( name[0] )
195 gi.Printf("g_saber2 is set to %s\n", name);
196 return;
197 }
198
199 if ( !g_entities[0].client || !saber || !saber[0] )
200 {
201 return;
202 }
203
204 gi.cvar_set( "g_saber", saber );
205 WP_SetSaber( &g_entities[0], 0, saber );
206 if ( saber2 && saber2[0] && !(g_entities[0].client->ps.saber[0].saberFlags&SFL_TWO_HANDED) )
207 {//want to use a second saber and first one is not twoHanded
208 gi.cvar_set( "g_saber2", saber2 );
209 WP_SetSaber( &g_entities[0], 1, saber2 );
210 }
211 else
212 {
213 gi.cvar_set( "g_saber2", "" );
214 WP_RemoveSaber( &g_entities[0], 1 );
215 }
216 }
217
Svcmd_SaberBlade_f()218 static void Svcmd_SaberBlade_f()
219 {
220 if ( gi.argc() < 2 )
221 {
222 gi.Printf( "USAGE: saberblade <sabernum> <bladenum> [0 = off, 1 = on, no arg = toggle]\n" );
223 return;
224 }
225 if ( &g_entities[0] == NULL || g_entities[0].client == NULL )
226 {
227 return;
228 }
229 int sabernum = atoi(gi.argv(1)) - 1;
230 if ( sabernum < 0 || sabernum > 1 )
231 {
232 return;
233 }
234 if ( sabernum > 0 && !g_entities[0].client->ps.dualSabers )
235 {
236 return;
237 }
238 //FIXME: what if don't even have a single saber at all?
239 int bladenum = atoi(gi.argv(2)) - 1;
240 if ( bladenum < 0 || bladenum >= g_entities[0].client->ps.saber[sabernum].numBlades )
241 {
242 return;
243 }
244 qboolean turnOn;
245 if ( gi.argc() > 2 )
246 {//explicit
247 turnOn = (qboolean)(atoi(gi.argv(3))!=0);
248 }
249 else
250 {//toggle
251 turnOn = (qboolean)!g_entities[0].client->ps.saber[sabernum].blade[bladenum].active;
252 }
253
254 g_entities[0].client->ps.SaberBladeActivate( sabernum, bladenum, turnOn );
255 }
256
Svcmd_SaberColor_f()257 static void Svcmd_SaberColor_f()
258 {//FIXME: just list the colors, each additional listing sets that blade
259 int saberNum = atoi(gi.argv(1));
260 const char *color[MAX_BLADES];
261 int bladeNum;
262
263 for ( bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ )
264 {
265 color[bladeNum] = gi.argv(2+bladeNum);
266 }
267
268 if ( saberNum < 1 || saberNum > 2 || gi.argc() < 3 )
269 {
270 gi.Printf( "Usage: saberColor <saberNum> <blade1 color> <blade2 color> ... <blade8 color>\n" );
271 gi.Printf( "valid saberNums: 1 or 2\n" );
272 gi.Printf( "valid colors: red, orange, yellow, green, blue, and purple\n" );
273
274 return;
275 }
276 saberNum--;
277
278 gentity_t *self = G_GetSelfForPlayerCmd();
279
280 for ( bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ )
281 {
282 if ( !color[bladeNum] || !color[bladeNum][0] )
283 {
284 break;
285 }
286 else
287 {
288 self->client->ps.saber[saberNum].blade[bladeNum].color = TranslateSaberColor( color[bladeNum] );
289 }
290 }
291
292 if ( saberNum == 0 )
293 {
294 gi.cvar_set( "g_saber_color", color[0] );
295 }
296 else if ( saberNum == 1 )
297 {
298 gi.cvar_set( "g_saber2_color", color[0] );
299 }
300 }
301
302 struct SetForceCmd {
303 const char *desc;
304 const char *cmdname;
305 const int maxlevel;
306 };
307
308 SetForceCmd SetForceTable[NUM_FORCE_POWERS] = {
309 { "forceHeal", "setForceHeal", FORCE_LEVEL_3 },
310 { "forceJump", "setForceJump", FORCE_LEVEL_3 },
311 { "forceSpeed", "setForceSpeed", FORCE_LEVEL_3 },
312 { "forcePush", "setForcePush", FORCE_LEVEL_3 },
313 { "forcePull", "setForcePull", FORCE_LEVEL_3 },
314 { "forceMindTrick", "setForceMindTrick", FORCE_LEVEL_4 },
315 { "forceGrip", "setForceGrip", FORCE_LEVEL_3 },
316 { "forceLightning", "setForceLightning", FORCE_LEVEL_3 },
317 { "saberThrow", "setSaberThrow", FORCE_LEVEL_3 },
318 { "saberDefense", "setSaberDefense", FORCE_LEVEL_3 },
319 { "saberOffense", "setSaberOffense", SS_NUM_SABER_STYLES-1 },
320 { "forceRage", "setForceRage", FORCE_LEVEL_3 },
321 { "forceProtect", "setForceProtect", FORCE_LEVEL_3 },
322 { "forceAbsorb", "setForceAbsorb", FORCE_LEVEL_3 },
323 { "forceDrain", "setForceDrain", FORCE_LEVEL_3 },
324 { "forceSight", "setForceSight", FORCE_LEVEL_3 },
325 };
326
Svcmd_ForceSetLevel_f(int forcePower)327 static void Svcmd_ForceSetLevel_f( int forcePower )
328 {
329 if ( !&g_entities[0] || !g_entities[0].client )
330 {
331 return;
332 }
333 const char *newVal = gi.argv(1);
334 if ( !VALIDSTRING( newVal ) )
335 {
336 gi.Printf( "Current %s level is %d\n", SetForceTable[forcePower].desc, g_entities[0].client->ps.forcePowerLevel[forcePower] );
337 gi.Printf( "Usage: %s <level> (0 - %i)\n", SetForceTable[forcePower].cmdname, SetForceTable[forcePower].maxlevel );
338 return;
339 }
340 int val = atoi(newVal);
341 if ( val > FORCE_LEVEL_0 )
342 {
343 g_entities[0].client->ps.forcePowersKnown |= ( 1 << forcePower );
344 }
345 else
346 {
347 g_entities[0].client->ps.forcePowersKnown &= ~( 1 << forcePower );
348 }
349 g_entities[0].client->ps.forcePowerLevel[forcePower] = val;
350 if ( g_entities[0].client->ps.forcePowerLevel[forcePower] < FORCE_LEVEL_0 )
351 {
352 g_entities[0].client->ps.forcePowerLevel[forcePower] = FORCE_LEVEL_0;
353 }
354 else if ( g_entities[0].client->ps.forcePowerLevel[forcePower] > SetForceTable[forcePower].maxlevel )
355 {
356 g_entities[0].client->ps.forcePowerLevel[forcePower] = SetForceTable[forcePower].maxlevel;
357 }
358 }
359
360 extern qboolean PM_SaberInStart( int move );
361 extern qboolean PM_SaberInTransition( int move );
362 extern qboolean PM_SaberInAttack( int move );
363 extern qboolean WP_SaberCanTurnOffSomeBlades( saberInfo_t *saber );
Svcmd_SaberAttackCycle_f(void)364 void Svcmd_SaberAttackCycle_f( void )
365 {
366 if ( !&g_entities[0] || !g_entities[0].client )
367 {
368 return;
369 }
370
371 gentity_t *self = G_GetSelfForPlayerCmd();
372 if ( self->s.weapon != WP_SABER )
373 {// saberAttackCycle button also switches to saber
374 gi.SendConsoleCommand("weapon 1" );
375 return;
376 }
377
378 if ( self->client->ps.dualSabers )
379 {//can't cycle styles with dualSabers, so just toggle second saber on/off
380 if ( WP_SaberCanTurnOffSomeBlades( &self->client->ps.saber[1] ) )
381 {//can turn second saber off
382 if ( self->client->ps.saber[1].ActiveManualOnly() )
383 {//turn it off
384 qboolean skipThisBlade;
385 for ( int bladeNum = 0; bladeNum < self->client->ps.saber[1].numBlades; bladeNum++ )
386 {
387 skipThisBlade = qfalse;
388 if ( WP_SaberBladeUseSecondBladeStyle( &self->client->ps.saber[1], bladeNum ) )
389 {//check to see if we should check the secondary style's flags
390 if ( (self->client->ps.saber[1].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE2) )
391 {
392 skipThisBlade = qtrue;
393 }
394 }
395 else
396 {//use the primary style's flags
397 if ( (self->client->ps.saber[1].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE) )
398 {
399 skipThisBlade = qtrue;
400 }
401 }
402 if ( !skipThisBlade )
403 {
404 self->client->ps.saber[1].BladeActivate( bladeNum, qfalse );
405 G_SoundIndexOnEnt( self, CHAN_WEAPON, self->client->ps.saber[1].soundOff );
406 }
407 }
408 }
409 else if ( !self->client->ps.saber[0].ActiveManualOnly() )
410 {//first one is off, too, so just turn that one on
411 if ( !self->client->ps.saberInFlight )
412 {//but only if it's in your hand!
413 self->client->ps.saber[0].Activate();
414 }
415 }
416 else
417 {//turn on the second one
418 self->client->ps.saber[1].Activate();
419 }
420 return;
421 }
422 }
423 else if ( self->client->ps.saber[0].numBlades > 1
424 && WP_SaberCanTurnOffSomeBlades( &self->client->ps.saber[0] ) )//self->client->ps.saber[0].type == SABER_STAFF )
425 {//can't cycle styles with saberstaff, so just toggles saber blades on/off
426 if ( self->client->ps.saberInFlight )
427 {//can't turn second blade back on if it's in the air, you naughty boy!
428 return;
429 }
430 /*
431 if ( self->client->ps.saber[0].singleBladeStyle == SS_NONE )
432 {//can't use just one blade?
433 return;
434 }
435 */
436 qboolean playedSound = qfalse;
437 if ( !self->client->ps.saber[0].blade[0].active )
438 {//first one is not even on
439 //turn only it on
440 self->client->ps.SaberBladeActivate( 0, 0, qtrue );
441 return;
442 }
443
444 qboolean skipThisBlade;
445 for ( int bladeNum = 1; bladeNum < self->client->ps.saber[0].numBlades; bladeNum++ )
446 {
447 if ( !self->client->ps.saber[0].blade[bladeNum].active )
448 {//extra is off, turn it on
449 self->client->ps.saber[0].BladeActivate( bladeNum, qtrue );
450 }
451 else
452 {//turn extra off
453 skipThisBlade = qfalse;
454 if ( WP_SaberBladeUseSecondBladeStyle( &self->client->ps.saber[1], bladeNum ) )
455 {//check to see if we should check the secondary style's flags
456 if ( (self->client->ps.saber[1].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE2) )
457 {
458 skipThisBlade = qtrue;
459 }
460 }
461 else
462 {//use the primary style's flags
463 if ( (self->client->ps.saber[1].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE) )
464 {
465 skipThisBlade = qtrue;
466 }
467 }
468 if ( !skipThisBlade )
469 {
470 self->client->ps.saber[0].BladeActivate( bladeNum, qfalse );
471 if ( !playedSound )
472 {
473 G_SoundIndexOnEnt( self, CHAN_WEAPON, self->client->ps.saber[0].soundOff );
474 playedSound = qtrue;
475 }
476 }
477 }
478 }
479 return;
480 }
481
482 int allowedStyles = self->client->ps.saberStylesKnown;
483 if ( self->client->ps.dualSabers
484 && self->client->ps.saber[0].Active()
485 && self->client->ps.saber[1].Active() )
486 {
487 allowedStyles |= (1<<SS_DUAL);
488 for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ )
489 {
490 if ( styleNum == SS_TAVION
491 && ((self->client->ps.saber[0].stylesLearned&(1<<SS_TAVION))||(self->client->ps.saber[1].stylesLearned&(1<<SS_TAVION)))//was given this style by one of my sabers
492 && !(self->client->ps.saber[0].stylesForbidden&(1<<SS_TAVION))
493 && !(self->client->ps.saber[1].stylesForbidden&(1<<SS_TAVION)) )
494 {//if have both sabers on, allow tavion only if one of our sabers specifically wanted to use it... (unless specifically forbidden)
495 }
496 else if ( styleNum == SS_DUAL
497 && !(self->client->ps.saber[0].stylesForbidden&(1<<SS_DUAL))
498 && !(self->client->ps.saber[1].stylesForbidden&(1<<SS_DUAL)) )
499 {//if have both sabers on, only dual style is allowed (unless specifically forbidden)
500 }
501 else
502 {
503 allowedStyles &= ~(1<<styleNum);
504 }
505 }
506 }
507
508 if ( !allowedStyles )
509 {
510 return;
511 }
512
513 int saberAnimLevel;
514 if ( !self->s.number )
515 {
516 saberAnimLevel = cg.saberAnimLevelPending;
517 }
518 else
519 {
520 saberAnimLevel = self->client->ps.saberAnimLevel;
521 }
522 saberAnimLevel++;
523 int sanityCheck = 0;
524 while ( self->client->ps.saberAnimLevel != saberAnimLevel
525 && !(allowedStyles&(1<<saberAnimLevel))
526 && sanityCheck < SS_NUM_SABER_STYLES+1 )
527 {
528 saberAnimLevel++;
529 if ( saberAnimLevel > SS_STAFF )
530 {
531 saberAnimLevel = SS_FAST;
532 }
533 sanityCheck++;
534 }
535
536 if ( !(allowedStyles&(1<<saberAnimLevel)) )
537 {
538 return;
539 }
540
541 WP_UseFirstValidSaberStyle( self, &saberAnimLevel );
542 if ( !self->s.number )
543 {
544 cg.saberAnimLevelPending = saberAnimLevel;
545 }
546 else
547 {
548 self->client->ps.saberAnimLevel = saberAnimLevel;
549 }
550
551 #ifndef FINAL_BUILD
552 switch ( saberAnimLevel )
553 {
554 case SS_FAST:
555 gi.Printf( S_COLOR_BLUE "Lightsaber Combat Style: Fast\n" );
556 //LIGHTSABERCOMBATSTYLE_FAST
557 break;
558 case SS_MEDIUM:
559 gi.Printf( S_COLOR_YELLOW "Lightsaber Combat Style: Medium\n" );
560 //LIGHTSABERCOMBATSTYLE_MEDIUM
561 break;
562 case SS_STRONG:
563 gi.Printf( S_COLOR_RED "Lightsaber Combat Style: Strong\n" );
564 //LIGHTSABERCOMBATSTYLE_STRONG
565 break;
566 case SS_DESANN:
567 gi.Printf( S_COLOR_CYAN "Lightsaber Combat Style: Desann\n" );
568 //LIGHTSABERCOMBATSTYLE_DESANN
569 break;
570 case SS_TAVION:
571 gi.Printf( S_COLOR_MAGENTA "Lightsaber Combat Style: Tavion\n" );
572 //LIGHTSABERCOMBATSTYLE_TAVION
573 break;
574 case SS_DUAL:
575 gi.Printf( S_COLOR_MAGENTA "Lightsaber Combat Style: Dual\n" );
576 //LIGHTSABERCOMBATSTYLE_TAVION
577 break;
578 case SS_STAFF:
579 gi.Printf( S_COLOR_MAGENTA "Lightsaber Combat Style: Staff\n" );
580 //LIGHTSABERCOMBATSTYLE_TAVION
581 break;
582 }
583 //gi.Printf("\n");
584 #endif
585 }
586
G_ReleaseEntity(gentity_t * grabber)587 qboolean G_ReleaseEntity( gentity_t *grabber )
588 {
589 if ( grabber && grabber->client && grabber->client->ps.heldClient < ENTITYNUM_WORLD )
590 {
591 gentity_t *heldClient = &g_entities[grabber->client->ps.heldClient];
592 grabber->client->ps.heldClient = ENTITYNUM_NONE;
593 if ( heldClient && heldClient->client )
594 {
595 heldClient->client->ps.heldByClient = ENTITYNUM_NONE;
596
597 heldClient->owner = NULL;
598 }
599 return qtrue;
600 }
601 return qfalse;
602 }
603
G_GrabEntity(gentity_t * grabber,const char * target)604 void G_GrabEntity( gentity_t *grabber, const char *target )
605 {
606 if ( !grabber || !grabber->client )
607 {
608 return;
609 }
610 gentity_t *heldClient = G_Find( NULL, FOFS(targetname), (char *)target );
611 if ( heldClient && heldClient->client && heldClient != grabber )//don't grab yourself, it's not polite
612 {//found him
613 grabber->client->ps.heldClient = heldClient->s.number;
614 heldClient->client->ps.heldByClient = grabber->s.number;
615
616 heldClient->owner = grabber;
617 }
618 }
619
Svcmd_ICARUS_f(void)620 static void Svcmd_ICARUS_f( void )
621 {
622 Quake3Game()->Svcmd();
623 }
624
625 template <int32_t power>
Svcmd_ForceSetLevel_f(void)626 static void Svcmd_ForceSetLevel_f(void)
627 {
628 Svcmd_ForceSetLevel_f(power);
629 }
630
Svcmd_SetForceAll_f(void)631 static void Svcmd_SetForceAll_f(void)
632 {
633 for ( int i = FP_HEAL; i < NUM_FORCE_POWERS; i++ )
634 {
635 Svcmd_ForceSetLevel_f( i );
636 }
637
638 if( gi.argc() > 1 )
639 {
640 for ( int i = SS_NONE+1; i < SS_NUM_SABER_STYLES; i++ )
641 {
642 g_entities[0].client->ps.saberStylesKnown |= (1<<i);
643 }
644 }
645 }
646
Svcmd_SetSaberAll_f(void)647 static void Svcmd_SetSaberAll_f(void)
648 {
649 Svcmd_ForceSetLevel_f( FP_SABERTHROW );
650 Svcmd_ForceSetLevel_f( FP_SABER_DEFENSE );
651 Svcmd_ForceSetLevel_f( FP_SABER_OFFENSE );
652 for ( int i = SS_NONE+1; i < SS_NUM_SABER_STYLES; i++ )
653 {
654 g_entities[0].client->ps.saberStylesKnown |= (1<<i);
655 }
656 }
657
Svcmd_RunScript_f(void)658 static void Svcmd_RunScript_f(void)
659 {
660 const char *cmd2 = gi.argv(1);
661
662 if ( cmd2 && cmd2[0] )
663 {
664 const char *cmd3 = gi.argv(2);
665 if ( cmd3 && cmd3[0] )
666 {
667 gentity_t *found = NULL;
668 if ( (found = G_Find(NULL, FOFS(targetname), cmd2 ) ) != NULL )
669 {
670 Quake3Game()->RunScript( found, cmd3 );
671 }
672 else
673 {
674 //can't find cmd2
675 gi.Printf( S_COLOR_RED "runscript: can't find targetname %s\n", cmd2 );
676 }
677 }
678 else
679 {
680 Quake3Game()->RunScript( &g_entities[0], cmd2 );
681 }
682 }
683 else
684 {
685 gi.Printf( S_COLOR_RED "usage: runscript <ent targetname> scriptname\n" );
686 }
687 }
688
Svcmd_PlayerTeam_f(void)689 static void Svcmd_PlayerTeam_f(void)
690 {
691 const char *cmd2 = gi.argv(1);
692
693 if ( !*cmd2 || !cmd2[0] )
694 {
695 gi.Printf( S_COLOR_RED "'playerteam' - change player team, requires a team name!\n" );
696 gi.Printf( S_COLOR_RED "Current team is: %s\n", GetStringForID( TeamTable, g_entities[0].client->playerTeam ) );
697 gi.Printf( S_COLOR_RED "Valid team names are:\n");
698 for ( int n = (TEAM_FREE + 1); n < TEAM_NUM_TEAMS; n++ )
699 {
700 gi.Printf( S_COLOR_RED "%s\n", GetStringForID( TeamTable, n ) );
701 }
702 }
703 else
704 {
705 team_t team;
706
707 team = (team_t)GetIDForString( TeamTable, cmd2 );
708 if ( team == (team_t)-1 )
709 {
710 gi.Printf( S_COLOR_RED "'playerteam' unrecognized team name %s!\n", cmd2 );
711 gi.Printf( S_COLOR_RED "Current team is: %s\n", GetStringForID( TeamTable, g_entities[0].client->playerTeam ) );
712 gi.Printf( S_COLOR_RED "Valid team names are:\n");
713 for ( int n = TEAM_FREE; n < TEAM_NUM_TEAMS; n++ )
714 {
715 gi.Printf( S_COLOR_RED "%s\n", GetStringForID( TeamTable, n ) );
716 }
717 }
718 else
719 {
720 g_entities[0].client->playerTeam = team;
721 //FIXME: convert Imperial, Malon, Hirogen and Klingon to Scavenger?
722 }
723 }
724 }
725
Svcmd_Control_f(void)726 static void Svcmd_Control_f(void)
727 {
728 const char *cmd2 = gi.argv(1);
729 if ( !*cmd2 || !cmd2[0] )
730 {
731 if ( !G_ClearViewEntity( &g_entities[0] ) )
732 {
733 gi.Printf( S_COLOR_RED "control <NPC_targetname>\n", cmd2 );
734 }
735 }
736 else
737 {
738 Q3_SetViewEntity( 0, cmd2 );
739 }
740 }
741
Svcmd_Grab_f(void)742 static void Svcmd_Grab_f(void)
743 {
744 const char *cmd2 = gi.argv(1);
745 if ( !*cmd2 || !cmd2[0] )
746 {
747 if ( !G_ReleaseEntity( &g_entities[0] ) )
748 {
749 gi.Printf( S_COLOR_RED "grab <NPC_targetname>\n", cmd2 );
750 }
751 }
752 else
753 {
754 G_GrabEntity( &g_entities[0], cmd2 );
755 }
756 }
757
Svcmd_Knockdown_f(void)758 static void Svcmd_Knockdown_f(void)
759 {
760 G_Knockdown( &g_entities[0], &g_entities[0], vec3_origin, 300, qtrue );
761 }
762
Svcmd_PlayerModel_f(void)763 static void Svcmd_PlayerModel_f(void)
764 {
765 if ( gi.argc() == 1 )
766 {
767 gi.Printf( S_COLOR_RED "USAGE: playerModel <NPC Name>\n playerModel <g2model> <skinhead> <skintorso> <skinlower>\n playerModel player (builds player from customized menu settings)" S_COLOR_WHITE "\n" );
768 gi.Printf( "playerModel = %s ", va("%s %s %s %s\n", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string ) );
769 }
770 else if ( gi.argc() == 2 )
771 {
772 G_ChangePlayerModel( &g_entities[0], gi.argv(1) );
773 }
774 else if ( gi.argc() == 5 )
775 {
776 //instead of setting it directly via a command, we now store it in cvars
777 //G_ChangePlayerModel( &g_entities[0], va("%s|%s|%s|%s", gi.argv(1), gi.argv(2), gi.argv(3), gi.argv(4)) );
778 gi.cvar_set("g_char_model", gi.argv(1) );
779 gi.cvar_set("g_char_skin_head", gi.argv(2) );
780 gi.cvar_set("g_char_skin_torso", gi.argv(3) );
781 gi.cvar_set("g_char_skin_legs", gi.argv(4) );
782 G_InitPlayerFromCvars( &g_entities[0] );
783 }
784 }
785
Svcmd_PlayerTint_f(void)786 static void Svcmd_PlayerTint_f(void)
787 {
788 if ( gi.argc() == 4 )
789 {
790 g_entities[0].client->renderInfo.customRGBA[0] = atoi(gi.argv(1));
791 g_entities[0].client->renderInfo.customRGBA[1] = atoi(gi.argv(2));
792 g_entities[0].client->renderInfo.customRGBA[2] = atoi(gi.argv(3));
793 gi.cvar_set("g_char_color_red", gi.argv(1) );
794 gi.cvar_set("g_char_color_green", gi.argv(2) );
795 gi.cvar_set("g_char_color_blue", gi.argv(3) );
796 }
797 else
798 {
799 gi.Printf( S_COLOR_RED "USAGE: playerTint <red 0 - 255> <green 0 - 255> <blue 0 - 255>\n" );
800 gi.Printf( "playerTint = %s\n", va("%d %d %d", g_char_color_red->integer, g_char_color_green->integer, g_char_color_blue->integer ) );
801 }
802 }
803
Svcmd_IKnowKungfu_f(void)804 static void Svcmd_IKnowKungfu_f(void)
805 {
806 gi.cvar_set( "g_debugMelee", "1" );
807 G_SetWeapon( &g_entities[0], WP_MELEE );
808 for ( int i = FP_FIRST; i < NUM_FORCE_POWERS; i++ )
809 {
810 g_entities[0].client->ps.forcePowersKnown |= ( 1 << i );
811 if ( i == FP_TELEPATHY )
812 {
813 g_entities[0].client->ps.forcePowerLevel[i] = FORCE_LEVEL_4;
814 }
815 else
816 {
817 g_entities[0].client->ps.forcePowerLevel[i] = FORCE_LEVEL_3;
818 }
819 }
820 }
821
Svcmd_Secrets_f(void)822 static void Svcmd_Secrets_f(void)
823 {
824 const gentity_t *pl = &g_entities[0];
825 if(pl->client->sess.missionStats.totalSecrets < 1)
826 {
827 gi.Printf( "There are" S_COLOR_RED " NO " S_COLOR_WHITE "secrets on this map!\n" );
828 }
829 else if(pl->client->sess.missionStats.secretsFound == pl->client->sess.missionStats.totalSecrets)
830 {
831 gi.Printf( "You've found all " S_COLOR_GREEN "%i" S_COLOR_WHITE " secrets on this map!\n", pl->client->sess.missionStats.secretsFound );
832 }
833 else
834 {
835 gi.Printf( "You've found " S_COLOR_GREEN "%i" S_COLOR_WHITE " out of " S_COLOR_GREEN "%i" S_COLOR_WHITE " secrets!\n", pl->client->sess.missionStats.secretsFound, pl->client->sess.missionStats.totalSecrets );
836 }
837 }
838
839 // PADAWAN - g_spskill 0 + cg_crosshairForceHint 1 + handicap 100
840 // JEDI - g_spskill 1 + cg_crosshairForceHint 1 + handicap 100
841 // JEDI KNIGHT - g_spskill 2 + cg_crosshairForceHint 0 + handicap 100
842 // JEDI MASTER - g_spskill 2 + cg_crosshairForceHint 0 + handicap 50
843
844 extern cvar_t *g_spskill;
Svcmd_Difficulty_f(void)845 static void Svcmd_Difficulty_f(void)
846 {
847 if(gi.argc() == 1)
848 {
849 if(g_spskill->integer == 0)
850 {
851 gi.Printf( S_COLOR_GREEN "Current Difficulty: Padawan" S_COLOR_WHITE "\n" );
852 }
853 else if(g_spskill->integer == 1)
854 {
855 gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi" S_COLOR_WHITE "\n" );
856 }
857 else if(g_spskill->integer == 2)
858 {
859 int crosshairHint = gi.Cvar_VariableIntegerValue("cg_crosshairForceHint");
860 int handicap = gi.Cvar_VariableIntegerValue("handicap");
861 if(handicap == 100 && crosshairHint == 0)
862 {
863 gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi Knight" S_COLOR_WHITE "\n" );
864 }
865 else if(handicap == 50 && crosshairHint == 0)
866 {
867 gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi Master" S_COLOR_WHITE "\n" );
868 }
869 else
870 {
871 gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi Knight (Custom)" S_COLOR_WHITE "\n" );
872 gi.Printf( S_COLOR_GREEN "Crosshair Force Hint: %i" S_COLOR_WHITE "\n", crosshairHint != 0 ? 1 : 0 );
873 gi.Printf( S_COLOR_GREEN "Handicap: %i" S_COLOR_WHITE "\n", handicap );
874 }
875 }
876 else
877 {
878 gi.Printf( S_COLOR_RED "Invalid difficulty cvar set! g_spskill (%i) [0-2] is valid range only" S_COLOR_WHITE "\n", g_spskill->integer );
879 }
880 }
881 }
882
883 #define CMD_NONE (0x00000000u)
884 #define CMD_CHEAT (0x00000001u)
885 #define CMD_ALIVE (0x00000002u)
886
887 typedef struct svcmd_s {
888 const char *name;
889 void (*func)(void);
890 uint32_t flags;
891 } svcmd_t;
892
svcmdcmp(const void * a,const void * b)893 static int svcmdcmp( const void *a, const void *b ) {
894 return Q_stricmp( (const char *)a, ((svcmd_t*)b)->name );
895 }
896
897 // FIXME some of these should be made CMD_ALIVE too!
898 static svcmd_t svcmds[] = {
899 { "entitylist", Svcmd_EntityList_f, CMD_NONE },
900 { "game_memory", Svcmd_GameMem_f, CMD_NONE },
901
902 { "nav", Svcmd_Nav_f, CMD_CHEAT },
903 { "npc", Svcmd_NPC_f, CMD_CHEAT },
904 { "use", Svcmd_Use_f, CMD_CHEAT },
905 { "ICARUS", Svcmd_ICARUS_f, CMD_CHEAT },
906
907 { "saberColor", Svcmd_SaberColor_f, CMD_CHEAT },
908 { "saber", Svcmd_Saber_f, CMD_CHEAT },
909 { "saberBlade", Svcmd_SaberBlade_f, CMD_CHEAT },
910
911 { "setForceJump", Svcmd_ForceSetLevel_f<FP_LEVITATION>, CMD_CHEAT },
912 { "setSaberThrow", Svcmd_ForceSetLevel_f<FP_SABERTHROW>, CMD_CHEAT },
913 { "setForceHeal", Svcmd_ForceSetLevel_f<FP_HEAL>, CMD_CHEAT },
914 { "setForcePush", Svcmd_ForceSetLevel_f<FP_PUSH>, CMD_CHEAT },
915 { "setForcePull", Svcmd_ForceSetLevel_f<FP_PULL>, CMD_CHEAT },
916 { "setForceSpeed", Svcmd_ForceSetLevel_f<FP_SPEED>, CMD_CHEAT },
917 { "setForceGrip", Svcmd_ForceSetLevel_f<FP_GRIP>, CMD_CHEAT },
918 { "setForceLightning", Svcmd_ForceSetLevel_f<FP_LIGHTNING>, CMD_CHEAT },
919 { "setMindTrick", Svcmd_ForceSetLevel_f<FP_TELEPATHY>, CMD_CHEAT },
920 { "setSaberDefense", Svcmd_ForceSetLevel_f<FP_SABER_DEFENSE>, CMD_CHEAT },
921 { "setSaberOffense", Svcmd_ForceSetLevel_f<FP_SABER_OFFENSE>, CMD_CHEAT },
922 { "setForceRage", Svcmd_ForceSetLevel_f<FP_RAGE>, CMD_CHEAT },
923 { "setForceDrain", Svcmd_ForceSetLevel_f<FP_DRAIN>, CMD_CHEAT },
924 { "setForceProtect", Svcmd_ForceSetLevel_f<FP_PROTECT>, CMD_CHEAT },
925 { "setForceAbsorb", Svcmd_ForceSetLevel_f<FP_ABSORB>, CMD_CHEAT },
926 { "setForceSight", Svcmd_ForceSetLevel_f<FP_SEE>, CMD_CHEAT },
927 { "setForceAll", Svcmd_SetForceAll_f, CMD_CHEAT },
928 { "setSaberAll", Svcmd_SetSaberAll_f, CMD_CHEAT },
929
930 { "saberAttackCycle", Svcmd_SaberAttackCycle_f, CMD_NONE },
931
932 { "runscript", Svcmd_RunScript_f, CMD_CHEAT },
933
934 { "playerTeam", Svcmd_PlayerTeam_f, CMD_CHEAT },
935
936 { "control", Svcmd_Control_f, CMD_CHEAT },
937 { "grab", Svcmd_Grab_f, CMD_CHEAT },
938 { "knockdown", Svcmd_Knockdown_f, CMD_CHEAT },
939
940 { "playerModel", Svcmd_PlayerModel_f, CMD_NONE },
941 { "playerTint", Svcmd_PlayerTint_f, CMD_NONE },
942
943 { "nexttestaxes", G_NextTestAxes, CMD_NONE },
944
945 { "exitview", Svcmd_ExitView_f, CMD_NONE },
946
947 { "iknowkungfu", Svcmd_IKnowKungfu_f, CMD_CHEAT },
948
949 { "secrets", Svcmd_Secrets_f, CMD_NONE },
950 { "difficulty", Svcmd_Difficulty_f, CMD_NONE },
951
952 //{ "say", Svcmd_Say_f, qtrue },
953 //{ "toggleallowvote", Svcmd_ToggleAllowVote_f, qfalse },
954 //{ "toggleuserinfovalidation", Svcmd_ToggleUserinfoValidation_f, qfalse },
955 };
956 static const size_t numsvcmds = ARRAY_LEN( svcmds );
957
958 /*
959 =================
960 ConsoleCommand
961 =================
962 */
ConsoleCommand(void)963 qboolean ConsoleCommand( void ) {
964 const char *cmd = gi.argv(0);
965 const svcmd_t *command = (const svcmd_t *)Q_LinearSearch( cmd, svcmds, numsvcmds, sizeof( svcmds[0] ), svcmdcmp );
966
967 if ( !command )
968 return qfalse;
969
970 if ( (command->flags & CMD_CHEAT)
971 && !g_cheats->integer )
972 {
973 gi.Printf( "Cheats are not enabled on this server.\n" );
974 return qtrue;
975 }
976 else if ( (command->flags & CMD_ALIVE)
977 && (g_entities[0].health <= 0) )
978 {
979 gi.Printf( "You must be alive to use this command.\n" );
980 return qtrue;
981 }
982 else
983 command->func();
984 return qtrue;
985 }
986
987