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