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 "cg_headers.h"
25 
26 #define	CG_PLAYERS_CPP
27 #include "cg_media.h"
28 #include "FxScheduler.h"
29 #include "../game/ghoul2_shared.h"
30 #include "../game/anims.h"
31 #include "../game/wp_saber.h"
32 #include "../game/g_vehicles.h"
33 #include "../Rufl/hstring.h"
34 
35 #define	LOOK_SWING_SCALE	0.5f
36 #define	CG_SWINGSPEED		0.3f
37 
38 #include "animtable.h"
39 
40 extern qboolean WP_SaberBladeUseSecondBladeStyle( saberInfo_t *saber, int bladeNum );
41 extern void WP_SaberSwingSound( gentity_t *ent, int saberNum, swingType_t swingType );
42 
43 extern vmCvar_t	cg_debugHealthBars;
44 /*
45 
46 player entities generate a great deal of information from implicit ques
47 taken from the entityState_t
48 
49 */
50 
51 //rww - generic function for applying a shader to the skin.
52 extern vmCvar_t	cg_g2Marks;
CG_AddGhoul2Mark(int type,float size,vec3_t hitloc,vec3_t hitdirection,int entnum,vec3_t entposition,float entangle,CGhoul2Info_v & ghoul2,vec3_t modelScale,int lifeTime,int firstModel,vec3_t uaxis)53 void CG_AddGhoul2Mark(int type, float size, vec3_t hitloc, vec3_t hitdirection,
54 				int entnum, vec3_t entposition, float entangle, CGhoul2Info_v &ghoul2, vec3_t modelScale, int lifeTime, int firstModel, vec3_t uaxis )
55 {
56 	if ( !cg_g2Marks.integer )
57 	{//don't want these
58 		return;
59 	}
60 
61 	static SSkinGoreData goreSkin;
62 
63 	memset ( &goreSkin, 0, sizeof(goreSkin) );
64 
65 	goreSkin.growDuration = -1; // do not grow
66 	goreSkin.goreScaleStartFraction = 1.0; // default start scale
67 	goreSkin.frontFaces = true; // yes front
68 	goreSkin.backFaces = false; // no back
69 	goreSkin.lifeTime = lifeTime;
70 	goreSkin.firstModel = firstModel;
71 	/*
72 	//NOTE: sorry, have to disable fade-out of marks, causes sorting issues
73 	if (lifeTime > 0)
74 	{
75 		goreSkin.fadeOutTime = lifeTime*0.1; //use whatever you want here -rww
76 	}
77 	goreSkin.fadeRGB = true; //fade on RGB and alpha instead of just alpha (not needed for all shaders, but whatever)
78 	*/
79 
80 	goreSkin.currentTime = cg.time;
81 	goreSkin.entNum      = entnum;
82 	goreSkin.SSize		 = size;
83 	goreSkin.TSize		 = size;
84 	goreSkin.shader		 = type;
85 	goreSkin.theta		 = flrand(0.0f,6.28f);
86 
87 	if (uaxis)
88 	{
89 		goreSkin.backFaces = true;
90 		goreSkin.SSize		 = 6;
91 		goreSkin.TSize		 = 3;
92 		goreSkin.depthStart = -10;	//arbitrary depths, just limiting marks to near hit loc
93 		goreSkin.depthEnd	= 15;
94 		goreSkin.useTheta = false;
95 		VectorCopy(uaxis, goreSkin.uaxis);
96 		if(	VectorNormalize(goreSkin.uaxis) < 0.001f )
97 		{//too short to make a mark
98 			return;
99 		}
100 	}
101 	else
102 	{
103 		goreSkin.depthStart = -1000;
104 		goreSkin.depthEnd	= 1000;
105 		goreSkin.useTheta = true;
106 	}
107 	VectorCopy(modelScale, goreSkin.scale);
108 
109 	if ( VectorCompare( hitdirection, vec3_origin ) )
110 	{//wtf, no dir?  Make one up
111 		VectorSubtract( entposition, hitloc, goreSkin.rayDirection);
112 		VectorNormalize( goreSkin.rayDirection );
113 	}
114 	else
115 	{//use passed in value
116 		VectorCopy ( hitdirection, goreSkin.rayDirection);
117 	}
118 
119 	VectorCopy ( hitloc, goreSkin.hitLocation );
120 	VectorCopy ( entposition, goreSkin.position );
121 	goreSkin.angles[YAW] = entangle;
122 
123 	gi.G2API_AddSkinGore(ghoul2,goreSkin);
124 }
125 
126 qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *headModelName, const char *headSkinName,
127 									const char *torsoModelName, const char *torsoSkinName,
128 									const char *legsModelName, const char *legsSkinName );
129 
130 static void CG_PlayerFootsteps( centity_t *const cent, footstepType_t footStepType );
131 static void CG_PlayerAnimEvents( int animFileIndex, qboolean torso, int oldFrame, int frame, int entNum );
132 extern void BG_G2SetBoneAngles( centity_t *cent, gentity_t *gent, int boneIndex, const vec3_t angles, const int flags,
133 							 const Eorientations up, const Eorientations left, const Eorientations forward, qhandle_t *modelList );
134 extern void FX_BorgDeathSparkParticles( vec3_t origin, vec3_t angles, vec3_t vel, vec3_t user );
135 extern qboolean PM_SaberInSpecialAttack( int anim );
136 extern qboolean PM_SaberInAttack( int move );
137 extern qboolean PM_SaberInTransitionAny( int move );
138 extern int PM_GetTurnAnim( gentity_t *gent, int anim );
139 extern int PM_AnimLength( int index, animNumber_t anim );
140 extern qboolean PM_InRoll( playerState_t *ps );
141 extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
142 extern qboolean PM_SuperBreakWinAnim( int anim );
143 
144 //Basic set of custom sounds that everyone needs
145 // (keep numbers in ascending order in order for variant-capping to work)
146 const char	*cg_customBasicSoundNames[MAX_CUSTOM_BASIC_SOUNDS] =
147 {
148 	"*death1.wav",
149 	"*death2.wav",
150 	"*death3.wav",
151 	"*jump1.wav",
152 	"*pain25.wav",
153 	"*pain50.wav",
154 	"*pain75.wav",
155 	"*pain100.wav",
156 	"*gurp1.wav",
157 	"*gurp2.wav",
158 	"*drown.wav",
159 	"*gasp.wav",
160 	"*land1.wav",
161 	"*falling1.wav",
162 };
163 
164 //Used as a supplement to the basic set for enemies and hazard team
165 // (keep numbers in ascending order in order for variant-capping to work)
166 const char	*cg_customCombatSoundNames[MAX_CUSTOM_COMBAT_SOUNDS] =
167 {
168 	"*anger1.wav",	//Say when acquire an enemy when didn't have one before
169 	"*anger2.wav",
170 	"*anger3.wav",
171 	"*victory1.wav",	//Say when killed an enemy
172 	"*victory2.wav",
173 	"*victory3.wav",
174 	"*confuse1.wav",	//Say when confused
175 	"*confuse2.wav",
176 	"*confuse3.wav",
177 	"*pushed1.wav",	//Say when force-pushed
178 	"*pushed2.wav",
179 	"*pushed3.wav",
180 	"*choke1.wav",
181 	"*choke2.wav",
182 	"*choke3.wav",
183 	"*ffwarn.wav",
184 	"*ffturn.wav",
185 };
186 
187 //Used as a supplement to the basic set for stormtroopers
188 // (keep numbers in ascending order in order for variant-capping to work)
189 const char	*cg_customExtraSoundNames[MAX_CUSTOM_EXTRA_SOUNDS] =
190 {
191 	"*chase1.wav",
192 	"*chase2.wav",
193 	"*chase3.wav",
194 	"*cover1.wav",
195 	"*cover2.wav",
196 	"*cover3.wav",
197 	"*cover4.wav",
198 	"*cover5.wav",
199 	"*detected1.wav",
200 	"*detected2.wav",
201 	"*detected3.wav",
202 	"*detected4.wav",
203 	"*detected5.wav",
204 	"*lost1.wav",
205 	"*outflank1.wav",
206 	"*outflank2.wav",
207 	"*escaping1.wav",
208 	"*escaping2.wav",
209 	"*escaping3.wav",
210 	"*giveup1.wav",
211 	"*giveup2.wav",
212 	"*giveup3.wav",
213 	"*giveup4.wav",
214 	"*look1.wav",
215 	"*look2.wav",
216 	"*sight1.wav",
217 	"*sight2.wav",
218 	"*sight3.wav",
219 	"*sound1.wav",
220 	"*sound2.wav",
221 	"*sound3.wav",
222 	"*suspicious1.wav",
223 	"*suspicious2.wav",
224 	"*suspicious3.wav",
225 	"*suspicious4.wav",
226 	"*suspicious5.wav",
227 };
228 
229 //Used as a supplement to the basic set for jedi
230 // (keep numbers in ascending order in order for variant-capping to work)
231 const char	*cg_customJediSoundNames[MAX_CUSTOM_JEDI_SOUNDS] =
232 {
233 	"*combat1.wav",
234 	"*combat2.wav",
235 	"*combat3.wav",
236 	"*jdetected1.wav",
237 	"*jdetected2.wav",
238 	"*jdetected3.wav",
239 	"*taunt1.wav",
240 	"*taunt2.wav",
241 	"*taunt3.wav",
242 	"*jchase1.wav",
243 	"*jchase2.wav",
244 	"*jchase3.wav",
245 	"*jlost1.wav",
246 	"*jlost2.wav",
247 	"*jlost3.wav",
248 	"*deflect1.wav",
249 	"*deflect2.wav",
250 	"*deflect3.wav",
251 	"*gloat1.wav",
252 	"*gloat2.wav",
253 	"*gloat3.wav",
254 	"*pushfail.wav",
255 };
256 
257 
258 // done at registration time only...
259 //
260 // cuts down on sound-variant registration for low end machines,
261 //		eg *gloat1.wav (plus...2,...3) can be capped to all be just *gloat1.wav
262 //
GetCustomSound_VariantCapped(const char * ppsTable[],int iEntryNum,qboolean bForceVariant1)263 static const char *GetCustomSound_VariantCapped(const char *ppsTable[], int iEntryNum, qboolean bForceVariant1)
264 {
265 	extern vmCvar_t	cg_VariantSoundCap;
266 
267 //	const int iVariantCap = 2;	// test
268 	const int &iVariantCap = cg_VariantSoundCap.integer;
269 
270 	if (iVariantCap || bForceVariant1)
271 	{
272 		char *p = (char *)strchr(ppsTable[iEntryNum],'.');
273 		if (p && p-2 > ppsTable[iEntryNum] && isdigit(p[-1]) && !isdigit(p[-2]))
274 		{
275 			int iThisVariant = p[-1]-'0';
276 
277 			if (iThisVariant > iVariantCap || bForceVariant1)
278 			{
279 				// ok, let's not load this variant, so pick a random one below the cap value...
280 				//
281 				for (int i=0; i<2; i++)	// 1st pass, choose random, 2nd pass (if random not in list), choose xxx1, else fall through...
282 				{
283 					char sName[MAX_QPATH];
284 
285 					Q_strncpyz(sName, ppsTable[iEntryNum], sizeof(sName));
286 					p = strchr(sName,'.');
287 					if (p)
288 					{
289 						*p = '\0';
290 						sName[strlen(sName)-1] = '\0';	// strip the digit
291 
292 						int iRandom = bForceVariant1 ? 1 : (!i ? Q_irand(1,iVariantCap) : 1);
293 
294 						strcat(sName,va("%d",iRandom));
295 
296 						// does this exist in the entries before the original one?...
297 						//
298 						for (int iScanNum=0; iScanNum<iEntryNum; iScanNum++)
299 						{
300 							if (!Q_stricmp(ppsTable[iScanNum], sName))
301 							{
302 								// yeah, this entry is also present in the table, so ok to return it
303 								//
304 								return ppsTable[iScanNum];
305 							}
306 						}
307 					}
308 				}
309 
310 				// didn't find an entry corresponding to either the random name, or the xxxx1 version,
311 				//	so give up and drop through to return the original...
312 				//
313 			}
314 		}
315 	}
316 
317 	return ppsTable[iEntryNum];
318 }
319 
320 extern	cvar_t	*g_sex;
321 extern	cvar_t	*com_buildScript;
CG_RegisterCustomSounds(clientInfo_t * ci,int iSoundEntryBase,int iTableEntries,const char * ppsTable[],const char * psDir)322 static void CG_RegisterCustomSounds(clientInfo_t *ci, int iSoundEntryBase,
323 									int iTableEntries, const char *ppsTable[], const char *psDir
324 									)
325 {
326 	for ( int i=0 ; i<iTableEntries; i++ )
327 	{
328 		char	s[MAX_QPATH]={0};
329 		const char *pS = GetCustomSound_VariantCapped(ppsTable,i, qfalse);
330 		COM_StripExtension( pS, s, sizeof(s) );
331 
332 		sfxHandle_t hSFX = 0;
333 		if ( g_sex->string[0] == 'f' )
334 		{
335 			hSFX = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s_f.wav", psDir, s + 1) );
336 		}
337 		if (hSFX == 0 || com_buildScript->integer)
338 		{
339 			hSFX = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s.wav", psDir, s + 1) );
340 		}
341 		if (hSFX == 0)
342 		{
343 			// hmmm... variant in table was missing, so forcibly-retry with %1 version (which we may have just tried, but wtf?)...
344 			//
345 			pS = GetCustomSound_VariantCapped(ppsTable,i, qtrue);
346 			COM_StripExtension( pS, s, sizeof(s) );
347 			if ( g_sex->string[0] == 'f' )
348 			{
349 				hSFX = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s_f.wav", psDir, s + 1) );
350 			}
351 			if (hSFX == 0 || com_buildScript->integer)
352 			{
353 				hSFX = cgi_S_RegisterSound( va("sound/chars/%s/misc/%s.wav", psDir, s + 1) );
354 			}
355 			//
356 			// and fall through regardless...
357 			//
358 		}
359 
360 		ci->sounds[i + iSoundEntryBase] = hSFX;
361 	}
362 }
363 
364 
365 
366 /*
367 ================
368 CG_CustomSound
369 
370   NOTE: when you call this, check the value.  If zero, do not try to play the sound.
371 		Either that or when a sound that doesn't exist is played, don't play the null
372 		sound honk and don't display the error message
373 
374 ================
375 */
CG_CustomSound(int entityNum,const char * soundName,int customSoundSet)376 static sfxHandle_t	CG_CustomSound( int entityNum, const char *soundName, int customSoundSet )
377 {
378 	clientInfo_t *ci;
379 	int			i;
380 
381 	if ( soundName[0] != '*' )
382 	{
383 		return cgi_S_RegisterSound( soundName );
384 	}
385 
386 	if ( !g_entities[entityNum].client )
387 	{
388 		// No client, this should never happen, so just don't
389 #ifndef FINAL_BUILD
390 //		CG_Printf( "custom sound not on client: %s", soundName );
391 #endif
392 		return 0;
393 	}
394 	else
395 	{
396 		ci = &g_entities[entityNum].client->clientInfo;
397 	}
398 
399 	//FIXME: if the sound you want to play could not be found, pick another from the same
400 	//general grouping?  ie: if you want ff_2c and there is none, try ff_2b or ff_2a...
401 	switch ( customSoundSet )
402 	{
403 	case CS_BASIC:
404 		// There should always be a clientInfo structure if there is a client, but just make sure...
405 		if ( ci )
406 		{
407 			for ( i = 0 ; i < MAX_CUSTOM_BASIC_SOUNDS && cg_customBasicSoundNames[i] ; i++ )
408 			{
409 				if ( !Q_stricmp( soundName, cg_customBasicSoundNames[i] ) )
410 				{
411 					return ci->sounds[i];
412 				}
413 			}
414 		}
415 		break;
416 	case CS_COMBAT:
417 		// There should always be a clientInfo structure if there is a client, but just make sure...
418 		if ( ci )
419 		{
420 			for ( i = 0 ; i < MAX_CUSTOM_COMBAT_SOUNDS && cg_customCombatSoundNames[i] ; i++ )
421 			{
422 				if ( !Q_stricmp( soundName, cg_customCombatSoundNames[i] ) )
423 				{
424 					return ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS];
425 				}
426 			}
427 		}
428 		break;
429 	case CS_EXTRA:
430 		// There should always be a clientInfo structure if there is a client, but just make sure...
431 		if ( ci )
432 		{
433 			for ( i = 0 ; i < MAX_CUSTOM_EXTRA_SOUNDS && cg_customExtraSoundNames[i] ; i++ )
434 			{
435 				if ( !Q_stricmp( soundName, cg_customExtraSoundNames[i] ) )
436 				{
437 					return ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS];
438 				}
439 			}
440 		}
441 		break;
442 	case CS_JEDI:
443 		// There should always be a clientInfo structure if there is a client, but just make sure...
444 		if ( ci )
445 		{
446 			for ( i = 0 ; i < MAX_CUSTOM_JEDI_SOUNDS && cg_customJediSoundNames[i] ; i++ )
447 			{
448 				if ( !Q_stricmp( soundName, cg_customJediSoundNames[i] ) )
449 				{
450 					return ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS+MAX_CUSTOM_EXTRA_SOUNDS];
451 				}
452 			}
453 		}
454 		break;
455 	case CS_TRY_ALL:
456 	default:
457 		//no set specified, search all
458 		if ( ci )
459 		{
460 			for ( i = 0 ; i < MAX_CUSTOM_BASIC_SOUNDS && cg_customBasicSoundNames[i] ; i++ )
461 			{
462 				if ( !Q_stricmp( soundName, cg_customBasicSoundNames[i] ) )
463 				{
464 					return ci->sounds[i];
465 				}
466 			}
467 			for ( i = 0 ; i < MAX_CUSTOM_COMBAT_SOUNDS && cg_customCombatSoundNames[i] ; i++ )
468 			{
469 				if ( !Q_stricmp( soundName, cg_customCombatSoundNames[i] ) )
470 				{
471 					return ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS];
472 				}
473 			}
474 			for ( i = 0 ; i < MAX_CUSTOM_EXTRA_SOUNDS && cg_customExtraSoundNames[i] ; i++ )
475 			{
476 				if ( !Q_stricmp( soundName, cg_customExtraSoundNames[i] ) )
477 				{
478 					return ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS];
479 				}
480 			}
481 			for ( i = 0 ; i < MAX_CUSTOM_JEDI_SOUNDS && cg_customJediSoundNames[i] ; i++ )
482 			{
483 				if ( !Q_stricmp( soundName, cg_customJediSoundNames[i] ) )
484 				{
485 					return ci->sounds[i+MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS+MAX_CUSTOM_EXTRA_SOUNDS];
486 				}
487 			}
488 		}
489 		break;
490 	}
491 
492 #ifdef FINAL_BUILD
493 	CG_Printf( "Unknown custom sound: %s", soundName );
494 #else
495 	CG_Error( "Unknown custom sound: %s", soundName );
496 #endif
497 	return 0;
498 }
499 
CG_TryPlayCustomSound(vec3_t origin,int entityNum,soundChannel_t channel,const char * soundName,int customSoundSet)500 qboolean CG_TryPlayCustomSound( vec3_t origin, int entityNum, soundChannel_t channel, const char *soundName, int customSoundSet )
501 {
502 	sfxHandle_t	soundIndex = CG_CustomSound( entityNum, soundName, customSoundSet );
503 	if ( !soundIndex )
504 	{
505 		return qfalse;
506 	}
507 
508 	cgi_S_StartSound( origin, entityNum, channel, soundIndex );
509 	return qtrue;
510 }
511 /*
512 ======================
513 CG_NewClientinfo
514 
515   For player only, NPCs get them through NPC_stats and G_ModelIndex
516 ======================
517 */
CG_NewClientinfo(int clientNum)518 void CG_NewClientinfo( int clientNum )
519 {
520 	clientInfo_t *ci;
521 	const char	*configstring;
522 	const char	*v;
523 //	const char	*s;
524 //	int			i;
525 
526 	configstring = CG_ConfigString( clientNum + CS_PLAYERS );
527 
528 	if ( !configstring[0] )
529 	{
530 		return;		// player just left
531 	}
532 	//ci = &cgs.clientinfo[clientNum];
533 	if ( !(g_entities[clientNum].client) )
534 	{
535 		return;
536 	}
537 	ci = &g_entities[clientNum].client->clientInfo;
538 
539 	// isolate the player's name
540 	v = Info_ValueForKey(configstring, "n");
541 	Q_strncpyz( ci->name, v, sizeof( ci->name ) );
542 
543 	// handicap
544 	v = Info_ValueForKey( configstring, "hc" );
545 	ci->handicap = atoi( v );
546 
547 	// team
548 	v = Info_ValueForKey( configstring, "t" );
549 	ci->team = (team_t) atoi( v );
550 
551 	// legsModel
552 	v = Info_ValueForKey( configstring, "legsModel" );
553 
554 	Q_strncpyz(			g_entities[clientNum].client->renderInfo.legsModelName, v,
555 				sizeof(	g_entities[clientNum].client->renderInfo.legsModelName));
556 
557 	// torsoModel
558 	v = Info_ValueForKey( configstring, "torsoModel" );
559 
560 	Q_strncpyz(			g_entities[clientNum].client->renderInfo.torsoModelName, v,
561 				sizeof(	g_entities[clientNum].client->renderInfo.torsoModelName));
562 
563 	// headModel
564 	v = Info_ValueForKey( configstring, "headModel" );
565 
566 	Q_strncpyz(			g_entities[clientNum].client->renderInfo.headModelName, v,
567 				sizeof(	g_entities[clientNum].client->renderInfo.headModelName));
568 
569 	// sounds
570 	v = Info_ValueForKey( configstring, "snd" );
571 
572 	ci->customBasicSoundDir = G_NewString( v );
573 
574 	//player uses only the basic custom and combat sound sets, not the extra or jedi
575 	CG_RegisterCustomSounds(ci,
576 							0,							// int iSoundEntryBase,
577 							MAX_CUSTOM_BASIC_SOUNDS,	// int iTableEntries,
578 							cg_customBasicSoundNames,	// const char *ppsTable[],
579 							ci->customBasicSoundDir		// const char *psDir
580 							);
581 
582 	CG_RegisterCustomSounds(ci,
583 							MAX_CUSTOM_BASIC_SOUNDS,	// int iSoundEntryBase,
584 							MAX_CUSTOM_COMBAT_SOUNDS,	// int iTableEntries,
585 							cg_customCombatSoundNames,	// const char *ppsTable[],
586 							ci->customBasicSoundDir		// const char *psDir
587 							);
588 	ci->infoValid = qfalse;
589 }
590 
591 /*
592 CG_RegisterNPCCustomSounds
593 */
CG_RegisterNPCCustomSounds(clientInfo_t * ci)594 void CG_RegisterNPCCustomSounds( clientInfo_t *ci )
595 {
596 //	const char	*s;
597 //	int			i;
598 
599 	// sounds
600 
601 	if ( ci->customBasicSoundDir && ci->customBasicSoundDir[0] )
602 	{
603 		CG_RegisterCustomSounds(ci,
604 								0,							// int iSoundEntryBase,
605 								MAX_CUSTOM_BASIC_SOUNDS,	// int iTableEntries,
606 								cg_customBasicSoundNames,	// const char *ppsTable[],
607 								ci->customBasicSoundDir		// const char *psDir
608 								);
609 	}
610 
611 	if ( ci->customCombatSoundDir && ci->customCombatSoundDir[0] )
612 	{
613 		CG_RegisterCustomSounds(ci,
614 								MAX_CUSTOM_BASIC_SOUNDS,	// int iSoundEntryBase,
615 								MAX_CUSTOM_COMBAT_SOUNDS,	// int iTableEntries,
616 								cg_customCombatSoundNames,	// const char *ppsTable[],
617 								ci->customCombatSoundDir	// const char *psDir
618 								);
619 	}
620 
621 	if ( ci->customExtraSoundDir && ci->customExtraSoundDir[0] )
622 	{
623 		CG_RegisterCustomSounds(ci,
624 								MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS,	// int iSoundEntryBase,
625 								MAX_CUSTOM_EXTRA_SOUNDS,	// int iTableEntries,
626 								cg_customExtraSoundNames,	// const char *ppsTable[],
627 								ci->customExtraSoundDir		// const char *psDir
628 								);
629 	}
630 
631 	if ( ci->customJediSoundDir && ci->customJediSoundDir[0] )
632 	{
633 		CG_RegisterCustomSounds(ci,
634 								MAX_CUSTOM_BASIC_SOUNDS+MAX_CUSTOM_COMBAT_SOUNDS+MAX_CUSTOM_EXTRA_SOUNDS,							// int iSoundEntryBase,
635 								MAX_CUSTOM_JEDI_SOUNDS,		// int iTableEntries,
636 								cg_customJediSoundNames,	// const char *ppsTable[],
637 								ci->customJediSoundDir		// const char *psDir
638 								);
639 	}
640 }
641 
642 //=============================================================================
643 
644 /*
645 void CG_RegisterNPCEffects( team_t team )
646 
647   This should register all the shaders, models and sounds used by a specific type
648   of NPC's spawn, death and other miscellaneous effects.  NOT WEAPON EFFECTS, as those
649   are taken care of in CG_RegisterWeapon
650 */
651 /*
652 void CG_RegisterNPCEffects( team_t team )
653 {
654 	switch( team )
655 	{
656 
657 	case TEAM_ENEMY:
658 		break;
659 
660 	case TEAM_NEUTRAL:
661 		break;
662 
663 	case TEAM_PLAYER:
664 		break;
665 
666 	default:
667 		break;
668 	}
669 }
670 */
671 /*
672 =============================================================================
673 
674 PLAYER ANIMATION
675 
676 =============================================================================
677 */
678 
ValidAnimFileIndex(int index)679 qboolean ValidAnimFileIndex ( int index )
680 {
681 	if ( index < 0 || index >= level.numKnownAnimFileSets )
682 	{
683 		Com_Printf( S_COLOR_RED "Bad animFileIndex: %d\n", index );
684 		return qfalse;
685 	}
686 
687 	return qtrue;
688 }
689 
690 
691 /*
692 ======================
693 CG_ClearAnimEvtCache
694 
695 resets all the eventcache so that a vid restart will recache them
696 ======================
697 */
CG_ClearAnimEvtCache(void)698 void CG_ClearAnimEvtCache( void )
699 {
700 	int i;
701 	for (i=0; i < level.numKnownAnimFileSets; i++)
702 	{
703 		// TODO: Make this work again?
704 	//	level.knownAnimFileSets[i].eventsParsed = qfalse;
705 	}
706 }
707 
708 /*
709 ===============
710 CG_SetLerpFrameAnimation
711 ===============
712 */
CG_SetLerpFrameAnimation(clientInfo_t * ci,lerpFrame_t * lf,int newAnimation)713 static void CG_SetLerpFrameAnimation( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation )
714 {
715 	animation_t	*anim;
716 
717 	if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS )
718 	{
719 #ifdef FINAL_BUILD
720 		newAnimation = 0;
721 #else
722 		CG_Error( "Bad animation number: %i for ", newAnimation, ci->name );
723 #endif
724 	}
725 
726 	lf->animationNumber = newAnimation;
727 
728 	if ( !ValidAnimFileIndex( ci->animFileIndex ) )
729 	{
730 #ifdef FINAL_BUILD
731 		ci->animFileIndex = 0;
732 #else
733 		CG_Error( "Bad animFileIndex: %i for %s", ci->animFileIndex, ci->name);
734 #endif
735 	}
736 
737 	anim = &level.knownAnimFileSets[ci->animFileIndex].animations[ newAnimation ];
738 
739 	lf->animation = anim;
740 	lf->animationTime = lf->frameTime + abs(anim->frameLerp);
741 }
742 
743 /*
744 ===============
745 CG_RunLerpFrame
746 
747 Sets cg.snap, cg.oldFrame, and cg.backlerp
748 cg.time should be between oldFrameTime and frameTime after exit
749 ===============
750 */
CG_RunLerpFrame(clientInfo_t * ci,lerpFrame_t * lf,int newAnimation,float fpsMod,int entNum)751 static qboolean CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float fpsMod, int entNum ) {
752 	int			f, animFrameTime;
753 	animation_t	*anim;
754 	qboolean	newFrame = qfalse;
755 
756 	if(fpsMod > 2 || fpsMod < 0.5)
757 	{//should have been set right
758 		fpsMod = 1.0f;
759 	}
760 
761 	// see if the animation sequence is switching
762 	//FIXME: allow multiple-frame overlapped lerping between sequences? - Possibly last 3 of last seq and first 3 of next seq?
763 	if ( newAnimation != lf->animationNumber || !lf->animation )
764 	{
765 		CG_SetLerpFrameAnimation( ci, lf, newAnimation );
766 	}
767 
768 	// if we have passed the current frame, move it to
769 	// oldFrame and calculate a new frame
770 	if ( cg.time >= lf->frameTime )
771 	{
772 		lf->oldFrame = lf->frame;
773 		lf->oldFrameTime = lf->frameTime;
774 
775 		// get the next frame based on the animation
776 		anim = lf->animation;
777 		//Do we need to speed up or slow down the anim?
778 		/*if(fpsMod != 1.0)
779 		{//Note!  despite it's name, a higher fpsMod slows down the anim, a lower one speeds it up
780 			animFrameTime = ceil(lf->frameTime * fpsMod);
781 		}
782 		else*/
783 		{
784 			animFrameTime = abs(anim->frameLerp);
785 
786 			//special hack for player to ensure quick weapon change
787 			if ( entNum == 0 )
788 			{
789 				if ( lf->animationNumber == TORSO_DROPWEAP1 || lf->animationNumber == TORSO_RAISEWEAP1 )
790 				{
791 					animFrameTime = 50;
792 				}
793 			}
794 		}
795 
796 		if ( cg.time < lf->animationTime )
797 		{
798 			lf->frameTime = lf->animationTime;		// initial lerp
799 		}
800 		else
801 		{
802 			lf->frameTime = lf->oldFrameTime + animFrameTime;
803 		}
804 
805 		f = ( lf->frameTime - lf->animationTime ) / animFrameTime;
806 		if ( f >= anim->numFrames )
807 		{//Reached the end of the anim
808 			//FIXME: Need to set a flag here to TASK_COMPLETE
809 			f -= anim->numFrames;
810 			if ( anim->loopFrames != -1 ) //Before 0 meant no loop
811 			{
812 				if(anim->numFrames - anim->loopFrames == 0)
813 				{
814 					f %= anim->numFrames;
815 				}
816 				else
817 				{
818 					f %= (anim->numFrames - anim->loopFrames);
819 				}
820 				f += anim->loopFrames;
821 			}
822 			else
823 			{
824 				f = anim->numFrames - 1;
825 				if (f<0)
826 				{
827 					f = 0;
828 				}
829 				// the animation is stuck at the end, so it
830 				// can immediately transition to another sequence
831 				lf->frameTime = cg.time;
832 			}
833 		}
834 
835 		if ( anim->frameLerp < 0 )
836 		{
837 			lf->frame = anim->firstFrame + anim->numFrames - 1 - f;
838 		}
839 		else
840 		{
841 			lf->frame = anim->firstFrame + f;
842 		}
843 
844 		if ( cg.time > lf->frameTime )
845 		{
846 			lf->frameTime = cg.time;
847 		}
848 
849 		newFrame = qtrue;
850 	}
851 
852 	if ( lf->frameTime > cg.time + 200 )
853 	{
854 		lf->frameTime = cg.time;
855 	}
856 
857 	if ( lf->oldFrameTime > cg.time )
858 	{
859 		lf->oldFrameTime = cg.time;
860 	}
861 	// calculate current lerp value
862 	if ( lf->frameTime == lf->oldFrameTime )
863 	{
864 		lf->backlerp = 0;
865 	}
866 	else
867 	{
868 		lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
869 	}
870 
871 	return newFrame;
872 }
873 
874 
875 /*
876 ===============
877 CG_ClearLerpFrame
878 ===============
879 */
CG_ClearLerpFrame(clientInfo_t * ci,lerpFrame_t * lf,int animationNumber)880 static void CG_ClearLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber )
881 {
882 	lf->frameTime = lf->oldFrameTime = cg.time;
883 	CG_SetLerpFrameAnimation( ci, lf, animationNumber );
884 	if ( lf->animation->frameLerp < 0 )
885 	{//Plays backwards
886 		lf->oldFrame = lf->frame = (lf->animation->firstFrame + lf->animation->numFrames);
887 	}
888 	else
889 	{
890 		lf->oldFrame = lf->frame = lf->animation->firstFrame;
891 	}
892 }
893 
894 /*
895 ===============
896 CG_PlayerAnimation
897 ===============
898 */
CG_PlayerAnimation(centity_t * cent,int * legsOld,int * legs,float * legsBackLerp,int * torsoOld,int * torso,float * torsoBackLerp)899 static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp,
900 						int *torsoOld, int *torso, float *torsoBackLerp ) {
901 	clientInfo_t	*ci;
902 	int				legsAnim;
903 	int				legsTurnAnim = -1;
904 	qboolean		newLegsFrame = qfalse;
905 	qboolean		newTorsoFrame = qfalse;
906 
907 	ci = &cent->gent->client->clientInfo;
908 	//Changed this from cent->currentState.legsAnim to cent->gent->client->ps.legsAnim because it was screwing up our timers when we've just changed anims while turning
909 	legsAnim = cent->gent->client->ps.legsAnim;
910 
911 	// do the shuffle turn frames locally (MAN this is an Fugly-ass hack!)
912 
913 	if ( cent->pe.legs.yawing )
914 	{
915 		legsTurnAnim = PM_GetTurnAnim( cent->gent, legsAnim );
916 	}
917 
918 	if ( legsTurnAnim != -1 )
919 	{
920 		newLegsFrame = CG_RunLerpFrame( ci, &cent->pe.legs, legsTurnAnim, cent->gent->client->renderInfo.legsFpsMod, cent->gent->s.number );
921 		//This line doesn't seem to serve any useful purpose, rather it
922 		//breaks things since any task waiting for a lower anim to complete
923 		//never will finish if this happens!!!
924 		//cent->gent->client->ps.legsAnimTimer = 0;
925 	}
926 	else
927 	{
928 		newLegsFrame = CG_RunLerpFrame( ci, &cent->pe.legs, legsAnim, cent->gent->client->renderInfo.legsFpsMod, cent->gent->s.number);
929 	}
930 
931 	*legsOld = cent->pe.legs.oldFrame;
932 	*legs = cent->pe.legs.frame;
933 	*legsBackLerp = cent->pe.legs.backlerp;
934 
935 	if( newLegsFrame )
936 	{
937 		if ( ValidAnimFileIndex( ci->animFileIndex ) )
938 		{
939 			CG_PlayerAnimEvents( ci->animFileIndex, qfalse, cent->pe.legs.frame, cent->pe.legs.frame, cent->currentState.number );
940 		}
941 	}
942 
943 	newTorsoFrame = CG_RunLerpFrame( ci, &cent->pe.torso, cent->gent->client->ps.torsoAnim, cent->gent->client->renderInfo.torsoFpsMod, cent->gent->s.number );
944 
945 	*torsoOld = cent->pe.torso.oldFrame;
946 	*torso = cent->pe.torso.frame;
947 	*torsoBackLerp = cent->pe.torso.backlerp;
948 
949 	if( newTorsoFrame )
950 	{
951 		if ( ValidAnimFileIndex( ci->animFileIndex ) )
952 		{
953 			CG_PlayerAnimEvents(ci->animFileIndex, qtrue, cent->pe.torso.frame, cent->pe.torso.frame, cent->currentState.number );
954 		}
955 	}
956 }
957 
958 
959 extern int PM_LegsAnimForFrame( gentity_t *ent, int legsFrame );
960 extern int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame );
CG_PlayerAnimEventDo(centity_t * cent,animevent_t * animEvent)961 static void CG_PlayerAnimEventDo( centity_t *cent, animevent_t *animEvent )
962 {
963 	//FIXME: pass in event, switch off the type
964 	if ( cent == NULL || animEvent == NULL )
965 	{
966 		return;
967 	}
968 
969 	soundChannel_t channel = CHAN_AUTO;
970 	switch ( animEvent->eventType )
971 	{
972 	case AEV_SOUNDCHAN:
973 		channel = (soundChannel_t)animEvent->eventData[AED_SOUNDCHANNEL];
974 	case AEV_SOUND:
975 		// are there variations on the sound?
976 		{
977 			const int holdSnd = animEvent->eventData[ AED_SOUNDINDEX_START+Q_irand( 0, animEvent->eventData[AED_SOUND_NUMRANDOMSNDS] ) ];
978 			if ( holdSnd > 0 )
979 			{
980 				if ( cgs.sound_precache[ holdSnd ] )
981 				{
982 					cgi_S_StartSound( NULL, cent->currentState.clientNum, channel, cgs.sound_precache[holdSnd ] );
983 				}
984 				else
985 				{//try a custom sound
986 					const char *s = CG_ConfigString( CS_SOUNDS + holdSnd );
987 					CG_TryPlayCustomSound(NULL, cent->currentState.clientNum, channel, va("%s.wav",s), CS_TRY_ALL );
988 				}
989 			}
990 		}
991 		break;
992 	case AEV_SABER_SWING:
993 		if ( cent->gent )
994 		{//cheat over to game side and play sound from there...
995 			WP_SaberSwingSound( cent->gent, animEvent->eventData[AED_SABER_SWING_SABERNUM], (swingType_t)animEvent->eventData[AED_SABER_SWING_TYPE] );
996 		}
997 		break;
998 	case AEV_SABER_SPIN:
999 		if ( cent->gent
1000 			&& cent->gent->client )
1001 		{
1002 			saberInfo_t *saber = &cent->gent->client->ps.saber[animEvent->eventData[AED_SABER_SPIN_SABERNUM]];
1003 			if ( saber )
1004 			{
1005 				int spinSound = 0;
1006 				if ( saber->spinSound
1007 					&& cgs.sound_precache[saber->spinSound] )
1008 				{//use override
1009 					spinSound = cgs.sound_precache[saber->spinSound];
1010 				}
1011 				else
1012 				{
1013 					switch ( animEvent->eventData[AED_SABER_SPIN_TYPE] )
1014 					{
1015 					case 0://saberspinoff
1016 						spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspinoff.wav" );
1017 						break;
1018 					case 1://saberspin
1019 						spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin.wav" );
1020 						break;
1021 					case 2://saberspin1
1022 						spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin1.wav" );
1023 						break;
1024 					case 3://saberspin2
1025 						spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin2.wav" );
1026 						break;
1027 					case 4://saberspin3
1028 						spinSound = cgi_S_RegisterSound( "sound/weapons/saber/saberspin3.wav" );
1029 						break;
1030 					default://random saberspin1-3
1031 						spinSound = cgi_S_RegisterSound( va( "sound/weapons/saber/saberspin%d.wav", Q_irand(1,3)) );
1032 						break;
1033 					}
1034 				}
1035 				if ( spinSound )
1036 				{
1037 					cgi_S_StartSound( NULL, cent->currentState.clientNum, CHAN_AUTO, spinSound );
1038 				}
1039 			}
1040 		}
1041 		break;
1042 	case AEV_FOOTSTEP:
1043 		CG_PlayerFootsteps( cent, (footstepType_t)animEvent->eventData[AED_FOOTSTEP_TYPE] );
1044 		break;
1045 	case AEV_EFFECT:
1046 			if ( animEvent->eventData[AED_EFFECTINDEX] == -1 )
1047 		{//invalid effect
1048 			if ( animEvent->stringData != NULL
1049 				&& animEvent->stringData[0] )
1050 			{//some sort of hard-coded effect
1051 				if ( Q_stricmp( "push_l", animEvent->stringData ) == 0 )
1052 				{
1053 					cgi_S_StartSound ( cent->lerpOrigin, cent->currentState.clientNum, CHAN_AUTO, cgi_S_RegisterSound( "sound/weapons/force/push.wav" ) );
1054 					cent->gent->client->ps.powerups[PW_FORCE_PUSH] = cg.time + animEvent->eventData[AED_EFFECT_PROBABILITY];//AED_EFFECT_PROBABILITY in this case is the number of ms for the effect to last
1055 					cent->gent->client->pushEffectFadeTime = 0;
1056 				}
1057 				else if ( Q_stricmp( "push_r", animEvent->stringData ) == 0 )
1058 				{
1059 					cgi_S_StartSound ( cent->lerpOrigin, cent->currentState.clientNum, CHAN_AUTO, cgi_S_RegisterSound( "sound/weapons/force/push.wav" ) );
1060 					cent->gent->client->ps.powerups[PW_FORCE_PUSH_RHAND] = cg.time + animEvent->eventData[AED_EFFECT_PROBABILITY];//AED_EFFECT_PROBABILITY in this case is the number of ms for the effect to last
1061 					cent->gent->client->pushEffectFadeTime = 0;
1062 				}
1063 				else if ( Q_stricmp( "scepter_beam", animEvent->stringData ) == 0 )
1064 				{
1065 					int modelIndex = cent->gent->weaponModel[1];
1066 					if ( modelIndex <= 0 )
1067 					{
1068 						modelIndex = cent->gent->cinematicModel;
1069 					}
1070 					if ( modelIndex > 0 )
1071 					{//we have a cinematic model
1072 						int boltIndex = gi.G2API_AddBolt( &cent->gent->ghoul2[modelIndex], "*flash" );
1073 						if ( boltIndex > -1 )
1074 						{//cinematic model has a flash bolt
1075 							CG_PlayEffectBolted( "scepter/beam.efx", modelIndex, boltIndex, cent->currentState.clientNum, cent->lerpOrigin, animEvent->eventData[AED_EFFECT_PROBABILITY], qtrue );//AED_EFFECT_PROBABILITY in this case is the number of ms for the effect to last
1076 						}
1077 					}
1078 				}
1079 				//FIXME: add more
1080 			}
1081 		}
1082 		else
1083 		{
1084 			//add bolt, play effect
1085 			if ( animEvent->stringData != NULL && cent && cent->gent && cent->gent->ghoul2.size() )
1086 			{//have a bolt name we want to use
1087 				animEvent->eventData[AED_MODELINDEX] = cent->gent->playerModel;
1088 				if ( ( Q_stricmpn( "*blade", animEvent->stringData, 6 ) == 0
1089 					   || Q_stricmp( "*flash", animEvent->stringData ) == 0 )
1090 					&& cent->gent->weaponModel[0] > 0 )
1091 				{//must be a weapon, try weapon 0?
1092 					animEvent->eventData[AED_BOLTINDEX] = gi.G2API_AddBolt( &cent->gent->ghoul2[cent->gent->weaponModel[0]], animEvent->stringData );
1093 					if ( animEvent->eventData[AED_BOLTINDEX] != -1 )
1094 					{//found it!
1095 						animEvent->eventData[AED_MODELINDEX] = cent->gent->weaponModel[0];
1096 					}
1097 					else
1098 					{//hmm, just try on the player model, then?
1099 						animEvent->eventData[AED_BOLTINDEX] = gi.G2API_AddBolt( &cent->gent->ghoul2[cent->gent->playerModel], animEvent->stringData );
1100 					}
1101 				}
1102 				else
1103 				{
1104 					animEvent->eventData[AED_BOLTINDEX] = gi.G2API_AddBolt( &cent->gent->ghoul2[cent->gent->playerModel], animEvent->stringData );
1105 				}
1106 				animEvent->stringData = NULL;//so we don't try to do this again
1107 			}
1108 			if ( animEvent->eventData[AED_BOLTINDEX] != -1 )
1109 			{//have a bolt we want to play the effect on
1110 				CG_PlayEffectIDBolted( animEvent->eventData[AED_EFFECTINDEX],
1111 								animEvent->eventData[AED_MODELINDEX],
1112 								animEvent->eventData[AED_BOLTINDEX],
1113 								cent->currentState.clientNum,
1114 								cent->lerpOrigin );
1115 			}
1116 			else
1117 			{//play at origin?  FIXME: maybe allow a fwd/rt/up offset?
1118 				const vec3_t up = {0,0,1};
1119 				CG_PlayEffectID( animEvent->eventData[AED_EFFECTINDEX], cent->lerpOrigin, up );
1120 				//G_PlayEffect( animEvent->eventData[AED_EFFECTINDEX], cent->lerpOrigin, up );
1121 				//theFxScheduler.PlayEffect( animEvent->eventData[AED_EFFECTINDEX], cent->lerpOrigin, qfalse );
1122 			}
1123 		}
1124 		break;
1125 	case AEV_FIRE:
1126 		//add fire event
1127 		if ( animEvent->eventData[AED_FIRE_ALT] )
1128 		{
1129 			G_AddEvent( cent->gent, EV_ALT_FIRE, 0 );
1130 		}
1131 		else
1132 		{
1133 			G_AddEvent( cent->gent, EV_FIRE_WEAPON, 0 );
1134 		}
1135 		break;
1136 	case AEV_MOVE:
1137 		//make him jump
1138 		if ( cent && cent->gent && cent->gent->client )
1139 		{
1140 			if ( cent->gent->client->ps.groundEntityNum != ENTITYNUM_NONE )
1141 			{//on something
1142 				vec3_t	fwd, rt, up, angles = {0, cent->gent->client->ps.viewangles[YAW], 0};
1143 				AngleVectors( angles, fwd, rt, up );
1144 				//FIXME: set or add to velocity?
1145 				VectorScale( fwd, animEvent->eventData[AED_MOVE_FWD], cent->gent->client->ps.velocity );
1146 				VectorMA( cent->gent->client->ps.velocity, animEvent->eventData[AED_MOVE_RT], rt, cent->gent->client->ps.velocity );
1147 				VectorMA( cent->gent->client->ps.velocity, animEvent->eventData[AED_MOVE_UP], up, cent->gent->client->ps.velocity );
1148 
1149 				if ( animEvent->eventData[AED_MOVE_UP] > 0 )
1150 				{//a jump
1151 					cent->gent->client->ps.pm_flags |= PMF_JUMPING;
1152 
1153 					G_AddEvent( cent->gent, EV_JUMP, 0 );
1154 					//FIXME: if have force jump, do this?  or specify sound in the event data?
1155 					//cent->gent->client->ps.forceJumpZStart = cent->gent->client->ps.origin[2];//so we don't take damage if we land at same height
1156 					//G_SoundOnEnt( cent->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
1157 				}
1158 			}
1159 		}
1160 		break;
1161 	default:
1162 		return;
1163 		break;
1164 	}
1165 }
1166 
CG_PlayerAnimEvents(int animFileIndex,qboolean torso,int oldFrame,int frame,int entNum)1167 static void CG_PlayerAnimEvents( int animFileIndex, qboolean torso, int oldFrame, int frame, int entNum )
1168 {
1169 	int		i;
1170 	int		firstFrame = 0, lastFrame = 0;
1171 	qboolean	doEvent = qfalse, inSameAnim = qfalse, loopAnim = qfalse, match = qfalse, animBackward = qfalse;
1172 	animevent_t *animEvents = NULL;
1173 	int		glaIndex = -1;
1174 
1175 	if ( g_entities[entNum].ghoul2.size() )
1176 	{
1177 		glaIndex = gi.G2API_GetAnimIndex(&(g_entities[entNum].ghoul2[0]));
1178 	}
1179 
1180 	if ( torso )
1181 	{
1182 		animEvents = level.knownAnimFileSets[animFileIndex].torsoAnimEvents;
1183 	}
1184 	else
1185 	{
1186 		animEvents = level.knownAnimFileSets[animFileIndex].legsAnimEvents;
1187 	}
1188 	if ( abs(oldFrame-frame) > 1 )
1189 	{//given a range, see if keyFrame falls in that range
1190 		int oldAnim, anim;
1191 		if ( torso )
1192 		{
1193 			//more precise, slower
1194 			oldAnim = PM_TorsoAnimForFrame( &g_entities[entNum], oldFrame );
1195 			anim = PM_TorsoAnimForFrame( &g_entities[entNum], frame );
1196 		}
1197 		else
1198 		{
1199 			//more precise, slower
1200 			oldAnim = PM_LegsAnimForFrame( &g_entities[entNum], oldFrame );
1201 			anim = PM_LegsAnimForFrame( &g_entities[entNum], frame );
1202 		}
1203 
1204 		if ( anim != oldAnim )
1205 		{//not in same anim
1206 			inSameAnim = qfalse;
1207 			//FIXME: we *could* see if the oldFrame was *just about* to play the keyframed sound...
1208 		}
1209 		else
1210 		{//still in same anim, check for looping anim
1211 			inSameAnim = qtrue;
1212 			animation_t *animation = &level.knownAnimFileSets[animFileIndex].animations[anim];
1213 			animBackward = (qboolean)(animation->frameLerp<0);
1214 			if ( animation->loopFrames != -1 )
1215 			{//a looping anim!
1216 				loopAnim = qtrue;
1217 				firstFrame = animation->firstFrame;
1218 				lastFrame = animation->firstFrame+animation->numFrames;
1219 			}
1220 		}
1221 	}
1222 
1223 	hstring myModel = g_entities[entNum].NPC_type;		//apparently NPC_type is always the same as the model name???
1224 
1225 	// Check for anim event
1226 	for ( i=0; i < MAX_ANIM_EVENTS; ++i )
1227 	{
1228 		if ( animEvents[i].eventType == AEV_NONE )	// No event, end of list
1229 		{
1230 			break;
1231 		}
1232 
1233 		if (glaIndex != -1 && animEvents[i].glaIndex!=glaIndex)
1234 		{
1235 			continue;
1236 		}
1237 
1238 		match = qfalse;
1239 		if (animEvents[i].modelOnly==0 || animEvents[i].modelOnly==myModel.handle())
1240 		{
1241 			if ( animEvents[i].keyFrame == frame )
1242 			{//exact match
1243 				match = qtrue;
1244 			}
1245 			else if ( abs(oldFrame-frame) > 1 )//&& cg_reliableAnimEvents.integer )
1246 			{//given a range, see if keyFrame falls in that range
1247 				if ( inSameAnim )
1248 				{//if changed anims altogether, sorry, the sound is lost
1249 					if ( abs(oldFrame-animEvents[i].keyFrame) <= 3
1250 						|| abs(frame-animEvents[i].keyFrame) <= 3 )
1251 					{//must be at least close to the keyframe
1252 						if ( animBackward )
1253 						{//animation plays backwards
1254 							if ( oldFrame > animEvents[i].keyFrame && frame < animEvents[i].keyFrame )
1255 							{//old to new passed through keyframe
1256 								match = qtrue;
1257 							}
1258 							else if ( loopAnim )
1259 							{//hmm, didn't pass through it linearally, see if we looped
1260 								if ( animEvents[i].keyFrame >= firstFrame && animEvents[i].keyFrame < lastFrame )
1261 								{//keyframe is in this anim
1262 									if ( oldFrame > animEvents[i].keyFrame
1263 										&& frame > oldFrame )
1264 									{//old to new passed through keyframe
1265 										match = qtrue;
1266 									}
1267 								}
1268 							}
1269 						}
1270 						else
1271 						{//anim plays forwards
1272 							if ( oldFrame < animEvents[i].keyFrame && frame > animEvents[i].keyFrame )
1273 							{//old to new passed through keyframe
1274 								match = qtrue;
1275 							}
1276 							else if ( loopAnim )
1277 							{//hmm, didn't pass through it linearally, see if we looped
1278 								if ( animEvents[i].keyFrame >= firstFrame && animEvents[i].keyFrame < lastFrame )
1279 								{//keyframe is in this anim
1280 									if ( oldFrame < animEvents[i].keyFrame
1281 										&& frame < oldFrame )
1282 									{//old to new passed through keyframe
1283 										match = qtrue;
1284 									}
1285 								}
1286 							}
1287 						}
1288 					}
1289 				}
1290 			}
1291 			if ( match )
1292 			{
1293 				switch ( animEvents[i].eventType )
1294 				{
1295 				case AEV_SOUNDCHAN:
1296 				case AEV_SOUND:
1297 					// Determine probability of playing sound
1298 					if (!animEvents[i].eventData[AED_SOUND_PROBABILITY])	// 100%
1299 					{
1300 						doEvent = qtrue;
1301 					}
1302 					else if (animEvents[i].eventData[AED_SOUND_PROBABILITY] > Q_irand(0, 99) )
1303 					{
1304 						doEvent = qtrue;
1305 					}
1306 					break;
1307 				case AEV_SABER_SWING:
1308 					// Determine probability of playing sound
1309 					if (!animEvents[i].eventData[AED_SABER_SWING_PROBABILITY])	// 100%
1310 					{
1311 						doEvent = qtrue;
1312 					}
1313 					else if (animEvents[i].eventData[AED_SABER_SWING_PROBABILITY] > Q_irand(0, 99) )
1314 					{
1315 						doEvent = qtrue;
1316 					}
1317 					break;
1318 				case AEV_SABER_SPIN:
1319 					// Determine probability of playing sound
1320 					if (!animEvents[i].eventData[AED_SABER_SPIN_PROBABILITY])	// 100%
1321 					{
1322 						doEvent = qtrue;
1323 					}
1324 					else if (animEvents[i].eventData[AED_SABER_SPIN_PROBABILITY] > Q_irand(0, 99) )
1325 					{
1326 						doEvent = qtrue;
1327 					}
1328 					break;
1329 				case AEV_FOOTSTEP:
1330 					// Determine probability of playing sound
1331 					//Com_Printf( "Footstep event on frame %d, even should be on frame %d, off by %d\n", frame, animEvents[i].keyFrame, frame-animEvents[i].keyFrame );
1332 					if (!animEvents[i].eventData[AED_FOOTSTEP_PROBABILITY])	// 100%
1333 					{
1334 						doEvent = qtrue;
1335 					}
1336 					else if (animEvents[i].eventData[AED_FOOTSTEP_PROBABILITY] > Q_irand(0, 99) )
1337 					{
1338 						doEvent = qtrue;
1339 					}
1340 					break;
1341 				case AEV_EFFECT:
1342 					// Determine probability of playing sound
1343 					if (!animEvents[i].eventData[AED_EFFECT_PROBABILITY])	// 100%
1344 					{
1345 						doEvent = qtrue;
1346 					}
1347 					else if (animEvents[i].eventData[AED_EFFECT_PROBABILITY] > Q_irand(0, 99) )
1348 					{
1349 						doEvent = qtrue;
1350 					}
1351 					break;
1352 				case AEV_FIRE:
1353 					// Determine probability of playing sound
1354 					if (!animEvents[i].eventData[AED_FIRE_PROBABILITY])	// 100%
1355 					{
1356 						doEvent = qtrue;
1357 					}
1358 					else if (animEvents[i].eventData[AED_FIRE_PROBABILITY] > Q_irand(0, 99) )
1359 					{
1360 						doEvent = qtrue;
1361 					}
1362 					break;
1363 				case AEV_MOVE:
1364 					doEvent = qtrue;
1365 					break;
1366 				default:
1367 					//doEvent = qfalse;//implicit
1368 					break;
1369 				}
1370 				// do event
1371 				if ( doEvent )
1372 				{
1373 					CG_PlayerAnimEventDo( &cg_entities[entNum], &animEvents[i] );
1374 				}
1375 			}// end if event matches
1376 		}// end if model matches
1377 	}// end for
1378 }
1379 
CGG2_AnimEvents(centity_t * cent)1380 static void CGG2_AnimEvents( centity_t *cent )
1381 {
1382 	if ( !cent || !cent->gent || !cent->gent->client)
1383 	{
1384 		return;
1385 	}
1386 	if ( !cent->gent->ghoul2.size() )
1387 	{//sorry, ghoul2 models only
1388 		return;
1389 	}
1390 	assert(cent->gent->playerModel>=0&&cent->gent->playerModel<cent->gent->ghoul2.size());
1391 	if ( ValidAnimFileIndex( cent->gent->client->clientInfo.animFileIndex ) )
1392 	{
1393 		int		junk, curFrame=0;
1394 		float	currentFrame=0, animSpeed;
1395 
1396 		if (cent->gent->rootBone>=0&&gi.G2API_GetBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->rootBone, cg.time, &currentFrame, &junk, &junk, &junk, &animSpeed, cgs.model_draw ))
1397 		{
1398 			// the above may have failed, not sure what to do about it, current frame will be zero in that case
1399 			curFrame = floor( currentFrame );
1400 		}
1401 		if ( curFrame != cent->gent->client->renderInfo.legsFrame )
1402 		{
1403 			CG_PlayerAnimEvents( cent->gent->client->clientInfo.animFileIndex, qfalse, cent->gent->client->renderInfo.legsFrame, curFrame, cent->currentState.clientNum );
1404 		}
1405 		cent->gent->client->renderInfo.legsFrame = curFrame;
1406 		cent->pe.legs.frame = curFrame;
1407 
1408 		if (cent->gent->lowerLumbarBone>=0&& gi.G2API_GetBoneAnimIndex(&cent->gent->ghoul2[cent->gent->playerModel], cent->gent->lowerLumbarBone, cg.time, &currentFrame, &junk, &junk, &junk, &animSpeed, cgs.model_draw ) )
1409 		{
1410 			curFrame = floor( currentFrame );
1411 		}
1412 		if ( curFrame != cent->gent->client->renderInfo.torsoFrame )
1413 		{
1414 			CG_PlayerAnimEvents( cent->gent->client->clientInfo.animFileIndex, qtrue, cent->gent->client->renderInfo.torsoFrame, curFrame, cent->currentState.clientNum );
1415 		}
1416 		cent->gent->client->renderInfo.torsoFrame = curFrame;
1417 		cent->pe.torso.frame = curFrame;
1418 	}
1419 }
1420 /*
1421 =============================================================================
1422 
1423 PLAYER ANGLES
1424 
1425 =============================================================================
1426 */
1427 
1428 /*
1429 ==================
1430 CG_UpdateAngleClamp
1431 Turn curAngle toward destAngle at angleSpeed, but stay within clampMin and Max
1432 ==================
1433 */
CG_UpdateAngleClamp(float destAngle,float clampMin,float clampMax,float angleSpeed,float * curAngle,float normalAngle)1434 static void CG_UpdateAngleClamp( float destAngle, float clampMin, float clampMax, float angleSpeed, float *curAngle, float normalAngle)
1435 {
1436 	float	swing;
1437 	float	move;
1438 	float	scale;
1439 	float	actualSpeed;
1440 
1441 	swing = AngleSubtract( destAngle, *curAngle );
1442 
1443 	if(swing == 0)
1444 	{//Don't have to turn
1445 		return;
1446 	}
1447 
1448 	// modify the angleSpeed depending on the delta
1449 	// so it doesn't seem so linear
1450 	scale = fabs( swing );
1451 	if (swing > 0)
1452 	{
1453 		if ( swing < clampMax * 0.25 )
1454 		{//Pretty small way to go
1455 			scale = 0.25;
1456 		}
1457 		else if ( swing > clampMax * 2.0 )
1458 		{//Way out of our range
1459 			scale = 2.0;
1460 		}
1461 		else
1462 		{//Scale it smoothly
1463 			scale = swing/clampMax;
1464 		}
1465 	}
1466 	else// if (swing < 0)
1467 	{
1468 		if ( swing > clampMin * 0.25 )
1469 		{//Pretty small way to go
1470 			scale = 0.5;
1471 		}
1472 		else if ( swing < clampMin * 2.0 )
1473 		{//Way out of our range
1474 			scale = 2.0;
1475 		}
1476 		else
1477 		{//Scale it smoothly
1478 			scale = swing/clampMin;
1479 		}
1480 	}
1481 
1482 	actualSpeed = scale * angleSpeed;
1483 	// swing towards the destination angle
1484 	if ( swing >= 0 )
1485 	{
1486 		move = cg.frametime * actualSpeed;
1487 		if ( move >= swing )
1488 		{//our turnspeed is so fast, no need to swing, just match
1489 			*curAngle = destAngle;
1490 		}
1491 		else
1492 		{
1493 			*curAngle = AngleNormalize360( *curAngle + move );
1494 		}
1495 	}
1496 	else if ( swing < 0 )
1497 	{
1498 		move = cg.frametime * -actualSpeed;
1499 		if ( move <= swing )
1500 		{//our turnspeed is so fast, no need to swing, just match
1501 			*curAngle = destAngle;
1502 		}
1503 		else
1504 		{
1505 			*curAngle = AngleNormalize180( *curAngle + move );
1506 		}
1507 	}
1508 
1509 	swing = AngleSubtract( *curAngle, normalAngle );
1510 
1511 	// clamp to no more than normalAngle + tolerance
1512 	if ( swing > clampMax )
1513 	{
1514 		*curAngle = AngleNormalize180( normalAngle + clampMax );
1515 	}
1516 	else if ( swing < clampMin )
1517 	{
1518 		*curAngle = AngleNormalize180( normalAngle + clampMin );
1519 	}
1520 }
1521 /*
1522 ==================
1523 CG_SwingAngles
1524 
1525   If the body is not locked OR if the upper part is trying to swing beyond it's
1526 	range, turn the lower body part to catch up.
1527 
1528   Parms:	desired angle,		(Our eventual goal angle
1529 			min swing tolerance,(Lower angle value threshold at which to start turning)
1530 			max swing tolerance,(Upper angle value threshold at which to start turning)
1531 			min clamp tolerance,(Lower angle value threshold to clamp output angle to)
1532 			max clamp tolerance,(Upper angle value threshold to clamp output angle to)
1533 			angle speed,		(How fast to turn)
1534 			current angle,		(Current angle to modify)
1535 			locked mode			(Don't turn unless you exceed the swing/clamp tolerance)
1536 ==================
1537 */
CG_SwingAngles(float destAngle,float swingTolMin,float swingTolMax,float clampMin,float clampMax,float angleSpeed,float * curAngle,qboolean * turning)1538 static void CG_SwingAngles( float destAngle,
1539 					float swingTolMin, float swingTolMax,
1540 					float clampMin, float clampMax,
1541 					float angleSpeed, float *curAngle,
1542 					qboolean *turning )
1543 {
1544 	float	swing;
1545 	float	move;
1546 	float	scale;
1547 
1548 	swing = AngleSubtract( destAngle, *curAngle );
1549 
1550 	if(swing == 0)
1551 	{//Don't have to turn
1552 		*turning = qfalse;
1553 	}
1554 	else
1555 	{
1556 		*turning = qtrue;
1557 	}
1558 
1559 	//If we're not turning, then we're done
1560 	if ( *turning == qfalse)
1561 		return;
1562 
1563 	// modify the angleSpeed depending on the delta
1564 	// so it doesn't seem so linear
1565 	scale = fabs( swing );
1566 
1567 	if (swing > 0)
1568 	{
1569 		if ( clampMax <= 0 )
1570 		{
1571 			*curAngle = destAngle;
1572 			return;
1573 		}
1574 
1575 		if ( swing < swingTolMax * 0.5 )
1576 		{//Pretty small way to go
1577 			scale = 0.5;
1578 		}
1579 		else if ( scale < swingTolMax )
1580 		{//More than halfway to go
1581 			scale = 1.0;
1582 		}
1583 		else
1584 		{//Way out of our range
1585 			scale = 2.0;
1586 		}
1587 	}
1588 	else// if (swing < 0)
1589 	{
1590 		if ( clampMin >= 0 )
1591 		{
1592 			*curAngle = destAngle;
1593 			return;
1594 		}
1595 
1596 		if ( swing > swingTolMin * 0.5 )
1597 		{//Pretty small way to go
1598 			scale = 0.5;
1599 		}
1600 		else if ( scale > swingTolMin )
1601 		{//More than halfway to go
1602 			scale = 1.0;
1603 		}
1604 		else
1605 		{//Way out of our range
1606 			scale = 2.0;
1607 		}
1608 	}
1609 
1610 	// swing towards the destination angle
1611 	if ( swing >= 0 )
1612 	{
1613 		move = cg.frametime * scale * angleSpeed;
1614 		if ( move >= swing )
1615 		{//our turnspeed is so fast, no need to swing, just match
1616 			move = swing;
1617 		}
1618 		*curAngle = AngleNormalize360( *curAngle + move );
1619 	}
1620 	else if ( swing < 0 )
1621 	{
1622 		move = cg.frametime * scale * -angleSpeed;
1623 		if ( move <= swing )
1624 		{//our turnspeed is so fast, no need to swing, just match
1625 			move = swing;
1626 		}
1627 		*curAngle = AngleNormalize360( *curAngle + move );
1628 	}
1629 
1630 
1631 	// clamp to no more than tolerance
1632 	if ( swing > clampMax )
1633 	{
1634 		*curAngle = AngleNormalize360( destAngle - (clampMax - 1) );
1635 	}
1636 	else if ( swing < clampMin )
1637 	{
1638 		*curAngle = AngleNormalize360( destAngle + (-clampMin - 1) );
1639 	}
1640 }
1641 
1642 /*
1643 ===============
1644 CG_BreathPuffs
1645 ===============
1646 Description: Makes the player appear to have breath puffs (from the cold).
1647 Added 11/06/02 by Aurelio Reis.
1648 */
1649 extern vmCvar_t	cg_drawBreath;
CG_BreathPuffs(centity_t * cent,vec3_t angles,vec3_t origin)1650 static void CG_BreathPuffs( centity_t *cent, vec3_t angles, vec3_t origin )
1651 {
1652 	gclient_t *client = cent->gent->client;
1653 
1654 	/*	cg_drawBreath.integer	== 0 - Don't draw at all.
1655 								== 1 - Draw both (but bubbles only when under water).
1656 								== 2 - Draw only cold breath.
1657 								== 3 - Draw only under water bubbles (when under water)	*/
1658 
1659 	if ( !client
1660 		|| cg_drawBreath.integer == 0
1661 		|| !cg.renderingThirdPerson
1662 		|| client->ps.pm_type == PM_DEAD
1663 		|| client->breathPuffTime > cg.time )
1664 	{
1665 		return;
1666 	}
1667 
1668 	// Get the head-front bolt/tag.
1669 	int bolt = gi.G2API_AddBolt( &cent->gent->ghoul2[cent->gent->playerModel], "*head_front" );
1670 	if ( bolt == -1 )
1671 	{
1672 		return;
1673 	}
1674 
1675 	vec3_t vEffectOrigin;
1676 	mdxaBone_t	boltMatrix;
1677 	gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, bolt, &boltMatrix, angles, origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
1678 	gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, vEffectOrigin );
1679 
1680 	int contents = cgi_CM_PointContents( vEffectOrigin, 0 );
1681 	if ( contents & ( CONTENTS_SLIME | CONTENTS_LAVA ) )	// If they're submerged in something bad, leave.
1682 	{
1683 		return;
1684 	}
1685 
1686 	// Show bubbles effect if we're under water.
1687 	if ( (contents & CONTENTS_WATER) && ( cg_drawBreath.integer == 1 || cg_drawBreath.integer == 3 ) )
1688 	{
1689 		CG_PlayEffectBolted( "misc/waterbreath", cent->gent->playerModel, bolt, cent->currentState.clientNum, vEffectOrigin );
1690 	}
1691 	// Draw cold breath effect.
1692 	else if ( cg_drawBreath.integer == 1 || cg_drawBreath.integer == 2 )
1693 	{
1694 		CG_PlayEffectBolted( "misc/breath", cent->gent->playerModel, bolt, cent->currentState.clientNum, vEffectOrigin );
1695 	}
1696 
1697 	// TODO: It'd be nice if they breath faster when they're more damaged or when running...
1698 	if ( gi.VoiceVolume[cent->currentState.number] > 0 )
1699 	{//make breath when talking
1700 		client->breathPuffTime = cg.time + 300; // every 200 ms
1701 	}
1702 	else
1703 	{
1704 		client->breathPuffTime = cg.time + 3000; // every 3 seconds.
1705 	}
1706 }
1707 
1708 #define LOOK_DEFAULT_SPEED	0.15f
1709 #define LOOK_TALKING_SPEED	0.15f
1710 
CG_CheckLookTarget(centity_t * cent,vec3_t lookAngles,float * lookingSpeed)1711 static qboolean CG_CheckLookTarget( centity_t *cent, vec3_t	lookAngles, float *lookingSpeed )
1712 {
1713 	if ( !cent->gent->ghoul2.size() )
1714 	{
1715 		if ( !cent->gent->client->clientInfo.torsoModel || !cent->gent->client->clientInfo.headModel )
1716 		{
1717 			return qfalse;
1718 		}
1719 	}
1720 
1721 	//FIXME: also clamp the lookAngles based on the clamp + the existing difference between
1722 	//		headAngles and torsoAngles?  But often the tag_torso is straight but the torso itself
1723 	//		is deformed to not face straight... sigh...
1724 
1725 	//Now calc head angle to lookTarget, if any
1726 	if ( cent->gent->client->renderInfo.lookTarget >= 0 && cent->gent->client->renderInfo.lookTarget < ENTITYNUM_WORLD )
1727 	{
1728 		vec3_t	lookDir, lookOrg = { 0.0f }, eyeOrg;
1729 		if ( cent->gent->client->renderInfo.lookMode == LM_ENT )
1730 		{
1731 			centity_t	*lookCent = &cg_entities[cent->gent->client->renderInfo.lookTarget];
1732 			if ( lookCent && lookCent->gent )
1733 			{
1734 				if ( lookCent->gent != cent->gent->enemy )
1735 				{//We turn heads faster than headbob speed, but not as fast as if watching an enemy
1736 					if ( cent->gent->client->NPC_class == CLASS_ROCKETTROOPER )
1737 					{//they look around slowly and deliberately
1738 						*lookingSpeed = LOOK_DEFAULT_SPEED*0.25f;
1739 					}
1740 					else
1741 					{
1742 						*lookingSpeed = LOOK_DEFAULT_SPEED;
1743 					}
1744 				}
1745 
1746 				//FIXME: Ignore small deltas from current angles so we don't bob our head in synch with theirs?
1747 
1748 				if ( cent->gent->client->renderInfo.lookTarget == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer )
1749 				{//Special case- use cg.refdef.vieworg if looking at player and not in third person view
1750 					VectorCopy( cg.refdef.vieworg, lookOrg );
1751 				}
1752 				else if ( lookCent->gent->client )
1753 				{
1754 					VectorCopy( lookCent->gent->client->renderInfo.eyePoint, lookOrg );
1755 				}
1756 				else if ( lookCent->gent->s.pos.trType == TR_INTERPOLATE )
1757 				{
1758 					VectorCopy( lookCent->lerpOrigin, lookOrg );
1759 				}
1760 				else if ( lookCent->gent->inuse && !VectorCompare( lookCent->gent->currentOrigin, vec3_origin ) )
1761 				{
1762 					VectorCopy( lookCent->gent->currentOrigin, lookOrg );
1763 				}
1764 				else
1765 				{//at origin of world
1766 					return qfalse;
1767 				}
1768 				//Look in dir of lookTarget
1769 			}
1770 		}
1771 		else if ( cent->gent->client->renderInfo.lookMode == LM_INTEREST && cent->gent->client->renderInfo.lookTarget > -1 && cent->gent->client->renderInfo.lookTarget < MAX_INTEREST_POINTS )
1772 		{
1773 			VectorCopy( level.interestPoints[cent->gent->client->renderInfo.lookTarget].origin, lookOrg );
1774 		}
1775 		else
1776 		{
1777 			return qfalse;
1778 		}
1779 
1780 		VectorCopy( cent->gent->client->renderInfo.eyePoint, eyeOrg );
1781 
1782 		VectorSubtract( lookOrg, eyeOrg, lookDir );
1783 #if 1
1784 		vectoangles( lookDir, lookAngles );
1785 #else
1786 		//FIXME: get the angle of the head tag and account for that when finding the lookAngles-
1787 		//		so if they're lying on their back we get an accurate lookAngle...
1788 		vec3_t	headDirs[3];
1789 		vec3_t	finalDir;
1790 
1791 		AnglesToAxis( cent->gent->client->renderInfo.headAngles, headDirs );
1792 		VectorRotate( lookDir, headDirs, finalDir );
1793 		vectoangles( finalDir, lookAngles );
1794 #endif
1795 		for ( int i = 0; i < 3; i++ )
1796 		{
1797 			lookAngles[i] = AngleNormalize180( lookAngles[i] );
1798 			cent->gent->client->renderInfo.eyeAngles[i] = AngleNormalize180( cent->gent->client->renderInfo.eyeAngles[i] );
1799 		}
1800 		AnglesSubtract( lookAngles, cent->gent->client->renderInfo.eyeAngles, lookAngles );
1801 		return qtrue;
1802 	}
1803 
1804 	return qfalse;
1805 }
1806 
1807 /*
1808 =================
1809 CG_AddHeadBob
1810 =================
1811 */
CG_AddHeadBob(centity_t * cent,vec3_t addTo)1812 static qboolean CG_AddHeadBob( centity_t *cent, vec3_t addTo )
1813 {
1814 	renderInfo_t	*renderInfo	= &cent->gent->client->renderInfo;
1815 	const int		volume		= gi.VoiceVolume[cent->gent->s.clientNum];
1816 	const int		volChange	= volume - renderInfo->lastVoiceVolume;//was *3 because voice fromLA was too low
1817 	int				i;
1818 
1819 	renderInfo->lastVoiceVolume = volume;
1820 
1821 	if ( !volume )
1822 	{
1823 		// Not talking, set our target to be the normal head position
1824 		VectorClear( renderInfo->targetHeadBobAngles );
1825 
1826 		if ( VectorLengthSquared( renderInfo->headBobAngles ) < 1.0f )
1827 		{
1828 			// We are close enough to being back to our normal head position, so we are done for now
1829 			return qfalse;
1830 		}
1831 	}
1832 	else if ( volChange > 2 )
1833 	{
1834 		// a big positive change in volume
1835 		for ( i = 0; i < 3; i++ )
1836 		{
1837 			// Move our head angle target a bit
1838 			renderInfo->targetHeadBobAngles[i] += Q_flrand( -1.0 * volChange, 1.0 * volChange );
1839 
1840 			// Clamp so we don't get too out of hand
1841 			if ( renderInfo->targetHeadBobAngles[i] > 7.0f )
1842 				renderInfo->targetHeadBobAngles[i] = 7.0f;
1843 
1844 			if ( renderInfo->targetHeadBobAngles[i] < -7.0f )
1845 				renderInfo->targetHeadBobAngles[i] = -7.0f;
1846 		}
1847 	}
1848 
1849 	for ( i = 0; i < 3; i++ )
1850 	{
1851 		// Always try to move head angles towards our target
1852 		renderInfo->headBobAngles[i] += ( renderInfo->targetHeadBobAngles[i] - renderInfo->headBobAngles[i] ) * ( cg.frametime / 150.0f );
1853 		if ( addTo )
1854 		{
1855 			addTo[i] = AngleNormalize180( addTo[i] + AngleNormalize180( renderInfo->headBobAngles[i] ) );
1856 		}
1857 	}
1858 
1859 	// We aren't back to our normal position yet, so we still have to apply headBobAngles
1860 	return qtrue;
1861 }
1862 
1863 extern float vectoyaw( const vec3_t vec );
CG_PlayerLegsYawFromMovement(centity_t * cent,const vec3_t velocity,float * yaw,float fwdAngle,float swingTolMin,float swingTolMax,qboolean alwaysFace)1864 static qboolean CG_PlayerLegsYawFromMovement( centity_t *cent, const vec3_t velocity, float *yaw, float fwdAngle, float swingTolMin, float swingTolMax, qboolean alwaysFace )
1865 {
1866 	float newAddAngle, angleDiff, turnRate = 10, addAngle = 0;
1867 
1868 	//figure out what the offset, if any, should be
1869 	if ( velocity[0] || velocity[1] )
1870 	{
1871 		float	moveYaw;
1872 		moveYaw = vectoyaw( velocity );
1873 		addAngle = AngleDelta( cent->lerpAngles[YAW], moveYaw )*-1;
1874 		if ( addAngle > 150 || addAngle < -150 )
1875 		{
1876 			addAngle = 0;
1877 		}
1878 		else
1879 		{
1880 			//FIXME: use actual swing/clamp tolerances
1881 			if ( addAngle > swingTolMax )
1882 			{
1883 				addAngle = swingTolMax;
1884 			}
1885 			else if ( addAngle < swingTolMin )
1886 			{
1887 				addAngle = swingTolMin;
1888 			}
1889 			if ( cent->gent->client->ps.pm_flags&PMF_BACKWARDS_RUN )
1890 			{
1891 				addAngle *= -1;
1892 			}
1893 			turnRate = 5;
1894 		}
1895 	}
1896 	else if ( !alwaysFace )
1897 	{
1898 		return qfalse;
1899 	}
1900 	if ( cent->gent && cent->gent->client && cent->gent->client->ps.forcePowersActive & (1 << FP_SPEED) )
1901 	{//using force speed
1902 		//scale up the turning speed
1903 		turnRate /= cg_timescale.value;
1904 	}
1905 	//lerp the legs angle to the new angle
1906 	angleDiff = AngleDelta( cent->pe.legs.yawAngle, (*yaw+addAngle) );
1907 	newAddAngle = angleDiff*cg.frameInterpolation*-1;
1908 	if ( fabs(newAddAngle) > fabs(angleDiff) )
1909 	{
1910 		newAddAngle = angleDiff*-1;
1911 	}
1912 	if ( newAddAngle > turnRate )
1913 	{
1914 		newAddAngle = turnRate;
1915 	}
1916 	else if ( newAddAngle < -turnRate )
1917 	{
1918 		newAddAngle = -turnRate;
1919 	}
1920 	*yaw = cent->pe.legs.yawAngle + newAddAngle;
1921 	//Now clamp
1922 	angleDiff = AngleDelta( fwdAngle, *yaw );
1923 	if ( angleDiff > swingTolMax )
1924 	{
1925 		*yaw = fwdAngle - swingTolMax;
1926 	}
1927 	else if ( angleDiff < swingTolMin )
1928 	{
1929 		*yaw = fwdAngle - swingTolMin;
1930 	}
1931 	return qtrue;
1932 }
1933 
CG_ATSTLegsYaw(centity_t * cent,vec3_t trailingLegsAngles)1934 static void CG_ATSTLegsYaw( centity_t *cent, vec3_t trailingLegsAngles )
1935 {
1936 
1937 	float ATSTLegsYaw = cent->lerpAngles[YAW];
1938 
1939 	CG_PlayerLegsYawFromMovement( cent, cent->gent->client->ps.velocity, &ATSTLegsYaw, cent->lerpAngles[YAW], -60, 60, qtrue );
1940 
1941 	float legAngleDiff = AngleNormalize180(ATSTLegsYaw) - AngleNormalize180(cent->pe.legs.yawAngle);
1942 	int legsAnim = cent->currentState.legsAnim;
1943 	qboolean moving = (qboolean)!VectorCompare(cent->gent->client->ps.velocity, vec3_origin);
1944 	if ( moving || legsAnim == BOTH_TURN_LEFT1 || legsAnim == BOTH_TURN_RIGHT1 || fabs(legAngleDiff) > 45 )
1945 	{//moving or turning or beyond the turn allowance
1946 		if ( legsAnim == BOTH_STAND1 && !moving )
1947 		{//standing
1948 			if ( legAngleDiff > 0 )
1949 			{
1950 				NPC_SetAnim( cent->gent, SETANIM_LEGS, BOTH_TURN_LEFT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1951 			}
1952 			else
1953 			{
1954 				NPC_SetAnim( cent->gent, SETANIM_LEGS, BOTH_TURN_RIGHT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1955 			}
1956 			VectorSet( trailingLegsAngles, 0, cent->pe.legs.yawAngle, 0 );
1957 			cent->gent->client->renderInfo.legsYaw = trailingLegsAngles[YAW];
1958 		}
1959 		else if ( legsAnim == BOTH_TURN_LEFT1 || legsAnim == BOTH_TURN_RIGHT1 )
1960 		{//turning
1961 			legAngleDiff = AngleSubtract( ATSTLegsYaw, cent->gent->client->renderInfo.legsYaw );
1962 			float add = 0;
1963 			if ( legAngleDiff > 50 )
1964 			{
1965 				cent->pe.legs.yawAngle += legAngleDiff - 50;
1966 			}
1967 			else if ( legAngleDiff < -50 )
1968 			{
1969 				cent->pe.legs.yawAngle += legAngleDiff + 50;
1970 			}
1971 			float animLength = PM_AnimLength( cent->gent->client->clientInfo.animFileIndex, (animNumber_t)legsAnim );
1972 			legAngleDiff *= ( animLength - cent->gent->client->ps.legsAnimTimer)/animLength;
1973 			VectorSet( trailingLegsAngles, 0, cent->pe.legs.yawAngle+legAngleDiff+add, 0 );
1974 			if ( !cent->gent->client->ps.legsAnimTimer )
1975 			{//FIXME: if start turning in the middle of this, our legs pop back to the old cent->pe.legs.yawAngle...
1976 				cent->gent->client->renderInfo.legsYaw = trailingLegsAngles[YAW];
1977 			}
1978 		}
1979 		else
1980 		{//moving
1981 			legAngleDiff = AngleSubtract( ATSTLegsYaw, cent->pe.legs.yawAngle );
1982 			//FIXME: framerate dependant!!!
1983 			if ( legAngleDiff > 50 )
1984 			{
1985 				legAngleDiff -= 50;
1986 			}
1987 			else if ( legAngleDiff > 5 )
1988 			{
1989 				legAngleDiff = 5;
1990 			}
1991 			else if ( legAngleDiff < -50 )
1992 			{
1993 				legAngleDiff += 50;
1994 			}
1995 			else if ( legAngleDiff < -5 )
1996 			{
1997 				legAngleDiff = -5;
1998 			}
1999 			legAngleDiff *= cg.frameInterpolation;
2000 			VectorSet( trailingLegsAngles, 0, AngleNormalize180(cent->pe.legs.yawAngle + legAngleDiff), 0 );
2001 			cent->gent->client->renderInfo.legsYaw = trailingLegsAngles[YAW];
2002 		}
2003 		cent->gent->client->renderInfo.legsYaw = cent->pe.legs.yawAngle = trailingLegsAngles[YAW];
2004 		cent->pe.legs.yawing = qtrue;
2005 	}
2006 	else
2007 	{
2008 		VectorSet( trailingLegsAngles, 0, cent->pe.legs.yawAngle, 0 );
2009 		cent->gent->client->renderInfo.legsYaw = cent->pe.legs.yawAngle = trailingLegsAngles[YAW];
2010 		cent->pe.legs.yawing = qfalse;
2011 	}
2012 	return;
2013 }
2014 
2015 extern qboolean G_ClassHasBadBones( int NPC_class );
2016 extern void G_BoneOrientationsForClass( int NPC_class, const char *boneName, Eorientations *oUp, Eorientations *oRt, Eorientations *oFwd );
2017 extern qboolean PM_FlippingAnim( int anim );
2018 extern qboolean PM_SpinningSaberAnim( int anim );
2019 static CGhoul2Info_v	dummyGhoul2;
2020 static int				dummyRootBone;
2021 static int				dummyHipsBolt;
CG_G2ClientSpineAngles(centity_t * cent,vec3_t viewAngles,const vec3_t angles,vec3_t thoracicAngles,vec3_t ulAngles,vec3_t llAngles)2022 static void CG_G2ClientSpineAngles( centity_t *cent, vec3_t viewAngles, const vec3_t angles, vec3_t thoracicAngles, vec3_t ulAngles, vec3_t llAngles )
2023 {
2024 	vec3_t	motionBoneCorrectAngles = {0};
2025 	cent->pe.torso.pitchAngle = viewAngles[PITCH];
2026 	viewAngles[YAW] = AngleDelta( cent->lerpAngles[YAW], angles[YAW] );
2027 	cent->pe.torso.yawAngle = viewAngles[YAW];
2028 
2029 	/*
2030 	if ( G_ClassHasBadBones( cent->gent->client->NPC_class ) )
2031 	{//don't use lower bones
2032 		VectorClear( thoracicAngles );
2033 		VectorClear( ulAngles );
2034 		VectorClear( llAngles );
2035 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, POSITIVE_Y, POSITIVE_Z, cgs.model_draw );
2036 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, POSITIVE_Y, POSITIVE_Z, cgs.model_draw );
2037 		return;
2038 	}
2039 	*/
2040 
2041 	if ( cent->gent->client->NPC_class == CLASS_SABER_DROID )
2042 	{//don't use lower bones
2043 		VectorClear( thoracicAngles );
2044 		VectorClear( ulAngles );
2045 		VectorClear( llAngles );
2046 		return;
2047 	}
2048 
2049 	if ( cg_motionBoneComp.integer
2050 		&& !PM_FlippingAnim( cent->currentState.legsAnim )
2051 		&& !PM_SpinningSaberAnim( cent->currentState.legsAnim )
2052 		&& !PM_SpinningSaberAnim( cent->currentState.torsoAnim )
2053 		&& cent->currentState.legsAnim != cent->currentState.torsoAnim //NOTE: presumes your legs & torso are on the same frame, though they *should* be because PM_SetAnimFinal tries to keep them in synch
2054 		&& !G_ClassHasBadBones( cent->gent->client->NPC_class ) )//these guys' bones are so fucked up we shouldn't even bother with this motion bone comp...
2055 	{//FIXME: no need to do this if legs and torso on are same frame
2056 		mdxaBone_t	boltMatrix;
2057 
2058 		if ( cg_motionBoneComp.integer > 2 && cent->gent->rootBone >= 0 && cent->gent->lowerLumbarBone >= 0 )
2059 		{//expensive version
2060 			//have a local ghoul2 instance to mess with for this stuff... :/
2061 			//remember the frame the lower is on
2062 			float	upperFrame, animSpeed;
2063 			int		junk;
2064 			vec3_t	llFwd, llRt, destPAngles, curPAngles, tempAng;
2065 
2066 			if ( !dummyGhoul2.size() )
2067 			{//set it up
2068 				int dummyHModel = cgi_R_RegisterModel( "models/players/_humanoid/_humanoid.glm" );
2069 				gi.G2API_InitGhoul2Model( dummyGhoul2, "models/players/_humanoid/_humanoid.glm", dummyHModel, NULL_HANDLE, NULL_HANDLE, 0, 0 );
2070 				dummyRootBone = gi.G2API_GetBoneIndex( &dummyGhoul2[0], "model_root", qtrue );
2071 				dummyHipsBolt = gi.G2API_AddBolt( &dummyGhoul2[0], "pelvis" );
2072 			}
2073 
2074 			gi.G2API_GetBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->lowerLumbarBone, cg.time, &upperFrame, &junk, &junk, &junk, &animSpeed, cgs.model_draw );
2075 			//set the dummyGhoul2 lower body to same frame as upper
2076 			gi.G2API_SetBoneAnimIndex(&dummyGhoul2[0], dummyRootBone, upperFrame, upperFrame, BONE_ANIM_OVERRIDE_FREEZE, 1, cg.time, upperFrame, 0 );
2077 			//get the dummyGhoul2 lower_lumbar orientation
2078 			gi.G2API_GetBoltMatrix( dummyGhoul2, 0, dummyHipsBolt, &boltMatrix, vec3_origin, vec3_origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
2079 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Z, llFwd );
2080 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, llRt );
2081 			vectoangles( llFwd, destPAngles );
2082 			vectoangles( llRt, tempAng );
2083 			destPAngles[ROLL] = -tempAng[PITCH];
2084 			//get my lower_lumbar
2085 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->crotchBolt, &boltMatrix, vec3_origin, vec3_origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
2086 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Z, llFwd );
2087 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, llRt );
2088 			vectoangles( llFwd, curPAngles );
2089 			vectoangles( llRt, tempAng );
2090 			curPAngles[ROLL] = -tempAng[PITCH];
2091 
2092 			//get the difference
2093 			for ( int ang = 0; ang < 3; ang++ )
2094 			{
2095 				motionBoneCorrectAngles[ang] = AngleNormalize180( AngleDelta( AngleNormalize180( destPAngles[ang] ), AngleNormalize180( curPAngles[ang]) ) );
2096 			}
2097 #ifdef _DEBUG
2098 			Com_Printf( "motion bone correction:  %4.2f %4.2f %4.2f\n", motionBoneCorrectAngles[PITCH], motionBoneCorrectAngles[YAW], motionBoneCorrectAngles[ROLL] );
2099 #endif// _DEBUG
2100 			/*
2101 			for ( int ang = 0; ang < 3; ang++ )
2102 			{
2103 				viewAngles[ang] = AngleNormalize180( viewAngles[ang] - AngleNormalize180( destLLAngles[ang] ) );
2104 			}
2105 			*/
2106 		}
2107 		else
2108 		{
2109 			//adjust for motion offset
2110 			vec3_t		motionFwd, motionAngles;
2111 
2112 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->motionBolt, &boltMatrix, vec3_origin, cent->lerpOrigin, cg.time, cgs.model_draw, cent->currentState.modelScale );
2113 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, motionFwd );
2114 			vectoangles( motionFwd, motionAngles );
2115 			if ( cg_motionBoneComp.integer > 1 )
2116 			{//do roll, too
2117 				vec3_t motionRt, tempAng;
2118 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_X, motionRt );
2119 				vectoangles( motionRt, tempAng );
2120 				motionAngles[ROLL] = -tempAng[PITCH];
2121 			}
2122 
2123 			for ( int ang = 0; ang < 3; ang++ )
2124 			{
2125 				viewAngles[ang] = AngleNormalize180( viewAngles[ang] - AngleNormalize180( motionAngles[ang] ) );
2126 			}
2127 		}
2128 	}
2129 	//distribute the angles differently up the spine
2130 	//NOTE: each of these distributions must add up to 1.0f
2131 	if ( cent->gent->client->NPC_class == CLASS_HAZARD_TROOPER )
2132 	{//only uses lower_lumbar and upper_lumbar to look around
2133 		VectorClear( thoracicAngles );
2134 		ulAngles[PITCH] = viewAngles[PITCH]*0.50f;
2135 		llAngles[PITCH] = viewAngles[PITCH]*0.50f+motionBoneCorrectAngles[PITCH];
2136 
2137 		ulAngles[YAW] = viewAngles[YAW]*0.45f;
2138 		llAngles[YAW] = viewAngles[YAW]*0.55f+motionBoneCorrectAngles[YAW];
2139 
2140 		ulAngles[ROLL] = viewAngles[ROLL]*0.45f;
2141 		llAngles[ROLL] = viewAngles[ROLL]*0.55f+motionBoneCorrectAngles[ROLL];
2142 	}
2143 	else if ( cent->gent->client->NPC_class == CLASS_ASSASSIN_DROID )
2144 	{//each bone has only 1 axis of rotation!
2145 		//upper lumbar does not pitch
2146 		thoracicAngles[PITCH] = viewAngles[PITCH]*0.40f;
2147 		ulAngles[PITCH] = 0.0f;
2148 		llAngles[PITCH] = viewAngles[PITCH]*0.60f+motionBoneCorrectAngles[PITCH];
2149 		//only upper lumbar yaws
2150 		thoracicAngles[YAW] = 0.0f;
2151 		ulAngles[YAW] = viewAngles[YAW];
2152 		llAngles[YAW] = motionBoneCorrectAngles[YAW];
2153 		//no bone is capable of rolling
2154 		thoracicAngles[ROLL] = 0.0f;
2155 		ulAngles[ROLL] = 0.0f;
2156 		llAngles[ROLL] = motionBoneCorrectAngles[ROLL];
2157 	}
2158 	else
2159 	{//use all 3 bones
2160 		thoracicAngles[PITCH] = viewAngles[PITCH]*0.20f;
2161 		ulAngles[PITCH] = viewAngles[PITCH]*0.40f;
2162 		llAngles[PITCH] = viewAngles[PITCH]*0.40f+motionBoneCorrectAngles[PITCH];
2163 
2164 		thoracicAngles[YAW] = viewAngles[YAW]*0.20f;
2165 		ulAngles[YAW] = viewAngles[YAW]*0.35f;
2166 		llAngles[YAW] = viewAngles[YAW]*0.45f+motionBoneCorrectAngles[YAW];
2167 
2168 		thoracicAngles[ROLL] = viewAngles[ROLL]*0.20f;
2169 		ulAngles[ROLL] = viewAngles[ROLL]*0.35f;
2170 		llAngles[ROLL] = viewAngles[ROLL]*0.45f+motionBoneCorrectAngles[ROLL];
2171 	}
2172 
2173 	if ( G_IsRidingVehicle( cent->gent ) )// && type == VH_SPEEDER ?
2174 	{//aim torso forward too
2175 		ulAngles[YAW] = llAngles[YAW] = 0;
2176 
2177 		// Only if they have weapon can they pitch forward/back.
2178 		if ( cent->gent->client->ps.weapon == WP_NONE || cent->gent->client->ps.weapon == WP_SABER )
2179 		{
2180 			ulAngles[PITCH] = llAngles[PITCH] = 0;
2181 		}
2182 	}
2183 	//thoracic is added modified again by neckAngle calculations, so don't set it until then
2184 	if ( G_ClassHasBadBones( cent->gent->client->NPC_class ) )
2185 	{
2186 		Eorientations oUp, oRt, oFwd;
2187 		if ( cent->gent->client->NPC_class == CLASS_RANCOR )
2188 		{
2189 			llAngles[YAW] = llAngles[ROLL] = 0.0f;
2190 			ulAngles[YAW] = ulAngles[ROLL] = 0.0f;
2191 		}
2192 		G_BoneOrientationsForClass( cent->gent->client->NPC_class, "upper_lumbar", &oUp, &oRt, &oFwd );
2193 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, ulAngles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, cgs.model_draw);
2194 		G_BoneOrientationsForClass( cent->gent->client->NPC_class, "lower_lumbar", &oUp, &oRt, &oFwd );
2195 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, llAngles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, cgs.model_draw);
2196 	}
2197 	else
2198 	{
2199 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2200 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2201 	}
2202 }
2203 
CG_G2ClientNeckAngles(centity_t * cent,const vec3_t lookAngles,vec3_t headAngles,vec3_t neckAngles,vec3_t thoracicAngles,vec3_t headClampMinAngles,vec3_t headClampMaxAngles)2204 static void CG_G2ClientNeckAngles( centity_t *cent, const vec3_t lookAngles, vec3_t headAngles, vec3_t neckAngles, vec3_t thoracicAngles, vec3_t headClampMinAngles, vec3_t headClampMaxAngles )
2205 {
2206 	/*
2207 	if ( G_ClassHasBadBones( cent->gent->client->NPC_class ) )
2208 	{//don't use lower bones
2209 		VectorClear( thoracicAngles );
2210 		VectorClear( headAngles );
2211 		VectorClear( neckAngles );
2212 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, POSITIVE_Y, POSITIVE_Z, cgs.model_draw );
2213 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, POSITIVE_Y, POSITIVE_Z, cgs.model_draw );
2214 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, POSITIVE_Y, POSITIVE_Z, cgs.model_draw );
2215 		return;
2216 	}
2217 	*/
2218 	if ( cent->gent->client->NPC_class == CLASS_HAZARD_TROOPER )
2219 	{//don't use upper bones
2220 		return;
2221 	}
2222 	vec3_t	lA;
2223 	VectorCopy( lookAngles, lA );
2224 	//clamp the headangles (which should now be relative to the cervical (neck) angles
2225 	if ( lA[PITCH] < headClampMinAngles[PITCH] )
2226 	{
2227 		lA[PITCH] = headClampMinAngles[PITCH];
2228 	}
2229 	else if ( lA[PITCH] > headClampMaxAngles[PITCH] )
2230 	{
2231 		lA[PITCH] = headClampMaxAngles[PITCH];
2232 	}
2233 
2234 	if ( lA[YAW] < headClampMinAngles[YAW] )
2235 	{
2236 		lA[YAW] = headClampMinAngles[YAW];
2237 	}
2238 	else if ( lA[YAW] > headClampMaxAngles[YAW] )
2239 	{
2240 		lA[YAW] = headClampMaxAngles[YAW];
2241 	}
2242 
2243 	if ( lA[ROLL] < headClampMinAngles[ROLL] )
2244 	{
2245 		lA[ROLL] = headClampMinAngles[ROLL];
2246 	}
2247 	else if ( lA[ROLL] > headClampMaxAngles[ROLL] )
2248 	{
2249 		lA[ROLL] = headClampMaxAngles[ROLL];
2250 	}
2251 
2252 	//split it up between the neck and cranium
2253 	if ( cent->gent->client->NPC_class == CLASS_ASSASSIN_DROID )
2254 	{//each bone has only 1 axis of rotation!
2255 		//thoracic only pitches, split with cervical
2256 		if ( thoracicAngles[PITCH] )
2257 		{//already been set above, blend them
2258 			thoracicAngles[PITCH] = (thoracicAngles[PITCH] + (lA[PITCH] * 0.5f)) * 0.5f;
2259 		}
2260 		else
2261 		{
2262 			thoracicAngles[PITCH] = lA[PITCH] * 0.5f;
2263 		}
2264 		thoracicAngles[YAW] = thoracicAngles[ROLL] = 0.0f;
2265 		//cervical only pitches, split with thoracis
2266 		neckAngles[PITCH] = lA[PITCH] * 0.5f;
2267 		neckAngles[YAW] = 0.0f;
2268 		neckAngles[ROLL] = 0.0f;
2269 		//cranium only yaws
2270 		headAngles[PITCH] = 0.0f;
2271 		headAngles[YAW] = lA[YAW];
2272 		headAngles[ROLL] = 0.0f;
2273 		//no bones roll
2274 	}
2275 	else if ( cent->gent->client->NPC_class == CLASS_SABER_DROID )
2276 	{//each bone has only 1 axis of rotation!
2277 		//no thoracic
2278 		VectorClear( thoracicAngles );
2279 		//cervical only yaws
2280 		neckAngles[PITCH] = 0.0f;
2281 		neckAngles[YAW] = lA[YAW];
2282 		neckAngles[ROLL] = 0.0f;
2283 		//cranium only pitches
2284 		headAngles[PITCH] = lA[PITCH];
2285 		headAngles[YAW] = 0.0f;
2286 		headAngles[ROLL] = 0.0f;
2287 		//none of the bones roll
2288 	}
2289 	else
2290 	{
2291 		if ( thoracicAngles[PITCH] )
2292 		{//already been set above, blend them
2293 			thoracicAngles[PITCH] = (thoracicAngles[PITCH] + (lA[PITCH] * 0.4f)) * 0.5f;
2294 		}
2295 		else
2296 		{
2297 			thoracicAngles[PITCH] = lA[PITCH] * 0.4f;
2298 		}
2299 		if ( thoracicAngles[YAW] )
2300 		{//already been set above, blend them
2301 			thoracicAngles[YAW] = (thoracicAngles[YAW] + (lA[YAW] * 0.1f)) * 0.5f;
2302 		}
2303 		else
2304 		{
2305 			thoracicAngles[YAW] = lA[YAW] * 0.1f;
2306 		}
2307 		if ( thoracicAngles[ROLL] )
2308 		{//already been set above, blend them
2309 			thoracicAngles[ROLL] = (thoracicAngles[ROLL] + (lA[ROLL] * 0.1f)) * 0.5f;
2310 		}
2311 		else
2312 		{
2313 			thoracicAngles[ROLL] = lA[ROLL] * 0.1f;
2314 		}
2315 
2316 		neckAngles[PITCH] = lA[PITCH] * 0.2f;
2317 		neckAngles[YAW] = lA[YAW] * 0.3f;
2318 		neckAngles[ROLL] = lA[ROLL] * 0.3f;
2319 
2320 		headAngles[PITCH] = lA[PITCH] * 0.4f;
2321 		headAngles[YAW] = lA[YAW] * 0.6f;
2322 		headAngles[ROLL] = lA[ROLL] * 0.6f;
2323 	}
2324 
2325 	if ( G_IsRidingVehicle( cent->gent ) )// && type == VH_SPEEDER ?
2326 	{//aim torso forward too
2327 		headAngles[YAW] = neckAngles[YAW] = thoracicAngles[YAW] = 0;
2328 
2329 		// Only if they have weapon can they pitch forward/back.
2330 		if ( cent->gent->client->ps.weapon == WP_NONE || cent->gent->client->ps.weapon == WP_SABER )
2331 		{
2332 			thoracicAngles[PITCH] = 0;
2333 		}
2334 
2335 		/* ABORTED ATTEMPT AT AIMING GUN WITH SHOULDER WHEN ON BIKE...  POSSIBLY RETURN TO THIS LATER
2336 		if ( cent->gent &&
2337 			 cent->gent->client &&
2338 			 cent->gent->enemy &&
2339 			 cent->gent->humerusRBone!=-1 &&
2340 			(cent->gent->client->ps.torsoAnim==BOTH_VS_ATR_G || cent->gent->client->ps.torsoAnim==BOTH_VS_ATF_G))
2341 		{
2342 			vec3_t		toEnemy;
2343 			vec3_t		toEnemyAngles;
2344 			float		toEnemyDistance;
2345 			gentity_t*	actor = cent->gent;
2346 			vec3_t		actorPos;
2347 			vec3_t		actorAim;
2348 			vec3_t		actorAngles;
2349 			float		actorAimDot;
2350 
2351 			mdxaBone_t	boltMatrix;
2352 			vec3_t		boltAngles;
2353 
2354 			gi.G2API_GetBoltMatrix( actor->ghoul2, actor->playerModel, actor->humerusRBone, &boltMatrix, vec3_origin, cent->lerpOrigin, cg.time, cgs.model_draw, cent->currentState.modelScale );
2355 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, actorPos );
2356 
2357 			VectorSubtract(actorPos, actor->enemy->currentOrigin, toEnemy);
2358 			toEnemyDistance = VectorNormalize(toEnemy);
2359 
2360 			AngleVectors(actor->currentAngles, actorAim, 0, 0);
2361 			actorAimDot = DotProduct(toEnemy, actorAim);
2362 
2363 			if (actorAimDot>0.9f || (actorAimDot<0.1f && actorAimDot>-0.1f))
2364 			{
2365 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, actorAim );
2366 				vectoangles( actorAim, actorAngles );
2367 				vectoangles( toEnemy, toEnemyAngles );
2368 
2369 				boltAngles[0] = AngleDelta(actorAngles[0], toEnemyAngles[0]);
2370 				boltAngles[1] = AngleDelta(actorAngles[1], toEnemyAngles[1]);
2371 				boltAngles[2] = AngleDelta(actorAngles[2], toEnemyAngles[2]);
2372 
2373 				BG_G2SetBoneAngles( cent, actor, actor->humerusRBone, boltAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2374 			}
2375 		}*/
2376 	}
2377 	if ( G_ClassHasBadBones( cent->gent->client->NPC_class ) )
2378 	{
2379 		Eorientations oUp, oRt, oFwd;
2380 		if ( cent->gent->client->NPC_class != CLASS_RANCOR )
2381 		{//Rancor doesn't use cranium and cervical
2382 			G_BoneOrientationsForClass( cent->gent->client->NPC_class, "cranium", &oUp, &oRt, &oFwd );
2383 			BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, headAngles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, cgs.model_draw );
2384 			G_BoneOrientationsForClass( cent->gent->client->NPC_class, "cervical", &oUp, &oRt, &oFwd );
2385 			BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, neckAngles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, cgs.model_draw);
2386 		}
2387 		if ( cent->gent->client->NPC_class != CLASS_SABER_DROID )
2388 		{//saber droid doesn't use thoracic
2389 			if ( cent->gent->client->NPC_class == CLASS_RANCOR )
2390 			{
2391 				thoracicAngles[YAW] = thoracicAngles[ROLL] = 0.0f;
2392 			}
2393 			G_BoneOrientationsForClass( cent->gent->client->NPC_class, "thoracic", &oUp, &oRt, &oFwd );
2394 			BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, thoracicAngles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, cgs.model_draw);
2395 		}
2396 	}
2397 	else
2398 	{
2399 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, headAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2400 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, neckAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2401 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2402 	}
2403 }
2404 
CG_UpdateLookAngles(centity_t * cent,vec3_t lookAngles,float lookSpeed,float minPitch,float maxPitch,float minYaw,float maxYaw,float minRoll,float maxRoll)2405 static void CG_UpdateLookAngles( centity_t *cent, vec3_t lookAngles, float lookSpeed, float minPitch, float maxPitch, float minYaw, float maxYaw, float minRoll, float maxRoll )
2406 {
2407 	if ( !cent || !cent->gent || !cent->gent->client )
2408 	{
2409 		return;
2410 	}
2411 	if ( cent->gent->client->renderInfo.lookingDebounceTime > cg.time )
2412 	{
2413 		//clamp so don't get "Exorcist" effect
2414 		if ( lookAngles[PITCH] > maxPitch )
2415 		{
2416 			lookAngles[PITCH] = maxPitch;
2417 		}
2418 		else if ( lookAngles[PITCH] < minPitch )
2419 		{
2420 			lookAngles[PITCH] = minPitch;
2421 		}
2422 		if ( lookAngles[YAW] > maxYaw )
2423 		{
2424 			lookAngles[YAW] = maxYaw;
2425 		}
2426 		else if ( lookAngles[YAW] < minYaw )
2427 		{
2428 			lookAngles[YAW] = minYaw;
2429 		}
2430 		if ( lookAngles[ROLL] > maxRoll )
2431 		{
2432 			lookAngles[ROLL] = maxRoll;
2433 		}
2434 		else if ( lookAngles[ROLL] < minRoll )
2435 		{
2436 			lookAngles[ROLL] = minRoll;
2437 		}
2438 
2439 		//slowly lerp to this new value
2440 		//Remember last headAngles
2441 		vec3_t	oldLookAngles;
2442 		VectorCopy( cent->gent->client->renderInfo.lastHeadAngles, oldLookAngles );
2443 		vec3_t lookAnglesDiff;
2444 		VectorSubtract( lookAngles, oldLookAngles, lookAnglesDiff );
2445 
2446 		for ( int ang = 0; ang < 3; ang++ )
2447 		{
2448 			lookAnglesDiff[ang] = AngleNormalize180( lookAnglesDiff[ang] );
2449 		}
2450 
2451 		if( VectorLengthSquared( lookAnglesDiff ) )
2452 		{
2453 			lookAngles[PITCH] = AngleNormalize180( oldLookAngles[PITCH]+(lookAnglesDiff[PITCH]*cg.frameInterpolation*lookSpeed) );
2454 			lookAngles[YAW] = AngleNormalize180( oldLookAngles[YAW]+(lookAnglesDiff[YAW]*cg.frameInterpolation*lookSpeed) );
2455 			lookAngles[ROLL] = AngleNormalize180( oldLookAngles[ROLL]+(lookAnglesDiff[ROLL]*cg.frameInterpolation*lookSpeed) );
2456 		}
2457 	}
2458 	//Remember current lookAngles next time
2459 	VectorCopy( lookAngles, cent->gent->client->renderInfo.lastHeadAngles );
2460 }
2461 
2462 /*
2463 ===============
2464 CG_PlayerAngles
2465 
2466 Handles seperate torso motion
2467 
2468   legs pivot based on direction of movement
2469 
2470   head always looks exactly at cent->lerpAngles
2471 
2472   if motion < 20 degrees, show in head only
2473   if < 45 degrees, also show in torso
2474 
2475 ===============
2476 */
2477 extern int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim );
2478 extern float PM_GetTimeScaleMod( gentity_t *gent );
CG_G2PlayerAngles(centity_t * cent,vec3_t legs[3],vec3_t angles)2479 static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles )
2480 {
2481 	vec3_t		headAngles, neckAngles, chestAngles, thoracicAngles = {0,0,0};//legsAngles, torsoAngles,
2482 	vec3_t		ulAngles, llAngles;
2483 	//float		speed;
2484 	//vec3_t		velocity;
2485 	vec3_t		lookAngles, viewAngles;
2486 	/*
2487 	float		headYawClampMin, headYawClampMax;
2488 	float		headPitchClampMin, headPitchClampMax;
2489 	float		torsoYawSwingTolMin, torsoYawSwingTolMax;
2490 	float		torsoYawClampMin, torsoYawClampMax;
2491 	float		torsoPitchSwingTolMin, torsoPitchSwingTolMax;
2492 	float		torsoPitchClampMin, torsoPitchClampMax;
2493 	float		legsYawSwingTolMin, legsYawSwingTolMax;
2494 	float		yawSpeed, maxYawSpeed, lookingSpeed;
2495 	*/
2496 	float		lookAngleSpeed = LOOK_TALKING_SPEED;//shut up the compiler
2497 	//float		swing, scale;
2498 	//int			i;
2499 	qboolean	looking = qfalse, talking = qfalse;
2500 
2501 	if ( cent->gent
2502 		&& (cent->gent->flags&FL_NO_ANGLES) )
2503 	{//flatten out all bone angles we might have been overriding
2504 		cent->lerpAngles[PITCH] = cent->lerpAngles[ROLL] = 0;
2505 		VectorCopy( cent->lerpAngles, angles );
2506 
2507 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2508 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2509 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2510 
2511 		cent->pe.torso.pitchAngle = 0;
2512 		cent->pe.torso.yawAngle = 0;
2513 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2514 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2515 
2516 		cent->pe.legs.pitchAngle = angles[0];
2517 		cent->pe.legs.yawAngle = angles[1];
2518 		if ( cent->gent->client )
2519 		{
2520 			cent->gent->client->renderInfo.legsYaw = angles[1];
2521 		}
2522 		AnglesToAxis( angles, legs );
2523 		return;
2524 	}
2525 	// Dead entity
2526 	if ( cent->gent && cent->gent->health <= 0 )
2527 	{
2528 		if ( cent->gent->hipsBone != -1 )
2529 		{
2530 			gi.G2API_StopBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone );
2531 		}
2532 
2533 		VectorCopy( cent->lerpAngles, angles );
2534 
2535 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2536 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2537 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2538 
2539 		cent->pe.torso.pitchAngle = 0;
2540 		cent->pe.torso.yawAngle = 0;
2541 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2542 		BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw );
2543 
2544 		cent->pe.legs.pitchAngle = angles[0];
2545 		cent->pe.legs.yawAngle = angles[1];
2546 		if ( cent->gent->client )
2547 		{
2548 			cent->gent->client->renderInfo.legsYaw = angles[1];
2549 		}
2550 		AnglesToAxis( angles, legs );
2551 		return;
2552 	}
2553 
2554 	if ( cent->gent && cent->gent->client
2555 		&& (cent->gent->client->NPC_class != CLASS_GONK )
2556 		&& (cent->gent->client->NPC_class != CLASS_INTERROGATOR)
2557 		&& (cent->gent->client->NPC_class != CLASS_SENTRY)
2558 		&& (cent->gent->client->NPC_class != CLASS_PROBE )
2559 		&& (cent->gent->client->NPC_class != CLASS_R2D2 )
2560 		&& (cent->gent->client->NPC_class != CLASS_R5D2)
2561 		&& (cent->gent->client->NPC_class != CLASS_ATST||!cent->gent->s.number) )
2562 	{// If we are rendering third person, we should just force the player body to always fully face
2563 		//	whatever way they are looking, otherwise, you can end up with gun shots coming off of the
2564 		//	gun at angles that just look really wrong.
2565 
2566 		//NOTENOTE: shots are coming out of the gun at ridiculous angles. The head & torso
2567 		//should pitch *some* when looking up and down...
2568 		VectorCopy( cent->lerpAngles, angles );
2569 		angles[PITCH] = 0;
2570 
2571 		if ( cent->gent->client )
2572 		{
2573 			if ( cent->gent->client->NPC_class != CLASS_ATST )
2574 			{
2575 				if ( !PM_SpinningSaberAnim( cent->currentState.legsAnim ) )
2576 				{//don't turn legs if in a spinning saber transition
2577 					//FIXME: use actual swing/clamp tolerances?
2578 					if ( cent->gent->client->ps.groundEntityNum != ENTITYNUM_NONE && !PM_InRoll( &cent->gent->client->ps ) )
2579 					{//on the ground
2580 						CG_PlayerLegsYawFromMovement( cent, cent->gent->client->ps.velocity, &angles[YAW], cent->lerpAngles[YAW], -60, 60, qtrue );
2581 					}
2582 					else
2583 					{//face legs to front
2584 						CG_PlayerLegsYawFromMovement( cent, vec3_origin, &angles[YAW], cent->lerpAngles[YAW], -60, 60, qtrue );
2585 					}
2586 				}
2587 			}
2588 		}
2589 
2590 		//VectorClear( viewAngles );
2591 		VectorCopy( cent->lerpAngles, viewAngles );
2592 		viewAngles[YAW] = viewAngles[ROLL] = 0;
2593 		if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_RANCOR )
2594 		{//rancor uses full pitch
2595 			if ( cent->gent->count )
2596 			{//don't look up or down at enemy when he's in your hand...
2597 				viewAngles[PITCH] = 0.0f;
2598 			}
2599 			else if ( cent->gent->enemy )
2600 			{
2601 				if ( cent->gent->enemy->s.solid == SOLID_BMODEL )
2602 				{//don't look up or down at architecture?
2603 					viewAngles[PITCH] = 0.0f;
2604 				}
2605 				else if ( cent->gent->client->ps.legsAnim == BOTH_MELEE1 )
2606 				{//don't look up or down when smashing the ground
2607 					viewAngles[PITCH] = 0.0f;
2608 				}
2609 				else
2610 				{
2611 					vec3_t eDir, eAngles, lookFrom;
2612 					VectorCopy( cent->lerpOrigin, lookFrom );
2613 					lookFrom[2] += cent->gent->maxs[2]*0.6f;
2614 					VectorSubtract( cg_entities[cent->gent->enemy->s.number].lerpOrigin, lookFrom, eDir );
2615 					vectoangles( eDir, eAngles );
2616 					viewAngles[PITCH] = AngleNormalize180(eAngles[0]);
2617 					if ( cent->gent->client->ps.legsAnim == BOTH_ATTACK2 )
2618 					{//swinging at something on the ground
2619 						if ( viewAngles[PITCH] > 0.0f )
2620 						{//don't look down
2621 							viewAngles[PITCH] = 0.0f;
2622 						}
2623 					}
2624 					else if ( cent->gent->client->ps.legsAnim == BOTH_ATTACK4 )
2625 					{//in breath attack anim
2626 						if ( viewAngles[PITCH] > 0.0f )
2627 						{//don't look down
2628 							viewAngles[PITCH] = 0.0f;
2629 						}
2630 						else
2631 						{//exaggerate looking up
2632 							viewAngles[PITCH] *= 2.0f;
2633 						}
2634 					}
2635 					else if ( viewAngles[PITCH] > 0.0f )
2636 					{//reduce looking down
2637 						viewAngles[PITCH] *= 0.5f;
2638 					}
2639 					//clamp?
2640 					/*
2641 					if ( viewAngles[PITCH] > 30.0f )
2642 					{
2643 						viewAngles[PITCH] > 30.0f;
2644 					}
2645 					if ( viewAngles[PITCH] < -75.0f )
2646 					{
2647 						viewAngles[PITCH] = -75.0f;
2648 					}
2649 					*/
2650 				}
2651 			}
2652 			else
2653 			{
2654 				viewAngles[PITCH] = 0.0f;
2655 			}
2656 		}
2657 		else
2658 		{
2659 			viewAngles[PITCH] *= 0.5;
2660 		}
2661 		VectorCopy( viewAngles, lookAngles );
2662 
2663 	//	if ( cent->gent && !Q_stricmp( "atst", cent->gent->NPC_type ) )
2664 		if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_ATST )
2665 		{
2666 			lookAngles[YAW] = 0;
2667 			BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, lookAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2668 			VectorCopy( viewAngles, lookAngles );
2669 		}
2670 		else
2671 		{
2672 			if ( cg_turnAnims.integer && !in_camera && cent->gent->hipsBone >= 0 )
2673 			{
2674 				//override the hips bone with a turn anim when turning
2675 				//and clear it when we're not... does blend from and to parent actually work?
2676 				int startFrame, endFrame;
2677 				const qboolean animatingHips = gi.G2API_GetAnimRangeIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone, &startFrame, &endFrame );
2678 
2679 				//FIXME: make legs lag behind when turning in place, only play turn anim when legs have to catch up
2680 				if ( angles[YAW] == cent->pe.legs.yawAngle )
2681 				{
2682 					gi.G2API_StopBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone );
2683 				}
2684 				else if ( VectorCompare( vec3_origin, cent->gent->client->ps.velocity ) )
2685 				{//FIXME: because of LegsYawFromMovement, we play the turnAnims when we stop running, which looks really bad.
2686 					int turnAnim = PM_TurnAnimForLegsAnim( cent->gent, cent->gent->client->ps.legsAnim );
2687 					if ( turnAnim != -1 && cent->gent->health > 0 )
2688 					{
2689 						animation_t *animations = level.knownAnimFileSets[cent->gent->client->clientInfo.animFileIndex].animations;
2690 
2691 						if ( !animatingHips || ( animations[turnAnim].firstFrame != startFrame ) )// only set the anim if we aren't going to do the same animation again
2692 						{
2693 							float animSpeed = 50.0f / animations[turnAnim].frameLerp * PM_GetTimeScaleMod( cent->gent );
2694 
2695 							gi.G2API_SetBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone,
2696 								animations[turnAnim].firstFrame, animations[turnAnim].firstFrame+animations[turnAnim].numFrames,
2697 								BONE_ANIM_OVERRIDE_LOOP/*|BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND*/, animSpeed, cg.time, -1, 100 );
2698 						}
2699 					}
2700 					else
2701 					{
2702 						gi.G2API_StopBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone );
2703 					}
2704 				}
2705 				else
2706 				{
2707 					gi.G2API_StopBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone );
2708 				}
2709 			}
2710 
2711 			CG_G2ClientSpineAngles( cent, viewAngles, angles, thoracicAngles, ulAngles, llAngles );
2712 		}
2713 
2714 		vec3_t	trailingLegsAngles;
2715 		if ( cent->gent->client && cent->gent->client->NPC_class == CLASS_ATST )
2716 		{
2717 			CG_ATSTLegsYaw( cent, trailingLegsAngles );
2718 			AnglesToAxis( trailingLegsAngles, legs );
2719 			angles[YAW] = trailingLegsAngles[YAW];
2720 		}
2721 		/*
2722 		else if ( cent->gent->client && cent->gent->client->NPC_class == CLASS_WAMPA )
2723 		{
2724 			CG_ATSTLegsYaw( cent, trailingLegsAngles );
2725 			AnglesToAxis( trailingLegsAngles, legs );
2726 			angles[YAW] = trailingLegsAngles[YAW];
2727 		}
2728 		*/
2729 		// either riding a vehicle or we are a vehicle
2730 		if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_VEHICLE )
2731 		{//you are a vehicle, just use your lerpAngles which comes from m_vOrientation
2732 			cent->pe.legs.yawing = qfalse;
2733 			cent->pe.legs.yawAngle = cent->lerpAngles[YAW];
2734 			if ( cent->gent->client )
2735 			{
2736 				cent->gent->client->renderInfo.legsYaw = cent->lerpAngles[YAW];
2737 			}
2738 			AnglesToAxis( cent->lerpAngles, legs );
2739 			if ( cent->gent->m_pVehicle )
2740 			{
2741 				if ( cent->gent->m_pVehicle->m_pVehicleInfo )
2742 				{
2743                     if ( cent->gent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER
2744 						|| cent->gent->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER )
2745 					{
2746 						VectorCopy( cent->lerpAngles, angles );
2747 					}
2748 				}
2749 			}
2750 		}
2751 		else if ( G_IsRidingVehicle( cent->gent ) )
2752 		{//riding a vehicle, get the vehicle's lerpAngles (which comes from m_vOrientation)
2753 			cent->pe.legs.yawing = qfalse;
2754 			cent->pe.legs.yawAngle = cg_entities[cent->gent->owner->s.number].lerpAngles[YAW];
2755 			if ( cent->gent->client )
2756 			{
2757 				cent->gent->client->renderInfo.legsYaw = cg_entities[cent->gent->owner->s.number].lerpAngles[YAW];
2758 			}
2759 			AnglesToAxis( cg_entities[cent->gent->owner->s.number].lerpAngles, legs );
2760 		}
2761 		else
2762 		{
2763 
2764 			//set the legs.yawing field so we play the turning anim when turning in place
2765 			if ( angles[YAW] == cent->pe.legs.yawAngle )
2766 			{
2767 				cent->pe.legs.yawing = qfalse;
2768 			}
2769 			else
2770 			{
2771 				cent->pe.legs.yawing = qtrue;
2772 			}
2773 			cent->pe.legs.yawAngle = angles[YAW];
2774 			if ( cent->gent->client )
2775 			{
2776 				cent->gent->client->renderInfo.legsYaw = angles[YAW];
2777 			}
2778 			if ( ((cent->gent->client->ps.eFlags&EF_FORCE_GRIPPED)||((cent->gent->client->NPC_class == CLASS_BOBAFETT||cent->gent->client->NPC_class == CLASS_ROCKETTROOPER)&&cent->gent->client->moveType==MT_FLYSWIM))
2779 				&& cent->gent->client->ps.groundEntityNum == ENTITYNUM_NONE )
2780 			{
2781 				vec3_t	centFwd, centRt;
2782 				float	divFactor = 1.0f;
2783 				if ( (cent->gent->client->NPC_class == CLASS_BOBAFETT||cent->gent->client->NPC_class == CLASS_ROCKETTROOPER)
2784 					&& cent->gent->client->moveType == MT_FLYSWIM )
2785 				{
2786 					divFactor = 3.0f;
2787 				}
2788 
2789 				AngleVectors( cent->lerpAngles, centFwd, centRt, NULL );
2790 				angles[PITCH] = AngleNormalize180( DotProduct( cent->gent->client->ps.velocity, centFwd )/(2*divFactor) );
2791 				if ( angles[PITCH] > 90 )
2792 				{
2793 					angles[PITCH] = 90;
2794 				}
2795 				else if ( angles[PITCH] < -90 )
2796 				{
2797 					angles[PITCH] = -90;
2798 				}
2799 				angles[ROLL] = AngleNormalize180( DotProduct( cent->gent->client->ps.velocity, centRt )/(10*divFactor) );
2800 				if ( angles[ROLL] > 90 )
2801 				{
2802 					angles[ROLL] = 90;
2803 				}
2804 				else if ( angles[ROLL] < -90 )
2805 				{
2806 					angles[ROLL] = -90;
2807 				}
2808 			}
2809 			AnglesToAxis( angles, legs );
2810 		}
2811 
2812 		//clamp relative to forward of cervical bone!
2813 		if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_ATST )
2814 		{
2815 			looking = qfalse;
2816 			VectorCopy( vec3_origin, chestAngles );
2817 		}
2818 		else
2819 		{
2820 			//look at lookTarget!
2821 			float	lookingSpeed = 0.3f;
2822 			looking = CG_CheckLookTarget( cent, lookAngles, &lookingSpeed );
2823 			//Now add head bob when talking
2824 			talking = CG_AddHeadBob( cent, lookAngles );
2825 
2826 			//NOTE: previously, lookAngleSpeed was always 0.25f for the player
2827 			//Figure out how fast head should be turning
2828 			if ( cent->pe.torso.yawing || cent->pe.torso.pitching )
2829 			{//If torso is turning, we want to turn head just as fast
2830 				if ( cent->gent->NPC )
2831 				{
2832 					lookAngleSpeed = cent->gent->NPC->stats.yawSpeed/150;//about 0.33 normally
2833 				}
2834 				else
2835 				{
2836 					lookAngleSpeed = CG_SWINGSPEED;
2837 				}
2838 			}
2839 			else if ( talking )
2840 			{//Slow for head bobbing
2841 				lookAngleSpeed = LOOK_TALKING_SPEED;
2842 			}
2843 			else if ( looking )
2844 			{//Not talking, set it up for looking at enemy, CheckLookTarget will scale it down if neccessary
2845 				lookAngleSpeed = lookingSpeed;
2846 			}
2847 			else if ( cent->gent->client->renderInfo.lookingDebounceTime > cg.time )
2848 			{//Not looking, not talking, head is returning from a talking head bob, use talking speed
2849 				lookAngleSpeed = LOOK_TALKING_SPEED;
2850 			}
2851 
2852 			if ( looking || talking )
2853 			{//want to keep doing this lerp behavior for a full second after stopped looking (so don't snap)
2854 				//we should have a relative look angle, normalized to 180
2855 				cent->gent->client->renderInfo.lookingDebounceTime = cg.time + 1000;
2856 			}
2857 			else
2858 			{
2859 				//still have a relative look angle from above
2860 			}
2861 
2862 			if ( cent->gent->client->NPC_class == CLASS_RANCOR )
2863 			{//always use the viewAngles we calced
2864 				VectorCopy( viewAngles, lookAngles );
2865 			}
2866 			CG_UpdateLookAngles( cent, lookAngles, lookAngleSpeed, -50.0f, 50.0f, -70.0f, 70.0f, -30.0f, 30.0f );
2867 		}
2868 
2869 		if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_ATST )
2870 		{
2871 			VectorCopy( cent->lerpAngles, lookAngles );
2872 			lookAngles[0] = lookAngles[2] = 0;
2873 			lookAngles[YAW] -= trailingLegsAngles[YAW];
2874 			BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, lookAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2875 		}
2876 		else
2877 		{
2878 			vec3_t headClampMinAngles = {-25,-55,-10}, headClampMaxAngles = {50,50,10};
2879 			CG_G2ClientNeckAngles( cent, lookAngles, headAngles, neckAngles, thoracicAngles, headClampMinAngles, headClampMaxAngles );
2880 		}
2881 		return;
2882 	}
2883 	// All other entities
2884 	else if ( cent->gent && cent->gent->client )
2885 	{
2886 		if ( (cent->gent->client->NPC_class == CLASS_PROBE )
2887 			|| (cent->gent->client->NPC_class == CLASS_R2D2 )
2888 			|| (cent->gent->client->NPC_class == CLASS_R5D2)
2889 			|| (cent->gent->client->NPC_class == CLASS_RANCOR)
2890 			|| (cent->gent->client->NPC_class == CLASS_WAMPA)
2891 			|| (cent->gent->client->NPC_class == CLASS_ATST) )
2892 		{
2893 			VectorCopy( cent->lerpAngles, angles );
2894 			angles[PITCH] = 0;
2895 
2896 			//FIXME: use actual swing/clamp tolerances?
2897 			if ( cent->gent->client->ps.groundEntityNum != ENTITYNUM_NONE )
2898 			{//on the ground
2899 				CG_PlayerLegsYawFromMovement( cent, cent->gent->client->ps.velocity, &angles[YAW], cent->lerpAngles[YAW], -60, 60, qtrue );
2900 			}
2901 			else
2902 			{//face legs to front
2903 				CG_PlayerLegsYawFromMovement( cent, vec3_origin, &angles[YAW], cent->lerpAngles[YAW], -60, 60, qtrue );
2904 			}
2905 
2906 			VectorCopy( cent->lerpAngles, viewAngles );
2907 //			viewAngles[YAW] = viewAngles[ROLL] = 0;
2908 			viewAngles[PITCH] *= 0.5;
2909 			VectorCopy( viewAngles, lookAngles );
2910 
2911 			lookAngles[1] = 0;
2912 
2913 			if ( cent->gent->client->NPC_class == CLASS_ATST )
2914 			{//body pitch
2915 				BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, lookAngles, BONE_ANGLES_POSTMULT,POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
2916 			}
2917 
2918 			VectorCopy( viewAngles, lookAngles );
2919 
2920 			vec3_t	trailingLegsAngles;
2921 			if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_ATST )
2922 			{
2923 				CG_ATSTLegsYaw( cent, trailingLegsAngles );
2924 				AnglesToAxis( trailingLegsAngles, legs );
2925 			}
2926 			/*
2927 			else if ( cent->gent->client
2928 				&& (cent->gent->client->NPC_class == CLASS_WAMPA||cent->gent->client->NPC_class == CLASS_RANCOR) )
2929 			{
2930 				CG_ATSTLegsYaw( cent, trailingLegsAngles );
2931 				AnglesToAxis( trailingLegsAngles, legs );
2932 			}
2933 			*/
2934 			else
2935 			{
2936 				//FIXME: this needs to properly set the legs.yawing field so we don't erroneously play the turning anim, but we do play it when turning in place
2937 				if ( angles[YAW] == cent->pe.legs.yawAngle )
2938 				{
2939 					cent->pe.legs.yawing = qfalse;
2940 				}
2941 				else
2942 				{
2943 					cent->pe.legs.yawing = qtrue;
2944 				}
2945 
2946 				cent->pe.legs.yawAngle = angles[YAW];
2947 				if ( cent->gent->client )
2948 				{
2949 					cent->gent->client->renderInfo.legsYaw = angles[YAW];
2950 				}
2951 				AnglesToAxis( angles, legs );
2952 			}
2953 
2954 //			if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_ATST )
2955 //			{
2956 //				looking = qfalse;
2957 //			}
2958 //			else
2959 			{	//look at lookTarget!
2960 				//FIXME: snaps to side when lets go of lookTarget... ?
2961 				float	lookingSpeed = 0.3f;
2962 				looking = CG_CheckLookTarget( cent, lookAngles, &lookingSpeed );
2963 				lookAngles[PITCH] = lookAngles[ROLL] = 0;//droids can't pitch or roll their heads
2964 				if ( looking )
2965 				{//want to keep doing this lerp behavior for a full second after stopped looking (so don't snap)
2966 					cent->gent->client->renderInfo.lookingDebounceTime = cg.time + 1000;
2967 				}
2968 			}
2969 			if ( cent->gent->client->renderInfo.lookingDebounceTime > cg.time )
2970 			{	//adjust for current body orientation
2971 				lookAngles[YAW] -= cent->pe.torso.yawAngle;
2972 				lookAngles[YAW] -= cent->pe.legs.yawAngle;
2973 
2974 				//normalize
2975 				lookAngles[YAW] = AngleNormalize180( lookAngles[YAW] );
2976 
2977 				//slowly lerp to this new value
2978 				//Remember last headAngles
2979 				vec3_t	oldLookAngles;
2980 				VectorCopy( cent->gent->client->renderInfo.lastHeadAngles, oldLookAngles );
2981 				if( VectorCompare( oldLookAngles, lookAngles ) == qfalse )
2982 				{
2983 					//FIXME: This clamp goes off viewAngles,
2984 					//but really should go off the tag_torso's axis[0] angles, no?
2985 					lookAngles[YAW] = oldLookAngles[YAW]+(lookAngles[YAW]-oldLookAngles[YAW])*cg.frameInterpolation*0.25;
2986 				}
2987 				//Remember current lookAngles next time
2988 				VectorCopy( lookAngles, cent->gent->client->renderInfo.lastHeadAngles );
2989 			}
2990 			else
2991 			{//Remember current lookAngles next time
2992 				VectorCopy( lookAngles, cent->gent->client->renderInfo.lastHeadAngles );
2993 			}
2994 			if ( cent->gent->client->NPC_class == CLASS_ATST )
2995 			{
2996 				VectorCopy( cent->lerpAngles, lookAngles );
2997 				lookAngles[0] = lookAngles[2] = 0;
2998 				lookAngles[YAW] -= trailingLegsAngles[YAW];
2999 			}
3000 			else
3001 			{
3002 				lookAngles[PITCH] = lookAngles[ROLL] = 0;
3003 				lookAngles[YAW] -= cent->pe.legs.yawAngle;
3004 			}
3005 			if ( cent->gent->client->NPC_class == CLASS_WAMPA )
3006 			{
3007 				Eorientations oUp, oRt, oFwd;
3008 				G_BoneOrientationsForClass( cent->gent->client->NPC_class, "cranium", &oUp, &oRt, &oFwd );
3009 				BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, lookAngles, BONE_ANGLES_POSTMULT, oUp, oRt, oFwd, cgs.model_draw );
3010 			}
3011 			else
3012 			{
3013 				BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, lookAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw);
3014 			}
3015 			//return;
3016 		}
3017 		else//if ( (cent->gent->client->NPC_class == CLASS_GONK ) || (cent->gent->client->NPC_class == CLASS_INTERROGATOR) || (cent->gent->client->NPC_class == CLASS_SENTRY) )
3018 		{
3019 			VectorCopy( cent->lerpAngles, angles );
3020 			cent->pe.torso.pitchAngle = 0;
3021 			cent->pe.torso.yawAngle = 0;
3022 			cent->pe.legs.pitchAngle = angles[0];
3023 			cent->gent->client->renderInfo.legsYaw = cent->pe.legs.yawAngle = angles[1];
3024 			AnglesToAxis( angles, legs );
3025 			//return;
3026 		}
3027 	}
3028 }
3029 
CG_PlayerAngles(centity_t * cent,vec3_t legs[3],vec3_t torso[3],vec3_t head[3])3030 static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] )
3031 {
3032 	vec3_t		legsAngles, torsoAngles, headAngles;
3033 	vec3_t		lookAngles, viewAngles;
3034 	float		headYawClampMin, headYawClampMax;
3035 	float		headPitchClampMin, headPitchClampMax;
3036 	float		torsoYawSwingTolMin, torsoYawSwingTolMax;
3037 	float		torsoYawClampMin, torsoYawClampMax;
3038 	float		torsoPitchSwingTolMin, torsoPitchSwingTolMax;
3039 	float		torsoPitchClampMin, torsoPitchClampMax;
3040 	float		legsYawSwingTolMin, legsYawSwingTolMax;
3041 	float		maxYawSpeed, yawSpeed, lookingSpeed;
3042 	float		lookAngleSpeed = LOOK_TALKING_SPEED;//shut up the compiler
3043 	float		swing, scale;
3044 	int			i;
3045 	qboolean	looking = qfalse, talking = qfalse;
3046 
3047 	if ( cg.renderingThirdPerson && cent->gent && cent->gent->s.number == 0 )
3048 	{
3049 		// If we are rendering third person, we should just force the player body to always fully face
3050 		//	whatever way they are looking, otherwise, you can end up with gun shots coming off of the
3051 		//	gun at angles that just look really wrong.
3052 
3053 		//NOTENOTE: shots are coming out of the gun at ridiculous angles. The head & torso
3054 		//should pitch *some* when looking up and down...
3055 
3056 		//VectorClear( viewAngles );
3057 		VectorCopy( cent->lerpAngles, viewAngles );
3058 
3059 		viewAngles[YAW] = viewAngles[ROLL] = 0;
3060 		viewAngles[PITCH] *= 0.5;
3061 		AnglesToAxis( viewAngles, head );
3062 
3063 		viewAngles[PITCH] *= 0.75;
3064 		cent->pe.torso.pitchAngle = viewAngles[PITCH];
3065 		cent->pe.torso.yawAngle = viewAngles[YAW];
3066 		AnglesToAxis( viewAngles, torso );
3067 
3068 		VectorCopy( cent->lerpAngles, lookAngles );
3069 		lookAngles[PITCH] = 0;
3070 
3071 		//FIXME: this needs to properly set the legs.yawing field so we don't erroneously play the turning anim, but we do play it when turning in place
3072 		if ( lookAngles[YAW] == cent->pe.legs.yawAngle )
3073 		{
3074 			cent->pe.legs.yawing = qfalse;
3075 		}
3076 		else
3077 		{
3078 			cent->pe.legs.yawing = qtrue;
3079 		}
3080 
3081 		if ( cent->gent->client->ps.velocity[0] || cent->gent->client->ps.velocity[1] )
3082 		{
3083 			float	moveYaw;
3084 			moveYaw = vectoyaw( cent->gent->client->ps.velocity );
3085 			lookAngles[YAW] = cent->lerpAngles[YAW] + AngleDelta( cent->lerpAngles[YAW], moveYaw );
3086 		}
3087 
3088 		cent->pe.legs.yawAngle = lookAngles[YAW];
3089 		if ( cent->gent->client )
3090 		{
3091 			cent->gent->client->renderInfo.legsYaw = lookAngles[YAW];
3092 		}
3093 		AnglesToAxis( lookAngles, legs );
3094 
3095 		return;
3096 	}
3097 
3098 	if ( cent->currentState.clientNum != 0 )
3099 	{
3100 		headYawClampMin = -cent->gent->client->renderInfo.headYawRangeLeft;
3101 		headYawClampMax = cent->gent->client->renderInfo.headYawRangeRight;
3102 		//These next two are only used for a calc below- this clamp is done in PM_UpdateViewAngles
3103 		headPitchClampMin = -cent->gent->client->renderInfo.headPitchRangeUp;
3104 		headPitchClampMax = cent->gent->client->renderInfo.headPitchRangeDown;
3105 
3106 		torsoYawSwingTolMin = headYawClampMin * 0.3;
3107 		torsoYawSwingTolMax = headYawClampMax * 0.3;
3108 		torsoPitchSwingTolMin = headPitchClampMin * 0.5;
3109 		torsoPitchSwingTolMax =  headPitchClampMax * 0.5;
3110 		torsoYawClampMin = -cent->gent->client->renderInfo.torsoYawRangeLeft;
3111 		torsoYawClampMax = cent->gent->client->renderInfo.torsoYawRangeRight;
3112 		torsoPitchClampMin = -cent->gent->client->renderInfo.torsoPitchRangeUp;
3113 		torsoPitchClampMax = cent->gent->client->renderInfo.torsoPitchRangeDown;
3114 
3115 		legsYawSwingTolMin = torsoYawClampMin * 0.5;
3116 		legsYawSwingTolMax = torsoYawClampMax * 0.5;
3117 
3118 		if ( cent->gent && cent->gent->next_roff_time && cent->gent->next_roff_time >= cg.time )
3119 		{//Following a roff, body must keep up with head, yaw-wise
3120 			headYawClampMin =
3121 			headYawClampMax =
3122 			torsoYawSwingTolMin =
3123 			torsoYawSwingTolMax =
3124 			torsoYawClampMin =
3125 			torsoYawClampMax =
3126 			legsYawSwingTolMin =
3127 			legsYawSwingTolMax = 0;
3128 		}
3129 
3130 		yawSpeed = maxYawSpeed = cent->gent->NPC->stats.yawSpeed/150;//about 0.33 normally
3131 	}
3132 	else
3133 	{
3134 		headYawClampMin = -70;
3135 		headYawClampMax = 70;
3136 
3137 		//These next two are only used for a calc below- this clamp is done in PM_UpdateViewAngles
3138 		headPitchClampMin = -90;
3139 		headPitchClampMax = 90;
3140 
3141 		torsoYawSwingTolMin = -90;
3142 		torsoYawSwingTolMax = 90;
3143 		torsoPitchSwingTolMin = -90;
3144 		torsoPitchSwingTolMax = 90;
3145 		torsoYawClampMin = -90;
3146 		torsoYawClampMax = 90;
3147 		torsoPitchClampMin = -90;
3148 		torsoPitchClampMax = 90;
3149 
3150 		legsYawSwingTolMin = -90;
3151 		legsYawSwingTolMax = 90;
3152 
3153 		yawSpeed = maxYawSpeed = CG_SWINGSPEED;
3154 	}
3155 
3156 	if(yawSpeed <= 0)
3157 	{//Just in case
3158 		yawSpeed = 0.5f;	//was 0.33
3159 	}
3160 
3161 	lookingSpeed = yawSpeed;
3162 
3163 	VectorCopy( cent->lerpAngles, headAngles );
3164 	headAngles[YAW] = AngleNormalize360( headAngles[YAW] );
3165 	VectorClear( legsAngles );
3166 	VectorClear( torsoAngles );
3167 
3168 	// --------- yaw -------------
3169 
3170 	//Clamp and swing the legs
3171 	legsAngles[YAW] = headAngles[YAW];
3172 
3173 	if(cent->gent->client->renderInfo.renderFlags & RF_LOCKEDANGLE)
3174 	{
3175 		cent->gent->client->renderInfo.legsYaw = cent->pe.legs.yawAngle = cent->gent->client->renderInfo.lockYaw;
3176 		cent->pe.legs.yawing = qfalse;
3177 		legsAngles[YAW] = cent->pe.legs.yawAngle;
3178 	}
3179 	else
3180 	{
3181 		qboolean alwaysFace = qfalse;
3182 		if ( cent->gent && cent->gent->health > 0 )
3183 		{
3184 			if ( cent->gent->enemy )
3185 			{
3186 				alwaysFace = qtrue;
3187 			}
3188 			if ( CG_PlayerLegsYawFromMovement( cent, cent->gent->client->ps.velocity, &legsAngles[YAW], headAngles[YAW], torsoYawClampMin, torsoYawClampMax, alwaysFace ) )
3189 			{
3190 				if ( legsAngles[YAW] == cent->pe.legs.yawAngle )
3191 				{
3192 					cent->pe.legs.yawing = qfalse;
3193 				}
3194 				else
3195 				{
3196 					cent->pe.legs.yawing = qtrue;
3197 				}
3198 				cent->pe.legs.yawAngle = legsAngles[YAW];
3199 				if ( cent->gent->client )
3200 				{
3201 					cent->gent->client->renderInfo.legsYaw = legsAngles[YAW];
3202 				}
3203 			}
3204 			else
3205 			{
3206 				CG_SwingAngles( legsAngles[YAW], legsYawSwingTolMin, legsYawSwingTolMax, torsoYawClampMin, torsoYawClampMax, maxYawSpeed, &cent->pe.legs.yawAngle, &cent->pe.legs.yawing );
3207 				legsAngles[YAW] = cent->pe.legs.yawAngle;
3208 				if ( cent->gent->client )
3209 				{
3210 					cent->gent->client->renderInfo.legsYaw = legsAngles[YAW];
3211 				}
3212 			}
3213 		}
3214 		else
3215 		{
3216 			CG_SwingAngles( legsAngles[YAW], legsYawSwingTolMin, legsYawSwingTolMax, torsoYawClampMin, torsoYawClampMax, maxYawSpeed, &cent->pe.legs.yawAngle, &cent->pe.legs.yawing );
3217 			legsAngles[YAW] = cent->pe.legs.yawAngle;
3218 			if ( cent->gent->client )
3219 			{
3220 				cent->gent->client->renderInfo.legsYaw = legsAngles[YAW];
3221 			}
3222 		}
3223 	}
3224 
3225 	/*
3226 	legsAngles[YAW] = cent->pe.legs.yawAngle;
3227 	if ( cent->gent->client )
3228 	{
3229 		cent->gent->client->renderInfo.legsYaw = legsAngles[YAW];
3230 	}
3231 	*/
3232 
3233 	// torso
3234 	// If applicable, swing the lower parts to catch up with the head
3235 	CG_SwingAngles( headAngles[YAW], torsoYawSwingTolMin, torsoYawSwingTolMax, headYawClampMin, headYawClampMax, yawSpeed, &cent->pe.torso.yawAngle, &cent->pe.torso.yawing);
3236 	torsoAngles[YAW] = cent->pe.torso.yawAngle;
3237 
3238 	// ---------- pitch -----------
3239 
3240 	//As the body twists to its extents, the back tends to arch backwards
3241 
3242 
3243 	float dest;
3244 	// only show a fraction of the pitch angle in the torso
3245 	if ( headAngles[PITCH] > 180 )
3246 	{
3247 		dest = (-360 + headAngles[PITCH]) * 0.75;
3248 	}
3249 	else
3250 	{
3251 		dest = headAngles[PITCH] * 0.75;
3252 	}
3253 
3254 	CG_SwingAngles( dest, torsoPitchSwingTolMin, torsoPitchSwingTolMax, torsoPitchClampMin, torsoPitchClampMax, 0.1f, &cent->pe.torso.pitchAngle, &cent->pe.torso.pitching );
3255 	torsoAngles[PITCH] = cent->pe.torso.pitchAngle;
3256 	// --------- roll -------------
3257 
3258 	// pain twitch - FIXME: don't do this if you have no head (like droids?)
3259 	// Maybe need to have clamp angles for roll as well as pitch and yaw?
3260 	//CG_AddPainTwitch( cent, torsoAngles );
3261 
3262 	//----------- Special head looking ---------------
3263 
3264 	//FIXME: to clamp the head angles, figure out tag_head's offset from tag_torso and add
3265 	//	that to whatever offset we're getting here... so turning the head in an
3266 	//	anim that also turns the head doesn't allow the head to turn out of range.
3267 
3268 	//Start with straight ahead
3269 	VectorCopy( headAngles, viewAngles );
3270 	VectorCopy( headAngles, lookAngles );
3271 
3272 	//Remember last headAngles
3273 	VectorCopy( cent->gent->client->renderInfo.lastHeadAngles, headAngles );
3274 
3275 	//See if we're looking at someone/thing
3276 	looking = CG_CheckLookTarget( cent, lookAngles, &lookingSpeed );
3277 
3278 	//Now add head bob when talking
3279 /*	if ( cent->gent->client->clientInfo.extensions )
3280 	{
3281 		talking = CG_AddHeadBob( cent, lookAngles );
3282 	}
3283 */
3284 	//Figure out how fast head should be turning
3285 	if ( cent->pe.torso.yawing || cent->pe.torso.pitching )
3286 	{//If torso is turning, we want to turn head just as fast
3287 		lookAngleSpeed = yawSpeed;
3288 	}
3289 	else if ( talking )
3290 	{//Slow for head bobbing
3291 		lookAngleSpeed = LOOK_TALKING_SPEED;
3292 	}
3293 	else if ( looking )
3294 	{//Not talking, set it up for looking at enemy, CheckLookTarget will scale it down if neccessary
3295 		lookAngleSpeed = lookingSpeed;
3296 	}
3297 	else if ( cent->gent->client->renderInfo.lookingDebounceTime > cg.time )
3298 	{//Not looking, not talking, head is returning from a talking head bob, use talking speed
3299 		lookAngleSpeed = LOOK_TALKING_SPEED;
3300 	}
3301 
3302 	if ( looking || talking )
3303 	{//Keep this type of looking for a second after stopped looking
3304 		cent->gent->client->renderInfo.lookingDebounceTime = cg.time + 1000;
3305 	}
3306 
3307 	if ( cent->gent->client->renderInfo.lookingDebounceTime > cg.time )
3308 	{
3309 		//Calc our actual desired head angles
3310 		for ( i = 0; i < 3; i++ )
3311 		{
3312 			lookAngles[i] = AngleNormalize360( cent->gent->client->renderInfo.headBobAngles[i] + lookAngles[i] );
3313 		}
3314 
3315 		if( VectorCompare( headAngles, lookAngles ) == qfalse )
3316 		{
3317 			//FIXME: This clamp goes off viewAngles,
3318 			//but really should go off the tag_torso's axis[0] angles, no?
3319 			CG_UpdateAngleClamp( lookAngles[PITCH], headPitchClampMin/1.25, headPitchClampMax/1.25, lookAngleSpeed, &headAngles[PITCH], viewAngles[PITCH] );
3320 			CG_UpdateAngleClamp( lookAngles[YAW], headYawClampMin/1.25, headYawClampMax/1.25, lookAngleSpeed, &headAngles[YAW], viewAngles[YAW] );
3321 			CG_UpdateAngleClamp( lookAngles[ROLL], -10, 10, lookAngleSpeed, &headAngles[ROLL], viewAngles[ROLL] );
3322 		}
3323 
3324 		if ( !cent->gent->enemy || cent->gent->enemy->s.number != cent->gent->client->renderInfo.lookTarget )
3325 		{
3326 			//NOTE: Hacky, yes, I know, but necc.
3327 			//We want to turn the body to follow the lookTarget
3328 			//ONLY IF WE DON'T HAVE AN ENEMY OR OUR ENEMY IS NOT OUR LOOKTARGET
3329 			//This is the piece of code that was making the enemies not face where
3330 			//they were actually aiming.
3331 
3332 			//Yaw change
3333 			swing = AngleSubtract( legsAngles[YAW], headAngles[YAW] );
3334 			scale = fabs( swing ) / ( torsoYawClampMax + 0.01 );	//NOTENOTE: Some ents have a clamp of 0, which is bad for division
3335 
3336 			scale *= LOOK_SWING_SCALE;
3337 			torsoAngles[YAW] = legsAngles[YAW] - ( swing * scale );
3338 
3339 			//Pitch change
3340 			swing = AngleSubtract( legsAngles[PITCH], headAngles[PITCH] );
3341 			scale = fabs( swing ) / ( torsoPitchClampMax + 0.01 );	//NOTENOTE: Some ents have a clamp of 0, which is bad for division
3342 
3343 			scale *= LOOK_SWING_SCALE;
3344 			torsoAngles[PITCH] = legsAngles[PITCH] - ( swing * scale );
3345 		}
3346 	}
3347 	else
3348 	{//Look straight ahead
3349 		VectorCopy( viewAngles, headAngles );
3350 	}
3351 
3352 	//Remember current headAngles next time
3353 	VectorCopy( headAngles, cent->gent->client->renderInfo.lastHeadAngles );
3354 
3355 	//-------------------------------------------------------------
3356 
3357 	// pull the angles back out of the hierarchial chain
3358 	AnglesSubtract( headAngles, torsoAngles, headAngles );
3359 	AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
3360 	AnglesToAxis( legsAngles, legs );
3361 	AnglesToAxis( torsoAngles, torso );
3362 	AnglesToAxis( headAngles, head );
3363 }
3364 
3365 
3366 //==========================================================================
3367 
3368 
3369 /*
3370 ===============
3371 CG_TrailItem
3372 ===============
3373 */
3374 /*
3375 static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) {
3376 	refEntity_t		ent;
3377 	vec3_t			angles;
3378 	vec3_t			axis[3];
3379 
3380 	VectorCopy( cent->lerpAngles, angles );
3381 	angles[PITCH] = 0;
3382 	angles[ROLL] = 0;
3383 	AnglesToAxis( angles, axis );
3384 
3385 	memset( &ent, 0, sizeof( ent ) );
3386 	VectorMA( cent->lerpOrigin, -24, axis[0], ent.origin );
3387 	ent.origin[2] += 20;
3388 	VectorScale( cg.autoAxis[0], 0.75, ent.axis[0] );
3389 	VectorScale( cg.autoAxis[1], 0.75, ent.axis[1] );
3390 	VectorScale( cg.autoAxis[2], 0.75, ent.axis[2] );
3391 	ent.hModel = hModel;
3392 	cgi_R_AddRefEntityToScene( &ent );
3393 }
3394 */
3395 
3396 /*
3397 ===============
3398 CG_PlayerPowerups
3399 ===============
3400 */
3401 extern void CG_Seeker( centity_t *cent );
CG_PlayerPowerups(centity_t * cent)3402 static void CG_PlayerPowerups( centity_t *cent )
3403 {
3404 	if ( !cent->currentState.powerups )
3405 	{
3406 		return;
3407 	}
3408 /*
3409 
3410 	// quad gives a dlight
3411 	if ( cent->currentState.powerups & ( 1 << PW_QUAD ) ) {
3412 		cgi_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2, 0.2, 1 );
3413 	}
3414 
3415 	// redflag
3416 	if ( cent->currentState.powerups & ( 1 << PW_REDFLAG ) ) {
3417 		CG_TrailItem( cent, cgs.media.redFlagModel );
3418 		cgi_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1, 0.2, 0.2 );
3419 	}
3420 
3421 	// blueflag
3422 	if ( cent->currentState.powerups & ( 1 << PW_BLUEFLAG ) ) {
3423 		CG_TrailItem( cent, cgs.media.blueFlagModel );
3424 		cgi_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2, 0.2, 1 );
3425 	}
3426 */
3427 	// invul gives a dlight
3428 //	if ( cent->currentState.powerups & ( 1 << PW_BATTLESUIT ) )
3429 //	{
3430 //		cgi_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.8f, 0.8f, 0.2f );
3431 //	}
3432 
3433 	// seeker coolness
3434 /*	if ( cent->currentState.powerups & ( 1 << PW_SEEKER ) )
3435 	{
3436 //		CG_Seeker(cent);
3437 	}*/
3438 }
3439 
3440 #define	SHADOW_DISTANCE		128
_PlayerShadow(const vec3_t origin,const float orientation,float * const shadowPlane,const float radius,qhandle_t markShader)3441 static qboolean _PlayerShadow( const vec3_t origin, const float orientation, float *const shadowPlane, const float radius, qhandle_t markShader ) {
3442 	vec3_t		end, mins = {-7, -7, 0}, maxs = {7, 7, 2};
3443 	trace_t		trace;
3444 	float		alpha;
3445 
3446 	// send a trace down from the player to the ground
3447 	VectorCopy( origin, end );
3448 	end[2] -= SHADOW_DISTANCE;
3449 
3450 	cgi_CM_BoxTrace( &trace, origin, end, mins, maxs, 0, MASK_PLAYERSOLID );
3451 
3452 	// no shadow if too high
3453 	if ( trace.fraction == 1.0 || (trace.startsolid && trace.allsolid) ) {
3454 		return qfalse;
3455 	}
3456 
3457 	*shadowPlane = trace.endpos[2] + 1;
3458 
3459 	// no mark for stencil or projection shadows
3460 	if ( cg_shadows.integer == 1
3461 		|| (in_camera && cg_shadows.integer == 2) )//don't want stencil shadows during a cinematic
3462 	{
3463 		// fade the shadow out with height
3464 		alpha = 1.0 - trace.fraction;
3465 
3466 		// add the mark as a temporary, so it goes directly to the renderer
3467 		// without taking a spot in the cg_marks array
3468 		CG_ImpactMark( markShader, trace.endpos, trace.plane.normal,
3469 			orientation, 1,1,1,alpha, qfalse, radius, qtrue );
3470 	}
3471 	return qtrue;
3472 }
3473 
3474 /*
3475 ===============
3476 CG_PlayerShadow
3477 
3478 Returns the Z component of the surface being shadowed
3479 
3480   should it return a full plane instead of a Z?
3481 ===============
3482 */
CG_PlayerShadow(centity_t * const cent,float * const shadowPlane)3483 static qboolean CG_PlayerShadow( centity_t *const cent, float *const shadowPlane ) {
3484 	*shadowPlane = 0;
3485 
3486 	if ( cg_shadows.integer == 0 ) {
3487 		return qfalse;
3488 	}
3489 
3490 	// no shadows when cloaked
3491 	if ( cent->currentState.powerups & ( 1 << PW_CLOAKED ))
3492 	{
3493 		return qfalse;
3494 	}
3495 
3496 	if ( cent->gent->client->NPC_class == CLASS_SAND_CREATURE )
3497 	{//sand creatures have no shadow
3498 		return qfalse;
3499 	}
3500 
3501 	vec3_t rootOrigin;
3502 	vec3_t tempAngles;
3503 	tempAngles[PITCH]	= 0;
3504 	tempAngles[YAW]		= cent->pe.legs.yawAngle;
3505 	tempAngles[ROLL]	= 0;
3506 	if (cent->gent->rootBone>=0 && cent->gent->ghoul2.IsValid() && cent->gent->ghoul2[0].animModelIndexOffset)//If it has an animOffset it's a cinematic anim
3507 	{	//i might be running out of my bounding box, so get my root origin
3508 		mdxaBone_t	boltMatrix;
3509 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->rootBone,
3510 				&boltMatrix, tempAngles, cent->lerpOrigin,
3511 				cg.time, cgs.model_draw, cent->currentState.modelScale);
3512 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, rootOrigin );
3513 	}
3514 	else
3515 	{
3516 		VectorCopy(cent->lerpOrigin,rootOrigin);
3517 	}
3518 
3519 	if ( DistanceSquared( cg.refdef.vieworg, rootOrigin ) > cg_shadowCullDistance.value * cg_shadowCullDistance.value )
3520 	{
3521 		// Shadow is too far away, don't do any traces, don't do any marks...blah
3522 		return qfalse;
3523 	}
3524 
3525 	if (cent->gent->client->NPC_class == CLASS_ATST)
3526 	{
3527 		qboolean bShadowed;
3528 		mdxaBone_t	boltMatrix;
3529 		vec3_t sideOrigin;
3530 
3531 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footLBolt,
3532 				&boltMatrix, tempAngles, cent->lerpOrigin,
3533 				cg.time, cgs.model_draw, cent->currentState.modelScale);
3534 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, sideOrigin );
3535 		sideOrigin[2] += 30;	//fudge up a bit for coplaner
3536 		bShadowed = _PlayerShadow(sideOrigin, 0, shadowPlane, 28, cgs.media.shadowMarkShader);
3537 
3538 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footRBolt,
3539 				&boltMatrix, tempAngles, cent->lerpOrigin, cg.time,
3540 				cgs.model_draw, cent->currentState.modelScale);
3541 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, sideOrigin );
3542 		sideOrigin[2] += 30;	//fudge up a bit for coplaner
3543 		bShadowed = (qboolean)(_PlayerShadow(sideOrigin, 0, shadowPlane, 28, cgs.media.shadowMarkShader) || bShadowed);
3544 
3545 		bShadowed = (qboolean)( _PlayerShadow(rootOrigin, cent->pe.legs.yawAngle, shadowPlane, 64, cgs.media.shadowMarkShader) || bShadowed);
3546 		return bShadowed;
3547 	}
3548 	else if ( cent->gent->client->NPC_class == CLASS_RANCOR )
3549 	{
3550 		return _PlayerShadow(rootOrigin, cent->pe.legs.yawAngle, shadowPlane, 64, cgs.media.shadowMarkShader);
3551 	}
3552 	else
3553 	{
3554 		return _PlayerShadow(rootOrigin, cent->pe.legs.yawAngle, shadowPlane, 16, cgs.media.shadowMarkShader);
3555 	}
3556 
3557 }
3558 
CG_LandingEffect(vec3_t origin,vec3_t normal,int material)3559 void CG_LandingEffect( vec3_t origin, vec3_t normal, int material )
3560 {
3561 	int	effectID = -1;
3562 	switch ( material )
3563 	{
3564 	case MATERIAL_MUD:
3565 		effectID = cgs.effects.landingMud;
3566 		break;
3567 	case MATERIAL_DIRT:
3568 		effectID = cgs.effects.landingDirt;
3569 		break;
3570 	case MATERIAL_SAND:
3571 		effectID = cgs.effects.landingSand;
3572 		break;
3573 	case MATERIAL_SNOW:
3574 		effectID = cgs.effects.landingSnow;
3575 		break;
3576 	case MATERIAL_GRAVEL:
3577 		effectID = cgs.effects.landingGravel;
3578 		break;
3579 	}
3580 
3581 	if ( effectID != -1 )
3582 	{
3583 		theFxScheduler.PlayEffect( effectID, origin, normal );
3584 	}
3585 }
3586 #define	FOOTSTEP_DISTANCE	32
_PlayerFootStep(const vec3_t origin,const vec3_t traceDir,const float orientation,const float radius,centity_t * const cent,footstepType_t footStepType)3587 static void _PlayerFootStep( const vec3_t origin,
3588 								const vec3_t traceDir,
3589 								const float orientation,
3590 								const float radius,
3591 								centity_t *const cent, footstepType_t footStepType )
3592 {
3593 	vec3_t		end, mins = {-7, -7, 0}, maxs = {7, 7, 2};
3594 	trace_t		trace;
3595 	footstep_t	soundType = FOOTSTEP_TOTAL;
3596 	bool		bMark = false;
3597 	int			effectID = -1;
3598 	//float		alpha;
3599 
3600 	// send a trace down from the player to the ground
3601 	VectorCopy( origin, end );
3602 	VectorMA( origin, FOOTSTEP_DISTANCE, traceDir, end );//was end[2] -= FOOTSTEP_DISTANCE;
3603 
3604 	cgi_CM_BoxTrace( &trace, origin, end, mins, maxs, 0, MASK_PLAYERSOLID );
3605 
3606 	// no shadow if too high
3607 	if ( trace.fraction >= 1.0f )
3608 	{
3609 		return;
3610 	}
3611 
3612 	//check for foot-steppable surface flag
3613 	switch( trace.surfaceFlags & MATERIAL_MASK )
3614 	{
3615 		case MATERIAL_MUD:
3616 			bMark = true;
3617 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3618 				soundType = FOOTSTEP_MUDRUN;
3619 			} else {
3620 				soundType = FOOTSTEP_MUDWALK;
3621 			}
3622 			effectID = cgs.effects.footstepMud;
3623 			break;
3624 		case MATERIAL_DIRT:
3625 			bMark = true;
3626 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3627 				soundType = FOOTSTEP_DIRTRUN;
3628 			} else {
3629 				soundType = FOOTSTEP_DIRTWALK;
3630 			}
3631 			effectID = cgs.effects.footstepSand;
3632 			break;
3633 		case MATERIAL_SAND:
3634 			bMark = true;
3635 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3636 				soundType = FOOTSTEP_SANDRUN;
3637 			} else {
3638 				soundType = FOOTSTEP_SANDWALK;
3639 			}
3640 			effectID = cgs.effects.footstepSand;
3641 			break;
3642 		case MATERIAL_SNOW:
3643 			bMark = true;
3644 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3645 				soundType = FOOTSTEP_SNOWRUN;
3646 			} else {
3647 				soundType = FOOTSTEP_SNOWWALK;
3648 			}
3649 			effectID = cgs.effects.footstepSnow;
3650 			break;
3651 		case MATERIAL_SHORTGRASS:
3652 		case MATERIAL_LONGGRASS:
3653 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3654 				soundType = FOOTSTEP_GRASSRUN;
3655 			} else {
3656 				soundType = FOOTSTEP_GRASSWALK;
3657 			}
3658 			break;
3659 		case MATERIAL_SOLIDMETAL:
3660 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3661 				soundType = FOOTSTEP_METALRUN;
3662 			} else {
3663 				soundType = FOOTSTEP_METALWALK;
3664 			}
3665 			break;
3666 		case MATERIAL_HOLLOWMETAL:
3667 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3668 				soundType = FOOTSTEP_PIPERUN;
3669 			} else {
3670 				soundType = FOOTSTEP_PIPEWALK;
3671 			}
3672 			break;
3673 		case MATERIAL_GRAVEL:
3674 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3675 				soundType = FOOTSTEP_GRAVELRUN;
3676 			} else {
3677 				soundType = FOOTSTEP_GRAVELWALK;
3678 			}
3679 			effectID = cgs.effects.footstepGravel;
3680 			break;
3681 		case MATERIAL_CARPET:
3682 		case MATERIAL_FABRIC:
3683 		case MATERIAL_CANVAS:
3684 		case MATERIAL_RUBBER:
3685 		case MATERIAL_PLASTIC:
3686 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3687 				soundType = FOOTSTEP_RUGRUN;
3688 			} else {
3689 				soundType = FOOTSTEP_RUGWALK;
3690 			}
3691 			break;
3692 		case MATERIAL_SOLIDWOOD:
3693 		case MATERIAL_HOLLOWWOOD:
3694 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3695 				soundType = FOOTSTEP_WOODRUN;
3696 			} else {
3697 				soundType = FOOTSTEP_WOODWALK;
3698 			}
3699 			break;
3700 
3701 		default:
3702 		//fall through
3703 		case MATERIAL_GLASS:
3704 		case MATERIAL_WATER:
3705 		case MATERIAL_FLESH:
3706 		case MATERIAL_BPGLASS:
3707 		case MATERIAL_DRYLEAVES:
3708 		case MATERIAL_GREENLEAVES:
3709 		case MATERIAL_TILES:
3710 		case MATERIAL_PLASTER:
3711 		case MATERIAL_SHATTERGLASS:
3712 		case MATERIAL_ARMOR:
3713 		case MATERIAL_COMPUTER:
3714 
3715 		case MATERIAL_CONCRETE:
3716 		case MATERIAL_ROCK:
3717 		case MATERIAL_ICE:
3718 		case MATERIAL_MARBLE:
3719 			if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
3720 				soundType = FOOTSTEP_STONERUN;
3721 			} else {
3722 				soundType = FOOTSTEP_STONEWALK;
3723 			}
3724 			break;
3725 	}
3726 
3727 	if (soundType < FOOTSTEP_TOTAL)
3728 	{
3729 	 	cgi_S_StartSound( NULL, cent->currentState.clientNum, CHAN_BODY, cgs.media.footsteps[soundType][Q_irand( 0, 3)] );
3730 	}
3731 
3732 	if ( cg_footsteps.integer < 4 )
3733 	{//debugging - 4 always does footstep effect
3734 		if ( cg_footsteps.integer < 2 )	//1 for sounds, 2 for effects, 3 for marks
3735 		{
3736 			return;
3737 		}
3738 	}
3739 
3740 	if ( effectID != -1 )
3741 	{
3742 		theFxScheduler.PlayEffect( effectID, trace.endpos, trace.plane.normal );
3743 	}
3744 
3745 	if ( cg_footsteps.integer < 4 )
3746 	{//debugging - 4 always does footprint decal
3747 		if (!bMark || cg_footsteps.integer < 3)	//1 for sounds, 2 for effects, 3 for marks
3748 		{
3749 			return;
3750 		}
3751 	}
3752 	qhandle_t footMarkShader;
3753 	switch ( footStepType )
3754 	{
3755 	case FOOTSTEP_HEAVY_R:
3756 		footMarkShader = cgs.media.fshrMarkShader;
3757 		break;
3758 	case FOOTSTEP_HEAVY_L:
3759 		footMarkShader = cgs.media.fshlMarkShader;
3760 		break;
3761 	case FOOTSTEP_R:
3762 		footMarkShader = cgs.media.fsrMarkShader;
3763 		break;
3764 	default:
3765 	case FOOTSTEP_L:
3766 		footMarkShader = cgs.media.fslMarkShader;
3767 		break;
3768 	}
3769 
3770 	// fade the shadow out with height
3771 //	alpha = 1.0 - trace.fraction;
3772 
3773 	// add the mark as a temporary, so it goes directly to the renderer
3774 	// without taking a spot in the cg_marks array
3775 
3776 	vec3_t		projNormal;
3777 	VectorCopy(trace.plane.normal,projNormal);
3778 	if (projNormal[2]>0.5f)
3779 	{
3780 		// footsteps will not have the correct orientation for all surfaces, so punt and set the projection to Z
3781 		projNormal[0]=0.0f;
3782 		projNormal[1]=0.0f;
3783 		projNormal[2]=1.0f;
3784 	}
3785 	CG_ImpactMark( footMarkShader, trace.endpos,projNormal,
3786 		orientation, 1,1,1, 1.0f, qfalse, radius, qfalse );
3787 }
3788 
3789 extern vmCvar_t	cg_footsteps;
CG_PlayerFootsteps(centity_t * const cent,footstepType_t footStepType)3790 static void CG_PlayerFootsteps( centity_t *const cent, footstepType_t footStepType )
3791 {
3792 	if ( cg_footsteps.integer == 0 )
3793 	{
3794 		return;
3795 	}
3796 
3797 	//FIXME: make this a feature of NPCs in the NPCs.cfg? Specify a footstep shader, if any?
3798 	if ( cent->gent->client->NPC_class != CLASS_ATST
3799 		&& cent->gent->client->NPC_class != CLASS_CLAW
3800 		&& cent->gent->client->NPC_class != CLASS_FISH
3801 		&& cent->gent->client->NPC_class != CLASS_FLIER2
3802 		&& cent->gent->client->NPC_class != CLASS_GLIDER
3803 		&& cent->gent->client->NPC_class != CLASS_INTERROGATOR
3804 		&& cent->gent->client->NPC_class != CLASS_MURJJ
3805 		&& cent->gent->client->NPC_class != CLASS_PROBE
3806 		&& cent->gent->client->NPC_class != CLASS_R2D2
3807 		&& cent->gent->client->NPC_class != CLASS_R5D2
3808 		&& cent->gent->client->NPC_class != CLASS_REMOTE
3809 		&& cent->gent->client->NPC_class != CLASS_SEEKER
3810 		&& cent->gent->client->NPC_class != CLASS_SENTRY
3811 		&& cent->gent->client->NPC_class != CLASS_SWAMP )
3812 	{
3813 		mdxaBone_t	boltMatrix;
3814 		vec3_t tempAngles, sideOrigin, footDownDir;
3815 
3816 		tempAngles[PITCH]	= 0;
3817 		tempAngles[YAW]		= cent->pe.legs.yawAngle;
3818 		tempAngles[ROLL]	= 0;
3819 
3820 		int footBolt = cent->gent->footLBolt;
3821 		if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_R)
3822 		{
3823 			footBolt = cent->gent->footRBolt;
3824 		}
3825 		//FIXME: get yaw orientation of the foot and use on decal
3826 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, footBolt,
3827 				&boltMatrix, tempAngles, cent->lerpOrigin,
3828 				cg.time, cgs.model_draw, cent->currentState.modelScale);
3829 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, sideOrigin );
3830 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, footDownDir );
3831 		VectorMA( sideOrigin, -8.0f, footDownDir, sideOrigin );//was [2] += 15;	//fudge up a bit for coplanar
3832 		_PlayerFootStep( sideOrigin, footDownDir, cent->pe.legs.yawAngle, 6, cent, footStepType );
3833 	}
3834 }
3835 
_PlayerSplash(const vec3_t origin,const vec3_t velocity,const float radius,const int maxUp)3836 static void _PlayerSplash( const vec3_t origin, const vec3_t velocity, const float radius, const int maxUp )
3837 {
3838 	static vec3_t WHITE={1,1,1};
3839 	vec3_t		start, end;
3840 	trace_t		trace;
3841 	int			contents;
3842 
3843 	VectorCopy( origin, end );
3844 	end[2] -= 24;
3845 
3846 	// if the feet aren't in liquid, don't make a mark
3847 	// this won't handle moving water brushes, but they wouldn't draw right anyway...
3848 	contents = cgi_CM_PointContents( end, 0 );
3849 	if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) )
3850 	{
3851 		return;
3852 	}
3853 
3854 	VectorCopy( origin, start );
3855 	if ( maxUp < 32 )
3856 	{//our head may actually be lower than 32 above our origin
3857 		start[2] += maxUp;
3858 	}
3859 	else
3860 	{
3861 		start[2] += 32;
3862 	}
3863 
3864 	// if the head isn't out of liquid, don't make a mark
3865 	contents = cgi_CM_PointContents( start, 0 );
3866 	if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
3867 	{
3868 		return;
3869 	}
3870 
3871 	// trace down to find the surface
3872 	cgi_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) );
3873 
3874 	if ( trace.fraction == 1.0 )
3875 	{
3876 		return;
3877 	}
3878 
3879 	VectorCopy( trace.endpos, end );
3880 
3881 	end[0] += Q_flrand(-1.0f, 1.0f) * 3.0f;
3882 	end[1] += Q_flrand(-1.0f, 1.0f) * 3.0f;
3883 	end[2] += 1.0f; //fudge up
3884 
3885 	int t = VectorLengthSquared( velocity );
3886 
3887 	if ( t > 8192 ) // oh, magic number
3888 	{
3889 		t = 8192;
3890 	}
3891 
3892 	float alpha = ( t / 8192.0f ) * 0.6f + 0.2f;
3893 
3894 	FX_AddOrientedParticle( -1, end, trace.plane.normal, NULL, NULL,
3895 								6.0f, radius + Q_flrand(0.0f, 1.0f) * 48.0f, 0,
3896 								alpha, 0.0f, 0.0f,
3897 								WHITE, WHITE, 0.0f,
3898 								Q_flrand(0.0f, 1.0f) * 360, Q_flrand(-1.0f, 1.0f) * 6.0f, NULL, NULL, 0.0f, 0 ,0, 1200,
3899 								cgs.media.wakeMarkShader, FX_ALPHA_LINEAR | FX_SIZE_LINEAR );
3900 }
3901 
3902 /*
3903 ===============
3904 CG_PlayerSplash
3905 
3906 Draw a mark at the water surface
3907 ===============
3908 */
CG_PlayerSplash(centity_t * cent)3909 static void CG_PlayerSplash( centity_t *cent )
3910 {
3911 	if ( !cg_shadows.integer )
3912 	{
3913 		return;
3914 	}
3915 
3916 	if ( cent->gent && cent->gent->client )
3917 	{
3918 		gclient_t *cl = cent->gent->client;
3919 
3920 		if ( cent->gent->disconnectDebounceTime < cg.time ) // can't do these expanding ripples all the time
3921 		{
3922 			if ( cl->NPC_class == CLASS_ATST )
3923 			{
3924 				mdxaBone_t	boltMatrix;
3925 				vec3_t		tempAngles, sideOrigin;
3926 
3927 				tempAngles[PITCH]	= 0;
3928 				tempAngles[YAW]		= cent->pe.legs.yawAngle;
3929 				tempAngles[ROLL]	= 0;
3930 
3931 				gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footLBolt,
3932 						&boltMatrix, tempAngles, cent->lerpOrigin,
3933 						cg.time, cgs.model_draw, cent->currentState.modelScale );
3934 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, sideOrigin );
3935 				sideOrigin[2] += 22;	//fudge up a bit for coplaner
3936 				_PlayerSplash( sideOrigin, cl->ps.velocity, 42, cent->gent->maxs[2] );
3937 
3938 				gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footRBolt,
3939 						&boltMatrix, tempAngles, cent->lerpOrigin, cg.time,
3940 						cgs.model_draw, cent->currentState.modelScale);
3941 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, sideOrigin );
3942 				sideOrigin[2] += 22;	//fudge up a bit for coplaner
3943 
3944 				_PlayerSplash( sideOrigin, cl->ps.velocity, 42, cent->gent->maxs[2] );
3945 			}
3946 			else
3947 			{
3948 				// player splash mark
3949 				_PlayerSplash( cent->lerpOrigin, cl->ps.velocity, 36, cl->renderInfo.eyePoint[2] - cent->lerpOrigin[2] + 5 );
3950 			}
3951 
3952 			cent->gent->disconnectDebounceTime = cg.time + 125 + Q_flrand(0.0f, 1.0f) * 50.0f;
3953 		}
3954 	}
3955 }
3956 
3957 
3958 /*
3959 ===============
3960 CG_LightningBolt
3961 ===============
3962 */
3963 #if 0
3964 static void CG_LightningBolt( centity_t *cent, vec3_t origin )
3965 {
3966 	// FIXME:  This sound also plays when the weapon first fires which causes little sputtering sounds..not exactly cool
3967 	// Must be currently firing
3968 	if ( !( cent->currentState.eFlags & EF_FIRING ) )
3969 		return;
3970 
3971 	//Must be a durational weapon
3972 //	if ( cent->currentState.weapon == WP_DEMP2 && cent->currentState.eFlags & EF_ALT_FIRING )
3973 //	{ /*nothing*/ }
3974 //	else
3975 	{
3976 		return;
3977 	}
3978 
3979 /*	trace_t		trace;
3980 	gentity_t	*traceEnt;
3981 	vec3_t		end, forward, org, angs;
3982 	qboolean	spark = qfalse, impact = qtrue;//, weak = qfalse;
3983 
3984 	// for lightning weapons coming from the player, it had better hit the crosshairs or else..
3985 	if ( cent->gent->s.number )
3986 	{
3987 		VectorCopy( origin, org );
3988 	}
3989 	else
3990 	{
3991 		VectorCopy( cg.refdef.vieworg, org );
3992 	}
3993 
3994 	// Find the impact point of the beam
3995 	VectorCopy( cent->lerpAngles, angs );
3996 
3997 	AngleVectors( angs, forward, NULL, NULL );
3998 
3999 	VectorMA( org, weaponData[cent->currentState.weapon].range, forward, end );
4000 
4001 	CG_Trace( &trace, org, vec3_origin, vec3_origin, end, cent->currentState.number, MASK_SHOT );
4002 	traceEnt = &g_entities[ trace.entityNum ];
4003 
4004 	// Make sparking be a bit less frame-rate dependent..also never add sparking when we hit a surface with a NOIMPACT flag
4005 	if ( cent->gent->fx_time < cg.time && !(trace.surfaceFlags & SURF_NOIMPACT ))
4006 	{
4007 		spark = qtrue;
4008 		cent->gent->fx_time = cg.time + Q_flrand(0.0f, 1.0f) * 100 + 100;
4009 	}
4010 
4011 	// Don't draw certain kinds of impacts when it hits a player and such..or when we hit a surface with a NOIMPACT flag
4012 	if ( (traceEnt->takedamage && traceEnt->client) || (trace.surfaceFlags & SURF_NOIMPACT) )
4013 	{
4014 		impact = qfalse;
4015 	}
4016 
4017 	// Add in the effect
4018 	switch ( cent->currentState.weapon )
4019 	{
4020 	case WP_DEMP2:
4021 //		vec3_t org;
4022 
4023 extern void FX_DEMP2_AltBeam( vec3_t start, vec3_t end, vec3_t normal, //qboolean spark,
4024 									vec3_t targ1, vec3_t targ2 );
4025 
4026 		// Move the beam back a bit to help cover up the poly edges on the fire beam
4027 //		VectorMA( origin, -4, forward, org );
4028 //		FIXME:  Looks and works like ASS, so don't let people see it until it improves
4029 		FX_DEMP2_AltBeam( origin, trace.endpos, trace.plane.normal, cent->gent->pos1, cent->gent->pos2 );
4030 		break;
4031 
4032 	}
4033 	*/
4034 }
4035 #endif
4036 
4037 //-------------------------------------------
4038 #define REFRACT_EFFECT_DURATION		500
4039 void CG_ForcePushBlur( const vec3_t org, qboolean darkSide = qfalse );
CG_ForcePushRefraction(vec3_t org,centity_t * cent)4040 static void CG_ForcePushRefraction( vec3_t org, centity_t *cent )
4041 {
4042 	refEntity_t ent;
4043 	vec3_t ang;
4044 	float scale;
4045 	float vLen;
4046 	float alpha;
4047 	int tDif;
4048 
4049 	if (!cg_renderToTextureFX.integer)
4050 	{
4051 		CG_ForcePushBlur(org);
4052 		return;
4053 	}
4054 
4055 	if (!cent->gent ||
4056 		!cent->gent->client)
4057 	{ //can only do this for player/npc's
4058 		return;
4059 	}
4060 
4061 	if (!cent->gent->client->pushEffectFadeTime)
4062 	{ //the duration for the expansion and fade
4063 		cent->gent->client->pushEffectFadeTime = cg.time + REFRACT_EFFECT_DURATION;
4064 	}
4065 
4066 	//closer tDif is to 0, the closer we are to
4067 	//being "done"
4068 	tDif = (cent->gent->client->pushEffectFadeTime - cg.time);
4069 	if ((REFRACT_EFFECT_DURATION-tDif) < 200)
4070 	{ //stop following the hand after a little and stay in a fixed spot
4071 		//save the initial spot of the effect
4072 		VectorCopy(org, cent->gent->client->pushEffectOrigin);
4073 	}
4074 
4075 	//scale from 1.0f to 0.1f then hold at 0.1 for the rest of the duration
4076 	if (cent->gent->client->ps.forcePowersActive & ( 1 << FP_PULL ) )
4077 	{
4078 		scale = (float)(REFRACT_EFFECT_DURATION-tDif)*0.003f;
4079 	}
4080 	else
4081 	{
4082 		scale = (float)(tDif)*0.003f;
4083 	}
4084 	if (scale > 1.0f)
4085 	{
4086 		scale = 1.0f;
4087 	}
4088 	else if (scale < 0.2f)
4089 	{
4090 		scale = 0.2f;
4091 	}
4092 
4093 	//start alpha at 244, fade to 10
4094 	alpha = (float)tDif*0.488f;
4095 
4096 	if (alpha > 244.0f)
4097 	{
4098 		alpha = 244.0f;
4099 	}
4100 	else if (alpha < 10.0f)
4101 	{
4102 		alpha = 10.0f;
4103 	}
4104 
4105 	memset( &ent, 0, sizeof( ent ) );
4106 	ent.shaderTime = (cent->gent->client->pushEffectFadeTime-REFRACT_EFFECT_DURATION) / 1000.0f;
4107 
4108 	VectorCopy( cent->gent->client->pushEffectOrigin, ent.origin );
4109 
4110 	VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]);
4111 	vLen = VectorLength(ent.axis[0]);
4112 	if (vLen <= 0.1f)
4113 	{	// Entity is right on vieworg.  quit.
4114 		return;
4115 	}
4116 
4117 	vectoangles(ent.axis[0], ang);
4118 	ang[ROLL] += 180.0f;
4119 
4120 	AnglesToAxis(ang, ent.axis);
4121 
4122 	//radius must be a power of 2, and is the actual captured texture size
4123 	if (vLen < 128)
4124 	{
4125 		ent.radius = 256;
4126 	}
4127 	else if (vLen < 256)
4128 	{
4129 		ent.radius = 128;
4130 	}
4131 	else if (vLen < 512)
4132 	{
4133 		ent.radius = 64;
4134 	}
4135 	else
4136 	{
4137 		ent.radius = 32;
4138 	}
4139 
4140 	VectorScale(ent.axis[0], scale, ent.axis[0]);
4141 	VectorScale(ent.axis[1], scale, ent.axis[1]);
4142 	VectorScale(ent.axis[2], scale, ent.axis[2]);
4143 
4144 	ent.hModel = cgs.media.halfShieldModel;
4145 	ent.customShader = cgs.media.refractShader;
4146 	ent.nonNormalizedAxes = qtrue;
4147 
4148 	//make it partially transparent so it blends with the background
4149 	ent.renderfx = (RF_DISTORTION|RF_ALPHA_FADE);
4150 	ent.shaderRGBA[0] = 255.0f;
4151 	ent.shaderRGBA[1] = 255.0f;
4152 	ent.shaderRGBA[2] = 255.0f;
4153 	ent.shaderRGBA[3] = alpha;
4154 
4155 
4156 	cgi_R_AddRefEntityToScene( &ent );
4157 }
4158 
CG_ForcePushBlur(const vec3_t org,qboolean darkSide)4159 void CG_ForcePushBlur( const vec3_t org, qboolean darkSide )
4160 {
4161 	localEntity_t	*ex;
4162 
4163 	ex = CG_AllocLocalEntity();
4164 	ex->leType = LE_PUFF;
4165 	ex->refEntity.reType = RT_SPRITE;
4166 	ex->radius = 2.0f;
4167 	ex->startTime = cg.time;
4168 	ex->endTime = ex->startTime + 120;
4169 	VectorCopy( org, ex->pos.trBase );
4170 	ex->pos.trTime = cg.time;
4171 	ex->pos.trType = TR_LINEAR;
4172 	VectorScale( cg.refdef.viewaxis[1], 55, ex->pos.trDelta );
4173 
4174 	if ( darkSide )
4175 	{//make it red
4176 		ex->color[0] = 60;
4177 		ex->color[1] = 8;
4178 		ex->color[2] = 8;
4179 	}
4180 	else
4181 	{//blue
4182 		ex->color[0] = 24;
4183 		ex->color[1] = 32;
4184 		ex->color[2] = 40;
4185 	}
4186 	ex->refEntity.customShader = cgi_R_RegisterShader( "gfx/effects/forcePush" );
4187 
4188 	ex = CG_AllocLocalEntity();
4189 	ex->leType = LE_PUFF;
4190 	ex->refEntity.reType = RT_SPRITE;
4191 	ex->refEntity.rotation = 180.0f;
4192 	ex->radius = 2.0f;
4193 	ex->startTime = cg.time;
4194 	ex->endTime = ex->startTime + 120;
4195 	VectorCopy( org, ex->pos.trBase );
4196 	ex->pos.trTime = cg.time;
4197 	ex->pos.trType = TR_LINEAR;
4198 	VectorScale( cg.refdef.viewaxis[1], -55, ex->pos.trDelta );
4199 
4200 	if ( darkSide )
4201 	{//make it red
4202 		ex->color[0] = 60;
4203 		ex->color[1] = 8;
4204 		ex->color[2] = 8;
4205 	}
4206 	else
4207 	{//blue
4208 		ex->color[0] = 24;
4209 		ex->color[1] = 32;
4210 		ex->color[2] = 40;
4211 	}
4212 	ex->refEntity.customShader = cgi_R_RegisterShader( "gfx/effects/forcePush" );
4213 }
4214 
CG_ForcePushBodyBlur(centity_t * cent,const vec3_t origin,vec3_t tempAngles)4215 static void CG_ForcePushBodyBlur( centity_t *cent, const vec3_t origin, vec3_t tempAngles )
4216 {
4217 	vec3_t fxOrg;
4218 	mdxaBone_t	boltMatrix;
4219 
4220 	// Head blur
4221 	CG_ForcePushBlur( cent->gent->client->renderInfo.eyePoint );
4222 
4223 	// Do a torso based blur
4224 	if (cent->gent->torsoBolt>=0)
4225 	{
4226 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->torsoBolt,
4227 						&boltMatrix, tempAngles, origin, cg.time,
4228 						cgs.model_draw, cent->currentState.modelScale);
4229 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4230 		CG_ForcePushBlur( fxOrg );
4231 	}
4232 
4233 	if (cent->gent->handRBolt>=0)
4234 	{
4235 		// Do a right-hand based blur
4236 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handRBolt,
4237 						&boltMatrix, tempAngles, origin, cg.time,
4238 						cgs.model_draw, cent->currentState.modelScale);
4239 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4240 		CG_ForcePushBlur( fxOrg );
4241 	}
4242 
4243 	if (cent->gent->handLBolt>=0)
4244 	{
4245 		// Do a left-hand based blur
4246 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handLBolt,
4247 						&boltMatrix, tempAngles, origin, cg.time,
4248 						cgs.model_draw, cent->currentState.modelScale);
4249 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4250 		CG_ForcePushBlur( fxOrg );
4251 	}
4252 
4253 	// Do the knees
4254 	if (cent->gent->kneeLBolt>=0)
4255 	{
4256 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->kneeLBolt,
4257 						&boltMatrix, tempAngles, origin, cg.time,
4258 						cgs.model_draw, cent->currentState.modelScale);
4259 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4260 		CG_ForcePushBlur( fxOrg );
4261 	}
4262 
4263 	if (cent->gent->kneeRBolt>=0)
4264 	{
4265 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->kneeRBolt,
4266 						&boltMatrix, tempAngles, origin, cg.time,
4267 						cgs.model_draw, cent->currentState.modelScale);
4268 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4269 		CG_ForcePushBlur( fxOrg );
4270 	}
4271 
4272 	if (cent->gent->elbowLBolt>=0)
4273 	{
4274 		// Do the elbows
4275 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowLBolt,
4276 						&boltMatrix, tempAngles, origin, cg.time,
4277 						cgs.model_draw, cent->currentState.modelScale);
4278 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4279 		CG_ForcePushBlur( fxOrg );
4280 	}
4281 	if (cent->gent->elbowRBolt>=0)
4282 	{
4283 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->elbowRBolt,
4284 						&boltMatrix, tempAngles, origin, cg.time,
4285 						cgs.model_draw, cent->currentState.modelScale);
4286 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4287 		CG_ForcePushBlur( fxOrg );
4288 	}
4289 }
4290 
CG_ForceElectrocution(centity_t * cent,const vec3_t origin,vec3_t tempAngles,qhandle_t shader,qboolean alwaysDo=qfalse)4291 static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t tempAngles, qhandle_t shader, qboolean alwaysDo = qfalse )
4292 {
4293 	// Undoing for now, at least this code should compile if I ( or anyone else ) decides to work on this effect
4294 	qboolean	found = qfalse;
4295 	vec3_t		fxOrg, fxOrg2, dir;
4296 	vec3_t		rgb = {1.0f,1.0f,1.0f};
4297 	mdxaBone_t	boltMatrix;
4298 
4299 	int bolt=-1;
4300 	int iter=0;
4301 	// Pick a random start point
4302 	while (bolt<0)
4303 	{
4304 		int test;
4305 		if (iter>5)
4306 		{
4307 			test=iter-5;
4308 		}
4309 		else
4310 		{
4311 			test=Q_irand(0,6);
4312 		}
4313 		switch(test)
4314 		{
4315 		case 0:
4316 			// Right Elbow
4317 			bolt=cent->gent->elbowRBolt;
4318 			break;
4319 		case 1:
4320 			// Left Hand
4321 			bolt=cent->gent->handLBolt;
4322 			break;
4323 		case 2:
4324 			// Right hand
4325 			bolt=cent->gent->handRBolt;
4326 			break;
4327 		case 3:
4328 			// Left Foot
4329 			bolt=cent->gent->footLBolt;
4330 			break;
4331 		case 4:
4332 			// Right foot
4333 			bolt=cent->gent->footRBolt;
4334 			break;
4335 		case 5:
4336 			// Torso
4337 			bolt=cent->gent->torsoBolt;
4338 			break;
4339 		case 6:
4340 		default:
4341 			// Left Elbow
4342 			bolt=cent->gent->elbowLBolt;
4343 			break;
4344 		}
4345 		if (++iter==20)
4346 			break;
4347 	}
4348 	if (bolt>=0)
4349 	{
4350 		found = gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, bolt,
4351 				&boltMatrix, tempAngles, origin, cg.time,
4352 				cgs.model_draw, cent->currentState.modelScale);
4353 	}
4354 	// Make sure that it's safe to even try and get these values out of the Matrix, otherwise the values could be garbage
4355 	if ( found )
4356 	{
4357 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4358 		if ( Q_flrand(0.0f, 1.0f) > 0.5f )
4359 		{
4360 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_X, dir );
4361 		}
4362 		else
4363 		{
4364 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, dir );
4365 		}
4366 
4367 		// Add some fudge, makes us not normalized, but that isn't really important
4368 		dir[0] += Q_flrand(-1.0f, 1.0f) * 0.4f;
4369 		dir[1] += Q_flrand(-1.0f, 1.0f) * 0.4f;
4370 		dir[2] += Q_flrand(-1.0f, 1.0f) * 0.4f;
4371 	}
4372 	else
4373 	{
4374 		// Just use the lerp Origin and a random direction
4375 		VectorCopy( cent->lerpOrigin, fxOrg );
4376 		VectorSet( dir, Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f) ); // Not normalized, but who cares.
4377 		if ( cent->gent && cent->gent->client )
4378 		{
4379 			switch ( cent->gent->client->NPC_class )
4380 			{
4381 			case CLASS_PROBE:
4382 				fxOrg[2] += 50;
4383 				break;
4384 			case CLASS_MARK1:
4385 				fxOrg[2] += 50;
4386 				break;
4387 			case CLASS_ATST:
4388 				fxOrg[2] += 120;
4389 				break;
4390 			default:
4391 				break;
4392 			}
4393 		}
4394 	}
4395 
4396 	VectorMA( fxOrg, Q_flrand(0.0f, 1.0f) * 40 + 40, dir, fxOrg2 );
4397 
4398 	trace_t	tr;
4399 
4400 	CG_Trace( &tr, fxOrg, NULL, NULL, fxOrg2, -1, CONTENTS_SOLID );
4401 
4402 	if ( tr.fraction < 1.0f || Q_flrand(0.0f, 1.0f) > 0.94f || alwaysDo )
4403 	{
4404 		FX_AddElectricity( -1, fxOrg, tr.endpos,
4405 			1.5f, 4.0f, 0.0f,
4406 			1.0f, 0.5f, 0.0f,
4407 			rgb, rgb, 0.0f,
4408 			5.5f, Q_flrand(0.0f, 1.0f) * 50 + 100, shader, FX_ALPHA_LINEAR | FX_SIZE_LINEAR | FX_BRANCH | FX_GROW | FX_TAPER, -1, -1 );
4409 	}
4410 }
4411 
CG_BoltedEffects(centity_t * cent,const vec3_t origin,vec3_t tempAngles)4412 static void CG_BoltedEffects( centity_t *cent, const vec3_t origin, vec3_t tempAngles )
4413 {
4414 	if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_VEHICLE )
4415 	{
4416 		Vehicle_t *pVeh = cent->gent->m_pVehicle;
4417 		gentity_t *parent = cent->gent;
4418  		if (pVeh->m_ulFlags&VEH_ARMORLOW
4419 			&& (pVeh->m_iLastFXTime<=cg.time)
4420 			&& Q_irand(0,1)==0 )
4421 		{
4422 			pVeh->m_iLastFXTime = cg.time + 50;//Q_irand(50, 100);
4423 			CG_PlayEffectIDBolted(pVeh->m_pVehicleInfo->iArmorLowFX, parent->playerModel, parent->crotchBolt, parent->s.number, parent->currentOrigin);
4424 		}
4425 	}
4426 }
4427 
4428 /*
4429 ===============
4430 CG_PlayerCanSeeCent
4431 
4432 tests force sight level
4433 ===============
4434 */
CG_PlayerCanSeeCent(centity_t * cent)4435 qboolean CG_PlayerCanSeeCent( centity_t *cent )
4436 {//return true if this cent is in view
4437 	//NOTE: this is similar to the func SV_PlayerCanSeeEnt in sv_snapshot
4438 	if ( (cent->currentState.eFlags&EF_FORCE_VISIBLE) )
4439 	{//can always be seen
4440 		return qtrue;
4441 	}
4442 
4443 	if ( g_entities[0].client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2
4444 		&& cent->currentState.eType != ET_PLAYER )
4445 	{//TEST: level 1 only sees force hints and enemies
4446 		return qfalse;
4447 	}
4448 
4449 	float dot = 0.25f;//1.0f;
4450 	float range = 512.0f;
4451 
4452 	switch ( g_entities[0].client->ps.forcePowerLevel[FP_SEE] )
4453 	{
4454 	case FORCE_LEVEL_1:
4455 		//dot = 0.95f;
4456 		range = 1024.0f;
4457 		break;
4458 	case FORCE_LEVEL_2:
4459 		//dot = 0.7f;
4460 		range = 2048.0f;
4461 		break;
4462 	case FORCE_LEVEL_3:
4463 	case FORCE_LEVEL_4:
4464 	case FORCE_LEVEL_5:
4465 		//dot = 0.4f;
4466 		range = 4096.0f;
4467 		break;
4468 	}
4469 
4470 	vec3_t	centDir, lookDir;
4471 	VectorSubtract( cent->lerpOrigin, cg.refdef.vieworg, centDir );
4472 	float centDist = VectorNormalize( centDir );
4473 
4474 	if ( centDist < 128.0f )
4475 	{//can always see them if they're really close
4476 		return qtrue;
4477 	}
4478 
4479 	if ( centDist > range )
4480 	{//too far away to see them
4481 		return qfalse;
4482 	}
4483 
4484 	dot += (0.99f-dot)*centDist/range;//the farther away they are, the more in front they have to be
4485 
4486 	AngleVectors( cg.refdefViewAngles, lookDir, NULL, NULL );
4487 
4488 	if ( DotProduct( centDir, lookDir ) < dot )
4489 	{//not in force sight cone
4490 		return qfalse;
4491 	}
4492 
4493 	return qtrue;
4494 }
4495 
4496 /*
4497 ===============
4498 CG_AddForceSightShell
4499 
4500 Adds the special effect
4501 ===============
4502 */
4503 extern void CG_AddHealthBarEnt( int entNum );
CG_AddForceSightShell(refEntity_t * ent,centity_t * cent)4504 void CG_AddForceSightShell( refEntity_t *ent, centity_t *cent )
4505 {
4506 	ent->customShader = cgs.media.forceShell;
4507 	ent->renderfx &= ~RF_RGB_TINT;
4508 	// See through walls.
4509 	ent->renderfx |= (RF_MORELIGHT|RF_NODEPTH);
4510 
4511 	if ( (cent->currentState.eFlags&EF_FORCE_VISIBLE)
4512 		|| (cent->currentState.eType == ET_PLAYER && cent->gent && cent->gent->message) )
4513 	{
4514 		ent->shaderRGBA[0] = 0;
4515 		ent->shaderRGBA[1] = 0;
4516 		ent->shaderRGBA[2] = 255;
4517 		ent->shaderRGBA[3] = 254;
4518 
4519 		cgi_R_AddRefEntityToScene( ent );
4520 		return;
4521 	}
4522 
4523 	ent->shaderRGBA[0] = 255;
4524 	ent->shaderRGBA[1] = 255;
4525 	ent->shaderRGBA[2] = 0;
4526 
4527 	//if ( g_entities[0].client->ps.forcePowerLevel[FP_SEE] > FORCE_LEVEL_2 )
4528 	//{TEST: level 3 identifies friend or foe with color
4529 		team_t team = TEAM_NEUTRAL;
4530 		if ( cent->gent && cent->gent->client )
4531 		{
4532 			team = cent->gent->client->playerTeam;
4533 		}
4534 		else if ( cent->gent && cent->gent->owner )
4535 		{
4536 			if ( cent->gent->owner->client )
4537 			{
4538 				team = cent->gent->owner->client->playerTeam;
4539 			}
4540 			else
4541 			{
4542 				team = cent->gent->owner->noDamageTeam;
4543 			}
4544 		}
4545 		switch ( team )
4546 		{
4547 		case TEAM_ENEMY:
4548 			ent->shaderRGBA[0] = 255;
4549 			ent->shaderRGBA[1] = 0;
4550 			ent->shaderRGBA[2] = 0;
4551 			break;
4552 		case TEAM_PLAYER:
4553 			ent->shaderRGBA[0] = 0;
4554 			ent->shaderRGBA[1] = 255;
4555 			ent->shaderRGBA[2] = 0;
4556 			break;
4557 		case TEAM_FREE:
4558 			if ( cent->gent && cent->gent->client )
4559 			{
4560 				if ( cent->gent->client->NPC_class == CLASS_TUSKEN
4561 					|| cent->gent->client->NPC_class == CLASS_RANCOR
4562 					|| cent->gent->client->NPC_class == CLASS_WAMPA
4563 					|| cent->gent->client->NPC_class == CLASS_SAND_CREATURE )
4564 				{
4565 					ent->shaderRGBA[0] = 255;
4566 					ent->shaderRGBA[1] = 0;
4567 					ent->shaderRGBA[2] = 0;
4568 				}
4569 			}
4570 			break;
4571 		default:
4572 			break;
4573 		}
4574 
4575 	if ( g_entities[0].client->ps.forcePowerLevel[FP_SEE] > FORCE_LEVEL_2 )
4576 	{//TEST: level 3 also displays health
4577 		if ( cent->gent && cent->gent->health > 0 && cent->gent->max_health > 0 )
4578 		{//draw a health bar over them
4579 			CG_AddHealthBarEnt( cent->currentState.clientNum );
4580 		}
4581 	}
4582 
4583 	/*
4584 	if ( g_entities[0].client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 )
4585 	{ //only level 2+ can see players through walls
4586 		ent->renderfx &= ~RF_NODEPTH;
4587 	}
4588 	*/
4589 
4590 	//FIXME: make it darker or more translucent the further away it is?
4591 
4592 	cgi_R_AddRefEntityToScene( ent );
4593 }
4594 
4595 /*
4596 ===============
4597 CG_AddRefEntityWithPowerups
4598 
4599 Adds a piece with modifications or duplications for powerups
4600 ===============
4601 */
CG_AddRefEntityWithPowerups(refEntity_t * ent,int powerups,centity_t * cent)4602 void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cent )
4603 {
4604 	if ( !cent )
4605 	{
4606 		cgi_R_AddRefEntityToScene( ent );
4607 		return;
4608 	}
4609 	gentity_t *gent  = cent->gent;
4610 	if ( !gent )
4611 	{
4612 		cgi_R_AddRefEntityToScene( ent );
4613 		return;
4614 	}
4615 	if ( gent->client->ps.powerups[PW_DISRUPTION] < cg.time )
4616 	{//disruptor
4617 		if (( powerups & ( 1 << PW_DISRUPTION )))
4618 		{
4619 			//stop drawing him after this effect
4620 			gent->client->ps.eFlags |= EF_NODRAW;
4621 			return;
4622 		}
4623 	}
4624 
4625 //	if ( gent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 )
4626 //	{
4627 //		centity_t *cent = &cg_entities[gent->s.number];
4628 //		cgi_S_AddLoopingSound( 0, cent->lerpOrigin, vec3_origin, cgs.media.overchargeLoopSound );
4629 //	}
4630 
4631 	//get the dude's color choice in
4632 	ent->shaderRGBA[0] = gent->client->renderInfo.customRGBA[0];
4633 	ent->shaderRGBA[1] = gent->client->renderInfo.customRGBA[1];
4634 	ent->shaderRGBA[2] = gent->client->renderInfo.customRGBA[2];
4635 	ent->shaderRGBA[3] = gent->client->renderInfo.customRGBA[3];
4636 
4637 	// If certain states are active, we don't want to add in the regular body
4638 	if ( !gent->client->ps.powerups[PW_CLOAKED] &&
4639 		!gent->client->ps.powerups[PW_UNCLOAKING] &&
4640 		!gent->client->ps.powerups[PW_DISRUPTION] )
4641 	{
4642 		cgi_R_AddRefEntityToScene( ent );
4643 	}
4644 
4645 	// Disruptor Gun Alt-fire
4646 	if ( gent->client->ps.powerups[PW_DISRUPTION] )
4647 	{
4648 		// I guess when something dies, it looks like pos1 gets set to the impact point on death, we can do fun stuff with this
4649 		vec3_t tempAng;
4650 		VectorSubtract( gent->pos1, ent->origin, ent->oldorigin );
4651 		//er, adjust this to get the proper position in model space... account for yaw
4652 		float tempLength = VectorNormalize( ent->oldorigin );
4653 		vectoangles( ent->oldorigin, tempAng );
4654 		tempAng[YAW] -= gent->client->ps.viewangles[YAW];
4655 		AngleVectors( tempAng, ent->oldorigin, NULL, NULL );
4656 		VectorScale( ent->oldorigin, tempLength, ent->oldorigin );
4657 
4658 		ent->endTime = gent->fx_time;
4659 		ent->renderfx |= (RF_DISINTEGRATE2);
4660 
4661 		ent->customShader = cgi_R_RegisterShader( "gfx/effects/burn" );
4662 		cgi_R_AddRefEntityToScene( ent );
4663 
4664 		ent->renderfx &= ~(RF_DISINTEGRATE2);
4665 		ent->renderfx |= (RF_DISINTEGRATE1);
4666 		ent->customShader = 0;
4667 		cgi_R_AddRefEntityToScene( ent );
4668 
4669 		if ( cg.time - ent->endTime < 1000 && (cg_timescale.value * cg_timescale.value * Q_flrand(0.0f, 1.0f)) > 0.05f )
4670 		{
4671 			vec3_t fxOrg;
4672 			mdxaBone_t	boltMatrix;
4673 
4674 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, gent->playerModel, gent->torsoBolt,
4675 					&boltMatrix, gent->currentAngles, ent->origin, cg.time,
4676 					cgs.model_draw, gent->s.modelScale);
4677 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg );
4678 
4679 			VectorMA( fxOrg, -18, cg.refdef.viewaxis[0], fxOrg );
4680 			fxOrg[2] += Q_flrand(-1.0f, 1.0f) * 20;
4681 			theFxScheduler.PlayEffect( "disruptor/death_smoke", fxOrg );
4682 
4683 			if ( Q_flrand(0.0f, 1.0f) > 0.5f )
4684 			{
4685 				theFxScheduler.PlayEffect( "disruptor/death_smoke", fxOrg );
4686 			}
4687 		}
4688 	}
4689 
4690 	// Cloaking & Uncloaking Technology
4691 	//----------------------------------------
4692 
4693 	if (( powerups & ( 1 << PW_UNCLOAKING )))
4694 	{//in the middle of cloaking
4695 		if ((cg.snap->ps.forcePowersActive & (1 << FP_SEE))
4696 			&& cg.snap->ps.clientNum != cent->currentState.number
4697 			&& CG_PlayerCanSeeCent( cent ))
4698 		{//just draw him
4699 			cgi_R_AddRefEntityToScene( ent );
4700 		}
4701 		else
4702 		{
4703 			float perc = (float)(gent->client->ps.powerups[PW_UNCLOAKING] - cg.time) / 2000.0f;
4704 			if (( powerups & ( 1 << PW_CLOAKED )))
4705 			{//actually cloaking, so reverse it
4706 				perc = 1.0f - perc;
4707 			}
4708 
4709 			if ( perc >= 0.0f && perc <= 1.0f )
4710 			{
4711 				ent->renderfx &= ~RF_ALPHA_FADE;
4712 				ent->renderfx |= RF_RGB_TINT;
4713 				ent->shaderRGBA[0] = ent->shaderRGBA[1] = ent->shaderRGBA[2] = 255.0f * perc;
4714 				ent->shaderRGBA[3] = 0;
4715 				ent->customShader = cgs.media.cloakedShader;
4716 				cgi_R_AddRefEntityToScene( ent );
4717 
4718 				ent->shaderRGBA[0] = ent->shaderRGBA[1] = ent->shaderRGBA[2] = 255;
4719 				ent->shaderRGBA[3] = 255 * (1.0f - perc); // let model alpha in
4720 				ent->customShader = 0; // use regular skin
4721 				ent->renderfx &= ~RF_RGB_TINT;
4722 				ent->renderfx |= RF_ALPHA_FADE;
4723 				cgi_R_AddRefEntityToScene( ent );
4724 			}
4725 		}
4726 	}
4727 	else if (( powerups & ( 1 << PW_CLOAKED )))
4728 	{//fully cloaked
4729 		if ((cg.snap->ps.forcePowersActive & (1 << FP_SEE))
4730 			&& cg.snap->ps.clientNum != cent->currentState.number
4731 			&& CG_PlayerCanSeeCent( cent ))
4732 		{//just draw him
4733 			cgi_R_AddRefEntityToScene( ent );
4734 		}
4735 		else
4736 		{
4737 			if (cg_renderToTextureFX.integer && cg_shadows.integer != 2 && cgs.glconfig.stencilBits >= 4)
4738 			{
4739 				cgi_R_SetRefractProp(1.0f, 0.0f, qfalse, qfalse); //don't need to do this every frame.. but..
4740 				ent->customShader = 2; //crazy "refractive" shader
4741 				cgi_R_AddRefEntityToScene( ent );
4742 				ent->customShader = 0;
4743 			}
4744 			else
4745 			{ //stencil buffer's in use, sorry
4746 				ent->renderfx = 0;//&= ~(RF_RGB_TINT|RF_ALPHA_FADE);
4747 				ent->shaderRGBA[0] = ent->shaderRGBA[1] = ent->shaderRGBA[2] = ent->shaderRGBA[3] = 255;
4748 				ent->customShader = cgs.media.cloakedShader;
4749 				cgi_R_AddRefEntityToScene( ent );
4750 			}
4751 		}
4752 	}
4753 
4754 	// Electricity
4755 	//------------------------------------------------
4756 	if ( (powerups & ( 1 << PW_SHOCKED )) )
4757 	{
4758 		int	dif = gent->client->ps.powerups[PW_SHOCKED] - cg.time;
4759 
4760 		if ( dif > 0 && Q_flrand(0.0f, 1.0f) > 0.4f )
4761 		{
4762 			// fade out over the last 500 ms
4763 			int brightness = 255;
4764 
4765 			if ( dif < 500 )
4766 			{
4767 				brightness = floor((dif - 500.0f) / 500.0f * 255.0f );
4768 			}
4769 
4770 			ent->renderfx |= RF_RGB_TINT;
4771 			ent->shaderRGBA[0] = ent->shaderRGBA[1] = ent->shaderRGBA[2] = brightness;
4772 			ent->shaderRGBA[3] = 255;
4773 
4774 			if ( rand() & 1 )
4775 			{
4776 				ent->customShader = cgs.media.electricBodyShader;
4777 			}
4778 			else
4779 			{
4780 				ent->customShader = cgs.media.electricBody2Shader;
4781 			}
4782 
4783 			cgi_R_AddRefEntityToScene( ent );
4784 
4785 			if ( Q_flrand(0.0f, 1.0f) > 0.9f )
4786 				cgi_S_StartSound ( ent->origin, gent->s.number, CHAN_AUTO, cgi_S_RegisterSound( "sound/effects/energy_crackle.wav" ) );
4787 		}
4788 	}
4789 
4790 	// FORCE speed does blur trails
4791 	//------------------------------------------------------
4792 	if ( cg_speedTrail.integer
4793 		&& (gent->client->ps.forcePowersActive & (1 << FP_SPEED) //in force speed
4794 		|| cent->gent->client->ps.legsAnim == BOTH_FORCELONGLEAP_START//or force long jump - FIXME: only 1st half of that anim?
4795 		|| cent->gent->client->ps.legsAnim == BOTH_FORCELONGLEAP_ATTACK )//or force long jump attack
4796 		&& (gent->s.number || cg.renderingThirdPerson) ) // looks dumb doing this with first peron mode on
4797 	{
4798 		//FIXME: debounce this
4799 		localEntity_t	*ex;
4800 
4801 		ex = CG_AllocLocalEntity();
4802 		ex->leType = LE_FADE_MODEL;
4803 		memcpy( &ex->refEntity, ent, sizeof( refEntity_t ));
4804 
4805 		ex->refEntity.renderfx |= (RF_ALPHA_FADE | RF_NOSHADOW | RF_G2MINLOD ) ;
4806 		//ex->refEntity.renderfx |= RF_ALPHA_FADE;
4807 		ex->startTime = cg.time;
4808 		ex->endTime = ex->startTime + 75;
4809 		VectorCopy( ex->refEntity.origin, ex->pos.trBase );
4810 		VectorClear( ex->pos.trDelta );
4811 
4812 		if ( gent->client->renderInfo.customRGBA[0]
4813 			|| gent->client->renderInfo.customRGBA[1]
4814 			|| gent->client->renderInfo.customRGBA[2] )
4815 		{
4816 			ex->color[0] = gent->client->renderInfo.customRGBA[0];
4817 			ex->color[1] = gent->client->renderInfo.customRGBA[1];
4818 			ex->color[2] = gent->client->renderInfo.customRGBA[2];
4819 		}
4820 		else
4821 		{
4822 			ex->color[0] = ex->color[1] = ex->color[2] = 255.0f;
4823 		}
4824 		ex->color[3] = 50.0f;
4825 	}
4826 
4827 	// Personal Shields
4828 	//------------------------
4829 	if ( powerups & ( 1 << PW_BATTLESUIT ))
4830 	{
4831 		float diff = gent->client->ps.powerups[PW_BATTLESUIT] - cg.time;
4832 		float t;
4833 
4834 		if ( diff > 0 )
4835 		{
4836 			t = 1.0f - ( diff / (ARMOR_EFFECT_TIME * 2.0f));
4837 			// Only display when we have damage
4838 			if ( t < 0.0f || t > 1.0f )
4839 			{
4840 			}
4841 			else
4842 			{
4843 				ent->shaderRGBA[0] = ent->shaderRGBA[1] = ent->shaderRGBA[2] = 255.0f * t;
4844 				ent->shaderRGBA[3] = 255;
4845 				ent->renderfx &= ~RF_ALPHA_FADE;
4846 				ent->renderfx |= RF_RGB_TINT;
4847 				ent->customShader = cgs.media.personalShieldShader;
4848 
4849 				cgi_R_AddRefEntityToScene( ent );
4850 			}
4851 		}
4852 	}
4853 
4854 	// Galak Mech shield bubble
4855 	//------------------------------------------------------
4856 	if ( powerups & ( 1 << PW_GALAK_SHIELD ))
4857 	{
4858 /*		refEntity_t tent;
4859 
4860 		memset( &tent, 0, sizeof( refEntity_t ));
4861 
4862 		tent.reType = RT_LATHE;
4863 
4864 		// Setting up the 2d control points, these get swept around to make a 3D lathed model
4865 		VectorSet2( tent.axis[0], 0.5, 0 );		// start point of curve
4866 		VectorSet2( tent.axis[1], 50,	85 );		// control point 1
4867 		VectorSet2( tent.axis[2], 135, -100 );		// control point 2
4868 		VectorSet2( tent.oldorigin, 0, -90 );		// end point of curve
4869 
4870 		if ( gent->client->poisonTime && gent->client->poisonTime + 1000 > cg.time )
4871 		{
4872 			VectorCopy( gent->pos4, tent.lightingOrigin );
4873 			tent.frame = gent->client->poisonTime;
4874 		}
4875 
4876 		mdxaBone_t	boltMatrix;
4877 		vec3_t angles = {0,gent->client->ps.legsYaw,0};
4878 
4879 		gi.G2API_GetBoltMatrix( cent->gent->ghoul2, gent->playerModel, gent->genericBolt1, &boltMatrix, angles, cent->lerpOrigin, cg.time, cgs.model_draw, cent->currentState.modelScale );
4880 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, tent.origin );// pass in the emitter origin here
4881 
4882 		tent.endTime = gent->fx_time + 1000;			// if you want the shell to build around the guy, pass in a time that is 1000ms after the start of the turn-on-effect
4883 		tent.customShader = cgi_R_RegisterShader( "gfx/effects/irid_shield" );
4884 
4885 		cgi_R_AddRefEntityToScene( &tent );*/
4886 	}
4887 
4888 	// Invincibility -- effect needs work
4889 	//------------------------------------------------------
4890 	/*
4891 	if ( powerups & ( 1 << PW_INVINCIBLE ))
4892 	{
4893 		theFxScheduler.PlayEffect( cgs.effects.forceInvincibility, cent->lerpOrigin );
4894 	}
4895 	*/
4896 
4897 	// Healing -- could use some work....maybe also make it NOT be framerate dependant
4898 	//------------------------------------------------------
4899 /*	if ( powerups & ( 1 << PW_HEALING ))
4900 	{
4901 		vec3_t axis[3];
4902 
4903 		AngleVectors( cent->gent->client->renderInfo.eyeAngles, axis[0], axis[1], axis[2] );
4904 
4905 		theFxScheduler.PlayEffect( cgs.effects.forceHeal, cent->gent->client->renderInfo.eyePoint, axis );
4906 	}
4907 */
4908 	// Push Blur
4909 	if ( gent->forcePushTime > cg.time && gi.G2API_HaveWeGhoul2Models( cent->gent->ghoul2 ) )
4910 	{
4911 		CG_ForcePushBlur( ent->origin );
4912 	}
4913 
4914 	//new Jedi Academy force powers
4915 	//Rage effect
4916 	if ((cent->gent->client->ps.forcePowersActive & (1 << FP_RAGE)) &&
4917 		(cg.renderingThirdPerson || cent->currentState.number != cg.snap->ps.clientNum))
4918 	{
4919 		//ent->renderfx &= ~RF_FORCE_ENT_ALPHA;
4920 		//ent->renderfx &= ~RF_MINLIGHT;
4921 
4922 		ent->renderfx |= RF_RGB_TINT;
4923 		ent->shaderRGBA[0] = 255;
4924 		ent->shaderRGBA[1] = ent->shaderRGBA[2] = 0;
4925 		ent->shaderRGBA[3] = 255;
4926 
4927 		if ( rand() & 1 )
4928 		{
4929 			ent->customShader = cgs.media.electricBodyShader;
4930 		}
4931 		else
4932 		{
4933 			ent->customShader = cgs.media.electricBody2Shader;
4934 		}
4935 
4936 		cgi_R_AddRefEntityToScene( ent);
4937 	}
4938 
4939 	//FIXME: Tavion possessed effect?  White?
4940 	//For now, these two are using the old shield shader. This is just so that you
4941 	//can tell it apart from the JM/duel shaders, but it's still very obvious.
4942 	if ( (cent->gent->client->ps.forcePowersActive & (1 << FP_PROTECT))
4943 		&& (cent->gent->client->ps.forcePowersActive & (1 << FP_ABSORB)) )
4944 	{//using both at once, save ourselves some rendering
4945 		//protect+absorb is represented by cyan..
4946 		ent->shaderRGBA[0] = 0;
4947 		ent->shaderRGBA[1] = 255;
4948 		ent->shaderRGBA[2] = 255;
4949 		ent->shaderRGBA[3] = 254;
4950 
4951 		ent->renderfx &= ~RF_RGB_TINT;
4952 		//ent->renderfx &= ~RF_FORCE_ENT_ALPHA;
4953 		if ( cent->gent->client->ps.forcePowerLevel[FP_PROTECT] > FORCE_LEVEL_1
4954 			|| cent->gent->client->ps.forcePowerLevel[FP_ABSORB] > FORCE_LEVEL_1 )
4955 		{
4956 			ent->customShader = cgs.media.forceShell;
4957 		}
4958 		else
4959 		{
4960 			ent->customShader = cgs.media.playerShieldDamage;
4961 		}
4962 
4963 		cgi_R_AddRefEntityToScene( ent );
4964 	}
4965 	else if ( cent->gent->client->ps.forcePowersActive & (1 << FP_PROTECT) )
4966 	{ //protect is represented by green..
4967 		ent->shaderRGBA[0] = 0;
4968 		ent->shaderRGBA[1] = 255;
4969 		ent->shaderRGBA[2] = 0;
4970 		ent->shaderRGBA[3] = 254;
4971 
4972 		ent->renderfx &= ~RF_RGB_TINT;
4973 		//ent->renderfx &= ~RF_FORCE_ENT_ALPHA;
4974 		if ( cent->gent->client->ps.forcePowerLevel[FP_PROTECT] > FORCE_LEVEL_1 )
4975 		{
4976 			ent->customShader = cgs.media.forceShell;
4977 		}
4978 		else
4979 		{
4980 			ent->customShader = cgs.media.playerShieldDamage;
4981 		}
4982 
4983 		cgi_R_AddRefEntityToScene( ent );
4984 	}
4985 	else if ( cent->gent->client->ps.forcePowersActive & (1 << FP_ABSORB))
4986 	{ //absorb is represented by blue..
4987 		ent->shaderRGBA[0] = 0;
4988 		ent->shaderRGBA[1] = 0;
4989 		ent->shaderRGBA[2] = 255;
4990 		ent->shaderRGBA[3] = 254;
4991 
4992 		ent->renderfx &= ~RF_RGB_TINT;
4993 		//ent->renderfx &= ~RF_FORCE_ENT_ALPHA;
4994 		if ( cent->gent->client->ps.forcePowerLevel[FP_ABSORB] > FORCE_LEVEL_1 )
4995 		{
4996 			ent->customShader = cgs.media.forceShell;
4997 		}
4998 		else
4999 		{
5000 			ent->customShader = cgs.media.playerShieldDamage;
5001 		}
5002 
5003 		cgi_R_AddRefEntityToScene( ent );
5004 	}
5005 
5006 	if ((cg.snap->ps.forcePowersActive & (1 << FP_SEE))
5007 		&& cg.snap->ps.clientNum != cent->currentState.number
5008 		&& (cent->currentState.eFlags&EF_FORCE_VISIBLE
5009 			|| ((cent->gent->health > 0 || cent->gent->message )
5010 			&& cent->currentState.eType == ET_PLAYER//other things handle this in their own render funcs
5011 			&& CG_PlayerCanSeeCent( cent ))
5012 			)
5013 		)
5014 	{//force sight draws auras around living things
5015 		CG_AddForceSightShell( ent, cent );
5016 	}
5017 
5018 	//temp stuff for drain
5019 	if ( ( (cent->gent->client->ps.eFlags&EF_FORCE_DRAINED) || cent->gent->client->ps.forcePowersActive&(1<<FP_DRAIN) ) &&
5020 		(cg.renderingThirdPerson || cent->currentState.number != cg.snap->ps.clientNum))
5021 	{//draining or being drained
5022 		ent->renderfx |= RF_RGB_TINT;
5023 		ent->shaderRGBA[0] = 255;
5024 		ent->shaderRGBA[1] = ent->shaderRGBA[2] = 0;
5025 		ent->shaderRGBA[3] = 255;
5026 
5027 		if ( rand() & 1 )
5028 		{
5029 			ent->customShader = cgs.media.electricBodyShader;
5030 		}
5031 		else
5032 		{
5033 			ent->customShader = cgs.media.electricBody2Shader;
5034 		}
5035 
5036 		cgi_R_AddRefEntityToScene( ent);
5037 	}
5038 }
5039 
5040 
5041 /*
5042 -------------------------
5043 CG_G2SetHeadBlink
5044 -------------------------
5045 */
CG_G2SetHeadBlink(centity_t * cent,qboolean bStart)5046 static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart )
5047 {
5048 	if ( !cent )
5049 	{
5050 		return;
5051 	}
5052 	gentity_t *gent = cent->gent;
5053 	//FIXME: get these boneIndices game-side and pass it down?
5054 	//FIXME: need a version of this that *doesn't* need the mFileName in the ghoul2
5055 	const int hLeye = gi.G2API_GetBoneIndex( &gent->ghoul2[0], "leye", qtrue );
5056 	if (hLeye == -1)
5057 	{
5058 		return;
5059 	}
5060 
5061 	vec3_t	desiredAngles = {0};
5062 	int blendTime = 80;
5063 	qboolean bWink = qfalse;
5064 
5065 	if (bStart)
5066 	{
5067 		desiredAngles[YAW] = -38;
5068 		if ( !in_camera && Q_flrand(0.0f, 1.0f) > 0.95f )
5069 		{
5070 			bWink = qtrue;
5071 			blendTime /=3;
5072 		}
5073 	}
5074 	gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->playerModel], hLeye, desiredAngles,
5075 		BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time );
5076 	const int hReye = gi.G2API_GetBoneIndex( &gent->ghoul2[0], "reye", qtrue );
5077 	if (hReye == -1)
5078 	{
5079 		return;
5080 	}
5081 
5082 	if (!bWink)
5083 	gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->playerModel], hReye, desiredAngles,
5084 		BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time );
5085 }
5086 
5087 /*
5088 -------------------------
5089 CG_G2SetHeadAnims
5090 -------------------------
5091 */
CG_G2SetHeadAnim(centity_t * cent,int anim)5092 static void CG_G2SetHeadAnim( centity_t *cent, int anim )
5093 {
5094 	gentity_t	*gent = cent->gent;
5095 	const int blendTime = 50;
5096 	const animation_t *animations = level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations;
5097 	int	animFlags = BONE_ANIM_OVERRIDE ;//| BONE_ANIM_BLEND;
5098 	// animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps).
5099 //	float		timeScaleMod = (cg_timescale.value&&gent&&gent->s.clientNum==0&&!player_locked&&!MatrixMode&&gent->client->ps.forcePowersActive&(1<<FP_SPEED))?(1.0/cg_timescale.value):1.0;
5100 	const float		timeScaleMod = (cg_timescale.value)?(1.0/cg_timescale.value):1.0;
5101 	float animSpeed = 50.0f / animations[anim].frameLerp * timeScaleMod;
5102 
5103 	if (animations[anim].numFrames <= 0)
5104 	{
5105 		return;
5106 	}
5107 	if (anim == FACE_DEAD)
5108 	{
5109 		animFlags |= BONE_ANIM_OVERRIDE_FREEZE;
5110 	}
5111 	// animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps).
5112 	int	firstFrame;
5113 	int	lastFrame;
5114 	if ( animSpeed < 0 )
5115 	{//play anim backwards
5116 
5117 		lastFrame = animations[anim].firstFrame -1;
5118 		firstFrame = (animations[anim].numFrames -1) + animations[anim].firstFrame ;
5119 	}
5120 	else
5121 	{
5122 		firstFrame = animations[anim].firstFrame;
5123 		lastFrame = (animations[anim].numFrames) + animations[anim].firstFrame;
5124 	}
5125 
5126 	// first decide if we are doing an animation on the head already
5127 //	int startFrame, endFrame;
5128 //	const qboolean animatingHead =  gi.G2API_GetAnimRangeIndex(&gent->ghoul2[gent->playerModel], cent->gent->faceBone, &startFrame, &endFrame);
5129 
5130 //	if (!animatingHead || ( animations[anim].firstFrame != startFrame ) )// only set the anim if we aren't going to do the same animation again
5131 	{
5132 		gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], cent->gent->faceBone,
5133 			firstFrame, lastFrame, animFlags, animSpeed, cg.time, -1, blendTime);
5134 	}
5135 }
5136 
CG_G2PlayerHeadAnims(centity_t * cent)5137 static qboolean CG_G2PlayerHeadAnims( centity_t *cent )
5138 {
5139 	if(!ValidAnimFileIndex(cent->gent->client->clientInfo.animFileIndex))
5140 	{
5141 		return qfalse;
5142 	}
5143 
5144 	if (cent->gent->faceBone == BONE_INDEX_INVALID)
5145 	{	// i don't have a face
5146 		return qfalse;
5147 	}
5148 
5149 	int anim = -1;
5150 
5151 	if ( cent->gent->health <= 0 )
5152 	{//Dead people close their eyes and don't make faces!
5153 		anim = FACE_DEAD;
5154 	}
5155 	else
5156 	{
5157 		if (!cent->gent->client->facial_blink)
5158 		{	// set the timers
5159 			cent->gent->client->facial_blink = cg.time + Q_flrand(4000.0, 8000.0);
5160 			cent->gent->client->facial_timer = cg.time + Q_flrand(6000.0, 10000.0);
5161 		}
5162 
5163 		//are we blinking?
5164 		if (cent->gent->client->facial_blink < 0)
5165 		{	// yes, check if we are we done blinking ?
5166 			if (-(cent->gent->client->facial_blink) < cg.time)
5167 			{	// yes, so reset blink timer
5168 				cent->gent->client->facial_blink = cg.time + Q_flrand(4000.0, 8000.0);
5169 				CG_G2SetHeadBlink( cent, qfalse );	//stop the blink
5170 			}
5171 		}
5172 		else // no we aren't blinking
5173 		{
5174 			if (cent->gent->client->facial_blink < cg.time)// but should we start ?
5175 			{
5176 				CG_G2SetHeadBlink( cent, qtrue );
5177 				if (cent->gent->client->facial_blink == 1)
5178 				{//requested to stay shut by SET_FACEEYESCLOSED
5179 					cent->gent->client->facial_blink = -(cg.time + 99999999.0f);// set blink timer
5180 				}
5181 				else
5182 				{
5183 					cent->gent->client->facial_blink = -(cg.time + 300.0f);// set blink timer
5184 				}
5185 			}
5186 		}
5187 
5188 
5189 		if (gi.VoiceVolume[cent->gent->s.clientNum] > 0)	// if we aren't talking, then it will be 0, -1 for talking but paused
5190 		{
5191 			anim = FACE_TALK1 + gi.VoiceVolume[cent->gent->s.clientNum] -1;
5192 			cent->gent->client->facial_timer = cg.time + Q_flrand(2000.0, 7000.0);
5193 			if ( cent->gent->client->breathPuffTime > cg.time + 300 )
5194 			{//when talking, do breath puff
5195 				cent->gent->client->breathPuffTime = cg.time;
5196 			}
5197 		}
5198 		else if (gi.VoiceVolume[cent->gent->s.clientNum] == -1 )
5199 		{//talking but silent
5200 			anim = FACE_TALK0;
5201 			cent->gent->client->facial_timer = cg.time + Q_flrand(2000.0, 7000.0);
5202 		}
5203 		else if (gi.VoiceVolume[cent->gent->s.clientNum] == 0)	//don't do aux if in a slient part of speech
5204 		{//not talking
5205 			if (cent->gent->client->facial_timer < 0)	// are we auxing ?
5206 			{	//yes
5207 				if (-(cent->gent->client->facial_timer) < cg.time)// are we done auxing ?
5208 				{	// yes, reset aux timer
5209 					cent->gent->client->facial_timer = cg.time + Q_flrand(7000.0, 10000.0);
5210 				}
5211 				else
5212 				{	// not yet, so choose anim
5213 					anim = cent->gent->client->facial_anim;
5214 				}
5215 			}
5216 			else // no we aren't auxing
5217 			{	// but should we start ?
5218 				if (cent->gent->client->facial_timer < cg.time)
5219 				{//yes
5220 					cent->gent->client->facial_anim = FACE_ALERT + Q_irand(0,2);	//alert, smile, frown
5221 					// set aux timer
5222 					cent->gent->client->facial_timer = -(cg.time + 2000.0);
5223 					anim = cent->gent->client->facial_anim;
5224 				}
5225 			}
5226 		}//talking
5227 	}//dead
5228 	if (anim != -1)
5229 	{
5230 		CG_G2SetHeadAnim( cent, anim );
5231 		return qtrue;
5232 	}
5233 	return qfalse;
5234 }
5235 
5236 /*
5237 -------------------------
5238 CG_PlayerHeadExtension
5239 -------------------------
5240 */
5241 /*
5242 int CG_PlayerHeadExtension( centity_t *cent, refEntity_t *head )
5243 {
5244 	clientInfo_t	*ci = &cent->gent->client->clientInfo;;
5245 
5246 	// if we have facial texture extensions, go get the sound override and add it to the face skin
5247 	// if we aren't talking, then it will be 0
5248 	if (ci->extensions && (gi.VoiceVolume[cent->gent->s.clientNum] > 0))
5249 	{//FIXME: When talking, look at talkTarget, if any
5250 		//ALSO: When talking, add a head bob/movement on syllables - when gi.VoiceVolume[] changes drastically
5251 
5252 		if ( cent->gent->health <= 0 )
5253 		{//Dead people close their eyes and don't make faces!  They also tell no tales...  BUM BUM BAHHHHHHH!
5254 			//Make them always blink and frown
5255 			head->customSkin = ci->headSkin + 3;
5256 			return qtrue;
5257 		}
5258 
5259 		head->customSkin = ci->headSkin + 4+gi.VoiceVolume[cent->gent->s.clientNum];
5260 		//reset the frown and blink timers
5261 	}
5262 	else
5263 	// ok, we have facial extensions, but we aren't speaking. Lets decide if we need to frown or blink
5264 	if (ci->extensions)
5265 	{
5266 		int	add_in = 0;
5267 
5268 		// deal with blink first
5269 
5270 		//Dead people close their eyes and don't make faces!  They also tell no tales...  BUM BUM BAHHHHHHH!
5271 		if ( cent->gent->health <= 0 )
5272 		{
5273 			//Make them always blink and frown
5274 			head->customSkin = ci->headSkin + 3;
5275 			return qtrue;
5276 		}
5277 
5278 		if (!cent->gent->client->facial_blink)
5279 		{	// reset blink timer
5280 			cent->gent->client->facial_blink = cg.time + Q_flrand(3000.0, 5000.0);
5281 			cent->gent->client->facial_frown = cg.time + Q_flrand(6000.0, 10000.0);
5282 			cent->gent->client->facial_aux = cg.time + Q_flrand(6000.0, 10000.0);
5283 		}
5284 
5285 
5286 		// now deal with auxing
5287 		// are we frowning ?
5288 		if (cent->gent->client->facial_aux < 0)
5289 		{
5290 			// are we done frowning ?
5291 			if (-(cent->gent->client->facial_aux) < cg.time)
5292 			{
5293 				// reset frown timer
5294 				cent->gent->client->facial_aux = cg.time + Q_flrand(6000.0, 10000.0);
5295 			}
5296 			else
5297 			{
5298 				// yes so set offset to frown
5299 				add_in = 4;
5300 			}
5301 		}
5302 		// no we aren't frowning
5303 		else
5304 		{
5305 			// but should we start ?
5306 			if (cent->gent->client->facial_aux < cg.time)
5307 			{
5308 				add_in = 4;
5309 				// set blink timer
5310 				cent->gent->client->facial_aux = -(cg.time + 3000.0);
5311 			}
5312 		}
5313 
5314 		// now, if we aren't auxing - lets see if we should be blinking or frowning
5315 		if (!add_in)
5316 		{
5317 			if( gi.VoiceVolume[cent->gent->s.clientNum] == -1 )
5318 			{//then we're talking and don't want to use blinking normal frames, force open eyes.
5319 				add_in = 0;
5320 				// reset blink timer
5321 				cent->gent->client->facial_blink = cg.time + Q_flrand(3000.0, 5000.0);
5322 			}
5323 			// are we blinking ?
5324 			else if (cent->gent->client->facial_blink < 0)
5325 			{
5326 
5327 				// yes so set offset to blink
5328 				add_in = 1;
5329 
5330 				// are we done blinking ?
5331 				if (-(cent->gent->client->facial_blink) < cg.time)
5332 				{
5333 					add_in = 0;
5334 					// reset blink timer
5335 					cent->gent->client->facial_blink = cg.time + Q_flrand(3000.0, 5000.0);
5336 				}
5337 			}
5338 			// no we aren't blinking
5339 			else
5340 			{
5341 				// but should we start ?
5342 				if (cent->gent->client->facial_blink < cg.time)
5343 				{
5344 					add_in = 1;
5345 					// set blink timer
5346 					cent->gent->client->facial_blink = -(cg.time + 200.0);
5347 				}
5348 			}
5349 
5350 			// now deal with frowning
5351 			// are we frowning ?
5352 			if (cent->gent->client->facial_frown < 0)
5353 			{
5354 
5355 				// yes so set offset to frown
5356 				add_in += 2;
5357 
5358 				// are we done frowning ?
5359 				if (-(cent->gent->client->facial_frown) < cg.time)
5360 				{
5361 					add_in -= 2;
5362 					// reset frown timer
5363 					cent->gent->client->facial_frown = cg.time + Q_flrand(6000.0, 10000.0);
5364 				}
5365 			}
5366 			// no we aren't frowning
5367 			else
5368 			{
5369 				// but should we start ?
5370 				if (cent->gent->client->facial_frown < cg.time)
5371 				{
5372 					add_in += 2;
5373 					// set blink timer
5374 				 	cent->gent->client->facial_frown = -(cg.time + 3000.0);
5375 				}
5376 			}
5377 		}
5378 
5379 		// add in whatever we should
5380 		head->customSkin = ci->headSkin + add_in;
5381 	}
5382 	// at this point, we don't have any facial extensions, so who cares ?
5383 	else
5384 	{
5385 		head->customSkin = ci->headSkin;
5386 	}
5387 
5388 	return qtrue;
5389 }
5390 */
5391 
5392 //--------------------------------------------------------------
5393 // CG_GetTagWorldPosition
5394 //
5395 // Can pass in NULL for the axis
5396 //--------------------------------------------------------------
CG_GetTagWorldPosition(refEntity_t * model,char * tag,vec3_t pos,vec3_t axis[3])5397 void CG_GetTagWorldPosition( refEntity_t *model, char *tag, vec3_t pos, vec3_t axis[3] )
5398 {
5399 	orientation_t	orientation;
5400 
5401 	// Get the requested tag
5402 	cgi_R_LerpTag( &orientation, model->hModel, model->oldframe, model->frame,
5403 		1.0f - model->backlerp, tag );
5404 
5405 	VectorCopy( model->origin, pos );
5406 	for ( int i = 0 ; i < 3 ; i++ )
5407 	{
5408 		VectorMA( pos, orientation.origin[i], model->axis[i], pos );
5409 	}
5410 
5411 	if ( axis )
5412 	{
5413 		MatrixMultiply( orientation.axis, model->axis, axis );
5414 	}
5415 }
5416 
5417 static qboolean calcedMp = qfalse;
5418 
5419 /*
5420 -------------------------
5421 CG_GetPlayerLightLevel
5422 -------------------------
5423 */
5424 
CG_GetPlayerLightLevel(centity_t * cent)5425 static void CG_GetPlayerLightLevel( centity_t *cent )
5426 {
5427 	vec3_t	ambient={0}, directed, lightDir;
5428 
5429 	//Poll the renderer for the light level
5430 	if ( cent->currentState.clientNum == cg.snap->ps.clientNum )
5431 	{//hAX0R
5432 		ambient[0] = 666;
5433 	}
5434 	cgi_R_GetLighting( cent->lerpOrigin, ambient, directed, lightDir );
5435 
5436 	//Get the maximum value for the player
5437 	cent->gent->lightLevel = directed[0];
5438 
5439 	if ( directed[1] > cent->gent->lightLevel )
5440 		cent->gent->lightLevel = directed[1];
5441 
5442 	if ( directed[2] > cent->gent->lightLevel )
5443 		cent->gent->lightLevel = directed[2];
5444 
5445 	if ( cent->gent->client->ps.weapon == WP_SABER && cent->gent->client->ps.SaberLength() > 0 )
5446 	{
5447 		cent->gent->lightLevel += (cent->gent->client->ps.SaberLength()/cent->gent->client->ps.SaberLengthMax())*200;
5448 	}
5449 }
5450 
5451 /*
5452 ===============
5453 CG_StopWeaponSounds
5454 
5455 Stops any weapon sounds as needed
5456 ===============
5457 */
CG_StopWeaponSounds(centity_t * cent)5458 static void CG_StopWeaponSounds( centity_t *cent )
5459 {
5460 	weaponInfo_t	*weapon = &cg_weapons[ cent->currentState.weapon ];
5461 
5462 	if ( cent->currentState.weapon == WP_SABER )
5463 	{
5464 		if ( cent->gent && cent->gent->client )
5465 		{
5466 			if ( !cent->gent->client->ps.SaberActive() )
5467 			{//neither saber is on
5468 				return;
5469 			}
5470 			else if ( cent->gent->client->ps.saberInFlight ) //cent->gent->client->ps.saberInFlight )
5471 			{//throwing saber
5472 				if ( !cent->gent->client->ps.dualSabers || !cent->gent->client->ps.saber[1].Active() )
5473 				{//don't have a second saber or it's not on
5474 					return;
5475 				}
5476 			}
5477 		}
5478 
5479 		cgi_S_AddLoopingSound( cent->currentState.number,
5480 			cent->lerpOrigin,
5481 			vec3_origin,
5482 			cgs.sound_precache[g_entities[cent->currentState.clientNum].client->ps.saber[0].soundLoop] );
5483 		return;
5484 	}
5485 
5486 	if ( cent->currentState.weapon == WP_STUN_BATON || cent->currentState.weapon == WP_CONCUSSION )
5487 	{	//idling sounds
5488 		cgi_S_AddLoopingSound( cent->currentState.number,
5489 			cent->lerpOrigin,
5490 			vec3_origin,
5491 			weapon->firingSound );
5492 		return;
5493 	}
5494 
5495 	if ( !( cent->currentState.eFlags & EF_FIRING ) )
5496 	{
5497 		if ( cent->pe.lightningFiring )
5498 		{
5499 			if ( weapon->stopSound )
5500 			{
5501 				cgi_S_StartSound( cent->lerpOrigin, cent->currentState.number, CHAN_WEAPON, weapon->stopSound );
5502 			}
5503 
5504 			cent->pe.lightningFiring = qfalse;
5505 		}
5506 		return;
5507 	}
5508 
5509 	if ( cent->currentState.eFlags & EF_ALT_FIRING )
5510 	{
5511 		if ( weapon->altFiringSound )
5512 		{
5513 			cgi_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, weapon->altFiringSound );
5514 		}
5515 		cent->pe.lightningFiring = qtrue;
5516 	}
5517 }
5518 
5519 
5520 //--------------- SABER STUFF --------
5521 extern void CG_Smoke( vec3_t origin, vec3_t dir, float radius, float speed, qhandle_t shader, int flags);
CG_SaberDoWeaponHitMarks(gclient_t * client,gentity_t * saberEnt,gentity_t * hitEnt,int saberNum,int bladeNum,vec3_t hitPos,vec3_t hitDir,vec3_t uaxis,vec3_t splashBackDir,float sizeTimeScale)5522 void CG_SaberDoWeaponHitMarks( gclient_t *client, gentity_t *saberEnt, gentity_t *hitEnt, int saberNum, int bladeNum, vec3_t hitPos, vec3_t hitDir, vec3_t uaxis, vec3_t splashBackDir, float sizeTimeScale )
5523 {
5524 	if ( client
5525 		&& sizeTimeScale > 0.0f
5526 		&& hitEnt
5527 		&& hitEnt->client
5528 		&& hitEnt->ghoul2.size() )
5529 	{//burn mark with glow
5530 		//FIXME: set the correct angle based on direction of swing
5531 		//FIXME: keep a count of these on the ent and don't add too many
5532 		int lifeTime = (1.01-(float)(hitEnt->health)/hitEnt->max_health) * (float)Q_irand( 5000, 10000 );
5533 		float size = 0.0f;
5534 		int weaponMarkShader = 0, markShader = cgs.media.bdecal_saberglowmark;
5535 
5536 		//First: do mark decal on hitEnt
5537 		if ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) )
5538 		{
5539 			if ( client->ps.saber[saberNum].g2MarksShader2[0] )
5540 			{//we have a shader to use instead of the standard mark shader
5541 				markShader = cgi_R_RegisterShader( client->ps.saber[saberNum].g2MarksShader2 );
5542 				lifeTime = Q_irand( 20000, 30000 );//last longer if overridden
5543 			}
5544 		}
5545 		else
5546 		{
5547 			if ( client->ps.saber[saberNum].g2MarksShader[0] )
5548 			{//we have a shader to use instead of the standard mark shader
5549 				markShader = cgi_R_RegisterShader( client->ps.saber[saberNum].g2MarksShader );
5550 				lifeTime = Q_irand( 20000, 30000 );//last longer if overridden
5551 			}
5552 		}
5553 
5554 		if ( markShader )
5555 		{
5556 			lifeTime = ceil( (float)lifeTime * sizeTimeScale );
5557 			size = Q_flrand( 2.0f, 3.0f ) * sizeTimeScale;
5558 			CG_AddGhoul2Mark( markShader, size, hitPos, hitDir, hitEnt->s.number,
5559 				hitEnt->client->ps.origin, hitEnt->client->renderInfo.legsYaw, hitEnt->ghoul2, hitEnt->s.modelScale,
5560 				lifeTime, 0, uaxis );
5561 		}
5562 
5563 		//now do weaponMarkShader - splashback decal on weapon
5564 		if ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) )
5565 		{
5566 			if ( client->ps.saber[saberNum].g2WeaponMarkShader2[0] )
5567 			{//we have a shader to use instead of the standard mark shader
5568 				weaponMarkShader = cgi_R_RegisterShader( client->ps.saber[saberNum].g2WeaponMarkShader2 );
5569 				lifeTime = Q_irand( 7000, 12000 );//last longer if overridden
5570 			}
5571 		}
5572 		else
5573 		{
5574 			if ( client->ps.saber[saberNum].g2WeaponMarkShader[0] )
5575 			{//we have a shader to use instead of the standard mark shader
5576 				weaponMarkShader = cgi_R_RegisterShader( client->ps.saber[saberNum].g2WeaponMarkShader );
5577 				lifeTime = Q_irand( 7000, 12000 );//last longer if overridden
5578 			}
5579 		}
5580 
5581 		if ( weaponMarkShader )
5582 		{
5583 			centity_t *splatterOnCent = (saberEnt&&client->ps.saberInFlight?&cg_entities[saberEnt->s.number]:&cg_entities[client->ps.clientNum]);
5584 			float yawAngle = 0;
5585 			vec3_t backDir;
5586 			VectorScale( hitDir, -1, backDir );
5587 			if ( !splatterOnCent->gent->client )
5588 			{
5589 				yawAngle = splatterOnCent->lerpAngles[YAW];
5590 			}
5591 			else
5592 			{
5593 				yawAngle = splatterOnCent->gent->client->renderInfo.legsYaw;
5594 			}
5595 			lifeTime = ceil( (float)lifeTime * sizeTimeScale );
5596 			size = Q_flrand( 1.0f, 3.0f ) * sizeTimeScale;
5597 			if ( splatterOnCent->gent->ghoul2.size() > saberNum+1 )
5598 			{
5599 				CG_AddGhoul2Mark( weaponMarkShader, size, hitPos, backDir, splatterOnCent->currentState.number,
5600 					splatterOnCent->lerpOrigin, yawAngle, splatterOnCent->gent->ghoul2, splatterOnCent->currentState.modelScale,
5601 					lifeTime, saberNum+1, uaxis/*splashBackDir*/ );
5602 			}
5603 		}
5604 	}
5605 }
5606 
CG_RGBForSaberColor(saber_colors_t color,vec3_t rgb)5607 static void CG_RGBForSaberColor( saber_colors_t color, vec3_t rgb )
5608 {
5609 	switch( color )
5610 	{
5611 		case SABER_RED:
5612 			VectorSet( rgb, 1.0f, 0.2f, 0.2f );
5613 			break;
5614 		case SABER_ORANGE:
5615 			VectorSet( rgb, 1.0f, 0.5f, 0.1f );
5616 			break;
5617 		case SABER_YELLOW:
5618 			VectorSet( rgb, 1.0f, 1.0f, 0.2f );
5619 			break;
5620 		case SABER_GREEN:
5621 			VectorSet( rgb, 0.2f, 1.0f, 0.2f );
5622 			break;
5623 		case SABER_BLUE:
5624 			VectorSet( rgb, 0.2f, 0.4f, 1.0f );
5625 			break;
5626 		case SABER_PURPLE:
5627 			VectorSet( rgb, 0.9f, 0.2f, 1.0f );
5628 			break;
5629 	}
5630 }
5631 
CG_DoSaberLight(saberInfo_t * saber)5632 static void CG_DoSaberLight( saberInfo_t *saber )
5633 {
5634 	int firstBlade = 0;
5635 	int lastBlade;
5636 	//RGB combine all the colors of the sabers you're using into one averaged color!
5637 	if ( !saber )
5638 	{
5639 		return;
5640 	}
5641 
5642 	lastBlade = saber->numBlades - 1;
5643 
5644 	if ( (saber->saberFlags2&SFL2_NO_DLIGHT) )
5645 	{
5646 		if ( saber->bladeStyle2Start > 0 )
5647 		{
5648 			if ( (saber->saberFlags2&SFL2_NO_DLIGHT2) )
5649 			{
5650 				return;
5651 			}
5652 			else
5653 			{
5654 				firstBlade = saber->bladeStyle2Start;
5655 			}
5656 		}
5657 		else
5658 		{
5659 			return;
5660 		}
5661 	}
5662 	else if ( saber->bladeStyle2Start > 0 )
5663 	{
5664 		if ( (saber->saberFlags2&SFL2_NO_DLIGHT2) )
5665 		{
5666 			lastBlade = saber->bladeStyle2Start;
5667 		}
5668 	}
5669 
5670 	vec3_t		positions[MAX_BLADES*2], mid={0}, rgbs[MAX_BLADES*2], rgb={0};
5671 	float		lengths[MAX_BLADES*2]={0}, totallength = 0, numpositions = 0, dist, diameter = 0;
5672 	int			i, j;
5673 
5674 	if ( saber )
5675 	{
5676 		for ( i = firstBlade; i <= lastBlade; i++ )
5677 		{
5678 			if ( saber->blade[i].length >= MIN_SABERBLADE_DRAW_LENGTH )
5679 			{
5680 				//FIXME: make RGB sabers
5681 				CG_RGBForSaberColor( saber->blade[i].color, rgbs[i] );
5682 				lengths[i] = saber->blade[i].length;
5683 				if ( saber->blade[i].length*2.0f > diameter )
5684 				{
5685 					diameter = saber->blade[i].length*2.0f;
5686 				}
5687 				totallength += saber->blade[i].length;
5688 				VectorMA( saber->blade[i].muzzlePoint, saber->blade[i].length, saber->blade[i].muzzleDir, positions[i] );
5689 				if ( !numpositions )
5690 				{//first blade, store middle of that as midpoint
5691 					VectorMA( saber->blade[i].muzzlePoint, saber->blade[i].length*0.5, saber->blade[i].muzzleDir, mid );
5692 					VectorCopy( rgbs[i], rgb );
5693 				}
5694 				numpositions++;
5695 			}
5696 		}
5697 	}
5698 
5699 	if ( totallength )
5700 	{//actually have something to do
5701 		if ( numpositions == 1 )
5702 		{//only 1 blade, midpoint is already set (halfway between the start and end of that blade), rgb is already set, so it diameter
5703 		}
5704 		else
5705 		{//multiple blades, calc averages
5706 			VectorClear( mid );
5707 			VectorClear( rgb );
5708 			//now go through all the data and get the average RGB and middle position and the radius
5709 			for ( i = 0; i < MAX_BLADES*2; i++ )
5710 			{
5711 				if ( lengths[i] )
5712 				{
5713 					VectorMA( rgb, lengths[i], rgbs[i], rgb );
5714 					VectorAdd( mid, positions[i], mid );
5715 				}
5716 			}
5717 
5718 			//get middle rgb
5719 			VectorScale( rgb, 1/totallength, rgb );//get the average, normalized RGB
5720 			//get mid position
5721 			VectorScale( mid, 1/numpositions, mid );
5722 			//find the farthest distance between the blade tips, this will be our diameter
5723 			for ( i = 0; i < MAX_BLADES*2; i++ )
5724 			{
5725 				if ( lengths[i] )
5726 				{
5727 					for ( j = 0; j < MAX_BLADES*2; j++ )
5728 					{
5729 						if ( lengths[j] )
5730 						{
5731 							dist = Distance( positions[i], positions[j] );
5732 							if ( dist > diameter )
5733 							{
5734 								diameter = dist;
5735 							}
5736 						}
5737 					}
5738 				}
5739 			}
5740 		}
5741 
5742 		cgi_R_AddLightToScene( mid, diameter + (Q_flrand(0.0f, 1.0f)*8.0f), rgb[0], rgb[1], rgb[2] );
5743 	}
5744 }
5745 
CG_DoSaber(vec3_t origin,vec3_t dir,float length,float lengthMax,float radius,saber_colors_t color,int rfx,qboolean doLight)5746 static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight )
5747 {
5748 	vec3_t		mid;
5749 	qhandle_t	blade = 0, glow = 0;
5750 	refEntity_t saber;
5751 	float radiusmult;
5752 
5753 	if ( length < MIN_SABERBLADE_DRAW_LENGTH )
5754 	{
5755 		// if the thing is so short, just forget even adding me.
5756 		return;
5757 	}
5758 
5759 	// Find the midpoint of the saber for lighting purposes
5760 	VectorMA( origin, length * 0.5f, dir, mid );
5761 
5762 	switch( color )
5763 	{
5764 		case SABER_RED:
5765 			glow = cgs.media.redSaberGlowShader;
5766 			blade = cgs.media.redSaberCoreShader;
5767 			break;
5768 		case SABER_ORANGE:
5769 			glow = cgs.media.orangeSaberGlowShader;
5770 			blade = cgs.media.orangeSaberCoreShader;
5771 			break;
5772 		case SABER_YELLOW:
5773 			glow = cgs.media.yellowSaberGlowShader;
5774 			blade = cgs.media.yellowSaberCoreShader;
5775 			break;
5776 		case SABER_GREEN:
5777 			glow = cgs.media.greenSaberGlowShader;
5778 			blade = cgs.media.greenSaberCoreShader;
5779 			break;
5780 		case SABER_BLUE:
5781 			glow = cgs.media.blueSaberGlowShader;
5782 			blade = cgs.media.blueSaberCoreShader;
5783 			break;
5784 		case SABER_PURPLE:
5785 			glow = cgs.media.purpleSaberGlowShader;
5786 			blade = cgs.media.purpleSaberCoreShader;
5787 			break;
5788 	}
5789 
5790 	// always add a light because sabers cast a nice glow before they slice you in half!!  or something...
5791 	if ( doLight )
5792 	{//FIXME: RGB combine all the colors of the sabers you're using into one averaged color!
5793 		vec3_t rgb={1,1,1};
5794 		CG_RGBForSaberColor( color, rgb );
5795 		cgi_R_AddLightToScene( mid, (length*1.4f) + (Q_flrand(0.0f, 1.0f)*3.0f), rgb[0], rgb[1], rgb[2] );
5796 	}
5797 
5798 	memset( &saber, 0, sizeof( refEntity_t ));
5799 
5800 	// Saber glow is it's own ref type because it uses a ton of sprites, otherwise it would eat up too many
5801 	//	refEnts to do each glow blob individually
5802 	saber.saberLength = length;
5803 
5804 	// Jeff, I did this because I foolishly wished to have a bright halo as the saber is unleashed.
5805 	// It's not quite what I'd hoped tho.  If you have any ideas, go for it!  --Pat
5806 	if (length < lengthMax )
5807 	{
5808 		radiusmult = 1.0 + (2.0 / length);		// Note this creates a curve, and length cannot be < 0.5.
5809 	}
5810 	else
5811 	{
5812 		radiusmult = 1.0;
5813 	}
5814 
5815 	float radiusRange = radius * 0.075f;
5816 	float radiusStart = radius-radiusRange;
5817 
5818 	saber.radius = (radiusStart + Q_flrand(-1.0f, 1.0f) * radiusRange)*radiusmult;
5819 	//saber.radius = (2.8f + Q_flrand(-1.0f, 1.0f) * 0.2f)*radiusmult;
5820 
5821 
5822 	VectorCopy( origin, saber.origin );
5823 	VectorCopy( dir, saber.axis[0] );
5824 	saber.reType = RT_SABER_GLOW;
5825 	saber.customShader = glow;
5826 	saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff;
5827 	saber.renderfx = rfx;
5828 
5829 	cgi_R_AddRefEntityToScene( &saber );
5830 
5831 	// Do the hot core
5832 	VectorMA( origin, length, dir, saber.origin );
5833 	VectorMA( origin, -1, dir, saber.oldorigin );
5834 	saber.customShader = blade;
5835 	saber.reType = RT_LINE;
5836 	radiusStart = radius/3.0f;
5837 	saber.radius = (radiusStart + Q_flrand(-1.0f, 1.0f) * radiusRange)*radiusmult;
5838 //	saber.radius = (1.0 + Q_flrand(-1.0f, 1.0f) * 0.2f)*radiusmult;
5839 
5840 	cgi_R_AddRefEntityToScene( &saber );
5841 }
5842 
5843 #define	MAX_MARK_FRAGMENTS	128
5844 #define	MAX_MARK_POINTS		384
5845 extern markPoly_t *CG_AllocMark();
5846 
CG_CreateSaberMarks(vec3_t start,vec3_t end,vec3_t normal)5847 static void CG_CreateSaberMarks( vec3_t start, vec3_t end, vec3_t normal )
5848 {
5849 //	byte			colors[4];
5850 	int				i, j, numFragments;
5851 	vec3_t			axis[3], originalPoints[4], mid;
5852 	vec3_t			markPoints[MAX_MARK_POINTS], projection;
5853 	polyVert_t		*v, verts[MAX_VERTS_ON_POLY];
5854 	markPoly_t		*mark;
5855 	markFragment_t	markFragments[MAX_MARK_FRAGMENTS], *mf;
5856 
5857 	if ( !cg_addMarks.integer ) {
5858 		return;
5859 	}
5860 
5861 	float	radius = 0.65f;
5862 
5863 	VectorSubtract( end, start, axis[1] );
5864 	VectorNormalizeFast( axis[1] );
5865 
5866 	// create the texture axis
5867 	VectorCopy( normal, axis[0] );
5868 	CrossProduct( axis[1], axis[0], axis[2] );
5869 
5870 	// create the full polygon that we'll project
5871 	for ( i = 0 ; i < 3 ; i++ )
5872 	{
5873 		originalPoints[0][i] = start[i] - radius * axis[1][i] - radius * axis[2][i];
5874 		originalPoints[1][i] = end[i] + radius * axis[1][i] - radius * axis[2][i];
5875 		originalPoints[2][i] = end[i] + radius * axis[1][i] + radius * axis[2][i];
5876 		originalPoints[3][i] = start[i] - radius * axis[1][i] + radius * axis[2][i];
5877 	}
5878 
5879 	VectorScale( normal, -1, projection );
5880 
5881 	// get the fragments
5882 	numFragments = cgi_CM_MarkFragments( 4, (const float (*)[3])originalPoints,
5883 					projection, MAX_MARK_POINTS, markPoints[0], MAX_MARK_FRAGMENTS, markFragments );
5884 
5885 
5886 	for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ )
5887 	{
5888 		// we have an upper limit on the complexity of polygons that we store persistantly
5889 		if ( mf->numPoints > MAX_VERTS_ON_POLY )
5890 		{
5891 			mf->numPoints = MAX_VERTS_ON_POLY;
5892 		}
5893 
5894 		for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ )
5895 		{
5896 			vec3_t delta;
5897 
5898 			// Set up our texture coords, this may need some work
5899 			VectorCopy( markPoints[mf->firstPoint + j], v->xyz );
5900 			VectorAdd( end, start, mid );
5901 			VectorScale( mid, 0.5f, mid );
5902 			VectorSubtract( v->xyz, mid, delta );
5903 
5904 			v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * (0.05f + Q_flrand(0.0f, 1.0f) * 0.03f);
5905 			v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * (0.15f + Q_flrand(0.0f, 1.0f) * 0.05f);
5906 		}
5907 
5908 		// save it persistantly, do burn first
5909 		mark = CG_AllocMark();
5910 		mark->time = cg.time;
5911 		mark->alphaFade = qtrue;
5912 		mark->markShader = cgs.media.rivetMarkShader;
5913 		mark->poly.numVerts = mf->numPoints;
5914 		mark->color[0] = mark->color[1] = mark->color[2] = mark->color[3] = 255;
5915 		memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) );
5916 
5917 		// And now do a glow pass
5918 		// by moving the start time back, we can hack it to fade out way before the burn does
5919 		mark = CG_AllocMark();
5920 		mark->time = cg.time - 8500;
5921 		mark->alphaFade = qfalse;
5922 		mark->markShader = cgi_R_RegisterShader("gfx/effects/saberDamageGlow" );
5923 		mark->poly.numVerts = mf->numPoints;
5924 		mark->color[0] = 215 + Q_flrand(0.0f, 1.0f) * 40.0f;
5925 		mark->color[1] = 96 + Q_flrand(0.0f, 1.0f) * 32.0f;
5926 		mark->color[2] = mark->color[3] = Q_flrand(0.0f, 1.0f)*15.0f;
5927 		memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) );
5928 	}
5929 }
5930 
5931 extern void FX_AddPrimitive( CEffect **effect, int killTime );
5932 //-------------------------------------------------------
CG_CheckSaberInWater(centity_t * cent,centity_t * scent,int saberNum,int modelIndex,vec3_t origin,vec3_t angles)5933 void CG_CheckSaberInWater( centity_t *cent, centity_t *scent, int saberNum, int modelIndex, vec3_t origin, vec3_t angles )
5934 {
5935 	gclient_t *client = cent->gent->client;
5936 	if ( !client )
5937 	{
5938 		return;
5939 	}
5940 	if ( !scent ||
5941 		modelIndex == -1 ||
5942 		scent->gent->ghoul2.size() <= modelIndex ||
5943 		scent->gent->ghoul2[modelIndex].mBltlist.size() <= 0 ||	//using a camera puts away your saber so you have no bolts
5944 		scent->gent->ghoul2[modelIndex].mModelindex == -1 )
5945 	{
5946 		return;
5947 	}
5948 	if ( cent && cent->gent && cent->gent->client
5949 		&& (cent->gent->client->ps.saber[saberNum].saberFlags&SFL_ON_IN_WATER) )
5950 	{//saber can stay on underwater
5951 		return;
5952 	}
5953 	if (gi.totalMapContents() & (CONTENTS_WATER|CONTENTS_SLIME))
5954 	{
5955 		vec3_t		saberOrg;
5956 		mdxaBone_t	boltMatrix;
5957 
5958 		// figure out where the actual model muzzle is
5959 		gi.G2API_GetBoltMatrix( scent->gent->ghoul2, modelIndex, 0, &boltMatrix, angles, origin, cg.time, cgs.model_draw, scent->currentState.modelScale );
5960 		// work the matrix axis stuff into the original axis and origins used.
5961 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, saberOrg );
5962 
5963 		const int contents = gi.pointcontents( saberOrg, cent->currentState.clientNum );
5964 		if ( contents & (CONTENTS_WATER|CONTENTS_SLIME) )
5965 		{//still in water
5966 			client->ps.saberEventFlags |= SEF_INWATER;
5967 			return;
5968 		}
5969 	}
5970 	//not in water
5971 	client->ps.saberEventFlags &= ~SEF_INWATER;
5972 }
5973 
CG_AddSaberBladeGo(centity_t * cent,centity_t * scent,refEntity_t * saber,int renderfx,int modelIndex,vec3_t origin,vec3_t angles,int saberNum,int bladeNum)5974 static void CG_AddSaberBladeGo( centity_t *cent, centity_t *scent, refEntity_t *saber, int renderfx, int modelIndex, vec3_t origin, vec3_t angles, int saberNum, int bladeNum )
5975 {
5976 	vec3_t	org_, end,//org_future,
5977 			axis_[3] = {{0,0,0}, {0,0,0}, {0,0,0}};//, axis_future[3]={0,0,0, 0,0,0, 0,0,0};	// shut the compiler up
5978 	trace_t	trace;
5979 	float	length;
5980 	int		bolt;
5981 	mdxaBone_t	boltMatrix;
5982 	qboolean tagHack = qfalse;
5983 
5984 	gclient_t *client = cent->gent->client;
5985 
5986 	if ( !client )
5987 	{
5988 		return;
5989 	}
5990 /*
5991 Ghoul2 Insert Start
5992 */
5993 
5994 //	if (scent->gent->ghoul2.size())
5995 	if(1)
5996 	{
5997 		if ( !scent ||
5998 			modelIndex == -1 ||
5999 			scent->gent->ghoul2.size() <= modelIndex ||
6000 			scent->gent->ghoul2[modelIndex].mModelindex == -1 )
6001 		{
6002 			return;
6003 		}
6004 
6005 		/*
6006 		if ( cent->gent->client->ps.saber[saberNum].type == SABER_CLAW )
6007 		{//hack - come off the forearm
6008 			int	fwdAxis = POSITIVE_Y;
6009 			int	rtAxis = POSITIVE_X;
6010 			int	upAxis = POSITIVE_Z;
6011 			if ( saberNum == 0 )
6012 			{
6013 				bolt = gi.G2API_AddBolt( &cent->gent->ghoul2[cent->gent->playerModel], "*r_hand_cap_r_arm" );
6014 				if ( bolt == -1 )
6015 				{
6016 					bolt = cent->gent->handRBolt;
6017 					fwdAxis = NEGATIVE_Y;
6018 					rtAxis = POSITIVE_Z;
6019 					upAxis = NEGATIVE_X;
6020 				}
6021 			}
6022 			else
6023 			{
6024 				bolt = gi.G2API_AddBolt( &cent->gent->ghoul2[cent->gent->playerModel], "*l_hand_cap_l_arm" );
6025 				if ( bolt == -1 )
6026 				{
6027 					bolt = cent->gent->handLBolt;
6028 					fwdAxis = NEGATIVE_Y;
6029 					rtAxis = POSITIVE_Z;
6030 					upAxis = POSITIVE_X;
6031 				}
6032 			}
6033 			tagHack = qtrue;//use the hacked switch statement below to position and orient the blades
6034 			// figure out where the actual model muzzle is
6035 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, bolt, &boltMatrix, angles, origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
6036 			// work the matrix axis stuff into the original axis and origins used.
6037 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, org_);
6038 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, (Eorientations)fwdAxis, axis_[0]);
6039 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, (Eorientations)rtAxis, axis_[1]);//right
6040 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, (Eorientations)upAxis, axis_[2]);//up
6041 		}
6042 		else
6043 		*/
6044 		{
6045 			// figure out where the actual model muzzle is
6046 
6047 			//old way - only 1 tag ever in a saber:
6048 			//gi.G2API_GetBoltMatrix(scent->gent->ghoul2, modelIndex, 0, &boltMatrix, angles, origin, cg.time, cgs.model_draw, scent->currentState.modelScale);
6049 
6050 			//New way, multiple blade tags:
6051 			char *tagName = va( "*blade%d", bladeNum+1 );
6052 			bolt = gi.G2API_AddBolt( &scent->gent->ghoul2[modelIndex], tagName );
6053 
6054 			if ( bolt == -1 )
6055 			{
6056 				tagHack = qtrue;//use the hacked switch statement below to position and orient the blades
6057 				//hmm, just fall back to the most basic tag (this will also make it work with pre-JKA saber models
6058 				bolt = gi.G2API_AddBolt( &scent->gent->ghoul2[modelIndex], "*flash" );
6059 				if ( bolt == -1 )
6060 				{//no tag_flash either?!!
6061 					bolt = 0;
6062 				}
6063 			}
6064 
6065 			//if there is an effect on this blade, play it
6066 			if (  !WP_SaberBladeUseSecondBladeStyle( &cent->gent->client->ps.saber[saberNum], bladeNum )
6067 					&& cent->gent->client->ps.saber[saberNum].bladeEffect )
6068 			{
6069 				CG_PlayEffectIDBolted( cent->gent->client->ps.saber[saberNum].bladeEffect, modelIndex, bolt, scent->currentState.clientNum, scent->lerpOrigin, -1, qfalse );
6070 			}
6071 			else if ( WP_SaberBladeUseSecondBladeStyle( &cent->gent->client->ps.saber[saberNum], bladeNum )
6072 					&& cent->gent->client->ps.saber[saberNum].bladeEffect2 )
6073 			{
6074 				CG_PlayEffectIDBolted( cent->gent->client->ps.saber[saberNum].bladeEffect2, modelIndex, bolt, scent->currentState.clientNum, scent->lerpOrigin, -1, qfalse );
6075 			}
6076 			//get the boltMatrix
6077 			gi.G2API_GetBoltMatrix(scent->gent->ghoul2, modelIndex, bolt, &boltMatrix, angles, origin, cg.time, cgs.model_draw, scent->currentState.modelScale);
6078 
6079 			// work the matrix axis stuff into the original axis and origins used.
6080 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, org_);
6081 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, NEGATIVE_X, axis_[0]);//front (was NEGATIVE_Y, but the md3->glm exporter screws up this tag somethin' awful)
6082 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, NEGATIVE_Y, axis_[1]);//right
6083 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, POSITIVE_Z, axis_[2]);//up
6084 		}
6085 
6086 		//Now figure out where this info will be next frame
6087 		/*
6088 		{
6089 			vec3_t	futureOrigin, futureAngles, orgDiff, angDiff;
6090 			int futuretime;
6091 
6092 			//futuretime = (int)((cg.time + 99)/50) * 50;
6093 			futuretime = cg.time+100;
6094 
6095 			VectorCopy( angles, futureAngles );
6096 			VectorCopy( origin, futureOrigin );
6097 
6098 			//note: for a thrown saber, this does nothing, really
6099 			if ( cent->gent )
6100 			{
6101 				VectorSubtract( cent->lerpOrigin, cent->gent->lastOrigin, orgDiff );
6102 				VectorSubtract( cent->lerpAngles, cent->gent->lastAngles, angDiff );
6103 				VectorAdd( futureOrigin, orgDiff, futureOrigin );
6104 				for ( int i = 0; i < 3; i++ )
6105 				{
6106 					futureAngles[i] = AngleNormalize360( futureAngles[i]+angDiff[i] );
6107 				}
6108 			}
6109 
6110 			// figure out where the actual model muzzle will be after next server frame.
6111 			gi.G2API_GetBoltMatrix(scent->gent->ghoul2, modelIndex, 0, &boltMatrix, futureAngles, futureOrigin, futuretime, cgs.model_draw, scent->currentState.modelScale);
6112 			// work the matrix axis stuff into the original axis and origins used.
6113 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, org_future);
6114 			gi.G2API_GiveMeVectorFromMatrix(boltMatrix, NEGATIVE_X, axis_future[0]);//was NEGATIVE_Y, but the md3->glm exporter screws up this tag somethin' awful
6115 		}
6116 		*/
6117 	}
6118 	else
6119 	{
6120 		CG_GetTagWorldPosition( saber, "*flash", org_, axis_ );
6121 	}
6122 
6123 /*
6124 Ghoul2 Insert End
6125 */
6126 
6127 //====FIXMEFIXMEFIXMEFIXMEFIXME========================================================
6128 //FIXME: temp hack until we have a tag_flash2 for the second (3rd? 4th?) blade
6129 	//FIXME: maybe fall back on this if the saber model has only 1 tag_flash?
6130 	//			or, better yet, if the saber info doesn't list tagnames for the blades?
6131 	if ( tagHack )
6132 	{
6133 		switch ( cent->gent->client->ps.saber[saberNum].type )
6134 		{
6135 		case SABER_SINGLE:
6136 		case SABER_DAGGER:
6137 		case SABER_LANCE:
6138 			break;
6139 		case SABER_STAFF:
6140 			if ( bladeNum == 1 )
6141 			{
6142 				VectorScale( axis_[0], -1, axis_[0] );
6143 				VectorMA( org_, 16, axis_[0], org_ );
6144 			}
6145 			break;
6146 		case SABER_BROAD:
6147 			if ( bladeNum == 0 )
6148 			{
6149 				VectorMA( org_, -1, axis_[1], org_ );
6150 			}
6151 			else if ( bladeNum == 1 )
6152 			{
6153 				VectorMA( org_, 1, axis_[1], org_ );
6154 			}
6155 			break;
6156 		case SABER_PRONG:
6157 			if ( bladeNum == 0 )
6158 			{
6159 				VectorMA( org_, -3, axis_[1], org_ );
6160 			}
6161 			else if ( bladeNum == 1 )
6162 			{
6163 				VectorMA( org_, 3, axis_[1], org_ );
6164 			}
6165 			break;
6166 		case SABER_ARC:
6167 			VectorSubtract( axis_[1], axis_[2], axis_[1] );
6168 			VectorNormalizeFast( axis_[1] );
6169 			switch ( bladeNum )
6170 			{
6171 			case 0:
6172 				VectorMA( org_, 8, axis_[0], org_ );
6173 				VectorScale( axis_[0], 0.75f, axis_[0] );
6174 				VectorScale( axis_[1], 0.25f, axis_[1] );
6175 				VectorAdd( axis_[0], axis_[1], axis_[0] );
6176 				//VectorNormalize( axis_[0] );
6177 				break;
6178 			case 1:
6179 				//VectorMA( org_, 0, axis_[0], org_ );
6180 				VectorScale( axis_[0], 0.25f, axis_[0] );
6181 				VectorScale( axis_[1], 0.75f, axis_[1] );
6182 				VectorAdd( axis_[0], axis_[1], axis_[0] );
6183 				//VectorNormalize( axis_[0] );
6184 				break;
6185 			case 2:
6186 				VectorMA( org_, -8, axis_[0], org_ );
6187 				VectorScale( axis_[0], -0.25f, axis_[0] );
6188 				VectorScale( axis_[1], 0.75f, axis_[1] );
6189 				VectorAdd( axis_[0], axis_[1], axis_[0] );
6190 				//VectorNormalize( axis_[0] );
6191 				break;
6192 			case 3:
6193 				VectorMA( org_, -16, axis_[0], org_ );
6194 				VectorScale( axis_[0], -0.75f, axis_[0] );
6195 				VectorScale( axis_[1], 0.25f, axis_[1] );
6196 				VectorAdd( axis_[0], axis_[1], axis_[0] );
6197 				//VectorNormalize( axis_[0] );
6198 				break;
6199 			}
6200 			break;
6201 		case SABER_SAI:
6202 			if ( bladeNum == 1 )
6203 			{
6204 				VectorMA( org_, -3, axis_[1], org_ );
6205 			}
6206 			else if ( bladeNum == 2 )
6207 			{
6208 				VectorMA( org_, 3, axis_[1], org_ );
6209 			}
6210 			break;
6211 		case SABER_CLAW:
6212 			switch ( bladeNum )
6213 			{
6214 			case 0:
6215 				VectorMA( org_, 2, axis_[0], org_ );
6216 				VectorMA( org_, 2, axis_[2], org_ );
6217 				break;
6218 			case 1:
6219 				VectorMA( org_, 2, axis_[0], org_ );
6220 				VectorMA( org_, 2, axis_[2], org_ );
6221 				VectorMA( org_, 2, axis_[1], org_ );
6222 				break;
6223 			case 2:
6224 				VectorMA( org_, 2, axis_[0], org_ );
6225 				VectorMA( org_, 2, axis_[2], org_ );
6226 				VectorMA( org_, -2, axis_[1], org_ );
6227 				break;
6228 			}
6229 			/*
6230 			if ( bladeNum == 1 )
6231 			{
6232 				VectorMA( org_, -2, axis_[1], org_ );
6233 			}
6234 			else if ( bladeNum == 2 )
6235 			{
6236 				VectorMA( org_, 2, axis_[1], org_ );
6237 			}
6238 			*/
6239 			break;
6240 		case SABER_STAR:
6241 			/*
6242 			if ( saber )
6243 			{
6244 				VectorCopy( saber->origin, org_ );
6245 			}
6246 			else
6247 			{
6248 				bolt = cent->gent->handRBolt;
6249 				if ( saberNum == 1 )
6250 				{
6251 					bolt = cent->gent->handLBolt;
6252 				}
6253 				// figure out where the actual model muzzle is
6254 				gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, bolt, &boltMatrix, angles, origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
6255 				// work the matrix axis stuff into the original axis and origins used.
6256 				gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, org_);
6257 			}
6258 			*/
6259 			/*
6260 			if ( cent->currentState.clientNum && cent->gent->client->ps.saberInFlight )
6261 			{//WTF?  For some reason, sabers thrown by NPCs are 90 degrees off on roll
6262 				VectorCopy( axis_[1], axis_[2] );
6263 			}
6264 			*/
6265 			switch ( bladeNum )
6266 			{
6267 			case 0:
6268 				VectorMA( org_, 8, axis_[0], org_ );
6269 				break;
6270 			case 1:
6271 				VectorScale( axis_[0], 0.33f, axis_[0] );
6272 				VectorScale( axis_[2], 0.67f, axis_[2] );
6273 				VectorAdd( axis_[0], axis_[2], axis_[0] );
6274 				//VectorNormalize( axis_[0] );
6275 				VectorMA( org_, 8, axis_[0], org_ );
6276 				break;
6277 			case 2:
6278 				VectorScale( axis_[0], -0.33f, axis_[0] );
6279 				VectorScale( axis_[2], 0.67f, axis_[2] );
6280 				VectorAdd( axis_[0], axis_[2], axis_[0] );
6281 				//VectorNormalize( axis_[0] );
6282 				VectorMA( org_, 8, axis_[0], org_ );
6283 				break;
6284 			case 3:
6285 				VectorScale( axis_[0], -1, axis_[0] );
6286 				VectorMA( org_, 8, axis_[0], org_ );
6287 				break;
6288 			case 4:
6289 				VectorScale( axis_[0], -0.33f, axis_[0] );
6290 				VectorScale( axis_[2], -0.67f, axis_[2] );
6291 				VectorAdd( axis_[0], axis_[2], axis_[0] );
6292 				//VectorNormalize( axis_[0] );
6293 				VectorMA( org_, 8, axis_[0], org_ );
6294 				break;
6295 			case 5:
6296 				VectorScale( axis_[0], 0.33f, axis_[0] );
6297 				VectorScale( axis_[2], -0.67f, axis_[2] );
6298 				VectorAdd( axis_[0], axis_[2], axis_[0] );
6299 				//VectorNormalize( axis_[0] );
6300 				VectorMA( org_, 8, axis_[0], org_ );
6301 				break;
6302 			}
6303 			break;
6304 		case SABER_TRIDENT:
6305 			switch ( bladeNum )
6306 			{
6307 			case 0:
6308 				VectorMA( org_, 24, axis_[0], org_ );
6309 				break;
6310 			case 1:
6311 				VectorMA( org_, -6, axis_[1], org_ );
6312 				VectorMA( org_, 24, axis_[0], org_ );
6313 				break;
6314 			case 2:
6315 				VectorMA( org_, 6, axis_[1], org_ );
6316 				VectorMA( org_, 24, axis_[0], org_ );
6317 				break;
6318 			case 3:
6319 				VectorMA( org_, -32, axis_[0], org_ );
6320 				VectorScale( axis_[0], -1, axis_[0] );
6321 				break;
6322 			}
6323 			break;
6324 		case SABER_SITH_SWORD:
6325 			//no blade
6326 			break;
6327 		default:
6328 			break;
6329 		}
6330 	}
6331 //====FIXMEFIXMEFIXMEFIXMEFIXME========================================================
6332 
6333 	//store where saber is this frame
6334 	VectorCopy( org_, cent->gent->client->ps.saber[saberNum].blade[bladeNum].muzzlePoint );
6335 	VectorCopy( axis_[0], cent->gent->client->ps.saber[saberNum].blade[bladeNum].muzzleDir );
6336 	if ( saberNum == 0 && bladeNum == 0 )
6337 	{
6338 		VectorCopy( org_, cent->gent->client->renderInfo.muzzlePoint );
6339 		VectorCopy( axis_[0], cent->gent->client->renderInfo.muzzleDir );
6340 		cent->gent->client->renderInfo.mPCalcTime = cg.time;
6341 	}
6342 	//length for purposes of rendering and marks trace will be longer than blade so we don't damage past a wall's surface
6343 	if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length < cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax )
6344 	{
6345 		if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length < cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax - 8 )
6346 		{
6347 			length = cent->gent->client->ps.saber[saberNum].blade[bladeNum].length + 8;
6348 		}
6349 		else
6350 		{
6351 			length = cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax;
6352 		}
6353 	}
6354 	else
6355 	{
6356 		length = cent->gent->client->ps.saber[saberNum].blade[bladeNum].length;
6357 	}
6358 	VectorMA( org_, length, axis_[0], end );
6359 
6360 	// Now store where the saber will be after next frame.
6361 	//VectorCopy(org_future, cent->gent->client->renderInfo.muzzlePointNext);
6362 	//VectorCopy(axis_future[0], cent->gent->client->renderInfo.muzzleDirNext);
6363 
6364 	VectorAdd( end, axis_[0], end );
6365 
6366 	// If the saber is in flight we shouldn't trace from the player to the muzzle point
6367 	if ( cent->gent->client->ps.saberInFlight && saberNum == 0 )
6368 	{
6369 		trace.fraction = 1.0f;
6370 	}
6371 	else
6372 	{
6373 		vec3_t rootOrigin;
6374 		if (cent->gent->rootBone>=0 && cent->gent->ghoul2.IsValid() && cent->gent->ghoul2[0].animModelIndexOffset)//If it has an animOffset it's a cinematic anim
6375 		{	//i might be running out of my bounding box, so get my root origin
6376 			mdxaBone_t	boltMatrix;
6377 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->rootBone,
6378 					&boltMatrix, angles, cent->lerpOrigin,
6379 					cg.time, cgs.model_draw, cent->currentState.modelScale);
6380 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, rootOrigin );
6381 		}
6382 		else
6383 		{
6384 			VectorCopy( cent->lerpOrigin, rootOrigin );
6385 		}
6386 		gi.trace( &trace, rootOrigin, NULL, NULL, cent->gent->client->ps.saber[saberNum].blade[bladeNum].muzzlePoint, cent->currentState.number, CONTENTS_SOLID, (EG2_Collision)0, 0 );
6387 	}
6388 
6389 	if ( trace.fraction < 1.0f )
6390 	{
6391 		// Saber is on the other side of a wall
6392 		cent->gent->client->ps.saber[saberNum].blade[bladeNum].length = 0.1f;
6393 		cent->gent->client->ps.saberEventFlags &= ~SEF_INWATER;
6394 	}
6395 	else
6396 	{
6397 		extern vmCvar_t cg_saberEntMarks;
6398 		int traceMask = MASK_SOLID;
6399 		qboolean noMarks = qfalse;
6400 
6401 		if (   (!WP_SaberBladeUseSecondBladeStyle( &cent->gent->client->ps.saber[saberNum], bladeNum )
6402 				&& (cent->gent->client->ps.saber[saberNum].saberFlags2&SFL2_NO_IDLE_EFFECT) )
6403 			|| ( WP_SaberBladeUseSecondBladeStyle( &cent->gent->client->ps.saber[saberNum], bladeNum )
6404 				&& (cent->gent->client->ps.saber[saberNum].saberFlags2&SFL2_NO_IDLE_EFFECT2) )
6405 		   )
6406 		{//do no effects when idle
6407 			if ( !cent->gent->client->ps.saberInFlight
6408 				&& !PM_SaberInAttack( cent->gent->client->ps.saberMove )
6409 				&& !PM_SaberInTransitionAny( cent->gent->client->ps.saberMove )
6410 				&& !PM_SaberInSpecialAttack( cent->gent->client->ps.torsoAnim ) )
6411 			{//idle, do no marks
6412 				noMarks = qtrue;
6413 			}
6414 		}
6415 		if ( cg_saberEntMarks.integer )
6416 		{
6417 			if ( cent->gent->client->ps.saberInFlight
6418 				|| PM_SaberInAttack( cent->gent->client->ps.saberMove )
6419 				//|| PM_SaberInTransitionAny( cent->gent->client->ps.saberMove )
6420 				|| PM_SaberInSpecialAttack( cent->gent->client->ps.torsoAnim ) )
6421 			{
6422 				traceMask |= (CONTENTS_BODY|CONTENTS_CORPSE);
6423 			}
6424 		}
6425 
6426 		for ( int i = 0; i < 1; i++ )//was 2 because it would go through architecture and leave saber trails on either side of the brush - but still looks bad if we hit a corner, blade is still 8 longer than hit
6427 		{
6428 			if ( i )
6429 			{//tracing from end to base
6430 				gi.trace( &trace, end, NULL, NULL, org_, cent->currentState.clientNum, traceMask, (EG2_Collision)0, 0 );
6431 			}
6432 			else
6433 			{//tracing from base to end
6434 				gi.trace( &trace, org_, NULL, NULL, end, cent->currentState.clientNum, traceMask|CONTENTS_WATER|CONTENTS_SLIME, (EG2_Collision)0, 0 );
6435 			}
6436 
6437 			if ( trace.fraction < 1.0f )
6438 			{
6439 				if ( (trace.contents&CONTENTS_WATER) || (trace.contents&CONTENTS_SLIME) )
6440 				{
6441 					if ( !noMarks )
6442 					{
6443 						/*
6444 						if ( !(cent->gent->client->ps.saberEventFlags&SEF_INWATER) )
6445 						{
6446 						}
6447 						*/
6448 						if ( !Q_irand( 0, 10 ) )
6449 						{//FIXME: don't do this this way.... :)
6450 							vec3_t	spot;
6451 							VectorCopy( trace.endpos, spot );
6452 							spot[2] += 4;
6453 							if ( Q_irand( 1, client->ps.saber[saberNum].numBlades ) == 1 )
6454 							{
6455 								theFxScheduler.PlayEffect( "saber/boil", spot );
6456 								cgi_S_StartSound ( spot, -1, CHAN_AUTO, cgi_S_RegisterSound( "sound/weapons/saber/hitwater.wav" ) );
6457 							}
6458 						}
6459 						//cent->gent->client->ps.saberEventFlags |= SEF_INWATER;
6460 						//don't do other trace
6461 					}
6462 					i = 1;
6463 				}
6464 				else
6465 				{
6466 					if ( !noMarks )
6467 					{
6468 						if ( ( !WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && !(client->ps.saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS) )
6469 							|| ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && !(client->ps.saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS2) ) )
6470 						{
6471 							if ( !(trace.surfaceFlags & SURF_NOIMPACT) // never spark on sky
6472 								&& (trace.entityNum == ENTITYNUM_WORLD || cg_entities[trace.entityNum].currentState.solid == SOLID_BMODEL)
6473 								&& Q_irand( 1, client->ps.saber[saberNum].numBlades ) == 1 )
6474 							{
6475 								//was "sparks/spark"
6476 								theFxScheduler.PlayEffect( "sparks/spark_nosnd", trace.endpos, trace.plane.normal );
6477 							}
6478 						}
6479 						// All I need is a bool to mark whether I have a previous point to work with.
6480 						//....come up with something better..
6481 						if ( client->ps.saber[saberNum].blade[bladeNum].trail.haveOldPos[i] )
6482 						{
6483 							if ( trace.entityNum == ENTITYNUM_WORLD || (cg_entities[trace.entityNum].currentState.eFlags & EF_PERMANENT) || cg_entities[trace.entityNum].currentState.eType == ET_TERRAIN )
6484 							{//only put marks on architecture
6485 								if ( (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && !(client->ps.saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS))
6486 									|| (WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && !(client->ps.saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS2)) )
6487 								{
6488 									// Let's do some cool burn/glowing mark bits!!!
6489 									CG_CreateSaberMarks( client->ps.saber[saberNum].blade[bladeNum].trail.oldPos[i], trace.endpos, trace.plane.normal );
6490 
6491 									if ( Q_irand( 1, client->ps.saber[saberNum].numBlades ) == 1 )
6492 									{
6493 										//make a sound
6494 										if ( cg.time - cent->gent->client->ps.saberHitWallSoundDebounceTime >= 100 )
6495 										{//ugh, need to have a real sound debouncer... or do this game-side
6496 											cent->gent->client->ps.saberHitWallSoundDebounceTime = cg.time;
6497 											cgi_S_StartSound ( cent->lerpOrigin, cent->currentState.clientNum, CHAN_ITEM, cgi_S_RegisterSound( va ( "sound/weapons/saber/saberhitwall%d.wav", Q_irand( 1, 3 ) ) ) );
6498 										}
6499 									}
6500 								}
6501 							}
6502 							else if ( !i )
6503 							{//can put marks on G2 clients (but only on base to tip trace)
6504 								gentity_t *hitEnt = &g_entities[trace.entityNum];
6505 								vec3_t uaxis, splashBackDir;
6506 								VectorSubtract(client->ps.saber[saberNum].blade[bladeNum].trail.oldPos[i], trace.endpos, uaxis);
6507 								VectorScale( axis_[0], -1, splashBackDir );
6508 								//FIXME: if not hitting the first model on the enemy, don't do this!
6509 								CG_SaberDoWeaponHitMarks( client, (scent!=NULL?scent->gent:NULL), hitEnt, saberNum, bladeNum, trace.endpos, axis_[0], uaxis, splashBackDir, 0.25f );
6510 							}
6511 						}
6512 						else
6513 						{
6514 							// if we impact next frame, we'll mark a slash mark
6515 							client->ps.saber[saberNum].blade[bladeNum].trail.haveOldPos[i] = qtrue;
6516 						}
6517 					}
6518 				}
6519 
6520 				// stash point so we can connect-the-dots later
6521 				VectorCopy( trace.endpos, client->ps.saber[saberNum].blade[bladeNum].trail.oldPos[i] );
6522 				VectorCopy( trace.plane.normal, client->ps.saber[saberNum].blade[bladeNum].trail.oldNormal[i] );
6523 
6524 				if ( !i && trace.contents&(CONTENTS_SOLID|CONTENTS_TERRAIN|CONTENTS_SHOTCLIP) )
6525 				{	//Now that we don't let the blade go through walls, we need to shorten the blade when it hits one
6526 					cent->gent->client->ps.saber[saberNum].blade[bladeNum].length = cent->gent->client->ps.saber[saberNum].blade[bladeNum].length * trace.fraction;//this will stop damage from going through walls
6527 					if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length <= 0.1f )
6528 					{//SIGH... hack so it doesn't play the saber turn-on sound that plays when you first turn the saber on (assumed when saber is active but length is zero)
6529 						cent->gent->client->ps.saber[saberNum].blade[bladeNum].length = 0.1f;//FIXME: may go through walls still??
6530 					}
6531 					//FIXME: should probably re-extend instantly, not use the "turning-on" growth rate
6532 				}
6533 			}
6534 			else
6535 			{
6536 				cent->gent->client->ps.saberEventFlags &= ~SEF_INWATER;
6537 				if ( client->ps.saber[saberNum].blade[bladeNum].trail.haveOldPos[i] )
6538 				{
6539 					if ( !noMarks )
6540 					{
6541 						if ( (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && !(client->ps.saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS))
6542 							|| (WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && !(client->ps.saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS2)) )
6543 						{
6544 							// Hmmm, no impact this frame, but we have an old point
6545 							// Let's put the mark there, we should use an endcap mark to close the line, but we
6546 							//	can probably just get away with a round mark
6547 							//CG_ImpactMark( cgs.media.rivetMarkShader, client->ps.saber[saberNum].blade[bladeNum].trail.oldPos[i], client->ps.saber[saberNum].blade[bladeNum].trail.oldNormal[i],
6548 							//		0.0f, 1.0f, 1.0f, 1.0f, 1.0f, qfalse, 1.1f, qfalse );
6549 						}
6550 					}
6551 				}
6552 
6553 				// we aren't impacting, so turn off our mark tracking mechanism
6554 				client->ps.saber[saberNum].blade[bladeNum].trail.haveOldPos[i] = qfalse;
6555 			}
6556 		}
6557 	}
6558 
6559 	// Added 10/02/02 by Aurelio Reis.
6560 	// If the Blade is not active, leave here; we do not Render it!
6561 /*	if ( cent->gent->client->ps.saber[saberNum].type == SABER_SITH_SWORD )
6562 	{//draws no blade or trail
6563 		//FIXME: draw some sort of energy halo and motion trail!
6564 		return;
6565 	}
6566 */
6567 	if ( !client->ps.saber[saberNum].blade[bladeNum].active && client->ps.saber[saberNum].blade[bladeNum].length <= 0 )
6568 	{
6569 		return;
6570 	}
6571 
6572 	if ( (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && client->ps.saber[saberNum].trailStyle < 2 )
6573 		 || ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && client->ps.saber[saberNum].trailStyle2 < 2 ) )
6574 	{//okay to draw the trail
6575 		saberTrail_t	*saberTrail = &client->ps.saber[saberNum].blade[bladeNum].trail;
6576 
6577 #define SABER_TRAIL_TIME	40.0f
6578 
6579 		// if we happen to be timescaled or running in a high framerate situation, we don't want to flood
6580 		//	the system with very small trail slices...but perhaps doing it by distance would yield better results?
6581 		if ( saberTrail->lastTime > cg.time )
6582 		{//after a pause, cg.time jumps ahead in time for one frame
6583 		//and lastTime gets set to that and will freak out, so, since
6584 		//it's never valid for saberTrail->lastTime to be > cg.time,
6585 		//cap it to cg.time here
6586 			saberTrail->lastTime = cg.time;
6587 		}
6588 		if ( cg.time > saberTrail->lastTime + 2  && saberTrail->inAction ) // 2ms
6589 		{
6590 			if ( saberTrail->inAction && cg.time < saberTrail->lastTime + 300 ) // if we have a stale segment, don't draw until we have a fresh one
6591 			{
6592 				vec3_t	rgb1={255,255,255};
6593 
6594 				if ( cent->gent->client->ps.saber[saberNum].type != SABER_SITH_SWORD
6595 					&& ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) || client->ps.saber[saberNum].trailStyle != 1 )
6596 					&& ( !WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) || client->ps.saber[saberNum].trailStyle2 != 1 )
6597 				   )
6598 				{
6599 					switch( client->ps.saber[saberNum].blade[bladeNum].color )
6600 					{
6601 						case SABER_RED:
6602 							VectorSet( rgb1, 255.0f, 0.0f, 0.0f );
6603 							break;
6604 						case SABER_ORANGE:
6605 							VectorSet( rgb1, 255.0f, 64.0f, 0.0f );
6606 							break;
6607 						case SABER_YELLOW:
6608 							VectorSet( rgb1, 255.0f, 255.0f, 0.0f );
6609 							break;
6610 						case SABER_GREEN:
6611 							VectorSet( rgb1, 0.0f, 255.0f, 0.0f );
6612 							break;
6613 						case SABER_BLUE:
6614 							VectorSet( rgb1, 0.0f, 64.0f, 255.0f );
6615 							break;
6616 						case SABER_PURPLE:
6617 							VectorSet( rgb1, 220.0f, 0.0f, 255.0f );
6618 							break;
6619 					}
6620 				}
6621 
6622 				float diff = cg.time - saberTrail->lastTime;
6623 
6624 				// I'm not sure that clipping this is really the best idea
6625 				if ( diff <= SABER_TRAIL_TIME * 2 )
6626 				{
6627 					// build a quad
6628 					CTrail *fx = new CTrail;
6629 
6630 					float duration;
6631 
6632 					if ( cent->gent->client->ps.saber[saberNum].type == SABER_SITH_SWORD
6633 						|| (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && client->ps.saber[saberNum].trailStyle == 1 )
6634 						|| ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && client->ps.saber[saberNum].trailStyle2 == 1 )
6635 					   )
6636 					{
6637 						fx->mShader = cgs.media.swordTrailShader;
6638 						duration = saberTrail->duration/2.0f; // stay around twice as long
6639 						VectorSet( rgb1, 32.0f, 32.0f, 32.0f ); // make the sith sword trail pretty faint
6640 					}
6641 					else
6642 					{
6643 						fx->mShader = cgs.media.saberBlurShader;
6644 						duration = saberTrail->duration/5.0f;
6645 					}
6646 
6647 					float oldAlpha = 1.0f - ( diff / duration );
6648 
6649 					// Go from new muzzle to new end...then to old end...back down to old muzzle...finally
6650 					//	connect back to the new muzzle...this is our trail quad
6651 					VectorCopy( org_, fx->mVerts[0].origin );
6652 					VectorMA( end, 3.0f, axis_[0], fx->mVerts[1].origin );
6653 
6654 					VectorCopy( saberTrail->tip, fx->mVerts[2].origin );
6655 					VectorCopy( saberTrail->base, fx->mVerts[3].origin );
6656 
6657 					// New muzzle
6658 					VectorCopy( rgb1, fx->mVerts[0].rgb );
6659 					fx->mVerts[0].alpha = 255.0f;
6660 
6661 					fx->mVerts[0].ST[0] = 0.0f;
6662 					fx->mVerts[0].ST[1] = 0.99f;
6663 					fx->mVerts[0].destST[0] = 0.99f;
6664 					fx->mVerts[0].destST[1] = 0.99f;
6665 
6666 					// new tip
6667 					VectorCopy( rgb1, fx->mVerts[1].rgb );
6668 					fx->mVerts[1].alpha = 255.0f;
6669 
6670 					fx->mVerts[1].ST[0] = 0.0f;
6671 					fx->mVerts[1].ST[1] = 0.0f;
6672 					fx->mVerts[1].destST[0] = 0.99f;
6673 					fx->mVerts[1].destST[1] = 0.0f;
6674 
6675 					// old tip
6676 					VectorCopy( rgb1, fx->mVerts[2].rgb );
6677 					fx->mVerts[2].alpha = 255.0f;
6678 
6679 					fx->mVerts[2].ST[0] = 0.99f - oldAlpha; // NOTE: this just happens to contain the value I want
6680 					fx->mVerts[2].ST[1] = 0.0f;
6681 					fx->mVerts[2].destST[0] = 0.99f + fx->mVerts[2].ST[0];
6682 					fx->mVerts[2].destST[1] = 0.0f;
6683 
6684 					// old muzzle
6685 					VectorCopy( rgb1, fx->mVerts[3].rgb );
6686 					fx->mVerts[3].alpha = 255.0f;
6687 
6688 					fx->mVerts[3].ST[0] = 0.99f - oldAlpha; // NOTE: this just happens to contain the value I want
6689 					fx->mVerts[3].ST[1] = 0.99f;
6690 					fx->mVerts[3].destST[0] = 0.99f + fx->mVerts[2].ST[0];
6691 					fx->mVerts[3].destST[1] = 0.99f;
6692 
6693 	//				fx->SetFlags( FX_USE_ALPHA );
6694 					FX_AddPrimitive( (CEffect**)&fx, duration );//SABER_TRAIL_TIME );
6695 				}
6696 			}
6697 
6698 			// we must always do this, even if we aren't active..otherwise we won't know where to pick up from
6699 			VectorCopy( org_, saberTrail->base );
6700 			VectorMA( end, 3.0f, axis_[0], saberTrail->tip );
6701 			saberTrail->lastTime = cg.time;
6702 		}
6703 	}
6704 
6705 	if ( cent->gent->client->ps.saber[saberNum].type == SABER_SITH_SWORD)
6706 	{
6707 		// don't need to do nuthin else
6708 		return;
6709 	}
6710 
6711 	qboolean noDlight = qfalse;
6712 	if ( client->ps.saber[saberNum].numBlades >= 3
6713 		|| (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_DLIGHT) )
6714 		|| ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_DLIGHT2) )
6715 		)
6716 	{
6717 		noDlight = qtrue;
6718 	}
6719 
6720 	if ( (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE) )
6721 		 || ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE2) ) )
6722 	{//don't draw a blade
6723 		if ( !noDlight )
6724 		{//but still do dlight
6725 			CG_DoSaberLight( &client->ps.saber[saberNum] );
6726 		}
6727 		return;
6728 	}
6729 	// Pass in the renderfx flags attached to the saber weapon model...this is done so that saber glows
6730 	//	will get rendered properly in a mirror...not sure if this is necessary??
6731 	CG_DoSaber(
6732 		org_, axis_[0], length,
6733 		client->ps.saber[saberNum].blade[bladeNum].lengthMax,
6734 		client->ps.saber[saberNum].blade[bladeNum].radius,
6735 		client->ps.saber[saberNum].blade[bladeNum].color,
6736 		renderfx, (qboolean)!noDlight );
6737 }
6738 
CG_AddSaberBlade(centity_t * cent,centity_t * scent,refEntity_t * saber,int renderfx,int modelIndex,vec3_t origin,vec3_t angles)6739 void CG_AddSaberBlade( centity_t *cent, centity_t *scent, refEntity_t *saber, int renderfx, int modelIndex, vec3_t origin, vec3_t angles )
6740 {
6741 	//FIXME: if this is a dropped saber, it could be possible that it's the second saber?
6742 	if ( cent->gent->client )
6743 	{
6744 		for ( int i = 0; i < cent->gent->client->ps.saber[0].numBlades;i++ )
6745 		{
6746 			CG_AddSaberBladeGo( cent, scent, saber, renderfx, modelIndex, origin, angles, 0, i );
6747 		}
6748 		if ( cent->gent->client->ps.saber[0].numBlades > 2 )
6749 		{// add blended light
6750 			CG_DoSaberLight( &cent->gent->client->ps.saber[0] );
6751 		}
6752 	}
6753 }
6754 
6755 /*
6756 static void CG_AddSaberBlades( centity_t *cent, int renderfx, vec3_t origin, vec3_t angles, int saberNum )
6757 {
6758 	if ( cent->gent->client )
6759 	{
6760 		for ( int i = 0; i < cent->gent->client->ps.saber[saberNum].numBlades; i++ )
6761 		{
6762 			CG_AddSaberBladeGo( cent, cent, NULL, renderfx, cent->gent->weaponModel[saberNum], origin, angles, saberNum, i );
6763 		}
6764 	}
6765 }
6766 */
6767 //--------------- END SABER STUFF --------
6768 
6769 /*
6770 ===============
6771 CG_Player
6772 
6773   FIXME: Extend this to be a render func for all multiobject entities in the game such that:
6774 
6775 	You can have and stack multiple animated pieces (not just legs and torso)
6776 	You can attach "bolt-ons" that either animate or don't (weapons, heads, borg pieces)
6777 	You can attach any object to any tag on any object (weapon on the head, etc.)
6778 
6779 	Basically, keep a list of objects:
6780 		Root object (in this case, the legs) with this info:
6781 			model
6782 			skin
6783 			effects
6784 			scale
6785 			if animated, frame or anim number
6786 			drawn at origin and angle of master entity
6787 		Animated objects, with this info:
6788 			model
6789 			skin
6790 			effects
6791 			scale
6792 			frame or anim number
6793 			object it's attached to
6794 			tag to attach it's tag_parent to
6795 			angle offset to attach it with
6796 		Non-animated objects, with this info:
6797 			model
6798 			skin
6799 			effects
6800 			scale
6801 			object it's attached to
6802 			tag to attach it's tag_parent to
6803 			angle offset to attach it with
6804 
6805   ALSO:
6806 	Move the auto angle setting back up to the game
6807 	Implement 3-axis scaling
6808 	Implement alpha
6809 	Implement tint
6810 	Implement other effects (generic call effect at org and dir (or tag), with parms?)
6811 
6812 ===============
6813 */
6814 extern qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, char *returnSurfName, int returnSize );
6815 extern qboolean G_ControlledByPlayer( gentity_t *self );
6816 extern qboolean G_RagDoll(gentity_t *ent, vec3_t forcedAngles);
6817 int	cg_saberOnSoundTime[MAX_GENTITIES] = {0};
6818 
CG_Player(centity_t * cent)6819 void CG_Player( centity_t *cent ) {
6820 	clientInfo_t	*ci;
6821 	qboolean		shadow, staticScale = qfalse;
6822 	float			shadowPlane;
6823 	const weaponData_t  *wData = NULL;
6824 
6825 	if ( cent->currentState.eFlags & EF_NODRAW )
6826 	{
6827 		return;
6828 	}
6829 
6830 	//make sure this thing has a gent and client
6831 	if(!cent->gent)
6832 	{
6833 		return;
6834 	}
6835 
6836 	if(!cent->gent->client)
6837 	{
6838 		return;
6839 	}
6840 
6841 	if( cent->gent->s.number == 0 && cg.weaponSelect == WP_NONE && cg.zoomMode == 1 )
6842 	{
6843 		// HACK
6844 		return;
6845 	}
6846 
6847 	calcedMp = qfalse;
6848 
6849 	//Get the player's light level for stealth calculations
6850 	CG_GetPlayerLightLevel( cent );
6851 
6852 	if ((in_camera) && cent->currentState.clientNum == 0 )	// If player in camera then no need for shadow
6853 	{
6854 		return;
6855 	}
6856 
6857 	if(cent->currentState.number == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer )
6858 	{
6859 		calcedMp = qtrue;
6860 	}
6861 
6862 	ci = &cent->gent->client->clientInfo;
6863 
6864 	if ( !ci->infoValid )
6865 	{
6866 		return;
6867 	}
6868 
6869 	G_RagDoll(cent->gent, cent->lerpAngles);
6870 
6871 	if ( cent->currentState.weapon )
6872 	{
6873 		wData = &weaponData[cent->currentState.weapon];
6874 	}
6875 /*
6876 Ghoul2 Insert Start
6877 */
6878 	if (cent->gent->ghoul2.size())	//do we have ghoul models attached?
6879 	{
6880 		refEntity_t			ent;
6881 		vec3_t				tempAngles;
6882 		memset (&ent, 0, sizeof(ent));
6883 
6884 		//FIXME: if at all possible, do all our sets before our gets to do only *1* G2 skeleton transform per render frame
6885 		CG_SetGhoul2Info(&ent, cent);
6886 
6887 		// Weapon sounds may need to be stopped, so check now
6888 		CG_StopWeaponSounds( cent );
6889 
6890 		// add powerups floating behind the player
6891 		CG_PlayerPowerups( cent );
6892 
6893 		// add the shadow
6894 		//FIXME: funcs that modify our origin below will cause the shadow to be in the wrong spot
6895 		shadow = CG_PlayerShadow( cent, &shadowPlane );
6896 
6897 		// add a water splash if partially in and out of water
6898 		CG_PlayerSplash( cent );
6899 
6900 		// get the player model information
6901 		ent.renderfx = 0;
6902 		if ( !cg.renderingThirdPerson || cg.zoomMode )
6903 		{//in first person or zoomed in
6904 			if ( cg.snap->ps.viewEntity <= 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD)
6905 			{//no viewentity
6906 				if ( cent->currentState.number == cg.snap->ps.clientNum )
6907 				{//I am the player
6908 					if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE )
6909 					{//not using saber or fists
6910 						ent.renderfx = RF_THIRD_PERSON;			// only draw in mirrors
6911 					}
6912 				}
6913 			}
6914 			else if ( cent->currentState.number == cg.snap->ps.viewEntity )
6915 			{//I am the view entity
6916 				if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE )
6917 				{//not using first person saber test or, if so, not using saber
6918 					ent.renderfx = RF_THIRD_PERSON;			// only draw in mirrors
6919 				}
6920 			}
6921 		}
6922 
6923 		if ( cent->gent->client->ps.powerups[PW_DISINT_2] > cg.time )
6924 		{//ghost!
6925 			ent.renderfx = RF_THIRD_PERSON;			// only draw in mirrors
6926 		}
6927 		else if (cg_shadows.integer == 2 && (ent.renderfx & RF_THIRD_PERSON))
6928 		{ //show stencil shadow in first person now because we can -rww
6929 			ent.renderfx |= RF_SHADOW_ONLY;
6930 		}
6931 
6932 		if ( (cg_shadows.integer == 2 && !in_camera) || (cg_shadows.integer == 3 && shadow) )
6933 		{
6934 			ent.renderfx |= RF_SHADOW_PLANE;
6935 		}
6936 		ent.shadowPlane = shadowPlane;
6937 		ent.renderfx |= RF_LIGHTING_ORIGIN;			// use the same origin for all
6938 		if ( cent->gent->NPC && cent->gent->NPC->scriptFlags & SCF_MORELIGHT )
6939 		{
6940 			ent.renderfx |= RF_MORELIGHT;			//bigger than normal min light
6941 		}
6942 
6943 		CG_RegisterWeapon( cent->currentState.weapon );
6944 
6945 //---------------
6946 		Vehicle_t *pVeh;
6947 
6948 		if ( cent->currentState.eFlags & EF_LOCKED_TO_WEAPON && cent->gent && cent->gent->health > 0 && cent->gent->owner )
6949 		{
6950 			centity_t	*chair = &cg_entities[cent->gent->owner->s.number];
6951 			if ( chair && chair->gent )
6952 			{
6953 				vec3_t		temp;
6954 				mdxaBone_t	boltMatrix;
6955 
6956 				//NOTE: call this so it updates on the server and client
6957 				if ( chair->gent->bounceCount )
6958 				{//EWeb
6959 					// We'll set the turret angles directly
6960 					VectorClear( temp );
6961 					VectorClear( chair->gent->pos1 );
6962 
6963 					temp[PITCH] = cent->lerpAngles[PITCH];
6964 					chair->gent->pos1[YAW] = AngleSubtract( cent->lerpAngles[YAW], chair->gent->s.angles[YAW] );//remember which dir our turret is facing for later
6965 					cent->lerpAngles[ROLL] = 0;
6966 
6967                     BG_G2SetBoneAngles( chair, chair->gent, chair->gent->lowerLumbarBone, chair->gent->pos1, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, cgs.model_draw );
6968                     BG_G2SetBoneAngles( chair, chair->gent, chair->gent->upperLumbarBone, temp, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, cgs.model_draw );
6969 				}
6970 				else
6971 				{
6972 					// We'll set the turret yaw directly
6973 					VectorClear( chair->gent->s.apos.trBase );
6974 					VectorClear( temp );
6975 
6976 					chair->gent->s.apos.trBase[YAW] = cent->lerpAngles[YAW];
6977 					temp[PITCH] = -cent->lerpAngles[PITCH];
6978 					cent->lerpAngles[ROLL] = 0;
6979                     BG_G2SetBoneAngles( chair, chair->gent, chair->gent->lowerLumbarBone, temp, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, cgs.model_draw );
6980 				}
6981 				//gi.G2API_SetBoneAngles( &chair->gent->ghoul2[0], "swivel_bone", temp, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, cgs.model_draw );
6982 				VectorCopy( temp, chair->gent->lastAngles );
6983 
6984 				gi.G2API_StopBoneAnimIndex( &cent->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone );
6985 
6986 				// Getting the seat bolt here
6987 				gi.G2API_GetBoltMatrix( chair->gent->ghoul2, chair->gent->playerModel, chair->gent->headBolt,
6988 						&boltMatrix, chair->gent->s.apos.trBase, chair->gent->currentOrigin, cg.time,
6989 						cgs.model_draw, chair->currentState.modelScale );
6990 
6991 				if ( chair->gent->bounceCount )
6992 				{//put behind it, not in chair
6993 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent.origin );
6994 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, chair->gent->pos3 );
6995 					chair->gent->pos3[2] = 0;
6996 					VectorNormalizeFast( chair->gent->pos3 );
6997 					VectorMA( ent.origin, -44.0f, chair->gent->pos3, ent.origin );
6998 					ent.origin[2] = cent->lerpOrigin[2];
6999 					cent->lerpAngles[YAW] = vectoyaw( chair->gent->pos3 );
7000 					cent->lerpAngles[ROLL] = 0;
7001 					CG_G2PlayerAngles( cent, ent.axis, tempAngles);
7002 					calcedMp = qtrue;
7003 				}
7004 				else
7005 				{//sitting in it
7006 					// Storing ent position, bolt position, and bolt axis
7007 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent.origin );
7008 					VectorCopy( ent.origin, chair->gent->pos2 );
7009 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_Y, chair->gent->pos3 );
7010 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Z, chair->gent->pos4 );
7011 
7012 					AnglesToAxis( cent->lerpAngles, ent.axis );
7013 					VectorCopy( cent->lerpAngles, tempAngles);//tempAngles is needed a lot below
7014 				}
7015 
7016 				VectorCopy( ent.origin, ent.oldorigin );
7017 				VectorCopy( ent.origin, ent.lightingOrigin );
7018 
7019 				// FIXME:  Mike claims that hacking the eyepoint will make them shoot at me.......so,
7020 				//	we move up from the seat bolt and store off that point.
7021 	//			VectorMA( ent.origin, -20, chair->gent->pos3, cent->gent->client->renderInfo.eyePoint );
7022 	//			VectorMA( cent->gent->client->renderInfo.eyePoint, 40, chair->gent->pos4, cent->gent->client->renderInfo.eyePoint );
7023 			}
7024 		}
7025 		else if ( ( pVeh = G_IsRidingVehicle( cent->gent ) ) != NULL )
7026 		{//rider
7027 			CG_G2PlayerAngles( cent, ent.axis, tempAngles);
7028 			//Deal with facial expressions
7029 			CG_G2PlayerHeadAnims( cent );
7030 
7031 			centity_t *vehEnt = &cg_entities[cent->gent->owner->s.number];
7032 			CG_CalcEntityLerpPositions( vehEnt );
7033 			// Get the driver tag.
7034 			mdxaBone_t	boltMatrix;
7035 			gi.G2API_GetBoltMatrix( vehEnt->gent->ghoul2, vehEnt->gent->playerModel, vehEnt->gent->crotchBolt,
7036 					&boltMatrix, vehEnt->lerpAngles, vehEnt->lerpOrigin, (cg.time?cg.time:level.time), NULL, vehEnt->currentState.modelScale );
7037 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent.origin );
7038 
7039 			float savPitch = cent->lerpAngles[PITCH];
7040 			VectorCopy( vehEnt->lerpAngles, cent->lerpAngles );
7041 			AnglesToAxis( cent->lerpAngles, ent.axis );
7042 
7043 			VectorCopy( ent.origin, ent.oldorigin );
7044 			VectorCopy( ent.origin, ent.lightingOrigin );
7045 
7046 			VectorCopy( cent->lerpAngles, tempAngles );//tempAngles is needed a lot below
7047 			VectorCopy( ent.origin, cent->lerpOrigin );
7048 			VectorCopy( ent.origin, cent->gent->client->ps.origin );
7049 			//bah, keep our pitch!
7050 			cent->lerpAngles[PITCH] = savPitch;
7051 		}
7052 		else if ( ( (cent->gent->client->ps.eFlags&EF_HELD_BY_RANCOR)||(cent->gent->client->ps.eFlags&EF_HELD_BY_WAMPA) )
7053 			&& cent->gent && cent->gent->activator )
7054 		{
7055 			centity_t	*monster = &cg_entities[cent->gent->activator->s.number];
7056 			if ( monster && monster->gent && monster->gent->inuse && monster->gent->health > 0 )
7057 			{
7058 				mdxaBone_t	boltMatrix;
7059 				// Getting the bolt here
7060 				//in mouth
7061 				int boltIndex = monster->gent->gutBolt;
7062 				if ( monster->gent->count == 1 )
7063 				{//in hand
7064 					boltIndex = monster->gent->handRBolt;
7065 				}
7066 				vec3_t rancAngles = {0};
7067 				rancAngles[YAW] = monster->lerpAngles[YAW];
7068 				gi.G2API_GetBoltMatrix( monster->gent->ghoul2, monster->gent->playerModel, boltIndex,
7069 						&boltMatrix, rancAngles, monster->lerpOrigin, cg.time,
7070 						cgs.model_draw, monster->currentState.modelScale );
7071 				// Storing ent position, bolt position, and bolt axis
7072 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent.origin );
7073 				if ( (cent->gent->client->ps.eFlags&EF_HELD_BY_WAMPA) )
7074 				{
7075 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, ent.axis[0] );
7076 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_X, ent.axis[1] );
7077 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_Z, ent.axis[2] );
7078 				}
7079 				else if ( monster->gent->count == 1 )
7080 				{
7081 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, ent.axis[0] );
7082 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_X, ent.axis[1] );
7083 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_Z, ent.axis[2] );
7084 				}
7085 				else
7086 				{
7087 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_Z, ent.axis[0] );
7088 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, ent.axis[1] );
7089 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_X, ent.axis[2] );
7090 				}
7091 				//FIXME: this is messing up our axis and turning us inside-out
7092 				if ( cent->gent->client->isRagging )
7093 				{//hack, ragdoll has you way at bottom of bounding box
7094 					VectorMA( ent.origin, 32, ent.axis[2], ent.origin );
7095 				}
7096 				VectorCopy( ent.origin, ent.oldorigin );
7097 				VectorCopy( ent.origin, ent.lightingOrigin );
7098 
7099 				vectoangles( ent.axis[0], cent->lerpAngles );
7100 				vec3_t temp;
7101 				vectoangles( ent.axis[2], temp );
7102 				cent->lerpAngles[ROLL] = -temp[PITCH];
7103 
7104 				VectorCopy( cent->lerpAngles, tempAngles );//tempAngles is needed a lot below
7105 				VectorCopy( ent.origin, cent->lerpOrigin );
7106 				VectorCopy( ent.origin, cent->gent->client->ps.origin );
7107 			//	if ( (cent->gent->client->ps.eFlags&EF_HELD_BY_WAMPA) )
7108 			//	{
7109 					vectoangles( ent.axis[0], cent->lerpAngles );
7110 					VectorCopy( cent->lerpAngles, tempAngles );//tempAngles is needed a lot below
7111 			//	}
7112 			//	else
7113 			//	{
7114 			//		//cent->gent->client->ps.viewangles[YAW] = cent->lerpAngles[YAW];
7115 			//	}
7116 			}
7117 			else
7118 			{//wtf happened to the guy holding me?  Better get out
7119 				cent->gent->activator = NULL;
7120 				cent->gent->client->ps.eFlags &= ~(EF_HELD_BY_WAMPA|EF_HELD_BY_RANCOR);
7121 			}
7122 		}
7123 		else if ( (cent->gent->client->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
7124 			&& cent->gent
7125 			&& cent->gent->activator )
7126 		{
7127 			centity_t	*sand_creature = &cg_entities[cent->gent->activator->s.number];
7128 			if ( sand_creature && sand_creature->gent )
7129 			{
7130 				mdxaBone_t	boltMatrix;
7131 				// Getting the bolt here
7132 				//in hand
7133 				vec3_t scAngles = {0};
7134 				scAngles[YAW] = sand_creature->lerpAngles[YAW];
7135 				gi.G2API_GetBoltMatrix( sand_creature->gent->ghoul2, sand_creature->gent->playerModel, sand_creature->gent->gutBolt,
7136 						&boltMatrix, scAngles, sand_creature->lerpOrigin, cg.time,
7137 						cgs.model_draw, sand_creature->currentState.modelScale );
7138 				// Storing ent position, bolt position, and bolt axis
7139 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent.origin );
7140 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, ent.axis[0] );
7141 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_X, ent.axis[1] );
7142 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, POSITIVE_Z, ent.axis[2] );
7143 				//FIXME: this is messing up our axis and turning us inside-out
7144 				if ( cent->gent->client->isRagging )
7145 				{//hack, ragdoll has you way at bottom of bounding box
7146 					VectorMA( ent.origin, 32, ent.axis[2], ent.origin );
7147 				}
7148 				VectorCopy( ent.origin, ent.oldorigin );
7149 				VectorCopy( ent.origin, ent.lightingOrigin );
7150 
7151 				vectoangles( ent.axis[0], cent->lerpAngles );
7152 				vec3_t temp;
7153 				vectoangles( ent.axis[2], temp );
7154 				cent->lerpAngles[ROLL] = -temp[PITCH];
7155 
7156 				VectorCopy( cent->lerpAngles, tempAngles );//tempAngles is needed a lot below
7157 				VectorCopy( ent.origin, cent->lerpOrigin );
7158 				VectorCopy( ent.origin, cent->gent->client->ps.origin );
7159 				cent->gent->client->ps.viewangles[YAW] = cent->lerpAngles[YAW];
7160 			}
7161 		}
7162 		else
7163 		{
7164 //---------------
7165 			CG_G2PlayerAngles( cent, ent.axis, tempAngles);
7166 			//Deal with facial expressions
7167 			CG_G2PlayerHeadAnims( cent );
7168 
7169 			/*
7170 			if ( cent->gent->client->ps.eFlags & EF_FORCE_DRAINED
7171 				&& !VectorCompare( cent->gent->client->ps.forceDrainOrg, vec3_origin ) )
7172 			{//HACKHACKHACK!!!! being drained
7173 				VectorCopy( cent->gent->client->ps.forceDrainOrg, ent.origin);
7174 			}
7175 			else
7176 			*/
7177 			{
7178 				VectorCopy( cent->lerpOrigin, ent.origin);
7179 			}
7180 
7181 			if (ent.modelScale[2] && ent.modelScale[2] != 1.0f)
7182 			{
7183 				ent.origin[2] += 24 * (ent.modelScale[2] - 1);
7184 			}
7185 			VectorCopy( ent.origin, ent.oldorigin);
7186 			VectorCopy( ent.origin, ent.lightingOrigin );
7187 		}
7188 
7189 		if ( cent->gent && cent->gent->client )
7190 		{
7191 			cent->gent->client->ps.legsYaw = tempAngles[YAW];
7192 		}
7193 		ScaleModelAxis(&ent);
7194 
7195 //HACK - add swoop model
7196 		/*
7197 		if ( cent->currentState.vehicleIndex != VEHICLE_NONE
7198 			&& g_vehicleInfo[cent->currentState.vehicleIndex].type == VH_SPEEDER )
7199 		{//add it at my origin
7200 			//FIXME: should be a G2 model
7201 			refEntity_t	swoopEnt;
7202 
7203 			memset (&swoopEnt, 0, sizeof(swoopEnt));
7204 
7205 			VectorCopy( cent->lerpOrigin, swoopEnt.origin );
7206 			VectorMA( swoopEnt.origin, -32, ent.axis[2], swoopEnt.origin );
7207 			VectorCopy( swoopEnt.origin, swoopEnt.oldorigin );
7208 			AnglesToAxis( cent->currentState.vehicleAngles, swoopEnt.axis );
7209 			swoopEnt.hModel = cgs.model_draw[g_vehicleInfo[cent->currentState.vehicleIndex].modelIndex];
7210 			cgi_R_AddRefEntityToScene( &swoopEnt );
7211 		}
7212 		*/
7213 //HACK - add swoop model
7214 
7215 extern vmCvar_t	cg_thirdPersonAlpha;
7216 
7217 		if ( (cent->gent->s.number == 0 || G_ControlledByPlayer( cent->gent )) )
7218 		{
7219 			float alpha = 1.0f;
7220 			if ( (cg.overrides.active&CG_OVERRIDE_3RD_PERSON_APH) )
7221 			{
7222 				alpha = cg.overrides.thirdPersonAlpha;
7223 			}
7224 			else
7225 			{
7226 				alpha = cg_thirdPersonAlpha.value;
7227 			}
7228 
7229 			if ( alpha < 1.0f )
7230 			{
7231 				ent.renderfx |= RF_ALPHA_FADE;
7232 				ent.shaderRGBA[3] = (unsigned char)(alpha * 255.0f);
7233 			}
7234 		}
7235 
7236 		if ( cg_debugHealthBars.integer )
7237 		{
7238 			if ( cent->gent && cent->gent->health > 0 && cent->gent->max_health > 0 )
7239 			{//draw a health bar over them
7240 				CG_AddHealthBarEnt( cent->currentState.clientNum );
7241 			}
7242 		}
7243 		CG_AddRefEntityWithPowerups( &ent, cent->currentState.powerups, cent );
7244 		VectorCopy( tempAngles, cent->renderAngles );
7245 
7246 		//Initialize all these to *some* valid data
7247 		VectorCopy( ent.origin, cent->gent->client->renderInfo.headPoint );
7248 		VectorCopy( ent.origin, cent->gent->client->renderInfo.handRPoint );
7249 		VectorCopy( ent.origin, cent->gent->client->renderInfo.handLPoint );
7250 		VectorCopy( ent.origin, cent->gent->client->renderInfo.footRPoint );
7251 		VectorCopy( ent.origin, cent->gent->client->renderInfo.footLPoint );
7252 		VectorCopy( ent.origin, cent->gent->client->renderInfo.torsoPoint );
7253 		VectorCopy( cent->lerpAngles, cent->gent->client->renderInfo.torsoAngles );
7254 		VectorCopy( ent.origin, cent->gent->client->renderInfo.crotchPoint );
7255 		if ( cent->currentState.number != 0
7256 			|| cg.renderingThirdPerson
7257 			|| cg.snap->ps.stats[STAT_HEALTH] <= 0
7258 			|| ( !cg.renderingThirdPerson && (cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) )//First person saber
7259 			)
7260 		{//in some third person mode or NPC
7261 			//we don't override thes in pure 1st person because they will be set before this func
7262 			VectorCopy( ent.origin, cent->gent->client->renderInfo.eyePoint );
7263 			VectorCopy( cent->lerpAngles, cent->gent->client->renderInfo.eyeAngles );
7264 			if ( !cent->gent->client->ps.saberInFlight )
7265 			{
7266 				VectorCopy( ent.origin, cent->gent->client->renderInfo.muzzlePoint );
7267 				VectorCopy( ent.axis[0], cent->gent->client->renderInfo.muzzleDir );
7268 			}
7269 		}
7270 		//now try to get the right data
7271 
7272 		mdxaBone_t	boltMatrix;
7273 		vec3_t		tempAxis, G2Angles = {0, tempAngles[YAW], 0};
7274 
7275 		if ( cent->gent->handRBolt != -1 )
7276 		{
7277 			//Get handRPoint
7278 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handRBolt,
7279 							&boltMatrix, G2Angles, ent.origin, cg.time,
7280 							cgs.model_draw, cent->currentState.modelScale );
7281 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.handRPoint );
7282 		}
7283 		if ( cent->gent->handLBolt != -1 )
7284 		{
7285 			//always get handLPoint too...?
7286 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->handLBolt,
7287 							&boltMatrix, G2Angles, ent.origin, cg.time,
7288 							cgs.model_draw, cent->currentState.modelScale );
7289 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.handLPoint );
7290 		}
7291 		if ( cent->gent->footLBolt != -1 )
7292 		{
7293 			//get the feet
7294 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footLBolt,
7295 							&boltMatrix, G2Angles, ent.origin, cg.time,
7296 							cgs.model_draw, cent->currentState.modelScale );
7297 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.footLPoint );
7298 		}
7299 
7300 		if ( cent->gent->footRBolt != -1 )
7301 		{
7302 			gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->footRBolt,
7303 							&boltMatrix, G2Angles, ent.origin, cg.time,
7304 							cgs.model_draw, cent->currentState.modelScale );
7305 			gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.footRPoint );
7306 		}
7307 
7308 		//Handle saber
7309 		if ( cent->gent
7310 			&& cent->gent->client
7311 			&& ( cent->currentState.weapon == WP_SABER || cent->gent->client->ps.saberInFlight )
7312 			&& cent->gent->client->NPC_class != CLASS_ATST )
7313 		{//FIXME: somehow saberactive is getting lost over the network
7314 			//loop this and do for both sabers
7315 			int	numSabers = 1;
7316 			if ( cent->gent->client->ps.dualSabers )
7317 			{
7318 				numSabers = 2;
7319 			}
7320 			for ( int saberNum = 0; saberNum < numSabers; saberNum++ )
7321 			{
7322 				if ( cent->gent->client->ps.saberEventFlags&SEF_INWATER )
7323 				{
7324 					cent->gent->client->ps.saber[saberNum].Deactivate();
7325 				}
7326 				//loop this and do for both blades
7327 				for ( int bladeNum = 0; bladeNum < cent->gent->client->ps.saber[saberNum].numBlades; bladeNum++ )
7328 				{
7329 					if ( !cent->gent->client->ps.saber[saberNum].blade[bladeNum].active ||
7330 						cent->gent->client->ps.saber[saberNum].blade[bladeNum].length > cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax )//hack around network lag for now
7331 					{//saber blade is off
7332 						if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length > 0 )
7333 						{
7334 							if ( cent->gent->client->ps.stats[STAT_HEALTH] <= 0 )
7335 							{//dead, didn't actively turn it off
7336 								cent->gent->client->ps.saber[saberNum].blade[bladeNum].length -= cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100;
7337 							}
7338 							else
7339 							{//actively turned it off, shrink faster
7340 								cent->gent->client->ps.saber[saberNum].blade[bladeNum].length -= cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100;
7341 							}
7342 						}
7343 					}
7344 					else
7345 					{//saber blade is on
7346 						if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length < cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax )
7347 						{
7348 							if ( !cent->gent->client->ps.saber[saberNum].blade[bladeNum].length )
7349 							{
7350 								qhandle_t saberOnSound = cgs.sound_precache[g_entities[cent->currentState.clientNum].client->ps.saber[saberNum].soundOn];
7351 								if ( !cent->gent->client->ps.weaponTime
7352 									&& !saberNum//first saber only
7353 									&& !bladeNum )//first blade only
7354 								{//make us play the turn on anim
7355 									cent->gent->client->ps.weaponstate = WEAPON_RAISING;
7356 									cent->gent->client->ps.weaponTime = 250;
7357 								}
7358 								if ( cent->gent->client->ps.saberInFlight && saberNum == 0 )
7359 								{//play it on the saber
7360 									if ( cg_saberOnSoundTime[cent->currentState.number] < cg.time )
7361 									{
7362 										cgi_S_UpdateEntityPosition( cent->gent->client->ps.saberEntityNum, g_entities[cent->gent->client->ps.saberEntityNum].currentOrigin );
7363 										cgi_S_StartSound (NULL, cent->gent->client->ps.saberEntityNum, CHAN_AUTO, saberOnSound );
7364 										cg_saberOnSoundTime[cent->currentState.number] = cg.time;//so we don't play multiple on sounds at one time
7365 									}
7366 								}
7367 								else
7368 								{
7369 									if ( cg_saberOnSoundTime[cent->currentState.number] < cg.time )
7370 									{
7371 										cgi_S_StartSound (NULL, cent->currentState.number, CHAN_AUTO, saberOnSound );
7372 										cg_saberOnSoundTime[cent->currentState.number] = cg.time;//so we don't play multiple on sounds at one time
7373 									}
7374 								}
7375 							}
7376 							if ( cg.frametime > 0 )
7377 							{
7378 								if ( PM_SuperBreakWinAnim( cent->gent->client->ps.torsoAnim ) )
7379 								{//just keep it full length!
7380 									//NOTE: does this mean it will go through walls now...?
7381 									cent->gent->client->ps.saber[saberNum].blade[bladeNum].length = cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax;
7382 								}
7383 								else
7384 								{
7385 									cent->gent->client->ps.saber[saberNum].blade[bladeNum].length += cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100;
7386 								}
7387 							}
7388 							if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length > cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax )
7389 							{
7390 								cent->gent->client->ps.saber[saberNum].blade[bladeNum].length = cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax;
7391 							}
7392 						}
7393 					}
7394 
7395 					if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length > 0 )
7396 					{
7397 						if ( !cent->gent->client->ps.saberInFlight || saberNum != 0 )//&& cent->gent->client->ps.saberActive)
7398 						{//holding the saber in-hand
7399 		//						CGhoul2Info *currentModel = &cent->gent->ghoul2[1];
7400 		//						CGhoul2Info *nextModel = &cent->gent->ghoul2[1];
7401 							//FIXME: need a version of this that *doesn't* need the mFileName in the ghoul2
7402 							//FIXME: use an actual surfaceIndex?
7403 							char	handName[MAX_QPATH];
7404 							if ( saberNum == 0 )
7405 							{
7406 								//this returns qfalse if it doesn't exist or isn't being rendered
7407 								if ( G_GetRootSurfNameWithVariant( cent->gent, "r_hand", handName, sizeof(handName) ) ) //!gi.G2API_GetSurfaceRenderStatus( &cent->gent->ghoul2[cent->gent->playerModel], "r_hand" ) )//surf is still on
7408 								{
7409 									CG_AddSaberBladeGo( cent, cent, NULL, ent.renderfx, cent->gent->weaponModel[saberNum], ent.origin, tempAngles, saberNum, bladeNum );
7410 									//CG_AddSaberBlades( cent, ent.renderfx, ent.origin, tempAngles, saberNum );
7411 								}//else, the limb will draw the blade itself
7412 							}
7413 							else if ( saberNum == 1 )
7414 							{
7415 								//this returns qfalse if it doesn't exist or isn't being rendered
7416 								if ( G_GetRootSurfNameWithVariant( cent->gent, "l_hand", handName, sizeof(handName) ) ) //!gi.G2API_GetSurfaceRenderStatus( &cent->gent->ghoul2[cent->gent->playerModel], "l_hand" ) )//surf is still on
7417 								{
7418 									CG_AddSaberBladeGo( cent, cent, NULL, ent.renderfx, cent->gent->weaponModel[saberNum], ent.origin, tempAngles, saberNum, bladeNum );
7419 									//CG_AddSaberBlades( cent, ent.renderfx, ent.origin, tempAngles, saberNum );
7420 								}//else, the limb will draw the blade itself
7421 							}
7422 						}//in-flight saber draws it's own blade
7423 					}
7424 					else
7425 					{
7426 						if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length < 0 )
7427 						{
7428 							cent->gent->client->ps.saber[saberNum].blade[bladeNum].length = 0;
7429 						}
7430 						//if ( cent->gent->client->ps.saberEventFlags&SEF_INWATER )
7431 						{
7432 							CG_CheckSaberInWater( cent, cent, saberNum, cent->gent->weaponModel[saberNum], ent.origin, tempAngles );
7433 						}
7434 					}
7435 					if ( cent->currentState.weapon == WP_SABER
7436 						&& (cent->gent->client->ps.saber[saberNum].blade[bladeNum].length > 0 || cent->gent->client->ps.saberInFlight) )
7437 					{
7438 						calcedMp = qtrue;
7439 					}
7440 				}
7441 			}
7442 			//add the light
7443 			if ( cent->gent->client->ps.dualSabers )
7444 			{
7445 				if ( cent->gent->client->ps.saber[0].Length() > 0.0f
7446 					&& !cent->gent->client->ps.saberInFlight )
7447 				{
7448 					if ( cent->gent->client->ps.saber[0].numBlades > 2 )
7449 					{// add blended light
7450 						CG_DoSaberLight( &cent->gent->client->ps.saber[0] );
7451 					}
7452 				}
7453 				if ( cent->gent->client->ps.saber[1].Length() > 0.0f )
7454 				{
7455 					if ( cent->gent->client->ps.saber[1].numBlades > 2 )
7456 					{// add blended light
7457 						CG_DoSaberLight( &cent->gent->client->ps.saber[1] );
7458 					}
7459 				}
7460 			}
7461 			else if ( cent->gent->client->ps.saber[0].Length() > 0.0f
7462 				&& !cent->gent->client->ps.saberInFlight )
7463 			{
7464 				if ( cent->gent->client->ps.saber[0].numBlades > 2 )
7465 				{// add blended light
7466 					CG_DoSaberLight( &cent->gent->client->ps.saber[0] );
7467 				}
7468 			}
7469 		}
7470 
7471 		if ( cent->currentState.number != 0
7472 			|| cg.renderingThirdPerson
7473 			|| cg.snap->ps.stats[STAT_HEALTH] <= 0
7474 			|| ( !cg.renderingThirdPerson && (cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) )//First person saber
7475 			)
7476 		{//if NPC, third person, or dead, unless using saber
7477 			//Get eyePoint & eyeAngles
7478 			/*
7479 			if ( cg.snap->ps.viewEntity > 0
7480 				&& cg.snap->ps.viewEntity < ENTITYNUM_WORLD
7481 				&& cg.snap->ps.viewEntity == cent->currentState.clientNum )
7482 			{//player is in an entity camera view, ME
7483 				VectorCopy( ent.origin, cent->gent->client->renderInfo.eyePoint );
7484 				VectorCopy( tempAngles, cent->gent->client->renderInfo.eyeAngles );
7485 				VectorCopy( ent.origin, cent->gent->client->renderInfo.headPoint );
7486 			}
7487 			else
7488 			*/if ( cent->gent->headBolt == -1 )
7489 			{//no headBolt
7490 				VectorCopy( ent.origin, cent->gent->client->renderInfo.eyePoint );
7491 				VectorCopy( tempAngles, cent->gent->client->renderInfo.eyeAngles );
7492 				VectorCopy( ent.origin, cent->gent->client->renderInfo.headPoint );
7493 			}
7494 			else
7495 			{
7496 				//FIXME: if head is missing, we should let the dismembered head set our eyePoint...
7497 				gi.G2API_GetBoltMatrix(cent->gent->ghoul2, cent->gent->playerModel, cent->gent->headBolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7498 				gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, cent->gent->client->renderInfo.eyePoint);
7499 				if ( cent->gent->client->NPC_class == CLASS_RANCOR )
7500 				{//temp hack
7501 					gi.G2API_GiveMeVectorFromMatrix(boltMatrix,	POSITIVE_X, tempAxis);
7502 				}
7503 				else
7504 				{
7505 					gi.G2API_GiveMeVectorFromMatrix(boltMatrix,	NEGATIVE_Y, tempAxis);
7506 				}
7507 				vectoangles( tempAxis, cent->gent->client->renderInfo.eyeAngles );
7508 				//estimate where the neck would be...
7509 				gi.G2API_GiveMeVectorFromMatrix(boltMatrix,	NEGATIVE_Z, tempAxis);//go down to find neck
7510 				VectorMA( cent->gent->client->renderInfo.eyePoint, 8, tempAxis, cent->gent->client->renderInfo.headPoint );
7511 
7512 				// Play the breath puffs (or not).
7513 				CG_BreathPuffs( cent, tempAngles, ent.origin );
7514 			}
7515 			//Get torsoPoint & torsoAngles
7516 			if (cent->gent->chestBolt>=0)
7517 			{
7518 				gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->chestBolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7519 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.torsoPoint );
7520 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Z, tempAxis );
7521 				vectoangles( tempAxis, cent->gent->client->renderInfo.torsoAngles );
7522 			}
7523 			else
7524 			{
7525 				VectorCopy( ent.origin, cent->gent->client->renderInfo.torsoPoint);
7526 				VectorClear(cent->gent->client->renderInfo.torsoAngles);
7527 			}
7528 			//get crotchPoint
7529 			if (cent->gent->crotchBolt>=0)
7530 			{
7531 				gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->crotchBolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7532 				gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.crotchPoint );
7533 			}
7534 			else
7535 			{
7536 				VectorCopy( ent.origin, cent->gent->client->renderInfo.crotchPoint);
7537 			}
7538 			//NOTE: these are used for any case where an NPC fires and the next shot needs to come out
7539 			//		of a new barrel/point.  That way the muzzleflash will draw on the old barrel/point correctly
7540 			//NOTE: I'm only doing this for the saboteur right now - AT-STs might need this... others?
7541 			vec3_t oldMP = {0,0,0};
7542 			vec3_t oldMD = {0,0,0};
7543 
7544 			if( !calcedMp )
7545 			{
7546 				if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_ATST)
7547 				{//FIXME: different for the three different weapon positions
7548 					mdxaBone_t		boltMatrix;
7549 					int				bolt;
7550 					entityState_t	*es;
7551 
7552 					es = &cent->currentState;
7553 
7554 					// figure out where the actual model muzzle is
7555 					if (es->weapon == WP_ATST_MAIN)
7556 					{
7557 						if ( !es->number )
7558 						{//player, just use left one, I guess
7559 							if ( cent->gent->alt_fire )
7560 							{
7561 								bolt = cent->gent->handRBolt;
7562 							}
7563 							else
7564 							{
7565 								bolt = cent->gent->handLBolt;
7566 							}
7567 						}
7568 						else if (cent->gent->count > 0)
7569 						{
7570 							cent->gent->count = 0;
7571 							bolt = cent->gent->handLBolt;
7572 						}
7573 						else
7574 						{
7575 							cent->gent->count = 1;
7576 							bolt = cent->gent->handRBolt;
7577 						}
7578 					}
7579 					else	// ATST SIDE weapons
7580 					{
7581 						if ( cent->gent->alt_fire)
7582 						{
7583 							bolt = cent->gent->genericBolt2;
7584 						}
7585 						else
7586 						{
7587 							bolt = cent->gent->genericBolt1;
7588 						}
7589 					}
7590 
7591 					gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, bolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7592 
7593 					// work the matrix axis stuff into the original axis and origins used.
7594 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.muzzlePoint );
7595 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, cent->gent->client->renderInfo.muzzleDir );
7596 				}
7597 				else if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_GALAKMECH )
7598 				{
7599 					int bolt = -1;
7600 					if ( cent->gent->lockCount )
7601 					{//using the big laser beam
7602 						bolt = cent->gent->handLBolt;
7603 					}
7604 					else//repeater
7605 					{
7606 						if ( cent->gent->alt_fire )
7607 						{//fire from the lower barrel (not that anyone will ever notice this, but...)
7608 							bolt = cent->gent->genericBolt3;
7609 						}
7610 						else
7611 						{
7612 							bolt = cent->gent->handRBolt;
7613 						}
7614 					}
7615 
7616 					if ( bolt == -1 )
7617 					{
7618 						VectorCopy( ent.origin, cent->gent->client->renderInfo.muzzlePoint );
7619 						AngleVectors( tempAngles, cent->gent->client->renderInfo.muzzleDir, NULL, NULL );
7620 					}
7621 					else
7622 					{
7623 						gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, bolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7624 
7625 						// work the matrix axis stuff into the original axis and origins used.
7626 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.muzzlePoint );
7627 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, cent->gent->client->renderInfo.muzzleDir );
7628 					}
7629 				}
7630 				// Set the Vehicle Muzzle Point and Direction.
7631 				else if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_VEHICLE )
7632 				{
7633 					// Get the Position and Direction of the Tag and use that as our Muzzles Properties.
7634 					mdxaBone_t	boltMatrix;
7635 					vec3_t		velocity;
7636 					VectorCopy(cent->gent->client->ps.velocity, velocity);
7637 					velocity[2] = 0;
7638  					for ( int i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
7639 					{
7640  						if ( cent->gent->m_pVehicle->m_iMuzzleTag[i] != -1 )
7641 						{
7642 							gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->playerModel, cent->gent->m_pVehicle->m_iMuzzleTag[i], &boltMatrix, cent->lerpAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7643 							gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->m_pVehicle->m_Muzzles[i].m_vMuzzlePos );
7644 							gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, cent->gent->m_pVehicle->m_Muzzles[i].m_vMuzzleDir );
7645   							VectorMA(cent->gent->m_pVehicle->m_Muzzles[i].m_vMuzzlePos, 0.075f, velocity, cent->gent->m_pVehicle->m_Muzzles[i].m_vMuzzlePos);
7646 						}
7647 						else
7648 						{
7649 							break;
7650 						}
7651 					}
7652 				}
7653 				else if ( cent->gent->client && cent->gent->NPC//client NPC
7654 					/*
7655 					&& cent->gent->client->NPC_class == CLASS_REBORN//cultist
7656 					&& cent->gent->NPC->rank >= RANK_LT_COMM//commando
7657 					*/
7658 					&& cent->gent->s.weapon == WP_BLASTER_PISTOL//using blaster pistol
7659 					&& cent->gent->weaponModel[1] )//one in each hand
7660 				{
7661 					qboolean getBoth = qfalse;
7662 					int	oldOne = 0;
7663 					if ( cent->muzzleFlashTime > 0 && wData && !(cent->currentState.eFlags & EF_LOCKED_TO_WEAPON ))
7664 					{//we need to get both muzzles since we're toggling and we fired recently
7665 						getBoth = qtrue;
7666 						oldOne = (cent->gent->count)?0:1;
7667 					}
7668 					if ( ( cent->gent->weaponModel[cent->gent->count] != -1)
7669 						&& ( cent->gent->ghoul2.size() > cent->gent->weaponModel[cent->gent->count] )
7670 						&& ( cent->gent->ghoul2[cent->gent->weaponModel[cent->gent->count]].mModelindex != -1) )
7671 					{//get whichever one we're using now
7672 						mdxaBone_t	boltMatrix;
7673 						// figure out where the actual model muzzle is
7674 						gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->weaponModel[cent->gent->count], 0, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7675 						// work the matrix axis stuff into the original axis and origins used.
7676 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.muzzlePoint );
7677 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, cent->gent->client->renderInfo.muzzleDir );
7678 					}
7679 					//get the old one too, if needbe, and store it in muzzle2
7680 					if ( getBoth
7681 						&& ( cent->gent->weaponModel[oldOne] != -1) //have a second weapon
7682 						&& ( cent->gent->ghoul2.size() > cent->gent->weaponModel[oldOne] ) //have a valid ghoul model index
7683 						&& ( cent->gent->ghoul2[cent->gent->weaponModel[oldOne]].mModelindex != -1) )//model exists and was loaded
7684 					{//saboteur commando, toggle the muzzle point back and forth between the two pistols each time he fires
7685 						mdxaBone_t	boltMatrix;
7686 						// figure out where the actual model muzzle is
7687 						gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->weaponModel[oldOne], 0, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7688 						// work the matrix axis stuff into the original axis and origins used.
7689 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, oldMP );
7690 						gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, oldMD );
7691 					}
7692 				}
7693 				else if (( cent->gent->weaponModel[0] != -1) &&
7694 					( cent->gent->ghoul2.size() > cent->gent->weaponModel[0] ) &&
7695 					( cent->gent->ghoul2[cent->gent->weaponModel[0]].mModelindex != -1))
7696 				{
7697 					mdxaBone_t	boltMatrix;
7698 					// figure out where the actual model muzzle is
7699 					gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->weaponModel[0], 0, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale );
7700 					// work the matrix axis stuff into the original axis and origins used.
7701 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.muzzlePoint );
7702 					gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, cent->gent->client->renderInfo.muzzleDir );
7703 				}
7704 				else
7705 				{
7706 					VectorCopy( cent->gent->client->renderInfo.eyePoint, cent->gent->client->renderInfo.muzzlePoint );
7707 					AngleVectors( cent->gent->client->renderInfo.eyeAngles, cent->gent->client->renderInfo.muzzleDir, NULL, NULL );
7708 				}
7709 				cent->gent->client->renderInfo.mPCalcTime = cg.time;
7710 			}
7711 
7712 			// Draw Vehicle Muzzle Flashs.
7713 			if ( cent->gent && cent->gent->client && cent->gent->client->NPC_class == CLASS_VEHICLE )
7714 			{
7715 				for ( int i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
7716 				{
7717 					/*if ( cent->gent->m_pVehicle->m_pVehicleInfo->weap1ID == cent->gent->m_pVehicle->m_pVehicleInfo->weapMuzzle[i] )
7718 					{
7719 						iDelay = cent->gent->m_pVehicle->m_pVehicleInfo->weap1Delay;
7720 					}
7721 					else if ( cent->gent->m_pVehicle->m_pVehicleInfo->weap2ID == cent->gent->m_pVehicle->m_pVehicleInfo->weapMuzzle[i] )
7722 					{
7723 						iDelay = cent->gent->m_pVehicle->m_pVehicleInfo->weap2Delay;
7724 					}
7725 
7726 					if ( cent->gent->m_pVehicle->m_Muzzles[i].m_iMuzzleWait - cg.time > ( iDelay - 500 ) )*/
7727 
7728 					if ( cent->gent->m_pVehicle->m_Muzzles[i].m_bFired )
7729 					{
7730 						const char *effect = &weaponData[ cent->gent->m_pVehicle->m_pVehicleInfo->weapMuzzle[i] ].mMuzzleEffect[0];
7731 						if ( effect )
7732 						{
7733 							theFxScheduler.PlayEffect( effect, cent->gent->m_pVehicle->m_Muzzles[i].m_vMuzzlePos, cent->gent->m_pVehicle->m_Muzzles[i].m_vMuzzleDir );
7734 						}
7735 						cent->gent->m_pVehicle->m_Muzzles[i].m_bFired = false;
7736 					}
7737 				}
7738 			}
7739 			// Pick the right effect for the type of weapon we are, defaults to no effect unless explicitly specified
7740 			else if ( cent->muzzleFlashTime > 0 && wData && !(cent->currentState.eFlags & EF_LOCKED_TO_WEAPON ))
7741 			{
7742 				const char *effect = NULL;
7743 
7744 				cent->muzzleFlashTime  = 0;
7745 
7746 				// Try and get a default muzzle so we have one to fall back on
7747 				if ( wData->mMuzzleEffect[0] )
7748 				{
7749 					effect = &wData->mMuzzleEffect[0];
7750 				}
7751 
7752 				if ( cent->altFire )
7753 				{
7754 					// We're alt-firing, so see if we need to override with a custom alt-fire effect
7755 					if ( wData->mAltMuzzleEffect[0] )
7756 					{
7757 						effect = &wData->mAltMuzzleEffect[0];
7758 					}
7759 				}
7760 
7761 				if (/*( cent->currentState.eFlags & EF_FIRING || cent->currentState.eFlags & EF_ALT_FIRING ) &&*/ effect )
7762 				{
7763 					if ( cent->gent && cent->gent->NPC )
7764 					{
7765 						if ( !VectorCompare( oldMP, vec3_origin )
7766 							&& !VectorCompare( oldMD, vec3_origin ) )
7767 						{//we have an old muzzlePoint we want to use
7768 							theFxScheduler.PlayEffect( effect, oldMP, oldMD );
7769 						}
7770 						else
7771 						{//use the current one
7772 							theFxScheduler.PlayEffect( effect, cent->gent->client->renderInfo.muzzlePoint,
7773 														cent->gent->client->renderInfo.muzzleDir );
7774 						}
7775 					}
7776 					else
7777 					{
7778 						// We got an effect and we're firing, so let 'er rip.
7779 						theFxScheduler.PlayEffect( effect, cent->currentState.clientNum );
7780 					}
7781 				}
7782 			}
7783 
7784 			//play special force effects
7785 			/*
7786 			if ( cent->gent->NPC && ( cent->gent->NPC->confusionTime > cg.time || cent->gent->NPC->charmedTime > cg.time || cent->gent->NPC->controlledTime > cg.time) )
7787 			{// we are currently confused, so play an effect at the headBolt position
7788 				if ( TIMER_Done( cent->gent, "confusionEffectDebounce" ) )
7789 				{//ARGH!!!
7790 					theFxScheduler.PlayEffect( cgs.effects.forceConfusion, cent->gent->client->renderInfo.eyePoint );
7791 					TIMER_Set( cent->gent, "confusionEffectDebounce", 1000 );
7792 				}
7793 			}
7794 			*/
7795 
7796 			if ( cent->gent->client && cent->gent->forcePushTime > cg.time )
7797 			{//being pushed
7798 				CG_ForcePushBodyBlur( cent, ent.origin, tempAngles );
7799 			}
7800 
7801 			//This is now being done via an effect and the animevents.cfg
7802 			//if ( cent->gent->client->ps.powerups[PW_FORCE_PUSH] > cg.time ||
7803 			if ( (cent->gent->client->ps.forcePowersActive & (1<<FP_GRIP)) )
7804 			{//doing the gripping
7805 				//FIXME: effect?
7806 				CG_ForcePushBlur( cent->gent->client->renderInfo.handLPoint, qtrue );
7807 			}
7808 
7809 			if ( cent->gent->client->ps.eFlags & EF_FORCE_GRIPPED )
7810 			{//being gripped
7811 				CG_ForcePushBlur( cent->gent->client->renderInfo.headPoint, qtrue );
7812 			}
7813 
7814 			if ( cent->gent->client && cent->gent->client->ps.powerups[PW_SHOCKED] > cg.time )
7815 			{//being electrocuted
7816 				CG_ForceElectrocution( cent, ent.origin, tempAngles, cgs.media.boltShader );
7817 			}
7818 
7819 			if ( cent->gent->client->ps.eFlags & EF_FORCE_DRAINED
7820 				|| (cent->currentState.powerups&(1<<PW_DRAINED)) )
7821 			{//being drained
7822 				//do red electricity lines off them and red drain shell on them
7823 				CG_ForceElectrocution( cent, ent.origin, tempAngles, cgs.media.drainShader, qtrue );
7824 			}
7825 
7826 			if ( cent->gent->client->ps.forcePowersActive&(1<<FP_LIGHTNING) )
7827 			{//doing the electrocuting
7828 				//FIXME: if the target is absorbing or blocking lightning w/saber, draw a beam from my hand to his (hand?chest?saber?)
7829 				vec3_t tAng, fxDir;
7830 				VectorCopy( cent->lerpAngles, tAng );
7831 				if ( cent->gent->client->ps.forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_2 )
7832 				{//arc
7833 					vec3_t	fxAxis[3];
7834 					AnglesToAxis( tAng, fxAxis );
7835 					theFxScheduler.PlayEffect( cgs.effects.forceLightningWide, cent->gent->client->renderInfo.handLPoint, fxAxis );
7836 					if ( cent->gent->client->ps.torsoAnim == BOTH_FORCE_2HANDEDLIGHTNING
7837 						|| cent->gent->client->ps.torsoAnim == BOTH_FORCE_2HANDEDLIGHTNING_START
7838 						|| cent->gent->client->ps.torsoAnim == BOTH_FORCE_2HANDEDLIGHTNING_HOLD
7839 						|| cent->gent->client->ps.torsoAnim == BOTH_FORCE_2HANDEDLIGHTNING_RELEASE )
7840 					{//jackin' 'em up, Palpatine-style
7841 						theFxScheduler.PlayEffect( cgs.effects.forceLightningWide, cent->gent->client->renderInfo.handRPoint, fxAxis );
7842 					}
7843 				}
7844 				else
7845 				{//line
7846 					AngleVectors( tAng, fxDir, NULL, NULL );
7847 					theFxScheduler.PlayEffect( cgs.effects.forceLightning, cent->gent->client->renderInfo.handLPoint, fxDir );
7848 				}
7849 			}
7850 
7851 			if ( (cent->gent->client->ps.eFlags&EF_POWERING_ROSH) )
7852 			{
7853 				vec3_t tAng, fxDir;
7854 				VectorCopy( cent->lerpAngles, tAng );
7855 				AngleVectors( tAng, fxDir, NULL, NULL );
7856 				theFxScheduler.PlayEffect( cgs.effects.forceDrain, cent->gent->client->renderInfo.handLPoint, fxDir );//theFxScheduler.RegisterEffect( "force/dr1" )
7857 			}
7858 
7859 			if ( cent->gent->client->ps.forcePowersActive&(1<<FP_DRAIN)
7860 				&& cent->gent->client->ps.forceDrainEntityNum >= ENTITYNUM_WORLD )
7861 			{//doing the draining and not on a single person
7862 				vec3_t tAng, fxDir;
7863 				VectorCopy( cent->lerpAngles, tAng );
7864 				if ( cent->gent->client->ps.forcePowerLevel[FP_DRAIN] > FORCE_LEVEL_2 )
7865 				{//arc
7866 					vec3_t	fxAxis[3];
7867 					AnglesToAxis( tAng, fxAxis );
7868 					theFxScheduler.PlayEffect( cgs.effects.forceDrainWide, cent->gent->client->renderInfo.handLPoint, fxAxis );
7869 				}
7870 				else
7871 				{//line
7872 					AngleVectors( tAng, fxDir, NULL, NULL );
7873 					theFxScheduler.PlayEffect( cgs.effects.forceDrain, cent->gent->client->renderInfo.handLPoint, fxDir );
7874 				}
7875 			}
7876 			//spotlight?
7877 			if ( (cent->currentState.eFlags&EF_SPOTLIGHT) )
7878 			{//FIXME: player's view should glare/flare if look at this... maybe build into the effect?
7879 				// hack for the spotlight
7880 				vec3_t	org, eyeFwd;
7881 
7882 				AngleVectors( cent->gent->client->renderInfo.eyeAngles, eyeFwd, NULL, NULL );
7883 				theFxScheduler.PlayEffect( "rockettrooper/light_cone", cent->gent->client->renderInfo.eyePoint, eyeFwd );
7884 				// stay a bit back from the server-side's trace impact point...this may not be enough?
7885 				VectorMA( cent->gent->client->renderInfo.eyePoint, cent->gent->speed - 5, eyeFwd, org );
7886 				float radius = cent->gent->speed;
7887 				if ( radius < 128.0f )
7888 				{
7889 					radius = 128.0f;
7890 				}
7891 				else if ( radius > 1024.0f )
7892 				{
7893 					radius = 1024.0f;
7894 				}
7895 				cgi_R_AddLightToScene( org, radius, 1.0f, 1.0f, 1.0f );
7896 			}
7897 		}
7898 		//"refraction" effect -rww
7899 		if ( cent->gent->client->ps.powerups[PW_FORCE_PUSH] > cg.time )
7900 		{
7901 			CG_ForcePushRefraction(cent->gent->client->renderInfo.handLPoint, cent);
7902 		}
7903 		else if ( cent->gent->client->ps.powerups[PW_FORCE_PUSH_RHAND] > cg.time )
7904 		{
7905 			CG_ForcePushRefraction(cent->gent->client->renderInfo.handRPoint, cent);
7906 		}
7907 		else
7908 		{
7909 			cent->gent->client->ps.forcePowersActive &= ~( 1 << FP_PULL );
7910 		}
7911 
7912 		//bolted effects
7913 		CG_BoltedEffects( cent, ent.origin, tempAngles );
7914 		//As good a place as any, I suppose, to do this keyframed sound thing
7915 		CGG2_AnimEvents( cent );
7916 		//setup old system for gun to look at
7917 		//CG_RunLerpFrame( ci, &cent->pe.torso, cent->gent->client->ps.torsoAnim, cent->gent->client->renderInfo.torsoFpsMod, cent->gent->s.number );
7918 		if ( cent->gent && cent->gent->client && cent->gent->client->ps.weapon == WP_SABER )
7919 		{
7920 extern qboolean PM_KickingAnim( int anim );
7921 			if ( !PM_KickingAnim( cent->gent->client->ps.torsoAnim )
7922 				|| cent->gent->client->ps.torsoAnim == BOTH_A7_KICK_S )
7923 			{//not kicking (unless it's the spinning kick)
7924 				if ( cg_timescale.value < 1.0f && (cent->gent->client->ps.forcePowersActive&(1<<FP_SPEED)) )
7925 				{
7926 					int wait = floor( (float)FRAMETIME/2.0f );
7927 					//sanity check
7928 					if ( cent->gent->client->ps.saberDamageDebounceTime - cg.time > wait )
7929 					{//when you unpause the game with force speed on, the time gets *really* wiggy...
7930 						cent->gent->client->ps.saberDamageDebounceTime = cg.time + wait;
7931 					}
7932 					if ( cent->gent->client->ps.saberDamageDebounceTime <= cg.time )
7933 					{
7934 extern void WP_SabersDamageTrace( gentity_t *ent, qboolean noEffects );
7935 extern void WP_SaberUpdateOldBladeData( gentity_t *ent );
7936 						//FIXME: this causes an ASSLOAD of effects
7937 						WP_SabersDamageTrace( cent->gent, qtrue );
7938 						WP_SaberUpdateOldBladeData( cent->gent );
7939 						cent->gent->client->ps.saberDamageDebounceTime = cg.time + floor((float)wait*cg_timescale.value);
7940 					}
7941 				}
7942 			}
7943 		}
7944 	}
7945 	else
7946 	{
7947 	refEntity_t		legs;
7948 	refEntity_t		torso;
7949 	refEntity_t		head;
7950 	refEntity_t		gun;
7951 	refEntity_t		flash;
7952 	refEntity_t		flashlight;
7953 	int				renderfx, i;
7954 	const weaponInfo_t	*weapon;
7955 
7956 
7957 /*
7958 Ghoul2 Insert End
7959 */
7960 
7961 	memset( &legs, 0, sizeof(legs) );
7962 	memset( &torso, 0, sizeof(torso) );
7963 	memset( &head, 0, sizeof(head) );
7964 	memset( &gun, 0, sizeof(gun) );
7965 	memset( &flash, 0, sizeof(flash) );
7966 	memset( &flashlight, 0, sizeof(flashlight) );
7967 
7968 	// Weapon sounds may need to be stopped, so check now
7969 	CG_StopWeaponSounds( cent );
7970 
7971 	//FIXME: pass in the axis/angles offset between the tag_torso and the tag_head?
7972 	// get the rotation information
7973 	CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis );
7974 	if ( cent->gent && cent->gent->client )
7975 	{
7976 		cent->gent->client->ps.legsYaw = cent->lerpAngles[YAW];
7977 	}
7978 
7979 	// get the animation state (after rotation, to allow feet shuffle)
7980 	// NB: Also plays keyframed animSounds (Bob- hope you dont mind, I was here late and at about 5:30 Am i needed to do something to keep me awake and i figured you wouldn't mind- you might want to check it, though, to make sure I wasn't smoking crack and missed something important, it is pretty late and I'm getting pretty close to being up for 24 hours here, so i wouldn't doubt if I might have messed something up, but i tested it and it looked right.... noticed in old code base i was doing it wrong too, whic            h explains why I was getting so many damn sounds all the time!  I had to lower the probabilities because it seemed like i was getting way too many sounds, and that was the problem!  Well, should be fixed now I think...)
7981 	CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp,
7982 		 &torso.oldframe, &torso.frame, &torso.backlerp );
7983 
7984 	cent->gent->client->renderInfo.legsFrame = cent->pe.legs.frame;
7985 	cent->gent->client->renderInfo.torsoFrame = cent->pe.torso.frame;
7986 
7987 	// add powerups floating behind the player
7988 	CG_PlayerPowerups( cent );
7989 
7990 	// add the shadow
7991 	shadow = CG_PlayerShadow( cent, &shadowPlane );
7992 
7993 	// add a water splash if partially in and out of water
7994 	CG_PlayerSplash( cent );
7995 
7996 	// get the player model information
7997 	renderfx = 0;
7998 	if ( !cg.renderingThirdPerson || cg.zoomMode )
7999 	{
8000 		if ( cg.snap->ps.viewEntity <= 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD)
8001 		{//no viewentity
8002 			if ( cent->currentState.number == cg.snap->ps.clientNum )
8003 			{//I am the player
8004 				if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE )
8005 				{//not using saber or fists
8006 					renderfx = RF_THIRD_PERSON;			// only draw in mirrors
8007 				}
8008 			}
8009 		}
8010 		else if ( cent->currentState.number == cg.snap->ps.viewEntity )
8011 		{//I am the view entity
8012 			if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE )
8013 			{//not using saber or fists
8014 				renderfx = RF_THIRD_PERSON;			// only draw in mirrors
8015 			}
8016 		}
8017 	}
8018 
8019 	if ( (cg_shadows.integer == 2) || (cg_shadows.integer == 3 && shadow) )
8020 	{
8021 		renderfx |= RF_SHADOW_PLANE;
8022 	}
8023 	renderfx |= RF_LIGHTING_ORIGIN;			// use the same origin for all
8024 	if ( cent->gent->NPC && cent->gent->NPC->scriptFlags & SCF_MORELIGHT )
8025 	{
8026 		renderfx |= RF_MORELIGHT;			//bigger than normal min light
8027 	}
8028 
8029 	if ( cent->gent && cent->gent->client )
8030 	{
8031 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.headPoint );
8032 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.handRPoint );
8033 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.handLPoint );
8034 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.footRPoint );
8035 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.footLPoint );
8036 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.torsoPoint );
8037 		VectorCopy( cent->lerpAngles, cent->gent->client->renderInfo.torsoAngles );
8038 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.crotchPoint );
8039 	}
8040 	if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD && cg.snap->ps.viewEntity == cent->currentState.clientNum )
8041 	{//player is in an entity camera view, ME
8042 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.eyePoint );
8043 		VectorCopy( cent->lerpAngles, cent->gent->client->renderInfo.eyeAngles );
8044 		VectorCopy( cent->lerpOrigin, cent->gent->client->renderInfo.headPoint );
8045 	}
8046 	//
8047 	// add the legs
8048 	//
8049 	legs.hModel = ci->legsModel;
8050 	legs.customSkin = ci->legsSkin;
8051 
8052 	VectorCopy( cent->lerpOrigin, legs.origin );
8053 
8054 	//Scale applied to a refEnt will apply to any models attached to it...
8055 	//This seems to copy the scale to every piece attached, kinda cool, but doesn't
8056 	//allow the body to be scaled up without scaling a bolt on or whatnot...
8057 	//Only apply scale if it's not 100% scale...
8058 	if(cent->currentState.modelScale[0] != 0.0f)
8059 	{
8060 		VectorScale( legs.axis[0], cent->currentState.modelScale[0], legs.axis[0] );
8061 		legs.nonNormalizedAxes = qtrue;
8062 	}
8063 
8064 	if(cent->currentState.modelScale[1] != 0.0f)
8065 	{
8066 		VectorScale( legs.axis[1], cent->currentState.modelScale[1], legs.axis[1] );
8067 		legs.nonNormalizedAxes = qtrue;
8068 	}
8069 
8070 	if(cent->currentState.modelScale[2] != 0.0f)
8071 	{
8072 		VectorScale( legs.axis[2], cent->currentState.modelScale[2], legs.axis[2] );
8073 		legs.nonNormalizedAxes = qtrue;
8074 		if ( !staticScale )
8075 		{
8076 			//FIXME:? need to know actual height of leg model bottom to origin, not hardcoded
8077 			legs.origin[2] += 24 * (cent->currentState.modelScale[2] - 1);
8078 		}
8079 	}
8080 
8081 	VectorCopy( legs.origin, legs.lightingOrigin );
8082 	legs.shadowPlane = shadowPlane;
8083 	legs.renderfx = renderfx;
8084 	VectorCopy (legs.origin, legs.oldorigin);	// don't positionally lerp at all
8085 
8086 	CG_AddRefEntityWithPowerups( &legs, cent->currentState.powerups, cent );
8087 
8088 	// if the model failed, allow the default nullmodel to be displayed
8089 	if (!legs.hModel)
8090 	{
8091 		return;
8092 	}
8093 
8094 	//
8095 	// add the torso
8096 	//
8097 	torso.hModel = ci->torsoModel;
8098 	if (torso.hModel)
8099 	{
8100 		orientation_t	tag_torso;
8101 
8102 		torso.customSkin = ci->torsoSkin;
8103 
8104 		VectorCopy( cent->lerpOrigin, torso.lightingOrigin );
8105 
8106 		CG_PositionRotatedEntityOnTag( &torso, &legs, legs.hModel, "tag_torso", &tag_torso );
8107 		VectorCopy( torso.origin, cent->gent->client->renderInfo.torsoPoint );
8108 		vectoangles( tag_torso.axis[0], cent->gent->client->renderInfo.torsoAngles );
8109 
8110 		torso.shadowPlane = shadowPlane;
8111 		torso.renderfx = renderfx;
8112 
8113 		CG_AddRefEntityWithPowerups( &torso, cent->currentState.powerups, cent );
8114 
8115 		//
8116 		// add the head
8117 		//
8118 		head.hModel = ci->headModel;
8119 		if (head.hModel)
8120 		{
8121 			orientation_t	tag_head;
8122 
8123 			//Deal with facial expressions
8124 			//CG_PlayerHeadExtension( cent, &head );
8125 
8126 			VectorCopy( cent->lerpOrigin, head.lightingOrigin );
8127 
8128 			CG_PositionRotatedEntityOnTag( &head, &torso, torso.hModel, "tag_head", &tag_head );
8129 			VectorCopy( head.origin, cent->gent->client->renderInfo.headPoint );
8130 			vectoangles( tag_head.axis[0], cent->gent->client->renderInfo.headAngles );
8131 
8132 			head.shadowPlane = shadowPlane;
8133 			head.renderfx = renderfx;
8134 
8135 			CG_AddRefEntityWithPowerups( &head, cent->currentState.powerups, cent );
8136 
8137 			if ( cent->gent && cent->gent->NPC && ( cent->gent->NPC->confusionTime > cg.time || cent->gent->NPC->charmedTime > cg.time || cent->gent->NPC->controlledTime > cg.time) )
8138 			{
8139 				// we are currently confused, so play an effect
8140 				if ( TIMER_Done( cent->gent, "confusionEffectDebounce" ) )
8141 				{//ARGH!!!
8142 					theFxScheduler.PlayEffect( cgs.effects.forceConfusion, head.origin );
8143 					TIMER_Set( cent->gent, "confusionEffectDebounce", 1000 );
8144 				}
8145 			}
8146 
8147 			if ( !calcedMp )
8148 			{//First person player's eyePoint and eyeAngles should be copies from cg.refdef...
8149 				//Calc this client's eyepoint
8150 				VectorCopy( head.origin, cent->gent->client->renderInfo.eyePoint );
8151 				// race is gone, eyepoint should refer to the tag/bolt on the model... if this breaks something let me know - dmv
8152 			//	VectorMA( cent->gent->client->renderInfo.eyePoint, CG_EyePointOfsForRace[cent->gent->client->race][1]*scaleFactor[2], head.axis[2], cent->gent->client->renderInfo.eyePoint );//up
8153 			//	VectorMA( cent->gent->client->renderInfo.eyePoint, CG_EyePointOfsForRace[cent->gent->client->race][0]*scaleFactor[0], head.axis[0], cent->gent->client->renderInfo.eyePoint );//forward
8154 				//Calc this client's eyeAngles
8155 				vectoangles( head.axis[0], cent->gent->client->renderInfo.eyeAngles );
8156 			}
8157 		}
8158 		else
8159 		{
8160 			VectorCopy( torso.origin, cent->gent->client->renderInfo.eyePoint );
8161 			cent->gent->client->renderInfo.eyePoint[2] += cent->gent->maxs[2] - 4;
8162 			vectoangles( torso.axis[0], cent->gent->client->renderInfo.eyeAngles );
8163 		}
8164 
8165 		//
8166 		// add the gun
8167 		//
8168 		CG_RegisterWeapon( cent->currentState.weapon );
8169 		weapon = &cg_weapons[cent->currentState.weapon];
8170 
8171 		gun.hModel = weapon->weaponWorldModel;
8172 		if (gun.hModel)
8173 		{
8174 			qboolean drawGun = qtrue;
8175 			//FIXME: allow scale, animation and angle offsets
8176 			VectorCopy( cent->lerpOrigin, gun.lightingOrigin );
8177 
8178 			//FIXME: allow it to be put anywhere and move this out of if(torso.hModel)
8179 			//Will have to call CG_PositionRotatedEntityOnTag
8180 
8181 			CG_PositionEntityOnTag( &gun, &torso, torso.hModel, "tag_weapon");
8182 
8183 //--------------------- start saber hacks
8184 /*
8185 			if ( cent->gent && cent->gent->client && ( cent->currentState.weapon == WP_SABER || cent->gent->client->ps.saberInFlight ) )
8186 			{
8187 				int numSabers = 1;
8188 				if ( cent->gent->client->ps.dualSabers )
8189 				{
8190 					numSabers = 2;
8191 				}
8192 				for ( int saberNum = 0; saberNum < numSabers; saberNum++ )
8193 				{
8194 					if ( !cent->gent->client->ps.saber[saberNum].active )//!cent->gent->client->ps.saberActive )
8195 					{//saber is off
8196 						if ( cent->gent->client->ps.saber[saberNum].length > 0 )
8197 						{
8198 							if ( cent->gent->client->ps.stats[STAT_HEALTH] <= 0 )
8199 							{//dead, didn't actively turn it off
8200 								cent->gent->client->ps.saber[saberNum].length -= cent->gent->client->ps.saber[saberNum].lengthMax/10 * cg.frametime/100;
8201 							}
8202 							else
8203 							{//actively turned it off, shrink faster
8204 								cent->gent->client->ps.saber[saberNum].length -= cent->gent->client->ps.saber[saberNum].lengthMax/3 * cg.frametime/100;
8205 							}
8206 						}
8207 						if ( cent->gent->client->ps.saber[saberNum].length < 0 )
8208 						{
8209 							cent->gent->client->ps.saber[saberNum].length = 0;
8210 						}
8211 					}
8212 					else if ( cent->gent->client->ps.saber[saberNum].length < cent->gent->client->ps.saber[saberNum].lengthMax )
8213 					{//saber is on
8214 						if ( !cent->gent->client->ps.saber[saberNum].length )
8215 						{
8216 							if ( cent->gent->client->ps.saberInFlight )
8217 							{//play it on the saber
8218 								cgi_S_UpdateEntityPosition( cent->gent->client->ps.saberEntityNum, g_entities[cent->gent->client->ps.saberEntityNum].currentOrigin );
8219 								cgi_S_StartSound (NULL, cent->gent->client->ps.saberEntityNum, CHAN_AUTO, cgs.sound_precache[cent->gent->client->ps.saber[0].soundOn] );
8220 							}
8221 							else
8222 							{
8223 								cgi_S_StartSound (NULL, cent->currentState.number, CHAN_AUTO, cgs.sound_precache[cent->gent->client->ps.saber[0].soundOn] );
8224 #ifdef _IMMERSION
8225 								cgi_FF_Start( cgi_FF_Register( "fffx/weapons/saber/saberon", FF_CHANNEL_WEAPON ), cent->currentState.number );
8226 #endif // _IMMERSION
8227 							}
8228 						}
8229 						cent->gent->client->ps.saber[saberNum].length += cent->gent->client->ps.saber[saberNum].lengthMax/6 * cg.frametime/100;//= saber[saberNum].lengthMax;
8230 						if ( cent->gent->client->ps.saber[saberNum].length > cent->gent->client->ps.saber[saberNum].lengthMax )
8231 						{
8232 							cent->gent->client->ps.saber[saberNum].length = cent->gent->client->ps.saber[saberNum].lengthMax;
8233 						}
8234 					}
8235 
8236 					if ( cent->gent->client->ps.saberInFlight )
8237 					{//not holding the saber in-hand
8238 						drawGun = qfalse;
8239 					}
8240 					if ( cent->gent->client->ps.saber[saberNum].length > 0 )
8241 					{
8242 						if ( !cent->gent->client->ps.saberInFlight )
8243 						{//holding the saber in-hand
8244 							CG_AddSaberBlade( cent, cent, &gun, renderfx, 0, NULL, NULL );
8245 							calcedMp = qtrue;
8246 						}
8247 					}
8248 					else
8249 					{
8250 						//if ( cent->gent->client->ps.saberEventFlags&SEF_INWATER )
8251 						{
8252 							CG_CheckSaberInWater( cent, cent, 0, 0, NULL, NULL );
8253 						}
8254 					}
8255 				}
8256 			}
8257 
8258 */
8259 //--------------------- end saber hacks
8260 
8261 			gun.shadowPlane = shadowPlane;
8262 			gun.renderfx = renderfx;
8263 
8264 			if ( drawGun )
8265 			{
8266 				CG_AddRefEntityWithPowerups( &gun,
8267 					(cent->currentState.powerups & ((1<<PW_CLOAKED)|(1<<PW_BATTLESUIT)) ),
8268 					cent );
8269 			}
8270 
8271 			//
8272 			// add the flash (even if invisible)
8273 			//
8274 
8275 			// impulse flash
8276 			if ( cent->muzzleFlashTime > 0 && wData && !(cent->currentState.eFlags & EF_LOCKED_TO_WEAPON ))
8277 			{
8278 				int effect = 0;
8279 
8280 				cent->muzzleFlashTime  = 0;
8281 
8282 				CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash");
8283 
8284 				// Try and get a default muzzle so we have one to fall back on
8285 				if ( wData->mMuzzleEffectID )
8286 				{
8287 					effect = wData->mMuzzleEffectID;
8288 				}
8289 
8290 				if ( cent->currentState.eFlags & EF_ALT_FIRING )
8291 				{
8292 					// We're alt-firing, so see if we need to override with a custom alt-fire effect
8293 					if ( wData->mAltMuzzleEffectID )
8294 					{
8295 						effect = wData->mAltMuzzleEffectID;
8296 					}
8297 				}
8298 
8299 				if (( cent->currentState.eFlags & EF_FIRING || cent->currentState.eFlags & EF_ALT_FIRING ) && effect )
8300 				{
8301 					vec3_t up={0,0,1}, ax[3];
8302 
8303 					VectorCopy( flash.axis[0], ax[0] );
8304 
8305 					CrossProduct( up, ax[0], ax[1] );
8306 					CrossProduct( ax[0], ax[1], ax[2] );
8307 
8308 					if (( cent->gent && cent->gent->NPC ) || cg.renderingThirdPerson )
8309 					{
8310 						theFxScheduler.PlayEffect( effect, flash.origin, ax );
8311 					}
8312 					else
8313 					{
8314 						// We got an effect and we're firing, so let 'er rip.
8315 						theFxScheduler.PlayEffect( effect, flash.origin, ax );
8316 					}
8317 				}
8318 			}
8319 
8320 			if ( !calcedMp && !(cent->currentState.eFlags & EF_LOCKED_TO_WEAPON ))
8321 			{// Set the muzzle point
8322 				orientation_t orientation;
8323 
8324 				cgi_R_LerpTag( &orientation, weapon->weaponModel, gun.oldframe, gun.frame,
8325 					1.0f - gun.backlerp, "tag_flash" );
8326 
8327 				// FIXME: allow origin offsets along tag?
8328 				VectorCopy( gun.origin, cent->gent->client->renderInfo.muzzlePoint );
8329 				for ( i = 0 ; i < 3 ; i++ )
8330 				{
8331 					VectorMA( cent->gent->client->renderInfo.muzzlePoint, orientation.origin[i], gun.axis[i], cent->gent->client->renderInfo.muzzlePoint );
8332 				}
8333 //				VectorCopy( gun.axis[0], cent->gent->client->renderInfo.muzzleDir );
8334 //				VectorAdd( gun.axis[0], orientation.axis[0], cent->gent->client->renderInfo.muzzleDir );
8335 //				VectorNormalize( cent->gent->client->renderInfo.muzzleDir );
8336 
8337 
8338 				cent->gent->client->renderInfo.mPCalcTime = cg.time;
8339 				// Weapon wasn't firing anymore, so ditch any weapon associated looping sounds.
8340 				//cent->gent->s.loopSound = 0;
8341 			}
8342 		}
8343 	}
8344 	else
8345 	{
8346 		VectorCopy( legs.origin, cent->gent->client->renderInfo.eyePoint );
8347 		cent->gent->client->renderInfo.eyePoint[2] += cent->gent->maxs[2] - 4;
8348 		vectoangles( legs.axis[0], cent->gent->client->renderInfo.eyeAngles );
8349 	}
8350 
8351 	}
8352 
8353 	//FIXME: for debug, allow to draw a cone of the NPC's FOV...
8354 	if ( cent->currentState.number == 0 && cg.renderingThirdPerson )
8355 	{
8356 		playerState_t *ps = &cg.predicted_player_state;
8357 
8358 		if (( ps->weaponstate == WEAPON_CHARGING_ALT && ps->weapon == WP_BRYAR_PISTOL )
8359 			|| ( ps->weaponstate == WEAPON_CHARGING_ALT && ps->weapon == WP_BLASTER_PISTOL )
8360 			|| ( ps->weapon == WP_BOWCASTER && ps->weaponstate == WEAPON_CHARGING )
8361 			|| ( ps->weapon == WP_DEMP2 && ps->weaponstate == WEAPON_CHARGING_ALT ))
8362 		{
8363 			int		shader = 0;
8364 			float	val = 0.0f, scale = 1.0f;
8365 			vec3_t	WHITE	= {1.0f,1.0f,1.0f};
8366 
8367 			if ( ps->weapon == WP_BRYAR_PISTOL
8368 				|| ps->weapon == WP_BLASTER_PISTOL )
8369 			{
8370 				// Hardcoded max charge time of 1 second
8371 				val = ( cg.time - ps->weaponChargeTime ) * 0.001f;
8372 				shader = cgi_R_RegisterShader( "gfx/effects/bryarFrontFlash" );
8373 			}
8374 			else if ( ps->weapon == WP_BOWCASTER )
8375 			{
8376 				// Hardcoded max charge time of 1 second
8377 				val = ( cg.time - ps->weaponChargeTime ) * 0.001f;
8378 				shader = cgi_R_RegisterShader( "gfx/effects/greenFrontFlash" );
8379 			}
8380 			else if ( ps->weapon == WP_DEMP2 )
8381 			{
8382 				// Hardcoded max charge time of 1 second
8383 				val = ( cg.time - ps->weaponChargeTime ) * 0.001f;
8384 				shader = cgi_R_RegisterShader( "gfx/misc/lightningFlash" );
8385 				scale = 1.75f;
8386 			}
8387 
8388 			if ( val < 0.0f )
8389 			{
8390 				val = 0.0f;
8391 			}
8392 			else if ( val > 1.0f )
8393 			{
8394 				val = 1.0f;
8395 				CGCam_Shake( 0.1f, 100 );
8396 			}
8397 			else
8398 			{
8399 				CGCam_Shake( val * val * 0.3f, 100 );
8400 			}
8401 
8402 			val += Q_flrand(0.0f, 1.0f) * 0.5f;
8403 
8404 			FX_AddSprite( cent->gent->client->renderInfo.muzzlePoint, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, Q_flrand(0.0f, 1.0f) * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA );
8405 		}
8406 	}
8407 }
8408 
8409 //=====================================================================
8410 
8411 /*
8412 ===============
8413 CG_ResetPlayerEntity
8414 
8415 A player just came into view or teleported, so reset all animation info
8416 
8417 FIXME: We do not need to do this, we can remember the last anim and frame they were
8418 on and coontinue from there.
8419 ===============
8420 */
CG_ResetPlayerEntity(centity_t * cent)8421 void CG_ResetPlayerEntity( centity_t *cent ) {
8422 //	cent->errorTime = -99999;		// guarantee no error decay added
8423 //	cent->extrapolated = qfalse;
8424 
8425 	if ( cent->gent && cent->gent->ghoul2.size() )
8426 	{
8427 		if ( cent->currentState.clientNum < MAX_CLIENTS )
8428 		{
8429 			CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], &cent->pe.legs, cent->currentState.legsAnim );
8430 			CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], &cent->pe.torso, cent->currentState.torsoAnim );
8431 		}
8432 		else if ( cent->gent && cent->gent->client )
8433 		{
8434 			CG_ClearLerpFrame( &cent->gent->client->clientInfo, &cent->pe.legs, cent->currentState.legsAnim );
8435 			CG_ClearLerpFrame( &cent->gent->client->clientInfo, &cent->pe.torso, cent->currentState.torsoAnim );
8436 		}
8437 	}
8438 	//else????
8439 
8440 	EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
8441 	EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
8442 
8443 // Removed by BTO (VV) - These values are crap anyway. Also changed below to use lerp instead
8444 //	VectorCopy( cent->lerpOrigin, cent->rawOrigin );
8445 //	VectorCopy( cent->lerpAngles, cent->rawAngles );
8446 
8447 	memset( &cent->pe.legs, 0, sizeof( cent->pe.legs ) );
8448 	cent->pe.legs.yawAngle = cent->lerpAngles[YAW];
8449 	cent->pe.legs.yawing = qfalse;
8450 	cent->pe.legs.pitchAngle = 0;
8451 	cent->pe.legs.pitching = qfalse;
8452 
8453 	memset( &cent->pe.torso, 0, sizeof( cent->pe.legs ) );
8454 	cent->pe.torso.yawAngle = cent->lerpAngles[YAW];
8455 	cent->pe.torso.yawing = qfalse;
8456 	cent->pe.torso.pitchAngle = cent->lerpAngles[PITCH];
8457 	cent->pe.torso.pitching = qfalse;
8458 }
8459