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 "../icarus/IcarusInterface.h"
25 #include "../cgame/cg_local.h"
26 #include "Q3_Interface.h"
27 #include "g_local.h"
28 #include "g_functions.h"
29 #include "anims.h"
30 #include "wp_saber.h"
31 #include "g_vehicles.h"
32 #include "objectives.h"
33 #include "b_local.h"
34 
35 extern int WP_SaberInitBladeData( gentity_t *ent );
36 extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum );
37 extern qboolean	CheatsOk( gentity_t *ent );
38 extern void Boba_Precache( void );
39 
40 extern cvar_t	*g_char_model;
41 extern cvar_t	*g_char_skin_head;
42 extern cvar_t	*g_char_skin_torso;
43 extern cvar_t	*g_char_skin_legs;
44 extern cvar_t	*g_char_color_red;
45 extern cvar_t	*g_char_color_green;
46 extern cvar_t	*g_char_color_blue;
47 extern cvar_t	*g_saber;
48 extern cvar_t	*g_saber2;
49 extern cvar_t	*g_saber_color;
50 extern cvar_t	*g_saber2_color;
51 extern cvar_t	*g_saberDarkSideSaberColor;
52 
53 // g_client.c -- client functions that don't happen every frame
54 
55 float DEFAULT_MINS_0 = -16;
56 float DEFAULT_MINS_1 = -16;
57 float DEFAULT_MAXS_0 = 16;
58 float DEFAULT_MAXS_1 = 16;
59 float DEFAULT_PLAYER_RADIUS	= sqrt((DEFAULT_MAXS_0*DEFAULT_MAXS_0) + (DEFAULT_MAXS_1*DEFAULT_MAXS_1));
60 vec3_t playerMins = {DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2};
61 vec3_t playerMinsStep = {DEFAULT_MINS_0, DEFAULT_MINS_1, DEFAULT_MINS_2+STEPSIZE};
62 vec3_t playerMaxs = {DEFAULT_MAXS_0, DEFAULT_MAXS_1, DEFAULT_MAXS_2};
63 
64 void SP_misc_teleporter_dest (gentity_t *ent);
65 
66 /*QUAK-ED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) KEEP_PREV DROPTOFLOOR x x x STUN_BATON NOWEAPON x
67 potential spawning position for deathmatch games.
68 Targets will be fired when someone spawns in on them.
69 */
SP_info_player_deathmatch(gentity_t * ent)70 void SP_info_player_deathmatch(gentity_t *ent) {
71 	SP_misc_teleporter_dest (ent);
72 
73 	if ( ent->spawnflags & 32 ) // STUN_BATON
74 	{
75 		RegisterItem( FindItemForWeapon( WP_STUN_BATON ));
76 	}
77 	else
78 	{
79 		RegisterItem( FindItemForWeapon( WP_SABER ) );	//these are given in ClientSpawn(), but we register them now before cgame starts
80 		saberInfo_t	saber;
81 		WP_SaberParseParms( g_saber->string, &saber );//get saber sounds and models cached before client begins
82 		if (saber.model) G_ModelIndex( saber.model );
83 		if (saber.brokenSaber1) G_ModelIndex( saber.brokenSaber1 );
84 		if (saber.brokenSaber2) G_ModelIndex( saber.brokenSaber2 );
85 		if (saber.skin) G_SkinIndex( saber.skin );
86 		WP_SaberFreeStrings(saber);
87 	}
88 }
89 
90 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) KEEP_PREV DROPTOFLOOR x x x STUN_BATON NOWEAPON x
91 KEEP_PREV - keep previous health + armor
92 DROPTOFLOOR - Player will start on the first solid structure under it
93 STUN_BATON - Gives player the stun baton and bryar pistol, but not the saber, plus any weapons they may have carried over from previous levels.
94 
95 Targets will be fired when someone spawns in on them.
96 equivalant to info_player_deathmatch
97 */
SP_info_player_start(gentity_t * ent)98 void SP_info_player_start(gentity_t *ent) {
99 	ent->classname = "info_player_deathmatch";
100 
101 	SP_info_player_deathmatch( ent );
102 }
103 
104 
105 
106 /*
107 =======================================================================
108 
109   SelectSpawnPoint
110 
111 =======================================================================
112 */
113 
114 /*
115 ================
116 SpotWouldTelefrag
117 
118 ================
119 */
SpotWouldTelefrag(gentity_t * spot,team_t checkteam)120 qboolean SpotWouldTelefrag( gentity_t *spot, team_t checkteam )
121 {
122 	int			i, num;
123 	gentity_t	*touch[MAX_GENTITIES], *hit;
124 	vec3_t		mins, maxs;
125 
126 	// If we have a mins, use that instead of the hardcoded bounding box
127 	if ( !VectorCompare(spot->mins, vec3_origin) && VectorLength( spot->mins ) )
128 		VectorAdd( spot->s.origin, spot->mins, mins );
129 	else
130 		VectorAdd( spot->s.origin, playerMins, mins );
131 
132 	// If we have a maxs, use that instead of the hardcoded bounding box
133 	if ( !VectorCompare(spot->maxs, vec3_origin) && VectorLength( spot->maxs ) )
134 		VectorAdd( spot->s.origin, spot->maxs, maxs );
135 	else
136 		VectorAdd( spot->s.origin, playerMaxs, maxs );
137 
138 	num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
139 
140 	for (i=0 ; i<num ; i++)
141 	{
142 		hit = touch[i];
143 		if ( hit != spot && hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 )
144 		{
145 			if ( hit->contents & CONTENTS_BODY )
146 			{
147 				if( checkteam == TEAM_FREE || hit->client->playerTeam == checkteam )
148 				{//checking against teammates only...?
149 					return qtrue;
150 				}
151 			}
152 		}
153 	}
154 
155 	return qfalse;
156 }
157 
SpotWouldTelefrag2(gentity_t * mover,vec3_t dest)158 qboolean SpotWouldTelefrag2( gentity_t *mover, vec3_t dest )
159 {
160 	int			i, num;
161 	gentity_t	*touch[MAX_GENTITIES], *hit;
162 	vec3_t		mins, maxs;
163 
164 	VectorAdd( dest, mover->mins, mins );
165 	VectorAdd( dest, mover->maxs, maxs );
166 	num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
167 
168 	for (i=0 ; i<num ; i++)
169 	{
170 		hit = touch[i];
171 		if ( hit == mover )
172 		{
173 			continue;
174 		}
175 
176 		if ( hit->contents & mover->contents )
177 		{
178 			return qtrue;
179 		}
180 	}
181 
182 	return qfalse;
183 }
184 /*
185 ================
186 SelectNearestDeathmatchSpawnPoint
187 
188 Find the spot that we DON'T want to use
189 ================
190 */
191 #define	MAX_SPAWN_POINTS	128
SelectNearestDeathmatchSpawnPoint(vec3_t from,team_t team)192 gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from, team_t team ) {
193 	gentity_t	*spot;
194 	float		dist, nearestDist;
195 	gentity_t	*nearestSpot;
196 
197 	nearestDist = (float)WORLD_SIZE*(float)WORLD_SIZE;
198 	nearestSpot = NULL;
199 	spot = NULL;
200 
201 	while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
202 		/*if ( team == TEAM_RED && ( spot->spawnflags & 2 ) ) {
203 			continue;
204 		}
205 		if ( team == TEAM_BLUE && ( spot->spawnflags & 1 ) ) {
206 			continue;
207 		}*/
208 
209 		if ( spot->targetname != NULL ) {
210 			//this search routine should never find a spot that is targetted
211 			continue;
212 		}
213 		dist = DistanceSquared( spot->s.origin, from );
214 		if ( dist < nearestDist ) {
215 			nearestDist = dist;
216 			nearestSpot = spot;
217 		}
218 	}
219 
220 	return nearestSpot;
221 }
222 
223 
224 /*
225 ================
226 SelectRandomDeathmatchSpawnPoint
227 
228 go to a random point that doesn't telefrag
229 ================
230 */
231 #define	MAX_SPAWN_POINTS	128
SelectRandomDeathmatchSpawnPoint(team_t team)232 gentity_t *SelectRandomDeathmatchSpawnPoint( team_t team ) {
233 	gentity_t	*spot;
234 	int			count;
235 	int			selection;
236 	gentity_t	*spots[MAX_SPAWN_POINTS];
237 
238 	count = 0;
239 	spot = NULL;
240 
241 	while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) {
242 		/*if ( team == TEAM_RED && ( spot->spawnflags & 2 ) ) {
243 			continue;
244 		}
245 		if ( team == TEAM_BLUE && ( spot->spawnflags & 1 ) ) {
246 			continue;
247 		}*/
248 
249 		if ( spot->targetname != NULL ) {
250 			//this search routine should never find a spot that is targetted
251 			continue;
252 		}
253 		if ( SpotWouldTelefrag( spot, TEAM_FREE ) ) {
254 			continue;
255 		}
256 		spots[ count ] = spot;
257 		count++;
258 	}
259 
260 	if ( !count ) {	// no spots that won't telefrag
261 		spot = G_Find( NULL, FOFS(classname), "info_player_deathmatch");
262 		if ( !spot )
263 		{
264 			return NULL;
265 		}
266 		if ( spot->targetname != NULL )
267 		{
268 			//this search routine should never find a spot that is targetted
269 			return NULL;
270 		}
271 		else
272 		{
273 			return spot;
274 		}
275 	}
276 
277 	selection = rand() % count;
278 	return spots[ selection ];
279 }
280 
281 
282 /*
283 ===========
284 SelectSpawnPoint
285 
286 Chooses a player start, deathmatch start, etc
287 ============
288 */
SelectSpawnPoint(vec3_t avoidPoint,team_t team,vec3_t origin,vec3_t angles)289 gentity_t *SelectSpawnPoint ( vec3_t avoidPoint, team_t team, vec3_t origin, vec3_t angles ) {
290 	gentity_t	*spot;
291 	gentity_t	*nearestSpot;
292 
293 	if ( level.spawntarget[0] )
294 	{//we have a spawnpoint specified, try to find it
295 		if ( (nearestSpot = spot = G_Find( NULL, FOFS(targetname), level.spawntarget )) == NULL )
296 		{//you HAVE to be able to find the desired spot
297 			G_Error( "Couldn't find spawntarget %s\n", level.spawntarget );
298 			return NULL;
299 		}
300 	}
301 	else
302 	{//not looking for a special startspot
303 		nearestSpot = SelectNearestDeathmatchSpawnPoint( avoidPoint, team );
304 
305 		spot = SelectRandomDeathmatchSpawnPoint ( team );
306 		if ( spot == nearestSpot ) {
307 			// roll again if it would be real close to point of death
308 			spot = SelectRandomDeathmatchSpawnPoint ( team );
309 		}
310 	}
311 
312 	// find a single player start spot
313 	if (!spot) {
314 		G_Error( "Couldn't find a spawn point\n" );
315 	}
316 
317 
318 	VectorCopy( spot->s.origin, origin );
319 	if ( spot->spawnflags & 2 )
320 	{
321 		trace_t		tr;
322 
323 		origin[2] = MIN_WORLD_COORD;
324 		gi.trace(&tr, spot->s.origin, playerMins, playerMaxs, origin, ENTITYNUM_NONE, MASK_PLAYERSOLID, (EG2_Collision)0, 0 );
325 		if ( tr.fraction < 1.0 && !tr.allsolid && !tr.startsolid )
326 		{//found a floor
327 			VectorCopy(tr.endpos, origin );
328 		}
329 		else
330 		{//In solid or too far
331 			VectorCopy( spot->s.origin, origin );
332 		}
333 	}
334 
335 	origin[2] += 9;
336 	VectorCopy (spot->s.angles, angles);
337 
338 	return spot;
339 }
340 
341 
342 //======================================================================
343 
344 
345 /*
346 ==================
347 SetClientViewAngle
348 
349 ==================
350 */
SetClientViewAngle(gentity_t * ent,vec3_t angle)351 void SetClientViewAngle( gentity_t *ent, vec3_t angle ) {
352 	int			i;
353 
354 	// set the delta angle
355 	for (i=0 ; i<3 ; i++)
356 	{
357 		ent->client->ps.delta_angles[i] = (ANGLE2SHORT(angle[i]) - ent->client->pers.cmd_angles[i])&0xffff;
358 	}
359 	VectorCopy( angle, ent->s.angles );
360 	VectorCopy (ent->s.angles, ent->client->ps.viewangles);
361 }
362 
363 /*
364 ================
365 respawn
366 ================
367 */
respawn(gentity_t * ent)368 void respawn( gentity_t *ent ) {
369 
370 	gi.SendConsoleCommand("load *respawn\n");	// special case
371 }
372 
373 /*
374 ===========
375 ClientCheckName
376 ============
377 */
ClientCleanName(const char * in,char * out,int outSize)378 static void ClientCleanName( const char *in, char *out, int outSize )
379 {
380 	int outpos = 0, colorlessLen = 0, spaces = 0, ats = 0;
381 
382 	// discard leading spaces
383 	for ( ; *in == ' '; in++);
384 
385 	// discard leading asterisk's (fail raven for using * as a skipnotify)
386 	// apparently .* causes the issue too so... derp
387 	//for(; *in == '*'; in++);
388 
389 	for(; *in && outpos < outSize - 1; in++)
390 	{
391 		out[outpos] = *in;
392 
393 		if ( *in == ' ' )
394 		{// don't allow too many consecutive spaces
395 			if ( spaces > 2 )
396 				continue;
397 
398 			spaces++;
399 		}
400 		else if ( *in == '@' )
401 		{// don't allow too many consecutive at signs
402 			if ( ats > 2 )
403 				continue;
404 
405 			ats++;
406 		}
407 		else if ( outpos > 0 && out[outpos-1] == Q_COLOR_ESCAPE )
408 		{
409 			if ( Q_IsColorStringExt( &out[outpos-1] ) )
410 			{
411 				colorlessLen--;
412 
413 #if 0
414 				if ( ColorIndex( *in ) == 0 )
415 				{// Disallow color black in names to prevent players from getting advantage playing in front of black backgrounds
416 					outpos--;
417 					continue;
418 				}
419 #endif
420 			}
421 			else
422 			{
423 				spaces = ats = 0;
424 				colorlessLen++;
425 			}
426 		}
427 		else
428 		{
429 			spaces = ats = 0;
430 			colorlessLen++;
431 		}
432 
433 		outpos++;
434 	}
435 
436 	out[outpos] = '\0';
437 
438 	// don't allow empty names
439 	if ( *out == '\0' || colorlessLen == 0 )
440 		Q_strncpyz( out, "Padawan", outSize );
441 }
442 
443 /*
444 ===========
445 ClientUserInfoChanged
446 
447 Called from ClientConnect when the player first connects and
448 directly by the server system when the player updates a userinfo variable.
449 
450 The game can override any of the settings and call gi.SetUserinfo
451 if desired.
452 ============
453 */
ClientUserinfoChanged(int clientNum)454 void ClientUserinfoChanged( int clientNum ) {
455 	gentity_t	*ent = g_entities + clientNum;
456 	gclient_t	*client = ent->client;
457 	int			health=100, maxHealth=100;
458 	const char	*s=NULL;
459 	char		userinfo[MAX_INFO_STRING]={0},	buf[MAX_INFO_STRING]={0},
460 				sound[MAX_STRING_CHARS]={0},	oldname[34]={0};
461 
462 	gi.GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
463 
464 	// check for malformed or illegal info strings
465 	/*if ( !Info_Validate(userinfo) ) {
466 		strcpy (userinfo, "\\name\\badinfo");
467 	}*/
468 
469 	// set name
470 	Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) );
471 	s = Info_ValueForKey (userinfo, "name");
472 	ClientCleanName( s, client->pers.netname, sizeof( client->pers.netname ) );
473 
474 	// set max health
475 	maxHealth = 100;
476 	health = Com_Clampi( 1, 100, atoi( Info_ValueForKey( userinfo, "handicap" ) ) );
477 	client->pers.maxHealth = health;
478 	if ( client->pers.maxHealth < 1 || client->pers.maxHealth > maxHealth )
479 		client->pers.maxHealth = 100;
480 	client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
481 
482 	// sounds
483 	Q_strncpyz( sound, Info_ValueForKey (userinfo, "snd"), sizeof( sound ) );
484 
485 	// send over a subset of the userinfo keys so other clients can
486 	// print scoreboards, display models, and play custom sounds
487 	buf[0] = '\0';
488 	Q_strcat( buf, sizeof( buf ), va( "n\\%s\\", client->pers.netname ) );
489 	Q_strcat( buf, sizeof( buf ), va( "t\\%i\\", client->sess.sessionTeam ) );
490 	Q_strcat( buf, sizeof( buf ),	  "headModel\\\\" );
491 	Q_strcat( buf, sizeof( buf ),	  "torsoModel\\\\" );
492 	Q_strcat( buf, sizeof( buf ),	  "legsModel\\\\" );
493 	Q_strcat( buf, sizeof( buf ), va( "hc\\%i\\", client->pers.maxHealth ) );
494 	Q_strcat( buf, sizeof( buf ), va( "snd\\%s\\", sound ) );
495 
496 	gi.SetConfigstring( CS_PLAYERS+clientNum, buf );
497 }
498 
499 
500 /*
501 ===========
502 ClientConnect
503 
504 Called when a player begins connecting to the server.
505 Called again for every map change or tournement restart.
506 
507 The session information will be valid after exit.
508 
509 Return NULL if the client should be allowed, otherwise return
510 a string with the reason for denial.
511 
512 Otherwise, the client will be sent the current gamestate
513 and will eventually get to ClientBegin.
514 
515 firstTime will be qtrue the very first time a client connects
516 to the server machine, but qfalse on map changes and tournement
517 restarts.
518 ============
519 */
ClientConnect(int clientNum,qboolean firstTime,SavedGameJustLoaded_e eSavedGameJustLoaded)520 char *ClientConnect( int clientNum, qboolean firstTime, SavedGameJustLoaded_e eSavedGameJustLoaded )
521 {
522 	gentity_t	*ent = &g_entities[ clientNum ];
523 	char		userinfo[MAX_INFO_STRING] = {0};
524 
525 	gi.GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
526 
527 	// they can connect
528 	ent->client = level.clients + clientNum;
529 	gclient_t *client = ent->client;
530 
531 //	if (!qbFromSavedGame)
532 	if (eSavedGameJustLoaded != eFULL)
533 	{
534 		clientSession_t savedSess = client->sess;	//
535 		memset( client, 0, sizeof(*client) );
536 		client->sess = savedSess;
537 		if ( firstTime ) {	//not loading full, and directconnect
538 			client->playerTeam = TEAM_PLAYER;	//set these now because after an auto_load kyle can see your team for a bit before you really join.
539 			client->enemyTeam = TEAM_ENEMY;
540 		}
541 	}
542 
543 	client->pers.connected = CON_CONNECTING;
544 
545 	if (eSavedGameJustLoaded == eFULL)//qbFromSavedGame)
546 	{
547 		// G_WriteClientSessionData( client ); // forget it, this is DM stuff anyway
548 		// get and distribute relevent paramters
549 		ClientUserinfoChanged( clientNum );
550 	}
551 	else
552 	{
553 		// read or initialize the session data
554 		if ( firstTime ) {
555 			G_InitSessionData( client, userinfo );
556 		}
557 		G_ReadSessionData( client );
558 
559 		// get and distribute relevent paramters
560 		ClientUserinfoChanged( clientNum );
561 
562 		// don't do the "xxx connected" messages if they were caried over from previous level
563 		if ( firstTime ) {
564 			gi.SendServerCommand( -1, "print \"%s connected\n\"", client->pers.netname);
565 		}
566 	}
567 
568 	return NULL;
569 }
570 
571 /*
572 ===========
573 ClientBegin
574 
575 called when a client has finished connecting, and is ready
576 to be placed into the level.  This will happen every level load,
577 and on transition between teams, but doesn't happen on respawns
578 ============
579 */
ClientBegin(int clientNum,usercmd_t * cmd,SavedGameJustLoaded_e eSavedGameJustLoaded)580 void ClientBegin( int clientNum, usercmd_t *cmd, SavedGameJustLoaded_e eSavedGameJustLoaded)
581 //												qboolean qbFromSavedGame
582 {
583 	gentity_t	*ent;
584 	gclient_t	*client;
585 
586 	ent = g_entities + clientNum;
587 	client = level.clients + clientNum;
588 
589 	if (eSavedGameJustLoaded == eFULL)//qbFromSavedGame)
590 	{
591 		client->pers.connected = CON_CONNECTED;
592 		ent->client = client;
593 		ClientSpawn( ent, eSavedGameJustLoaded );
594 	}
595 	else
596 	{
597 		if ( ent->linked ) {
598 			gi.unlinkentity( ent );
599 		}
600 		G_InitGentity( ent, qfalse );
601 		ent->e_TouchFunc = touchF_NULL;
602 		ent->e_PainFunc  = painF_PlayerPain;//painF_NULL;
603 		ent->client = client;
604 
605 		client->pers.connected = CON_CONNECTED;
606 		client->pers.teamState.state = TEAM_BEGIN;
607 		VectorCopyM( cmd->angles, client->pers.cmd_angles );
608 
609 		memset( &client->ps, 0, sizeof( client->ps ) );
610 		if( gi.Cvar_VariableIntegerValue( "g_clearstats" ) )
611 		{
612 			memset( &client->sess.missionStats, 0, sizeof( client->sess.missionStats ) );
613 			client->sess.missionStats.totalSecrets = gi.Cvar_VariableIntegerValue("newTotalSecrets");
614 		}
615 
616 		// locate ent at a spawn point
617 		if ( ClientSpawn( ent, eSavedGameJustLoaded) )	// SavedGameJustLoaded_e
618 		{
619 			// send teleport event
620 		}
621 		client->ps.inventory[INV_GOODIE_KEY] = 0;
622 		client->ps.inventory[INV_SECURITY_KEY] = 0;
623 	}
624 }
625 
626 
627 
628 /*
629 ============
630 Player_CacheFromPrevLevel
631   Description	: just need to grab the weapon items we're going to have when we spawn so they'll be cached
632   Return type	: void
633   Argument		: void
634 ============
635 */
Player_CacheFromPrevLevel(void)636 void Player_CacheFromPrevLevel(void)
637 {
638 	char	s[MAX_STRING_CHARS];
639 	int i;
640 
641 	gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
642 
643 	if (s[0])	// actually this would be safe anyway because of the way sscanf() works, but this is clearer
644 	{
645 		int iDummy, bits, ibits;
646 
647 		sscanf( s, "%i %i %i %i",
648 			&iDummy,//client->ps.stats[STAT_HEALTH],
649 			&iDummy,//client->ps.stats[STAT_ARMOR],
650 			&bits,	//client->ps.stats[STAT_WEAPONS]
651 			&ibits	//client->ps.stats[STAT_ITEMS]
652 			);
653 
654 		for ( i = 1 ; i < 16 ; i++ )
655 		{
656 			if ( bits & ( 1 << i ) )
657 			{
658 				RegisterItem( FindItemForWeapon( (weapon_t)i ) );
659 			}
660 		}
661 
662 extern gitem_t	*FindItemForInventory( int inv );
663 
664 		for ( i = 0 ; i < 16 ; i++ )
665 		{
666 			if ( ibits & ( 1 << i ) )
667 			{
668 				RegisterItem( FindItemForInventory( i ));
669 			}
670 		}
671 	}
672 }
673 
674 /*
675 ============
676 Player_RestoreFromPrevLevel
677   Description	: retrieve maptransition data recorded by server when exiting previous level (to carry over weapons/ammo/health/etc)
678   Return type	: void
679   Argument		: gentity_t *ent
680 ============
681 */
Player_RestoreFromPrevLevel(gentity_t * ent,SavedGameJustLoaded_e eSavedGameJustLoaded)682 static void Player_RestoreFromPrevLevel(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded)
683 {
684 	gclient_t	*client = ent->client;
685 	int			i;
686 
687 	assert(client);
688 	if (client)	// though I can't see it not being true...
689 	{
690 		char	s[MAX_STRING_CHARS];
691 		char	saber0Name[MAX_QPATH];
692 		char	saber1Name[MAX_QPATH];
693 		const char	*var;
694 
695 		gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
696 
697 		if (strlen(s))	// actually this would be safe anyway because of the way sscanf() works, but this is clearer
698 		{//				|general info				  |-force powers |-saber 1										   |-saber 2										  |-general saber
699 			int saber1BladeActive[8];
700 			int saber2BladeActive[8];
701 			unsigned int saber1BladeColor[8];
702 			unsigned int saber2BladeColor[8];
703 
704 			sscanf( s, "%i %i %i %i %i %i %i %f %f %f %i %i %i %i %i %s %i %i %i %i %i %i %i %i %u %u %u %u %u %u %u %u %s %i %i %i %i %i %i %i %i %u %u %u %u %u %u %u %u %i %i %i %i",
705 								&client->ps.stats[STAT_HEALTH],
706 								&client->ps.stats[STAT_ARMOR],
707 								&client->ps.stats[STAT_WEAPONS],
708 								&client->ps.stats[STAT_ITEMS],
709 								&client->ps.weapon,
710 								&client->ps.weaponstate,
711 								&client->ps.batteryCharge,
712 								&client->ps.viewangles[0],
713 								&client->ps.viewangles[1],
714 								&client->ps.viewangles[2],
715 								//force power data
716 								&client->ps.forcePowersKnown,
717 								&client->ps.forcePower,
718 								&client->ps.forcePowerMax,
719 								&client->ps.forcePowerRegenRate,
720 								&client->ps.forcePowerRegenAmount,
721 								//saber 1 data
722 								saber0Name,
723 								&saber1BladeActive[0],
724 								&saber1BladeActive[1],
725 								&saber1BladeActive[2],
726 								&saber1BladeActive[3],
727 								&saber1BladeActive[4],
728 								&saber1BladeActive[5],
729 								&saber1BladeActive[6],
730 								&saber1BladeActive[7],
731 								&saber1BladeColor[0],
732 								&saber1BladeColor[1],
733 								&saber1BladeColor[2],
734 								&saber1BladeColor[3],
735 								&saber1BladeColor[4],
736 								&saber1BladeColor[5],
737 								&saber1BladeColor[6],
738 								&saber1BladeColor[7],
739 								//saber 2 data
740 								saber1Name,
741 								&saber2BladeActive[0],
742 								&saber2BladeActive[1],
743 								&saber2BladeActive[2],
744 								&saber2BladeActive[3],
745 								&saber2BladeActive[4],
746 								&saber2BladeActive[5],
747 								&saber2BladeActive[6],
748 								&saber2BladeActive[7],
749 								&saber2BladeColor[0],
750 								&saber2BladeColor[1],
751 								&saber2BladeColor[2],
752 								&saber2BladeColor[3],
753 								&saber2BladeColor[4],
754 								&saber2BladeColor[5],
755 								&saber2BladeColor[6],
756 								&saber2BladeColor[7],
757 								//general saber data
758 								&client->ps.saberStylesKnown,
759 								&client->ps.saberAnimLevel,
760 								&client->ps.saberLockEnemy,
761 								&client->ps.saberLockTime
762 					);
763 			for (int j = 0; j < 8; j++)
764 			{
765 				client->ps.saber[0].blade[j].active = saber1BladeActive[j] ? qtrue : qfalse;
766 				client->ps.saber[0].blade[j].color = (saber_colors_t)saber1BladeColor[j];
767 				client->ps.saber[1].blade[j].active = saber2BladeActive[j] ? qtrue : qfalse;
768 				client->ps.saber[1].blade[j].color = (saber_colors_t)saber2BladeColor[j];
769 			}
770 
771 			ent->health = client->ps.stats[STAT_HEALTH];
772 
773 			if(ent->client->ps.saber[0].name && gi.bIsFromZone(ent->client->ps.saber[0].name, TAG_G_ALLOC)) {
774 				gi.Free(ent->client->ps.saber[0].name);
775 			}
776 			ent->client->ps.saber[0].name=0;
777 
778 			if(ent->client->ps.saber[1].name && gi.bIsFromZone(ent->client->ps.saber[1].name, TAG_G_ALLOC) ) {
779 				gi.Free(ent->client->ps.saber[1].name);
780 			}
781 			ent->client->ps.saber[1].name=0;
782 			//NOTE: if sscanf can get a "(null)" out of strings that had NULL string pointers plugged into the original string
783 			if ( saber0Name[0] && Q_stricmp( "(null)", saber0Name ) != 0 )
784 			{
785 				ent->client->ps.saber[0].name = G_NewString( saber0Name );
786 			}
787 			if ( saber1Name[0] && Q_stricmp( "(null)", saber1Name ) != 0 )
788 			{//have a second saber
789 				ent->client->ps.saber[1].name = G_NewString( saber1Name );
790 				ent->client->ps.dualSabers = qtrue;
791 			}
792 			else
793 			{//have only 1 saber
794 				ent->client->ps.dualSabers = qfalse;
795 			}
796 
797 // slight issue with ths for the moment in that although it'll correctly restore angles it doesn't take into account
798 //	the overall map orientation, so (eg) exiting east to enter south will be out by 90 degrees, best keep spawn angles for now
799 //
800 //			VectorClear (ent->client->pers.cmd_angles);
801 //
802 //			SetClientViewAngle( ent, ent->client->ps.viewangles);
803 
804 			//ammo
805 			gi.Cvar_VariableStringBuffer( "playerammo", s, sizeof(s) );
806 			i=0;
807 			var = strtok( s, " " );
808 			while( var != NULL )
809 			{
810 			  /* While there are tokens in "s" */
811 			  client->ps.ammo[i++] = atoi(var);
812 			  /* Get next token: */
813 			  var = strtok( NULL, " " );
814 			}
815 			assert (i==AMMO_MAX);
816 
817 			//inventory
818 			gi.Cvar_VariableStringBuffer( "playerinv", s, sizeof(s) );
819 			i=0;
820 			var = strtok( s, " " );
821 			while( var != NULL )
822 			{
823 			  /* While there are tokens in "s" */
824 			  client->ps.inventory[i++] = atoi(var);
825 			  /* Get next token: */
826 			  var = strtok( NULL, " " );
827 			}
828 			assert (i==INV_MAX);
829 
830 
831 			// the new JK2 stuff - force powers, etc...
832 			//
833 			gi.Cvar_VariableStringBuffer( "playerfplvl", s, sizeof(s) );
834 			i=0;
835 			var = strtok( s, " " );
836 			while( var != NULL )
837 			{
838 				/* While there are tokens in "s" */
839 				  client->ps.forcePowerLevel[i++] = atoi(var);
840 				/* Get next token: */
841 				var = strtok( NULL, " " );
842 			}
843 			assert (i==NUM_FORCE_POWERS);
844 
845 			client->ps.forceGripEntityNum = client->ps.forceDrainEntityNum = ENTITYNUM_NONE;
846 		}
847 	}
848 }
849 
850 /*
851 Ghoul2 Insert Start
852 */
853 
G_SetSkin(gentity_t * ent)854 static void G_SetSkin( gentity_t *ent )
855 {
856 	char	skinName[MAX_QPATH];
857 	//ok, lets register the skin name, and then pass that name to the config strings so the client can get it too.
858 	if (Q_stricmp( "hoth2", level.mapname ) == 0	//hack, is this the only map?
859 		||
860 		Q_stricmp( "hoth3", level.mapname ) == 0	// no! ;-)
861 		)
862 	{
863 		Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s|%s|%s", g_char_model->string, g_char_skin_head->string, "torso_g1", "lower_e1" );
864 	}
865 	else if(Q_stricmp(g_char_skin_head->string, "model_default") == 0 && Q_stricmp(g_char_skin_torso->string, "model_default") == 0 && Q_stricmp(g_char_skin_legs->string, "model_default") == 0)
866 	{
867 		Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", g_char_model->string );
868 	}
869 	else
870 	{
871 		Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s|%s|%s", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string );
872 	}
873 
874 	// lets see if it's out there
875 	int skin = gi.RE_RegisterSkin( skinName );
876 	if ( skin )
877 	{//what if this returns 0 because *one* part of a multi-skin didn't load?
878 		// put it in the config strings
879 		// and set the ghoul2 model to use it
880 		gi.G2API_SetSkin( &ent->ghoul2[ent->playerModel], G_SkinIndex( skinName ), skin );
881 	}
882 
883 	//color tinting
884 	if ( g_char_color_red->integer
885 		|| g_char_color_green->integer
886 		|| g_char_color_blue->integer )
887 	{
888 		ent->client->renderInfo.customRGBA[0] = g_char_color_red->integer;
889 		ent->client->renderInfo.customRGBA[1] = g_char_color_green->integer;
890 		ent->client->renderInfo.customRGBA[2] = g_char_color_blue->integer;
891 		ent->client->renderInfo.customRGBA[3] = 255;
892 	}
893 }
894 
G_StandardHumanoid(gentity_t * self)895 qboolean G_StandardHumanoid( gentity_t *self )
896 {
897 	if ( !self || !self->ghoul2.size() )
898 	{
899 		return qfalse;
900 	}
901 	if ( self->playerModel < 0 || self->playerModel >= self->ghoul2.size() )
902 	{
903 		return qfalse;
904 	}
905 	const char	*GLAName = gi.G2API_GetGLAName( &self->ghoul2[self->playerModel] );
906 	assert(GLAName);
907 	if (GLAName)
908 	{
909 		if ( !Q_stricmpn( "models/players/_humanoid", GLAName, 24 ) )///_humanoid", GLAName, 36) )
910 		{//only _humanoid skeleton is expected to have these
911 			return qtrue;
912 		}
913 		if ( !Q_stricmp( "models/players/protocol/protocol", GLAName ) )
914 		{//protocol droid duplicates many of these
915 			return qtrue;
916 		}
917 		if ( !Q_stricmp( "models/players/assassin_droid/model", GLAName ) )
918 		{//assassin_droid duplicates many of these
919 			return qtrue;
920 		}
921 		if ( !Q_stricmp( "models/players/saber_droid/model", GLAName ) )
922 		{//saber_droid duplicates many of these
923 			return qtrue;
924 		}
925 		if ( !Q_stricmp( "models/players/hazardtrooper/hazardtrooper", GLAName ) )
926 		{//hazardtrooper duplicates many of these
927 			return qtrue;
928 		}
929 		if ( !Q_stricmp( "models/players/rockettrooper/rockettrooper", GLAName ) )
930 		{//rockettrooper duplicates many of these
931 			return qtrue;
932 		}
933 		if ( !Q_stricmp( "models/players/wampa/wampa", GLAName ) )
934 		{//rockettrooper duplicates many of these
935 			return qtrue;
936 		}
937 	}
938 	return qfalse;
939 }
940 
G_ClassHasBadBones(int NPC_class)941 qboolean G_ClassHasBadBones( int NPC_class )
942 {
943 	switch ( NPC_class )
944 	{
945 	case CLASS_WAMPA:
946 	case CLASS_ROCKETTROOPER:
947 	case CLASS_SABER_DROID:
948 	case CLASS_HAZARD_TROOPER:
949 	case CLASS_ASSASSIN_DROID:
950 	case CLASS_RANCOR:
951 		return qtrue;
952 	}
953 	return qfalse;
954 }
955 
956 const char *AxesNames[] =
957 {
958 	"ORIGIN",//ORIGIN,
959 	"POSITIVE_X",//POSITIVE_X,
960 	"POSITIVE_Z",//POSITIVE_Z,
961 	"POSITIVE_Y",//POSITIVE_Y,
962 	"NEGATIVE_X",//NEGATIVE_X,
963 	"NEGATIVE_Z",//NEGATIVE_Z,
964 	"NEGATIVE_Y"//NEGATIVE_Y
965 };
966 
967 Eorientations testAxes[3]={POSITIVE_X,POSITIVE_Z,POSITIVE_Y};
968 int axes_0 = POSITIVE_X;
969 int axes_1 = POSITIVE_Z;
970 int axes_2 = POSITIVE_Y;
G_NextTestAxes(void)971 void G_NextTestAxes( void )
972 {
973 	static int whichAxes = 0;
974 	int axesCount = 0;
975 	do
976 	{
977 		whichAxes++;
978 		if ( whichAxes > 216 )
979 		{
980 			whichAxes = 0;
981 			Com_Printf( S_COLOR_RED"WRAPPED\n" );
982 			break;
983 		}
984 		axesCount = 0;
985 		axes_0 = 0;
986 		axes_1 = 0;
987 		axes_2 = 0;
988 		for ( axes_0 = 0; axes_0 < 6 && (axesCount<whichAxes); axes_0++ )
989 		{
990 			axesCount++;
991 			for ( axes_1 = 0; axes_1 < 6 && (axesCount<whichAxes); axes_1++ )
992 			{
993 				axesCount++;
994 				for ( axes_2 = 0; axes_2 < 6 && (axesCount<whichAxes); axes_2++ )
995 				{
996 					axesCount++;
997 				}
998 			}
999 		}
1000 		testAxes[0] = (Eorientations)((axes_0%6)+1);
1001 		testAxes[1] = (Eorientations)((axes_1%6)+1);
1002 		testAxes[2] = (Eorientations)((axes_2%6)+1);
1003 	} while ( testAxes[1] == testAxes[0] || (testAxes[1]-testAxes[0]) == 3 || (testAxes[0]-testAxes[1]) == 3
1004 		|| testAxes[2] == testAxes[0] || (testAxes[2]-testAxes[0]) == 3 || (testAxes[0]-testAxes[2]) == 3
1005 		|| testAxes[2] == testAxes[1] || (testAxes[2]-testAxes[1]) == 3 || (testAxes[1]-testAxes[2]) == 3 );
1006 
1007 	Com_Printf( "Up: %s\nRight: %s\nForward: %s\n", AxesNames[testAxes[0]], AxesNames[testAxes[1]], AxesNames[testAxes[2]] );
1008 	if ( testAxes[0] == POSITIVE_X
1009 		&& testAxes[1] == POSITIVE_Z
1010 		&& testAxes[2] == POSITIVE_Y )
1011 	{
1012 		Com_Printf( S_COLOR_RED"WRAPPED\n" );
1013 	}
1014 }
1015 
G_BoneOrientationsForClass(int NPC_class,const char * boneName,Eorientations * oUp,Eorientations * oRt,Eorientations * oFwd)1016 void G_BoneOrientationsForClass( int NPC_class, const char *boneName, Eorientations *oUp, Eorientations *oRt, Eorientations *oFwd )
1017 {
1018 	//defaults
1019 	*oUp = POSITIVE_X;
1020 	*oRt = NEGATIVE_Y;
1021 	*oFwd = NEGATIVE_Z;
1022 	//switch off class
1023 	switch ( NPC_class )
1024 	{
1025 	case CLASS_RANCOR:
1026 		*oUp = NEGATIVE_X;
1027 		*oRt = POSITIVE_Y;
1028 		*oFwd = POSITIVE_Z;
1029 		//*oUp = testAxes[0];
1030 		//*oRt = testAxes[1];
1031 		//*oFwd = testAxes[2];
1032 		break;
1033 	case CLASS_ROCKETTROOPER:
1034 	case CLASS_HAZARD_TROOPER:
1035 		//Root is:
1036 		//*oUp = POSITIVE_Z;
1037 		//*oRt = NEGATIVE_X;
1038 		//*oFwd = NEGATIVE_Y;
1039 		if ( Q_stricmp( "pelvis", boneName ) == 0 )
1040 		{//child of root
1041 			//in ModView:
1042 			//*oUp = NEGATIVE_X;
1043 			//*oRt = NEGATIVE_Z;
1044 			//*oFwd = NEGATIVE_Y;
1045 			//actual, when differences with root are accounted for:
1046 			*oUp = POSITIVE_Z;
1047 			*oRt = NEGATIVE_X;
1048 			*oFwd = NEGATIVE_Y;
1049 		}
1050 		else
1051 		{//all the rest are the same, children of root (not pelvis)
1052 			//in ModView:
1053 			//*oUp = POSITIVE_X;
1054 			//*oRt = POSITIVE_Y;
1055 			//*oFwd = POSITIVE_Z;
1056 			//actual, when differences with root are accounted for:
1057 			//*oUp = POSITIVE_Z;
1058 			//*oRt = NEGATIVE_Y;
1059 			//*oFwd = NEGATIVE_X;
1060 			*oUp = NEGATIVE_X;
1061 			*oRt = POSITIVE_Y;
1062 			*oFwd = POSITIVE_Z;
1063 		}
1064 		break;
1065 	case CLASS_SABER_DROID:
1066 		if ( Q_stricmp( "pelvis", boneName ) == 0
1067 			|| Q_stricmp( "thoracic", boneName ) == 0 )
1068 		{
1069 			*oUp = NEGATIVE_X;
1070 			*oRt = NEGATIVE_Z;
1071 			*oFwd = NEGATIVE_Y;
1072 		}
1073 		else
1074 		{
1075 			*oUp = NEGATIVE_X;//POSITIVE_X;
1076 			*oRt = POSITIVE_Y;
1077 			*oFwd = POSITIVE_Z;
1078 		}
1079 		break;
1080 	case CLASS_WAMPA:
1081 		if ( Q_stricmp( "pelvis", boneName ) == 0 )
1082 		{
1083 			*oUp = NEGATIVE_X;
1084 			*oRt = POSITIVE_Y;
1085 			*oFwd = NEGATIVE_Z;
1086 		}
1087 		else
1088 		{
1089 			//*oUp = POSITIVE_X;
1090 			//*oRt = POSITIVE_Y;
1091 			//*oFwd = POSITIVE_Z;
1092 			//kinda worked
1093 			*oUp = NEGATIVE_X;
1094 			*oRt = POSITIVE_Y;
1095 			*oFwd = POSITIVE_Z;
1096 		}
1097 		break;
1098 	case CLASS_ASSASSIN_DROID:
1099 		if ( Q_stricmp( "pelvis", boneName ) == 0
1100 			|| Q_stricmp( "lower_lumbar", boneName ) == 0
1101 			|| Q_stricmp( "upper_lumbar", boneName ) == 0 )
1102 		{//only these 3 bones on them are wrong
1103 			//*oUp = POSITIVE_X;
1104 			//*oRt = POSITIVE_Y;
1105 			//*oFwd = POSITIVE_Z;
1106 			*oUp = NEGATIVE_X;
1107 			*oRt = POSITIVE_Y;
1108 			*oFwd = POSITIVE_Z;
1109 		}
1110 		break;
1111 	}
1112 }
1113 
1114 extern void G_LoadAnimFileSet( gentity_t *ent, const char *modelName );
G_SetG2PlayerModelInfo(gentity_t * ent,const char * modelName,const char * customSkin,const char * surfOff,const char * surfOn)1115 qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn )
1116 {
1117 	if ( ent->playerModel != -1 )
1118 	{// we found the model ok
1119 		vec3_t	angles = {0,0,0};
1120 		const char	*token;
1121 		const char	*p;
1122 
1123 		//Now turn on/off any surfaces
1124 		if ( surfOff && surfOff[0] )
1125 		{
1126 			p = surfOff;
1127 			COM_BeginParseSession();
1128 			while ( 1 )
1129 			{
1130 				token = COM_ParseExt( &p, qtrue );
1131 				if ( !token[0] )
1132 				{//reached end of list
1133 					break;
1134 				}
1135 				//turn off this surf
1136 				gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], token, 0x00000002/*G2SURFACEFLAG_OFF*/ );
1137 
1138 			}
1139 			COM_EndParseSession();
1140 		}
1141 		if ( surfOn && surfOn[0] )
1142 		{
1143 			p = surfOn;
1144 			COM_BeginParseSession();
1145 			while ( 1 )
1146 			{
1147 				token = COM_ParseExt( &p, qtrue );
1148 				if ( !token[0] )
1149 				{//reached end of list
1150 					break;
1151 				}
1152 				//turn on this surf
1153 				gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], token, 0 );
1154 			}
1155 			COM_EndParseSession();
1156 		}
1157 		if ( ent->client->NPC_class == CLASS_IMPERIAL && ent->message )
1158 		{//carrying a key, turn on the key sleeve surface (assuming we have one)
1159 			gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "l_arm_key", 0 );
1160 		}
1161 
1162 		G_LoadAnimFileSet( ent, modelName );
1163 
1164 		ent->headBolt = ent->cervicalBolt = ent->torsoBolt = ent->gutBolt = ent->chestBolt =
1165 			ent->crotchBolt = ent->elbowLBolt = ent->elbowRBolt = ent->handLBolt =
1166 			ent->handRBolt = ent->kneeLBolt = ent->kneeRBolt = ent->footLBolt =
1167 			ent->footRBolt = -1;
1168 		// now turn on the bolt in the hand - this one would be best always turned on.
1169 		if ( G_StandardHumanoid( ent ) )
1170 		{//only _humanoid skeleton is expected to have these
1171 			ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
1172 			ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cervical" );
1173 			if ( !Q_stricmp("protocol", modelName ) )
1174 			{//*sigh*, no thoracic bone
1175 				ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
1176 				ent->chestBolt = ent->gutBolt;
1177 			}
1178 			else
1179 			{
1180 				ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic");
1181 				ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
1182 			}
1183 			ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_lumbar");
1184 			ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "pelvis");
1185 			ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_arm_elbow");
1186 			ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_arm_elbow");
1187 			ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand");
1188 			ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand");
1189 			ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_l_knee");
1190 			ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_r_knee");
1191 			ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_leg_foot");
1192 			ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_leg_foot");
1193 			if ( ent->client->NPC_class == CLASS_BOBAFETT
1194 				|| ent->client->NPC_class == CLASS_ROCKETTROOPER )
1195 			{//get jet bolts
1196 				ent->genericBolt1 = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*jet1" );
1197 				ent->genericBolt2 = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*jet2" );
1198 			}
1199 			if ( ent->client->NPC_class == CLASS_BOBAFETT )
1200 			{//get the flamethrower bolt
1201 				ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flamethrower");
1202 			}
1203 		}
1204 		else
1205 		{
1206 			if ( ent->client->NPC_class == CLASS_VEHICLE )
1207 			{//do vehicles tags
1208 
1209 				// Setup the driver tag (where the driver is mounted to).
1210 				ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*driver");
1211 
1212 				// Setup the droid unit (or other misc tag we're using this for).
1213 				ent->m_pVehicle->m_iDroidUnitTag = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*droidunit");
1214 
1215 				char strTemp[128];
1216 
1217 				// Setup the Exhausts.
1218 				for ( int i = 0; i < MAX_VEHICLE_EXHAUSTS; i++ )
1219 				{
1220 					Com_sprintf( strTemp, 128, "*exhaust%d", i + 1 );
1221 					ent->m_pVehicle->m_iExhaustTag[i] = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], strTemp );
1222 				}
1223 
1224 				// Setup the Muzzles.
1225 				for ( int i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
1226 				{
1227 					Com_sprintf( strTemp, 128, "*muzzle%d", i + 1 );
1228 					ent->m_pVehicle->m_iMuzzleTag[i] = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], strTemp );
1229 					if ( ent->m_pVehicle->m_iMuzzleTag[i] == -1 )
1230 					{//ergh, try *flash?
1231 						Com_sprintf( strTemp, 128, "*flash%d", i + 1 );
1232 						ent->m_pVehicle->m_iMuzzleTag[i] = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], strTemp );
1233 					}
1234 				}
1235 			}
1236 			else if ( ent->client->NPC_class == CLASS_HOWLER )
1237 			{
1238 				ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Tongue01" );// tongue base
1239 				ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Tongue08" );// tongue tip
1240 			}
1241 			else if ( !Q_stricmp( "gonk", modelName ) || !Q_stricmp( "seeker", modelName ) || !Q_stricmp( "remote", modelName )
1242 						|| !Q_stricmpn( "r2d2", modelName, 4 ) || !Q_stricmpn( "r5d2", modelName, 4 ) )
1243 			{//TEMP HACK: not a non-humanoid droid
1244 				ent->headBolt = -1;
1245 			}
1246 			else if (!Q_stricmp( "interrogator",modelName))
1247 			{
1248 				ent->headBolt = -1;
1249 			}
1250 			else if (!Q_stricmpn( "probe",modelName, 5))
1251 			{
1252 				ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cranium");		// head pivot point
1253 				ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash");		// Gun 1
1254 			}
1255 			else if (!Q_stricmp( "sentry",modelName))
1256 			{
1257 				ent->headBolt = -1;
1258 				ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1");		// Gun 1
1259 				ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2");		// Gun 2
1260 				ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash03");		// Gun 3
1261 			}
1262 			else if (!Q_stricmp( "mark1",modelName))
1263 			{
1264 				ent->headBolt = -1;
1265 				ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1");		// Blaster Gun 1
1266 				ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2");		// Blaster Gun 2
1267 				ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash3");		// Blaster Gun 3
1268 				ent->genericBolt4 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash4");		// Blaster Gun 4
1269 				ent->genericBolt5 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash5");		// Missile Gun 1
1270 			}
1271 			else if (!Q_stricmp( "mark2",modelName))
1272 			{
1273 				ent->headBolt = -1;
1274 				ent->handRBolt = ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash");		// Blaster Gun 1
1275 			}
1276 			else if (!Q_stricmp( "atst",modelName) )//&& (ent->client->playerTeam != TEAM_PLAYER))
1277 			{
1278 				ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head");
1279 
1280 				ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash1");		// Front guns
1281 				ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash2");
1282 
1283 				ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash3");		// Left side gun
1284 				ent->genericBolt2 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flash4");		// Right side missle launcher
1285 
1286 				ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_foot");
1287 				ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_foot");
1288 			}
1289 			else if ( !Q_stricmp( "minemonster", modelName ))
1290 			{
1291 				ent->handRBolt = ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_f1");
1292 			}
1293 			else if ( !Q_stricmp( "rancor", modelName )
1294 					|| !Q_stricmp( "mutant_rancor", modelName ))
1295 			{
1296 				ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand");
1297 				ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand");
1298 				ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
1299 				ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_mouth");
1300 			}
1301 			else if ( !Q_stricmp( "sand_creature", modelName ))
1302 			{
1303 				ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*mouth");
1304 				ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*ground");
1305 			}
1306 			else if ( !Q_stricmp( "wampa", modelName ))
1307 			{
1308 				ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes");
1309 				ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "neck_bone" );
1310 				ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_spine");
1311 				ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "mid_spine");
1312 				ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_spine");
1313 				ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "rear_bone");
1314 				ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_arm_elbow");
1315 				ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_arm_elbow");
1316 				ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_hand");
1317 				ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_hand");
1318 				ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_l_knee");
1319 				ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hips_r_knee");
1320 				ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_leg_foot");
1321 				ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_leg_foot");
1322 			}
1323 			else
1324 			{//TEMP HACK: not a non-humanoid droid
1325 				ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*weapon");//should be r_hand
1326 				if ( Q_stricmp( "atst", modelName ) )
1327 				{//not an ATST
1328 					ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*headg");
1329 					ent->cervicalBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "cervical" );
1330 					ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "lower_lumbar");
1331 					ent->gutBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "upper_lumbar");
1332 					ent->chestBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "thoracic");
1333 					ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "pelvis");
1334 					ent->elbowLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*bicep_lg");
1335 					ent->elbowRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*bicep_rg");
1336 					ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*hand_l");
1337 					ent->kneeLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*thigh_lg");
1338 					ent->kneeRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*thigh_rg");
1339 					ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*foot_lg");
1340 					ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*foot_rg");
1341 				}
1342 			}
1343 		}
1344 
1345 		ent->faceBone = BONE_INDEX_INVALID;
1346 		ent->craniumBone = BONE_INDEX_INVALID;
1347 		ent->cervicalBone = BONE_INDEX_INVALID;
1348 		ent->thoracicBone = BONE_INDEX_INVALID;
1349 		ent->upperLumbarBone = BONE_INDEX_INVALID;
1350 		ent->lowerLumbarBone = BONE_INDEX_INVALID;
1351 		ent->motionBone = BONE_INDEX_INVALID;
1352 		ent->hipsBone = BONE_INDEX_INVALID;
1353 		ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue );
1354 #ifndef FINAL_BUILD
1355 		if ( g_developer->integer && ent->rootBone == -1 )
1356 		{
1357 			Com_Error(ERR_DROP,"ERROR: model %s has no model_root bone (and hence cannot animate)!!!\n", modelName );
1358 		}
1359 #endif
1360 		ent->footLBone = BONE_INDEX_INVALID;
1361 		ent->footRBone = BONE_INDEX_INVALID;
1362 		ent->humerusRBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "rhumerus", qtrue );
1363 
1364 		// now add overrides on specific joints so the client can set angle overrides on the legs, torso and head
1365 		if ( ent->client->NPC_class == CLASS_VEHICLE )
1366 		{//do vehicles tags
1367 			//vehicleInfo_t *vehicle = ent->m_pVehicle->m_pVehicleInfo;
1368 		}
1369 		else if ( ent->client->NPC_class == CLASS_HOWLER )
1370 		{
1371 		}
1372 		else if ( !Q_stricmp( "gonk", modelName ) || !Q_stricmp( "seeker", modelName ) || !Q_stricmp( "remote", modelName ) )
1373 		{//
1374 		}
1375 		else if (!Q_stricmp( "sentry",modelName))
1376 		{
1377 		}
1378 		else if (!Q_stricmpn( "probe", modelName, 5 ))
1379 		{
1380 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1381 			if (ent->craniumBone>=0)
1382 			{
1383 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1384 			}
1385 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
1386 			if (ent->thoracicBone>=0)
1387 			{
1388 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1389 			}
1390 		}
1391 		else if (!Q_stricmp( "interrogator", modelName ))
1392 		{
1393 			ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "left_arm", qtrue );
1394 			if (ent->genericBone1>=0)
1395 			{
1396 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1397 			}
1398 			ent->genericBone2 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "right_arm", qtrue );
1399 			if (ent->genericBone2>=0)
1400 			{
1401 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone2, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1402 			}
1403 			ent->genericBone3 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "claw", qtrue );
1404 			if (ent->genericBone3>=0)
1405 			{
1406 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone3, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1407 			}
1408 		}
1409 		else if (!Q_stricmpn( "r2d2", modelName, 4 ))
1410 		{
1411 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1412 			if (ent->craniumBone>=0)
1413 			{
1414 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1415 			}
1416 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue );
1417 			if (ent->thoracicBone>=0)
1418 			{
1419 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1420 			}
1421 			ent->genericBone1 = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "f_eye", qtrue );
1422 			if (ent->genericBone1>=0)
1423 			{
1424 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->genericBone1, angles, BONE_ANGLES_POSTMULT, NEGATIVE_Y, NEGATIVE_X, NEGATIVE_Z, NULL, 0, 0 );
1425 			}
1426 		}
1427 		else if (!Q_stricmpn( "r5d2", modelName, 4 ))
1428 		{
1429 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1430 			if (ent->craniumBone>=0)
1431 			{
1432 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1433 			}
1434 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "body", qtrue );
1435 			if (ent->thoracicBone>=0)
1436 			{
1437 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1438 			}
1439 		}
1440 		else if ( !Q_stricmp( "atst", modelName ))
1441 		{
1442 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1443 			if (ent->craniumBone>=0)
1444 			{
1445 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1446 			}
1447 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1448 			if (ent->thoracicBone>=0)
1449 			{
1450 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1451 			}
1452 			ent->footLBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "l_tarsal", qtrue );
1453 			if (ent->footLBone>=0)
1454 			{
1455 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footLBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
1456 			}
1457 			ent->footRBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "r_tarsal", qtrue );
1458 			if (ent->footRBone>=0)
1459 			{
1460 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->footRBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
1461 			}
1462 		}
1463 		else if ( !Q_stricmp( "mark1", modelName ))
1464 		{
1465 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1466 			if (ent->craniumBone>=0)
1467 			{
1468 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1469 			}
1470 			ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1471 			if (ent->upperLumbarBone>=0)
1472 			{
1473 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1474 			}
1475 		}
1476 		else if ( !Q_stricmp( "mark2", modelName ))
1477 		{
1478 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1479 			if (ent->craniumBone>=0)
1480 			{
1481 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1482 			}
1483 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1484 			if (ent->thoracicBone>=0)
1485 			{
1486 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1487 			}
1488 		}
1489 		else if ( !Q_stricmp( "minemonster", modelName ))
1490 		{
1491 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic1", qtrue );
1492 			if (ent->thoracicBone>=0)
1493 			{
1494 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1495 			}
1496 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1497 			if (ent->craniumBone>=0)
1498 			{
1499 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1500 			}
1501 		}
1502 		else if ( ent->client->NPC_class == CLASS_RANCOR )
1503 			/*!Q_stricmp( "rancor", modelName )	|| !Q_stricmp( "mutant_rancor", modelName ) )*/
1504 		{
1505 			Eorientations oUp, oRt, oFwd;
1506 			//regular bones we need
1507 			ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_spine", qtrue );
1508 			if (ent->lowerLumbarBone>=0)
1509 			{
1510 				G_BoneOrientationsForClass( ent->client->NPC_class, "lower_lumbar", &oUp, &oRt, &oFwd );
1511 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1512 			}
1513 			ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "mid_spine", qtrue );
1514 			if (ent->upperLumbarBone>=0)
1515 			{
1516 				G_BoneOrientationsForClass( ent->client->NPC_class, "upper_lumbar", &oUp, &oRt, &oFwd );
1517 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1518 			}
1519 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_spine", qtrue );
1520 			if (ent->thoracicBone>=0)
1521 			{
1522 				G_BoneOrientationsForClass( ent->client->NPC_class, "thoracic", &oUp, &oRt, &oFwd );
1523 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1524 			}
1525 		}
1526 		else if ( !Q_stricmp( "sand_creature", modelName ))
1527 		{
1528 		}
1529 		else if ( !Q_stricmp( "wampa", modelName ) )
1530 		{
1531 			//Eorientations oUp, oRt, oFwd;
1532 			//bone needed for turning anims
1533 			/*
1534 			//SIGH... fucks him up BAD
1535 			ent->hipsBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
1536 			if (ent->hipsBone>=0)
1537 			{
1538 				G_BoneOrientationsForClass( ent->client->NPC_class, "pelvis", &oUp, &oRt, &oFwd );
1539 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->hipsBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
1540 			}
1541 			*/
1542 			/*
1543 			//SIGH... no anim split
1544 			ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue );
1545 			if (ent->lowerLumbarBone>=0)
1546 			{
1547 				G_BoneOrientationsForClass( ent->client->NPC_class, "lower_lumbar", &oUp, &oRt, &oFwd );
1548 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
1549 			}
1550 			*/
1551 			/*
1552 			//SIGH... spine wiggles fuck all this shit
1553 			ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue );
1554 			if (ent->upperLumbarBone>=0)
1555 			{
1556 				G_BoneOrientationsForClass( ent->client->NPC_class, "upper_lumbar", &oUp, &oRt, &oFwd );
1557 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
1558 			}
1559 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1560 			if (ent->thoracicBone>=0)
1561 			{
1562 				G_BoneOrientationsForClass( ent->client->NPC_class, "thoracic", &oUp, &oRt, &oFwd );
1563 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
1564 			}
1565 			ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue );
1566 			if (ent->cervicalBone>=0)
1567 			{
1568 				G_BoneOrientationsForClass( ent->client->NPC_class, "cervical", &oUp, &oRt, &oFwd );
1569 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
1570 			}
1571 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1572 			if (ent->craniumBone>=0)
1573 			{
1574 				G_BoneOrientationsForClass( ent->client->NPC_class, "cranium", &oUp, &oRt, &oFwd );
1575 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL );
1576 			}
1577 			*/
1578 		}
1579 		else if ( !Q_stricmp( "rockettrooper", modelName )
1580 			|| !Q_stricmp( "hazardtrooper", modelName )
1581 			|| !Q_stricmp( "saber_droid", modelName )
1582 			|| !Q_stricmp( "assassin_droid", modelName ) )
1583 		{
1584 			Eorientations oUp, oRt, oFwd;
1585 			if ( Q_stricmp( "saber_droid", modelName ) )
1586 			{//saber droid doesn't use these lower bones
1587 				//regular bones we need
1588 				ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue );
1589 				if (ent->upperLumbarBone>=0)
1590 				{
1591 					G_BoneOrientationsForClass( ent->client->NPC_class, "upper_lumbar", &oUp, &oRt, &oFwd );
1592 					gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1593 				}
1594 				ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue );
1595 				if (ent->lowerLumbarBone>=0)
1596 				{
1597 					G_BoneOrientationsForClass( ent->client->NPC_class, "lower_lumbar", &oUp, &oRt, &oFwd );
1598 					gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1599 				}
1600 			}
1601 			if ( Q_stricmp( "hazardtrooper", modelName ) )
1602 			{//hazard trooper doesn't have these upper bones
1603 				if ( Q_stricmp( "saber_droid", modelName ) )
1604 				{//saber droid doesn't use thoracic bone
1605 					ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1606 					if (ent->thoracicBone>=0)
1607 					{
1608 						G_BoneOrientationsForClass( ent->client->NPC_class, "thoracic", &oUp, &oRt, &oFwd );
1609 						gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1610 					}
1611 				}
1612 				ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue );
1613 				if (ent->cervicalBone>=0)
1614 				{
1615 					G_BoneOrientationsForClass( ent->client->NPC_class, "cervical", &oUp, &oRt, &oFwd );
1616 					gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1617 				}
1618 				ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1619 				if (ent->craniumBone>=0)
1620 				{
1621 					G_BoneOrientationsForClass( ent->client->NPC_class, "cranium", &oUp, &oRt, &oFwd );
1622 					gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, NULL, 0, 0 );
1623 				}
1624 			}
1625 		}
1626 		else
1627 		{
1628 			//special case motion bone - to match up split anims
1629 			ent->motionBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "Motion", qtrue );
1630 			if (ent->motionBone>=0)
1631 			{
1632 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->motionBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0 );
1633 			}
1634 			ent->motionBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "Motion");
1635 			//bone needed for turning anims
1636 			ent->hipsBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "pelvis", qtrue );
1637 			if (ent->hipsBone>=0)
1638 			{
1639 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->hipsBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1640 			}
1641 			//regular bones we need
1642 			ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "upper_lumbar", qtrue );
1643 			if (ent->upperLumbarBone>=0)
1644 			{
1645 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1646 			}
1647 			ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "lower_lumbar", qtrue );
1648 			if (ent->lowerLumbarBone>=0)
1649 			{
1650 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1651 			}
1652 
1653 			ent->faceBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "face", qtrue );
1654 			if (ent->faceBone>=0)
1655 			{
1656 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->faceBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1657 			}
1658 			ent->craniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cranium", qtrue );
1659 			if (ent->craniumBone>=0)
1660 			{
1661 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->craniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1662 			}
1663 			ent->cervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cervical", qtrue );
1664 			if (ent->cervicalBone>=0)
1665 			{
1666 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->cervicalBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1667 			}
1668 			ent->thoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "thoracic", qtrue );
1669 			if (ent->thoracicBone>=0)
1670 			{
1671 				gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->thoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 );
1672 			}
1673 		}
1674 		ent->client->clientInfo.infoValid = qtrue;
1675 
1676 	}
1677 
1678 	if ( ent->client->NPC_class == CLASS_SAND_CREATURE )
1679 	{
1680 		ent->s.radius = 256;
1681 	}
1682 	else if ( ent->client->NPC_class == CLASS_RANCOR )
1683 	{
1684 		if ( (ent->spawnflags&1) )
1685 		{//mutant
1686 			ent->s.radius = 300;
1687 		}
1688 		else
1689 		{
1690 			ent->s.radius = 150;
1691 		}
1692 	}
1693 	else if ( ent->s.radius <= 0 )//radius cannot be negative or zero
1694 	{//set the radius to be the largest axial distance on the entity
1695 		float	max;
1696 		max = ent->mins[0];//NOTE: mins is always negative
1697 		if ( max > ent->mins[1] )
1698 		{
1699 			max = ent->mins[1];
1700 		}
1701 
1702 		if ( max > ent->mins[2] )
1703 		{
1704 			max = ent->mins[2];
1705 		}
1706 
1707 		max = fabs(max);//convert to positive to compare with maxs
1708 		if ( max < ent->maxs[0] )
1709 		{
1710 			max = ent->maxs[0];
1711 		}
1712 
1713 		if ( max < ent->maxs[1] )
1714 		{
1715 			max = ent->maxs[1];
1716 		}
1717 
1718 		if ( max < ent->maxs[2] )
1719 		{
1720 			max = ent->maxs[2];
1721 		}
1722 
1723 		ent->s.radius = (int)max;
1724 
1725 		if (!ent->s.radius)	// Still no radius?
1726 		{
1727 			ent->s.radius = 60;
1728 		}
1729 	}
1730 
1731 	// set the weaponmodel to -1 so we don't try to remove it in Pmove before we have it built
1732 	ent->weaponModel[0] = -1;
1733 
1734 	if ( ent->playerModel == -1 )
1735 	{
1736 		return qfalse;
1737 	}
1738 	return qtrue;
1739 }
1740 
G_SetG2PlayerModel(gentity_t * const ent,const char * modelName,const char * customSkin,const char * surfOff,const char * surfOn)1741 void G_SetG2PlayerModel( gentity_t * const ent, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn )
1742 {
1743 	char	skinName[MAX_QPATH];
1744 
1745 	//ok, lets register the skin name, and then pass that name to the config strings so the client can get it too.
1746 	if ( !customSkin )
1747 	{//use the default
1748 		Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", modelName );
1749 	}
1750 	else
1751 	{
1752 		if (strchr(customSkin, '|'))
1753 		{//three part skin
1754 			Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s", modelName, customSkin );
1755 		}
1756 		else
1757 		{
1758 			Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_%s.skin", modelName, customSkin );
1759 		}
1760 	}
1761 	int skin = gi.RE_RegisterSkin( skinName );
1762 	//now generate the ghoul2 model this client should be.
1763 	if ( ent->client->NPC_class == CLASS_VEHICLE )
1764 	{//vehicles actually grab their model from the appropriate vehicle data entry
1765 
1766 		// This will register the model and other assets.
1767 		Vehicle_t *pVeh = ent->m_pVehicle;
1768 		pVeh->m_pVehicleInfo->RegisterAssets( pVeh );
1769 		ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName), pVeh->m_pVehicleInfo->modelIndex, G_SkinIndex( skinName ), NULL_HANDLE, 0, 0 );
1770 	}
1771 	else
1772 	{
1773 		//NOTE: it still loads the default skin's tga's because they're referenced in the .glm.
1774 		ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName), G_ModelIndex( va("models/players/%s/model.glm", modelName) ), G_SkinIndex( skinName ), NULL_HANDLE, 0, 0 );
1775 	}
1776 	if (ent->playerModel == -1)
1777 	{//try the stormtrooper as a default
1778 		gi.Printf( S_COLOR_RED"G_SetG2PlayerModel: cannot load model %s\n", modelName );
1779 		modelName = "stormtrooper";
1780 		Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", modelName );
1781 		skin = gi.RE_RegisterSkin( skinName );
1782 		ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", modelName), G_ModelIndex( va("models/players/%s/model.glm", modelName) ), NULL_HANDLE, NULL_HANDLE, 0, 0 );
1783 	}
1784 	if (ent->playerModel == -1)
1785 	{//very bad thing here!
1786 		Com_Error(ERR_DROP, "Cannot fall back to default model %s!", modelName);
1787 	}
1788 
1789 	gi.G2API_SetSkin( &ent->ghoul2[ent->playerModel], G_SkinIndex( skinName ), skin );//this is going to set the surfs on/off matching the skin file
1790 
1791 	// did we find a ghoul2 model? if so, load the animation.cfg file
1792 	if ( !G_SetG2PlayerModelInfo( ent, modelName, customSkin, surfOff, surfOn ) )
1793 	{//couldn't set g2 info, fall back to a mouse md3
1794 		NPC_ParseParms( "mouse", ent );
1795 		Com_Printf( S_COLOR_RED"couldn't load playerModel %s!\n", va("models/players/%s/model.glm", modelName) );
1796 	}
1797 }
1798 /*
1799 Ghoul2 Insert End
1800 */
1801 
G_RemovePlayerModel(gentity_t * ent)1802 void G_RemovePlayerModel( gentity_t *ent )
1803 {
1804 	if ( ent->playerModel >= 0 && ent->ghoul2.size() )
1805 	{
1806 		gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->playerModel );
1807 		ent->playerModel = -1;
1808 	}
1809 }
1810 
G_RemoveWeaponModels(gentity_t * ent)1811 void G_RemoveWeaponModels( gentity_t *ent )
1812 {
1813 	if ( ent->ghoul2.size() )
1814 	{
1815 		if ( ent->weaponModel[0] > 0 )
1816 		{
1817 			gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel[0] );
1818 			ent->weaponModel[0] = -1;
1819 		}
1820 		if ( ent->weaponModel[1] > 0 )
1821 		{
1822 			gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->weaponModel[1] );
1823 			ent->weaponModel[1] = -1;
1824 		}
1825 	}
1826 }
1827 
G_AddWeaponModels(gentity_t * ent)1828 void G_AddWeaponModels( gentity_t *ent )
1829 {
1830 	if ( !ent || !ent->client )
1831 	{
1832 		return;
1833 	}
1834 	if ( ent->weaponModel[0] == -1 )
1835 	{
1836 		if ( ent->client->ps.weapon == WP_SABER )
1837 		{
1838 			WP_SaberAddG2SaberModels( ent );
1839 		}
1840 		else if ( ent->client->ps.weapon != WP_NONE )
1841 		{
1842 			G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
1843 		}
1844 	}
1845 }
1846 
1847 extern saber_colors_t TranslateSaberColor( const char *name );
1848 extern void WP_RemoveSaber( gentity_t *ent, int saberNum );
1849 void G_ChangePlayerModel( gentity_t *ent, const char *newModel );
G_SetSabersFromCVars(gentity_t * ent)1850 void G_SetSabersFromCVars( gentity_t *ent )
1851 {
1852 	if ( g_saber->string
1853 		&& g_saber->string[0]
1854 		&& Q_stricmp( "none", g_saber->string )
1855 		&& Q_stricmp( "NULL", g_saber->string ) )
1856 	{//FIXME: how to specify second saber?
1857 		WP_SaberParseParms( g_saber->string, &ent->client->ps.saber[0] );
1858 		if ( ent->client->ps.saber[0].stylesLearned )
1859 		{
1860 			ent->client->ps.saberStylesKnown |= ent->client->ps.saber[0].stylesLearned;
1861 		}
1862 		if ( ent->client->ps.saber[0].singleBladeStyle )
1863 		{
1864 			ent->client->ps.saberStylesKnown |= ent->client->ps.saber[0].singleBladeStyle;
1865 		}
1866 	}
1867 
1868 	if ( player
1869 		&& player->client
1870 		&& player->client->sess.mission_objectives[LIGHTSIDE_OBJ].status == 2
1871 		&& g_saberDarkSideSaberColor->integer )
1872 	{//dark side!
1873 		//always use red
1874 		for ( int n = 0; n < MAX_BLADES; n++ )
1875 		{
1876 			ent->client->ps.saber[0].blade[n].color = SABER_RED;
1877 		}
1878 	}
1879 	else if ( g_saber_color->string )
1880 	{//FIXME: how to specify color for each blade and/or color for second saber?
1881 		saber_colors_t color = TranslateSaberColor( g_saber_color->string );
1882 		for ( int n = 0; n < MAX_BLADES; n++ )
1883 		{
1884 			ent->client->ps.saber[0].blade[n].color = color;
1885 		}
1886 	}
1887 	if ( g_saber2->string
1888 		&& g_saber2->string[0]
1889 		&& Q_stricmp( "none", g_saber2->string )
1890 		&& Q_stricmp( "NULL", g_saber2->string ) )
1891 	{
1892 		if ( !(ent->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) )
1893 		{//can't use a second saber if first one is a two-handed saber...?
1894 			WP_SaberParseParms( g_saber2->string, &ent->client->ps.saber[1] );
1895 			if ( ent->client->ps.saber[1].stylesLearned )
1896 			{
1897 				ent->client->ps.saberStylesKnown |= ent->client->ps.saber[1].stylesLearned;
1898 			}
1899 			if ( ent->client->ps.saber[1].singleBladeStyle )
1900 			{
1901 				ent->client->ps.saberStylesKnown |= ent->client->ps.saber[1].singleBladeStyle;
1902 			}
1903 			if ( (ent->client->ps.saber[1].saberFlags&SFL_TWO_HANDED) )
1904 			{//tsk tsk, can't use a twoHanded saber as second saber
1905 				WP_RemoveSaber( ent, 1 );
1906 			}
1907 			else
1908 			{
1909 				ent->client->ps.dualSabers = qtrue;
1910 				if ( player
1911 					&& player->client
1912 					&& player->client->sess.mission_objectives[LIGHTSIDE_OBJ].status == 2
1913 					&& g_saberDarkSideSaberColor->integer )
1914 				{//dark side!
1915 					//always use red
1916 					for ( int n = 0; n < MAX_BLADES; n++ )
1917 					{
1918 						ent->client->ps.saber[1].blade[n].color = SABER_RED;
1919 					}
1920 				}
1921 				else if ( g_saber2_color->string )
1922 				{//FIXME: how to specify color for each blade and/or color for second saber?
1923 					saber_colors_t color = TranslateSaberColor( g_saber2_color->string );
1924 					for ( int n = 0; n < MAX_BLADES; n++ )
1925 					{
1926 						ent->client->ps.saber[1].blade[n].color = color;
1927 					}
1928 				}
1929 			}
1930 		}
1931 	}
1932 }
1933 
G_InitPlayerFromCvars(gentity_t * ent)1934 void G_InitPlayerFromCvars( gentity_t *ent )
1935 {
1936 	//set model based on cvars
1937 	if(Q_stricmp(g_char_skin_head->string, "model_default") == 0 && Q_stricmp(g_char_skin_torso->string, "model_default") == 0 && Q_stricmp(g_char_skin_legs->string, "model_default") == 0)
1938 		G_ChangePlayerModel( ent, va("%s|model_default", g_char_model->string) );
1939 	else
1940 		G_ChangePlayerModel( ent, va("%s|%s|%s|%s", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string) );
1941 
1942 	//FIXME: parse these 2 from some cvar or require playermodel to be in a *.npc?
1943 	if( ent->NPC_type && gi.bIsFromZone(ent->NPC_type, TAG_G_ALLOC) ) {
1944 		gi.Free(ent->NPC_type);
1945 	}
1946 
1947 	// Bad casting I know, but NPC_type can also come the memory manager,
1948 	// and you can't free a const-pointer later on. This seemed like the
1949 	// better options.
1950 	ent->NPC_type = (char *)"player";//default for now
1951 	if( ent->client->clientInfo.customBasicSoundDir && gi.bIsFromZone(ent->client->clientInfo.customBasicSoundDir, TAG_G_ALLOC) ) {
1952 		gi.Free(ent->client->clientInfo.customBasicSoundDir);
1953 	}
1954 
1955 	char snd[512];
1956 	gi.Cvar_VariableStringBuffer( "snd", snd, sizeof(snd) );
1957 
1958 	ent->client->clientInfo.customBasicSoundDir = G_NewString(snd);	//copy current cvar
1959 
1960 	//set the lightsaber
1961 	G_RemoveWeaponModels( ent );
1962 	G_SetSabersFromCVars( ent );
1963 	//set up weapon models, etc.
1964 	G_AddWeaponModels( ent );
1965 	NPC_SetAnim( ent, SETANIM_LEGS, ent->client->ps.legsAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
1966 	NPC_SetAnim( ent, SETANIM_TORSO, ent->client->ps.torsoAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
1967 	if ( !ent->s.number )
1968 	{//the actual player, not an NPC pretending to be a player
1969 		ClientUserinfoChanged( ent->s.number );
1970 	}
1971 	//color tinting
1972 	//FIXME: the customRGBA shouldn't be set if the shader this guys .skin is using doesn't have the tinting on it
1973 	if ( g_char_color_red->integer
1974 		|| g_char_color_green->integer
1975 		|| g_char_color_blue->integer )
1976 	{
1977 		ent->client->renderInfo.customRGBA[0] = g_char_color_red->integer;
1978 		ent->client->renderInfo.customRGBA[1] = g_char_color_green->integer;
1979 		ent->client->renderInfo.customRGBA[2] = g_char_color_blue->integer;
1980 		ent->client->renderInfo.customRGBA[3] = 255;
1981 	}
1982 }
1983 
G_ChangePlayerModel(gentity_t * ent,const char * newModel)1984 void G_ChangePlayerModel( gentity_t *ent, const char *newModel )
1985 {
1986 	if ( !ent || !ent->client || !newModel )
1987 	{
1988 		return;
1989 	}
1990 
1991 	G_RemovePlayerModel( ent );
1992 	if ( Q_stricmp( "player", newModel ) == 0 )
1993 	{
1994 		G_InitPlayerFromCvars( ent );
1995 		return;
1996 	}
1997 
1998 	//attempt to free the string (currently can't since it's always "player" )
1999 	if( ent->NPC_type && gi.bIsFromZone(ent->NPC_type, TAG_G_ALLOC) ) {
2000 		gi.Free(ent->NPC_type);
2001 	}
2002 	ent->NPC_type = G_NewString( newModel );
2003 	G_RemoveWeaponModels( ent );
2004 
2005 	if ( strchr(newModel,'|') )
2006 	{
2007 		char name[MAX_QPATH];
2008 		strcpy(name, newModel);
2009 		char *p = strchr(name, '|');
2010 		*p=0;
2011 		p++;
2012 
2013 		if ( strstr(p, "model_default" ) )
2014 			G_SetG2PlayerModel( ent, name, NULL, NULL, NULL );
2015 		else
2016 			G_SetG2PlayerModel( ent, name, p, NULL, NULL );
2017 	}
2018 	else
2019 	{
2020 		//FIXME: everything but force powers gets reset, those should, too...
2021 		//		currently leaves them as is except where otherwise noted in the NPCs.cfg?
2022 		//FIXME: remove all weapons?
2023 		if ( NPC_ParseParms( ent->NPC_type, ent ) )
2024 		{
2025 			G_AddWeaponModels( ent );
2026 			NPC_SetAnim( ent, SETANIM_LEGS, ent->client->ps.legsAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
2027 			NPC_SetAnim( ent, SETANIM_TORSO, ent->client->ps.torsoAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
2028 			ClientUserinfoChanged( ent->s.number );
2029 			//Ugh, kind of a hack for now:
2030 			if ( ent->client->NPC_class == CLASS_BOBAFETT
2031 				|| ent->client->NPC_class == CLASS_ROCKETTROOPER )
2032 			{
2033 				//FIXME: remove saber, too?
2034 				Boba_Precache();	// player as boba?
2035 			}
2036 		}
2037 		else
2038 		{
2039 			gi.Printf( S_COLOR_RED"G_ChangePlayerModel: cannot find NPC %s\n", newModel );
2040 			G_ChangePlayerModel( ent, "stormtrooper" );	//need a better fallback?
2041 		}
2042 	}
2043 }
2044 
G_ReloadSaberData(gentity_t * ent)2045 void G_ReloadSaberData( gentity_t *ent )
2046 {
2047 	//dualSabers should already be set
2048 	if ( ent->client->ps.saber[0].name != NULL )
2049 	{
2050 		WP_SaberParseParms( ent->client->ps.saber[0].name, &ent->client->ps.saber[0], qfalse );
2051 		if ( ent->client->ps.saber[0].stylesLearned )
2052 		{
2053 			ent->client->ps.saberStylesKnown |= ent->client->ps.saber[0].stylesLearned;
2054 		}
2055 		if ( ent->client->ps.saber[0].singleBladeStyle )
2056 		{
2057 			ent->client->ps.saberStylesKnown |= ent->client->ps.saber[0].singleBladeStyle;
2058 		}
2059 	}
2060 	if ( ent->client->ps.saber[1].name != NULL )
2061 	{
2062 		WP_SaberParseParms( ent->client->ps.saber[1].name, &ent->client->ps.saber[1], qfalse );
2063 		if ( ent->client->ps.saber[1].stylesLearned )
2064 		{
2065 			ent->client->ps.saberStylesKnown |= ent->client->ps.saber[1].stylesLearned;
2066 		}
2067 		if ( ent->client->ps.saber[1].singleBladeStyle )
2068 		{
2069 			ent->client->ps.saberStylesKnown |= ent->client->ps.saber[1].singleBladeStyle;
2070 		}
2071 	}
2072 }
2073 
G_PlayerSpawned(void)2074 qboolean G_PlayerSpawned( void )
2075 {
2076 	if ( !player
2077 		|| !player->client
2078 		|| player->client->pers.teamState.state != TEAM_ACTIVE
2079 		|| level.time - player->client->pers.enterTime < 100 )
2080 	{//player hasn't spawned yet
2081 		return qfalse;
2082 	}
2083 	return qtrue;
2084 }
2085 
2086 /*
2087 ===========
2088 ClientSpawn
2089 
2090 Called every time a client is placed fresh in the world:
2091 after the first ClientBegin, and after each respawn
2092 Initializes all non-persistant parts of playerState
2093 ============
2094 */
2095 
G_CheckPlayerDarkSide(void)2096 qboolean G_CheckPlayerDarkSide( void )
2097 {
2098 	if ( player && player->client && player->client->sess.mission_objectives[LIGHTSIDE_OBJ].status == 2 )
2099 	{//dark side player!
2100 		player->client->playerTeam = TEAM_FREE;
2101 		player->client->enemyTeam = TEAM_FREE;
2102 		if ( g_saberDarkSideSaberColor->integer )
2103 		{//dark side!
2104 			//always use red
2105 			for ( int n = 0; n < MAX_BLADES; n++ )
2106 			{
2107 				player->client->ps.saber[0].blade[n].color = player->client->ps.saber[1].blade[n].color = SABER_RED;
2108 			}
2109 		}
2110 		G_SoundIndex( "sound/chars/jedi2/28je2008.wav" );
2111 		G_SoundIndex( "sound/chars/jedi2/28je2009.wav" );
2112 		G_SoundIndex( "sound/chars/jedi2/28je2012.wav" );
2113 		return qtrue;
2114 	}
2115 	return qfalse;
2116 }
2117 
2118 void G_ChangePlayerModel( gentity_t *ent, const char *newModel );
ClientSpawn(gentity_t * ent,SavedGameJustLoaded_e eSavedGameJustLoaded)2119 qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded )
2120 {
2121 	int		index;
2122 	vec3_t	spawn_origin, spawn_angles;
2123 	gclient_t	*client;
2124 	int		i;
2125 	clientPersistant_t	saved;
2126 	clientSession_t		savedSess;
2127 	clientInfo_t		savedCi;
2128 	int		persistant[MAX_PERSISTANT];
2129 	usercmd_t	ucmd;
2130 	gentity_t	*spawnPoint;
2131 	qboolean	beamInEffect = qfalse;
2132 	extern qboolean g_qbLoadTransition;
2133 
2134 	index = ent - g_entities;
2135 	client = ent->client;
2136 
2137 	if ( eSavedGameJustLoaded == eFULL && g_qbLoadTransition == qfalse )//qbFromSavedGame)
2138 	{//loading up a full save game
2139 		ent->client->pers.teamState.state = TEAM_ACTIVE;
2140 
2141 		// increment the spawncount so the client will detect the respawn
2142 		client->ps.persistant[PERS_SPAWN_COUNT]++;
2143 		client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
2144 
2145 		client->airOutTime = level.time + 12000;
2146 
2147 		for (i=0; i<3; i++)
2148 		{
2149 			ent->client->pers.cmd_angles[i] = 0.0f;
2150 		}
2151 
2152 		SetClientViewAngle( ent, ent->client->ps.viewangles);//spawn_angles );
2153 
2154 		gi.linkentity (ent);
2155 
2156 		// run the presend to set anything else
2157 		ClientEndFrame( ent );
2158 
2159 		// clear entity state values
2160 		PlayerStateToEntityState( &client->ps, &ent->s );
2161 
2162 		// ALL OF MY RAGE... they decided it would be a great idea to treat NPC_type like a player model here,
2163 		// which is all kinds of unbelievable. I will be having a stern talk with James later. --eez
2164 		if( ent->NPC_type &&
2165 			Q_stricmp( ent->NPC_type, "player" ) )
2166 		{
2167 			// FIXME: game doesn't like it when you pass ent->NPC_type into this func. Insert all kinds of noises here --eez
2168 			char bleh[MAX_SPAWN_VARS_CHARS];
2169 			Q_strncpyz(bleh, ent->NPC_type, sizeof(bleh));
2170 
2171 			G_ChangePlayerModel( ent, bleh );
2172 		}
2173 		else
2174 		{
2175 			G_LoadAnimFileSet( ent, ent->NPC_type );
2176 			G_SetSkin( ent );
2177 		}
2178 
2179 		//setup sabers
2180 		G_ReloadSaberData( ent );
2181 		//force power levels should already be set
2182 	}
2183 	else
2184 	{
2185 		// find a spawn point
2186 		// do it before setting health back up, so farthest
2187 		// ranging doesn't count this client
2188 		// don't spawn near existing origin if possible
2189 		spawnPoint = SelectSpawnPoint ( ent->client->ps.origin,
2190 			(team_t) ent->client->ps.persistant[PERS_TEAM], spawn_origin, spawn_angles);
2191 
2192 		ent->client->pers.teamState.state = TEAM_ACTIVE;
2193 
2194 		// clear everything but the persistant data
2195 		saved = client->pers;
2196 		savedSess = client->sess;
2197 		for ( i = 0 ; i < MAX_PERSISTANT ; i++ )
2198 		{
2199 			persistant[i] = client->ps.persistant[i];
2200 		}
2201 		//Preserve clientInfo
2202 		memcpy (&savedCi, &client->clientInfo, sizeof(clientInfo_t));
2203 
2204 		memset (client, 0, sizeof(*client));
2205 
2206 		memcpy (&client->clientInfo, &savedCi, sizeof(clientInfo_t));
2207 
2208 		client->pers = saved;
2209 		client->sess = savedSess;
2210 		for ( i = 0 ; i < MAX_PERSISTANT ; i++ )
2211 		{
2212 			client->ps.persistant[i] = persistant[i];
2213 		}
2214 
2215 		// increment the spawncount so the client will detect the respawn
2216 		client->ps.persistant[PERS_SPAWN_COUNT]++;
2217 		client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;
2218 
2219 		client->airOutTime = level.time + 12000;
2220 
2221 		// clear entity values
2222 		client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
2223 		ent->s.groundEntityNum = ENTITYNUM_NONE;
2224 		ent->client = &level.clients[index];
2225 		ent->mass = 10;
2226 		ent->takedamage = qtrue;
2227 		ent->inuse = qtrue;
2228 		SetInUse(ent);
2229 		ent->m_iIcarusID = IIcarusInterface::ICARUS_INVALID;
2230 		if ( !ent->NPC_type )
2231 		{
2232 			ent->NPC_type = (char *)"player";
2233 		}
2234 		ent->classname = "player";
2235 		ent->targetname = ent->script_targetname = "player";
2236 		if ( ent->client->NPC_class == CLASS_NONE )
2237 		{
2238 			ent->client->NPC_class = CLASS_PLAYER;
2239 		}
2240 		client->playerTeam = TEAM_PLAYER;
2241 		client->enemyTeam = TEAM_ENEMY;
2242 		ent->contents = CONTENTS_BODY;
2243 		ent->clipmask = MASK_PLAYERSOLID;
2244 		ent->e_DieFunc = dieF_player_die;
2245 		ent->waterlevel = 0;
2246 		ent->watertype = 0;
2247 		client->ps.friction = 6;
2248 		client->ps.gravity = g_gravity->value;
2249 		ent->flags &= ~FL_NO_KNOCKBACK;
2250 		client->renderInfo.lookTarget = ENTITYNUM_NONE;
2251 		client->renderInfo.lookTargetClearTime = 0;
2252 		client->renderInfo.lookMode = LM_ENT;
2253 
2254 		VectorCopy (playerMins, ent->mins);
2255 		VectorCopy (playerMaxs, ent->maxs);
2256 		client->crouchheight = CROUCH_MAXS_2;
2257 		client->standheight = DEFAULT_MAXS_2;
2258 
2259 		client->ps.clientNum = index;
2260 
2261 		// give default weapons
2262 		//these are precached in g_items, ClearRegisteredItems()
2263 		client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
2264 		//client->ps.inventory[INV_ELECTROBINOCULARS] = 1;
2265 		//ent->client->ps.inventory[INV_BACTA_CANISTER] = 1;
2266 
2267 		// give EITHER the saber or the stun baton..never both
2268 		if ( spawnPoint->spawnflags & 32 ) // STUN_BATON
2269 		{
2270 			client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_STUN_BATON );
2271 			client->ps.weapon = WP_STUN_BATON;
2272 		}
2273 		else
2274 		{	// give the saber because most test maps will not have the STUN BATON flag set
2275 			client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER );	//this is precached in SP_info_player_deathmatch
2276 			client->ps.weapon = WP_SABER;
2277 		}
2278 		// force the base weapon up
2279 		client->ps.weaponstate = WEAPON_READY;
2280 
2281 		for ( i = FIRST_WEAPON; i < MAX_PLAYER_WEAPONS; i++ ) // don't give ammo for explosives
2282 		{
2283 			if ( (client->ps.stats[STAT_WEAPONS]&(1<<i)) )
2284 			{//if starting with this weapon, gimme max ammo for it
2285 				client->ps.ammo[weaponData[i].ammoIndex] = ammoData[weaponData[i].ammoIndex].max;
2286 			}
2287 		}
2288 
2289 		if ( eSavedGameJustLoaded == eNO )
2290 		{
2291 			//FIXME: get player's info from NPCs.cfg
2292 			client->ps.dualSabers = qfalse;
2293 			WP_SaberParseParms( g_saber->string, &client->ps.saber[0] );//get saber info
2294 
2295 			client->ps.saberStylesKnown |= (1<<gi.Cvar_VariableIntegerValue("g_fighting_style"));
2296 
2297 //			if ( client->ps.saber[0].stylesLearned )
2298 //			{
2299 //				client->ps.saberStylesKnown |= client->ps.saber[0].stylesLearned;
2300 //			}
2301 //			if ( ent->client->ps.saber[1].singleSaberStyle )
2302 //			{
2303 //				ent->client->ps.saberStylesKnown |= ent->client->ps.saber[1].singleSaberStyle;
2304 //			}
2305 			WP_InitForcePowers( ent );//Initialize force powers
2306 		}
2307 		else
2308 		{//autoload, will be taken care of below
2309 		}
2310 		//
2311 
2312 		ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];
2313 		ent->client->dismemberProbHead = 0;
2314 		ent->client->dismemberProbArms = 5;
2315 		ent->client->dismemberProbHands = 20;
2316 		ent->client->dismemberProbWaist = 0;
2317 		ent->client->dismemberProbLegs = 0;
2318 
2319 		ent->client->ps.batteryCharge = 2500;
2320 
2321 		VectorCopy( spawn_origin, client->ps.origin );
2322 		VectorCopy( spawn_origin, ent->currentOrigin );
2323 
2324 		// the respawned flag will be cleared after the attack and jump keys come up
2325 		client->ps.pm_flags |= PMF_RESPAWNED;
2326 
2327 		SetClientViewAngle( ent, spawn_angles );
2328 
2329 		G_KillBox( ent );
2330 		gi.linkentity (ent);
2331 
2332 		// don't allow full run speed for a bit
2333 		client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
2334 		client->ps.pm_time = 100;
2335 
2336 		client->respawnTime = level.time;
2337 		client->latched_buttons = 0;
2338 
2339 		// set default animations
2340 		client->ps.torsoAnim = BOTH_STAND2;
2341 		client->ps.legsAnim = BOTH_STAND2;
2342 
2343 		//clear IK grabbing stuff
2344 		client->ps.heldClient = client->ps.heldByClient = ENTITYNUM_NONE;
2345 		client->ps.saberLockEnemy = ENTITYNUM_NONE;	//duh, don't think i'm locking with myself
2346 
2347 		// restore some player data
2348 		//
2349 		Player_RestoreFromPrevLevel(ent, eSavedGameJustLoaded);
2350 
2351 		//FIXME: put this BEFORE the Player_RestoreFromPrevLevel check above?
2352 		if (eSavedGameJustLoaded == eNO)
2353 		{//fresh start
2354 			if (!(spawnPoint->spawnflags&1))		// not KEEP_PREV
2355 			{//then restore health and armor
2356 				ent->health = client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];
2357 				ent->client->ps.forcePower = ent->client->ps.forcePowerMax;
2358 			}
2359 			G_InitPlayerFromCvars( ent );
2360 		}
2361 		else
2362 		{//autoload
2363 			if( ent->NPC_type &&
2364 			Q_stricmp( ent->NPC_type, "player" ) )
2365 			{
2366 				// FIXME: game doesn't like it when you pass ent->NPC_type into this func. Insert all kinds of noises here --eez
2367 				char bleh[MAX_SPAWN_VARS_CHARS];
2368 				Q_strncpyz(bleh, ent->NPC_type, sizeof(bleh));
2369 
2370 				G_ChangePlayerModel( ent, bleh );
2371 			}
2372 			else
2373 			{
2374 				G_LoadAnimFileSet( ent, ent->NPC_type );
2375 				G_SetSkin( ent );
2376 			}
2377 			G_ReloadSaberData( ent );
2378 			//force power levels should already be set
2379 		}
2380 
2381 		//NEVER start a map with either of your sabers or blades on...
2382 		ent->client->ps.SaberDeactivate();
2383 		// run a client frame to drop exactly to the floor,
2384 		// initialize animations and other things
2385 		client->ps.commandTime = level.time - 100;
2386 		ucmd = client->pers.lastCommand;
2387 		ucmd.serverTime = level.time;
2388 		VectorCopyM( client->pers.cmd_angles, ucmd.angles );
2389 		ucmd.weapon = client->ps.weapon;	// client think calls Pmove which sets the client->ps.weapon to ucmd.weapon, so ...
2390 		ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
2391 		ClientThink( ent-g_entities, &ucmd );
2392 
2393 		// run the presend to set anything else
2394 		ClientEndFrame( ent );
2395 
2396 		// clear entity state values
2397 		PlayerStateToEntityState( &client->ps, &ent->s );
2398 
2399 		//ICARUS include
2400 		Quake3Game()->FreeEntity( ent );
2401 		Quake3Game()->InitEntity( ent );
2402 
2403 		// Make sure no Sequencer exists then Get a new one.
2404 		IIcarusInterface::GetIcarus()->DeleteIcarusID( ent->m_iIcarusID );
2405 		ent->m_iIcarusID = IIcarusInterface::GetIcarus()->GetIcarusID( ent->s.number );
2406 
2407 		if ( spawnPoint->spawnflags & 64 )	//NOWEAPON
2408 		{//player starts with absolutely no weapons
2409 			ent->client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
2410 			ent->client->ps.ammo[weaponData[WP_NONE].ammoIndex] = 32000;
2411 			ent->client->ps.weapon = WP_NONE;
2412 			ent->client->ps.weaponstate = WEAPON_READY;
2413 			ent->client->ps.dualSabers = qfalse;
2414 		}
2415 
2416 		if ( ent->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_SABER ) )
2417 		{//set up so has lightsaber
2418 			WP_SaberInitBladeData( ent );
2419 			if ( (ent->weaponModel[0] <= 0 || (ent->weaponModel[1]<=0&&ent->client->ps.dualSabers)) //one or both of the saber models is not initialized
2420 				&& ent->client->ps.weapon == WP_SABER )//current weapon is saber
2421 			{//add the proper models
2422 				WP_SaberAddG2SaberModels( ent );
2423 			}
2424 		}
2425 		if ( ent->weaponModel[0] == -1 && ent->client->ps.weapon != WP_NONE )
2426 		{
2427 			G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 );
2428 		}
2429 
2430 		{
2431 			// fire the targets of the spawn point
2432 			G_UseTargets( spawnPoint, ent );
2433 			//Designers needed them to fire off target2's as well... this is kind of messy
2434 			G_UseTargets2( spawnPoint, ent, spawnPoint->target2 );
2435 
2436 			/*
2437 			// select the highest weapon number available, after any
2438 			// spawn given items have fired
2439 			client->ps.weapon = 1;
2440 			for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
2441 				if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
2442 					client->ps.weapon = i;
2443 					break;
2444 				}
2445 			}*/
2446 		}
2447 	}
2448 
2449 	client->pers.enterTime = level.time;//needed mainly to stop the weapon switch to WP_NONE that happens on loads
2450 	ent->max_health = client->ps.stats[STAT_MAX_HEALTH];
2451 
2452 	if ( eSavedGameJustLoaded == eNO )
2453 	{//on map transitions, Ghoul2 frame gets reset to zero, restart our anim
2454 		NPC_SetAnim( ent, SETANIM_LEGS, ent->client->ps.legsAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
2455 		NPC_SetAnim( ent, SETANIM_TORSO, ent->client->ps.torsoAnim, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART );
2456 	}
2457 
2458 	if ( ent->s.number == 0 )
2459 	{//player
2460 		G_CheckPlayerDarkSide();
2461 	}
2462 
2463 	if ( (ent->client->ps.stats[STAT_WEAPONS]&(1<<WP_SABER))
2464 		&& !ent->client->ps.saberStylesKnown )
2465 	{//um, if you have a saber, you need at least 1 style to use it with...
2466 		ent->client->ps.saberStylesKnown |= (1<<SS_MEDIUM);
2467 	}
2468 
2469 	return beamInEffect;
2470 }
2471 
2472 
2473 /*
2474 ===========
2475 ClientDisconnect
2476 
2477 Called when a player drops from the server.
2478 Will not be called between levels.
2479 ============
2480 */
ClientDisconnect(int clientNum)2481 void ClientDisconnect( int clientNum ) {
2482 	gentity_t	*ent;
2483 
2484 	ent = g_entities + clientNum;
2485 	if ( !ent->client ) {
2486 		return;
2487 	}
2488 
2489 	// send effect if they were completely connected
2490 /*	if ( ent->client->pers.connected == CON_CONNECTED ) {
2491 		// They don't get to take powerups with them!
2492 		// Especially important for stuff like CTF flags
2493 		TossClientItems ( ent );
2494 	}
2495 */
2496 	gi.unlinkentity (ent);
2497 	ent->s.modelindex = 0;
2498 	ent->inuse = qfalse;
2499 	ClearInUse(ent);
2500 	ent->classname = "disconnected";
2501 	ent->client->pers.connected = CON_DISCONNECTED;
2502 	ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE;
2503 
2504 	gi.SetConfigstring( CS_PLAYERS + clientNum, "");
2505 
2506 	IIcarusInterface::GetIcarus()->DeleteIcarusID(ent->m_iIcarusID);
2507 
2508 }
2509 
2510 
2511 
2512