codemp/cgame/cg_players.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // cg_players.c -- handle the media and animation for player entities
00004 #include "cg_local.h"
00005 #include "..\ghoul2\g2.h"
00006 #include "bg_saga.h"
00007 
00008 extern vmCvar_t cg_thirdPersonAlpha;
00009 
00010 extern int                      cgSiegeTeam1PlShader;
00011 extern int                      cgSiegeTeam2PlShader;
00012 
00013 extern void CG_AddRadarEnt(centity_t *cent);    //cg_ents.c
00014 extern void CG_AddBracketedEnt(centity_t *cent);        //cg_ents.c
00015 extern qboolean CG_InFighter( void );
00016 extern qboolean WP_SaberBladeUseSecondBladeStyle( saberInfo_t *saber, int bladeNum );
00017 
00018 
00019 //for g2 surface routines
00020 #define TURN_ON                         0x00000000
00021 #define TURN_OFF                        0x00000100
00022 
00023 extern stringID_table_t animTable [MAX_ANIMATIONS+1];
00024 
00025 char    *cg_customSoundNames[MAX_CUSTOM_SOUNDS] = {
00026         "*death1",
00027         "*death2",
00028         "*death3",
00029         "*jump1",
00030         "*pain25",
00031         "*pain50",
00032         "*pain75",
00033         "*pain100",
00034         "*falling1",
00035         "*choke1",
00036         "*choke2",
00037         "*choke3",
00038         "*gasp",
00039         "*land1",
00040         "*taunt",
00041         NULL
00042 };
00043 
00044 //NPC sounds:
00045 //Used as a supplement to the basic set for enemies and hazard team
00046 // (keep numbers in ascending order in order for variant-capping to work)
00047 const char      *cg_customCombatSoundNames[MAX_CUSTOM_COMBAT_SOUNDS] = 
00048 {
00049         "*anger1",      //Say when acquire an enemy when didn't have one before
00050         "*anger2",
00051         "*anger3",
00052         "*victory1",    //Say when killed an enemy
00053         "*victory2",
00054         "*victory3",
00055         "*confuse1",    //Say when confused
00056         "*confuse2",
00057         "*confuse3",
00058         "*pushed1",     //Say when force-pushed
00059         "*pushed2",
00060         "*pushed3",
00061         "*choke1",
00062         "*choke2",
00063         "*choke3",
00064         "*ffwarn",
00065         "*ffturn",
00066         NULL
00067 };
00068 
00069 //Used as a supplement to the basic set for stormtroopers
00070 // (keep numbers in ascending order in order for variant-capping to work)
00071 const char      *cg_customExtraSoundNames[MAX_CUSTOM_EXTRA_SOUNDS] = 
00072 {
00073         "*chase1",
00074         "*chase2",
00075         "*chase3",
00076         "*cover1",
00077         "*cover2",
00078         "*cover3",
00079         "*cover4",
00080         "*cover5",
00081         "*detected1",
00082         "*detected2",
00083         "*detected3",
00084         "*detected4",
00085         "*detected5",
00086         "*lost1",
00087         "*outflank1",
00088         "*outflank2",
00089         "*escaping1",
00090         "*escaping2",
00091         "*escaping3",
00092         "*giveup1",
00093         "*giveup2",
00094         "*giveup3",
00095         "*giveup4",
00096         "*look1",
00097         "*look2",
00098         "*sight1",
00099         "*sight2",
00100         "*sight3",
00101         "*sound1",
00102         "*sound2",
00103         "*sound3",
00104         "*suspicious1",
00105         "*suspicious2",
00106         "*suspicious3",
00107         "*suspicious4",
00108         "*suspicious5",
00109         NULL
00110 };
00111 
00112 //Used as a supplement to the basic set for jedi
00113 // (keep numbers in ascending order in order for variant-capping to work)
00114 const char      *cg_customJediSoundNames[MAX_CUSTOM_JEDI_SOUNDS] = 
00115 {
00116         "*combat1",
00117         "*combat2",
00118         "*combat3",
00119         "*jdetected1",
00120         "*jdetected2",
00121         "*jdetected3",
00122         "*taunt1",
00123         "*taunt2",
00124         "*taunt3",
00125         "*jchase1",
00126         "*jchase2",
00127         "*jchase3",
00128         "*jlost1",
00129         "*jlost2",
00130         "*jlost3",
00131         "*deflect1",
00132         "*deflect2",
00133         "*deflect3",
00134         "*gloat1",
00135         "*gloat2",
00136         "*gloat3",
00137         "*pushfail",
00138         NULL
00139 };
00140 
00141 //Used for DUEL taunts
00142 const char      *cg_customDuelSoundNames[MAX_CUSTOM_DUEL_SOUNDS] = 
00143 {
00144         "*anger1",      //Say when acquire an enemy when didn't have one before
00145         "*anger2",
00146         "*anger3",
00147         "*victory1",    //Say when killed an enemy
00148         "*victory2",
00149         "*victory3",
00150         "*taunt1",
00151         "*taunt2",
00152         "*taunt3",
00153         "*deflect1",
00154         "*deflect2",
00155         "*deflect3",
00156         "*gloat1",
00157         "*gloat2",
00158         "*gloat3",
00159         NULL
00160 };
00161 
00162 void CG_Disintegration(centity_t *cent, refEntity_t *ent);
00163 
00164 /*
00165 ================
00166 CG_CustomSound
00167 
00168 ================
00169 */
00170 sfxHandle_t     CG_CustomSound( int clientNum, const char *soundName ) {
00171         clientInfo_t *ci;
00172         int                     i;
00173         int                     numCSounds = 0;
00174         int                     numCComSounds = 0;
00175         int                     numCExSounds = 0;
00176         int                     numCJediSounds = 0;
00177         int                     numCSiegeSounds = 0;
00178         int                     numCDuelSounds = 0;
00179         char            lSoundName[MAX_QPATH];
00180 
00181         if ( soundName[0] != '*' ) {
00182                 return trap_S_RegisterSound( soundName );
00183         }
00184 
00185         COM_StripExtension(soundName, lSoundName);
00186 
00187         if ( clientNum < 0 )
00188         {
00189                 clientNum = 0;
00190         }
00191 
00192         if (clientNum >= MAX_CLIENTS)
00193         {
00194                 ci = cg_entities[clientNum].npcClient;
00195         }
00196         else
00197         {
00198                 ci = &cgs.clientinfo[ clientNum ];
00199         }
00200 
00201         if (!ci)
00202         {
00203                 return 0;
00204         }
00205 
00206         for (i = 0; i < MAX_CUSTOM_SOUNDS; i++)
00207         {
00208                 if (!cg_customSoundNames[i])
00209                 {
00210                         numCSounds = i;
00211                         break;
00212                 }
00213         }
00214 
00215         if (clientNum >= MAX_CLIENTS)
00216         { //these are only for npc's
00217                 for (i = 0; i < MAX_CUSTOM_SOUNDS; i++)
00218                 {
00219                         if (!cg_customCombatSoundNames[i])
00220                         {
00221                                 numCComSounds = i;
00222                                 break;
00223                         }
00224                 }
00225 
00226                 for (i = 0; i < MAX_CUSTOM_SOUNDS; i++)
00227                 {
00228                         if (!cg_customExtraSoundNames[i])
00229                         {
00230                                 numCExSounds = i;
00231                                 break;
00232                         }
00233                 }
00234 
00235                 for (i = 0; i < MAX_CUSTOM_SOUNDS; i++)
00236                 {
00237                         if (!cg_customJediSoundNames[i])
00238                         {
00239                                 numCJediSounds = i;
00240                                 break;
00241                         }
00242                 }
00243         }
00244 
00245     if (cgs.gametype >= GT_TEAM || cg_buildScript.integer)
00246         { //siege only
00247                 for (i = 0; i < MAX_CUSTOM_SOUNDS; i++)
00248                 {
00249                         if (!bg_customSiegeSoundNames[i])
00250                         {
00251                                 numCSiegeSounds = i;
00252                                 break;
00253                         }
00254                 }
00255         }
00256 
00257     if (cgs.gametype == GT_DUEL
00258                 || cgs.gametype == GT_POWERDUEL
00259                 || cg_buildScript.integer)
00260         { //Duel only
00261                 for (i = 0; i < MAX_CUSTOM_SOUNDS; i++)
00262                 {
00263                         if (!cg_customDuelSoundNames[i])
00264                         {
00265                                 numCDuelSounds = i;
00266                                 break;
00267                         }
00268                 }
00269         }
00270 
00271         for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ )
00272         {
00273                 if ( i < numCSounds && !strcmp( lSoundName, cg_customSoundNames[i] ) )
00274                 {
00275                         return ci->sounds[i];
00276                 }
00277                 else if ( (cgs.gametype >= GT_TEAM || cg_buildScript.integer) && i < numCSiegeSounds && !strcmp( lSoundName, bg_customSiegeSoundNames[i] ) )
00278                 { //siege only
00279                         return ci->siegeSounds[i];
00280                 }
00281                 else if ( (cgs.gametype == GT_DUEL || cgs.gametype == GT_POWERDUEL || cg_buildScript.integer) && i < numCDuelSounds && !strcmp( lSoundName, cg_customDuelSoundNames[i] ) )
00282                 { //siege only
00283                         return ci->duelSounds[i];
00284                 }
00285                 else if ( clientNum >= MAX_CLIENTS && i < numCComSounds && !strcmp( lSoundName, cg_customCombatSoundNames[i] ) )
00286                 { //npc only
00287                         return ci->combatSounds[i];
00288                 }
00289                 else if ( clientNum >= MAX_CLIENTS && i < numCExSounds && !strcmp( lSoundName, cg_customExtraSoundNames[i] ) )
00290                 { //npc only
00291                         return ci->extraSounds[i];
00292                 }
00293                 else if ( clientNum >= MAX_CLIENTS && i < numCJediSounds && !strcmp( lSoundName, cg_customJediSoundNames[i] ) )
00294                 { //npc only
00295                         return ci->jediSounds[i];
00296                 }
00297         }
00298 
00299         //CG_Error( "Unknown custom sound: %s", lSoundName );
00300 #ifndef FINAL_BUILD
00301         Com_Printf( "Unknown custom sound: %s", lSoundName );
00302 #endif
00303         return 0;
00304 }
00305 
00306 /*
00307 =============================================================================
00308 
00309 CLIENT INFO
00310 
00311 =============================================================================
00312 */
00313 #define MAX_SURF_LIST_SIZE      1024
00314 qboolean CG_ParseSurfsFile( const char *modelName, const char *skinName, char *surfOff, char *surfOn ) 
00315 {
00316         const char      *text_p;
00317         int                     len;
00318         const char      *token;
00319         const char      *value;
00320         char            text[20000];
00321         char            sfilename[MAX_QPATH];
00322         fileHandle_t    f;
00323         int                     i = 0;
00324 
00325         while (skinName && skinName[i])
00326         {
00327                 if (skinName[i] == '|')
00328                 { //this is a multi-part skin, said skins do not support .surf files
00329                         return qfalse;
00330                 }
00331 
00332                 i++;
00333         }
00334 
00335 
00336         // Load and parse .surf file
00337         Com_sprintf( sfilename, sizeof( sfilename ), "models/players/%s/model_%s.surf", modelName, skinName );
00338 
00339         // load the file
00340         len = trap_FS_FOpenFile( sfilename, &f, FS_READ );
00341         if ( len <= 0 ) 
00342         {//no file
00343                 return qfalse;
00344         }
00345         if ( len >= sizeof( text ) - 1 ) 
00346         {
00347                 Com_Printf( "File %s too long\n", sfilename );
00348                 return qfalse;
00349         }
00350 
00351         trap_FS_Read( text, len, f );
00352         text[len] = 0;
00353         trap_FS_FCloseFile( f );
00354 
00355         // parse the text
00356         text_p = text;
00357 
00358         memset( (char *)surfOff, 0, sizeof(surfOff) );
00359         memset( (char *)surfOn, 0, sizeof(surfOn) );
00360 
00361         // read information for surfOff and surfOn
00362         while ( 1 ) 
00363         {
00364                 token = COM_ParseExt( &text_p, qtrue );
00365                 if ( !token || !token[0] ) 
00366                 {
00367                         break;
00368                 }
00369 
00370                 // surfOff
00371                 if ( !Q_stricmp( token, "surfOff" ) ) 
00372                 {
00373                         if ( COM_ParseString( &text_p, &value ) ) 
00374                         {
00375                                 continue;
00376                         }
00377                         if ( surfOff && surfOff[0] )
00378                         {
00379                                 Q_strcat( surfOff, MAX_SURF_LIST_SIZE, "," );
00380                                 Q_strcat( surfOff, MAX_SURF_LIST_SIZE, value );
00381                         }
00382                         else
00383                         {
00384                                 Q_strncpyz( surfOff, value, MAX_SURF_LIST_SIZE );
00385                         }
00386                         continue;
00387                 }
00388                 
00389                 // surfOn
00390                 if ( !Q_stricmp( token, "surfOn" ) ) 
00391                 {
00392                         if ( COM_ParseString( &text_p, &value ) ) 
00393                         {
00394                                 continue;
00395                         }
00396                         if ( surfOn && surfOn[0] )
00397                         {
00398                                 Q_strcat( surfOn, MAX_SURF_LIST_SIZE, ",");
00399                                 Q_strcat( surfOn, MAX_SURF_LIST_SIZE, value );
00400                         }
00401                         else
00402                         {
00403                                 Q_strncpyz( surfOn, value, MAX_SURF_LIST_SIZE );
00404                         }
00405                         continue;
00406                 }
00407         }
00408         return qtrue;
00409 }
00410 
00411 /*
00412 ==========================
00413 CG_RegisterClientModelname
00414 ==========================
00415 */
00416 #include "../namespace_begin.h"
00417 qboolean BG_IsValidCharacterModel(const char *modelName, const char *skinName);
00418 qboolean BG_ValidateSkinForTeam( const char *modelName, char *skinName, int team, float *colors );
00419 #include "../namespace_end.h"
00420 
00421 static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelName, const char *skinName, const char *teamName, int clientNum ) {
00422         int handle;
00423         char            afilename[MAX_QPATH];
00424         char             *slash;
00425         char            GLAName[MAX_QPATH];
00426         vec3_t  tempVec = {0,0,0};
00427         qboolean badModel = qfalse;
00428         char    surfOff[MAX_SURF_LIST_SIZE];
00429         char    surfOn[MAX_SURF_LIST_SIZE];
00430         int             checkSkin;
00431         char    *useSkinName;
00432 
00433 retryModel:
00434         if (badModel)
00435         {
00436                 if (modelName && modelName[0])
00437                 {
00438                         Com_Printf("WARNING: Attempted to load an unsupported multiplayer model %s! (bad or missing bone, or missing animation sequence)\n", modelName);
00439                 }
00440 
00441                 modelName = "kyle";
00442                 skinName = "default";
00443 
00444                 badModel = qfalse;
00445         }
00446 
00447         // First things first.  If this is a ghoul2 model, then let's make sure we demolish this first.
00448         if (ci->ghoul2Model && trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
00449         {
00450                 trap_G2API_CleanGhoul2Models(&(ci->ghoul2Model));
00451         }
00452 
00453         if (!BG_IsValidCharacterModel(modelName, skinName))
00454         {
00455                 modelName = "kyle";
00456                 skinName = "default";
00457         }
00458 
00459         if ( cgs.gametype >= GT_TEAM && !cgs.jediVmerc && cgs.gametype != GT_SIEGE )
00460         { //We won't force colors for siege.
00461                 BG_ValidateSkinForTeam( ci->modelName, ci->skinName, ci->team, ci->colorOverride );
00462                 skinName = ci->skinName;
00463         }
00464         else
00465         {
00466                 ci->colorOverride[0] = ci->colorOverride[1] = ci->colorOverride[2] = 0.0f;
00467         }
00468 
00469         if (strchr(skinName, '|'))
00470         {//three part skin
00471                 useSkinName = va("models/players/%s/|%s", modelName, skinName);
00472         }
00473         else
00474         {
00475                 useSkinName = va("models/players/%s/model_%s.skin", modelName, skinName);
00476         }
00477 
00478         checkSkin = trap_R_RegisterSkin(useSkinName);
00479 
00480         if (checkSkin)
00481         {
00482                 ci->torsoSkin = checkSkin;
00483         }
00484         else
00485         { //fallback to the default skin
00486                 ci->torsoSkin = trap_R_RegisterSkin(va("models/players/%s/model_default.skin", modelName, skinName));
00487         }
00488         Com_sprintf( afilename, sizeof( afilename ), "models/players/%s/model.glm", modelName );
00489         handle = trap_G2API_InitGhoul2Model(&ci->ghoul2Model, afilename, 0, ci->torsoSkin, 0, 0, 0);
00490 
00491         if (handle<0)
00492         {
00493                 return qfalse;
00494         }
00495 
00496         // The model is now loaded.
00497 
00498         trap_G2API_SetSkin(ci->ghoul2Model, 0, ci->torsoSkin, ci->torsoSkin);
00499 
00500         GLAName[0] = 0;
00501 
00502         trap_G2API_GetGLAName( ci->ghoul2Model, 0, GLAName);
00503         if (GLAName[0] != 0)
00504         {
00505                 if (!strstr(GLAName, "players/_humanoid/") /*&&
00506                         (!strstr(GLAName, "players/rockettrooper/") || cgs.gametype != GT_SIEGE)*/) //only allow rockettrooper in siege
00507                 { //Bad!
00508                         badModel = qtrue;
00509                         goto retryModel;
00510                 }
00511         }
00512 
00513         if (!BGPAFtextLoaded)
00514         {
00515                 if (GLAName[0] == 0/*GLAName == NULL*/)
00516                 {
00517                         badModel = qtrue;
00518                         goto retryModel;
00519                 }
00520                 Q_strncpyz( afilename, GLAName, sizeof( afilename ));
00521                 slash = Q_strrchr( afilename, '/' );
00522                 if ( slash )
00523                 {
00524                         strcpy(slash, "/animation.cfg");
00525                 }       // Now afilename holds just the path to the animation.cfg
00526                 else 
00527                 {       // Didn't find any slashes, this is a raw filename right in base (whish isn't a good thing)
00528                         return qfalse;
00529                 }
00530 
00531                 //rww - All player models must use humanoid, no matter what.
00532                 if (Q_stricmp(afilename, "models/players/_humanoid/animation.cfg") /*&&
00533                         Q_stricmp(afilename, "models/players/rockettrooper/animation.cfg")*/)
00534                 {
00535                         Com_Printf( "Model does not use supported animation config.\n");
00536                         return qfalse;
00537                 }
00538                 else if (BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", bgHumanoidAnimations, qtrue) == -1)
00539                 {
00540                         Com_Printf( "Failed to load animation file models/players/_humanoid/animation.cfg\n" );
00541                         return qfalse;
00542                 }
00543 
00544                 BG_ParseAnimationEvtFile( "models/players/_humanoid/", 0, -1 ); //get the sounds for the humanoid anims
00545 //              if (cgs.gametype == GT_SIEGE)
00546 //              {
00547 //                      BG_ParseAnimationEvtFile( "models/players/rockettrooper/", 1, 1 ); //parse rockettrooper too
00548 //              }
00549                 //For the time being, we're going to have all real players use the generic humanoid soundset and that's it.
00550                 //Only npc's will use model-specific soundsets.
00551 
00552         //      BG_ParseAnimationSndFile(va("models/players/%s/", modelName), 0, -1);
00553         }
00554         else if (!bgAllEvents[0].eventsParsed)
00555         { //make sure the player anim sounds are loaded even if the anims already are
00556                 BG_ParseAnimationEvtFile( "models/players/_humanoid/", 0, -1 );
00557 //              if (cgs.gametype == GT_SIEGE)
00558 //              {
00559 //                      BG_ParseAnimationEvtFile( "models/players/rockettrooper/", 1, 1 );
00560 //              }
00561         }
00562 
00563         if ( CG_ParseSurfsFile( modelName, skinName, surfOff, surfOn ) )
00564         {//turn on/off any surfs
00565                 const char      *token;
00566                 const char      *p;
00567 
00568                 //Now turn on/off any surfaces
00569                 if ( surfOff && surfOff[0] )
00570                 {
00571                         p = surfOff;
00572                         while ( 1 ) 
00573                         {
00574                                 token = COM_ParseExt( &p, qtrue );
00575                                 if ( !token[0] ) 
00576                                 {//reached end of list
00577                                         break;
00578                                 }
00579                                 //turn off this surf
00580                                 trap_G2API_SetSurfaceOnOff( ci->ghoul2Model, token, 0x00000002/*G2SURFACEFLAG_OFF*/ );
00581                         }
00582                 }
00583                 if ( surfOn && surfOn[0] )
00584                 {
00585                         p = surfOn;
00586                         while ( 1 )
00587                         {
00588                                 token = COM_ParseExt( &p, qtrue );
00589                                 if ( !token[0] ) 
00590                                 {//reached end of list
00591                                         break;
00592                                 }
00593                                 //turn on this surf
00594                                 trap_G2API_SetSurfaceOnOff( ci->ghoul2Model, token, 0 );
00595                         }
00596                 }
00597         }
00598 
00599 
00600         ci->bolt_rhand = trap_G2API_AddBolt(ci->ghoul2Model, 0, "*r_hand");
00601         
00602         if (!trap_G2API_SetBoneAnim(ci->ghoul2Model, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, cg.time, -1, -1))
00603         {
00604                 badModel = qtrue;
00605         }
00606         
00607         if (!trap_G2API_SetBoneAngles(ci->ghoul2Model, 0, "upper_lumbar", tempVec, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, cg.time))
00608         {
00609                 badModel = qtrue;
00610         }
00611 
00612         if (!trap_G2API_SetBoneAngles(ci->ghoul2Model, 0, "cranium", tempVec, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, NULL, 0, cg.time))
00613         {
00614                 badModel = qtrue;
00615         }
00616 
00617         ci->bolt_lhand = trap_G2API_AddBolt(ci->ghoul2Model, 0, "*l_hand");
00618 
00619         //rhand must always be first bolt. lhand always second. Whichever you want the
00620         //jetpack bolted to must always be third.
00621         trap_G2API_AddBolt(ci->ghoul2Model, 0, "*chestg");
00622 
00623         //claw bolts
00624         trap_G2API_AddBolt(ci->ghoul2Model, 0, "*r_hand_cap_r_arm");
00625         trap_G2API_AddBolt(ci->ghoul2Model, 0, "*l_hand_cap_l_arm");
00626 
00627         ci->bolt_head = trap_G2API_AddBolt(ci->ghoul2Model, 0, "*head_top");
00628         if (ci->bolt_head == -1)
00629         {
00630                 ci->bolt_head = trap_G2API_AddBolt(ci->ghoul2Model, 0, "ceyebrow");
00631         }
00632 
00633         ci->bolt_motion = trap_G2API_AddBolt(ci->ghoul2Model, 0, "Motion");
00634 
00635         //We need a lower lumbar bolt for footsteps
00636         ci->bolt_llumbar = trap_G2API_AddBolt(ci->ghoul2Model, 0, "lower_lumbar");
00637 
00638         if (ci->bolt_rhand == -1 || ci->bolt_lhand == -1 || ci->bolt_head == -1 || ci->bolt_motion == -1 || ci->bolt_llumbar == -1)
00639         {
00640                 badModel = qtrue;
00641         }
00642 
00643         if (badModel)
00644         {
00645                 goto retryModel;
00646         }
00647 
00648         if (!Q_stricmp(modelName, "boba_fett"))
00649         { //special case, turn off the jetpack surfs
00650                 trap_G2API_SetSurfaceOnOff(ci->ghoul2Model, "torso_rjet", TURN_OFF);
00651                 trap_G2API_SetSurfaceOnOff(ci->ghoul2Model, "torso_cjet", TURN_OFF);
00652                 trap_G2API_SetSurfaceOnOff(ci->ghoul2Model, "torso_ljet", TURN_OFF);
00653         }
00654 
00655 //      ent->s.radius = 90;
00656 
00657         if (clientNum != -1)
00658         {
00659                 /*
00660                 if (cg_entities[clientNum].ghoul2 && trap_G2_HaveWeGhoul2Models(cg_entities[clientNum].ghoul2))
00661                 {
00662                         trap_G2API_CleanGhoul2Models(&(cg_entities[clientNum].ghoul2));
00663                 }
00664                 trap_G2API_DuplicateGhoul2Instance(ci->ghoul2Model, &cg_entities[clientNum].ghoul2);    
00665                 */
00666 
00667                 cg_entities[clientNum].ghoul2weapon = NULL;
00668         }
00669 
00670         Q_strncpyz (ci->teamName, teamName, sizeof(ci->teamName));
00671 
00672         // Model icon for drawing the portrait on screen
00673         ci->modelIcon = trap_R_RegisterShaderNoMip ( va ( "models/players/%s/icon_%s", modelName, skinName ) );
00674         if (!ci->modelIcon)
00675         {
00676         int i = 0;
00677                 int j;
00678                 char iconName[1024];
00679                 strcpy(iconName, "icon_");
00680                 j = strlen(iconName);
00681                 while (skinName[i] && skinName[i] != '|' && j < 1024)
00682                 {
00683             iconName[j] = skinName[i];
00684                         j++;
00685                         i++;
00686                 }
00687                 iconName[j] = 0;
00688                 if (skinName[i] == '|')
00689                 { //looks like it actually may be a custom model skin, let's try getting the icon...
00690                         ci->modelIcon = trap_R_RegisterShaderNoMip ( va ( "models/players/%s/%s", modelName, iconName ) );
00691                 }
00692         }
00693         return qtrue;
00694 }
00695 
00696 /*
00697 ====================
00698 CG_ColorFromString
00699 ====================
00700 */
00701 static void CG_ColorFromString( const char *v, vec3_t color ) {
00702         int val;
00703 
00704         VectorClear( color );
00705 
00706         val = atoi( v );
00707 
00708         if ( val < 1 || val > 7 ) {
00709                 VectorSet( color, 1, 1, 1 );
00710                 return;
00711         }
00712 
00713         if ( val & 1 ) {
00714                 color[2] = 1.0f;
00715         }
00716         if ( val & 2 ) {
00717                 color[1] = 1.0f;
00718         }
00719         if ( val & 4 ) {
00720                 color[0] = 1.0f;
00721         }
00722 }
00723 
00724 /*
00725 ====================
00726 CG_ColorFromInt
00727 ====================
00728 */
00729 static void CG_ColorFromInt( int val, vec3_t color ) {
00730         VectorClear( color );
00731 
00732         if ( val < 1 || val > 7 ) {
00733                 VectorSet( color, 1, 1, 1 );
00734                 return;
00735         }
00736 
00737         if ( val & 1 ) {
00738                 color[2] = 1.0f;
00739         }
00740         if ( val & 2 ) {
00741                 color[1] = 1.0f;
00742         }
00743         if ( val & 4 ) {
00744                 color[0] = 1.0f;
00745         }
00746 }
00747 
00748 //load anim info
00749 int CG_G2SkelForModel(void *g2)
00750 {
00751         int animIndex = -1;
00752         char GLAName[MAX_QPATH];
00753         char *slash;
00754 
00755         GLAName[0] = 0;
00756         trap_G2API_GetGLAName(g2, 0, GLAName);
00757 
00758         slash = Q_strrchr( GLAName, '/' );
00759         if ( slash )
00760         {
00761                 strcpy(slash, "/animation.cfg");
00762 
00763                 animIndex = BG_ParseAnimationFile(GLAName, NULL, qfalse);
00764         }
00765 
00766         return animIndex;
00767 }
00768 
00769 //get the appropriate anim events file index
00770 int CG_G2EvIndexForModel(void *g2, int animIndex)
00771 {
00772         int evtIndex = -1;
00773         char GLAName[MAX_QPATH];
00774         char *slash;
00775 
00776         if (animIndex == -1)
00777         {
00778                 assert(!"shouldn't happen, bad animIndex");
00779                 return -1;
00780         }
00781 
00782         GLAName[0] = 0;
00783         trap_G2API_GetGLAName(g2, 0, GLAName);
00784 
00785         slash = Q_strrchr( GLAName, '/' );
00786         if ( slash )
00787         {
00788                 slash++;
00789                 *slash = 0;
00790 
00791                 evtIndex = BG_ParseAnimationEvtFile(GLAName, animIndex, bgNumAnimEvents);
00792         }
00793 
00794         return evtIndex;
00795 }
00796 
00797 #define DEFAULT_FEMALE_SOUNDPATH "chars/mp_generic_female/misc"//"chars/tavion/misc"
00798 #define DEFAULT_MALE_SOUNDPATH "chars/mp_generic_male/misc"//"chars/kyle/misc"
00799 void CG_LoadCISounds(clientInfo_t *ci, qboolean modelloaded)
00800 {
00801         fileHandle_t f;
00802         qboolean        isFemale = qfalse;
00803         int                     i = 0;
00804         int                     fLen = 0;
00805         const char      *dir;
00806         char            soundpath[MAX_QPATH];
00807         char            soundName[1024];
00808         const char      *s;
00809 
00810         dir = ci->modelName;
00811 
00812         if ( !ci->skinName || !Q_stricmp( "default", ci->skinName ) )
00813         {//try default sounds.cfg first
00814                 fLen = trap_FS_FOpenFile(va("models/players/%s/sounds.cfg", dir), &f, FS_READ);
00815                 if ( !f ) 
00816                 {//no?  Look for _default sounds.cfg
00817                         fLen = trap_FS_FOpenFile(va("models/players/%s/sounds_default.cfg", dir), &f, FS_READ);
00818                 }
00819         }
00820         else
00821         {//use the .skin associated with this skin
00822                 fLen = trap_FS_FOpenFile(va("models/players/%s/sounds_%s.cfg", dir, ci->skinName), &f, FS_READ);
00823                 if ( !f ) 
00824                 {//fall back to default sounds
00825                         fLen = trap_FS_FOpenFile(va("models/players/%s/sounds.cfg", dir), &f, FS_READ);
00826                 }
00827         }
00828 
00829         soundpath[0] = 0;
00830 
00831         if (f)
00832         {
00833                 trap_FS_Read(soundpath, fLen, f);
00834                 soundpath[fLen] = 0;
00835 
00836                 i = fLen;
00837 
00838                 while (i >= 0 && soundpath[i] != '\n')
00839                 {
00840                         if (soundpath[i] == 'f')
00841                         {
00842                                 isFemale = qtrue;
00843                                 soundpath[i] = 0;
00844                         }
00845 
00846                         i--;
00847                 }
00848 
00849                 i = 0;
00850 
00851                 while (soundpath[i] && soundpath[i] != '\r' && soundpath[i] != '\n')
00852                 {
00853                         i++;
00854                 }
00855                 soundpath[i] = 0;
00856 
00857                 trap_FS_FCloseFile(f);
00858         }
00859 
00860         if (isFemale)
00861         {
00862                 ci->gender = GENDER_FEMALE;
00863         }
00864         else
00865         {
00866                 ci->gender = GENDER_MALE;
00867         }
00868 
00869         trap_S_ShutUp(qtrue);
00870 
00871         for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ )
00872         {
00873                 s = cg_customSoundNames[i];
00874                 if ( !s ) {
00875                         break;
00876                 }
00877 
00878                 Com_sprintf(soundName, sizeof(soundName), "%s", s+1);
00879                 COM_StripExtension(soundName, soundName);
00880                 //strip the extension because we might want .mp3's
00881 
00882                 ci->sounds[i] = 0;
00883                 // if the model didn't load use the sounds of the default model
00884                 if (soundpath[0])
00885                 {
00886                         ci->sounds[i] = trap_S_RegisterSound( va("sound/chars/%s/misc/%s", soundpath, soundName) );
00887                 }
00888                 else
00889                 {
00890                         if (modelloaded)
00891                         {
00892                                 ci->sounds[i] = trap_S_RegisterSound( va("sound/chars/%s/misc/%s", dir, soundName) );
00893                         }
00894                 }
00895 
00896                 if (!ci->sounds[i])
00897                 { //failed the load, try one out of the generic path
00898                         if (isFemale)
00899                         {
00900                                 ci->sounds[i] = trap_S_RegisterSound( va("sound/%s/%s", DEFAULT_FEMALE_SOUNDPATH, soundName) );
00901                         }
00902                         else
00903                         {
00904                                 ci->sounds[i] = trap_S_RegisterSound( va("sound/%s/%s", DEFAULT_MALE_SOUNDPATH, soundName) );
00905                         }
00906                 }
00907         }
00908 
00909         if (cgs.gametype >= GT_TEAM || cg_buildScript.integer)
00910         { //load the siege sounds then
00911                 for ( i = 0 ; i < MAX_CUSTOM_SIEGE_SOUNDS; i++ )
00912                 {
00913                         s = bg_customSiegeSoundNames[i];
00914                         if ( !s )
00915                         {
00916                                 break;
00917                         }
00918 
00919                         Com_sprintf(soundName, sizeof(soundName), "%s", s+1);
00920                         COM_StripExtension(soundName, soundName);
00921                         //strip the extension because we might want .mp3's
00922 
00923                         ci->siegeSounds[i] = 0;
00924                         // if the model didn't load use the sounds of the default model
00925                         if (soundpath[0])
00926                         {
00927                                 ci->siegeSounds[i] = trap_S_RegisterSound( va("sound/%s/%s", soundpath, soundName) );
00928                         }
00929                         else
00930                         {
00931                                 if (modelloaded)
00932                                 {
00933                                         ci->siegeSounds[i] = trap_S_RegisterSound( va("sound/chars/%s/misc/%s", dir, soundName) );
00934                                 }
00935                         }
00936 
00937                         if (!ci->siegeSounds[i])
00938                         { //failed the load, try one out of the generic path
00939                                 if (isFemale)
00940                                 {
00941                                         ci->siegeSounds[i] = trap_S_RegisterSound( va("sound/%s/%s", DEFAULT_FEMALE_SOUNDPATH, soundName) );
00942                                 }
00943                                 else
00944                                 {
00945                                         ci->siegeSounds[i] = trap_S_RegisterSound( va("sound/%s/%s", DEFAULT_MALE_SOUNDPATH, soundName) );
00946                                 }
00947                         }
00948                 }
00949         }
00950 
00951         if (cgs.gametype == GT_DUEL
00952                 ||cgs.gametype == GT_POWERDUEL
00953                 || cg_buildScript.integer)
00954         { //load the Duel sounds then
00955                 for ( i = 0 ; i < MAX_CUSTOM_DUEL_SOUNDS; i++ )
00956                 {
00957                         s = cg_customDuelSoundNames[i];
00958                         if ( !s )
00959                         {
00960                                 break;
00961                         }
00962 
00963                         Com_sprintf(soundName, sizeof(soundName), "%s", s+1);
00964                         COM_StripExtension(soundName, soundName);
00965                         //strip the extension because we might want .mp3's
00966 
00967                         ci->duelSounds[i] = 0;
00968                         // if the model didn't load use the sounds of the default model
00969                         if (soundpath[0])
00970                         {
00971                                 ci->duelSounds[i] = trap_S_RegisterSound( va("sound/chars/%s/misc/%s", soundpath, soundName) );
00972                         }
00973                         else
00974                         {
00975                                 if (modelloaded)
00976                                 {
00977                                         ci->duelSounds[i] = trap_S_RegisterSound( va("sound/chars/%s/misc/%s", dir, soundName) );
00978                                 }
00979                         }
00980 
00981                         if (!ci->duelSounds[i])
00982                         { //failed the load, try one out of the generic path
00983                                 if (isFemale)
00984                                 {
00985                                         ci->duelSounds[i] = trap_S_RegisterSound( va("sound/%s/%s", DEFAULT_FEMALE_SOUNDPATH, soundName) );
00986                                 }
00987                                 else
00988                                 {
00989                                         ci->duelSounds[i] = trap_S_RegisterSound( va("sound/%s/%s", DEFAULT_MALE_SOUNDPATH, soundName) );
00990                                 }
00991                         }
00992                 }
00993         }
00994 
00995         trap_S_ShutUp(qfalse);
00996 }
00997 
00998 /*
00999 ===================
01000 CG_LoadClientInfo
01001 
01002 Load it now, taking the disk hits.
01003 This will usually be deferred to a safe time
01004 ===================
01005 */
01006 void CG_LoadClientInfo( clientInfo_t *ci ) {
01007         qboolean        modelloaded;
01008         int                     clientNum;
01009         int                     i;
01010         char            teamname[MAX_QPATH];
01011 
01012         clientNum = ci - cgs.clientinfo;
01013 
01014         if (clientNum < 0 || clientNum >= MAX_CLIENTS)
01015         {
01016                 clientNum = -1;
01017         }
01018 
01019         ci->deferred = qfalse;
01020 
01021         /*
01022         if (ci->team == TEAM_SPECTATOR)
01023         {
01024                 // reset any existing players and bodies, because they might be in bad
01025                 // frames for this new model
01026                 clientNum = ci - cgs.clientinfo;
01027                 for ( i = 0 ; i < MAX_GENTITIES ; i++ ) {
01028                         if ( cg_entities[i].currentState.clientNum == clientNum
01029                                 && cg_entities[i].currentState.eType == ET_PLAYER ) {
01030                                 CG_ResetPlayerEntity( &cg_entities[i] );
01031                         }
01032                 }
01033 
01034                 if (ci->ghoul2Model && trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
01035                 {
01036                         trap_G2API_CleanGhoul2Models(&ci->ghoul2Model);
01037                 }
01038 
01039                 return;
01040         }
01041         */
01042 
01043         teamname[0] = 0;
01044         if( cgs.gametype >= GT_TEAM) {
01045                 if( ci->team == TEAM_BLUE ) {
01046                         Q_strncpyz(teamname, DEFAULT_BLUETEAM_NAME/*cg_blueTeamName.string*/, sizeof(teamname) );
01047                 } else {
01048                         Q_strncpyz(teamname, DEFAULT_REDTEAM_NAME/*cg_redTeamName.string*/, sizeof(teamname) );
01049                 }
01050         }
01051         if( teamname[0] ) {
01052                 strcat( teamname, "/" );
01053         }
01054         modelloaded = qtrue;
01055         if (cgs.gametype == GT_SIEGE &&
01056                 (ci->team == TEAM_SPECTATOR || ci->siegeIndex == -1))
01057         { //yeah.. kind of a hack I guess. Don't care until they are actually ingame with a valid class.
01058                 if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", teamname, -1 ) )
01059                 {
01060                         CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL );
01061                 }
01062         }
01063         else
01064         {
01065                 if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName, teamname, clientNum ) ) {
01066                         //CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname );
01067                         //rww - DO NOT error out here! Someone could just type in a nonsense model name and crash everyone's client.
01068                         //Give it a chance to load default model for this client instead.
01069 
01070                         // fall back to default team name
01071                         if( cgs.gametype >= GT_TEAM) {
01072                                 // keep skin name
01073                                 if( ci->team == TEAM_BLUE ) {
01074                                         Q_strncpyz(teamname, DEFAULT_BLUETEAM_NAME, sizeof(teamname) );
01075                                 } else {
01076                                         Q_strncpyz(teamname, DEFAULT_REDTEAM_NAME, sizeof(teamname) );
01077                                 }
01078                                 if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, ci->skinName, teamname, -1 ) ) {
01079                                         CG_Error( "DEFAULT_MODEL / skin (%s/%s) failed to register", DEFAULT_MODEL, ci->skinName );
01080                                 }
01081                         } else {
01082                                 if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", teamname, -1 ) ) {
01083                                         CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL );
01084                                 }
01085                         }
01086                         modelloaded = qfalse;
01087                 }
01088         }
01089 
01090         if (clientNum != -1)
01091         {
01092                 trap_G2API_ClearAttachedInstance(clientNum);
01093         }
01094 
01095         if (clientNum != -1 && ci->ghoul2Model && trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
01096         {
01097                 if (cg_entities[clientNum].ghoul2 && trap_G2_HaveWeGhoul2Models(cg_entities[clientNum].ghoul2))
01098                 {
01099                         trap_G2API_CleanGhoul2Models(&cg_entities[clientNum].ghoul2);
01100                 }
01101                 trap_G2API_DuplicateGhoul2Instance(ci->ghoul2Model, &cg_entities[clientNum].ghoul2);
01102 
01103                 //Attach the instance to this entity num so we can make use of client-server
01104                 //shared operations if possible.
01105                 trap_G2API_AttachInstanceToEntNum(cg_entities[clientNum].ghoul2, clientNum, qfalse);
01106 
01107 
01108                 if (trap_G2API_AddBolt(cg_entities[clientNum].ghoul2, 0, "face") == -1)
01109                 { //check now to see if we have this bone for setting anims and such
01110                         cg_entities[clientNum].noFace = qtrue;
01111                 }
01112 
01113                 cg_entities[clientNum].localAnimIndex = CG_G2SkelForModel(cg_entities[clientNum].ghoul2);
01114                 cg_entities[clientNum].eventAnimIndex = CG_G2EvIndexForModel(cg_entities[clientNum].ghoul2, cg_entities[clientNum].localAnimIndex);
01115         }
01116 
01117         ci->newAnims = qfalse;
01118         if ( ci->torsoModel ) {
01119                 orientation_t tag;
01120                 // if the torso model has the "tag_flag"
01121                 if ( trap_R_LerpTag( &tag, ci->torsoModel, 0, 0, 1, "tag_flag" ) ) {
01122                         ci->newAnims = qtrue;
01123                 }
01124         }
01125 
01126         // sounds
01127         if (cgs.gametype == GT_SIEGE &&
01128                 (ci->team == TEAM_SPECTATOR || ci->siegeIndex == -1))
01129         { //don't need to load sounds
01130         }
01131         else
01132         {
01133                 CG_LoadCISounds(ci, modelloaded);
01134         }
01135 
01136         ci->deferred = qfalse;
01137 
01138         // reset any existing players and bodies, because they might be in bad
01139         // frames for this new model
01140         clientNum = ci - cgs.clientinfo;
01141         for ( i = 0 ; i < MAX_GENTITIES ; i++ ) {
01142                 if ( cg_entities[i].currentState.clientNum == clientNum
01143                         && cg_entities[i].currentState.eType == ET_PLAYER ) {
01144                         CG_ResetPlayerEntity( &cg_entities[i] );
01145                 }
01146         }
01147 }
01148 
01149 
01150 //Take care of initializing all the ghoul2 saber stuff based on clientinfo data. -rww
01151 static void CG_InitG2SaberData(int saberNum, clientInfo_t *ci)
01152 {
01153         trap_G2API_InitGhoul2Model(&ci->ghoul2Weapons[saberNum], ci->saber[saberNum].model, 0, ci->saber[saberNum].skin, 0, 0, 0);
01154 
01155         if (ci->ghoul2Weapons[saberNum])
01156         {
01157                 int k = 0;
01158                 int tagBolt;
01159                 char *tagName;
01160 
01161                 if (ci->saber[saberNum].skin)
01162                 {
01163                         trap_G2API_SetSkin(ci->ghoul2Weapons[saberNum], 0, ci->saber[saberNum].skin, ci->saber[saberNum].skin);
01164                 }
01165 
01166                 if (ci->saber[saberNum].saberFlags & SFL_BOLT_TO_WRIST)
01167                 {
01168                         trap_G2API_SetBoltInfo(ci->ghoul2Weapons[saberNum], 0, 3+saberNum);
01169                 }
01170                 else
01171                 {
01172                         trap_G2API_SetBoltInfo(ci->ghoul2Weapons[saberNum], 0, saberNum);
01173                 }
01174 
01175                 while (k < ci->saber[saberNum].numBlades)
01176                 {
01177                         tagName = va("*blade%i", k+1);
01178                         tagBolt = trap_G2API_AddBolt(ci->ghoul2Weapons[saberNum], 0, tagName);
01179 
01180                         if (tagBolt == -1)
01181                         {
01182                                 if (k == 0)
01183                                 { //guess this is an 0ldsk3wl saber
01184                                         tagBolt = trap_G2API_AddBolt(ci->ghoul2Weapons[saberNum], 0, "*flash");
01185 
01186                                         if (tagBolt == -1)
01187                                         {
01188                                                 assert(0);
01189                                         }
01190                                         break;
01191                                 }
01192 
01193                                 if (tagBolt == -1)
01194                                 {
01195                                         assert(0);
01196                                         break;
01197                                 }
01198                         }
01199 
01200                         k++;
01201                 }
01202         }
01203 }
01204 
01205 
01206 /*
01207 ======================
01208 CG_CopyClientInfoModel
01209 ======================
01210 */
01211 static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to )
01212 {
01213         VectorCopy( from->headOffset, to->headOffset );
01214 //      to->footsteps = from->footsteps;
01215         to->gender = from->gender;
01216 
01217         to->legsModel = from->legsModel;
01218         to->legsSkin = from->legsSkin;
01219         to->torsoModel = from->torsoModel;
01220         to->torsoSkin = from->torsoSkin;
01221         //to->headModel = from->headModel;
01222         //to->headSkin = from->headSkin;
01223         to->modelIcon = from->modelIcon;
01224 
01225         to->newAnims = from->newAnims;
01226 
01227         //to->ghoul2Model = from->ghoul2Model;
01228         //rww - Trying to use the same ghoul2 pointer for two seperate clients == DISASTER
01229         assert(to->ghoul2Model != from->ghoul2Model);
01230 
01231         if (to->ghoul2Model && trap_G2_HaveWeGhoul2Models(to->ghoul2Model))
01232         {
01233                 trap_G2API_CleanGhoul2Models(&to->ghoul2Model);
01234         }
01235         if (from->ghoul2Model && trap_G2_HaveWeGhoul2Models(from->ghoul2Model))
01236         {
01237                 trap_G2API_DuplicateGhoul2Instance(from->ghoul2Model, &to->ghoul2Model);
01238         }
01239 
01240         //Don't do this, I guess. Just leave the saber info in the original, so it will be
01241         //properly initialized.
01242         /*
01243         strcpy(to->saberName, from->saberName);
01244         strcpy(to->saber2Name, from->saber2Name);
01245 
01246         while (i < MAX_SABERS)
01247         {
01248                 if (to->ghoul2Weapons[i] && trap_G2_HaveWeGhoul2Models(to->ghoul2Weapons[i]))
01249                 {
01250                         trap_G2API_CleanGhoul2Models(&to->ghoul2Weapons[i]);
01251                 }
01252 
01253                 WP_SetSaber(to->saber, 0, to->saberName);
01254                 WP_SetSaber(to->saber, 1, to->saber2Name);
01255 
01256                 j = 0;
01257 
01258                 while (j < MAX_SABERS)
01259                 {
01260                         if (to->saber[j].model[0])
01261                         {
01262                                 CG_InitG2SaberData(j, to);
01263                         }
01264                         j++;
01265                 }
01266                 i++;
01267         }
01268         */
01269 
01270         to->bolt_head = from->bolt_head;
01271         to->bolt_lhand = from->bolt_lhand;
01272         to->bolt_rhand = from->bolt_rhand;
01273         to->bolt_motion = from->bolt_motion;
01274         to->bolt_llumbar = from->bolt_llumbar;
01275 
01276         to->siegeIndex = from->siegeIndex;
01277 
01278         memcpy( to->sounds, from->sounds, sizeof( to->sounds ) );
01279         memcpy( to->siegeSounds, from->siegeSounds, sizeof( to->siegeSounds ) );
01280         memcpy( to->duelSounds, from->duelSounds, sizeof( to->duelSounds ) );
01281 }
01282 
01283 /*
01284 ======================
01285 CG_ScanForExistingClientInfo
01286 ======================
01287 */
01288 static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci, int clientNum ) {
01289         int             i;
01290         clientInfo_t    *match;
01291 
01292         for ( i = 0 ; i < cgs.maxclients ; i++ ) {
01293                 match = &cgs.clientinfo[ i ];
01294                 if ( !match->infoValid ) {
01295                         continue;
01296                 }
01297                 if ( match->deferred ) {
01298                         continue;
01299                 }
01300                 if ( !Q_stricmp( ci->modelName, match->modelName )
01301                         && !Q_stricmp( ci->skinName, match->skinName )
01302                         && !Q_stricmp( ci->saberName, match->saberName)
01303                         && !Q_stricmp( ci->saber2Name, match->saber2Name)
01304 //                      && !Q_stricmp( ci->headModelName, match->headModelName )
01305 //                      && !Q_stricmp( ci->headSkinName, match->headSkinName ) 
01306 //                      && !Q_stricmp( ci->blueTeam, match->blueTeam ) 
01307 //                      && !Q_stricmp( ci->redTeam, match->redTeam )
01308                         && (cgs.gametype < GT_TEAM || ci->team == match->team) 
01309                         && ci->siegeIndex == match->siegeIndex
01310                         && match->ghoul2Model
01311                         && match->bolt_head) //if the bolts haven't been initialized, this "match" is useless to us
01312                 {
01313                         // this clientinfo is identical, so use it's handles
01314 
01315                         ci->deferred = qfalse;
01316 
01317                         //rww - Filthy hack. If this is actually the info already belonging to us, just reassign the pointer.
01318                         //Switching instances when not necessary produces small animation glitches.
01319                         //Actually, before, were we even freeing the instance attached to the old clientinfo before copying
01320                         //this new clientinfo over it? Could be a nasty leak possibility. (though this should remedy it in theory)
01321                         if (clientNum == i)
01322                         {
01323                                 if (match->ghoul2Model && trap_G2_HaveWeGhoul2Models(match->ghoul2Model))
01324                                 { //The match has a valid instance (if it didn't, we'd probably already be fudged (^_^) at this state)
01325                                         if (ci->ghoul2Model && trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
01326                                         { //First kill the copy we have if we have one. (but it should be null)
01327                                                 trap_G2API_CleanGhoul2Models(&ci->ghoul2Model);
01328                                         }
01329 
01330                                         VectorCopy( match->headOffset, ci->headOffset );
01331 //                                      ci->footsteps = match->footsteps;
01332                                         ci->gender = match->gender;
01333 
01334                                         ci->legsModel = match->legsModel;
01335                                         ci->legsSkin = match->legsSkin;
01336                                         ci->torsoModel = match->torsoModel;
01337                                         ci->torsoSkin = match->torsoSkin;
01338                                         ci->modelIcon = match->modelIcon;
01339 
01340                                         ci->newAnims = match->newAnims;
01341 
01342                                         ci->bolt_head = match->bolt_head;
01343                                         ci->bolt_lhand = match->bolt_lhand;
01344                                         ci->bolt_rhand = match->bolt_rhand;
01345                                         ci->bolt_motion = match->bolt_motion;
01346                                         ci->bolt_llumbar = match->bolt_llumbar;
01347                                         ci->siegeIndex = match->siegeIndex;
01348 
01349                                         memcpy( ci->sounds, match->sounds, sizeof( ci->sounds ) );
01350                                         memcpy( ci->siegeSounds, match->siegeSounds, sizeof( ci->siegeSounds ) );
01351                                         memcpy( ci->duelSounds, match->duelSounds, sizeof( ci->duelSounds ) );
01352 
01353                                         //We can share this pointer, because it already belongs to this client.
01354                                         //The pointer itself and the ghoul2 instance is never actually changed, just passed between
01355                                         //clientinfo structures.
01356                                         ci->ghoul2Model = match->ghoul2Model;
01357 
01358                                         //Don't need to do this I guess, whenever this function is called the saber stuff should
01359                                         //already be taken care of in the new info.
01360                                         /*
01361                                         while (k < MAX_SABERS)
01362                                         {
01363                                                 if (match->ghoul2Weapons[k] && match->ghoul2Weapons[k] != ci->ghoul2Weapons[k])
01364                                                 {
01365                                                         if (ci->ghoul2Weapons[k])
01366                                                         {
01367                                                                 trap_G2API_CleanGhoul2Models(&ci->ghoul2Weapons[k]);
01368                                                         }
01369                             ci->ghoul2Weapons[k] = match->ghoul2Weapons[k];
01370                                                 }
01371                                                 k++;
01372                                         }
01373                                         */
01374                                 }
01375                         }
01376                         else
01377                         {
01378                                 CG_CopyClientInfoModel( match, ci );
01379                         }
01380 
01381                         return qtrue;
01382                 }
01383         }
01384 
01385         // nothing matches, so defer the load
01386         return qfalse;
01387 }
01388 
01389 /*
01390 ======================
01391 CG_SetDeferredClientInfo
01392 
01393 We aren't going to load it now, so grab some other
01394 client's info to use until we have some spare time.
01395 ======================
01396 */
01397 static void CG_SetDeferredClientInfo( clientInfo_t *ci ) {
01398         int             i;
01399         clientInfo_t    *match;
01400 
01401         // if someone else is already the same models and skins we
01402         // can just load the client info
01403         for ( i = 0 ; i < cgs.maxclients ; i++ ) {
01404                 match = &cgs.clientinfo[ i ];
01405                 if ( !match->infoValid || match->deferred ) {
01406                         continue;
01407                 }
01408                 if ( Q_stricmp( ci->skinName, match->skinName ) ||
01409                          Q_stricmp( ci->modelName, match->modelName ) ||
01410 //                       Q_stricmp( ci->headModelName, match->headModelName ) ||
01411 //                       Q_stricmp( ci->headSkinName, match->headSkinName ) ||
01412                          (cgs.gametype >= GT_TEAM && ci->team != match->team && ci->team != TEAM_SPECTATOR) ) {
01413                         continue;
01414                 }
01415 
01416                  /*
01417                 if (Q_stricmp(ci->saberName, match->saberName) ||
01418                         Q_stricmp(ci->saber2Name, match->saber2Name))
01419                 {
01420                         continue;
01421                 }
01422                 */
01423 
01424                 // just load the real info cause it uses the same models and skins
01425                 CG_LoadClientInfo( ci );
01426                 return;
01427         }
01428 
01429         // if we are in teamplay, only grab a model if the skin is correct
01430         if ( cgs.gametype >= GT_TEAM ) {
01431                 for ( i = 0 ; i < cgs.maxclients ; i++ ) {
01432                         match = &cgs.clientinfo[ i ];
01433                         if ( !match->infoValid || match->deferred ) {
01434                                 continue;
01435                         }
01436                         if ( ci->team != TEAM_SPECTATOR &&
01437                                 (Q_stricmp( ci->skinName, match->skinName ) ||
01438                                  (cgs.gametype >= GT_TEAM && ci->team != match->team)) ) {
01439                                 continue;
01440                         }
01441 
01442                         /*
01443                         if (Q_stricmp(ci->saberName, match->saberName) ||
01444                                 Q_stricmp(ci->saber2Name, match->saber2Name))
01445                         {
01446                                 continue;
01447                         }
01448                         */
01449 
01450                         ci->deferred = qtrue;
01451                         CG_CopyClientInfoModel( match, ci );
01452                         return;
01453                 }
01454                 // load the full model, because we don't ever want to show
01455                 // an improper team skin.  This will cause a hitch for the first
01456                 // player, when the second enters.  Combat shouldn't be going on
01457                 // yet, so it shouldn't matter
01458                 CG_LoadClientInfo( ci );
01459                 return;
01460         }
01461 
01462         // find the first valid clientinfo and grab its stuff
01463         for ( i = 0 ; i < cgs.maxclients ; i++ ) {
01464                 match = &cgs.clientinfo[ i ];
01465                 if ( !match->infoValid ) {
01466                         continue;
01467                 }
01468 
01469                 /*
01470                 if (Q_stricmp(ci->saberName, match->saberName) ||
01471                         Q_stricmp(ci->saber2Name, match->saber2Name))
01472                 {
01473                         continue;
01474                 }
01475                 */
01476 
01477                 if (match->deferred)
01478                 { //no deferring off of deferred info. Because I said so.
01479                         continue;
01480                 }
01481 
01482                 ci->deferred = qtrue;
01483                 CG_CopyClientInfoModel( match, ci );
01484                 return;
01485         }
01486 
01487         // we should never get here...
01488         //CG_Printf( "CG_SetDeferredClientInfo: no valid clients!\n" );
01489         //Actually it is possible now because of the unique sabers.
01490 
01491         CG_LoadClientInfo( ci );
01492 }
01493 
01494 /*
01495 ======================
01496 CG_NewClientInfo
01497 ======================
01498 */
01499 #include "../namespace_begin.h"
01500 void WP_SetSaber( int entNum, saberInfo_t *sabers, int saberNum, const char *saberName );
01501 #include "../namespace_end.h"
01502 
01503 void CG_NewClientInfo( int clientNum, qboolean entitiesInitialized ) {
01504         clientInfo_t *ci;
01505         clientInfo_t newInfo;
01506         const char      *configstring;
01507         const char      *v;
01508         char            *slash;
01509         void *oldGhoul2;
01510         void *oldG2Weapons[MAX_SABERS];
01511         int i = 0;
01512         int k = 0;
01513         qboolean saberUpdate[MAX_SABERS];
01514 
01515         ci = &cgs.clientinfo[clientNum];
01516 
01517         oldGhoul2 = ci->ghoul2Model;
01518 
01519         while (k < MAX_SABERS)
01520         {
01521                 oldG2Weapons[k] = ci->ghoul2Weapons[k];
01522                 k++;
01523         }
01524 
01525         configstring = CG_ConfigString( clientNum + CS_PLAYERS );
01526         if ( !configstring[0] ) {
01527                 if (ci->ghoul2Model && trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
01528                 { //clean this stuff up first
01529                         trap_G2API_CleanGhoul2Models(&ci->ghoul2Model);
01530                 }
01531                 k = 0;
01532                 while (k < MAX_SABERS)
01533                 {
01534                         if (ci->ghoul2Weapons[k] && trap_G2_HaveWeGhoul2Models(ci->ghoul2Weapons[k]))
01535                         {
01536                                 trap_G2API_CleanGhoul2Models(&ci->ghoul2Weapons[k]);
01537                         }
01538                         k++;
01539                 }
01540 
01541                 memset( ci, 0, sizeof( *ci ) );
01542                 return;         // player just left
01543         }
01544 
01545         // build into a temp buffer so the defer checks can use
01546         // the old value
01547         memset( &newInfo, 0, sizeof( newInfo ) );
01548 
01549         // isolate the player's name
01550         v = Info_ValueForKey(configstring, "n");
01551         Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
01552 
01553         // colors
01554         v = Info_ValueForKey( configstring, "c1" );
01555         CG_ColorFromString( v, newInfo.color1 );
01556 
01557         newInfo.icolor1 = atoi(v);
01558 
01559         v = Info_ValueForKey( configstring, "c2" );
01560         CG_ColorFromString( v, newInfo.color2 );
01561 
01562         newInfo.icolor2 = atoi(v);
01563 
01564         // bot skill
01565         v = Info_ValueForKey( configstring, "skill" );
01566         newInfo.botSkill = atoi( v );
01567 
01568         // handicap
01569         v = Info_ValueForKey( configstring, "hc" );
01570         newInfo.handicap = atoi( v );
01571 
01572         // wins
01573         v = Info_ValueForKey( configstring, "w" );
01574         newInfo.wins = atoi( v );
01575 
01576         // losses
01577         v = Info_ValueForKey( configstring, "l" );
01578         newInfo.losses = atoi( v );
01579 
01580         // team
01581         v = Info_ValueForKey( configstring, "t" );
01582         newInfo.team = atoi( v );
01583 
01584 //copy team info out to menu
01585         if ( clientNum == cg.clientNum) //this is me
01586         {
01587                 trap_Cvar_Set("ui_team", v);
01588         }
01589 
01590         // team task
01591         v = Info_ValueForKey( configstring, "tt" );
01592         newInfo.teamTask = atoi(v);
01593 
01594         // team leader
01595         v = Info_ValueForKey( configstring, "tl" );
01596         newInfo.teamLeader = atoi(v);
01597 
01598 //      v = Info_ValueForKey( configstring, "g_redteam" );
01599 //      Q_strncpyz(newInfo.redTeam, v, MAX_TEAMNAME);
01600 
01601 //      v = Info_ValueForKey( configstring, "g_blueteam" );
01602 //      Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME);
01603 
01604         // model
01605         v = Info_ValueForKey( configstring, "model" );
01606         if ( cg_forceModel.integer ) {
01607                 // forcemodel makes everyone use a single model
01608                 // to prevent load hitches
01609                 char modelStr[MAX_QPATH];
01610                 char *skin;
01611 
01612                 trap_Cvar_VariableStringBuffer( "model", modelStr, sizeof( modelStr ) );
01613                 if ( ( skin = strchr( modelStr, '/' ) ) == NULL) {
01614                         skin = "default";
01615                 } else {
01616                         *skin++ = 0;
01617                 }
01618                 Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) );
01619                 Q_strncpyz( newInfo.modelName, modelStr, sizeof( newInfo.modelName ) );
01620 
01621                 if ( cgs.gametype >= GT_TEAM ) {
01622                         // keep skin name
01623                         slash = strchr( v, '/' );
01624                         if ( slash ) {
01625                                 Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
01626                         }
01627                 }
01628         } else {
01629                 Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) );
01630 
01631                 slash = strchr( newInfo.modelName, '/' );
01632                 if ( !slash ) {
01633                         // modelName didn not include a skin name
01634                         Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
01635                 } else {
01636                         Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
01637                         // truncate modelName
01638                         *slash = 0;
01639                 }
01640         }
01641 
01642         if (cgs.gametype == GT_SIEGE)
01643         { //entries only sent in siege mode
01644                 //siege desired team
01645                 v = Info_ValueForKey( configstring, "sdt" );
01646                 if (v && v[0])
01647                 {
01648             newInfo.siegeDesiredTeam = atoi(v);
01649                 }
01650                 else
01651                 {
01652                         newInfo.siegeDesiredTeam = 0;
01653                 }
01654 
01655                 //siege classname
01656                 v = Info_ValueForKey( configstring, "siegeclass" );
01657                 newInfo.siegeIndex = -1;
01658 
01659                 if (v)
01660                 {
01661                         siegeClass_t *siegeClass = BG_SiegeFindClassByName(v);
01662 
01663                         if (siegeClass)
01664                         { //See if this class forces a model, if so, then use it. Same for skin.
01665                                 newInfo.siegeIndex = BG_SiegeFindClassIndexByName(v);
01666 
01667                                 if (siegeClass->forcedModel[0])
01668                                 {
01669                                         Q_strncpyz( newInfo.modelName, siegeClass->forcedModel, sizeof( newInfo.modelName ) );
01670                                 }
01671 
01672                                 if (siegeClass->forcedSkin[0])
01673                                 {
01674                                         Q_strncpyz( newInfo.skinName, siegeClass->forcedSkin, sizeof( newInfo.skinName ) );
01675                                 }
01676 
01677                                 if (siegeClass->hasForcedSaberColor)
01678                                 {
01679                                         newInfo.icolor1 = siegeClass->forcedSaberColor;
01680 
01681                                         CG_ColorFromInt( newInfo.icolor1, newInfo.color1 );
01682                                 }
01683                                 if (siegeClass->hasForcedSaber2Color)
01684                                 {
01685                                         newInfo.icolor2 = siegeClass->forcedSaber2Color;
01686 
01687                                         CG_ColorFromInt( newInfo.icolor2, newInfo.color2 );
01688                                 }
01689                         }
01690                 }
01691         }
01692 
01693         saberUpdate[0] = qfalse;
01694         saberUpdate[1] = qfalse;
01695 
01696         //saber being used
01697         v = Info_ValueForKey( configstring, "st" );
01698 
01699         if (v && Q_stricmp(v, ci->saberName))
01700         {
01701                 Q_strncpyz( newInfo.saberName, v, 64 );
01702                 WP_SetSaber(clientNum, newInfo.saber, 0, newInfo.saberName);
01703                 saberUpdate[0] = qtrue;
01704         }
01705         else
01706         {
01707                 Q_strncpyz( newInfo.saberName, ci->saberName, 64 );
01708                 memcpy(&newInfo.saber[0], &ci->saber[0], sizeof(newInfo.saber[0]));
01709                 newInfo.ghoul2Weapons[0] = ci->ghoul2Weapons[0];
01710         }
01711 
01712         v = Info_ValueForKey( configstring, "st2" );
01713 
01714         if (v && Q_stricmp(v, ci->saber2Name))
01715         {
01716                 Q_strncpyz( newInfo.saber2Name, v, 64 );
01717                 WP_SetSaber(clientNum, newInfo.saber, 1, newInfo.saber2Name);
01718                 saberUpdate[1] = qtrue;
01719         }
01720         else
01721         {
01722                 Q_strncpyz( newInfo.saber2Name, ci->saber2Name, 64 );
01723                 memcpy(&newInfo.saber[1], &ci->saber[1], sizeof(newInfo.saber[1]));
01724                 newInfo.ghoul2Weapons[1] = ci->ghoul2Weapons[1];
01725         }
01726 
01727         if (saberUpdate[0] || saberUpdate[1])
01728         {
01729                 int j = 0;
01730 
01731                 while (j < MAX_SABERS)
01732                 {
01733                         if (saberUpdate[j])
01734                         {
01735                                 if (newInfo.saber[j].model[0])
01736                                 {
01737                                         if (oldG2Weapons[j])
01738                                         { //free the old instance(s)
01739                                                 trap_G2API_CleanGhoul2Models(&oldG2Weapons[j]);
01740                                                 oldG2Weapons[j] = 0;
01741                                         }
01742 
01743                                         CG_InitG2SaberData(j, &newInfo);
01744                                 }
01745                                 else
01746                                 {
01747                                         if (oldG2Weapons[j])
01748                                         { //free the old instance(s)
01749                                                 trap_G2API_CleanGhoul2Models(&oldG2Weapons[j]);
01750                                                 oldG2Weapons[j] = 0;
01751                                         }
01752                                 }
01753 
01754                                 cg_entities[clientNum].weapon = 0;
01755                                 cg_entities[clientNum].ghoul2weapon = NULL; //force a refresh
01756                         }
01757                         j++;
01758                 }
01759         }
01760 
01761         //Check for any sabers that didn't get set again, if they didn't, then reassign the pointers for the new ci
01762         k = 0;
01763         while (k < MAX_SABERS)
01764         {
01765                 if (oldG2Weapons[k])
01766                 {
01767                         newInfo.ghoul2Weapons[k] = oldG2Weapons[k];
01768                 }
01769                 k++;
01770         }
01771 
01772         //duel team
01773         v = Info_ValueForKey( configstring, "dt" );
01774 
01775         if (v)
01776         {
01777                 newInfo.duelTeam = atoi(v);
01778         }
01779         else
01780         {
01781                 newInfo.duelTeam = 0;
01782         }
01783 
01784         // force powers
01785         v = Info_ValueForKey( configstring, "forcepowers" );
01786         Q_strncpyz( newInfo.forcePowers, v, sizeof( newInfo.forcePowers ) );
01787 
01788         if (cgs.gametype >= GT_TEAM     && !cgs.jediVmerc && cgs.gametype != GT_SIEGE )
01789         { //We won't force colors for siege.
01790                 BG_ValidateSkinForTeam( newInfo.modelName, newInfo.skinName, newInfo.team, newInfo.colorOverride );
01791         }
01792         else
01793         {
01794                 newInfo.colorOverride[0] = newInfo.colorOverride[1] = newInfo.colorOverride[2] = 0.0f;
01795         }
01796 
01797         // scan for an existing clientinfo that matches this modelname
01798         // so we can avoid loading checks if possible
01799         if ( !CG_ScanForExistingClientInfo( &newInfo, clientNum ) ) {
01800                 // if we are defering loads, just have it pick the first valid
01801                 if (cg.snap && cg.snap->ps.clientNum == clientNum)
01802                 { //rww - don't defer your own client info ever
01803                         CG_LoadClientInfo( &newInfo );
01804                 }
01805                 else if (  cg_deferPlayers.integer && cgs.gametype != GT_SIEGE && !cg_buildScript.integer && !cg.loading ) {
01806                         // keep whatever they had if it won't violate team skins
01807                         CG_SetDeferredClientInfo( &newInfo );
01808                 } else {
01809                         CG_LoadClientInfo( &newInfo );
01810                 }
01811         }
01812 
01813         // replace whatever was there with the new one
01814         newInfo.infoValid = qtrue;
01815         if (ci->ghoul2Model &&
01816                 ci->ghoul2Model != newInfo.ghoul2Model &&
01817                 trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
01818         { //We must kill this instance before we remove our only pointer to it from the cgame.
01819           //Otherwise we will end up with extra instances all over the place, I think.
01820                 trap_G2API_CleanGhoul2Models(&ci->ghoul2Model);
01821         }
01822         *ci = newInfo;
01823 
01824         //force a weapon change anyway, for all clients being rendered to the current client
01825         while (i < MAX_CLIENTS)
01826         {
01827                 cg_entities[i].ghoul2weapon = NULL;
01828                 i++;
01829         }
01830 
01831         if (clientNum != -1)
01832         { //don't want it using an invalid pointer to share
01833                 trap_G2API_ClearAttachedInstance(clientNum);
01834         }
01835 
01836         // Check if the ghoul2 model changed in any way.  This is safer than assuming we have a legal cent shile loading info.
01837         if (entitiesInitialized && ci->ghoul2Model && (oldGhoul2 != ci->ghoul2Model))
01838         {       // Copy the new ghoul2 model to the centity.
01839                 animation_t *anim;
01840                 centity_t *cent = &cg_entities[clientNum];
01841                 
01842                 anim = &bgHumanoidAnimations[ (cg_entities[clientNum].currentState.legsAnim) ];
01843 
01844                 if (anim)
01845                 {
01846                         int flags = BONE_ANIM_OVERRIDE_FREEZE;
01847                         int firstFrame = anim->firstFrame;
01848                         int setFrame = -1;
01849                         float animSpeed = 50.0f / anim->frameLerp;
01850 
01851                         if (anim->loopFrames != -1)
01852                         {
01853                                 flags = BONE_ANIM_OVERRIDE_LOOP;
01854                         }
01855 
01856                         if (cent->pe.legs.frame >= anim->firstFrame && cent->pe.legs.frame <= (anim->firstFrame + anim->numFrames))
01857                         {
01858                                 setFrame = cent->pe.legs.frame;
01859                         }
01860 
01861                         //rww - Set the animation again because it just got reset due to the model change
01862                         trap_G2API_SetBoneAnim(ci->ghoul2Model, 0, "model_root", firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, setFrame, 150);
01863 
01864                         cg_entities[clientNum].currentState.legsAnim = 0;
01865                 }
01866 
01867                 anim = &bgHumanoidAnimations[ (cg_entities[clientNum].currentState.torsoAnim) ];
01868 
01869                 if (anim)
01870                 {
01871                         int flags = BONE_ANIM_OVERRIDE_FREEZE;
01872                         int firstFrame = anim->firstFrame;
01873                         int setFrame = -1;
01874                         float animSpeed = 50.0f / anim->frameLerp;
01875 
01876                         if (anim->loopFrames != -1)
01877                         {
01878                                 flags = BONE_ANIM_OVERRIDE_LOOP;
01879                         }
01880 
01881                         if (cent->pe.torso.frame >= anim->firstFrame && cent->pe.torso.frame <= (anim->firstFrame + anim->numFrames))
01882                         {
01883                                 setFrame = cent->pe.torso.frame;
01884                         }
01885 
01886                         //rww - Set the animation again because it just got reset due to the model change
01887                         trap_G2API_SetBoneAnim(ci->ghoul2Model, 0, "lower_lumbar", firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, setFrame, 150);
01888 
01889                         cg_entities[clientNum].currentState.torsoAnim = 0;
01890                 }
01891 
01892                 if (cg_entities[clientNum].ghoul2 && trap_G2_HaveWeGhoul2Models(cg_entities[clientNum].ghoul2))
01893                 {
01894                         trap_G2API_CleanGhoul2Models(&cg_entities[clientNum].ghoul2);
01895                 }
01896                 trap_G2API_DuplicateGhoul2Instance(ci->ghoul2Model, &cg_entities[clientNum].ghoul2);
01897 
01898                 if (clientNum != -1)
01899                 {
01900                         //Attach the instance to this entity num so we can make use of client-server
01901                         //shared operations if possible.
01902                         trap_G2API_AttachInstanceToEntNum(cg_entities[clientNum].ghoul2, clientNum, qfalse);
01903                 }
01904 
01905                 if (trap_G2API_AddBolt(cg_entities[clientNum].ghoul2, 0, "face") == -1)
01906                 { //check now to see if we have this bone for setting anims and such
01907                         cg_entities[clientNum].noFace = qtrue;
01908                 }
01909 
01910                 cg_entities[clientNum].localAnimIndex = CG_G2SkelForModel(cg_entities[clientNum].ghoul2);
01911                 cg_entities[clientNum].eventAnimIndex = CG_G2EvIndexForModel(cg_entities[clientNum].ghoul2, cg_entities[clientNum].localAnimIndex);
01912 
01913                 if (cg_entities[clientNum].currentState.number != cg.predictedPlayerState.clientNum &&
01914                         cg_entities[clientNum].currentState.weapon == WP_SABER)
01915                 {
01916                         cg_entities[clientNum].weapon = cg_entities[clientNum].currentState.weapon;
01917                         if (cg_entities[clientNum].ghoul2 && ci->ghoul2Model)
01918                         {
01919                                 CG_CopyG2WeaponInstance(&cg_entities[clientNum], cg_entities[clientNum].currentState.weapon, cg_entities[clientNum].ghoul2);
01920                                 cg_entities[clientNum].ghoul2weapon = CG_G2WeaponInstance(&cg_entities[clientNum], cg_entities[clientNum].currentState.weapon);
01921                         }
01922                         if (!cg_entities[clientNum].currentState.saberHolstered)
01923                         { //if not holstered set length and desired length for both blades to full right now.
01924                                 int j;
01925                                 BG_SI_SetDesiredLength(&ci->saber[0], 0, -1);
01926                                 BG_SI_SetDesiredLength(&ci->saber[1], 0, -1);
01927 
01928                                 i = 0;
01929                                 while (i < MAX_SABERS)
01930                                 {
01931                                         j = 0;
01932                                         while (j < ci->saber[i].numBlades)
01933                                         {
01934                                                 ci->saber[i].blade[j].length = ci->saber[i].blade[j].lengthMax;
01935                                                 j++;
01936                                         }
01937                                         i++;
01938                                 }
01939                         }
01940                 }
01941         }
01942 }
01943 
01944 
01945 qboolean cgQueueLoad = qfalse;
01946 /*
01947 ======================
01948 CG_ActualLoadDeferredPlayers
01949 
01950 Called at the beginning of CG_Player if cgQueueLoad is set.
01951 ======================
01952 */
01953 void CG_ActualLoadDeferredPlayers( void )
01954 {
01955         int             i;
01956         clientInfo_t    *ci;
01957 
01958         // scan for a deferred player to load
01959         for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) {
01960                 if ( ci->infoValid && ci->deferred ) {
01961                         CG_LoadClientInfo( ci );
01962 //                      break;
01963                 }
01964         }
01965 }
01966 
01967 /*
01968 ======================
01969 CG_LoadDeferredPlayers
01970 
01971 Called each frame when a player is dead
01972 and the scoreboard is up
01973 so deferred players can be loaded
01974 ======================
01975 */
01976 void CG_LoadDeferredPlayers( void ) {
01977         cgQueueLoad = qtrue;
01978 }
01979 
01980 /*
01981 =============================================================================
01982 
01983 PLAYER ANIMATION
01984 
01985 =============================================================================
01986 */
01987 
01988 #define FOOTSTEP_DISTANCE       32
01989 static void _PlayerFootStep( const vec3_t origin, 
01990                                                                 const float orientation, 
01991                                                                 const float radius, 
01992                                                                 centity_t *const cent, footstepType_t footStepType ) 
01993 {
01994         vec3_t          end, mins = {-7, -7, 0}, maxs = {7, 7, 2};
01995         trace_t         trace;
01996         footstep_t      soundType = FOOTSTEP_TOTAL;
01997         qboolean        bMark = qfalse;
01998         qhandle_t footMarkShader;
01999         int                     effectID = -1;
02000         //float         alpha;
02001 
02002         // send a trace down from the player to the ground
02003         VectorCopy( origin, end );
02004         end[2] -= FOOTSTEP_DISTANCE;
02005 
02006         trap_CM_BoxTrace( &trace, origin, end, mins, maxs, 0, MASK_PLAYERSOLID );
02007 
02008         // no shadow if too high
02009         if ( trace.fraction >= 1.0f ) 
02010         {
02011                 return;
02012         }
02013 
02014         //check for foot-steppable surface flag
02015         switch( trace.surfaceFlags & MATERIAL_MASK )
02016         {
02017                 case MATERIAL_MUD:
02018                         bMark = qtrue;
02019                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02020                                 soundType = FOOTSTEP_MUDRUN;
02021                         } else {
02022                                 soundType = FOOTSTEP_MUDWALK;
02023                         }
02024                         effectID = cgs.effects.footstepMud;
02025                         break;
02026                 case MATERIAL_DIRT:                     
02027                         bMark = qtrue;
02028                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02029                                 soundType = FOOTSTEP_DIRTRUN;
02030                         } else {
02031                                 soundType = FOOTSTEP_DIRTWALK;
02032                         }
02033                         effectID = cgs.effects.footstepSand;
02034                         break;
02035                 case MATERIAL_SAND:                     
02036                         bMark = qtrue;
02037                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02038                                 soundType = FOOTSTEP_SANDRUN;
02039                         } else {
02040                                 soundType = FOOTSTEP_SANDWALK;
02041                         }
02042                         effectID = cgs.effects.footstepSand;
02043                         break;
02044                 case MATERIAL_SNOW:                     
02045                         bMark = qtrue;
02046                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02047                                 soundType = FOOTSTEP_SNOWRUN;
02048                         } else {
02049                                 soundType = FOOTSTEP_SNOWWALK;
02050                         }
02051                         effectID = cgs.effects.footstepSnow;
02052                         break;
02053                 case MATERIAL_SHORTGRASS:               
02054                 case MATERIAL_LONGGRASS:                
02055                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02056                                 soundType = FOOTSTEP_GRASSRUN;
02057                         } else {
02058                                 soundType = FOOTSTEP_GRASSWALK;
02059                         }
02060                         break;
02061                 case MATERIAL_SOLIDMETAL:               
02062                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02063                                 soundType = FOOTSTEP_METALRUN;
02064                         } else {
02065                                 soundType = FOOTSTEP_METALWALK;
02066                         }
02067                         break;
02068                 case MATERIAL_HOLLOWMETAL:      
02069                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02070                                 soundType = FOOTSTEP_PIPERUN;
02071                         } else {
02072                                 soundType = FOOTSTEP_PIPEWALK;
02073                         }
02074                         break;
02075                 case MATERIAL_GRAVEL:
02076                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02077                                 soundType = FOOTSTEP_GRAVELRUN;
02078                         } else {
02079                                 soundType = FOOTSTEP_GRAVELWALK;
02080                         }
02081                         effectID = cgs.effects.footstepGravel;
02082                         break;
02083                 case MATERIAL_CARPET:
02084                 case MATERIAL_FABRIC:
02085                 case MATERIAL_CANVAS:
02086                 case MATERIAL_RUBBER:
02087                 case MATERIAL_PLASTIC:
02088                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02089                                 soundType = FOOTSTEP_RUGRUN;
02090                         } else {
02091                                 soundType = FOOTSTEP_RUGWALK;
02092                         }
02093                         break;
02094                 case MATERIAL_SOLIDWOOD:
02095                 case MATERIAL_HOLLOWWOOD:
02096                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02097                                 soundType = FOOTSTEP_WOODRUN;
02098                         } else {
02099                                 soundType = FOOTSTEP_WOODWALK;
02100                         }
02101                         break;
02102 
02103                 default:
02104                 //fall through
02105                 case MATERIAL_GLASS:
02106                 case MATERIAL_WATER:
02107                 case MATERIAL_FLESH:
02108                 case MATERIAL_BPGLASS:
02109                 case MATERIAL_DRYLEAVES:
02110                 case MATERIAL_GREENLEAVES:
02111                 case MATERIAL_TILES:            
02112                 case MATERIAL_PLASTER:
02113                 case MATERIAL_SHATTERGLASS:
02114                 case MATERIAL_ARMOR:
02115                 case MATERIAL_COMPUTER:
02116 
02117                 case MATERIAL_CONCRETE:         
02118                 case MATERIAL_ROCK:                     
02119                 case MATERIAL_ICE:                      
02120                 case MATERIAL_MARBLE:                   
02121                         if ( footStepType == FOOTSTEP_HEAVY_R || footStepType == FOOTSTEP_HEAVY_L) {
02122                                 soundType = FOOTSTEP_STONERUN;
02123                         } else {
02124                                 soundType = FOOTSTEP_STONEWALK;
02125                         }
02126                         break;
02127         }
02128         if (soundType < FOOTSTEP_TOTAL) 
02129         {
02130                 trap_S_StartSound( NULL, cent->currentState.clientNum, CHAN_BODY, cgs.media.footsteps[soundType][rand()&3] );
02131         }
02132 
02133         if ( cg_footsteps.integer < 4 )
02134         {//debugging - 4 always does footstep effect
02135                 if ( cg_footsteps.integer < 2 ) //1 for sounds, 2 for effects, 3 for marks
02136                 {
02137                         return;
02138                 }
02139         }
02140 
02141         if ( effectID != -1 )
02142         {
02143                 trap_FX_PlayEffectID( effectID, trace.endpos, trace.plane.normal, -1, -1 );
02144         }
02145 
02146         if ( cg_footsteps.integer < 4 )
02147         {//debugging - 4 always does footstep effect
02148                 if ( !bMark || cg_footsteps.integer < 3 )       //1 for sounds, 2 for effects, 3 for marks
02149                 {
02150                         return;
02151                 }
02152         }
02153 
02154         switch ( footStepType )
02155         {
02156         case FOOTSTEP_HEAVY_R:
02157                 footMarkShader = cgs.media.fshrMarkShader;
02158                 break;
02159         case FOOTSTEP_HEAVY_L:
02160                 footMarkShader = cgs.media.fshlMarkShader;
02161                 break;
02162         case FOOTSTEP_R:
02163                 footMarkShader = cgs.media.fsrMarkShader;
02164                 break;
02165         default:
02166         case FOOTSTEP_L:
02167                 footMarkShader = cgs.media.fslMarkShader;
02168                 break;
02169         }
02170 
02171         // fade the shadow out with height
02172 //      alpha = 1.0 - trace.fraction;
02173 
02174         // add the mark as a temporary, so it goes directly to the renderer
02175         // without taking a spot in the cg_marks array
02176         if (trace.plane.normal[0] || trace.plane.normal[1] || trace.plane.normal[2])
02177         {
02178                 CG_ImpactMark( footMarkShader, trace.endpos, trace.plane.normal, 
02179                         orientation, 1,1,1, 1.0f, qfalse, radius, qfalse );
02180         }
02181 }
02182 
02183 static void CG_PlayerFootsteps( centity_t *cent, footstepType_t footStepType )
02184 {
02185         if ( !cg_footsteps.integer ) 
02186         {
02187                 return;
02188         }
02189 
02190         //FIXME: make this a feature of NPCs in the NPCs.cfg? Specify a footstep shader, if any?
02191         if ( cent->currentState.NPC_class != CLASS_ATST
02192                 && cent->currentState.NPC_class != CLASS_CLAW
02193                 && cent->currentState.NPC_class != CLASS_FISH
02194                 && cent->currentState.NPC_class != CLASS_FLIER2
02195                 && cent->currentState.NPC_class != CLASS_GLIDER
02196                 && cent->currentState.NPC_class != CLASS_INTERROGATOR
02197                 && cent->currentState.NPC_class != CLASS_MURJJ
02198                 && cent->currentState.NPC_class != CLASS_PROBE
02199                 && cent->currentState.NPC_class != CLASS_R2D2
02200                 && cent->currentState.NPC_class != CLASS_R5D2
02201                 && cent->currentState.NPC_class != CLASS_REMOTE
02202                 && cent->currentState.NPC_class != CLASS_SEEKER
02203                 && cent->currentState.NPC_class != CLASS_SENTRY
02204                 && cent->currentState.NPC_class != CLASS_SWAMP )
02205         {
02206                 mdxaBone_t      boltMatrix;
02207                 vec3_t tempAngles, sideOrigin;
02208                 int footBolt = -1;
02209 
02210                 tempAngles[PITCH]       = 0;
02211                 tempAngles[YAW]         = cent->pe.legs.yawAngle;
02212                 tempAngles[ROLL]        = 0;
02213 
02214                 switch ( footStepType )
02215                 {
02216                 case FOOTSTEP_R:
02217                 case FOOTSTEP_HEAVY_R:
02218                         footBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*r_leg_foot");//cent->gent->footRBolt;
02219                         break;
02220                 case FOOTSTEP_L:
02221                 case FOOTSTEP_HEAVY_L:
02222                 default:
02223                         footBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*l_leg_foot");//cent->gent->footLBolt;
02224                         break;
02225                 }
02226 
02227 
02228                 //FIXME: get yaw orientation of the foot and use on decal
02229                 trap_G2API_GetBoltMatrix( cent->ghoul2, 0, footBolt, &boltMatrix, tempAngles, cent->lerpOrigin, 
02230                                 cg.time, cgs.gameModels, cent->modelScale);
02231                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, sideOrigin );
02232                 sideOrigin[2] += 15;    //fudge up a bit for coplanar
02233                 _PlayerFootStep( sideOrigin, cent->pe.legs.yawAngle, 6, cent, footStepType );
02234         }
02235 }
02236 
02237 void CG_PlayerAnimEventDo( centity_t *cent, animevent_t *animEvent )
02238 {
02239         soundChannel_t channel = CHAN_AUTO;
02240         clientInfo_t *client = NULL;
02241         qhandle_t swingSound = 0;
02242         qhandle_t spinSound = 0;
02243 
02244         if ( !cent || !animEvent )
02245         {
02246                 return;
02247         }
02248 
02249         switch ( animEvent->eventType )
02250         {
02251         case AEV_SOUNDCHAN:
02252                 channel = (soundChannel_t)animEvent->eventData[AED_SOUNDCHANNEL];
02253         case AEV_SOUND:
02254                 {       // are there variations on the sound?
02255                         const int holdSnd = animEvent->eventData[ AED_SOUNDINDEX_START+Q_irand( 0, animEvent->eventData[AED_SOUND_NUMRANDOMSNDS] ) ];
02256                         if ( holdSnd > 0 )
02257                         {
02258                                 trap_S_StartSound( NULL, cent->currentState.number, channel, holdSnd );
02259                         }
02260                 }
02261                 break;
02262         case AEV_SABER_SWING:
02263                 if (cent->currentState.eType == ET_NPC)
02264                 {
02265                         client = cent->npcClient;
02266                         assert(client);
02267                 }
02268                 else
02269                 {
02270                         client = &cgs.clientinfo[cent->currentState.clientNum];
02271                 }
02272                 if ( client && client->infoValid && client->saber[animEvent->eventData[AED_SABER_SWING_SABERNUM]].swingSound[0] )
02273                 {//custom swing sound
02274                         swingSound = client->saber[0].swingSound[Q_irand(0,2)];
02275                 }
02276                 else
02277                 {
02278                         int             randomSwing = 1;
02279                         switch ( animEvent->eventData[AED_SABER_SWING_TYPE] )
02280                         {
02281                         default:
02282                         case 0://SWING_FAST
02283                                 randomSwing = Q_irand( 1, 3 );
02284                                 break;
02285                         case 1://SWING_MEDIUM
02286                                 randomSwing = Q_irand( 4, 6 );
02287                                 break;
02288                         case 2://SWING_STRONG
02289                                 randomSwing = Q_irand( 7, 9 );
02290                                 break;
02291                         }
02292                         swingSound = trap_S_RegisterSound(va("sound/weapons/saber/saberhup%i.wav", randomSwing));
02293                 }
02294                 trap_S_StartSound(cent->currentState.pos.trBase, cent->currentState.number, CHAN_AUTO, swingSound );
02295                 break;
02296         case AEV_SABER_SPIN:
02297                 if (cent->currentState.eType == ET_NPC)
02298                 {
02299                         client = cent->npcClient;
02300                         assert(client);
02301                 }
02302                 else
02303                 {
02304                         client = &cgs.clientinfo[cent->currentState.clientNum];
02305                 }
02306                 if ( client 
02307                         && client->infoValid 
02308                         && client->saber[AED_SABER_SPIN_SABERNUM].spinSound )
02309                 {//use override
02310                         spinSound = client->saber[AED_SABER_SPIN_SABERNUM].spinSound;
02311                 }
02312                 else
02313                 {
02314                         switch ( animEvent->eventData[AED_SABER_SPIN_TYPE] )
02315                         {
02316                         case 0://saberspinoff
02317                                 spinSound = trap_S_RegisterSound( "sound/weapons/saber/saberspinoff.wav" );
02318                                 break;
02319                         case 1://saberspin
02320                                 spinSound = trap_S_RegisterSound( "sound/weapons/saber/saberspin.wav" );
02321                                 break;
02322                         case 2://saberspin1
02323                                 spinSound = trap_S_RegisterSound( "sound/weapons/saber/saberspin1.wav" );
02324                                 break;
02325                         case 3://saberspin2
02326                                 spinSound = trap_S_RegisterSound( "sound/weapons/saber/saberspin2.wav" );
02327                                 break;
02328                         case 4://saberspin3
02329                                 spinSound = trap_S_RegisterSound( "sound/weapons/saber/saberspin3.wav" );
02330                                 break;
02331                         default://random saberspin1-3
02332                                 spinSound = trap_S_RegisterSound( va( "sound/weapons/saber/saberspin%d.wav", Q_irand(1,3)) );
02333                                 break;
02334                         }
02335                 }
02336                 if ( spinSound )
02337                 {
02338                         trap_S_StartSound( NULL, cent->currentState.clientNum, CHAN_AUTO, spinSound );
02339                 }
02340                 break;
02341         case AEV_FOOTSTEP:
02342                 CG_PlayerFootsteps( cent, (footstepType_t)animEvent->eventData[AED_FOOTSTEP_TYPE] );
02343                 break;
02344         case AEV_EFFECT:
02345 #if 0 //SP method
02346                 //add bolt, play effect
02347                 if ( animEvent->stringData != NULL && cent && cent->gent && cent->gent->ghoul2.size() )
02348                 {//have a bolt name we want to use
02349                         animEvent->eventData[AED_BOLTINDEX] = gi.G2API_AddBolt( &cent->gent->ghoul2[cent->gent->playerModel], animEvent->stringData );
02350                         animEvent->stringData = NULL;//so we don't try to do this again
02351                 }
02352                 if ( animEvent->eventData[AED_BOLTINDEX] != -1 )
02353                 {//have a bolt we want to play the effect on
02354                         G_PlayEffect( animEvent->eventData[AED_EFFECTINDEX], cent->gent->playerModel, animEvent->eventData[AED_BOLTINDEX], cent->currentState.clientNum );
02355                 }
02356                 else
02357                 {//play at origin?  FIXME: maybe allow a fwd/rt/up offset?
02358                         theFxScheduler.PlayEffect( animEvent->eventData[AED_EFFECTINDEX], cent->lerpOrigin, qfalse );
02359                 }
02360 #else //my method
02361                 if (animEvent->stringData && animEvent->stringData[0] && cent && cent->ghoul2)
02362                 {
02363                         animEvent->eventData[AED_MODELINDEX] = 0;
02364                         if ( ( Q_stricmpn( "*blade", animEvent->stringData, 6 ) == 0 
02365                                         || Q_stricmp( "*flash", animEvent->stringData ) == 0 ) )
02366                         {//must be a weapon, try weapon 0?
02367                                 animEvent->eventData[AED_BOLTINDEX] = trap_G2API_AddBolt( cent->ghoul2, 1, animEvent->stringData );
02368                                 if ( animEvent->eventData[AED_BOLTINDEX] != -1 )
02369                                 {//found it!
02370                                         animEvent->eventData[AED_MODELINDEX] = 1;
02371                                 }
02372                                 else
02373                                 {//hmm, just try on the player model, then?
02374                                         animEvent->eventData[AED_BOLTINDEX] = trap_G2API_AddBolt( cent->ghoul2, 0, animEvent->stringData );
02375                                 }
02376                         }
02377                         else
02378                         {
02379                                 animEvent->eventData[AED_BOLTINDEX] = trap_G2API_AddBolt( cent->ghoul2, 0, animEvent->stringData );
02380                         }
02381                         animEvent->stringData[0] = 0;
02382                 }
02383                 if ( animEvent->eventData[AED_BOLTINDEX] != -1 )
02384                 {
02385                         vec3_t lAngles;
02386                         vec3_t bPoint, bAngle;
02387                         mdxaBone_t matrix;
02388 
02389                         VectorSet(lAngles, 0, cent->lerpAngles[YAW], 0);
02390 
02391                         trap_G2API_GetBoltMatrix(cent->ghoul2, animEvent->eventData[AED_MODELINDEX], animEvent->eventData[AED_BOLTINDEX], &matrix, lAngles,
02392                                 cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
02393                         BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, bPoint);
02394                         VectorSet(bAngle, 0, 1, 0);
02395 
02396                         trap_FX_PlayEffectID(animEvent->eventData[AED_EFFECTINDEX], bPoint, bAngle, -1, -1);
02397                 }
02398                 else
02399                 {
02400                         vec3_t bAngle;
02401 
02402                         VectorSet(bAngle, 0, 1, 0);
02403                         trap_FX_PlayEffectID(animEvent->eventData[AED_EFFECTINDEX], cent->lerpOrigin, bAngle, -1, -1);
02404                 }
02405 #endif
02406                 break;
02407         //Would have to keep track of this on server to for these, it's not worth it.
02408         case AEV_FIRE:
02409         case AEV_MOVE:
02410                 break;
02411                 /*
02412         case AEV_FIRE:
02413                 //add fire event
02414                 if ( animEvent->eventData[AED_FIRE_ALT] )
02415                 {
02416                         G_AddEvent( cent->gent, EV_ALT_FIRE, 0 );
02417                 }
02418                 else
02419                 {
02420                         G_AddEvent( cent->gent, EV_FIRE_WEAPON, 0 );
02421                 }
02422                 break;
02423         case AEV_MOVE:
02424                 //make him jump
02425                 if ( cent && cent->gent && cent->gent->client )
02426                 {
02427                         if ( cent->gent->client->ps.groundEntityNum != ENTITYNUM_NONE )
02428                         {//on something
02429                                 vec3_t  fwd, rt, up, angles = {0, cent->gent->client->ps.viewangles[YAW], 0};
02430                                 AngleVectors( angles, fwd, rt, up );
02431                                 //FIXME: set or add to velocity?
02432                                 VectorScale( fwd, animEvent->eventData[AED_MOVE_FWD], cent->gent->client->ps.velocity );
02433                                 VectorMA( cent->gent->client->ps.velocity, animEvent->eventData[AED_MOVE_RT], rt, cent->gent->client->ps.velocity );
02434                                 VectorMA( cent->gent->client->ps.velocity, animEvent->eventData[AED_MOVE_UP], up, cent->gent->client->ps.velocity );
02435                                 
02436                                 if ( animEvent->eventData[AED_MOVE_UP] > 0 )
02437                                 {//a jump
02438                                         cent->gent->client->ps.pm_flags |= PMF_JUMPING;
02439 
02440                                         G_AddEvent( cent->gent, EV_JUMP, 0 );
02441                                         //FIXME: if have force jump, do this?  or specify sound in the event data?
02442                                         //cent->gent->client->ps.forceJumpZStart = cent->gent->client->ps.origin[2];//so we don't take damage if we land at same height
02443                                         //G_SoundOnEnt( cent->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
02444                                 }
02445                         }
02446                 }
02447                 break;
02448                 */
02449         default:
02450                 return;
02451                 break;
02452         }
02453 }
02454 
02455 /*
02456 void CG_PlayerAnimEvents( int animFileIndex, int eventFileIndex, qboolean torso, int oldFrame, int frame, const vec3_t org, int entNum )
02457 
02458 play any keyframed sounds - only when start a new frame
02459 This func is called once for legs and once for torso
02460 */
02461 void CG_PlayerAnimEvents( int animFileIndex, int eventFileIndex, qboolean torso, int oldFrame, int frame, int entNum )
02462 {
02463         int             i;
02464         int             firstFrame = 0, lastFrame = 0;
02465         qboolean        doEvent = qfalse, inSameAnim = qfalse, loopAnim = qfalse, match = qfalse, animBackward = qfalse;
02466         animevent_t *animEvents = NULL;
02467         
02468         if ( torso )
02469         {
02470                 animEvents = bgAllEvents[eventFileIndex].torsoAnimEvents;
02471         }
02472         else
02473         {
02474                 animEvents = bgAllEvents[eventFileIndex].legsAnimEvents;
02475         }
02476         if ( fabs((float)(oldFrame-frame)) > 1 )
02477         {//given a range, see if keyFrame falls in that range
02478                 int oldAnim, anim;
02479                 if ( torso )
02480                 {
02481                         /*
02482                         if ( cg_reliableAnimSounds.integer > 1 )
02483                         {//more precise, slower
02484                                 oldAnim = PM_TorsoAnimForFrame( &g_entities[entNum], oldFrame );
02485                                 anim = PM_TorsoAnimForFrame( &g_entities[entNum], frame );
02486                         }
02487                         else
02488                         */
02489                         {//less precise, but faster
02490                                 oldAnim = cg_entities[entNum].currentState.torsoAnim;
02491                                 anim = cg_entities[entNum].nextState.torsoAnim;
02492                         }
02493                 }
02494                 else
02495                 {
02496                         /*
02497                         if ( cg_reliableAnimSounds.integer > 1 )
02498                         {//more precise, slower
02499                                 oldAnim = PM_LegsAnimForFrame( &g_entities[entNum], oldFrame );
02500                                 anim = PM_TorsoAnimForFrame( &g_entities[entNum], frame );
02501                         }
02502                         else
02503                         */
02504                         {//less precise, but faster
02505                                 oldAnim = cg_entities[entNum].currentState.legsAnim;
02506                                 anim = cg_entities[entNum].nextState.legsAnim;
02507                         }
02508                 }
02509                 if ( anim != oldAnim )
02510                 {//not in same anim
02511                         inSameAnim = qfalse;
02512                         //FIXME: we *could* see if the oldFrame was *just about* to play the keyframed sound...
02513                 }
02514                 else
02515                 {//still in same anim, check for looping anim
02516                         animation_t *animation;
02517 
02518                         inSameAnim = qtrue;
02519                         animation = &bgAllAnims[animFileIndex].anims[anim];
02520                         animBackward = (animation->frameLerp<0);
02521                         if ( animation->loopFrames != -1 )
02522                         {//a looping anim!
02523                                 loopAnim = qtrue;
02524                                 firstFrame = animation->firstFrame;
02525                                 lastFrame = animation->firstFrame+animation->numFrames;
02526                         }
02527                 }
02528         }
02529 
02530         // Check for anim sound
02531         for (i=0;i<MAX_ANIM_EVENTS;++i)
02532         {
02533                 if ( animEvents[i].eventType == AEV_NONE )      // No event, end of list
02534                 {
02535                         break;
02536                 }
02537 
02538                 match = qfalse;
02539                 if ( animEvents[i].keyFrame == frame )
02540                 {//exact match
02541                         match = qtrue;
02542                 }
02543                 else if ( fabs((float)(oldFrame-frame)) > 1 /*&& cg_reliableAnimSounds.integer*/ )
02544                 {//given a range, see if keyFrame falls in that range
02545                         if ( inSameAnim )
02546                         {//if changed anims altogether, sorry, the sound is lost
02547                                 if ( fabs((float)(oldFrame-animEvents[i].keyFrame)) <= 3
02548                                          || fabs((float)(frame-animEvents[i].keyFrame)) <= 3 )
02549                                 {//must be at least close to the keyframe
02550                                         if ( animBackward )
02551                                         {//animation plays backwards
02552                                                 if ( oldFrame > animEvents[i].keyFrame && frame < animEvents[i].keyFrame )
02553                                                 {//old to new passed through keyframe
02554                                                         match = qtrue;
02555                                                 }
02556                                                 else if ( loopAnim )
02557                                                 {//hmm, didn't pass through it linearally, see if we looped
02558                                                         if ( animEvents[i].keyFrame >= firstFrame && animEvents[i].keyFrame < lastFrame )
02559                                                         {//keyframe is in this anim
02560                                                                 if ( oldFrame > animEvents[i].keyFrame 
02561                                                                         && frame > oldFrame )
02562                                                                 {//old to new passed through keyframe
02563                                                                         match = qtrue;
02564                                                                 }
02565                                                         }
02566                                                 }
02567                                         }
02568                                         else
02569                                         {//anim plays forwards
02570                                                 if ( oldFrame < animEvents[i].keyFrame && frame > animEvents[i].keyFrame )
02571                                                 {//old to new passed through keyframe
02572                                                         match = qtrue;
02573                                                 }
02574                                                 else if ( loopAnim )
02575                                                 {//hmm, didn't pass through it linearally, see if we looped
02576                                                         if ( animEvents[i].keyFrame >= firstFrame && animEvents[i].keyFrame < lastFrame )
02577                                                         {//keyframe is in this anim
02578                                                                 if ( oldFrame < animEvents[i].keyFrame 
02579                                                                         && frame < oldFrame )
02580                                                                 {//old to new passed through keyframe
02581                                                                         match = qtrue;
02582                                                                 }
02583                                                         }
02584                                                 }
02585                                         }
02586                                 }
02587                         }
02588                 }
02589                 if ( match )
02590                 {
02591                         switch ( animEvents[i].eventType )
02592                         {
02593                         case AEV_SOUND:
02594                         case AEV_SOUNDCHAN:
02595                                 // Determine probability of playing sound
02596                                 if (!animEvents[i].eventData[AED_SOUND_PROBABILITY])    // 100% 
02597                                 {
02598                                         doEvent = qtrue;
02599                                 }
02600                                 else if (animEvents[i].eventData[AED_SOUND_PROBABILITY] > Q_irand(0, 99) )
02601                                 {
02602                                         doEvent = qtrue;
02603                                 }
02604                                 break;
02605                         case AEV_SABER_SWING:
02606                                 // Determine probability of playing sound
02607                                 if (!animEvents[i].eventData[AED_SABER_SWING_PROBABILITY])      // 100% 
02608                                 {
02609                                         doEvent = qtrue;
02610                                 }
02611                                 else if (animEvents[i].eventData[AED_SABER_SWING_PROBABILITY] > Q_irand(0, 99) )
02612                                 {
02613                                         doEvent = qtrue;
02614                                 }
02615                                 break;
02616                         case AEV_SABER_SPIN:
02617                                 // Determine probability of playing sound
02618                                 if (!animEvents[i].eventData[AED_SABER_SPIN_PROBABILITY])       // 100% 
02619                                 {
02620                                         doEvent = qtrue;
02621                                 }
02622                                 else if (animEvents[i].eventData[AED_SABER_SPIN_PROBABILITY] > Q_irand(0, 99) )
02623                                 {
02624                                         doEvent = qtrue;
02625                                 }
02626                                 break;
02627                         case AEV_FOOTSTEP:
02628                                 // Determine probability of playing sound
02629                                 if (!animEvents[i].eventData[AED_FOOTSTEP_PROBABILITY]) // 100% 
02630                                 {
02631                                         doEvent = qtrue;
02632                                 }
02633                                 else if (animEvents[i].eventData[AED_FOOTSTEP_PROBABILITY] > Q_irand(0, 99) )
02634                                 {
02635                                         doEvent = qtrue;
02636                                 }
02637                                 break;
02638                         case AEV_EFFECT:
02639                                 // Determine probability of playing sound
02640                                 if (!animEvents[i].eventData[AED_EFFECT_PROBABILITY])   // 100% 
02641                                 {
02642                                         doEvent = qtrue;
02643                                 }
02644                                 else if (animEvents[i].eventData[AED_EFFECT_PROBABILITY] > Q_irand(0, 99) )
02645                                 {
02646                                         doEvent = qtrue;
02647                                 }
02648                                 break;
02649                         case AEV_FIRE:
02650                                 // Determine probability of playing sound
02651                                 if (!animEvents[i].eventData[AED_FIRE_PROBABILITY])     // 100% 
02652                                 {
02653                                         doEvent = qtrue;
02654                                 }
02655                                 else if (animEvents[i].eventData[AED_FIRE_PROBABILITY] > Q_irand(0, 99) )
02656                                 {
02657                                         doEvent = qtrue;
02658                                 }
02659                                 break;
02660                         case AEV_MOVE:
02661                                 doEvent = qtrue;
02662                                 break;
02663                         default:
02664                                 //doEvent = qfalse;//implicit
02665                                 break;
02666                         }
02667                         // do event
02668                         if ( doEvent )
02669                         {
02670                                 CG_PlayerAnimEventDo( &cg_entities[entNum], &animEvents[i] );
02671                         }
02672                 }
02673         }
02674 }
02675 
02676 void CG_TriggerAnimSounds( centity_t *cent )
02677 { //this also sets the lerp frames, so I suggest you keep calling it regardless of if you want anim sounds.
02678         int             curFrame = 0;
02679         float   currentFrame = 0;
02680         int             sFileIndex;
02681 
02682         assert(cent->localAnimIndex >= 0);
02683 
02684         sFileIndex = cent->eventAnimIndex;
02685 
02686         if (trap_G2API_GetBoneFrame(cent->ghoul2, "model_root", cg.time, &currentFrame, cgs.gameModels, 0))
02687         {
02688                 // the above may have failed, not sure what to do about it, current frame will be zero in that case
02689                 curFrame = floor( currentFrame );
02690         }
02691         if ( curFrame != cent->pe.legs.frame )
02692         {
02693                 CG_PlayerAnimEvents( cent->localAnimIndex, sFileIndex, qfalse, cent->pe.legs.frame, curFrame, cent->currentState.number );
02694         }
02695         cent->pe.legs.oldFrame = cent->pe.torso.frame;
02696         cent->pe.legs.frame = curFrame;
02697 
02698         if (cent->noLumbar)
02699         { //probably a droid or something.
02700                 cent->pe.torso.oldFrame = cent->pe.legs.oldFrame;
02701                 cent->pe.torso.frame = cent->pe.legs.frame;
02702                 return;
02703         }
02704 
02705         if (trap_G2API_GetBoneFrame(cent->ghoul2, "lower_lumbar", cg.time, &currentFrame, cgs.gameModels, 0))
02706         {
02707                 curFrame = floor( currentFrame );
02708         }
02709         if ( curFrame != cent->pe.torso.frame )
02710         {
02711                 CG_PlayerAnimEvents( cent->localAnimIndex, sFileIndex, qtrue, cent->pe.torso.frame, curFrame, cent->currentState.number );
02712         }
02713         cent->pe.torso.oldFrame = cent->pe.torso.frame;
02714         cent->pe.torso.frame = curFrame;        
02715         cent->pe.torso.backlerp = 1.0f - (currentFrame - (float)curFrame);      
02716 }
02717 
02718 
02719 static qboolean CG_FirstAnimFrame(lerpFrame_t *lf, qboolean torsoOnly, float speedScale);
02720 
02721 qboolean CG_InRoll( centity_t *cent )
02722 {
02723         switch ( (cent->currentState.legsAnim) )
02724         {
02725         case BOTH_GETUP_BROLL_B:
02726         case BOTH_GETUP_BROLL_F:
02727         case BOTH_GETUP_BROLL_L:
02728         case BOTH_GETUP_BROLL_R:
02729         case BOTH_GETUP_FROLL_B:
02730         case BOTH_GETUP_FROLL_F:
02731         case BOTH_GETUP_FROLL_L:
02732         case BOTH_GETUP_FROLL_R:
02733         case BOTH_ROLL_F:
02734         case BOTH_ROLL_B:
02735         case BOTH_ROLL_R:
02736         case BOTH_ROLL_L:
02737                 if ( cent->pe.legs.animationTime > cg.time )
02738                 {
02739                         return qtrue;
02740                 }
02741                 break;
02742         }
02743         return qfalse;
02744 }
02745 
02746 qboolean CG_InRollAnim( centity_t *cent )
02747 {
02748         switch ( (cent->currentState.legsAnim) )
02749         {
02750         case BOTH_ROLL_F:
02751         case BOTH_ROLL_B:
02752         case BOTH_ROLL_R:
02753         case BOTH_ROLL_L:
02754                 return qtrue;
02755         }
02756         return qfalse;
02757 }
02758 
02759 /*
02760 ===============
02761 CG_SetLerpFrameAnimation
02762 ===============
02763 */
02764 #include "../namespace_begin.h"
02765 qboolean BG_SaberStanceAnim( int anim );
02766 qboolean PM_RunningAnim( int anim );
02767 #include "../namespace_end.h"
02768 static void CG_SetLerpFrameAnimation( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float animSpeedMult, qboolean torsoOnly, qboolean flipState) {
02769         animation_t     *anim;
02770         float animSpeed;
02771         int       flags=BONE_ANIM_OVERRIDE_FREEZE;
02772         int oldAnim = -1;
02773         int blendTime = 100;
02774         float oldSpeed = lf->animationSpeed;
02775 
02776         if (cent->localAnimIndex > 0)
02777         { //rockettroopers can't have broken arms, nor can anything else but humanoids
02778                 ci->brokenLimbs = cent->currentState.brokenLimbs;
02779         }
02780 
02781         oldAnim = lf->animationNumber;
02782 
02783         lf->animationNumber = newAnimation;
02784 
02785         if ( newAnimation < 0 || newAnimation >= MAX_TOTALANIMATIONS ) {
02786                 CG_Error( "Bad animation number: %i", newAnimation );
02787         }
02788 
02789         anim = &bgAllAnims[cent->localAnimIndex].anims[ newAnimation ];
02790 
02791         lf->animation = anim;
02792         lf->animationTime = lf->frameTime + abs(anim->frameLerp);
02793 
02794         if (cent->localAnimIndex > 1 &&
02795                 anim->firstFrame == 0 &&
02796                 anim->numFrames == 0)
02797         { //We'll allow this for non-humanoids.
02798                 return;
02799         }
02800 
02801         if ( cg_debugAnim.integer && (cg_debugAnim.integer < 0 || cg_debugAnim.integer == cent->currentState.clientNum) ) {
02802                 if (lf == &cent->pe.legs)
02803                 {
02804                         CG_Printf( "%d: %d TORSO Anim: %i, '%s'\n", cg.time, cent->currentState.clientNum, newAnimation, GetStringForID(animTable, newAnimation));
02805                 }
02806                 else
02807                 {
02808                         CG_Printf( "%d: %d LEGS Anim: %i, '%s'\n", cg.time, cent->currentState.clientNum, newAnimation, GetStringForID(animTable, newAnimation));
02809                 }
02810         }
02811 
02812         if (cent->ghoul2)
02813         {
02814                 qboolean resumeFrame = qfalse;
02815                 int beginFrame = -1;
02816                 int firstFrame;
02817                 int lastFrame;
02818 #if 0 //disabled for now
02819                 float unused;
02820 #endif
02821 
02822                 animSpeed = 50.0f / anim->frameLerp;
02823                 if (lf->animation->loopFrames != -1)
02824                 {
02825                         flags = BONE_ANIM_OVERRIDE_LOOP;
02826                 }
02827 
02828                 if (animSpeed < 0)
02829                 {
02830                         lastFrame = anim->firstFrame;
02831                         firstFrame = anim->firstFrame + anim->numFrames;
02832                 }
02833                 else
02834                 {
02835                         firstFrame = anim->firstFrame;
02836                         lastFrame = anim->firstFrame + anim->numFrames;
02837                 }
02838 
02839                 if (cg_animBlend.integer)
02840                 {
02841                         flags |= BONE_ANIM_BLEND;
02842                 }
02843 
02844                 if (BG_InDeathAnim(newAnimation))
02845                 {
02846                         flags &= ~BONE_ANIM_BLEND;
02847                 }
02848                 else if ( oldAnim != -1 &&
02849                         BG_InDeathAnim(oldAnim))
02850                 {
02851                         flags &= ~BONE_ANIM_BLEND;
02852                 }
02853 
02854                 if (flags & BONE_ANIM_BLEND)
02855                 {
02856                         if (BG_FlippingAnim(newAnimation))
02857                         {
02858                                 blendTime = 200;
02859                         }
02860                         else if ( oldAnim != -1 &&
02861                                 (BG_FlippingAnim(oldAnim)) )
02862                         {
02863                                 blendTime = 200;
02864                         }
02865                 }
02866 
02867                 animSpeed *= animSpeedMult;
02868 
02869                 BG_SaberStartTransAnim(cent->currentState.number, cent->currentState.fireflag, cent->currentState.weapon, newAnimation, &animSpeed, cent->currentState.brokenLimbs);
02870 
02871                 if (torsoOnly)
02872                 {
02873                         if (lf->animationTorsoSpeed != animSpeedMult && newAnimation == oldAnim &&
02874                                 flipState == lf->lastFlip)
02875                         { //same animation, but changing speed, so we will want to resume off the frame we're on.
02876                                 resumeFrame = qtrue;
02877                         }
02878                         lf->animationTorsoSpeed = animSpeedMult;
02879                 }
02880                 else
02881                 {
02882                         if (lf->animationSpeed != animSpeedMult && newAnimation == oldAnim &&
02883                                 flipState == lf->lastFlip)
02884                         { //same animation, but changing speed, so we will want to resume off the frame we're on.
02885                                 resumeFrame = qtrue;
02886                         }
02887                         lf->animationSpeed = animSpeedMult;
02888                 }
02889 
02890                 //vehicles may have torso etc but we only want to animate the root bone
02891                 if ( cent->currentState.NPC_class == CLASS_VEHICLE )
02892                 {
02893                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", firstFrame, lastFrame, flags, animSpeed,cg.time, beginFrame, blendTime);
02894                         return;
02895                 }
02896 
02897                 if (torsoOnly && !cent->noLumbar)
02898                 { //rww - The guesswork based on the lerp frame figures is usually BS, so I've resorted to a call to get the frame of the bone directly.
02899                         float GBAcFrame = 0;
02900                         if (resumeFrame)
02901                         { //we already checked, and this is the same anim, same flip state, but different speed, so we want to resume with the new speed off of the same frame.
02902                                 trap_G2API_GetBoneFrame(cent->ghoul2, "lower_lumbar", cg.time, &GBAcFrame, NULL, 0);
02903                                 beginFrame = GBAcFrame;
02904                         }
02905 
02906                         //even if resuming, also be sure to check if we are running the same frame on the legs. If so, we want to use their frame no matter what.
02907                         trap_G2API_GetBoneFrame(cent->ghoul2, "model_root", cg.time, &GBAcFrame, NULL, 0);
02908 
02909                         if ((cent->currentState.torsoAnim) == (cent->currentState.legsAnim) && GBAcFrame >= anim->firstFrame && GBAcFrame <= (anim->firstFrame + anim->numFrames))
02910                         { //if the legs are already running this anim, pick up on the exact same frame to avoid the "wobbly spine" problem.
02911                                 beginFrame = GBAcFrame;
02912                         }
02913 
02914                         if (firstFrame > lastFrame || ci->torsoAnim == newAnimation)
02915                         { //don't resume on backwards playing animations.. I guess.
02916                                 beginFrame = -1;
02917                         }
02918 
02919                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lower_lumbar", firstFrame, lastFrame, flags, animSpeed,cg.time, beginFrame, blendTime);
02920 
02921                         // Update the torso frame with the new animation
02922                         cent->pe.torso.frame = firstFrame;
02923                         
02924                         if (ci)
02925                         {
02926                                 ci->torsoAnim = newAnimation;
02927                         }
02928                 }
02929                 else
02930                 {
02931                         if (resumeFrame)
02932                         { //we already checked, and this is the same anim, same flip state, but different speed, so we want to resume with the new speed off of the same frame.
02933                                 float GBAcFrame = 0;
02934                                 trap_G2API_GetBoneFrame(cent->ghoul2, "model_root", cg.time, &GBAcFrame, NULL, 0);
02935                                 beginFrame = GBAcFrame;
02936                         }
02937 
02938                         if ((beginFrame < firstFrame) || (beginFrame > lastFrame))
02939                         { //out of range, don't use it then.
02940                                 beginFrame = -1;
02941                         }
02942 
02943                         if (cent->currentState.torsoAnim == cent->currentState.legsAnim &&
02944                                 (ci->legsAnim != newAnimation || oldSpeed != animSpeed))
02945                         { //alright, we are starting an anim on the legs, and that same anim is already playing on the toro, so pick up the frame.
02946                                 float GBAcFrame = 0;
02947                                 int oldBeginFrame = beginFrame;
02948 
02949                                 trap_G2API_GetBoneFrame(cent->ghoul2, "lower_lumbar", cg.time, &GBAcFrame, NULL, 0);
02950                                 beginFrame = GBAcFrame;
02951                                 if ((beginFrame < firstFrame) || (beginFrame > lastFrame))
02952                                 { //out of range, don't use it then.
02953                                         beginFrame = oldBeginFrame;
02954                                 }
02955                         }
02956 
02957                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", firstFrame, lastFrame, flags, animSpeed, cg.time, beginFrame, blendTime);
02958 
02959                         if (ci)
02960                         {
02961                                 ci->legsAnim = newAnimation;
02962                         }
02963                 }
02964 
02965                 if (cent->localAnimIndex <= 1 && (cent->currentState.torsoAnim) == newAnimation && !cent->noLumbar)
02966                 { //make sure we're humanoid before we access the motion bone
02967                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "Motion", firstFrame, lastFrame, flags, animSpeed, cg.time, beginFrame, blendTime);
02968                 }
02969 
02970 #if 0 //disabled for now
02971                 if (cent->localAnimIndex <= 1 && cent->currentState.brokenLimbs &&
02972                         (cent->currentState.brokenLimbs & (1 << BROKENLIMB_LARM)))
02973                 { //broken left arm
02974                         char *brokenBone = "lhumerus";
02975                         animation_t *armAnim;
02976                         int armFirstFrame;
02977                         int armLastFrame;
02978                         int armFlags = 0;
02979                         float armAnimSpeed;
02980 
02981                         armAnim = &bgAllAnims[cent->localAnimIndex].anims[ BOTH_DEAD21 ];
02982                         ci->brokenLimbs = cent->currentState.brokenLimbs;
02983 
02984                         armFirstFrame = armAnim->firstFrame;
02985                         armLastFrame = armAnim->firstFrame+armAnim->numFrames;
02986                         armAnimSpeed = 50.0f / armAnim->frameLerp;
02987                         armFlags = BONE_ANIM_OVERRIDE_LOOP;
02988 
02989                         if (cg_animBlend.integer)
02990                         {
02991                                 armFlags |= BONE_ANIM_BLEND;
02992                         }
02993 
02994                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, brokenBone, armFirstFrame, armLastFrame, armFlags, armAnimSpeed, cg.time, -1, blendTime);
02995                 }
02996                 else if (cent->localAnimIndex <= 1 && cent->currentState.brokenLimbs &&
02997                         (cent->currentState.brokenLimbs & (1 << BROKENLIMB_RARM)))
02998                 { //broken right arm
02999                         char *brokenBone = "rhumerus";
03000                         char *supportBone = "lhumerus";
03001 
03002                         ci->brokenLimbs = cent->currentState.brokenLimbs;
03003 
03004                         //Only put the arm in a broken pose if the anim is such that we
03005                         //want to allow it.
03006                         if ((//cent->currentState.weapon == WP_MELEE ||
03007                                 cent->currentState.weapon != WP_SABER ||
03008                                 BG_SaberStanceAnim(newAnimation) ||
03009                                 PM_RunningAnim(newAnimation)) &&
03010                                 cent->currentState.torsoAnim == newAnimation &&
03011                                 (!ci->saber[1].model[0] || cent->currentState.weapon != WP_SABER))
03012                         {
03013                                 int armFirstFrame;
03014                                 int armLastFrame;
03015                                 int armFlags = 0;
03016                                 float armAnimSpeed;
03017                                 animation_t *armAnim;
03018 
03019                                 if (cent->currentState.weapon == WP_MELEE ||
03020                                         cent->currentState.weapon == WP_SABER ||
03021                                         cent->currentState.weapon == WP_BRYAR_PISTOL)
03022                                 { //don't affect this arm if holding a gun, just make the other arm support it
03023                                         armAnim = &bgAllAnims[cent->localAnimIndex].anims[ BOTH_ATTACK2 ];
03024 
03025                                         //armFirstFrame = armAnim->firstFrame;
03026                                         armFirstFrame = armAnim->firstFrame+armAnim->numFrames;
03027                                         armLastFrame = armAnim->firstFrame+armAnim->numFrames;
03028                                         armAnimSpeed = 50.0f / armAnim->frameLerp;
03029                                         armFlags = BONE_ANIM_OVERRIDE_LOOP;
03030 
03031                                         /*
03032                                         if (cg_animBlend.integer)
03033                                         {
03034                                                 armFlags |= BONE_ANIM_BLEND;
03035                                         }
03036                                         */
03037                                         //No blend on the broken arm
03038 
03039                                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, brokenBone, armFirstFrame, armLastFrame, armFlags, armAnimSpeed, cg.time, -1, 0);
03040                                 }
03041                                 else
03042                                 { //we want to keep the broken bone updated for some cases
03043                                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, brokenBone, firstFrame, lastFrame, flags, animSpeed, cg.time, beginFrame, blendTime);
03044                                 }
03045 
03046                                 if (newAnimation != BOTH_MELEE1 &&
03047                                         newAnimation != BOTH_MELEE2 &&
03048                                         (newAnimation == TORSO_WEAPONREADY2 || newAnimation == BOTH_ATTACK2 || cent->currentState.weapon < WP_BRYAR_PISTOL))
03049                                 {
03050                                         //Now set the left arm to "support" the right one
03051                                         armAnim = &bgAllAnims[cent->localAnimIndex].anims[ BOTH_STAND2 ];
03052                                         armFirstFrame = armAnim->firstFrame;
03053                                         armLastFrame = armAnim->firstFrame+armAnim->numFrames;
03054                                         armAnimSpeed = 50.0f / armAnim->frameLerp;
03055                                         armFlags = BONE_ANIM_OVERRIDE_LOOP;
03056 
03057                                         if (cg_animBlend.integer)
03058                                         {
03059                                                 armFlags |= BONE_ANIM_BLEND;
03060                                         }
03061 
03062                                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, supportBone, armFirstFrame, armLastFrame, armFlags, armAnimSpeed, cg.time, -1, 150);
03063                                 }
03064                                 else
03065                                 { //we want to keep the support bone updated for some cases
03066                                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, supportBone, firstFrame, lastFrame, flags, animSpeed, cg.time, beginFrame, blendTime);
03067                                 }
03068                         }
03069                         else if (cent->currentState.torsoAnim == newAnimation)
03070                         { //otherwise, keep it set to the same as the torso
03071                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, brokenBone, firstFrame, lastFrame, flags, animSpeed, cg.time, beginFrame, blendTime);
03072                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, supportBone, firstFrame, lastFrame, flags, animSpeed, cg.time, beginFrame, blendTime);
03073                         }
03074                 }
03075                 else if (ci &&
03076                         (ci->brokenLimbs ||
03077                         trap_G2API_GetBoneFrame(cent->ghoul2, "lhumerus", cg.time, &unused, cgs.gameModels, 0) ||
03078                         trap_G2API_GetBoneFrame(cent->ghoul2, "rhumerus", cg.time, &unused, cgs.gameModels, 0)))
03079                         //rwwFIXMEFIXME: brokenLimbs gets stomped sometimes, but it shouldn't.
03080                 { //remove the bone now so it can be set again
03081                         char *brokenBone = NULL;
03082                         int broken = 0;
03083 
03084                         //Warning: Don't remove bones that you've added as bolts unless you want to invalidate your bolt index
03085                         //(well, in theory, I haven't actually run into the problem)
03086                         if (ci->brokenLimbs & (1<<BROKENLIMB_LARM))
03087                         {
03088                                 brokenBone = "lhumerus";
03089                                 broken |= (1<<BROKENLIMB_LARM);
03090                         }
03091                         else if (ci->brokenLimbs & (1<<BROKENLIMB_RARM))
03092                         { //can only have one arm broken at once.
03093                                 brokenBone = "rhumerus";
03094                                 broken |= (1<<BROKENLIMB_RARM);
03095 
03096                                 //want to remove the support bone too then
03097                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lhumerus", 0, 1, 0, 0, cg.time, -1, 0);
03098                                 if (!trap_G2API_RemoveBone(cent->ghoul2, "lhumerus", 0))
03099                                 {
03100                                         assert(0);
03101                                         Com_Printf("WARNING: Failed to remove lhumerus\n");
03102                                 }
03103                         }
03104 
03105                         if (!brokenBone)
03106                         {
03107                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lhumerus", 0, 1, 0, 0, cg.time, -1, 0);
03108                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, "rhumerus", 0, 1, 0, 0, cg.time, -1, 0);
03109                                 trap_G2API_RemoveBone(cent->ghoul2, "lhumerus", 0);
03110                                 trap_G2API_RemoveBone(cent->ghoul2, "rhumerus", 0);
03111                                 ci->brokenLimbs = 0;
03112                         }
03113                         else
03114                         {
03115                                 //Set the flags and stuff to 0, so that the remove will succeed
03116                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, brokenBone, 0, 1, 0, 0, cg.time, -1, 0);
03117 
03118                                 //Now remove it
03119                                 if (!trap_G2API_RemoveBone(cent->ghoul2, brokenBone, 0))
03120                                 {
03121                                         assert(0);
03122                                         Com_Printf("WARNING: Failed to remove %s\n", brokenBone);
03123                                 }
03124                                 ci->brokenLimbs &= ~broken;
03125                         }
03126                 }
03127 #endif
03128         }
03129 }
03130 
03131 
03132 /*
03133 ===============
03134 CG_FirstAnimFrame
03135 
03136 Returns true if the lerpframe is on its first frame of animation.
03137 Otherwise false.
03138 
03139 This is used to scale an animation into higher-speed without restarting
03140 the animation before it completes at normal speed, in the case of a looping
03141 animation (such as the leg running anim).
03142 ===============
03143 */
03144 static qboolean CG_FirstAnimFrame(lerpFrame_t *lf, qboolean torsoOnly, float speedScale)
03145 {
03146         if (torsoOnly)
03147         {
03148                 if (lf->animationTorsoSpeed == speedScale)
03149                 {
03150                         return qfalse;
03151                 }
03152         }
03153         else
03154         {
03155                 if (lf->animationSpeed == speedScale)
03156                 {
03157                         return qfalse;
03158                 }
03159         }
03160 
03161         //I don't care where it is in the anim now, I am going to pick up from the same bone frame.
03162 /*
03163         if (lf->animation->numFrames < 2)
03164         {
03165                 return qtrue;
03166         }
03167 
03168         if (lf->animation->firstFrame == lf->frame)
03169         {
03170                 return qtrue;
03171         }
03172 */
03173 
03174         return qtrue;
03175 }
03176 
03177 /*
03178 ===============
03179 CG_RunLerpFrame
03180 
03181 Sets cg.snap, cg.oldFrame, and cg.backlerp
03182 cg.time should be between oldFrameTime and frameTime after exit
03183 ===============
03184 */
03185 static void CG_RunLerpFrame( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, qboolean flipState, int newAnimation, float speedScale, qboolean torsoOnly) 
03186 {
03187         // debugging tool to get no animations
03188         if ( cg_animSpeed.integer == 0 ) {
03189                 lf->oldFrame = lf->frame = lf->backlerp = 0;
03190                 return;
03191         }
03192 
03193         // see if the animation sequence is switching
03194         if (cent->currentState.forceFrame)
03195         {
03196                 if (lf->lastForcedFrame != cent->currentState.forceFrame)
03197                 {
03198                         int flags = BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND;
03199                         float animSpeed = 1.0f;
03200                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lower_lumbar", cent->currentState.forceFrame, cent->currentState.forceFrame+1, flags, animSpeed, cg.time, -1, 150);
03201                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", cent->currentState.forceFrame, cent->currentState.forceFrame+1, flags, animSpeed, cg.time, -1, 150);
03202                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "Motion", cent->currentState.forceFrame, cent->currentState.forceFrame+1, flags, animSpeed, cg.time, -1, 150);
03203                 }
03204 
03205                 lf->lastForcedFrame = cent->currentState.forceFrame;
03206 
03207                 lf->animationNumber = 0;
03208         }
03209         else
03210         {
03211                 lf->lastForcedFrame = -1;
03212 
03213                 if ( (newAnimation != lf->animationNumber || cent->currentState.brokenLimbs != ci->brokenLimbs || lf->lastFlip != flipState || !lf->animation) || (CG_FirstAnimFrame(lf, torsoOnly, speedScale)) ) 
03214                 {
03215                         CG_SetLerpFrameAnimation( cent, ci, lf, newAnimation, speedScale, torsoOnly, flipState);
03216                 }
03217         }
03218 
03219         lf->lastFlip = flipState;
03220 
03221         if ( lf->frameTime > cg.time + 200 ) {
03222                 lf->frameTime = cg.time;
03223         }
03224 
03225         if ( lf->oldFrameTime > cg.time ) {
03226                 lf->oldFrameTime = cg.time;
03227         }
03228         
03229         // calculate current lerp value
03230         if ( lf->frameTime == lf->oldFrameTime ) {
03231                 lf->backlerp = 0;
03232         } else {
03233                 lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
03234         }
03235 }
03236 
03237 
03238 /*
03239 ===============
03240 CG_ClearLerpFrame
03241 ===============
03242 */
03243 static void CG_ClearLerpFrame( centity_t *cent, clientInfo_t *ci, lerpFrame_t *lf, int animationNumber, qboolean torsoOnly) {
03244         lf->frameTime = lf->oldFrameTime = cg.time;
03245         CG_SetLerpFrameAnimation( cent, ci, lf, animationNumber, 1, torsoOnly, qfalse );
03246 
03247         if ( lf->animation->frameLerp < 0 )
03248         {//Plays backwards
03249                 lf->oldFrame = lf->frame = (lf->animation->firstFrame + lf->animation->numFrames);
03250         }
03251         else
03252         {
03253                 lf->oldFrame = lf->frame = lf->animation->firstFrame;
03254         }
03255 }
03256 
03257 
03258 /*
03259 ===============
03260 CG_PlayerAnimation
03261 ===============
03262 */
03263 #include "../namespace_begin.h"
03264 qboolean PM_WalkingAnim( int anim );
03265 #include "../namespace_end.h"
03266 
03267 static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp,
03268                                                 int *torsoOld, int *torso, float *torsoBackLerp ) {
03269         clientInfo_t    *ci;
03270         int                             clientNum;
03271         float                   speedScale;
03272 
03273         clientNum = cent->currentState.clientNum;
03274 
03275         if ( cg_noPlayerAnims.integer ) {
03276                 *legsOld = *legs = *torsoOld = *torso = 0;
03277                 return;
03278         }
03279 
03280         if (!PM_RunningAnim(cent->currentState.legsAnim) &&
03281                 !PM_WalkingAnim(cent->currentState.legsAnim))
03282         { //if legs are not in a walking/running anim then just animate at standard speed
03283                 speedScale = 1.0f;
03284         }
03285         else if (cent->currentState.forcePowersActive & (1 << FP_RAGE))
03286         {
03287                 speedScale = 1.3f;
03288         }
03289         else if (cent->currentState.forcePowersActive & (1 << FP_SPEED))
03290         {
03291                 speedScale = 1.7f;
03292         }
03293         else
03294         {
03295                 speedScale = 1.0f;
03296         }
03297 
03298         if (cent->currentState.eType == ET_NPC)
03299         {
03300                 ci = cent->npcClient;
03301                 assert(ci);
03302         }
03303         else
03304         {
03305                 ci = &cgs.clientinfo[ clientNum ];
03306         }
03307 
03308         CG_RunLerpFrame( cent, ci, &cent->pe.legs, cent->currentState.legsFlip, cent->currentState.legsAnim, speedScale, qfalse);
03309 
03310         if (!(cent->currentState.forcePowersActive & (1 << FP_RAGE)))
03311         { //don't affect torso anim speed unless raged
03312                 speedScale = 1.0f;
03313         }
03314         else
03315         {
03316                 speedScale = 1.7f;
03317         }
03318 
03319         *legsOld = cent->pe.legs.oldFrame;
03320         *legs = cent->pe.legs.frame;
03321         *legsBackLerp = cent->pe.legs.backlerp;
03322 
03323         // If this is not a vehicle, you may lerm the frame (since vehicles never have a torso anim). -AReis
03324         if ( cent->currentState.NPC_class != CLASS_VEHICLE )
03325         {       
03326                 CG_RunLerpFrame( cent, ci, &cent->pe.torso, cent->currentState.torsoFlip, cent->currentState.torsoAnim, speedScale, qtrue );
03327 
03328                 *torsoOld = cent->pe.torso.oldFrame;
03329                 *torso = cent->pe.torso.frame;
03330                 *torsoBackLerp = cent->pe.torso.backlerp;
03331         }
03332 }
03333 
03334 
03335 
03336 
03337 /*
03338 =============================================================================
03339 
03340 PLAYER ANGLES
03341 
03342 =============================================================================
03343 */
03344 
03345 #if 0
03346 typedef struct boneAngleParms_s {
03347         void *ghoul2;
03348         int modelIndex;
03349         char *boneName;
03350         vec3_t angles;
03351         int flags;
03352         int up;
03353         int right;
03354         int forward;
03355         qhandle_t *modelList;
03356         int blendTime;
03357         int currentTime;
03358 
03359         qboolean refreshSet;
03360 } boneAngleParms_t;
03361 
03362 boneAngleParms_t cgBoneAnglePostSet;
03363 #endif
03364 
03365 void CG_G2SetBoneAngles(void *ghoul2, int modelIndex, const char *boneName, const vec3_t angles, const int flags,
03366                                                                 const int up, const int right, const int forward, qhandle_t *modelList,
03367                                                                 int blendTime , int currentTime )
03368 { //we want to hold off on setting the bone angles until the end of the frame, because every time we set
03369   //them the entire skeleton has to be reconstructed.
03370 #if 0
03371         //This function should ONLY be called from CG_Player() or a function that is called only within CG_Player().
03372         //At the end of the frame we will check to use this information to call SetBoneAngles
03373         memset(&cgBoneAnglePostSet, 0, sizeof(cgBoneAnglePostSet));
03374         cgBoneAnglePostSet.ghoul2 = ghoul2;
03375         cgBoneAnglePostSet.modelIndex = modelIndex;
03376         cgBoneAnglePostSet.boneName = (char *)boneName;
03377 
03378         cgBoneAnglePostSet.angles[0] = angles[0];
03379         cgBoneAnglePostSet.angles[1] = angles[1];
03380         cgBoneAnglePostSet.angles[2] = angles[2];
03381 
03382         cgBoneAnglePostSet.flags = flags;
03383         cgBoneAnglePostSet.up = up;
03384         cgBoneAnglePostSet.right = right;
03385         cgBoneAnglePostSet.forward = forward;
03386         cgBoneAnglePostSet.modelList = modelList;
03387         cgBoneAnglePostSet.blendTime = blendTime;
03388         cgBoneAnglePostSet.currentTime = currentTime;
03389 
03390         cgBoneAnglePostSet.refreshSet = qtrue;
03391 #endif
03392         //We don't want to go with the delayed approach, we want out bolt points and everything to be updated in realtime.
03393         //We'll just take the reconstructs and live with them.
03394         trap_G2API_SetBoneAngles(ghoul2, modelIndex, boneName, angles, flags, up, right, forward, modelList,
03395                 blendTime, currentTime);
03396 }
03397 
03398 /*
03399 ================
03400 CG_Rag_Trace
03401 
03402 Variant on CG_Trace. Doesn't trace for ents because ragdoll engine trace code has no entity
03403 trace access. Maybe correct this sometime, so bmodel col. at least works with ragdoll.
03404 But I don't want to slow it down..
03405 ================
03406 */
03407 void    CG_Rag_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, 
03408                                          int skipNumber, int mask ) {
03409         trap_CM_BoxTrace ( result, start, end, mins, maxs, 0, mask);
03410         result->entityNum = result->fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
03411 }
03412 
03413 //#define _RAG_BOLT_TESTING
03414 
03415 #ifdef _RAG_BOLT_TESTING
03416 void CG_TempTestFunction(centity_t *cent, vec3_t forcedAngles)
03417 {
03418         mdxaBone_t boltMatrix;
03419         vec3_t tAngles;
03420         vec3_t bOrg;
03421         vec3_t bDir;
03422         vec3_t uOrg;
03423 
03424         VectorSet(tAngles, 0, cent->lerpAngles[YAW], 0);
03425 
03426         trap_G2API_GetBoltMatrix(cent->ghoul2, 1, 0, &boltMatrix, tAngles, cent->lerpOrigin,
03427                 cg.time, cgs.gameModels, cent->modelScale);
03428         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, bOrg);
03429         BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, bDir);
03430 
03431         VectorMA(bOrg, 40, bDir, uOrg);
03432 
03433         CG_TestLine(bOrg, uOrg, 50, 0x0000ff, 1);
03434 
03435         cent->turAngles[YAW] = forcedAngles[YAW];
03436 }
03437 #endif
03438 
03439 //list of valid ragdoll effectors
03440 static const char *cg_effectorStringTable[] =
03441 { //commented out the ones I don't want dragging to affect
03442 //      "thoracic",
03443 //      "rhand",
03444         "lhand",
03445         "rtibia",
03446         "ltibia",
03447         "rtalus",
03448         "ltalus",
03449 //      "rradiusX",
03450         "lradiusX",
03451         "rfemurX",
03452         "lfemurX",
03453 //      "ceyebrow",
03454         NULL //always terminate
03455 };
03456 
03457 //we want to see which way the pelvis is facing to get a relatively oriented base settling frame
03458 //this is to avoid the arms stretching in opposite directions on the body trying to reach the base
03459 //pose if the pelvis is flipped opposite of the base pose or something -rww
03460 static int CG_RagAnimForPositioning(centity_t *cent)
03461 {
03462         int bolt;
03463         vec3_t dir;
03464         mdxaBone_t matrix;
03465 
03466         assert(cent->ghoul2);
03467         bolt = trap_G2API_AddBolt(cent->ghoul2, 0, "pelvis");
03468         assert(bolt > -1);
03469 
03470         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, bolt, &matrix, cent->turAngles, cent->lerpOrigin,
03471                 cg.time, cgs.gameModels, cent->modelScale);
03472         BG_GiveMeVectorFromMatrix(&matrix, NEGATIVE_Z, dir);
03473 
03474         if (dir[2] > 0.0f)
03475         { //facing up
03476                 return BOTH_DEADFLOP2;
03477         }
03478         else
03479         { //facing down
03480                 return BOTH_DEADFLOP1;
03481         }
03482 }
03483 
03484 //rww - cgame interface for the ragdoll stuff.
03485 //Returns qtrue if the entity is now in a ragdoll state, otherwise qfalse.
03486 qboolean CG_RagDoll(centity_t *cent, vec3_t forcedAngles)
03487 {
03488         vec3_t usedOrg;
03489         qboolean inSomething = qfalse;
03490         int ragAnim;//BOTH_DEAD1; //BOTH_DEATH1;
03491 
03492         if (!cg_ragDoll.integer)
03493         {
03494                 return qfalse;
03495         }
03496 
03497         if (cent->localAnimIndex)
03498         { //don't rag non-humanoids
03499                 return qfalse;
03500         }
03501 
03502         VectorCopy(cent->lerpOrigin, usedOrg);
03503 
03504         if (!cent->isRagging)
03505         { //If we're not in a ragdoll state, perform the checks.
03506                 if (cent->currentState.eFlags & EF_RAG)
03507                 { //want to go into it no matter what then
03508                         inSomething = qtrue;
03509                 }
03510                 else if (cent->currentState.groundEntityNum == ENTITYNUM_NONE)
03511                 {
03512                         vec3_t cVel;
03513 
03514                         VectorCopy(cent->currentState.pos.trDelta, cVel);
03515 
03516                         if (VectorNormalize(cVel) > 400)
03517                         { //if he's flying through the air at a good enough speed, switch into ragdoll
03518                                 inSomething = qtrue;
03519                         }
03520                 }
03521 
03522                 if (cent->currentState.eType == ET_BODY)
03523                 { //just rag bodies immediately if their own was ragging on respawn
03524                         if (cent->ownerRagging)
03525                         {
03526                                 cent->isRagging = qtrue;
03527                                 return qfalse;
03528                         }
03529                 }
03530 
03531                 if (cg_ragDoll.integer > 1)
03532                 {
03533                         inSomething = qtrue;
03534                 }
03535 
03536                 if (!inSomething)
03537                 {
03538                         int anim = (cent->currentState.legsAnim);
03539                         int dur = (bgAllAnims[cent->localAnimIndex].anims[anim].numFrames-1) * fabs((float)(bgAllAnims[cent->localAnimIndex].anims[anim].frameLerp));
03540                         int i = 0;
03541                         int boltChecks[5];
03542                         vec3_t boltPoints[5];
03543                         vec3_t trStart, trEnd;
03544                         vec3_t tAng;
03545                         qboolean deathDone = qfalse;
03546                         trace_t tr;
03547                         mdxaBone_t boltMatrix;
03548 
03549                         VectorSet( tAng, cent->turAngles[PITCH], cent->turAngles[YAW], cent->turAngles[ROLL] );
03550 
03551                         if (cent->pe.legs.animationTime > 50 && (cg.time - cent->pe.legs.animationTime) > dur)
03552                         { //Looks like the death anim is done playing
03553                                 deathDone = qtrue;
03554                         }
03555 
03556                         if (deathDone)
03557                         { //only trace from the hands if the death anim is already done.
03558                                 boltChecks[0] = trap_G2API_AddBolt(cent->ghoul2, 0, "rhand");
03559                                 boltChecks[1] = trap_G2API_AddBolt(cent->ghoul2, 0, "lhand");
03560                         }
03561                         else
03562                         { //otherwise start the trace loop at the cranium.
03563                                 i = 2;
03564                         }
03565                         boltChecks[2] = trap_G2API_AddBolt(cent->ghoul2, 0, "cranium");
03566                         //boltChecks[3] = trap_G2API_AddBolt(cent->ghoul2, 0, "rtarsal");
03567                         //boltChecks[4] = trap_G2API_AddBolt(cent->ghoul2, 0, "ltarsal");
03568                         boltChecks[3] = trap_G2API_AddBolt(cent->ghoul2, 0, "rtalus");
03569                         boltChecks[4] = trap_G2API_AddBolt(cent->ghoul2, 0, "ltalus");
03570 
03571                         //This may seem bad, but since we have a bone cache now it should manage to not be too disgustingly slow.
03572                         //Do the head first, because the hands reference it anyway.
03573                         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, boltChecks[2], &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
03574                         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltPoints[2]);
03575 
03576                         while (i < 5)
03577                         {
03578                                 if (i < 2)
03579                                 { //when doing hands, trace to the head instead of origin
03580                                         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, boltChecks[i], &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
03581                                         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltPoints[i]);
03582                                         VectorCopy(boltPoints[i], trStart);
03583                                         VectorCopy(boltPoints[2], trEnd);
03584                                 }
03585                                 else
03586                                 {
03587                                         if (i > 2)
03588                                         { //2 is the head, which already has the bolt point.
03589                                                 trap_G2API_GetBoltMatrix(cent->ghoul2, 0, boltChecks[i], &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
03590                                                 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltPoints[i]);
03591                                         }
03592                                         VectorCopy(boltPoints[i], trStart);
03593                                         VectorCopy(cent->lerpOrigin, trEnd);
03594                                 }
03595 
03596                                 //Now that we have all that sorted out, trace between the two points we desire.
03597                                 CG_Rag_Trace(&tr, trStart, NULL, NULL, trEnd, cent->currentState.number, MASK_SOLID);
03598 
03599                                 if (tr.fraction != 1.0 || tr.startsolid || tr.allsolid)
03600                                 { //Hit something or start in solid, so flag it and break.
03601                                         //This is a slight hack, but if we aren't done with the death anim, we don't really want to
03602                                         //go into ragdoll unless our body has a relatively "flat" pitch.
03603 #if 0
03604                                         vec3_t vSub;
03605 
03606                                         //Check the pitch from the head to the right foot (should be reasonable)
03607                                         VectorSubtract(boltPoints[2], boltPoints[3], vSub);
03608                                         VectorNormalize(vSub);
03609                                         vectoangles(vSub, vSub);
03610 
03611                                         if (deathDone || (vSub[PITCH] < 50 && vSub[PITCH] > -50))
03612                                         {
03613                                                 inSomething = qtrue;
03614                                         }
03615 #else
03616                                         inSomething = qtrue;
03617 #endif
03618                                         break;
03619                                 }
03620 
03621                                 i++;
03622                         }
03623                 }
03624 
03625                 if (inSomething)
03626                 {
03627                         cent->isRagging = qtrue;
03628 #if 0
03629                         VectorClear(cent->lerpOriginOffset);
03630 #endif
03631                 }
03632         }
03633 
03634         if (cent->isRagging)
03635         { //We're in a ragdoll state, so make the call to keep our positions updated and whatnot.
03636                 sharedRagDollParams_t tParms;
03637                 sharedRagDollUpdateParams_t tuParms;
03638 
03639                 ragAnim = CG_RagAnimForPositioning(cent);
03640 
03641                 if (cent->ikStatus)
03642                 { //ik must be reset before ragdoll is started, or you'll get some interesting results.
03643                         trap_G2API_SetBoneIKState(cent->ghoul2, cg.time, NULL, IKS_NONE, NULL);
03644                         cent->ikStatus = qfalse;
03645                 }
03646 
03647                 //these will be used as "base" frames for the ragoll settling.
03648                 tParms.startFrame = bgAllAnims[cent->localAnimIndex].anims[ragAnim].firstFrame;// + bgAllAnims[cent->localAnimIndex].anims[ragAnim].numFrames;
03649                 tParms.endFrame = bgAllAnims[cent->localAnimIndex].anims[ragAnim].firstFrame + bgAllAnims[cent->localAnimIndex].anims[ragAnim].numFrames;
03650 #if 0
03651                 {
03652                         float animSpeed = 0;
03653                         int blendTime = 600;
03654                         int flags = 0;//BONE_ANIM_OVERRIDE_FREEZE;
03655 
03656                         if (bgAllAnims[cent->localAnimIndex].anims[ragAnim].loopFrames != -1)
03657                         {
03658                                 flags = BONE_ANIM_OVERRIDE_LOOP;
03659                         }
03660 
03661                         /*
03662                         if (cg_animBlend.integer)
03663                         {
03664                                 flags |= BONE_ANIM_BLEND;
03665                         }
03666                         */
03667 
03668                         animSpeed = 50.0f / bgAllAnims[cent->localAnimIndex].anims[ragAnim].frameLerp;
03669                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lower_lumbar", tParms.startFrame, tParms.endFrame, flags, animSpeed,cg.time, -1, blendTime);
03670                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "Motion", tParms.startFrame, tParms.endFrame, flags, animSpeed, cg.time, -1, blendTime);
03671                         trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", tParms.startFrame, tParms.endFrame, flags, animSpeed, cg.time, -1, blendTime);
03672                 }
03673 #elif 1 //with my new method of doing things I want it to continue the anim
03674                 {
03675                         float currentFrame;
03676                         int startFrame, endFrame;
03677                         int flags;
03678                         float animSpeed;
03679 
03680                         if (trap_G2API_GetBoneAnim(cent->ghoul2, "model_root", cg.time, &currentFrame, &startFrame, &endFrame, &flags, &animSpeed, cgs.gameModels, 0))
03681                         { //lock the anim on the current frame.
03682                                 int blendTime = 500;
03683                                 animation_t *curAnim = &bgAllAnims[cent->localAnimIndex].anims[cent->currentState.legsAnim];
03684 
03685                                 if (currentFrame >= (curAnim->firstFrame + curAnim->numFrames-1))
03686                                 { //this is sort of silly but it works for now.
03687                                         currentFrame = (curAnim->firstFrame + curAnim->numFrames-2);
03688                                 }
03689 
03690                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, "lower_lumbar", currentFrame, currentFrame+1, flags, animSpeed,cg.time, currentFrame, blendTime);
03691                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, "model_root", currentFrame, currentFrame+1, flags, animSpeed, cg.time, currentFrame, blendTime);
03692                                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, "Motion", currentFrame, currentFrame+1, flags, animSpeed, cg.time, currentFrame, blendTime);
03693                         }
03694                 }
03695 #endif
03696                 CG_G2SetBoneAngles(cent->ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
03697                 CG_G2SetBoneAngles(cent->ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
03698                 CG_G2SetBoneAngles(cent->ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
03699                 CG_G2SetBoneAngles(cent->ghoul2, 0, "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.gameModels, 0, cg.time);
03700 
03701                 VectorCopy(forcedAngles, tParms.angles);
03702                 VectorCopy(usedOrg, tParms.position);
03703                 VectorCopy(cent->modelScale, tParms.scale);
03704                 tParms.me = cent->currentState.number;
03705 
03706                 tParms.collisionType = 1;
03707                 tParms.RagPhase = RP_DEATH_COLLISION;
03708                 tParms.fShotStrength = 4;
03709 
03710                 trap_G2API_SetRagDoll(cent->ghoul2, &tParms);
03711 
03712                 VectorCopy(forcedAngles, tuParms.angles);
03713                 VectorCopy(usedOrg, tuParms.position);
03714                 VectorCopy(cent->modelScale, tuParms.scale);
03715                 tuParms.me = cent->currentState.number;
03716                 tuParms.settleFrame = tParms.endFrame-1;
03717 
03718                 if (cent->currentState.groundEntityNum != ENTITYNUM_NONE)
03719                 {
03720                         VectorClear(tuParms.velocity);
03721                 }
03722                 else
03723                 {
03724                         VectorScale(cent->currentState.pos.trDelta, 2.0f, tuParms.velocity);
03725                 }
03726 
03727                 trap_G2API_AnimateG2Models(cent->ghoul2, cg.time, &tuParms);
03728 
03729                 //So if we try to get a bolt point it's still correct
03730                 cent->turAngles[YAW] = 
03731                 cent->lerpAngles[YAW] = 
03732                 cent->pe.torso.yawAngle = 
03733                 cent->pe.legs.yawAngle = forcedAngles[YAW];
03734 
03735                 if (cent->currentState.ragAttach &&
03736                         (cent->currentState.eType != ET_NPC || cent->currentState.NPC_class != CLASS_VEHICLE))
03737                 {
03738                         centity_t *grabEnt;
03739 
03740                         if (cent->currentState.ragAttach == ENTITYNUM_NONE)
03741                         { //switch cl 0 and entitynum_none, so we can operate on the "if non-0" concept
03742                                 grabEnt = &cg_entities[0];
03743                         }
03744                         else
03745                         {
03746                                 grabEnt = &cg_entities[cent->currentState.ragAttach];
03747                         }
03748 
03749                         if (grabEnt->ghoul2)
03750                         {
03751                                 mdxaBone_t matrix;
03752                                 vec3_t bOrg;
03753                                 vec3_t thisHand;
03754                                 vec3_t hands;
03755                                 vec3_t pcjMin, pcjMax;
03756                                 vec3_t pDif;
03757                                 vec3_t thorPoint;
03758                                 float difLen;
03759                                 int thorBolt;
03760 
03761                                 //Get the person who is holding our hand's hand location
03762                                 trap_G2API_GetBoltMatrix(grabEnt->ghoul2, 0, 0, &matrix, grabEnt->turAngles, grabEnt->lerpOrigin,
03763                                         cg.time, cgs.gameModels, grabEnt->modelScale);
03764                                 BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, bOrg);
03765 
03766                                 //Get our hand's location
03767                                 trap_G2API_GetBoltMatrix(cent->ghoul2, 0, 0, &matrix, cent->turAngles, cent->lerpOrigin,
03768                                         cg.time, cgs.gameModels, cent->modelScale);
03769                                 BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, thisHand);
03770 
03771                                 //Get the position of the thoracic bone for hinting its velocity later on
03772                                 thorBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "thoracic");
03773                                 trap_G2API_GetBoltMatrix(cent->ghoul2, 0, thorBolt, &matrix, cent->turAngles, cent->lerpOrigin,
03774                                         cg.time, cgs.gameModels, cent->modelScale);
03775                                 BG_GiveMeVectorFromMatrix(&matrix, ORIGIN, thorPoint);
03776 
03777                                 VectorSubtract(bOrg, thisHand, hands);
03778 
03779                                 if (VectorLength(hands) < 3.0f)
03780                                 {
03781                                         trap_G2API_RagForceSolve(cent->ghoul2, qfalse);
03782                                 }
03783                                 else
03784                                 {
03785                                         trap_G2API_RagForceSolve(cent->ghoul2, qtrue);
03786                                 }
03787 
03788                                 //got the hand pos of him, now we want to make our hand go to it
03789                                 trap_G2API_RagEffectorGoal(cent->ghoul2, "rhand", bOrg);
03790                                 trap_G2API_RagEffectorGoal(cent->ghoul2, "rradius", bOrg);
03791                                 trap_G2API_RagEffectorGoal(cent->ghoul2, "rradiusX", bOrg);
03792                                 trap_G2API_RagEffectorGoal(cent->ghoul2, "rhumerusX", bOrg);
03793                                 trap_G2API_RagEffectorGoal(cent->ghoul2, "rhumerus", bOrg);
03794 
03795                                 //Make these two solve quickly so we can update decently
03796                                 trap_G2API_RagPCJGradientSpeed(cent->ghoul2, "rhumerus", 1.5f);
03797                                 trap_G2API_RagPCJGradientSpeed(cent->ghoul2, "rradius", 1.5f);
03798 
03799                                 //Break the constraints on them I suppose
03800                                 VectorSet(pcjMin, -999, -999, -999);
03801                                 VectorSet(pcjMax, 999, 999, 999);
03802                                 trap_G2API_RagPCJConstraint(cent->ghoul2, "rhumerus", pcjMin, pcjMax);
03803                                 trap_G2API_RagPCJConstraint(cent->ghoul2, "rradius", pcjMin, pcjMax);
03804 
03805                                 cent->overridingBones = cg.time + 2000;
03806 
03807                                 //hit the thoracic velocity to the hand point
03808                                 VectorSubtract(bOrg, thorPoint, hands);
03809                                 VectorNormalize(hands);
03810                                 VectorScale(hands, 2048.0f, hands);
03811                                 trap_G2API_RagEffectorKick(cent->ghoul2, "thoracic", hands);
03812                                 trap_G2API_RagEffectorKick(cent->ghoul2, "ceyebrow", hands);
03813 
03814                                 VectorSubtract(cent->ragLastOrigin, cent->lerpOrigin, pDif);
03815                                 VectorCopy(cent->lerpOrigin, cent->ragLastOrigin);
03816 
03817                                 if (cent->ragLastOriginTime >= cg.time && cent->currentState.groundEntityNum != ENTITYNUM_NONE)
03818                                 { //make sure it's reasonably updated
03819                                         difLen = VectorLength(pDif);
03820                                         if (difLen > 0.0f)
03821                                         { //if we're being dragged, then kick all the bones around a bit
03822                                                 vec3_t dVel;
03823                                                 vec3_t rVel;
03824                                                 int i = 0;
03825 
03826                                                 if (difLen < 12.0f)
03827                                                 {
03828                                                         VectorScale(pDif, 12.0f/difLen, pDif);
03829                                                         difLen = 12.0f;
03830                                                 }
03831 
03832                                                 while (cg_effectorStringTable[i])
03833                                                 {
03834                                                         VectorCopy(pDif, dVel);
03835                                                         dVel[2] = 0;
03836 
03837                                                         //Factor in a random velocity
03838                                                         VectorSet(rVel, flrand(-0.1f, 0.1f), flrand(-0.1f, 0.1f), flrand(0.1f, 0.5));
03839                                                         VectorScale(rVel, 8.0f, rVel);
03840 
03841                                                         VectorAdd(dVel, rVel, dVel);
03842                                                         VectorScale(dVel, 10.0f, dVel);
03843 
03844                                                         trap_G2API_RagEffectorKick(cent->ghoul2, cg_effectorStringTable[i], dVel);
03845 
03846 #if 0
03847                                                         {
03848                                                                 mdxaBone_t bm;
03849                                                                 vec3_t borg;
03850                                                                 vec3_t vorg;
03851                                                                 int b = trap_G2API_AddBolt(cent->ghoul2, 0, cg_effectorStringTable[i]);
03852 
03853                                                                 trap_G2API_GetBoltMatrix(cent->ghoul2, 0, b, &bm, cent->turAngles, cent->lerpOrigin, cg.time,
03854                                                                         cgs.gameModels, cent->modelScale);
03855                                                                 BG_GiveMeVectorFromMatrix(&bm, ORIGIN, borg);
03856 
03857                                                                 VectorMA(borg, 1.0f, dVel, vorg);
03858 
03859                                                                 CG_TestLine(borg, vorg, 50, 0x0000ff, 1);
03860                                                         }
03861 #endif
03862 
03863                                                         i++;
03864                                                 }
03865                                         }
03866                                 }
03867                                 cent->ragLastOriginTime = cg.time + 1000;
03868                         }
03869                 }
03870                 else if (cent->overridingBones)
03871                 { //reset things to their normal rag state
03872                         vec3_t pcjMin, pcjMax;
03873                         vec3_t dVel;
03874 
03875                         //got the hand pos of him, now we want to make our hand go to it
03876                         trap_G2API_RagEffectorGoal(cent->ghoul2, "rhand", NULL);
03877                         trap_G2API_RagEffectorGoal(cent->ghoul2, "rradius", NULL);
03878                         trap_G2API_RagEffectorGoal(cent->ghoul2, "rradiusX", NULL);
03879                         trap_G2API_RagEffectorGoal(cent->ghoul2, "rhumerusX", NULL);
03880                         trap_G2API_RagEffectorGoal(cent->ghoul2, "rhumerus", NULL);
03881 
03882                         VectorSet(dVel, 0.0f, 0.0f, -64.0f);
03883                         trap_G2API_RagEffectorKick(cent->ghoul2, "rhand", dVel);
03884 
03885                         trap_G2API_RagPCJGradientSpeed(cent->ghoul2, "rhumerus", 0.0f);
03886                         trap_G2API_RagPCJGradientSpeed(cent->ghoul2, "rradius", 0.0f);
03887 
03888                         VectorSet(pcjMin,-100.0f,-40.0f,-15.0f);
03889                         VectorSet(pcjMax,-15.0f,80.0f,15.0f);
03890                         trap_G2API_RagPCJConstraint(cent->ghoul2, "rhumerus", pcjMin, pcjMax);
03891 
03892                         VectorSet(pcjMin,-25.0f,-20.0f,-20.0f);
03893                         VectorSet(pcjMax,90.0f,20.0f,-20.0f);
03894                         trap_G2API_RagPCJConstraint(cent->ghoul2, "rradius", pcjMin, pcjMax);
03895 
03896                         if (cent->overridingBones < cg.time)
03897                         {
03898                                 trap_G2API_RagForceSolve(cent->ghoul2, qfalse);
03899                                 cent->overridingBones = 0;
03900                         }
03901                         else
03902                         {
03903                                 trap_G2API_RagForceSolve(cent->ghoul2, qtrue);
03904                         }
03905                 }
03906 
03907                 return qtrue;
03908         }
03909 
03910         return qfalse;
03911 }
03912 
03913 //set the bone angles of this client entity based on data from the server -rww
03914 void CG_G2ServerBoneAngles(centity_t *cent)
03915 {
03916         int i = 0;
03917         int bone = cent->currentState.boneIndex1;
03918         int flags, up, right, forward;
03919         vec3_t boneAngles;
03920 
03921         VectorCopy(cent->currentState.boneAngles1, boneAngles);
03922 
03923         while (i < 4)
03924         { //cycle through the 4 bone index values on the entstate
03925                 if (bone)
03926                 { //if it's non-0 then it could have something in it.
03927                         const char *boneName = CG_ConfigString(CS_G2BONES+bone);
03928 
03929                         if (boneName && boneName[0])
03930                         { //got the bone, now set the angles from the corresponding entitystate boneangles value.
03931                                 flags = BONE_ANGLES_POSTMULT;
03932 
03933                                 //get the orientation out of our bit field
03934                                 forward = (cent->currentState.boneOrient)&7; //3 bits from bit 0
03935                                 right = (cent->currentState.boneOrient>>3)&7; //3 bits from bit 3
03936                                 up = (cent->currentState.boneOrient>>6)&7; //3 bits from bit 6
03937 
03938                                 trap_G2API_SetBoneAngles(cent->ghoul2, 0, boneName, boneAngles, flags, up, right, forward, cgs.gameModels, 100, cg.time);
03939                         }
03940                 }
03941 
03942                 switch (i)
03943                 {
03944                 case 0:
03945                         bone = cent->currentState.boneIndex2;
03946                         VectorCopy(cent->currentState.boneAngles2, boneAngles);
03947                         break;
03948                 case 1:
03949                         bone = cent->currentState.boneIndex3;
03950                         VectorCopy(cent->currentState.boneAngles3, boneAngles);
03951                         break;
03952                 case 2:
03953                         bone = cent->currentState.boneIndex4;
03954                         VectorCopy(cent->currentState.boneAngles4, boneAngles);
03955                         break;
03956                 default:
03957                         break;
03958                 }
03959 
03960                 i++;
03961         }
03962 }
03963 
03964 /*
03965 -------------------------
03966 CG_G2SetHeadBlink
03967 -------------------------
03968 */
03969 static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart )
03970 {
03971         vec3_t  desiredAngles;
03972         int blendTime = 80;
03973         qboolean bWink = qfalse;
03974         const int hReye = trap_G2API_AddBolt( cent->ghoul2, 0, "reye" );
03975         const int hLeye = trap_G2API_AddBolt( cent->ghoul2, 0, "leye" );
03976 
03977         if (hLeye == -1)
03978         {
03979                 return;
03980         }
03981 
03982         VectorClear(desiredAngles);
03983 
03984         if (bStart)
03985         {
03986                 desiredAngles[YAW] = -50;
03987                 if ( random() > 0.95f )
03988                 {
03989                         bWink = qtrue;
03990                         blendTime /=3;
03991                 }
03992         }
03993         trap_G2API_SetBoneAngles( cent->ghoul2, 0, "leye", desiredAngles,
03994                 BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); 
03995 
03996         if (hReye == -1)
03997         {
03998                 return;
03999         }
04000         
04001         if (!bWink)
04002         {
04003                 trap_G2API_SetBoneAngles( cent->ghoul2, 0, "reye", desiredAngles,
04004                         BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); 
04005         }
04006 }
04007 
04008 /*
04009 -------------------------
04010 CG_G2SetHeadAnims
04011 -------------------------
04012 */
04013 static void CG_G2SetHeadAnim( centity_t *cent, int anim )
04014 {
04015         const int blendTime = 50;
04016         const animation_t *animations = bgAllAnims[cent->localAnimIndex].anims;
04017         int     animFlags = BONE_ANIM_OVERRIDE ;//| BONE_ANIM_BLEND;
04018         // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps).
04019 //      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;
04020         const float             timeScaleMod = (cg_timescale.value)?(1.0/cg_timescale.value):1.0;
04021         float animSpeed = 50.0f / animations[anim].frameLerp * timeScaleMod;
04022         int     firstFrame;
04023         int     lastFrame;
04024 
04025         if (animations[anim].numFrames <= 0)
04026         {
04027                 return;
04028         }
04029         if (anim == FACE_DEAD)
04030         {
04031                 animFlags |= BONE_ANIM_OVERRIDE_FREEZE;
04032         }
04033         // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps).
04034         if ( animSpeed < 0 )
04035         {//play anim backwards
04036                 
04037                 lastFrame = animations[anim].firstFrame -1;
04038                 firstFrame = (animations[anim].numFrames -1) + animations[anim].firstFrame ;
04039         }
04040         else
04041         {
04042                 firstFrame = animations[anim].firstFrame;
04043                 lastFrame = (animations[anim].numFrames) + animations[anim].firstFrame;
04044         }
04045 
04046         // first decide if we are doing an animation on the head already
04047 //      int startFrame, endFrame;
04048 //      const qboolean animatingHead =  gi.G2API_GetAnimRangeIndex(&gent->ghoul2[gent->playerModel], cent->gent->faceBone, &startFrame, &endFrame);
04049         
04050 //      if (!animatingHead || ( animations[anim].firstFrame != startFrame ) )// only set the anim if we aren't going to do the same animation again
04051         {
04052         //      gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], cent->gent->faceBone,
04053         //              firstFrame, lastFrame, animFlags, animSpeed, cg.time, -1, blendTime);
04054                 trap_G2API_SetBoneAnim(cent->ghoul2, 0, "face", firstFrame, lastFrame, animFlags, animSpeed,
04055                         cg.time, -1, blendTime);
04056         }
04057 }
04058 
04059 qboolean CG_G2PlayerHeadAnims( centity_t *cent )
04060 {
04061         clientInfo_t *ci = NULL;
04062         int anim = -1;
04063         int voiceVolume = 0;
04064 
04065         if(cent->localAnimIndex > 1)
04066         { //only do this for humanoids
04067                 return qfalse;
04068         }
04069         
04070         if (cent->noFace)
04071         {       // i don't have a face
04072                 return qfalse;
04073         }
04074 
04075         if (cent->currentState.number < MAX_CLIENTS)
04076         {
04077                 ci = &cgs.clientinfo[cent->currentState.number];
04078         }
04079         else
04080         {
04081                 ci = cent->npcClient;
04082         }
04083 
04084         if (!ci)
04085         {
04086                 return qfalse;
04087         }
04088 
04089         if ( cent->currentState.eFlags & EF_DEAD )
04090         {//Dead people close their eyes and don't make faces!
04091                 anim = FACE_DEAD;
04092                 ci->facial_blink = -1;
04093         }
04094         else 
04095         {
04096                 if (!ci->facial_blink)
04097                 {       // set the timers
04098                         ci->facial_blink = cg.time + flrand(4000.0, 8000.0);
04099                         ci->facial_frown = cg.time + flrand(6000.0, 10000.0);
04100                         ci->facial_aux = cg.time + flrand(6000.0, 10000.0);
04101                 }
04102                 
04103                 //are we blinking?
04104                 if (ci->facial_blink < 0)
04105                 {       // yes, check if we are we done blinking ?
04106                         if (-(ci->facial_blink) < cg.time)
04107                         {       // yes, so reset blink timer
04108                                 ci->facial_blink = cg.time + flrand(4000.0, 8000.0);
04109                                 CG_G2SetHeadBlink( cent, qfalse );      //stop the blink
04110                         }
04111                 }
04112                 else // no we aren't blinking 
04113                 {       
04114                         if (ci->facial_blink < cg.time)// but should we start ?
04115                         {
04116                                 CG_G2SetHeadBlink( cent, qtrue );
04117                                 if (ci->facial_blink == 1)
04118                                 {//requested to stay shut by SET_FACEEYESCLOSED
04119                                         ci->facial_blink = -(cg.time + 99999999.0f);// set blink timer
04120                                 }
04121                                 else
04122                                 {
04123                                         ci->facial_blink = -(cg.time + 300.0f);// set blink timer
04124                                 }
04125                         } 
04126                 }
04127                 
04128                 voiceVolume = trap_S_GetVoiceVolume(cent->currentState.number);
04129 
04130                 if (voiceVolume > 0)    // if we aren't talking, then it will be 0, -1 for talking but paused
04131                 {
04132                         anim = FACE_TALK1 + voiceVolume -1;
04133                 }
04134                 else if (voiceVolume == 0)      //don't do aux if in a slient part of speech
04135                 {//not talking
04136                         if (ci->facial_aux < 0) // are we auxing ?
04137                         {       //yes
04138                                 if (-(ci->facial_aux) < cg.time)// are we done auxing ?
04139                                 {       // yes, reset aux timer
04140                                         ci->facial_aux = cg.time + flrand(7000.0, 10000.0);
04141                                 }
04142                                 else
04143                                 {       // not yet, so choose aux
04144                                         anim = FACE_ALERT;
04145                                 }
04146                         }
04147                         else // no we aren't auxing 
04148                         {       // but should we start ?
04149                                 if (ci->facial_aux < cg.time)
04150                                 {//yes
04151                                         anim = FACE_ALERT;
04152                                         // set aux timer
04153                                         ci->facial_aux = -(cg.time + 2000.0);
04154                                 } 
04155                         }       
04156                         
04157                         if (anim != -1) //we we are auxing, see if we should override with a frown
04158                         {       
04159                                 if (ci->facial_frown < 0)// are we frowning ?
04160                                 {       // yes, 
04161                                         if (-(ci->facial_frown) < cg.time)//are we done frowning ?
04162                                         {       // yes, reset frown timer
04163                                                 ci->facial_frown = cg.time + flrand(7000.0, 10000.0);
04164                                         }
04165                                         else 
04166                                         {       // not yet, so choose frown
04167                                                 anim = FACE_FROWN;
04168                                         }
04169                                 }
04170                                 else// no we aren't frowning 
04171                                 {       // but should we start ?
04172                                         if (ci->facial_frown < cg.time)
04173                                         {
04174                                                 anim = FACE_FROWN;
04175                                                 // set frown timer
04176                                                 ci->facial_frown = -(cg.time + 2000.0);
04177                                         }
04178                                 }
04179                         }
04180 
04181                 }//talking
04182         }//dead
04183         if (anim != -1)
04184         {
04185                 CG_G2SetHeadAnim( cent, anim );
04186                 return qtrue;
04187         }
04188         return qfalse;
04189 }
04190 
04191 
04192 static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t legsAngles)
04193 {
04194         clientInfo_t *ci;
04195 
04196         //rww - now do ragdoll stuff
04197         if ((cent->currentState.eFlags & EF_DEAD) || (cent->currentState.eFlags & EF_RAG))
04198         {
04199                 vec3_t forcedAngles;
04200 
04201                 VectorClear(forcedAngles);
04202                 forcedAngles[YAW] = cent->lerpAngles[YAW];
04203 
04204                 if (CG_RagDoll(cent, forcedAngles))
04205                 { //if we managed to go into the rag state, give our ent axis the forced angles and return.
04206                         AnglesToAxis( forcedAngles, legs );
04207                         VectorCopy(forcedAngles, legsAngles);
04208                         return;
04209                 }
04210         }
04211         else if (cent->isRagging)
04212         {
04213                 cent->isRagging = qfalse;
04214                 trap_G2API_SetRagDoll(cent->ghoul2, NULL); //calling with null parms resets to no ragdoll.
04215         }
04216 
04217         if (cent->currentState.eType == ET_NPC)
04218         {
04219                 ci = cent->npcClient;
04220                 assert(ci);
04221         }
04222         else
04223         {
04224                 ci = &cgs.clientinfo[cent->currentState.number];
04225         }
04226 
04227         //rww - Quite possibly the most arguments for a function ever.
04228         if (cent->localAnimIndex <= 1)
04229         { //don't do these things on non-humanoids
04230                 vec3_t lookAngles;
04231                 entityState_t *emplaced = NULL;
04232 
04233                 if (cent->currentState.hasLookTarget)
04234                 {
04235                         VectorSubtract(cg_entities[cent->currentState.lookTarget].lerpOrigin, cent->lerpOrigin, lookAngles);
04236                         vectoangles(lookAngles, lookAngles);
04237                         ci->lookTime = cg.time + 1000;
04238                 }
04239                 else
04240                 {
04241                         VectorCopy(cent->lerpAngles, lookAngles);
04242                 }
04243                 lookAngles[PITCH] = 0;
04244 
04245                 if (cent->currentState.otherEntityNum2)
04246                 {
04247                         emplaced = &cg_entities[cent->currentState.otherEntityNum2].currentState;
04248                 }
04249 
04250                 BG_G2PlayerAngles(cent->ghoul2, ci->bolt_motion, &cent->currentState, cg.time,
04251                         cent->lerpOrigin, cent->lerpAngles, legs, legsAngles, &cent->pe.torso.yawing, &cent->pe.torso.pitching,
04252                         &cent->pe.legs.yawing, &cent->pe.torso.yawAngle, &cent->pe.torso.pitchAngle, &cent->pe.legs.yawAngle,
04253                         cg.frametime, cent->turAngles, cent->modelScale, ci->legsAnim, ci->torsoAnim, &ci->corrTime,
04254                         lookAngles, ci->lastHeadAngles, ci->lookTime, emplaced, &ci->superSmoothTime);
04255 
04256                 if (cent->currentState.heldByClient && cent->currentState.heldByClient <= MAX_CLIENTS)
04257                 { //then put our arm in this client's hand
04258                         //is index+1 because index 0 is valid.
04259                         int heldByIndex = cent->currentState.heldByClient-1;
04260                         centity_t *other = &cg_entities[heldByIndex];
04261 
04262                         if (other && other->ghoul2 && ci->bolt_lhand)
04263                         {
04264                                 mdxaBone_t boltMatrix;
04265                                 vec3_t boltOrg;
04266 
04267                                 trap_G2API_GetBoltMatrix(other->ghoul2, 0, ci->bolt_lhand, &boltMatrix, other->turAngles, other->lerpOrigin, cg.time, cgs.gameModels, other->modelScale);
04268                                 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltOrg);
04269 
04270                                 BG_IK_MoveArm(cent->ghoul2, ci->bolt_lhand, cg.time, &cent->currentState,
04271                                         cent->currentState.torsoAnim/*BOTH_DEAD1*/, boltOrg, &cent->ikStatus, cent->lerpOrigin, cent->lerpAngles, cent->modelScale, 500, qfalse);
04272                         }
04273                 }
04274                 else if (cent->ikStatus)
04275                 { //make sure we aren't IKing if we don't have anyone to hold onto us.
04276                         BG_IK_MoveArm(cent->ghoul2, ci->bolt_lhand, cg.time, &cent->currentState,
04277                                 cent->currentState.torsoAnim/*BOTH_DEAD1*/, vec3_origin, &cent->ikStatus, cent->lerpOrigin, cent->lerpAngles, cent->modelScale, 500, qtrue);
04278                 }
04279         }
04280         else if ( cent->m_pVehicle && cent->m_pVehicle->m_pVehicleInfo->type == VH_WALKER )
04281         {
04282                 vec3_t lookAngles;
04283 
04284                 VectorCopy(cent->lerpAngles, legsAngles);
04285                 legsAngles[PITCH] = 0;
04286                 AnglesToAxis( legsAngles, legs );
04287 
04288                 VectorCopy(cent->lerpAngles, lookAngles);
04289                 lookAngles[YAW] = lookAngles[ROLL] = 0;
04290 
04291                 BG_G2ATSTAngles( cent->ghoul2, cg.time, lookAngles );
04292         }
04293         else
04294         {
04295                 if (cent->currentState.eType == ET_NPC &&
04296                         cent->currentState.NPC_class == CLASS_VEHICLE &&
04297                         cent->m_pVehicle &&
04298                         cent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
04299                 { //fighters actually want to take pitch and roll into account for the axial angles
04300                         VectorCopy(cent->lerpAngles, legsAngles);
04301                         AnglesToAxis( legsAngles, legs );
04302                 }
04303                 else if (cent->currentState.eType == ET_NPC &&
04304                         cent->currentState.m_iVehicleNum &&
04305                         cent->currentState.NPC_class != CLASS_VEHICLE )
04306                 { //an NPC bolted to a vehicle should use the full angles
04307                         VectorCopy(cent->lerpAngles, legsAngles);
04308                         AnglesToAxis( legsAngles, legs );
04309                 }
04310                 else
04311                 {
04312                         vec3_t nhAngles;
04313 
04314                         if (cent->currentState.eType == ET_NPC &&
04315                                 cent->currentState.NPC_class == CLASS_VEHICLE &&
04316                                 cent->m_pVehicle &&
04317                                 cent->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER)
04318                         { //yeah, a hack, sorry.
04319                                 VectorSet(nhAngles, 0, cent->lerpAngles[YAW], cent->lerpAngles[ROLL]);
04320                         }
04321                         else
04322                         {
04323                                 VectorSet(nhAngles, 0, cent->lerpAngles[YAW], 0);
04324                         }
04325                         AnglesToAxis( nhAngles, legs );
04326                 }
04327         }
04328 
04329         //See if we have any bone angles sent from the server
04330         CG_G2ServerBoneAngles(cent);
04331 }
04332 //==========================================================================
04333 
04334 /*
04335 ===============
04336 CG_TrailItem
04337 ===============
04338 */
04339 #if 0
04340 static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) {
04341         refEntity_t             ent;
04342         vec3_t                  angles;
04343         vec3_t                  axis[3];
04344 
04345         VectorCopy( cent->lerpAngles, angles );
04346         angles[PITCH] = 0;
04347         angles[ROLL] = 0;
04348         AnglesToAxis( angles, axis );
04349 
04350         memset( &ent, 0, sizeof( ent ) );
04351         VectorMA( cent->lerpOrigin, -16, axis[0], ent.origin );
04352         ent.origin[2] += 16;
04353         angles[YAW] += 90;
04354         AnglesToAxis( angles, ent.axis );
04355 
04356         ent.hModel = hModel;
04357         trap_R_AddRefEntityToScene( &ent );
04358 }
04359 #endif
04360 
04361 
04362 /*
04363 ===============
04364 CG_PlayerFlag
04365 ===============
04366 */
04367 static void CG_PlayerFlag( centity_t *cent, qhandle_t hModel ) {
04368         refEntity_t             ent;
04369         vec3_t                  angles;
04370         vec3_t                  axis[3];
04371         vec3_t                  boltOrg, tAng, getAng, right;
04372         mdxaBone_t              boltMatrix;
04373         clientInfo_t    *ci;
04374 
04375         if (cent->currentState.number == cg.snap->ps.clientNum &&
04376                 !cg.renderingThirdPerson)
04377         {
04378                 return;
04379         }
04380 
04381         if (!cent->ghoul2)
04382         {
04383                 return;
04384         }
04385 
04386         if (cent->currentState.eType == ET_NPC)
04387         {
04388                 ci = cent->npcClient;
04389                 assert(ci);
04390         }
04391         else
04392         {
04393                 ci = &cgs.clientinfo[cent->currentState.number];
04394         }
04395 
04396         VectorSet( tAng, cent->turAngles[PITCH], cent->turAngles[YAW], cent->turAngles[ROLL] );
04397 
04398         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_llumbar, &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
04399         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltOrg);
04400 
04401         BG_GiveMeVectorFromMatrix(&boltMatrix, POSITIVE_X, tAng);
04402         vectoangles(tAng, tAng);
04403 
04404         VectorCopy(cent->lerpAngles, angles);
04405 
04406         boltOrg[2] -= 12;
04407         VectorSet(getAng, 0, cent->lerpAngles[1], 0);
04408         AngleVectors(getAng, 0, right, 0);
04409         boltOrg[0] += right[0]*8;
04410         boltOrg[1] += right[1]*8;
04411         boltOrg[2] += right[2]*8;
04412 
04413         angles[PITCH] = -cent->lerpAngles[PITCH]/2-30;
04414         angles[YAW] = tAng[YAW]+270;
04415 
04416         AnglesToAxis(angles, axis);
04417 
04418         memset( &ent, 0, sizeof( ent ) );
04419         VectorMA( boltOrg, 24, axis[0], ent.origin );
04420 
04421         angles[ROLL] += 20;
04422         AnglesToAxis( angles, ent.axis );
04423 
04424         ent.hModel = hModel;
04425 
04426         ent.modelScale[0] = 0.5;
04427         ent.modelScale[1] = 0.5;
04428         ent.modelScale[2] = 0.5;
04429         ScaleModelAxis(&ent);
04430 
04431         /*
04432         if (cent->currentState.number == cg.snap->ps.clientNum)
04433         { //If we're the current client (in third person), render the flag on our back transparently
04434                 ent.renderfx |= RF_FORCE_ENT_ALPHA;
04435                 ent.shaderRGBA[3] = 100;
04436         }
04437         */
04438         //FIXME: Not doing this at the moment because sorting totally messes up
04439 
04440         trap_R_AddRefEntityToScene( &ent );
04441 }
04442 
04443 
04444 /*
04445 ===============
04446 CG_PlayerPowerups
04447 ===============
04448 */
04449 static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) {
04450         int             powerups;
04451         clientInfo_t    *ci;
04452 
04453         powerups = cent->currentState.powerups;
04454         if ( !powerups ) {
04455                 return;
04456         }
04457 
04458         // quad gives a dlight
04459         if ( powerups & ( 1 << PW_QUAD ) ) {
04460                 trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 );
04461         }
04462 
04463         if (cent->currentState.eType == ET_NPC)
04464         {
04465                 ci = cent->npcClient;
04466                 assert(ci);
04467         }
04468         else
04469         {
04470                 ci = &cgs.clientinfo[ cent->currentState.clientNum ];
04471         }
04472         // redflag
04473         if ( powerups & ( 1 << PW_REDFLAG ) ) {
04474                 CG_PlayerFlag( cent, cgs.media.redFlagModel );
04475                 trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 0.2f, 0.2f );
04476         }
04477 
04478         // blueflag
04479         if ( powerups & ( 1 << PW_BLUEFLAG ) ) {
04480                 CG_PlayerFlag( cent, cgs.media.blueFlagModel );
04481                 trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1.0 );
04482         }
04483 
04484         // neutralflag
04485         if ( powerups & ( 1 << PW_NEUTRALFLAG ) ) {
04486                 trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 1.0, 1.0 );
04487         }
04488 
04489         // haste leaves smoke trails
04490         /*
04491         if ( powerups & ( 1 << PW_HASTE ) ) {
04492                 CG_HasteTrail( cent );
04493         }
04494         */
04495 }
04496 
04497 
04498 /*
04499 ===============
04500 CG_PlayerFloatSprite
04501 
04502 Float a sprite over the player's head
04503 ===============
04504 */
04505 static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) {
04506         int                             rf;
04507         refEntity_t             ent;
04508 
04509         if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) {
04510                 rf = RF_THIRD_PERSON;           // only show in mirrors
04511         } else {
04512                 rf = 0;
04513         }
04514 
04515         memset( &ent, 0, sizeof( ent ) );
04516         VectorCopy( cent->lerpOrigin, ent.origin );
04517         ent.origin[2] += 48;
04518         ent.reType = RT_SPRITE;
04519         ent.customShader = shader;
04520         ent.radius = 10;
04521         ent.renderfx = rf;
04522         ent.shaderRGBA[0] = 255;
04523         ent.shaderRGBA[1] = 255;
04524         ent.shaderRGBA[2] = 255;
04525         ent.shaderRGBA[3] = 255;
04526         trap_R_AddRefEntityToScene( &ent );
04527 }
04528 
04529 
04530 
04531 /*
04532 ===============
04533 CG_PlayerFloatSprite
04534 
04535 Same as above but allows custom RGBA values
04536 ===============
04537 */
04538 #if 0
04539 static void CG_PlayerFloatSpriteRGBA( centity_t *cent, qhandle_t shader, vec4_t rgba ) {
04540         int                             rf;
04541         refEntity_t             ent;
04542 
04543         if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) {
04544                 rf = RF_THIRD_PERSON;           // only show in mirrors
04545         } else {
04546                 rf = 0;
04547         }
04548 
04549         memset( &ent, 0, sizeof( ent ) );
04550         VectorCopy( cent->lerpOrigin, ent.origin );
04551         ent.origin[2] += 48;
04552         ent.reType = RT_SPRITE;
04553         ent.customShader = shader;
04554         ent.radius = 10;
04555         ent.renderfx = rf;
04556         ent.shaderRGBA[0] = rgba[0];
04557         ent.shaderRGBA[1] = rgba[1];
04558         ent.shaderRGBA[2] = rgba[2];
04559         ent.shaderRGBA[3] = rgba[3];
04560         trap_R_AddRefEntityToScene( &ent );
04561 }
04562 #endif
04563 
04564 
04565 /*
04566 ===============
04567 CG_PlayerSprites
04568 
04569 Float sprites over the player's head
04570 ===============
04571 */
04572 static void CG_PlayerSprites( centity_t *cent ) {
04573 //      int             team;
04574 
04575         if (cg.snap &&
04576                 CG_IsMindTricked(cent->currentState.trickedentindex,
04577                 cent->currentState.trickedentindex2,
04578                 cent->currentState.trickedentindex3,
04579                 cent->currentState.trickedentindex4,
04580                 cg.snap->ps.clientNum))
04581         {
04582                 return; //this entity is mind-tricking the current client, so don't render it
04583         }
04584 
04585         if ( cent->currentState.eFlags & EF_CONNECTION ) {
04586                 CG_PlayerFloatSprite( cent, cgs.media.connectionShader );
04587                 return;
04588         }
04589 
04590         if (cent->vChatTime > cg.time)
04591         {
04592                 CG_PlayerFloatSprite( cent, cgs.media.vchatShader );
04593         }
04594         else if ( cent->currentState.eType != ET_NPC && //don't draw talk balloons on NPCs
04595                 (cent->currentState.eFlags & EF_TALK) )
04596         {
04597                 CG_PlayerFloatSprite( cent, cgs.media.balloonShader );
04598                 return;
04599         }
04600 }
04601 
04602 /*
04603 ===============
04604 CG_PlayerShadow
04605 
04606 Returns the Z component of the surface being shadowed
04607 
04608   should it return a full plane instead of a Z?
04609 ===============
04610 */
04611 #define SHADOW_DISTANCE         128
04612 static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) {
04613         vec3_t          end, mins = {-15, -15, 0}, maxs = {15, 15, 2};
04614         trace_t         trace;
04615         float           alpha;
04616         float           radius = 24.0f;
04617 
04618         *shadowPlane = 0;
04619 
04620         if ( cg_shadows.integer == 0 ) {
04621                 return qfalse;
04622         }
04623 
04624         // no shadows when cloaked
04625         if ( cent->currentState.powerups & ( 1 << PW_CLOAKED )) 
04626         {
04627                 return qfalse;
04628         }
04629 
04630         if (cent->currentState.eFlags & EF_DEAD)
04631         {
04632                 return qfalse;
04633         }
04634 
04635         if (CG_IsMindTricked(cent->currentState.trickedentindex,
04636                 cent->currentState.trickedentindex2,
04637                 cent->currentState.trickedentindex3,
04638                 cent->currentState.trickedentindex4,
04639                 cg.snap->ps.clientNum))
04640         {
04641                 return qfalse; //this entity is mind-tricking the current client, so don't render it
04642         }
04643 
04644         if ( cg_shadows.integer == 1 )
04645         {//dropshadow
04646                 if (cent->currentState.m_iVehicleNum &&
04647                         cent->currentState.NPC_class != CLASS_VEHICLE )
04648                 {//riding a vehicle, no dropshadow
04649                         return qfalse;
04650                 }
04651         }
04652         // send a trace down from the player to the ground
04653         VectorCopy( cent->lerpOrigin, end );
04654         if (cg_shadows.integer == 2)
04655         { //stencil
04656                 end[2] -= 4096.0f;
04657 
04658                 trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
04659 
04660                 if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid )
04661                 {
04662                         trace.endpos[2] = cent->lerpOrigin[2]-25.0f;
04663                 }
04664         }
04665         else
04666         {
04667                 end[2] -= SHADOW_DISTANCE;
04668 
04669                 trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
04670 
04671                 // no shadow if too high
04672                 if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) {
04673                         return qfalse;
04674                 }
04675         }
04676 
04677         if (cg_shadows.integer == 2)
04678         { //stencil shadows need plane to be on ground
04679                 *shadowPlane = trace.endpos[2];
04680         }
04681         else
04682         {
04683                 *shadowPlane = trace.endpos[2] + 1;
04684         }
04685 
04686         if ( cg_shadows.integer != 1 ) {        // no mark for stencil or projection shadows
04687                 return qtrue;
04688         }
04689 
04690         // fade the shadow out with height
04691         alpha = 1.0 - trace.fraction;
04692 
04693         // bk0101022 - hack / FPE - bogus planes?
04694         //assert( DotProduct( trace.plane.normal, trace.plane.normal ) != 0.0f ) 
04695 
04696         // add the mark as a temporary, so it goes directly to the renderer
04697         // without taking a spot in the cg_marks array
04698         if ( cent->currentState.NPC_class == CLASS_REMOTE
04699                 || cent->currentState.NPC_class == CLASS_SEEKER )
04700         {
04701                 radius = 8.0f;
04702         }
04703         CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, 
04704                 cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, radius, qtrue );
04705 
04706         return qtrue;
04707 }
04708 
04709 
04710 /*
04711 ===============
04712 CG_PlayerSplash
04713 
04714 Draw a mark at the water surface
04715 ===============
04716 */
04717 static void CG_PlayerSplash( centity_t *cent ) {
04718         vec3_t          start, end;
04719         trace_t         trace;
04720         int                     contents;
04721         polyVert_t      verts[4];
04722 
04723         if ( !cg_shadows.integer ) {
04724                 return;
04725         }
04726 
04727         VectorCopy( cent->lerpOrigin, end );
04728         end[2] -= 24;
04729 
04730         // if the feet aren't in liquid, don't make a mark
04731         // this won't handle moving water brushes, but they wouldn't draw right anyway...
04732         contents = trap_CM_PointContents( end, 0 );
04733         if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
04734                 return;
04735         }
04736 
04737         VectorCopy( cent->lerpOrigin, start );
04738         start[2] += 32;
04739 
04740         // if the head isn't out of liquid, don't make a mark
04741         contents = trap_CM_PointContents( start, 0 );
04742         if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
04743                 return;
04744         }
04745 
04746         // trace down to find the surface
04747         trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) );
04748 
04749         if ( trace.fraction == 1.0 ) {
04750                 return;
04751         }
04752 
04753         // create a mark polygon
04754         VectorCopy( trace.endpos, verts[0].xyz );
04755         verts[0].xyz[0] -= 32;
04756         verts[0].xyz[1] -= 32;
04757         verts[0].st[0] = 0;
04758         verts[0].st[1] = 0;
04759         verts[0].modulate[0] = 255;
04760         verts[0].modulate[1] = 255;
04761         verts[0].modulate[2] = 255;
04762         verts[0].modulate[3] = 255;
04763 
04764         VectorCopy( trace.endpos, verts[1].xyz );
04765         verts[1].xyz[0] -= 32;
04766         verts[1].xyz[1] += 32;
04767         verts[1].st[0] = 0;
04768         verts[1].st[1] = 1;
04769         verts[1].modulate[0] = 255;
04770         verts[1].modulate[1] = 255;
04771         verts[1].modulate[2] = 255;
04772         verts[1].modulate[3] = 255;
04773 
04774         VectorCopy( trace.endpos, verts[2].xyz );
04775         verts[2].xyz[0] += 32;
04776         verts[2].xyz[1] += 32;
04777         verts[2].st[0] = 1;
04778         verts[2].st[1] = 1;
04779         verts[2].modulate[0] = 255;
04780         verts[2].modulate[1] = 255;
04781         verts[2].modulate[2] = 255;
04782         verts[2].modulate[3] = 255;
04783 
04784         VectorCopy( trace.endpos, verts[3].xyz );
04785         verts[3].xyz[0] += 32;
04786         verts[3].xyz[1] -= 32;
04787         verts[3].st[0] = 1;
04788         verts[3].st[1] = 0;
04789         verts[3].modulate[0] = 255;
04790         verts[3].modulate[1] = 255;
04791         verts[3].modulate[2] = 255;
04792         verts[3].modulate[3] = 255;
04793 
04794         trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts );
04795 }
04796 
04797 #define REFRACT_EFFECT_DURATION         500
04798 static void CG_ForcePushBlur( vec3_t org, centity_t *cent )
04799 {
04800         if (!cent || !cg_renderToTextureFX.integer)
04801         {
04802                 localEntity_t   *ex;
04803 
04804                 ex = CG_AllocLocalEntity();
04805                 ex->leType = LE_PUFF;
04806                 ex->refEntity.reType = RT_SPRITE;
04807                 ex->radius = 2.0f;
04808                 ex->startTime = cg.time;
04809                 ex->endTime = ex->startTime + 120;
04810                 VectorCopy( org, ex->pos.trBase );
04811                 ex->pos.trTime = cg.time;
04812                 ex->pos.trType = TR_LINEAR;
04813                 VectorScale( cg.refdef.viewaxis[1], 55, ex->pos.trDelta );
04814                         
04815                 ex->color[0] = 24;
04816                 ex->color[1] = 32;
04817                 ex->color[2] = 40;
04818                 ex->refEntity.customShader = trap_R_RegisterShader( "gfx/effects/forcePush" );
04819 
04820                 ex = CG_AllocLocalEntity();
04821                 ex->leType = LE_PUFF;
04822                 ex->refEntity.reType = RT_SPRITE;
04823                 ex->refEntity.rotation = 180.0f;
04824                 ex->radius = 2.0f;
04825                 ex->startTime = cg.time;
04826                 ex->endTime = ex->startTime + 120;
04827                 VectorCopy( org, ex->pos.trBase );
04828                 ex->pos.trTime = cg.time;
04829                 ex->pos.trType = TR_LINEAR;
04830                 VectorScale( cg.refdef.viewaxis[1], -55, ex->pos.trDelta );
04831                         
04832                 ex->color[0] = 24;
04833                 ex->color[1] = 32;
04834                 ex->color[2] = 40;
04835                 ex->refEntity.customShader = trap_R_RegisterShader( "gfx/effects/forcePush" );
04836         }
04837         else
04838         { //superkewl "refraction" (well sort of) effect -rww
04839                 refEntity_t ent;
04840                 vec3_t ang;
04841                 float scale;
04842                 float vLen;
04843                 float alpha;
04844                 int tDif;
04845 
04846                 if (!cent->bodyFadeTime)
04847                 { //the duration for the expansion and fade
04848                         cent->bodyFadeTime = cg.time + REFRACT_EFFECT_DURATION;
04849                 }
04850 
04851                 //closer tDif is to 0, the closer we are to
04852                 //being "done"
04853                 tDif = (cent->bodyFadeTime - cg.time);
04854 
04855                 if ((REFRACT_EFFECT_DURATION-tDif) < 200)
04856                 { //stop following the hand after a little and stay in a fixed spot
04857                         //save the initial spot of the effect
04858                         VectorCopy(org, cent->pushEffectOrigin);
04859                 }
04860 
04861                 //scale from 1.0f to 0.1f then hold at 0.1 for the rest of the duration
04862                 if (cent->currentState.powerups & (1 << PW_PULL))
04863                 {
04864                         scale = (float)(REFRACT_EFFECT_DURATION-tDif)*0.003f;
04865                 }
04866                 else
04867                 {
04868                         scale = (float)(tDif)*0.003f;
04869                 }
04870 
04871                 if (scale > 1.0f)
04872                 {
04873                         scale = 1.0f;
04874                 }
04875                 else if (scale < 0.2f)
04876                 {
04877                         scale = 0.2f;
04878                 }
04879 
04880                 //start alpha at 244, fade to 10
04881                 alpha = (float)tDif*0.488f;
04882 
04883                 if (alpha > 244.0f)
04884                 {
04885                         alpha = 244.0f;
04886                 }
04887                 else if (alpha < 10.0f)
04888                 {
04889                         alpha = 10.0f;
04890                 }
04891 
04892                 memset( &ent, 0, sizeof( ent ) );
04893                 ent.shaderTime = (cent->bodyFadeTime-REFRACT_EFFECT_DURATION) / 1000.0f;
04894 
04895                 VectorCopy( cent->pushEffectOrigin, ent.origin );
04896 
04897                 VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]);
04898                 vLen = VectorLength(ent.axis[0]);
04899                 if (vLen <= 0.1f)
04900                 {       // Entity is right on vieworg.  quit.
04901                         return;
04902                 }
04903 
04904                 vectoangles(ent.axis[0], ang);
04905                 ang[ROLL] += 180.0f;
04906                 AnglesToAxis(ang, ent.axis);
04907 
04908                 //radius must be a power of 2, and is the actual captured texture size
04909                 if (vLen < 128)
04910                 {
04911                         ent.radius = 256;
04912                 }
04913                 else if (vLen < 256)
04914                 {
04915                         ent.radius = 128;
04916                 }
04917                 else if (vLen < 512)
04918                 {
04919                         ent.radius = 64;
04920                 }
04921                 else
04922                 {
04923                         ent.radius = 32;
04924                 }
04925 
04926                 VectorScale(ent.axis[0], scale, ent.axis[0]);
04927                 VectorScale(ent.axis[1], scale, ent.axis[1]);
04928                 VectorScale(ent.axis[2], scale, ent.axis[2]);
04929 
04930                 ent.hModel = cgs.media.halfShieldModel;
04931                 ent.customShader = cgs.media.refractionShader; //cgs.media.cloakedShader;
04932                 ent.nonNormalizedAxes = qtrue;
04933 
04934                 //make it partially transparent so it blends with the background
04935                 ent.renderfx = (RF_DISTORTION|RF_FORCE_ENT_ALPHA);
04936                 ent.shaderRGBA[0] = 255.0f;
04937                 ent.shaderRGBA[1] = 255.0f;
04938                 ent.shaderRGBA[2] = 255.0f;
04939                 ent.shaderRGBA[3] = alpha;
04940 
04941                 trap_R_AddRefEntityToScene( &ent );
04942         }
04943 }
04944 
04945 static const char *cg_pushBoneNames[] =
04946 {
04947         "cranium",
04948         "lower_lumbar",
04949         "rhand",
04950         "lhand",
04951         "ltibia",
04952         "rtibia",
04953         "lradius",
04954         "rradius",
04955         NULL
04956 };
04957 
04958 static void CG_ForcePushBodyBlur( centity_t *cent )
04959 {
04960         vec3_t fxOrg;
04961         mdxaBone_t      boltMatrix;
04962         int bolt;
04963         int i;
04964 
04965         if (cent->localAnimIndex > 1)
04966         { //Sorry, the humanoid IS IN ANOTHER CASTLE.
04967                 return;
04968         }
04969 
04970         if (cg.snap &&
04971                 CG_IsMindTricked(cent->currentState.trickedentindex,
04972                 cent->currentState.trickedentindex2,
04973                 cent->currentState.trickedentindex3,
04974                 cent->currentState.trickedentindex4,
04975                 cg.snap->ps.clientNum))
04976         {
04977                 return; //this entity is mind-tricking the current client, so don't render it
04978         }
04979 
04980         assert(cent->ghoul2);
04981 
04982         for (i = 0; cg_pushBoneNames[i]; i++)
04983         { //go through all the bones we want to put a blur effect on
04984                 bolt = trap_G2API_AddBolt(cent->ghoul2, 0, cg_pushBoneNames[i]);
04985 
04986                 if (bolt == -1)
04987                 {
04988                         assert(!"You've got an invalid bone/bolt name in cg_pushBoneNames");
04989                         continue;
04990                 }
04991 
04992                 trap_G2API_GetBoltMatrix(cent->ghoul2, 0, bolt, &boltMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
04993                 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, fxOrg);
04994 
04995                 //standard effect, don't be refractive (for now)
04996                 CG_ForcePushBlur(fxOrg, NULL);
04997         }
04998 }
04999 
05000 static void CG_ForceGripEffect( vec3_t org )
05001 {
05002         localEntity_t   *ex;
05003         float wv = sin( cg.time * 0.004f ) * 0.08f + 0.1f;
05004 
05005         ex = CG_AllocLocalEntity();
05006         ex->leType = LE_PUFF;
05007         ex->refEntity.reType = RT_SPRITE;
05008         ex->radius = 2.0f;
05009         ex->startTime = cg.time;
05010         ex->endTime = ex->startTime + 120;
05011         VectorCopy( org, ex->pos.trBase );
05012         ex->pos.trTime = cg.time;
05013         ex->pos.trType = TR_LINEAR;
05014         VectorScale( cg.refdef.viewaxis[1], 55, ex->pos.trDelta );
05015                 
05016         ex->color[0] = 200+((wv*255));
05017         if (ex->color[0] > 255)
05018         {
05019                 ex->color[0] = 255;
05020         }
05021         ex->color[1] = 0;
05022         ex->color[2] = 0;
05023         ex->refEntity.customShader = trap_R_RegisterShader( "gfx/effects/forcePush" );
05024 
05025         ex = CG_AllocLocalEntity();
05026         ex->leType = LE_PUFF;
05027         ex->refEntity.reType = RT_SPRITE;
05028         ex->refEntity.rotation = 180.0f;
05029         ex->radius = 2.0f;
05030         ex->startTime = cg.time;
05031         ex->endTime = ex->startTime + 120;
05032         VectorCopy( org, ex->pos.trBase );
05033         ex->pos.trTime = cg.time;
05034         ex->pos.trType = TR_LINEAR;
05035         VectorScale( cg.refdef.viewaxis[1], -55, ex->pos.trDelta );
05036 
05037         /*
05038         ex->color[0] = 200+((wv*255));
05039         if (ex->color[0] > 255)
05040         {
05041                 ex->color[0] = 255;
05042         }
05043         */
05044         ex->color[0] = 255;
05045         ex->color[1] = 255;
05046         ex->color[2] = 255;
05047         ex->refEntity.customShader = cgs.media.redSaberGlowShader;//trap_R_RegisterShader( "gfx/effects/forcePush" );
05048 }
05049 
05050 
05051 /*
05052 ===============
05053 CG_AddRefEntityWithPowerups
05054 
05055 Adds a piece with modifications or duplications for powerups
05056 Also called by CG_Missile for quad rockets, but nobody can tell...
05057 ===============
05058 */
05059 void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ) {
05060 
05061         if (CG_IsMindTricked(state->trickedentindex,
05062                 state->trickedentindex2,
05063                 state->trickedentindex3,
05064                 state->trickedentindex4,
05065                 cg.snap->ps.clientNum))
05066         {
05067                 return; //this entity is mind-tricking the current client, so don't render it
05068         }
05069 
05070         trap_R_AddRefEntityToScene( ent );
05071 }
05072 
05073 #define MAX_SHIELD_TIME 2000.0
05074 #define MIN_SHIELD_TIME 2000.0
05075 
05076 
05077 void CG_PlayerShieldHit(int entitynum, vec3_t dir, int amount)
05078 {
05079         centity_t *cent;
05080         int     time;
05081 
05082         if (entitynum<0 || entitynum >= MAX_ENTITIES)
05083         {
05084                 return;
05085         }
05086 
05087         cent = &cg_entities[entitynum];
05088 
05089         if (amount > 100)
05090         {
05091                 time = cg.time + MAX_SHIELD_TIME;               // 2 sec.
05092         }
05093         else
05094         {
05095                 time = cg.time + 500 + amount*15;
05096         }
05097 
05098         if (time > cent->damageTime)
05099         {
05100                 cent->damageTime = time;
05101                 VectorScale(dir, -1, dir);
05102                 vectoangles(dir, cent->damageAngles);
05103         }
05104 }
05105 
05106 
05107 void CG_DrawPlayerShield(centity_t *cent, vec3_t origin)
05108 {
05109         refEntity_t ent;
05110         int                     alpha;
05111         float           scale;
05112         
05113         // Don't draw the shield when the player is dead.
05114         if (cent->currentState.eFlags & EF_DEAD)
05115         {
05116                 return;
05117         }
05118 
05119         memset( &ent, 0, sizeof( ent ) );
05120 
05121         VectorCopy( origin, ent.origin );
05122         ent.origin[2] += 10.0;
05123         AnglesToAxis( cent->damageAngles, ent.axis );
05124 
05125         alpha = 255.0 * ((cent->damageTime - cg.time) / MIN_SHIELD_TIME) + random()*16;
05126         if (alpha>255)
05127                 alpha=255;
05128 
05129         // Make it bigger, but tighter if more solid
05130         scale = 1.4 - ((float)alpha*(0.4/255.0));               // Range from 1.0 to 1.4
05131         VectorScale( ent.axis[0], scale, ent.axis[0] );
05132         VectorScale( ent.axis[1], scale, ent.axis[1] );
05133         VectorScale( ent.axis[2], scale, ent.axis[2] );
05134 
05135         ent.hModel = cgs.media.halfShieldModel;
05136         ent.customShader = cgs.media.halfShieldShader;
05137         ent.shaderRGBA[0] = alpha;
05138         ent.shaderRGBA[1] = alpha;
05139         ent.shaderRGBA[2] = alpha;
05140         ent.shaderRGBA[3] = 255;
05141         trap_R_AddRefEntityToScene( &ent );
05142 }
05143 
05144 
05145 void CG_PlayerHitFX(centity_t *cent)
05146 {
05147         // only do the below fx if the cent in question is...uh...me, and it's first person.
05148         if (cent->currentState.clientNum != cg.predictedPlayerState.clientNum || cg.renderingThirdPerson)
05149         {
05150                 if (cent->damageTime > cg.time
05151                         && cent->currentState.NPC_class != CLASS_VEHICLE )
05152                 {
05153                         CG_DrawPlayerShield(cent, cent->lerpOrigin);
05154                 }
05155 
05156                 return;
05157         }
05158 }
05159 
05160 
05161 
05162 /*
05163 =================
05164 CG_LightVerts
05165 =================
05166 */
05167 int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts )
05168 {
05169         int                             i, j;
05170         float                   incoming;
05171         vec3_t                  ambientLight;
05172         vec3_t                  lightDir;
05173         vec3_t                  directedLight;
05174 
05175         trap_R_LightForPoint( verts[0].xyz, ambientLight, directedLight, lightDir );
05176 
05177         for (i = 0; i < numVerts; i++) {
05178                 incoming = DotProduct (normal, lightDir);
05179                 if ( incoming <= 0 ) {
05180                         verts[i].modulate[0] = ambientLight[0];
05181                         verts[i].modulate[1] = ambientLight[1];
05182                         verts[i].modulate[2] = ambientLight[2];
05183                         verts[i].modulate[3] = 255;
05184                         continue;
05185                 } 
05186                 j = ( ambientLight[0] + incoming * directedLight[0] );
05187                 if ( j > 255 ) {
05188                         j = 255;
05189                 }
05190                 verts[i].modulate[0] = j;
05191 
05192                 j = ( ambientLight[1] + incoming * directedLight[1] );
05193                 if ( j > 255 ) {
05194                         j = 255;
05195                 }
05196                 verts[i].modulate[1] = j;
05197 
05198                 j = ( ambientLight[2] + incoming * directedLight[2] );
05199                 if ( j > 255 ) {
05200                         j = 255;
05201                 }
05202                 verts[i].modulate[2] = j;
05203 
05204                 verts[i].modulate[3] = 255;
05205         }
05206         return qtrue;
05207 }
05208 
05209 static void CG_RGBForSaberColor( saber_colors_t color, vec3_t rgb )
05210 {
05211         switch( color )
05212         {
05213                 case SABER_RED:
05214                         VectorSet( rgb, 1.0f, 0.2f, 0.2f );
05215                         break;
05216                 case SABER_ORANGE:
05217                         VectorSet( rgb, 1.0f, 0.5f, 0.1f );
05218                         break;
05219                 case SABER_YELLOW:
05220                         VectorSet( rgb, 1.0f, 1.0f, 0.2f );
05221                         break;
05222                 case SABER_GREEN:
05223                         VectorSet( rgb, 0.2f, 1.0f, 0.2f );
05224                         break;
05225                 case SABER_BLUE:
05226                         VectorSet( rgb, 0.2f, 0.4f, 1.0f );
05227                         break;
05228                 case SABER_PURPLE:
05229                         VectorSet( rgb, 0.9f, 0.2f, 1.0f );
05230                         break;
05231         }
05232 }
05233 
05234 static void CG_DoSaberLight( saberInfo_t *saber )
05235 {
05236         vec3_t          positions[MAX_BLADES*2], mid={0}, rgbs[MAX_BLADES*2], rgb={0};
05237         float           lengths[MAX_BLADES*2]={0}, totallength = 0, numpositions = 0, dist, diameter = 0;
05238         int                     i, j;
05239 
05240         //RGB combine all the colors of the sabers you're using into one averaged color!
05241         if ( !saber )
05242         { 
05243                 return;
05244         }
05245         
05246         if ( (saber->saberFlags2&SFL2_NO_DLIGHT) )
05247         {//no dlight!
05248                 return;
05249         }
05250 
05251         for ( i = 0; i < saber->numBlades; i++ )
05252         {
05253                 if ( saber->blade[i].length >= 0.5f )
05254                 {
05255                         //FIXME: make RGB sabers
05256                         CG_RGBForSaberColor( saber->blade[i].color, rgbs[i] );
05257                         lengths[i] = saber->blade[i].length;
05258                         if ( saber->blade[i].length*2.0f > diameter )
05259                         {
05260                                 diameter = saber->blade[i].length*2.0f;
05261                         }
05262                         totallength += saber->blade[i].length;
05263                         VectorMA( saber->blade[i].muzzlePoint, saber->blade[i].length, saber->blade[i].muzzleDir, positions[i] );
05264                         if ( !numpositions )
05265                         {//first blade, store middle of that as midpoint
05266                                 VectorMA( saber->blade[i].muzzlePoint, saber->blade[i].length*0.5, saber->blade[i].muzzleDir, mid );
05267                                 VectorCopy( rgbs[i], rgb );
05268                         }
05269                         numpositions++;
05270                 }
05271         }
05272 
05273         if ( totallength )
05274         {//actually have something to do        
05275                 if ( numpositions == 1 )
05276                 {//only 1 blade, midpoint is already set (halfway between the start and end of that blade), rgb is already set, so it diameter
05277                 }
05278                 else
05279                 {//multiple blades, calc averages
05280                         VectorClear( mid );
05281                         VectorClear( rgb );
05282                         //now go through all the data and get the average RGB and middle position and the radius
05283                         for ( i = 0; i < MAX_BLADES*2; i++ )
05284                         {
05285                                 if ( lengths[i] )
05286                                 {
05287                                         VectorMA( rgb, lengths[i], rgbs[i], rgb );
05288                                         VectorAdd( mid, positions[i], mid );
05289                                 }
05290                         }
05291 
05292                         //get middle rgb
05293                         VectorScale( rgb, 1/totallength, rgb );//get the average, normalized RGB
05294                         //get mid position
05295                         VectorScale( mid, 1/numpositions, mid );
05296                         //find the farthest distance between the blade tips, this will be our diameter
05297                         for ( i = 0; i < MAX_BLADES*2; i++ )
05298                         {
05299                                 if ( lengths[i] )
05300                                 {
05301                                         for ( j = 0; j < MAX_BLADES*2; j++ )
05302                                         {
05303                                                 if ( lengths[j] )
05304                                                 {
05305                                                         dist = Distance( positions[i], positions[j] );
05306                                                         if ( dist > diameter )
05307                                                         {
05308                                                                 diameter = dist;
05309                                                         }
05310                                                 }
05311                                         }
05312                                 }
05313                         }
05314                 }
05315 
05316                 trap_R_AddLightToScene( mid, diameter + (random()*8.0f), rgb[0], rgb[1], rgb[2] );
05317         }
05318 }
05319 
05320 void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight )
05321 {
05322         vec3_t          mid;
05323         qhandle_t       blade = 0, glow = 0;
05324         refEntity_t saber;
05325         float radiusmult;
05326         float radiusRange;
05327         float radiusStart;
05328 
05329         if ( length < 0.5f )
05330         {
05331                 // if the thing is so short, just forget even adding me.
05332                 return;
05333         }
05334 
05335         // Find the midpoint of the saber for lighting purposes
05336         VectorMA( origin, length * 0.5f, dir, mid );
05337 
05338         switch( color )
05339         {
05340                 case SABER_RED:
05341                         glow = cgs.media.redSaberGlowShader;
05342                         blade = cgs.media.redSaberCoreShader;
05343                         break;
05344                 case SABER_ORANGE:
05345                         glow = cgs.media.orangeSaberGlowShader;
05346                         blade = cgs.media.orangeSaberCoreShader;
05347                         break;
05348                 case SABER_YELLOW:
05349                         glow = cgs.media.yellowSaberGlowShader;
05350                         blade = cgs.media.yellowSaberCoreShader;
05351                         break;
05352                 case SABER_GREEN:
05353                         glow = cgs.media.greenSaberGlowShader;
05354                         blade = cgs.media.greenSaberCoreShader;
05355                         break;
05356                 case SABER_BLUE:
05357                         glow = cgs.media.blueSaberGlowShader;
05358                         blade = cgs.media.blueSaberCoreShader;
05359                         break;
05360                 case SABER_PURPLE:
05361                         glow = cgs.media.purpleSaberGlowShader;
05362                         blade = cgs.media.purpleSaberCoreShader;
05363                         break;
05364                 default:
05365                         glow = cgs.media.blueSaberGlowShader;
05366                         blade = cgs.media.blueSaberCoreShader;
05367                         break;
05368         }
05369 
05370         if (doLight)
05371         {       // always add a light because sabers cast a nice glow before they slice you in half!!  or something...
05372                 vec3_t rgb={1,1,1};
05373                 CG_RGBForSaberColor( color, rgb );
05374                 trap_R_AddLightToScene( mid, (length*1.4f) + (random()*3.0f), rgb[0], rgb[1], rgb[2] );
05375         }
05376 
05377         memset( &saber, 0, sizeof( refEntity_t ));
05378 
05379         // Saber glow is it's own ref type because it uses a ton of sprites, otherwise it would eat up too many
05380         //      refEnts to do each glow blob individually
05381         saber.saberLength = length;
05382 
05383         // Jeff, I did this because I foolishly wished to have a bright halo as the saber is unleashed.  
05384         // It's not quite what I'd hoped tho.  If you have any ideas, go for it!  --Pat
05385         if (length < lengthMax)
05386         {
05387                 radiusmult = 1.0 + (2.0 / length);              // Note this creates a curve, and length cannot be < 0.5.
05388         }
05389         else
05390         {
05391                 radiusmult = 1.0;
05392         }
05393 
05394         if (cg_saberTrail.integer == 2 && cg_shadows.integer != 2 && cgs.glconfig.stencilBits >= 4)
05395         { //draw the blade as a post-render so it doesn't get in the cap...
05396                 rfx |= RF_FORCEPOST;
05397         }
05398 
05399         radiusRange = radius * 0.075f;
05400         radiusStart = radius-radiusRange;
05401 
05402         saber.radius = (radiusStart + crandom() * radiusRange)*radiusmult;
05403         //saber.radius = (2.8f + crandom() * 0.2f)*radiusmult;
05404 
05405         VectorCopy( origin, saber.origin );
05406         VectorCopy( dir, saber.axis[0] );
05407         saber.reType = RT_SABER_GLOW;
05408         saber.customShader = glow;
05409         saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff;
05410         saber.renderfx = rfx;
05411 
05412         trap_R_AddRefEntityToScene( &saber );
05413 
05414         // Do the hot core
05415         VectorMA( origin, length, dir, saber.origin );
05416         VectorMA( origin, -1, dir, saber.oldorigin );
05417 
05418 
05419 //      CG_TestLine(saber.origin, saber.oldorigin, 50, 0x000000ff, 3);
05420         saber.customShader = blade;
05421         saber.reType = RT_LINE;
05422         radiusStart = radius/3.0f;
05423         saber.radius = (radiusStart + crandom() * radiusRange)*radiusmult;
05424 //      saber.radius = (1.0 + crandom() * 0.2f)*radiusmult;
05425 
05426         saber.shaderTexCoord[0] = saber.shaderTexCoord[1] = 1.0f;
05427         saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff;
05428 
05429         trap_R_AddRefEntityToScene( &saber );
05430 }
05431 
05432 //--------------------------------------------------------------
05433 // CG_GetTagWorldPosition
05434 //
05435 // Can pass in NULL for the axis
05436 //--------------------------------------------------------------
05437 void CG_GetTagWorldPosition( refEntity_t *model, char *tag, vec3_t pos, vec3_t axis[3] )
05438 {
05439         orientation_t   orientation;
05440         int i = 0;
05441 
05442         // Get the requested tag
05443         trap_R_LerpTag( &orientation, model->hModel, model->oldframe, model->frame,
05444                 1.0f - model->backlerp, tag );
05445 
05446         VectorCopy( model->origin, pos );
05447         for ( i = 0 ; i < 3 ; i++ ) 
05448         {
05449                 VectorMA( pos, orientation.origin[i], model->axis[i], pos );
05450         }
05451 
05452         if ( axis )
05453         {
05454                 MatrixMultiply( orientation.axis, model->axis, axis );
05455         }
05456 }
05457 
05458 #define MAX_MARK_FRAGMENTS      128
05459 #define MAX_MARK_POINTS         384
05460 extern markPoly_t *CG_AllocMark();
05461 
05462 void CG_CreateSaberMarks( vec3_t start, vec3_t end, vec3_t normal )
05463 {
05464 //      byte                    colors[4];
05465         int                             i, j;
05466         int                             numFragments;
05467         vec3_t                  axis[3], originalPoints[4], mid;
05468         vec3_t                  markPoints[MAX_MARK_POINTS], projection;
05469         polyVert_t              *v, verts[MAX_VERTS_ON_POLY];
05470         markPoly_t              *mark;
05471         markFragment_t  markFragments[MAX_MARK_FRAGMENTS], *mf;
05472 
05473         float   radius = 0.65f;
05474 
05475         if ( !cg_addMarks.integer ) 
05476         {
05477                 return;
05478         }
05479 
05480         VectorSubtract( end, start, axis[1] );
05481         VectorNormalize( axis[1] );
05482 
05483         // create the texture axis
05484         VectorCopy( normal, axis[0] );
05485         CrossProduct( axis[1], axis[0], axis[2] );
05486 
05487         // create the full polygon that we'll project
05488         for ( i = 0 ; i < 3 ; i++ ) 
05489         {       // stretch a bit more in the direction that we are traveling in...  debateable as to whether this makes things better or worse
05490                 originalPoints[0][i] = start[i] - radius * axis[1][i] - radius * axis[2][i];
05491                 originalPoints[1][i] = end[i] + radius * axis[1][i] - radius * axis[2][i];
05492                 originalPoints[2][i] = end[i] + radius * axis[1][i] + radius * axis[2][i];
05493                 originalPoints[3][i] = start[i] - radius * axis[1][i] + radius * axis[2][i];
05494         }
05495 
05496         VectorScale( normal, -1, projection );
05497 
05498         // get the fragments
05499         numFragments = trap_CM_MarkFragments( 4, (const float (*)[3])originalPoints,
05500                                         projection, MAX_MARK_POINTS, markPoints[0], MAX_MARK_FRAGMENTS, markFragments );
05501 
05502         for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ ) 
05503         {
05504                 // we have an upper limit on the complexity of polygons that we store persistantly
05505                 if ( mf->numPoints > MAX_VERTS_ON_POLY ) 
05506                 {
05507                         mf->numPoints = MAX_VERTS_ON_POLY;
05508                 }
05509 
05510                 for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ ) 
05511                 {
05512                         vec3_t delta;
05513 
05514                         // Set up our texture coords, this may need some work 
05515                         VectorCopy( markPoints[mf->firstPoint + j], v->xyz );
05516                         VectorAdd( end, start, mid );
05517                         VectorScale( mid, 0.5f, mid );
05518                         VectorSubtract( v->xyz, mid, delta );
05519 
05520                         v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * (0.05f + random() * 0.03f); 
05521                         v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * (0.15f + random() * 0.05f);     
05522                 }
05523 
05524                 if (cg_saberDynamicMarks.integer)
05525                 {
05526                         int i = 0;
05527                         int i_2 = 0;
05528                         addpolyArgStruct_t apArgs;
05529                         vec3_t x;
05530 
05531                         memset (&apArgs, 0, sizeof(apArgs));
05532 
05533                         while (i < 4)
05534                         {
05535                                 while (i_2 < 3)
05536                                 {
05537                                         apArgs.p[i][i_2] = verts[i].xyz[i_2];
05538 
05539                                         i_2++;
05540                                 }
05541 
05542                                 i_2 = 0;
05543                                 i++;
05544                         }
05545 
05546                         i = 0;
05547                         i_2 = 0;
05548 
05549                         while (i < 4)
05550                         {
05551                                 while (i_2 < 2)
05552                                 {
05553                                         apArgs.ev[i][i_2] = verts[i].st[i_2];
05554 
05555                                         i_2++;
05556                                 }
05557 
05558                                 i_2 = 0;
05559                                 i++;
05560                         }
05561 
05562                         //When using addpoly, having a situation like this tends to cause bad results.
05563                         //(I assume it doesn't like trying to draw a polygon over two planes and extends
05564                         //the vertex out to some odd value)
05565                         VectorSubtract(apArgs.p[0], apArgs.p[3], x);
05566                         if (VectorLength(x) > 3.0f)
05567                         {
05568                                 return;
05569                         }
05570 
05571                         apArgs.numVerts = mf->numPoints;
05572                         VectorCopy(vec3_origin, apArgs.vel);
05573                         VectorCopy(vec3_origin, apArgs.accel);
05574 
05575                         apArgs.alpha1 = 1.0f;
05576                         apArgs.alpha2 = 0.0f;
05577                         apArgs.alphaParm = 255.0f;
05578 
05579                         VectorSet(apArgs.rgb1, 0.0f, 0.0f, 0.0f);
05580                         VectorSet(apArgs.rgb2, 0.0f, 0.0f, 0.0f);
05581 
05582                         apArgs.rgbParm = 0.0f;
05583 
05584                         apArgs.bounce = 0;
05585                         apArgs.motionDelay = 0;
05586                         apArgs.killTime = cg_saberDynamicMarkTime.integer;
05587                         apArgs.shader = cgs.media.rivetMarkShader;
05588                         apArgs.flags = 0x08000000|0x00000004;
05589 
05590                         trap_FX_AddPoly(&apArgs);
05591 
05592                         apArgs.shader = cgs.media.mSaberDamageGlow;
05593                         apArgs.rgb1[0] = 215 + random() * 40.0f;
05594                         apArgs.rgb1[1] = 96 + random() * 32.0f;
05595                         apArgs.rgb1[2] = apArgs.alphaParm = random()*15.0f;
05596 
05597                         apArgs.rgb1[0] /= 255;
05598                         apArgs.rgb1[1] /= 255;
05599                         apArgs.rgb1[2] /= 255;
05600                         VectorCopy(apArgs.rgb1, apArgs.rgb2);
05601 
05602                         apArgs.killTime = 100;
05603 
05604                         trap_FX_AddPoly(&apArgs);
05605                 }
05606                 else
05607                 {
05608                         // save it persistantly, do burn first
05609                         mark = CG_AllocMark();
05610                         mark->time = cg.time;
05611                         mark->alphaFade = qtrue;
05612                         mark->markShader = cgs.media.rivetMarkShader;
05613                         mark->poly.numVerts = mf->numPoints;
05614                         mark->color[0] = mark->color[1] = mark->color[2] = mark->color[3] = 255;
05615                         memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) );
05616 
05617                         // And now do a glow pass
05618                         // by moving the start time back, we can hack it to fade out way before the burn does
05619                         mark = CG_AllocMark();
05620                         mark->time = cg.time - 8500;
05621                         mark->alphaFade = qfalse;
05622                         mark->markShader = cgs.media.mSaberDamageGlow;
05623                         mark->poly.numVerts = mf->numPoints;
05624                         mark->color[0] = 215 + random() * 40.0f;
05625                         mark->color[1] = 96 + random() * 32.0f;
05626                         mark->color[2] = mark->color[3] = random()*15.0f;
05627                         memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) );
05628                 }
05629         }
05630 }
05631 
05632 qboolean CG_G2TraceCollide(trace_t *tr, vec3_t const mins, vec3_t const maxs, const vec3_t lastValidStart, const vec3_t lastValidEnd)
05633 {
05634         G2Trace_t               G2Trace;
05635         centity_t               *g2Hit;
05636         vec3_t                  angles;
05637         int                             tN = 0;
05638         float                   fRadius = 0.0f;
05639 
05640         if (mins && maxs &&
05641                 (mins[0] || maxs[0]))
05642         {
05643                 fRadius=(maxs[0]-mins[0])/2.0f;
05644         }
05645 
05646         memset (&G2Trace, 0, sizeof(G2Trace));
05647 
05648         while (tN < MAX_G2_COLLISIONS)
05649         {
05650                 G2Trace[tN].mEntityNum = -1;
05651                 tN++;
05652         }
05653         g2Hit = &cg_entities[tr->entityNum];
05654 
05655         if (g2Hit && g2Hit->ghoul2)
05656         {
05657                 angles[ROLL] = angles[PITCH] = 0;
05658                 angles[YAW] = g2Hit->lerpAngles[YAW];
05659 
05660                 if (cg_optvehtrace.integer &&
05661                         g2Hit->currentState.eType == ET_NPC &&
05662                         g2Hit->currentState.NPC_class == CLASS_VEHICLE &&
05663                         g2Hit->m_pVehicle)
05664                 {
05665                         trap_G2API_CollisionDetectCache ( G2Trace, g2Hit->ghoul2, angles, g2Hit->lerpOrigin, cg.time, g2Hit->currentState.number, lastValidStart, lastValidEnd, g2Hit->modelScale, 0, cg_g2TraceLod.integer, fRadius );
05666                 }
05667                 else
05668                 {
05669                         trap_G2API_CollisionDetect ( G2Trace, g2Hit->ghoul2, angles, g2Hit->lerpOrigin, cg.time, g2Hit->currentState.number, lastValidStart, lastValidEnd, g2Hit->modelScale, 0, cg_g2TraceLod.integer, fRadius );
05670                 }
05671 
05672                 if (G2Trace[0].mEntityNum != g2Hit->currentState.number)
05673                 {
05674                         tr->fraction = 1.0f;
05675                         tr->entityNum = ENTITYNUM_NONE;
05676                         tr->startsolid = 0;
05677                         tr->allsolid = 0;
05678                         return qfalse;
05679                 }
05680                 else
05681                 { //Yay!
05682                         VectorCopy(G2Trace[0].mCollisionPosition, tr->endpos);
05683                         VectorCopy(G2Trace[0].mCollisionNormal, tr->plane.normal);
05684                         return qtrue;
05685                 }
05686         }
05687 
05688         return qfalse;
05689 }
05690 
05691 void CG_G2SaberEffects(vec3_t start, vec3_t end, centity_t *owner)
05692 {
05693         trace_t trace;
05694         vec3_t startTr;
05695         vec3_t endTr;
05696         qboolean backWards = qfalse;
05697         qboolean doneWithTraces = qfalse;
05698 
05699         while (!doneWithTraces)
05700         {
05701                 if (!backWards)
05702                 {
05703                         VectorCopy(start, startTr);
05704                         VectorCopy(end, endTr);
05705                 }
05706                 else
05707                 {
05708                         VectorCopy(end, startTr);
05709                         VectorCopy(start, endTr);
05710                 }
05711 
05712                 CG_Trace( &trace, startTr, NULL, NULL, endTr, owner->currentState.number, MASK_PLAYERSOLID );
05713 
05714                 if (trace.entityNum < MAX_CLIENTS)
05715                 { //hit a client..
05716                         CG_G2TraceCollide(&trace, NULL, NULL, startTr, endTr);
05717 
05718                         if (trace.entityNum != ENTITYNUM_NONE)
05719                         { //it succeeded with the ghoul2 trace
05720                                 trap_FX_PlayEffectID( cgs.effects.mSaberBloodSparks, trace.endpos, trace.plane.normal, -1, -1 );
05721                                 trap_S_StartSound(trace.endpos, trace.entityNum, CHAN_AUTO, trap_S_RegisterSound(va("sound/weapons/saber/saberhit%i.wav", Q_irand(1, 3))));
05722                         }
05723                 }
05724 
05725                 if (!backWards)
05726                 {
05727                         backWards = qtrue;
05728                 }
05729                 else
05730                 {
05731                         doneWithTraces = qtrue;
05732                 }
05733         }
05734 }
05735 
05736 #define CG_MAX_SABER_COMP_TIME 400 //last registered saber entity hit must match within this many ms for the client effect to take place.
05737 
05738 void CG_AddGhoul2Mark(int shader, float size, vec3_t start, vec3_t end, int entnum,
05739                                           vec3_t entposition, float entangle, void *ghoul2, vec3_t scale, int lifeTime)
05740 {
05741         SSkinGoreData goreSkin;
05742 
05743         assert(ghoul2);
05744 
05745         memset ( &goreSkin, 0, sizeof(goreSkin) );
05746 
05747         if (trap_G2API_GetNumGoreMarks(ghoul2, 0) >= cg_ghoul2Marks.integer)
05748         { //you've got too many marks already
05749                 return;
05750         }
05751 
05752         goreSkin.growDuration = -1; // default expandy time
05753         goreSkin.goreScaleStartFraction = 1.0; // default start scale
05754         goreSkin.frontFaces = qtrue;
05755         goreSkin.backFaces = qtrue;
05756         goreSkin.lifeTime = lifeTime; //last randomly 10-20 seconds
05757         /*
05758         if (lifeTime)
05759         {
05760                 goreSkin.fadeOutTime = lifeTime*0.1; //default fade duration is relative to lifetime.
05761         }
05762         goreSkin.fadeRGB = qtrue; //fade on RGB instead of alpha (this depends on the shader really, modify if needed)
05763         */
05764         //rwwFIXMEFIXME: fade has sorting issues with other non-fading decals, disabled until fixed
05765 
05766         goreSkin.baseModelOnly = qfalse;
05767         
05768         goreSkin.currentTime = cg.time;
05769         goreSkin.entNum      = entnum;
05770         goreSkin.SSize           = size;
05771         goreSkin.TSize           = size;
05772         goreSkin.theta           = flrand(0.0f,6.28f);
05773         goreSkin.shader          = shader;
05774 
05775         if (!scale[0] && !scale[1] && !scale[2])
05776         {
05777                 VectorSet(goreSkin.scale, 1.0f, 1.0f, 1.0f);
05778         }
05779         else
05780         {
05781                 VectorCopy(goreSkin.scale, scale);
05782         }
05783 
05784         VectorCopy (start, goreSkin.hitLocation);
05785 
05786         VectorSubtract(end, start, goreSkin.rayDirection);
05787         if (VectorNormalize(goreSkin.rayDirection)<.1f)
05788         {
05789                 return;
05790         }
05791 
05792         VectorCopy ( entposition, goreSkin.position );
05793         goreSkin.angles[YAW] = entangle;
05794 
05795         trap_G2API_AddSkinGore(ghoul2, &goreSkin);
05796 }
05797 
05798 void CG_SaberCompWork(vec3_t start, vec3_t end, centity_t *owner, int saberNum, int bladeNum)
05799 {
05800         trace_t trace;
05801         vec3_t startTr;
05802         vec3_t endTr;
05803         qboolean backWards = qfalse;
05804         qboolean doneWithTraces = qfalse;
05805         qboolean doEffect = qfalse;
05806         clientInfo_t *client = NULL;
05807 
05808         if ((cg.time - owner->serverSaberHitTime) > CG_MAX_SABER_COMP_TIME)
05809         {
05810                 return;
05811         }
05812 
05813         if (cg.time == owner->serverSaberHitTime)
05814         { //don't want to do it the same frame as the server hit, to avoid burst effect concentrations every x ms.
05815                 return;
05816         }
05817 
05818         while (!doneWithTraces)
05819         {
05820                 if (!backWards)
05821                 {
05822                         VectorCopy(start, startTr);
05823                         VectorCopy(end, endTr);
05824                 }
05825                 else
05826                 {
05827                         VectorCopy(end, startTr);
05828                         VectorCopy(start, endTr);
05829                 }
05830 
05831                 CG_Trace( &trace, startTr, NULL, NULL, endTr, owner->currentState.number, MASK_PLAYERSOLID );
05832 
05833                 if (trace.entityNum == owner->serverSaberHitIndex)
05834                 { //this is the guy the server says we last hit, so continue.
05835                         if (cg_entities[trace.entityNum].ghoul2)
05836                         { //If it has a g2 instance, do the proper ghoul2 checks
05837                                 CG_G2TraceCollide(&trace, NULL, NULL, startTr, endTr);
05838 
05839                                 if (trace.entityNum != ENTITYNUM_NONE)
05840                                 { //it succeeded with the ghoul2 trace
05841                                         doEffect = qtrue;
05842 
05843                                         if (cg_ghoul2Marks.integer)
05844                                         {
05845                                                 vec3_t ePos;
05846                                                 centity_t *trEnt = &cg_entities[trace.entityNum];
05847 
05848                                                 if (trEnt->ghoul2)
05849                                                 {
05850                                                         if (trEnt->currentState.eType != ET_NPC ||
05851                                                                 trEnt->currentState.NPC_class != CLASS_VEHICLE ||
05852                                                                 !trEnt->m_pVehicle ||
05853                                                                 trEnt->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
05854                                                         { //don't do on fighters cause they have crazy full axial angles
05855                                                                 int weaponMarkShader = 0, markShader = cgs.media.bdecal_saberglow;
05856 
05857                                                                 VectorSubtract(endTr, trace.endpos, ePos);
05858                                                                 VectorNormalize(ePos);
05859                                                                 VectorMA(trace.endpos, 4.0f, ePos, ePos);
05860 
05861                                                                 if (owner->currentState.eType == ET_NPC)
05862                                                                 {
05863                                                                         client = owner->npcClient;
05864                                                                 }
05865                                                                 else
05866                                                                 {
05867                                                                         client = &cgs.clientinfo[owner->currentState.clientNum];
05868                                                                 }
05869                                                                 if ( client 
05870                                                                         && client->infoValid )
05871                                                                 {
05872                                                                         if ( WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) )
05873                                                                         {
05874                                                                                 if ( client->saber[saberNum].g2MarksShader2 )
05875                                                                                 {//we have a shader to use instead of the standard mark shader
05876                                                                                         markShader = client->saber[saberNum].g2MarksShader2;
05877                                                                                 }
05878                                                                                 if ( client->saber[saberNum].g2WeaponMarkShader2 )
05879                                                                                 {//we have a shader to use as a splashback onto the weapon model
05880                                                                                         weaponMarkShader = client->saber[saberNum].g2WeaponMarkShader2;
05881                                                                                 }
05882                                                                         }
05883                                                                         else
05884                                                                         {
05885                                                                                 if ( client->saber[saberNum].g2MarksShader )
05886                                                                                 {//we have a shader to use instead of the standard mark shader
05887                                                                                         markShader = client->saber[saberNum].g2MarksShader;
05888                                                                                 }
05889                                                                                 if ( client->saber[saberNum].g2WeaponMarkShader )
05890                                                                                 {//we have a shader to use as a splashback onto the weapon model
05891                                                                                         weaponMarkShader = client->saber[saberNum].g2WeaponMarkShader;
05892                                                                                 }
05893                                                                         }
05894                                                                 }
05895                                                                 CG_AddGhoul2Mark(markShader, flrand(3.0f, 4.0f),
05896                                                                         trace.endpos, ePos, trace.entityNum, trEnt->lerpOrigin, trEnt->lerpAngles[YAW],
05897                                                                         trEnt->ghoul2, trEnt->modelScale, Q_irand(5000, 10000));
05898                                                                 if ( weaponMarkShader )
05899                                                                 {
05900                                                                         vec3_t splashBackDir;
05901                                                                         VectorScale( ePos, -1 , splashBackDir );
05902                                                                         CG_AddGhoul2Mark(weaponMarkShader, flrand(0.5f, 2.0f),
05903                                                                                 trace.endpos, splashBackDir, owner->currentState.clientNum, owner->lerpOrigin, owner->lerpAngles[YAW],
05904                                                                                 owner->ghoul2, owner->modelScale, Q_irand(5000, 10000));
05905                                                                 }
05906                                                         }
05907                                                 }
05908                                         }
05909                                 }
05910                         }
05911                         else
05912                         { //otherwise, we're all set.
05913                                 doEffect = qtrue;
05914                         }
05915 
05916                         if (doEffect)
05917                         {
05918                                 int hitPersonFxID = cgs.effects.mSaberBloodSparks;
05919                                 int hitOtherFxID = cgs.effects.mSaberCut;
05920 
05921                                 if (owner->currentState.eType == ET_NPC)
05922                                 {
05923                                         client = owner->npcClient;
05924                                 }
05925                                 else
05926                                 {
05927                                         client = &cgs.clientinfo[owner->currentState.clientNum];
05928                                 }
05929                                 if ( client && client->infoValid )
05930                                 {
05931                                         if ( WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) )
05932                                         {//use second blade style values
05933                                                 if ( client->saber[saberNum].hitPersonEffect2 )
05934                                                 {
05935                                                         hitPersonFxID = client->saber[saberNum].hitPersonEffect2;
05936                                                 }
05937                                                 if ( client->saber[saberNum].hitOtherEffect2 )
05938                                                 {//custom hit other effect
05939                                                         hitOtherFxID = client->saber[saberNum].hitOtherEffect2;
05940                                                 }
05941                                         }
05942                                         else
05943                                         {//use first blade style values
05944                                                 if ( client->saber[saberNum].hitPersonEffect )
05945                                                 {
05946                                                         hitPersonFxID = client->saber[saberNum].hitPersonEffect;
05947                                                 }
05948                                                 if ( client->saber[saberNum].hitOtherEffect )
05949                                                 {//custom hit other effect
05950                                                         hitOtherFxID = client->saber[saberNum].hitOtherEffect;
05951                                                 }
05952                                         }
05953                                 }
05954                                 if (!trace.plane.normal[0] && !trace.plane.normal[1] && !trace.plane.normal[2])
05955                                 { //who cares, just shoot it somewhere.
05956                                         trace.plane.normal[1] = 1;
05957                                 }
05958 
05959                                 if (owner->serverSaberFleshImpact)
05960                                 { //do standard player/live ent hit sparks
05961                                         trap_FX_PlayEffectID( hitPersonFxID, trace.endpos, trace.plane.normal, -1, -1 );
05962                                         //trap_S_StartSound(trace.endpos, trace.entityNum, CHAN_AUTO, trap_S_RegisterSound(va("sound/weapons/saber/saberhit%i.wav", Q_irand(1, 3))));
05963                                 }
05964                                 else
05965                                 { //do the cut effect
05966                                         trap_FX_PlayEffectID( hitOtherFxID, trace.endpos, trace.plane.normal, -1, -1 );
05967                                 }
05968                                 doEffect = qfalse;
05969                         }
05970                 }
05971 
05972                 /*
05973                 if (!backWards)
05974                 {
05975                         backWards = qtrue;
05976                 }
05977                 else
05978                 {
05979                         doneWithTraces = qtrue;
05980                 }
05981                 */
05982                 doneWithTraces = qtrue; //disabling backwards tr for now, sometimes it just makes too many effects.
05983         }
05984 }
05985 
05986 #define SABER_TRAIL_TIME        40.0f
05987 #define FX_USE_ALPHA            0x08000000
05988 
05989 #include "../namespace_begin.h"
05990 qboolean BG_SuperBreakWinAnim( int anim );
05991 #include "../namespace_end.h"
05992 
05993 void CG_AddSaberBlade( centity_t *cent, centity_t *scent, refEntity_t *saber, int renderfx, int modelIndex, int saberNum, int bladeNum, vec3_t origin, vec3_t angles, qboolean fromSaber, qboolean dontDraw)
05994 {
05995         vec3_t  org_, end, v,
05996                         axis_[3] = {0,0,0, 0,0,0, 0,0,0};       // shut the compiler up
05997         trace_t trace;
05998         int i = 0;
05999         int trailDur;
06000         float saberLen;
06001         float diff;
06002         clientInfo_t *client;
06003         centity_t *saberEnt;
06004         saberTrail_t *saberTrail;
06005         mdxaBone_t      boltMatrix;
06006         vec3_t futureAngles;
06007         effectTrailArgStruct_t fx;
06008         int scolor = 0;
06009         int     useModelIndex = 0;
06010 
06011         if (cent->currentState.eType == ET_NPC)
06012         {
06013                 client = cent->npcClient;
06014                 assert(client);
06015         }
06016         else
06017         {
06018                 client = &cgs.clientinfo[cent->currentState.number];
06019         }
06020 
06021         saberEnt = &cg_entities[cent->currentState.saberEntityNum];
06022         saberLen = client->saber[saberNum].blade[bladeNum].length;
06023 
06024         if (saberLen <= 0 && !dontDraw)
06025         { //don't bother then.
06026                 return;
06027         }
06028 
06029         futureAngles[YAW] = angles[YAW];
06030         futureAngles[PITCH] = angles[PITCH];
06031         futureAngles[ROLL] = angles[ROLL];
06032 
06033 
06034         if ( fromSaber )
06035         {
06036                 useModelIndex = 0;
06037         }
06038         else
06039         {
06040                 useModelIndex = saberNum+1;
06041         }
06042         //Assume bladeNum is equal to the bolt index because bolts should be added in order of the blades.
06043         //if there is an effect on this blade, play it
06044         if (  !WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum )
06045                         && client->saber[saberNum].bladeEffect )
06046         {
06047                 trap_FX_PlayBoltedEffectID(client->saber[saberNum].bladeEffect, scent->lerpOrigin, 
06048                         scent->ghoul2, bladeNum, scent->currentState.number, useModelIndex, -1, qfalse);
06049         }
06050         else if ( WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum )
06051                         && client->saber[saberNum].bladeEffect2 )
06052         {
06053                 trap_FX_PlayBoltedEffectID(client->saber[saberNum].bladeEffect2, scent->lerpOrigin, 
06054                         scent->ghoul2, bladeNum, scent->currentState.number, useModelIndex, -1, qfalse);
06055         }
06056         //get the boltMatrix
06057         trap_G2API_GetBoltMatrix(scent->ghoul2, useModelIndex, bladeNum, &boltMatrix, futureAngles, origin, cg.time, cgs.gameModels, scent->modelScale);
06058 
06059         // work the matrix axis stuff into the original axis and origins used.
06060         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, org_);
06061         BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, axis_[0]);
06062 
06063         if (!fromSaber && saberEnt && !cent->currentState.saberInFlight)
06064         {
06065                 VectorCopy(org_, saberEnt->currentState.pos.trBase);
06066 
06067                 VectorCopy(axis_[0], saberEnt->currentState.apos.trBase);
06068         }
06069 
06070         VectorMA( org_, saberLen, axis_[0], end );
06071         
06072         VectorAdd( end, axis_[0], end );
06073 
06074         if (cent->currentState.eType == ET_NPC)
06075         {
06076                 scolor = client->saber[saberNum].blade[bladeNum].color;
06077         }
06078         else
06079         {
06080                 if (saberNum == 0)
06081                 {
06082                         scolor = client->icolor1;
06083                 }
06084                 else
06085                 {
06086                         scolor = client->icolor2;
06087                 }
06088         }
06089 
06090         if (cgs.gametype >= GT_TEAM &&
06091                 cgs.gametype != GT_SIEGE &&
06092                 !cgs.jediVmerc &&
06093                 cent->currentState.eType != ET_NPC)
06094         {
06095                 if (client->team == TEAM_RED)
06096                 {
06097                         scolor = SABER_RED;
06098                 }
06099                 else if (client->team == TEAM_BLUE)
06100                 {
06101                         scolor = SABER_BLUE;
06102                 }
06103         }
06104 
06105         if (!cg_saberContact.integer)
06106         { //if we don't have saber contact enabled, just add the blade and don't care what it's touching
06107                 goto CheckTrail;
06108         }
06109 
06110         if (!dontDraw)
06111         {
06112                 if (cg_saberModelTraceEffect.integer)
06113                 {
06114                         CG_G2SaberEffects(org_, end, cent);
06115                 }
06116                 else if (cg_saberClientVisualCompensation.integer)
06117                 {
06118                         CG_Trace( &trace, org_, NULL, NULL, end, ENTITYNUM_NONE, MASK_SOLID );
06119 
06120                         if (trace.fraction != 1)
06121                         { //nudge the endpos a very small amount from the beginning to the end, so the comp trace hits at the end.
06122                         //I'm only bothering with this because I want to do a backwards trace too in the comp trace, so if the
06123                         //blade is sticking through a player or something the standard trace doesn't it, it will make sparks
06124                         //on each side.
06125                                 vec3_t seDif;
06126 
06127                                 VectorSubtract(trace.endpos, org_, seDif);
06128                                 VectorNormalize(seDif);
06129                                 trace.endpos[0] += seDif[0]*0.1f;
06130                                 trace.endpos[1] += seDif[1]*0.1f;
06131                                 trace.endpos[2] += seDif[2]*0.1f;
06132                         }
06133 
06134                         if (client->saber[saberNum].blade[bladeNum].storageTime < cg.time)
06135                         { //debounce it in case our framerate is absurdly high. Using storageTime since it's not used for anything else in the client.
06136                                 CG_SaberCompWork(org_, trace.endpos, cent, saberNum, bladeNum);
06137 
06138                                 client->saber[saberNum].blade[bladeNum].storageTime = cg.time + 5;
06139                         }
06140                 }
06141 
06142                 for ( 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
06143                 {
06144                         if ( i )
06145                         {//tracing from end to base
06146                                 CG_Trace( &trace, end, NULL, NULL, org_, ENTITYNUM_NONE, MASK_SOLID );
06147                         }
06148                         else
06149                         {//tracing from base to end
06150                                 CG_Trace( &trace, org_, NULL, NULL, end, ENTITYNUM_NONE, MASK_SOLID );
06151                         }
06152                         
06153                         if ( trace.fraction < 1.0f )
06154                         {
06155                                 vec3_t trDir;
06156                                 VectorCopy(trace.plane.normal, trDir);
06157                                 if (!trDir[0] && !trDir[1] && !trDir[2])
06158                                 {
06159                                         trDir[1] = 1;
06160                                 }
06161 
06162                                 if ( (client->saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS) )
06163                                 {//don't actually draw the marks/impact effects
06164                                 }
06165                                 else
06166                                 {
06167                                         if (!(trace.surfaceFlags & SURF_NOIMPACT) ) // never spark on sky
06168                                         {
06169                                                 trap_FX_PlayEffectID( cgs.effects.mSparks, trace.endpos, trDir, -1, -1 );
06170                                         }
06171                                 }
06172 
06173                                 //Stop saber? (it wouldn't look right if it was stuck through a thin wall and unable to hurt players on the other side)
06174                                 VectorSubtract(org_, trace.endpos, v);
06175                                 saberLen = VectorLength(v);
06176 
06177                                 VectorCopy(trace.endpos, end);
06178 
06179                                 if ( (client->saber[saberNum].saberFlags2&SFL2_NO_WALL_MARKS) )
06180                                 {//don't actually draw the marks
06181                                 }
06182                                 else
06183                                 {//draw marks if we hit a wall
06184                                         // All I need is a bool to mark whether I have a previous point to work with.
06185                                         //....come up with something better..
06186                                         if ( client->saber[saberNum].blade[bladeNum].trail.haveOldPos[i] )
06187                                         {
06188                                                 if ( trace.entityNum == ENTITYNUM_WORLD || cg_entities[trace.entityNum].currentState.eType == ET_TERRAIN || (cg_entities[trace.entityNum].currentState.eFlags & EF_PERMANENT) )
06189                                                 {//only put marks on architecture
06190                                                         // Let's do some cool burn/glowing mark bits!!!
06191                                                         CG_CreateSaberMarks( client->saber[saberNum].blade[bladeNum].trail.oldPos[i], trace.endpos, trace.plane.normal );
06192                                                 
06193                                                         //make a sound
06194                                                         if ( cg.time - client->saber[saberNum].blade[bladeNum].hitWallDebounceTime >= 100 )
06195                                                         {//ugh, need to have a real sound debouncer... or do this game-side
06196                                                                 client->saber[saberNum].blade[bladeNum].hitWallDebounceTime = cg.time;
06197                                                                 trap_S_StartSound ( trace.endpos, -1, CHAN_WEAPON, trap_S_RegisterSound( va("sound/weapons/saber/saberhitwall%i", Q_irand(1, 3)) ) );
06198                                                         }
06199                                                 }
06200                                         }
06201                                         else
06202                                         {
06203                                                 // if we impact next frame, we'll mark a slash mark
06204                                                 client->saber[saberNum].blade[bladeNum].trail.haveOldPos[i] = qtrue;
06205                 //                              CG_ImpactMark( cgs.media.rivetMarkShader, client->saber[saberNum].blade[bladeNum].trail.oldPos[i], client->saber[saberNum].blade[bladeNum].trail.oldNormal[i],
06206                 //                                              0.0f, 1.0f, 1.0f, 1.0f, 1.0f, qfalse, 1.1f, qfalse );
06207                                         }
06208                                 }
06209 
06210                                 // stash point so we can connect-the-dots later
06211                                 VectorCopy( trace.endpos, client->saber[saberNum].blade[bladeNum].trail.oldPos[i] );
06212                                 VectorCopy( trace.plane.normal, client->saber[saberNum].blade[bladeNum].trail.oldNormal[i] );
06213                         }
06214                         else
06215                         {
06216                                 if ( client->saber[saberNum].blade[bladeNum].trail.haveOldPos[i] )
06217                                 {
06218                                         // Hmmm, no impact this frame, but we have an old point
06219                                         // Let's put the mark there, we should use an endcap mark to close the line, but we 
06220                                         //      can probably just get away with a round mark
06221         //                                      CG_ImpactMark( cgs.media.rivetMarkShader, client->saber[saberNum].blade[bladeNum].trail.oldPos[i], client->saber[saberNum].blade[bladeNum].trail.oldNormal[i],
06222         //                                                      0.0f, 1.0f, 1.0f, 1.0f, 1.0f, qfalse, 1.1f, qfalse );
06223                                 }
06224 
06225                                 // we aren't impacting, so turn off our mark tracking mechanism
06226                                 client->saber[saberNum].blade[bladeNum].trail.haveOldPos[i] = qfalse;
06227                         }
06228                 }
06229         }
06230 CheckTrail:
06231 
06232         if (!cg_saberTrail.integer)
06233         { //don't do the trail in this case
06234                 goto JustDoIt;
06235         }
06236 
06237         if ( (!WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) && client->saber[saberNum].trailStyle > 1 )
06238                  || ( WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) && client->saber[saberNum].trailStyle2 > 1 ) )
06239         {//don't actually draw the trail at all
06240                 goto JustDoIt;
06241         }
06242 
06243         //FIXME: if trailStyle is 1, use the motion blur instead
06244 
06245         saberTrail = &client->saber[saberNum].blade[bladeNum].trail;
06246         saberTrail->duration = saberMoveData[cent->currentState.saberMove].trailLength;
06247 
06248         trailDur = (saberTrail->duration/5.0f);
06249         if (!trailDur)
06250         { //hmm.. ok, default
06251                 if ( BG_SuperBreakWinAnim(cent->currentState.torsoAnim) )
06252                 {
06253                         trailDur = 150;
06254                 }
06255                 else
06256                 {
06257                         trailDur = SABER_TRAIL_TIME;
06258                 }
06259         }
06260 
06261         // if we happen to be timescaled or running in a high framerate situation, we don't want to flood
06262         //      the system with very small trail slices...but perhaps doing it by distance would yield better results?
06263         if ( cg.time > saberTrail->lastTime + 2 || cg_saberTrail.integer == 2 ) // 2ms
06264         {
06265                 if (!dontDraw)
06266                 {
06267                         if ( (BG_SuperBreakWinAnim(cent->currentState.torsoAnim) || saberMoveData[cent->currentState.saberMove].trailLength > 0 || ((cent->currentState.powerups & (1 << PW_SPEED) && cg_speedTrail.integer)) || (cent->currentState.saberInFlight && saberNum == 0)) && cg.time < saberTrail->lastTime + 2000 ) // if we have a stale segment, don't draw until we have a fresh one
06268                         {
06269         #if 0
06270                                 if (cg_saberTrail.integer == 2 && cg_shadows.integer != 2 && cgs.glconfig.stencilBits >= 4)
06271                                 {
06272                                         polyVert_t      verts[4];
06273 
06274                                         VectorCopy( org_, verts[0].xyz );
06275                                         VectorMA( end, 3.0f, axis_[0], verts[1].xyz );
06276                                         VectorCopy( saberTrail->tip, verts[2].xyz );
06277                                         VectorCopy( saberTrail->base, verts[3].xyz );
06278 
06279                                         //tc doesn't even matter since we're just gonna stencil an outline, but whatever.
06280                                         verts[0].st[0] = 0;     
06281                                         verts[0].st[1] = 0;     
06282                                         verts[0].modulate[0] = 255;     
06283                                         verts[0].modulate[1] = 255;     
06284                                         verts[0].modulate[2] = 255;     
06285                                         verts[0].modulate[3] = 255;
06286 
06287                                         verts[1].st[0] = 0;     
06288                                         verts[1].st[1] = 1;     
06289                                         verts[1].modulate[0] = 255;     
06290                                         verts[1].modulate[1] = 255;     
06291                                         verts[1].modulate[2] = 255;     
06292                                         verts[1].modulate[3] = 255;
06293 
06294                                         verts[2].st[0] = 1;     
06295                                         verts[2].st[1] = 1;     
06296                                         verts[2].modulate[0] = 255;     
06297                                         verts[2].modulate[1] = 255;     
06298                                         verts[2].modulate[2] = 255;     
06299                                         verts[2].modulate[3] = 255;
06300 
06301                                         verts[3].st[0] = 1;     
06302                                         verts[3].st[1] = 0;     
06303                                         verts[3].modulate[0] = 255;     
06304                                         verts[3].modulate[1] = 255;     
06305                                         verts[3].modulate[2] = 255;     
06306                                         verts[3].modulate[3] = 255;
06307 
06308                                         //don't capture postrender objects (now we'll postrender the saber so it doesn't get in the capture)
06309                                         trap_R_SetRefractProp(1.0f, 0.0f, qtrue, qtrue);
06310 
06311                                         //shader 2 is always the crazy refractive shader.
06312                                         trap_R_AddPolyToScene( 2, 4, verts );
06313                                 }
06314                                 else
06315         #endif
06316                                 {
06317                                         vec3_t  rgb1={255.0f,255.0f,255.0f};
06318 
06319                                         switch( scolor )
06320                                         {
06321                                                 case SABER_RED:
06322                                                         VectorSet( rgb1, 255.0f, 0.0f, 0.0f );
06323                                                         break;
06324                                                 case SABER_ORANGE:
06325                                                         VectorSet( rgb1, 255.0f, 64.0f, 0.0f );
06326                                                         break;
06327                                                 case SABER_YELLOW:
06328                                                         VectorSet( rgb1, 255.0f, 255.0f, 0.0f );
06329                                                         break;
06330                                                 case SABER_GREEN:
06331                                                         VectorSet( rgb1, 0.0f, 255.0f, 0.0f );
06332                                                         break;
06333                                                 case SABER_BLUE:
06334                                                         VectorSet( rgb1, 0.0f, 64.0f, 255.0f );
06335                                                         break;
06336                                                 case SABER_PURPLE:
06337                                                         VectorSet( rgb1, 220.0f, 0.0f, 255.0f );
06338                                                         break;
06339                                                 default:
06340                                                         VectorSet( rgb1, 0.0f, 64.0f, 255.0f );
06341                                                         break;
06342                                         }
06343 
06344                                         //Here we will use the happy process of filling a struct in with arguments and passing it to a trap function
06345                                         //so that we can take the struct and fill in an actual CTrail type using the data within it once we get it
06346                                         //into the effects area
06347 
06348                                         // Go from new muzzle to new end...then to old end...back down to old muzzle...finally
06349                                         //      connect back to the new muzzle...this is our trail quad
06350                                         VectorCopy( org_, fx.mVerts[0].origin );
06351                                         VectorMA( end, 3.0f, axis_[0], fx.mVerts[1].origin );
06352 
06353                                         VectorCopy( saberTrail->tip, fx.mVerts[2].origin );
06354                                         VectorCopy( saberTrail->base, fx.mVerts[3].origin );
06355 
06356                                         diff = cg.time - saberTrail->lastTime;
06357 
06358                                         // I'm not sure that clipping this is really the best idea
06359                                         //This prevents the trail from showing at all in low framerate situations.
06360                                         //if ( diff <= SABER_TRAIL_TIME * 2 )
06361                                         if ( diff <= 10000 )
06362                                         { //don't draw it if the last time is way out of date
06363                                                 float oldAlpha = 1.0f - ( diff / trailDur );
06364 
06365                                                 if (cg_saberTrail.integer == 2 && cg_shadows.integer != 2 && cgs.glconfig.stencilBits >= 4)
06366                                                 {//does other stuff below
06367                                                 }
06368                                                 else
06369                                                 {
06370                                                         if ( (!WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) && client->saber[saberNum].trailStyle == 1 )
06371                                                                 || ( WP_SaberBladeUseSecondBladeStyle( &client->saber[saberNum], bladeNum ) && client->saber[saberNum].trailStyle2 == 1 ) )
06372                                                         {//motion trail
06373                                                                 fx.mShader = cgs.media.swordTrailShader;
06374                                                                 VectorSet( rgb1, 32.0f, 32.0f, 32.0f ); // make the sith sword trail pretty faint
06375                                                                 trailDur *= 2.0f; // stay around twice as long?
06376                                                         } 
06377                                                         else 
06378                                                         { 
06379                                                                 fx.mShader = cgs.media.saberBlurShader;
06380                                                         }
06381                                                         fx.mKillTime = trailDur;
06382                                                         fx.mSetFlags = FX_USE_ALPHA;
06383                                                 }
06384 
06385                                                 // New muzzle
06386                                                 VectorCopy( rgb1, fx.mVerts[0].rgb );
06387                                                 fx.mVerts[0].alpha = 255.0f;
06388 
06389                                                 fx.mVerts[0].ST[0] = 0.0f;
06390                                                 fx.mVerts[0].ST[1] = 1.0f;
06391                                                 fx.mVerts[0].destST[0] = 1.0f;
06392                                                 fx.mVerts[0].destST[1] = 1.0f;
06393 
06394                                                 // new tip
06395                                                 VectorCopy( rgb1, fx.mVerts[1].rgb );
06396                                                 fx.mVerts[1].alpha = 255.0f;
06397                                                 
06398                                                 fx.mVerts[1].ST[0] = 0.0f;
06399                                                 fx.mVerts[1].ST[1] = 0.0f;
06400                                                 fx.mVerts[1].destST[0] = 1.0f;
06401                                                 fx.mVerts[1].destST[1] = 0.0f;
06402 
06403                                                 // old tip
06404                                                 VectorCopy( rgb1, fx.mVerts[2].rgb );
06405                                                 fx.mVerts[2].alpha = 255.0f;
06406 
06407                                                 fx.mVerts[2].ST[0] = 1.0f - oldAlpha; // NOTE: this just happens to contain the value I want
06408                                                 fx.mVerts[2].ST[1] = 0.0f;
06409                                                 fx.mVerts[2].destST[0] = 1.0f + fx.mVerts[2].ST[0];
06410                                                 fx.mVerts[2].destST[1] = 0.0f;
06411 
06412                                                 // old muzzle
06413                                                 VectorCopy( rgb1, fx.mVerts[3].rgb );
06414                                                 fx.mVerts[3].alpha = 255.0f;
06415 
06416                                                 fx.mVerts[3].ST[0] = 1.0f - oldAlpha; // NOTE: this just happens to contain the value I want
06417                                                 fx.mVerts[3].ST[1] = 1.0f;
06418                                                 fx.mVerts[3].destST[0] = 1.0f + fx.mVerts[2].ST[0];
06419                                                 fx.mVerts[3].destST[1] = 1.0f;
06420 
06421                                                 if (cg_saberTrail.integer == 2 && cg_shadows.integer != 2 && cgs.glconfig.stencilBits >= 4)
06422                                                 {
06423                                                         trap_R_SetRefractProp(1.0f, 0.0f, qtrue, qtrue); //don't need to do this every frame.. but..
06424 
06425                                                         if (BG_SaberInAttack(cent->currentState.saberMove)
06426                                                                 ||BG_SuperBreakWinAnim(cent->currentState.torsoAnim))
06427                                                         { //in attack, strong trail
06428                                                                 fx.mKillTime = 300;
06429                                                         }
06430                                                         else
06431                                                         { //faded trail
06432                                                                 fx.mKillTime = 40;
06433                                                         }
06434                                                         fx.mShader = 2; //2 is always refractive shader
06435                                                         fx.mSetFlags = FX_USE_ALPHA;
06436                                                 }
06437                                                 /*
06438                                                 else
06439                                                 {
06440                                                         fx.mShader = cgs.media.saberBlurShader;
06441                                                         fx.mKillTime = trailDur;
06442                                                         fx.mSetFlags = FX_USE_ALPHA;
06443                                                 }
06444                                                 */
06445 
06446                                                 trap_FX_AddPrimitive(&fx);
06447                                         }
06448                                 }
06449                         }
06450                 }
06451 
06452                 // we must always do this, even if we aren't active..otherwise we won't know where to pick up from
06453                 VectorCopy( org_, saberTrail->base );
06454                 VectorMA( end, 3.0f, axis_[0], saberTrail->tip );
06455                 saberTrail->lastTime = cg.time;
06456         }
06457 
06458 JustDoIt:
06459 
06460         if (dontDraw)
06461         {
06462                 return;
06463         }
06464 
06465         if ( (client->saber[saberNum].saberFlags2&SFL2_NO_BLADE) )
06466         {//don't actually draw the blade at all
06467                 if ( client->saber[saberNum].numBlades < 3
06468                         && !(client->saber[saberNum].saberFlags2&SFL2_NO_DLIGHT) )
06469                 {//hmm, but still add the dlight
06470                         CG_DoSaberLight( &client->saber[saberNum] );
06471                 }
06472                 return;
06473         }
06474         // Pass in the renderfx flags attached to the saber weapon model...this is done so that saber glows
06475         //      will get rendered properly in a mirror...not sure if this is necessary??
06476         //CG_DoSaber( org_, axis_[0], saberLen, client->saber[saberNum].blade[bladeNum].lengthMax, client->saber[saberNum].blade[bladeNum].radius,
06477         //      scolor, renderfx, (qboolean)(saberNum==0&&bladeNum==0) );
06478         CG_DoSaber( org_, axis_[0], saberLen, client->saber[saberNum].blade[bladeNum].lengthMax, client->saber[saberNum].blade[bladeNum].radius,
06479                 scolor, renderfx, (qboolean)(client->saber[saberNum].numBlades < 3 && !(client->saber[saberNum].saberFlags2&SFL2_NO_DLIGHT)) );
06480 }
06481 
06482 int CG_IsMindTricked(int trickIndex1, int trickIndex2, int trickIndex3, int trickIndex4, int client)
06483 {
06484         int checkIn;
06485         int sub = 0;
06486 
06487         if (cg_entities[client].currentState.forcePowersActive & (1 << FP_SEE))
06488         {
06489                 return 0;
06490         }
06491 
06492         if (client > 47)
06493         {
06494                 checkIn = trickIndex4;
06495                 sub = 48;
06496         }
06497         else if (client > 31)
06498         {
06499                 checkIn = trickIndex3;
06500                 sub = 32;
06501         }
06502         else if (client > 15)
06503         {
06504                 checkIn = trickIndex2;
06505                 sub = 16;
06506         }
06507         else
06508         {
06509                 checkIn = trickIndex1;
06510         }
06511 
06512         if (checkIn & (1 << (client-sub)))
06513         {
06514                 return 1;
06515         }
06516         
06517         return 0;
06518 }
06519 
06520 #define SPEED_TRAIL_DISTANCE 6
06521 
06522 void CG_DrawPlayerSphere(centity_t *cent, vec3_t origin, float scale, int shader)
06523 {
06524         refEntity_t ent;
06525         vec3_t ang;
06526         float vLen;
06527         vec3_t viewDir;
06528         
06529         // Don't draw the shield when the player is dead.
06530         if (cent->currentState.eFlags & EF_DEAD)
06531         {
06532                 return;
06533         }
06534 
06535         memset( &ent, 0, sizeof( ent ) );
06536 
06537         VectorCopy( origin, ent.origin );
06538         ent.origin[2] += 9.0;
06539 
06540         VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]);
06541         vLen = VectorLength(ent.axis[0]);
06542         if (vLen <= 0.1f)
06543         {       // Entity is right on vieworg.  quit.
06544                 return;
06545         }
06546 
06547         VectorCopy(ent.axis[0], viewDir);
06548         VectorInverse(viewDir);
06549         VectorNormalize(viewDir);
06550 
06551         vectoangles(ent.axis[0], ang);
06552         ang[ROLL] += 180.0f;
06553         ang[PITCH] += 180.0f;
06554         AnglesToAxis(ang, ent.axis);
06555 
06556         VectorScale(ent.axis[0], scale, ent.axis[0]);
06557         VectorScale(ent.axis[1], scale, ent.axis[1]);
06558         VectorScale(ent.axis[2], scale, ent.axis[2]);
06559 
06560         ent.nonNormalizedAxes = qtrue;
06561 
06562         ent.hModel = cgs.media.halfShieldModel;
06563         ent.customShader = shader;      
06564 
06565         trap_R_AddRefEntityToScene( &ent );
06566 
06567         if (!cg.renderingThirdPerson && cent->currentState.number == cg.predictedPlayerState.clientNum)
06568         { //don't do the rest then
06569                 return;
06570         }
06571         if (!cg_renderToTextureFX.integer)
06572         {
06573                 return;
06574         }
06575 
06576         ang[PITCH] -= 180.0f;
06577         AnglesToAxis(ang, ent.axis);
06578 
06579         VectorScale(ent.axis[0], scale*0.5f, ent.axis[0]);
06580         VectorScale(ent.axis[1], scale*0.5f, ent.axis[1]);
06581         VectorScale(ent.axis[2], scale*0.5f, ent.axis[2]);
06582 
06583         ent.renderfx = (RF_DISTORTION|RF_FORCE_ENT_ALPHA);
06584         if (shader == cgs.media.invulnerabilityShader)
06585         { //ok, ok, this is a little hacky. sorry!
06586                 ent.shaderRGBA[0] = 0;
06587                 ent.shaderRGBA[1] = 255;
06588                 ent.shaderRGBA[2] = 0;
06589                 ent.shaderRGBA[3] = 100;
06590         }
06591         else if (shader == cgs.media.ysalimariShader)
06592         {
06593                 ent.shaderRGBA[0] = 255;
06594                 ent.shaderRGBA[1] = 255;
06595                 ent.shaderRGBA[2] = 0;
06596                 ent.shaderRGBA[3] = 100;
06597         }
06598         else if (shader == cgs.media.endarkenmentShader)
06599         {
06600                 ent.shaderRGBA[0] = 100;
06601                 ent.shaderRGBA[1] = 0;
06602                 ent.shaderRGBA[2] = 0;
06603                 ent.shaderRGBA[3] = 20;
06604         }
06605         else if (shader == cgs.media.enlightenmentShader)
06606         {
06607                 ent.shaderRGBA[0] = 255;
06608                 ent.shaderRGBA[1] = 255;
06609                 ent.shaderRGBA[2] = 255;
06610                 ent.shaderRGBA[3] = 20;
06611         }
06612         else
06613         { //ysal red/blue, boon
06614                 ent.shaderRGBA[0] = 255.0f;
06615                 ent.shaderRGBA[1] = 255.0f;
06616                 ent.shaderRGBA[2] = 255.0f;
06617                 ent.shaderRGBA[3] = 20;
06618         }
06619 
06620         ent.radius = 256;
06621 
06622         VectorMA(ent.origin, 40.0f, viewDir, ent.origin);
06623 
06624         ent.customShader = trap_R_RegisterShader("effects/refract_2");
06625         trap_R_AddRefEntityToScene( &ent );
06626 }
06627 
06628 void CG_AddLightningBeam(vec3_t start, vec3_t end)
06629 {
06630         vec3_t  dir, chaos,
06631                         c1, c2,
06632                         v1, v2;
06633         float   len,
06634                         s1, s2, s3;
06635 
06636         addbezierArgStruct_t b;
06637 
06638         VectorCopy(start, b.start);
06639         VectorCopy(end, b.end);
06640 
06641         VectorSubtract( b.end, b.start, dir );
06642         len = VectorNormalize( dir );
06643 
06644         // Get the base control points, we'll work from there
06645         VectorMA( b.start, 0.3333f * len, dir, c1 );
06646         VectorMA( b.start, 0.6666f * len, dir, c2 );
06647 
06648         // get some chaos values that really aren't very chaotic :)
06649         s1 = sin( cg.time * 0.005f ) * 2 + crandom() * 0.2f;
06650         s2 = sin( cg.time * 0.001f );
06651         s3 = sin( cg.time * 0.011f );
06652 
06653         VectorSet( chaos, len * 0.01f * s1,
06654                                                 len * 0.02f * s2,
06655                                                 len * 0.04f * (s1 + s2 + s3));
06656 
06657         VectorAdd( c1, chaos, c1 );
06658         VectorScale( chaos, 4.0f, v1 );
06659 
06660         VectorSet( chaos, -len * 0.02f * s3,
06661                                                 len * 0.01f * (s1 * s2),
06662                                                 -len * 0.02f * (s1 + s2 * s3));
06663 
06664         VectorAdd( c2, chaos, c2 );
06665         VectorScale( chaos, 2.0f, v2 );
06666 
06667         VectorSet( chaos, 1.0f, 1.0f, 1.0f );
06668 
06669         VectorCopy(c1, b.control1);
06670         VectorCopy(vec3_origin, b.control1Vel);
06671         VectorCopy(c2, b.control2);
06672         VectorCopy(vec3_origin, b.control2Vel);
06673 
06674         b.size1 = 6.0f;
06675         b.size2 = 6.0f;
06676         b.sizeParm = 0.0f;
06677         b.alpha1 = 0.0f;
06678         b.alpha2 = 0.2f;
06679         b.alphaParm = 0.5f;
06680         
06681         /*
06682         VectorCopy(WHITE, b.sRGB);
06683         VectorCopy(WHITE, b.eRGB);
06684         */
06685 
06686         b.sRGB[0] = 255;
06687         b.sRGB[1] = 255;
06688         b.sRGB[2] = 255;
06689         VectorCopy(b.sRGB, b.eRGB);
06690 
06691         b.rgbParm = 0.0f;
06692         b.killTime = 50;
06693         b.shader = trap_R_RegisterShader( "gfx/misc/electric2" );
06694         b.flags = 0x00000001; //FX_ALPHA_LINEAR
06695 
06696         trap_FX_AddBezier(&b);
06697 }
06698 
06699 void CG_AddRandomLightning(vec3_t start, vec3_t end)
06700 {
06701         vec3_t inOrg, outOrg;
06702 
06703         VectorCopy(start, inOrg);
06704         VectorCopy(end, outOrg);
06705 
06706         if ( rand() & 1 )
06707         {
06708                 outOrg[0] += Q_irand(0, 24);
06709                 inOrg[0] += Q_irand(0, 8);
06710         }
06711         else
06712         {
06713                 outOrg[0] -= Q_irand(0, 24);
06714                 inOrg[0] -= Q_irand(0, 8);
06715         }
06716 
06717         if ( rand() & 1 )
06718         {
06719                 outOrg[1] += Q_irand(0, 24);
06720                 inOrg[1] += Q_irand(0, 8);
06721         }
06722         else
06723         {
06724                 outOrg[1] -= Q_irand(0, 24);
06725                 inOrg[1] -= Q_irand(0, 8);
06726         }
06727 
06728         if ( rand() & 1 )
06729         {
06730                 outOrg[2] += Q_irand(0, 50);
06731                 inOrg[2] += Q_irand(0, 40);
06732         }
06733         else
06734         {
06735                 outOrg[2] -= Q_irand(0, 64);
06736                 inOrg[2] -= Q_irand(0, 40);
06737         }
06738 
06739         CG_AddLightningBeam(inOrg, outOrg);
06740 }
06741 
06742 extern char *forceHolocronModels[];
06743 
06744 qboolean CG_ThereIsAMaster(void)
06745 {
06746         int i = 0;
06747         centity_t *cent;
06748 
06749         while (i < MAX_CLIENTS)
06750         {
06751                 cent = &cg_entities[i];
06752 
06753                 if (cent && cent->currentState.isJediMaster)
06754                 {
06755                         return qtrue;
06756                 }
06757 
06758                 i++;
06759         }
06760 
06761         return qfalse;
06762 }
06763 
06764 #if 0
06765 void CG_DrawNoForceSphere(centity_t *cent, vec3_t origin, float scale, int shader)
06766 {
06767         refEntity_t ent;
06768         
06769         // Don't draw the shield when the player is dead.
06770         if (cent->currentState.eFlags & EF_DEAD)
06771         {
06772                 return;
06773         }
06774 
06775         memset( &ent, 0, sizeof( ent ) );
06776 
06777         VectorCopy( origin, ent.origin );
06778         ent.origin[2] += 9.0;
06779 
06780         VectorSubtract(cg.refdef.vieworg, ent.origin, ent.axis[0]);
06781         if (VectorNormalize(ent.axis[0]) <= 0.1f)
06782         {       // Entity is right on vieworg.  quit.
06783                 return;
06784         }
06785 
06786         VectorCopy(cg.refdef.viewaxis[2], ent.axis[2]);
06787         CrossProduct(ent.axis[0], ent.axis[2], ent.axis[1]);
06788 
06789         VectorScale(ent.axis[0], scale, ent.axis[0]);
06790         VectorScale(ent.axis[1], scale, ent.axis[1]);
06791         VectorScale(ent.axis[2], -scale, ent.axis[2]);
06792 
06793         ent.shaderRGBA[3] = (cent->currentState.genericenemyindex - cg.time)/8;
06794         ent.renderfx |= RF_RGB_TINT;
06795         if (ent.shaderRGBA[3] > 200)
06796         {
06797                 ent.shaderRGBA[3] = 200;
06798         }
06799         if (ent.shaderRGBA[3] < 1)
06800         {
06801                 ent.shaderRGBA[3] = 1;
06802         }
06803 
06804         ent.shaderRGBA[2] = 0;
06805         ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[3];
06806 
06807         ent.hModel = cgs.media.halfShieldModel;
06808         ent.customShader = shader;      
06809 
06810         trap_R_AddRefEntityToScene( &ent );
06811 }
06812 #endif
06813 
06814 //Checks to see if the model string has a * appended with a custom skin name after.
06815 //If so, it terminates the model string correctly, parses the skin name out, and returns
06816 //the handle of the registered skin.
06817 int CG_HandleAppendedSkin(char *modelName)
06818 {
06819         char skinName[MAX_QPATH];
06820         char *p;
06821         qhandle_t skinID = 0;
06822         int i = 0;
06823 
06824         //see if it has a skin name
06825         p = Q_strrchr(modelName, '*');
06826 
06827         if (p)
06828         { //found a *, we should have a model name before it and a skin name after it.
06829                 *p = 0; //terminate the modelName string at this point, then go ahead and parse to the next 0 for the skin.
06830                 p++;
06831 
06832                 while (p && *p)
06833                 {
06834                         skinName[i] = *p;
06835                         i++;
06836                         p++;
06837                 }
06838                 skinName[i] = 0;
06839 
06840                 if (skinName[0]) 
06841                 { //got it, register the skin under the model path.
06842                         char baseFolder[MAX_QPATH];
06843 
06844                         strcpy(baseFolder, modelName);
06845                         p = Q_strrchr(baseFolder, '/'); //go back to the first /, should be the path point
06846 
06847                         if (p)
06848                         { //got it.. terminate at the slash and register.
06849                                 char *useSkinName;
06850 
06851                                 *p = 0;
06852 
06853                                 if (strchr(skinName, '|'))
06854                                 {//three part skin
06855                                         useSkinName = va("%s/|%s", baseFolder, skinName);
06856                                 }
06857                                 else
06858                                 {
06859                                         useSkinName = va("%s/model_%s.skin", baseFolder, skinName);
06860                                 }
06861 
06862                                 skinID = trap_R_RegisterSkin(useSkinName);
06863                         }
06864                 }
06865         }
06866 
06867         return skinID;
06868 }
06869 
06870 //Create a temporary ghoul2 instance and get the gla name so we can try loading animation data and sounds.
06871 #include "../namespace_begin.h"
06872 void BG_GetVehicleModelName(char *modelname);
06873 void BG_GetVehicleSkinName(char *skinname);
06874 #include "../namespace_end.h"
06875 
06876 void CG_CacheG2AnimInfo(char *modelName)
06877 {
06878         void *g2 = NULL;
06879         char *slash;
06880         char useModel[MAX_QPATH];
06881         char useSkin[MAX_QPATH];
06882         int animIndex;
06883 
06884         strcpy(useModel, modelName);
06885         strcpy(useSkin, modelName);
06886 
06887         if (modelName[0] == '$')
06888         { //it's a vehicle name actually, let's precache the whole vehicle
06889                 BG_GetVehicleModelName(useModel);
06890                 BG_GetVehicleSkinName(useSkin);
06891                 if ( useSkin[0] )
06892                 { //use a custom skin
06893                         trap_R_RegisterSkin(va("models/players/%s/model_%s.skin", useModel, useSkin));
06894                 }
06895                 else
06896                 {
06897                         trap_R_RegisterSkin(va("models/players/%s/model_default.skin", useModel));
06898                 }
06899                 strcpy(useModel, va("models/players/%s/model.glm", useModel));
06900         }
06901 
06902         trap_G2API_InitGhoul2Model(&g2, useModel, 0, 0, 0, 0, 0);
06903 
06904         if (g2)
06905         {
06906                 char GLAName[MAX_QPATH];
06907                 char originalModelName[MAX_QPATH];
06908 
06909                 animIndex = -1;
06910 
06911                 GLAName[0] = 0;
06912                 trap_G2API_GetGLAName(g2, 0, GLAName);
06913 
06914                 strcpy(originalModelName, useModel);
06915                         
06916                 slash = Q_strrchr( GLAName, '/' );
06917                 if ( slash )
06918                 {
06919                         strcpy(slash, "/animation.cfg");
06920 
06921                         animIndex = BG_ParseAnimationFile(GLAName, NULL, qfalse);
06922                 }
06923 
06924                 if (animIndex != -1)
06925                 {
06926                         slash = Q_strrchr( originalModelName, '/' );
06927                         if ( slash )
06928                         {
06929                                 slash++;
06930                                 *slash = 0;
06931                         }
06932 
06933                         BG_ParseAnimationEvtFile(originalModelName, animIndex, bgNumAnimEvents);
06934                 }
06935 
06936                 //Now free the temp instance
06937                 trap_G2API_CleanGhoul2Models(&g2);
06938         }
06939 }
06940 
06941 static void CG_RegisterVehicleAssets( Vehicle_t *pVeh )
06942 {
06943         /*
06944         if ( pVeh->m_pVehicleInfo->exhaustFX )
06945         {
06946                 pVeh->m_pVehicleInfo->iExhaustFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->exhaustFX );
06947         }
06948         if ( pVeh->m_pVehicleInfo->trailFX )
06949         {
06950                 pVeh->m_pVehicleInfo->iTrailFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->trailFX );
06951         }
06952         if ( pVeh->m_pVehicleInfo->impactFX )
06953         {
06954                 pVeh->m_pVehicleInfo->iImpactFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->impactFX );
06955         }
06956         if ( pVeh->m_pVehicleInfo->explodeFX )
06957         {
06958                 pVeh->m_pVehicleInfo->iExplodeFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->explodeFX );
06959         }
06960         if ( pVeh->m_pVehicleInfo->wakeFX )
06961         {
06962                 pVeh->m_pVehicleInfo->iWakeFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->wakeFX );
06963         }
06964 
06965         if ( pVeh->m_pVehicleInfo->dmgFX )
06966         {
06967                 pVeh->m_pVehicleInfo->iDmgFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->dmgFX );
06968         }
06969         if ( pVeh->m_pVehicleInfo->wpn1FX )
06970         {
06971                 pVeh->m_pVehicleInfo->iWpn1FX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->wpn1FX );
06972         }
06973         if ( pVeh->m_pVehicleInfo->wpn2FX )
06974         {
06975                 pVeh->m_pVehicleInfo->iWpn2FX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->wpn2FX );
06976         }
06977         if ( pVeh->m_pVehicleInfo->wpn1FireFX )
06978         {
06979                 pVeh->m_pVehicleInfo->iWpn1FireFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->wpn1FireFX );
06980         }
06981         if ( pVeh->m_pVehicleInfo->wpn2FireFX )
06982         {
06983                 pVeh->m_pVehicleInfo->iWpn2FireFX = trap_FX_RegisterEffect( pVeh->m_pVehicleInfo->wpn2FireFX );
06984         }
06985         */
06986 }
06987 
06988 extern void CG_HandleNPCSounds(centity_t *cent);
06989 
06990 #include "../namespace_begin.h"
06991 extern void G_CreateAnimalNPC( Vehicle_t **pVeh, const char *strAnimalType );
06992 extern void G_CreateSpeederNPC( Vehicle_t **pVeh, const char *strType );
06993 extern void G_CreateWalkerNPC( Vehicle_t **pVeh, const char *strAnimalType );
06994 extern void G_CreateFighterNPC( Vehicle_t **pVeh, const char *strType );
06995 #include "../namespace_end.h"
06996 
06997 extern playerState_t *cgSendPS[MAX_GENTITIES];
06998 void CG_G2AnimEntModelLoad(centity_t *cent)
06999 {
07000         const char *cModelName = CG_ConfigString( CS_MODELS+cent->currentState.modelindex );
07001 
07002         if (!cent->npcClient)
07003         { //have not init'd client yet
07004                 return;
07005         }
07006 
07007         if (cModelName && cModelName[0])
07008         {
07009                 char modelName[MAX_QPATH];
07010                 int skinID;
07011                 char *slash;
07012 
07013                 strcpy(modelName, cModelName);
07014 
07015                 if (cent->currentState.NPC_class == CLASS_VEHICLE && modelName[0] == '$')
07016                 { //vehicles pass their veh names over as model names, then we get the model name from the veh type
07017                         //create a vehicle object clientside for this type
07018                         char *vehType = &modelName[1];
07019                         int iVehIndex = BG_VehicleGetIndex( vehType );
07020                         
07021                         switch( g_vehicleInfo[iVehIndex].type )
07022                         {
07023                                 case VH_ANIMAL:
07024                                         // Create the animal (making sure all it's data is initialized).
07025                                         G_CreateAnimalNPC( &cent->m_pVehicle, vehType );
07026                                         break;
07027                                 case VH_SPEEDER:
07028                                         // Create the speeder (making sure all it's data is initialized).
07029                                         G_CreateSpeederNPC( &cent->m_pVehicle, vehType );
07030                                         break;
07031                                 case VH_FIGHTER:
07032                                         // Create the fighter (making sure all it's data is initialized).
07033                                         G_CreateFighterNPC( &cent->m_pVehicle, vehType );
07034                                         break;
07035                                 case VH_WALKER:
07036                                         // Create the walker (making sure all it's data is initialized).
07037                                         G_CreateWalkerNPC( &cent->m_pVehicle, vehType );
07038                                         break;
07039 
07040                                 default:
07041                                         assert(!"vehicle with an unknown type - couldn't create vehicle_t");
07042                                         break;
07043                         }
07044                         
07045                         //set up my happy prediction hack
07046                         cent->m_pVehicle->m_vOrientation = &cgSendPS[cent->currentState.number]->vehOrientation[0];
07047 
07048                         cent->m_pVehicle->m_pParentEntity = (bgEntity_t *)cent;
07049 
07050                         //attach the handles for fx cgame-side
07051                         CG_RegisterVehicleAssets(cent->m_pVehicle);
07052 
07053                         BG_GetVehicleModelName(modelName);
07054                         if (cent->m_pVehicle->m_pVehicleInfo->skin &&
07055                                 cent->m_pVehicle->m_pVehicleInfo->skin[0])
07056                         { //use a custom skin
07057                                 skinID = trap_R_RegisterSkin(va("models/players/%s/model_%s.skin", modelName, cent->m_pVehicle->m_pVehicleInfo->skin));
07058                         }
07059                         else
07060                         {
07061                                 skinID = trap_R_RegisterSkin(va("models/players/%s/model_default.skin", modelName));
07062                         }
07063                         strcpy(modelName, va("models/players/%s/model.glm", modelName));
07064 
07065                         //this sound is *only* used for vehicles now
07066                         cgs.media.noAmmoSound = trap_S_RegisterSound( "sound/weapons/noammo.wav" );
07067                 }
07068                 else
07069                 {
07070                         skinID = CG_HandleAppendedSkin(modelName); //get the skin if there is one.
07071                 }
07072 
07073                 if (cent->ghoul2)
07074                 { //clean it first!
07075             trap_G2API_CleanGhoul2Models(&cent->ghoul2);
07076                 }
07077 
07078                 trap_G2API_InitGhoul2Model(&cent->ghoul2, modelName, 0, skinID, 0, 0, 0);
07079 
07080                 if (cent->ghoul2)
07081                 {
07082                         char GLAName[MAX_QPATH];
07083                         char originalModelName[MAX_QPATH];
07084                         char *saber;
07085                         int j = 0;
07086 
07087                         if (cent->currentState.NPC_class == CLASS_VEHICLE &&
07088                                 cent->m_pVehicle)
07089                         { //do special vehicle stuff
07090                                 char strTemp[128];
07091                                 int i;
07092 
07093                                 // Setup the default first bolt
07094                                 i = trap_G2API_AddBolt( cent->ghoul2, 0, "model_root" );
07095 
07096                                 // Setup the droid unit.
07097                                 cent->m_pVehicle->m_iDroidUnitTag = trap_G2API_AddBolt( cent->ghoul2, 0, "*droidunit" );
07098 
07099                                 // Setup the Exhausts.
07100                                 for ( i = 0; i < MAX_VEHICLE_EXHAUSTS; i++ )
07101                                 {
07102                                         Com_sprintf( strTemp, 128, "*exhaust%i", i + 1 );
07103                                         cent->m_pVehicle->m_iExhaustTag[i] = trap_G2API_AddBolt( cent->ghoul2, 0, strTemp );
07104                                 }
07105 
07106                                 // Setup the Muzzles.
07107                                 for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
07108                                 {
07109                                         Com_sprintf( strTemp, 128, "*muzzle%i", i + 1 );
07110                                         cent->m_pVehicle->m_iMuzzleTag[i] = trap_G2API_AddBolt( cent->ghoul2, 0, strTemp );
07111                                         if ( cent->m_pVehicle->m_iMuzzleTag[i] == -1 )
07112                                         {//ergh, try *flash?
07113                                                 Com_sprintf( strTemp, 128, "*flash%i", i + 1 );
07114                                                 cent->m_pVehicle->m_iMuzzleTag[i] = trap_G2API_AddBolt( cent->ghoul2, 0, strTemp );
07115                                         }
07116                                 }
07117 
07118                                 // Setup the Turrets.
07119                                 for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ )
07120                                 {
07121                                         if ( cent->m_pVehicle->m_pVehicleInfo->turret[i].gunnerViewTag )
07122                                         {
07123                                                 cent->m_pVehicle->m_iGunnerViewTag[i] = trap_G2API_AddBolt( cent->ghoul2, 0, cent->m_pVehicle->m_pVehicleInfo->turret[i].gunnerViewTag );
07124                                         }
07125                                         else
07126                                         {
07127                                                 cent->m_pVehicle->m_iGunnerViewTag[i] = -1;
07128                                         }
07129                                 }
07130                         }
07131 
07132                         if (cent->currentState.npcSaber1)
07133                         {
07134                                 saber = (char *)CG_ConfigString(CS_MODELS+cent->currentState.npcSaber1);
07135                                 assert(!saber || !saber[0] || saber[0] == '@');
07136                                 //valid saber names should always start with '@' for NPCs
07137 
07138                                 if (saber && saber[0])
07139                                 {
07140                                         saber++; //skip over the @
07141                                         WP_SetSaber(cent->currentState.number, cent->npcClient->saber, 0, saber);
07142                                 }
07143                         }
07144                         if (cent->currentState.npcSaber2)
07145                         {
07146                                 saber = (char *)CG_ConfigString(CS_MODELS+cent->currentState.npcSaber2);
07147                                 assert(!saber || !saber[0] || saber[0] == '@');
07148                                 //valid saber names should always start with '@' for NPCs
07149 
07150                                 if (saber && saber[0])
07151                                 {
07152                                         saber++; //skip over the @
07153                                         WP_SetSaber(cent->currentState.number, cent->npcClient->saber, 1, saber);
07154                                 }
07155                         }
07156 
07157                         // If this is a not vehicle, give it saber stuff...
07158                         if ( cent->currentState.NPC_class != CLASS_VEHICLE )
07159                         {
07160                                 while (j < MAX_SABERS)
07161                                 {
07162                                         if (cent->npcClient->saber[j].model[0])
07163                                         {
07164                                                 if (cent->npcClient->ghoul2Weapons[j])
07165                                                 { //free the old instance(s)
07166                                                         trap_G2API_CleanGhoul2Models(&cent->npcClient->ghoul2Weapons[j]);
07167                                                         cent->npcClient->ghoul2Weapons[j] = 0;
07168                                                 }
07169 
07170                                                 CG_InitG2SaberData(j, cent->npcClient);
07171                                         }
07172                                         j++;
07173                                 }
07174                         }
07175 
07176                         trap_G2API_SetSkin(cent->ghoul2, 0, skinID, skinID);
07177 
07178                         cent->localAnimIndex = -1;
07179 
07180                         GLAName[0] = 0;
07181                         trap_G2API_GetGLAName(cent->ghoul2, 0, GLAName);
07182 
07183                         strcpy(originalModelName, modelName);
07184 
07185                         if (GLAName[0] &&
07186                                 !strstr(GLAName, "players/_humanoid/") /*&&
07187                                 !strstr(GLAName, "players/rockettrooper/")*/)
07188                         { //it doesn't use humanoid anims.                              
07189                                 slash = Q_strrchr( GLAName, '/' );
07190                                 if ( slash )
07191                                 {
07192                                         strcpy(slash, "/animation.cfg");
07193 
07194                                         cent->localAnimIndex = BG_ParseAnimationFile(GLAName, NULL, qfalse);
07195                                 }
07196                         }
07197                         else
07198                         { //humanoid index.
07199                                 trap_G2API_AddBolt(cent->ghoul2, 0, "*r_hand");
07200                                 trap_G2API_AddBolt(cent->ghoul2, 0, "*l_hand");
07201 
07202                                 //rhand must always be first bolt. lhand always second. Whichever you want the
07203                                 //jetpack bolted to must always be third.
07204                                 trap_G2API_AddBolt(cent->ghoul2, 0, "*chestg");
07205 
07206                                 //claw bolts
07207                                 trap_G2API_AddBolt(cent->ghoul2, 0, "*r_hand_cap_r_arm");
07208                                 trap_G2API_AddBolt(cent->ghoul2, 0, "*l_hand_cap_l_arm");
07209 
07210                                 if (strstr(GLAName, "players/rockettrooper/"))
07211                                 {
07212                                         cent->localAnimIndex = 1;
07213                                 }
07214                                 else
07215                                 {
07216                                         cent->localAnimIndex = 0;
07217                                 }
07218 
07219                                 if (trap_G2API_AddBolt(cent->ghoul2, 0, "*head_top") == -1)
07220                                 {
07221                                         trap_G2API_AddBolt(cent->ghoul2, 0, "ceyebrow");
07222                                 }
07223                                 trap_G2API_AddBolt(cent->ghoul2, 0, "Motion");
07224                         }
07225 
07226                         // If this is a not vehicle...
07227                         if ( cent->currentState.NPC_class != CLASS_VEHICLE )
07228                         {
07229                                 if (trap_G2API_AddBolt(cent->ghoul2, 0, "lower_lumbar") == -1)
07230                                 { //check now to see if we have this bone for setting anims and such
07231                                         cent->noLumbar = qtrue;
07232                                 }
07233 
07234                                 if (trap_G2API_AddBolt(cent->ghoul2, 0, "face") == -1)
07235                                 { //check now to see if we have this bone for setting anims and such
07236                                         cent->noFace = qtrue;
07237                                 }
07238                         }
07239                         else
07240                         {
07241                                 cent->noLumbar = qtrue;
07242                                 cent->noFace = qtrue;
07243                         }
07244 
07245                         if (cent->localAnimIndex != -1)
07246                         {
07247                                 slash = Q_strrchr( originalModelName, '/' );
07248                                 if ( slash )
07249                                 {
07250                                         slash++;
07251                                         *slash = 0;
07252                                 }
07253 
07254                                 cent->eventAnimIndex = BG_ParseAnimationEvtFile(originalModelName, cent->localAnimIndex, bgNumAnimEvents);
07255                         }
07256                 }
07257         }
07258 
07259         trap_S_ShutUp(qtrue);
07260         CG_HandleNPCSounds(cent); //handle sound loading here as well.
07261         trap_S_ShutUp(qfalse);
07262 }
07263 
07264 //for now this is just gonna create a big explosion on the area of the surface,
07265 //because I am lazy.
07266 static void CG_CreateSurfaceDebris(centity_t *cent, int surfNum, int fxID, qboolean throwPart)
07267 {
07268         int lostPartFX = 0;
07269         int b;
07270         vec3_t v, d;
07271         mdxaBone_t boltMatrix;
07272         const char *surfName = bgToggleableSurfaces[surfNum];
07273 
07274         if (!cent->ghoul2)
07275         { //oh no
07276                 return;
07277         }
07278 
07279         //let's add the surface as a bolt so we can get the base point of it
07280         if (bgToggleableSurfaceDebris[surfNum] == 3)
07281         { //right wing flame
07282                 b = trap_G2API_AddBolt(cent->ghoul2, 0, "*r_wingdamage");
07283                 if ( throwPart
07284                         && cent->m_pVehicle
07285                         && cent->m_pVehicle->m_pVehicleInfo )
07286                 {
07287                         lostPartFX = cent->m_pVehicle->m_pVehicleInfo->iRWingFX;
07288                 }
07289         }
07290         else if (bgToggleableSurfaceDebris[surfNum] == 4)
07291         { //left wing flame
07292                 b = trap_G2API_AddBolt(cent->ghoul2, 0, "*l_wingdamage");
07293                 if ( throwPart
07294                         && cent->m_pVehicle
07295                         && cent->m_pVehicle->m_pVehicleInfo )
07296                 {
07297                         lostPartFX = cent->m_pVehicle->m_pVehicleInfo->iLWingFX;
07298                 }
07299         }
07300         else if (bgToggleableSurfaceDebris[surfNum] == 5)
07301         { //right wing flame 2
07302                 b = trap_G2API_AddBolt(cent->ghoul2, 0, "*r_wingdamage");
07303                 if ( throwPart
07304                         && cent->m_pVehicle
07305                         && cent->m_pVehicle->m_pVehicleInfo )
07306                 {
07307                         lostPartFX = cent->m_pVehicle->m_pVehicleInfo->iRWingFX;
07308                 }
07309         }
07310         else if (bgToggleableSurfaceDebris[surfNum] == 6)
07311         { //left wing flame 2
07312                 b = trap_G2API_AddBolt(cent->ghoul2, 0, "*l_wingdamage");
07313                 if ( throwPart
07314                         && cent->m_pVehicle
07315                         && cent->m_pVehicle->m_pVehicleInfo )
07316                 {
07317                         lostPartFX = cent->m_pVehicle->m_pVehicleInfo->iLWingFX;
07318                 }
07319         }
07320         else if (bgToggleableSurfaceDebris[surfNum] == 7)
07321         { //nose flame
07322                 b = trap_G2API_AddBolt(cent->ghoul2, 0, "*nosedamage");
07323                 if ( cent->m_pVehicle
07324                         && cent->m_pVehicle->m_pVehicleInfo )
07325                 {
07326                         lostPartFX = cent->m_pVehicle->m_pVehicleInfo->iNoseFX;
07327                 }
07328         }
07329         else
07330         {
07331                 b = trap_G2API_AddBolt(cent->ghoul2, 0, surfName);
07332         }
07333 
07334         if (b == -1)
07335         { //couldn't find this surface apparently
07336                 return;
07337         }
07338 
07339         //now let's get the position and direction of this surface and make a big explosion
07340         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, b, &boltMatrix, cent->lerpAngles, cent->lerpOrigin, cg.time,
07341                 cgs.gameModels, cent->modelScale);
07342         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, v);
07343         BG_GiveMeVectorFromMatrix(&boltMatrix, POSITIVE_Z, d);
07344 
07345         trap_FX_PlayEffectID(fxID, v, d, -1, -1);
07346         if ( throwPart && lostPartFX )
07347         {//throw off a ship part, too
07348                 vec3_t  fxFwd;
07349                 AngleVectors( cent->lerpAngles, fxFwd, NULL, NULL );
07350                 trap_FX_PlayEffectID(lostPartFX, v, fxFwd, -1, -1);
07351         }
07352 }
07353 
07354 //for now this is just gonna create a big explosion on the area of the surface,
07355 //because I am lazy.
07356 static void CG_CreateSurfaceSmoke(centity_t *cent, int shipSurf, int fxID)
07357 {
07358         int b = -1;
07359         vec3_t v, d;
07360         mdxaBone_t boltMatrix;
07361         const char *surfName = NULL;
07362 
07363         if (!cent->ghoul2)
07364         { //oh no
07365                 return;
07366         }
07367 
07368         //let's add the surface as a bolt so we can get the base point of it
07369         if ( shipSurf == SHIPSURF_FRONT )
07370         { //front flame/smoke
07371                 surfName = "*nosedamage";
07372         }
07373         else if (shipSurf == SHIPSURF_BACK )
07374         { //back flame/smoke
07375                 surfName = "*exhaust1";//FIXME: random?  Some point in-between?
07376         }
07377         else if (shipSurf == SHIPSURF_RIGHT )
07378         { //right wing flame/smoke
07379                 surfName = "*r_wingdamage";
07380         }
07381         else if (shipSurf == SHIPSURF_LEFT )
07382         { //left wing flame/smoke
07383                 surfName = "*l_wingdamage";
07384         }
07385         else
07386         {//unknown surf!
07387                 return;
07388         }
07389         b = trap_G2API_AddBolt(cent->ghoul2, 0, surfName);
07390         if (b == -1)
07391         { //couldn't find this surface apparently
07392                 return;
07393         }
07394 
07395         //now let's get the position and direction of this surface and make a big explosion
07396         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, b, &boltMatrix, cent->lerpAngles, cent->lerpOrigin, cg.time,
07397                 cgs.gameModels, cent->modelScale);
07398         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, v);
07399         BG_GiveMeVectorFromMatrix(&boltMatrix, POSITIVE_Z, d);
07400 
07401         trap_FX_PlayEffectID(fxID, v, d, -1, -1);
07402 }
07403 
07404 #define SMOOTH_G2ANIM_LERPANGLES
07405 
07406 qboolean CG_VehicleShouldDrawShields( centity_t *vehCent )
07407 {
07408         if ( vehCent->damageTime > cg.time //ship shields currently taking damage
07409                 && vehCent->currentState.NPC_class == CLASS_VEHICLE 
07410                 && vehCent->m_pVehicle
07411                 && vehCent->m_pVehicle->m_pVehicleInfo )
07412         {
07413                 return qtrue;
07414         }
07415         return qfalse;
07416 }
07417 
07418 /*
07419 extern  vmCvar_t                cg_showVehBounds;
07420 extern void BG_VehicleAdjustBBoxForOrientation( Vehicle_t *veh, vec3_t origin, vec3_t mins, vec3_t maxs,
07421                                                                                 int clientNum, int tracemask,
07422                                                                                 void (*localTrace)(trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask)); // bg_pmove.c
07423 */
07424 qboolean CG_VehicleAttachDroidUnit( centity_t *droidCent, refEntity_t *legs )
07425 {
07426         if ( droidCent
07427                 && droidCent->currentState.owner 
07428                 && droidCent->currentState.clientNum >= MAX_CLIENTS )
07429         {//the only NPCs that can ride a vehicle are droids...???
07430                 centity_t *vehCent = &cg_entities[droidCent->currentState.owner];
07431                 if ( vehCent 
07432                         && vehCent->m_pVehicle 
07433                         && vehCent->ghoul2
07434                         && vehCent->m_pVehicle->m_iDroidUnitTag != -1 )
07435                 {
07436                         mdxaBone_t boltMatrix;
07437                         vec3_t  fwd, rt, tempAng;
07438 
07439                         trap_G2API_GetBoltMatrix(vehCent->ghoul2, 0, vehCent->m_pVehicle->m_iDroidUnitTag, &boltMatrix, vehCent->lerpAngles, vehCent->lerpOrigin, cg.time,
07440                                 cgs.gameModels, vehCent->modelScale);
07441                         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, droidCent->lerpOrigin);
07442                         BG_GiveMeVectorFromMatrix(&boltMatrix, POSITIVE_X, fwd);//WTF???
07443                         BG_GiveMeVectorFromMatrix(&boltMatrix, NEGATIVE_Y, rt);//WTF???
07444                         vectoangles( fwd, droidCent->lerpAngles );
07445                         vectoangles( rt, tempAng );
07446                         droidCent->lerpAngles[ROLL] = tempAng[PITCH];
07447                         
07448                         return qtrue;
07449                 }
07450         }
07451         return qfalse;
07452 }
07453 
07454 void CG_G2Animated( centity_t *cent )
07455 {
07456 #ifdef SMOOTH_G2ANIM_LERPANGLES
07457         float angSmoothFactor = 0.7f;
07458 #endif
07459 
07460 
07461         if (!cent->ghoul2)
07462         { //Initialize this g2 anim ent, then return (will start rendering next frame)
07463                 CG_G2AnimEntModelLoad(cent);
07464                 cent->npcLocalSurfOff = 0;
07465                 cent->npcLocalSurfOn = 0;
07466                 return;
07467         }
07468 
07469         if (cent->npcLocalSurfOff != cent->currentState.surfacesOff ||
07470                 cent->npcLocalSurfOn != cent->currentState.surfacesOn)
07471         { //looks like it's time for an update.
07472                 int i = 0;
07473 
07474                 while (i < BG_NUM_TOGGLEABLE_SURFACES && bgToggleableSurfaces[i])
07475                 {
07476                         if (!(cent->npcLocalSurfOff & (1 << i)) &&
07477                                 (cent->currentState.surfacesOff & (1 << i)))
07478                         { //it wasn't off before but it's off now, so reflect this change in the g2 instance.
07479                                 if (bgToggleableSurfaceDebris[i] > 0)
07480                                 { //make some local debris of this thing?
07481                                         //FIXME: throw off the proper model effect, too
07482                                         CG_CreateSurfaceDebris(cent, i, cgs.effects.mShipDestDestroyed, qtrue);
07483                                 }
07484 
07485                                 trap_G2API_SetSurfaceOnOff(cent->ghoul2, bgToggleableSurfaces[i], TURN_OFF);
07486                         }
07487 
07488                         if (!(cent->npcLocalSurfOn & (1 << i)) &&
07489                                 (cent->currentState.surfacesOn & (1 << i)))
07490                         { //same as above, but on instead of off.
07491                                 trap_G2API_SetSurfaceOnOff(cent->ghoul2, bgToggleableSurfaces[i], TURN_ON);
07492                         }
07493 
07494                         i++;
07495                 }
07496 
07497                 cent->npcLocalSurfOff = cent->currentState.surfacesOff;
07498                 cent->npcLocalSurfOn = cent->currentState.surfacesOn;
07499         }
07500 
07501 
07502         /*
07503         if (cent->currentState.weapon &&
07504                 !trap_G2API_HasGhoul2ModelOnIndex(&(cent->ghoul2), 1) &&
07505                 !(cent->currentState.eFlags & EF_DEAD))
07506         { //if the server says we have a weapon and we haven't copied one onto ourselves yet, then do so.
07507                 trap_G2API_CopySpecificGhoul2Model(g2WeaponInstances[cent->currentState.weapon], 0, cent->ghoul2, 1);
07508 
07509                 if (cent->currentState.weapon == WP_SABER)
07510                 {
07511                         trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/saber/saberon.wav" ));
07512                 }
07513         }
07514         */
07515 
07516         if (cent->torsoBolt && !(cent->currentState.eFlags & EF_DEAD))
07517         { //he's alive and has a limb missing still, reattach it and reset the weapon
07518                 CG_ReattachLimb(cent);
07519         }
07520 
07521         if (((cent->currentState.eFlags & EF_DEAD) || (cent->currentState.eFlags & EF_RAG)) && !cent->localAnimIndex)
07522         {
07523                 vec3_t forcedAngles;
07524 
07525                 VectorClear(forcedAngles);
07526                 forcedAngles[YAW] = cent->lerpAngles[YAW];
07527 
07528                 CG_RagDoll(cent, forcedAngles);
07529         }
07530 
07531 #ifdef SMOOTH_G2ANIM_LERPANGLES
07532         if ((cent->lerpAngles[YAW] > 0 && cent->smoothYaw < 0) ||
07533                 (cent->lerpAngles[YAW] < 0 && cent->smoothYaw > 0))
07534         { //keep it from snapping around on the threshold
07535                 cent->smoothYaw = -cent->smoothYaw;
07536         }
07537         cent->lerpAngles[YAW] = cent->smoothYaw+(cent->lerpAngles[YAW]-cent->smoothYaw)*angSmoothFactor;
07538         cent->smoothYaw = cent->lerpAngles[YAW];
07539 #endif
07540 
07541         //now just render as a player
07542         CG_Player(cent);
07543 
07544         /*
07545         if ( cg_showVehBounds.integer )
07546         {//show vehicle bboxes
07547                 if ( cent->currentState.clientNum >= MAX_CLIENTS
07548                         && cent->currentState.NPC_class == CLASS_VEHICLE 
07549                         && cent->m_pVehicle
07550                         && cent->m_pVehicle->m_pVehicleInfo 
07551                         && cent->currentState.clientNum != cg.predictedVehicleState.clientNum )
07552                 {//not the predicted vehicle
07553                         vec3_t NPCDEBUG_RED = {1.0, 0.0, 0.0};
07554                         vec3_t absmin, absmax;
07555                         vec3_t bmins, bmaxs;
07556                         float *old = cent->m_pVehicle->m_vOrientation;
07557                         cent->m_pVehicle->m_vOrientation = &cent->lerpAngles[0];
07558 
07559                         BG_VehicleAdjustBBoxForOrientation( cent->m_pVehicle, cent->lerpOrigin, bmins, bmaxs,
07560                                                                                 cent->currentState.number, MASK_PLAYERSOLID, NULL );
07561                         cent->m_pVehicle->m_vOrientation = old;
07562 
07563                         VectorAdd( cent->lerpOrigin, bmins, absmin );
07564                         VectorAdd( cent->lerpOrigin, bmaxs, absmax );
07565                         CG_Cube( absmin, absmax, NPCDEBUG_RED, 0.25 );
07566                 }
07567         }
07568         */
07569 }
07570 //rww - here ends the majority of my g2animent stuff.
07571 
07572 //Disabled for now, I'm too lazy to keep it working with all the stuff changing around.
07573 #if 0
07574 int cgFPLSState = 0;
07575 
07576 void CG_ForceFPLSPlayerModel(centity_t *cent, clientInfo_t *ci)
07577 {
07578         animation_t *anim;
07579 
07580         if (cg_fpls.integer && !cg.renderingThirdPerson)
07581         {
07582                 int                             skinHandle;
07583 
07584                 skinHandle = trap_R_RegisterSkin("models/players/kyle/model_fpls2.skin");
07585 
07586                 trap_G2API_CleanGhoul2Models(&(ci->ghoul2Model));
07587 
07588                 ci->torsoSkin = skinHandle;
07589                 trap_G2API_InitGhoul2Model(&ci->ghoul2Model, "models/players/kyle/model.glm", 0, ci->torsoSkin, 0, 0, 0);
07590 
07591                 ci->bolt_rhand = trap_G2API_AddBolt(ci->ghoul2Model, 0, "*r_hand");
07592                 
07593                 trap_G2API_SetBoneAnim(ci->ghoul2Model, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, cg.time, -1, -1);
07594                 trap_G2API_SetBoneAngles(ci->ghoul2Model, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, cg.time);
07595                 trap_G2API_SetBoneAngles(ci->ghoul2Model, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, POSITIVE_X, NULL, 0, cg.time);
07596 
07597                 ci->bolt_lhand = trap_G2API_AddBolt(ci->ghoul2Model, 0, "*l_hand");
07598 
07599                 //rhand must always be first bolt. lhand always second. Whichever you want the
07600                 //jetpack bolted to must always be third.
07601                 trap_G2API_AddBolt(ci->ghoul2Model, 0, "*chestg");
07602 
07603                 //claw bolts
07604                 trap_G2API_AddBolt(ci->ghoul2Model, 0, "*r_hand_cap_r_arm");
07605                 trap_G2API_AddBolt(ci->ghoul2Model, 0, "*l_hand_cap_l_arm");
07606 
07607                 ci->bolt_head = trap_G2API_AddBolt(ci->ghoul2Model, 0, "*head_top");
07608                 if (ci->bolt_head == -1)
07609                 {
07610                         ci->bolt_head = trap_G2API_AddBolt(ci->ghoul2Model, 0, "ceyebrow");
07611                 }
07612 
07613                 ci->bolt_motion = trap_G2API_AddBolt(ci->ghoul2Model, 0, "Motion");
07614 
07615                 //We need a lower lumbar bolt for footsteps
07616                 ci->bolt_llumbar = trap_G2API_AddBolt(ci->ghoul2Model, 0, "lower_lumbar");
07617 
07618                 CG_CopyG2WeaponInstance(cent, cent->currentState.weapon, ci->ghoul2Model);
07619         }
07620         else
07621         {
07622                 CG_RegisterClientModelname(ci, ci->modelName, ci->skinName, ci->teamName, cent->currentState.number);
07623         }
07624 
07625         anim = &bgAllAnims[cent->localAnimIndex].anims[ cent->currentState.legsAnim ];
07626 
07627         if (anim)
07628         {
07629                 int flags = BONE_ANIM_OVERRIDE_FREEZE;
07630                 int firstFrame = anim->firstFrame;
07631                 int setFrame = -1;
07632                 float animSpeed = 50.0f / anim->frameLerp;
07633 
07634                 if (anim->loopFrames != -1)
07635                 {
07636                         flags = BONE_ANIM_OVERRIDE_LOOP;
07637                 }
07638 
07639                 if (cent->pe.legs.frame >= anim->firstFrame && cent->pe.legs.frame <= (anim->firstFrame + anim->numFrames))
07640                 {
07641                         setFrame = cent->pe.legs.frame;
07642                 }
07643 
07644                 trap_G2API_SetBoneAnim(ci->ghoul2Model, 0, "model_root", firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, setFrame, 150);
07645 
07646                 cent->currentState.legsAnim = 0;
07647         }
07648 
07649         anim = &bgAllAnims[cent->localAnimIndex].anims[ cent->currentState.torsoAnim ];
07650 
07651         if (anim)
07652         {
07653                 int flags = BONE_ANIM_OVERRIDE_FREEZE;
07654                 int firstFrame = anim->firstFrame;
07655                 int setFrame = -1;
07656                 float animSpeed = 50.0f / anim->frameLerp;
07657 
07658                 if (anim->loopFrames != -1)
07659                 {
07660                         flags = BONE_ANIM_OVERRIDE_LOOP;
07661                 }
07662 
07663                 if (cent->pe.torso.frame >= anim->firstFrame && cent->pe.torso.frame <= (anim->firstFrame + anim->numFrames))
07664                 {
07665                         setFrame = cent->pe.torso.frame;
07666                 }
07667 
07668                 trap_G2API_SetBoneAnim(ci->ghoul2Model, 0, "lower_lumbar", firstFrame, anim->firstFrame + anim->numFrames, flags, animSpeed, cg.time, setFrame, 150);
07669 
07670                 cent->currentState.torsoAnim = 0;
07671         }
07672 
07673         trap_G2API_CleanGhoul2Models(&(cent->ghoul2));
07674         trap_G2API_DuplicateGhoul2Instance(ci->ghoul2Model, &cent->ghoul2);
07675 
07676         //Attach the instance to this entity num so we can make use of client-server
07677         //shared operations if possible.
07678         trap_G2API_AttachInstanceToEntNum(cent->ghoul2, cent->currentState.number, qfalse);
07679 }
07680 #endif
07681 
07682 //for allocating and freeing npc clientinfo structures.
07683 //Remember to free this before game shutdown no matter what
07684 //and don't stomp over it, as it is dynamic memory from the
07685 //exe.
07686 void CG_CreateNPCClient(clientInfo_t **ci)
07687 {
07688         //trap_TrueMalloc((void **)ci, sizeof(clientInfo_t));
07689         *ci = (clientInfo_t *) BG_Alloc(sizeof(clientInfo_t));
07690 }
07691 
07692 void CG_DestroyNPCClient(clientInfo_t **ci)
07693 {
07694         memset(*ci, 0, sizeof(clientInfo_t));
07695         //trap_TrueFree((void **)ci);
07696 }
07697 
07698 static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t tempAngles, qhandle_t shader, qboolean alwaysDo )
07699 {
07700         // Undoing for now, at least this code should compile if I ( or anyone else ) decides to work on this effect
07701         qboolean        found = qfalse;
07702         vec3_t          fxOrg, fxOrg2, dir;
07703         vec3_t          rgb;
07704         mdxaBone_t      boltMatrix;
07705         trace_t         tr;
07706         int bolt=-1;
07707         int iter=0;
07708         int torsoBolt = -1;
07709         int crotchBolt = -1;
07710         int elbowLBolt = -1;
07711         int elbowRBolt = -1;
07712         int handLBolt = -1;
07713         int handRBolt = -1;
07714         int kneeLBolt = -1;
07715         int kneeRBolt = -1;
07716         int footLBolt = -1;
07717         int footRBolt = -1;
07718 
07719         VectorSet(rgb, 1, 1, 1);
07720 
07721         if (cent->localAnimIndex <= 1)
07722         { //humanoid
07723                 torsoBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "lower_lumbar");
07724                 crotchBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "pelvis");
07725                 elbowLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*l_arm_elbow");
07726                 elbowRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*r_arm_elbow");
07727                 handLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*l_hand");
07728                 handRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*r_hand");
07729                 kneeLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*hips_l_knee");
07730                 kneeRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*hips_r_knee");
07731                 footLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*l_leg_foot");
07732                 footRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*r_leg_foot");
07733         }
07734         else if (cent->currentState.NPC_class == CLASS_PROTOCOL)
07735         { //any others that can use these bolts too?
07736                 torsoBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "lower_lumbar");
07737                 crotchBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "pelvis");
07738                 elbowLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*bicep_lg");
07739                 elbowRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*bicep_rg");
07740                 handLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*hand_l");
07741                 handRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*weapon");
07742                 kneeLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*thigh_lg");
07743                 kneeRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*thigh_rg");
07744                 footLBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*foot_lg");
07745                 footRBolt = trap_G2API_AddBolt(cent->ghoul2, 0, "*foot_rg");
07746         }
07747 
07748         // Pick a random start point
07749         while (bolt<0)
07750         {
07751                 int test;
07752                 if (iter>5)
07753                 {
07754                         test=iter-5;
07755                 }
07756                 else
07757                 {
07758                         test=Q_irand(0,6);
07759                 }
07760                 switch(test)
07761                 {
07762                 case 0:
07763                         // Right Elbow
07764                         bolt=elbowRBolt;
07765                         break;
07766                 case 1:
07767                         // Left Hand
07768                         bolt=handLBolt;
07769                         break;
07770                 case 2:
07771                         // Right hand
07772                         bolt=handRBolt;
07773                         break;
07774                 case 3:
07775                         // Left Foot
07776                         bolt=footLBolt;
07777                         break;
07778                 case 4:
07779                         // Right foot
07780                         bolt=footRBolt;
07781                         break;
07782                 case 5:
07783                         // Torso
07784                         bolt=torsoBolt;
07785                         break;
07786                 case 6:
07787                 default:
07788                         // Left Elbow
07789                         bolt=elbowLBolt;
07790                         break;
07791                 }
07792                 if (++iter==20)
07793                         break;
07794         }
07795         if (bolt>=0)
07796         {
07797                 found = trap_G2API_GetBoltMatrix( cent->ghoul2, 0, bolt, 
07798                                 &boltMatrix, tempAngles, origin, cg.time, 
07799                                 cgs.gameModels, cent->modelScale);
07800         }
07801 
07802         // Make sure that it's safe to even try and get these values out of the Matrix, otherwise the values could be garbage
07803         if ( found )
07804         {
07805                 BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, fxOrg );
07806                 if ( random() > 0.5f )
07807                 {
07808                         BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, dir );
07809                 }
07810                 else
07811                 {
07812                         BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, dir );
07813                 }
07814 
07815                 // Add some fudge, makes us not normalized, but that isn't really important
07816                 dir[0] += crandom() * 0.4f;
07817                 dir[1] += crandom() * 0.4f;
07818                 dir[2] += crandom() * 0.4f;
07819         }
07820         else
07821         {
07822                 // Just use the lerp Origin and a random direction
07823                 VectorCopy( cent->lerpOrigin, fxOrg );
07824                 VectorSet( dir, crandom(), crandom(), crandom() ); // Not normalized, but who cares.
07825                 switch ( cent->currentState.NPC_class )
07826                 {
07827                 case CLASS_PROBE:
07828                         fxOrg[2] += 50;
07829                         break;
07830                 case CLASS_MARK1:
07831                         fxOrg[2] += 50;
07832                         break;
07833                 case CLASS_ATST:
07834                         fxOrg[2] += 120;
07835                         break;
07836                 default:
07837                         break;
07838                 }
07839         }
07840 
07841         VectorMA( fxOrg, random() * 40 + 40, dir, fxOrg2 );
07842 
07843         CG_Trace( &tr, fxOrg, NULL, NULL, fxOrg2, -1, CONTENTS_SOLID );
07844 
07845         if ( tr.fraction < 1.0f || random() > 0.94f || alwaysDo )
07846         {
07847                 addElectricityArgStruct_t p;
07848 
07849                 VectorCopy(fxOrg, p.start);
07850                 VectorCopy(tr.endpos, p.end);
07851                 p.size1 = 1.5f;
07852                 p.size2 = 4.0f;
07853                 p.sizeParm = 0.0f;
07854                 p.alpha1 = 1.0f;
07855                 p.alpha2 = 0.5f;
07856                 p.alphaParm = 0.0f;
07857                 VectorCopy(rgb, p.sRGB);
07858                 VectorCopy(rgb, p.eRGB);
07859                 p.rgbParm = 0.0f;
07860                 p.chaos = 5.0f;
07861                 p.killTime = (random() * 50 + 100);
07862                 p.shader = shader;
07863                 p.flags = (0x00000001 | 0x00000100 | 0x02000000 | 0x04000000 | 0x01000000);
07864 
07865                 trap_FX_AddElectricity(&p);
07866 
07867                 //In other words:
07868                 /*
07869                 FX_AddElectricity( fxOrg, tr.endpos,
07870                         1.5f, 4.0f, 0.0f, 
07871                         1.0f, 0.5f, 0.0f,
07872                         rgb, rgb, 0.0f,
07873                         5.5f, random() * 50 + 100, shader, FX_ALPHA_LINEAR | FX_SIZE_LINEAR | FX_BRANCH | FX_GROW | FX_TAPER );
07874                 */
07875         }
07876 }
07877 
07878 void *cg_g2JetpackInstance = NULL;
07879 
07880 #define JETPACK_MODEL "models/weapons2/jetpack/model.glm"
07881 
07882 void CG_InitJetpackGhoul2(void)
07883 {
07884         if (cg_g2JetpackInstance)
07885         {
07886                 assert(!"Tried to init jetpack inst, already init'd");
07887                 return;
07888         }
07889 
07890         trap_G2API_InitGhoul2Model(&cg_g2JetpackInstance, JETPACK_MODEL, 0, 0, 0, 0, 0);
07891 
07892         assert(cg_g2JetpackInstance);
07893 
07894         //Indicate which bolt on the player we will be attached to
07895         //In this case bolt 0 is rhand, 1 is lhand, and 2 is the bolt
07896         //for the jetpack (*chestg)
07897         trap_G2API_SetBoltInfo(cg_g2JetpackInstance, 0, 2);
07898 
07899         //Add the bolts jet effects will be played from
07900         trap_G2API_AddBolt(cg_g2JetpackInstance, 0, "torso_ljet");
07901         trap_G2API_AddBolt(cg_g2JetpackInstance, 0, "torso_rjet");
07902 }
07903 
07904 void CG_CleanJetpackGhoul2(void)
07905 {
07906         if (cg_g2JetpackInstance)
07907         {
07908                 trap_G2API_CleanGhoul2Models(&cg_g2JetpackInstance);
07909                 cg_g2JetpackInstance = NULL;
07910         }
07911 }
07912 
07913 #define RARMBIT                 (1 << (G2_MODELPART_RARM-10))
07914 #define RHANDBIT                (1 << (G2_MODELPART_RHAND-10))
07915 #define WAISTBIT                (1 << (G2_MODELPART_WAIST-10))
07916 
07917 #if 0
07918 static void CG_VehicleHeatEffect( vec3_t org, centity_t *cent )
07919 {
07920         refEntity_t ent;
07921         vec3_t ang;
07922         float scale;
07923         float vLen;
07924         float alpha;
07925 
07926         if (!cg_renderToTextureFX.integer)
07927         {
07928                 return;
07929         }
07930         scale = 0.1f;
07931 
07932         alpha = 200.0f;
07933 
07934         memset( &ent, 0, sizeof( ent ) );
07935 
07936         VectorCopy( org, ent.origin );
07937 
07938         VectorSubtract(ent.origin, cg.refdef.vieworg, ent.axis[0]);
07939         vLen = VectorLength(ent.axis[0]);
07940         if (VectorNormalize(ent.axis[0]) <= 0.1f)
07941         {       // Entity is right on vieworg.  quit.
07942                 return;
07943         }
07944 
07945         vectoangles(ent.axis[0], ang);
07946         AnglesToAxis(ang, ent.axis);
07947 
07948         //radius must be a power of 2, and is the actual captured texture size
07949         ent.radius = 32;
07950 
07951         VectorScale(ent.axis[0], scale, ent.axis[0]);
07952         VectorScale(ent.axis[1], scale, ent.axis[1]);
07953         VectorScale(ent.axis[2], -scale, ent.axis[2]);
07954 
07955         ent.hModel = cgs.media.halfShieldModel;
07956         ent.customShader = cgs.media.cloakedShader;
07957 
07958         //make it partially transparent so it blends with the background
07959         ent.renderfx = (RF_DISTORTION|RF_FORCE_ENT_ALPHA);
07960         ent.shaderRGBA[0] = 255.0f;
07961         ent.shaderRGBA[1] = 255.0f;
07962         ent.shaderRGBA[2] = 255.0f;
07963         ent.shaderRGBA[3] = alpha;
07964 
07965         trap_R_AddRefEntityToScene( &ent );
07966 }
07967 #endif
07968 
07969 static int lastFlyBySound[MAX_GENTITIES] = {0};
07970 #define FLYBYSOUNDTIME 2000
07971 int     cg_lastHyperSpaceEffectTime = 0;
07972 static CGAME_INLINE void CG_VehicleEffects(centity_t *cent)
07973 {
07974         Vehicle_t *pVehNPC;
07975 
07976         if (cent->currentState.eType != ET_NPC ||
07977                 cent->currentState.NPC_class != CLASS_VEHICLE ||
07978                 !cent->m_pVehicle)
07979         {
07980                 return;
07981         }
07982 
07983         pVehNPC = cent->m_pVehicle;
07984 
07985         if ( cent->currentState.clientNum == cg.predictedPlayerState.m_iVehicleNum//my vehicle
07986                 && (cent->currentState.eFlags2&EF2_HYPERSPACE) )//hyperspacing
07987         {//in hyperspace!
07988                 if ( cg.predictedVehicleState.hyperSpaceTime
07989                         && (cg.time-cg.predictedVehicleState.hyperSpaceTime) < HYPERSPACE_TIME )
07990                 {
07991                         if ( !cg_lastHyperSpaceEffectTime
07992                                 || (cg.time - cg_lastHyperSpaceEffectTime) > HYPERSPACE_TIME+500 )
07993                         {//can't be from the last time we were in hyperspace, so play the effect!
07994                                 trap_FX_PlayBoltedEffectID( cgs.effects.mHyperspaceStars, cent->lerpOrigin, cent->ghoul2, 0, 
07995                                                                                         cent->currentState.number, 0, 0, qtrue );
07996                                 cg_lastHyperSpaceEffectTime = cg.time;
07997                         }
07998                 }
07999         }
08000 
08001         //FLYBY sound
08002         if ( cent->currentState.clientNum != cg.predictedPlayerState.m_iVehicleNum
08003                 && (pVehNPC->m_pVehicleInfo->soundFlyBy||pVehNPC->m_pVehicleInfo->soundFlyBy2) )
08004         {//not my vehicle
08005                 if ( cent->currentState.speed && cg.predictedPlayerState.speed+cent->currentState.speed > 500 )
08006                 {//he's moving and between the two of us, we're moving fast
08007                         vec3_t diff;
08008                         VectorSubtract( cent->lerpOrigin, cg.predictedPlayerState.origin, diff );
08009                         if ( VectorLength( diff ) < 2048 )
08010                         {//close
08011                                 vec3_t  myFwd, theirFwd;
08012                                 AngleVectors( cg.predictedPlayerState.viewangles, myFwd, NULL, NULL );
08013                                 VectorScale( myFwd, cg.predictedPlayerState.speed, myFwd );
08014                                 AngleVectors( cent->lerpAngles, theirFwd, NULL, NULL );
08015                                 VectorScale( theirFwd, cent->currentState.speed, theirFwd );
08016                                 if ( lastFlyBySound[cent->currentState.clientNum]+FLYBYSOUNDTIME < cg.time )
08017                                 {//okay to do a flyby sound on this vehicle
08018                                         if ( DotProduct( myFwd, theirFwd ) < 500 )
08019                                         {
08020                                                 int flyBySound = 0;
08021                                                 if ( pVehNPC->m_pVehicleInfo->soundFlyBy && pVehNPC->m_pVehicleInfo->soundFlyBy2 )
08022                                                 {
08023                                                         flyBySound = Q_irand(0,1)?pVehNPC->m_pVehicleInfo->soundFlyBy:pVehNPC->m_pVehicleInfo->soundFlyBy2;
08024                                                 }
08025                                                 else if ( pVehNPC->m_pVehicleInfo->soundFlyBy  )
08026                                                 {
08027                                                         flyBySound = pVehNPC->m_pVehicleInfo->soundFlyBy;
08028                                                 }
08029                                                 else //if ( pVehNPC->m_pVehicleInfo->soundFlyBy2 )
08030                                                 {
08031                                                         flyBySound = pVehNPC->m_pVehicleInfo->soundFlyBy2;
08032                                                 }
08033                                                 trap_S_StartSound(NULL, cent->currentState.clientNum, CHAN_LESS_ATTEN, flyBySound );
08034                                                 lastFlyBySound[cent->currentState.clientNum] = cg.time;
08035                                         }
08036                                 }
08037                         }
08038                 }
08039         }
08040 
08041         if ( !cent->currentState.speed//was stopped
08042                 && cent->nextState.speed > 0//now moving forward
08043                 && cent->m_pVehicle->m_pVehicleInfo->soundEngineStart )
08044         {//engines rev up for the first time
08045                 trap_S_StartSound(NULL, cent->currentState.clientNum, CHAN_LESS_ATTEN, cent->m_pVehicle->m_pVehicleInfo->soundEngineStart );
08046         }
08047         // Animals don't exude any effects...
08048         if ( pVehNPC->m_pVehicleInfo->type != VH_ANIMAL )
08049         {
08050                 if (pVehNPC->m_pVehicleInfo->surfDestruction && cent->ghoul2)
08051                 { //see if anything has been blown off
08052                         int i = 0;
08053                         qboolean surfDmg = qfalse;
08054 
08055                         while (i < BG_NUM_TOGGLEABLE_SURFACES)
08056                         {
08057                                 if (bgToggleableSurfaceDebris[i] > 1)
08058                                 { //this is decidedly a destroyable surface, let's check its status
08059                                         int surfTest = trap_G2API_GetSurfaceRenderStatus(cent->ghoul2, 0, bgToggleableSurfaces[i]);
08060 
08061                                         if ( surfTest != -1
08062                                                 && (surfTest&TURN_OFF) )
08063                                         { //it exists, but it's off...
08064                                                 surfDmg = qtrue;
08065 
08066                                                 //create some flames
08067                         CG_CreateSurfaceDebris(cent, i, cgs.effects.mShipDestBurning, qfalse);
08068                                         }
08069                                 }
08070 
08071                                 i++;
08072                         }
08073 
08074                         if (surfDmg)
08075                         { //if any surface are damaged, neglect exhaust etc effects (so we don't have exhaust trails coming out of invisible surfaces)
08076                                 return;
08077                         }
08078                 }
08079 
08080                 if ( pVehNPC->m_iLastFXTime <= cg.time )
08081                 {//until we attach it, we need to debounce this
08082                         vec3_t  fwd, rt, up;
08083                         vec3_t  flat;
08084                         float nextFXDelay = 50;
08085                         VectorSet(flat, 0, cent->lerpAngles[1], cent->lerpAngles[2]);
08086                         AngleVectors( flat, fwd, rt, up );
08087                         if ( cent->currentState.speed > 0 )
08088                         {//FIXME: only do this when accelerator is being pressed! (must have a driver?)
08089                                 vec3_t  org;
08090                                 qboolean doExhaust = qfalse;
08091                                 VectorMA( cent->lerpOrigin, -16, up, org );
08092                                 VectorMA( org, -42, fwd, org );
08093                                 // Play damage effects.
08094                                 //if ( pVehNPC->m_iArmor <= 75 )
08095                                 if (0)
08096                                 {//hurt
08097                                         trap_FX_PlayEffectID( cgs.effects.mBlackSmoke, org, fwd, -1, -1 );
08098                                 }
08099                                 else if ( pVehNPC->m_pVehicleInfo->iTrailFX )
08100                                 {//okay, do normal trail
08101                                         trap_FX_PlayEffectID( pVehNPC->m_pVehicleInfo->iTrailFX, org, fwd, -1, -1 );
08102                                 }
08103                                 //=====================================================================
08104                                 //EXHAUST FX
08105                                 //=====================================================================
08106                                 //do exhaust
08107                                 if ( (cent->currentState.eFlags&EF_JETPACK_ACTIVE) )
08108                                 {//cheap way of telling us the vehicle is in "turbo" mode
08109                                         doExhaust = (pVehNPC->m_pVehicleInfo->iTurboFX!=0);
08110                                 }
08111                                 else
08112                                 {
08113                                         doExhaust = (pVehNPC->m_pVehicleInfo->iExhaustFX!=0);
08114                                 }
08115                                 if ( doExhaust && cent->ghoul2 )
08116                                 {
08117                                         int i;
08118                                         int fx;
08119 
08120                                         for ( i = 0; i < MAX_VEHICLE_EXHAUSTS; i++ )
08121                                         {
08122                                                 // We hit an invalid tag, we quit (they should be created in order so tough luck if not).
08123                                                 if ( pVehNPC->m_iExhaustTag[i] == -1 )
08124                                                 {
08125                                                         break;
08126                                                 }
08127 
08128                                                 if ( (cent->currentState.brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_HEAVY)) )
08129                                                 {//engine has taken heavy damage
08130                                                         if ( !Q_irand( 0, 1 ) )
08131                                                         {//50% chance of not drawing this engine glow this frame
08132                                                                 continue;
08133                                                         }
08134                                                 }
08135                                                 else if ( (cent->currentState.brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_LIGHT)) )
08136                                                 {//engine has taken light damage
08137                                                         if ( !Q_irand( 0, 4 ) )
08138                                                         {//20% chance of not drawing this engine glow this frame
08139                                                                 continue;
08140                                                         }
08141                                                 }
08142 
08143                                                 if ( (cent->currentState.eFlags&EF_JETPACK_ACTIVE) //cheap way of telling us the vehicle is in "turbo" mode
08144                                                         && pVehNPC->m_pVehicleInfo->iTurboFX )//they have a valid turbo exhaust effect to play
08145                                                 {
08146                                                         fx = pVehNPC->m_pVehicleInfo->iTurboFX;
08147                                                 }
08148                                                 else
08149                                                 {//play the normal one
08150                                                         fx = pVehNPC->m_pVehicleInfo->iExhaustFX;
08151                                                 }
08152 
08153                                                 if (pVehNPC->m_pVehicleInfo->type == VH_FIGHTER)
08154                                                 {
08155                                                         trap_FX_PlayBoltedEffectID(fx, cent->lerpOrigin, cent->ghoul2, pVehNPC->m_iExhaustTag[i], 
08156                                                                                                                 cent->currentState.number, 0, 0, qtrue);
08157                                                 }
08158                                                 else
08159                                                 { //fixme: bolt these too
08160                                                         mdxaBone_t boltMatrix;
08161                                                         vec3_t boltOrg, boltDir;
08162 
08163                                                         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, pVehNPC->m_iExhaustTag[i], &boltMatrix, flat,
08164                                                                 cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
08165 
08166                                                         BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltOrg);
08167                                                         VectorCopy(fwd, boltDir); //fixme?
08168 
08169                                                         trap_FX_PlayEffectID( fx, boltOrg, boltDir, -1, -1 );
08170                                                 }
08171                                         }
08172                                 }
08173                                 //=====================================================================
08174                                 //WING TRAIL FX
08175                                 //=====================================================================
08176                                 //do trail
08177                                 //FIXME: not in space!!!
08178                                 if ( pVehNPC->m_pVehicleInfo->iTrailFX != 0 && cent->ghoul2 )
08179                                 {
08180                                         int i;
08181                                         vec3_t boltOrg, boltDir;
08182                                         mdxaBone_t boltMatrix;
08183                                         vec3_t getBoltAngles;
08184 
08185                                         VectorCopy(cent->lerpAngles, getBoltAngles);
08186                                         if (pVehNPC->m_pVehicleInfo->type != VH_FIGHTER)
08187                                         { //only fighters use pitch/roll in refent axis
08188                         getBoltAngles[PITCH] = getBoltAngles[ROLL] = 0.0f;
08189                                         }
08190 
08191                                         for ( i = 1; i < 5; i++ )
08192                                         {
08193                                                 int trailBolt = trap_G2API_AddBolt(cent->ghoul2, 0, va("*trail%d",i) );
08194                                                 // We hit an invalid tag, we quit (they should be created in order so tough luck if not).
08195                                                 if ( trailBolt == -1 )
08196                                                 {
08197                                                         break;
08198                                                 }
08199 
08200                                                 trap_G2API_GetBoltMatrix(cent->ghoul2, 0, trailBolt, &boltMatrix, getBoltAngles,
08201                                                         cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
08202 
08203                                                 BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltOrg);
08204                                                 VectorCopy(fwd, boltDir); //fixme?
08205 
08206                                                 trap_FX_PlayEffectID( pVehNPC->m_pVehicleInfo->iTrailFX, boltOrg, boltDir, -1, -1 );
08207                                         }
08208                                 }
08209                         }
08210                         //FIXME armor needs to be sent over network
08211                         {
08212                                 if ( (cent->currentState.eFlags&EF_DEAD) )
08213                                 {//just plain dead, use flames 
08214                                         vec3_t  up ={0,0,1};
08215                                         vec3_t boltOrg;
08216 
08217                                         //if ( pVehNPC->m_iDriverTag == -1 )
08218                                         {//doh!  no tag
08219                                                 VectorCopy( cent->lerpOrigin, boltOrg );
08220                                         }
08221                                         //else
08222                                         //{
08223                                         //      mdxaBone_t boltMatrix;
08224                                         //      vec3_t getBoltAngles;
08225 
08226                                         //      VectorCopy(cent->lerpAngles, getBoltAngles);
08227                                         //      if (pVehNPC->m_pVehicleInfo->type != VH_FIGHTER)
08228                                         //      { //only fighters use pitch/roll in refent axis
08229                                         //              getBoltAngles[PITCH] = getBoltAngles[ROLL] = 0.0f;
08230                                         //      }
08231 
08232                                         //      trap_G2API_GetBoltMatrix(cent->ghoul2, 0, pVehNPC->m_iDriverTag, &boltMatrix, getBoltAngles,
08233                                         //              cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
08234 
08235                                         //      BG_GiveMeVectorFromMatrix(&boltMatrix, ORIGIN, boltOrg);
08236                                         //}
08237                                         trap_FX_PlayEffectID( cgs.effects.mShipDestBurning, boltOrg, up, -1, -1 );
08238                                 }
08239                         }
08240                         if ( cent->currentState.brokenLimbs )
08241                         {
08242                                 int i;
08243                                 if ( !Q_irand( 0, 5 ) )
08244                                 {
08245                                         for ( i = SHIPSURF_FRONT; i <= SHIPSURF_LEFT; i++ )
08246                                         {
08247                                                 if ( (cent->currentState.brokenLimbs&(1<<((i-SHIPSURF_FRONT)+SHIPSURF_DAMAGE_FRONT_HEAVY))) )
08248                                                 {//heavy damage, do both effects
08249                                                         if ( pVehNPC->m_pVehicleInfo->iInjureFX )
08250                                                         {
08251                                                                 CG_CreateSurfaceSmoke( cent, i, pVehNPC->m_pVehicleInfo->iInjureFX );
08252                                                         }
08253                                                         if ( pVehNPC->m_pVehicleInfo->iDmgFX )
08254                                                         {
08255                                                                 CG_CreateSurfaceSmoke( cent, i, pVehNPC->m_pVehicleInfo->iDmgFX );
08256                                                         }
08257                                                 }
08258                                                 else if ( (cent->currentState.brokenLimbs&(1<<((i-SHIPSURF_FRONT)+SHIPSURF_DAMAGE_FRONT_LIGHT))) )
08259                                                 {//only light damage
08260                                                         if ( pVehNPC->m_pVehicleInfo->iInjureFX )
08261                                                         {
08262                                                                 CG_CreateSurfaceSmoke( cent, i, pVehNPC->m_pVehicleInfo->iInjureFX );
08263                                                         }
08264                                                 }
08265                                         }
08266                                 }
08267                         }
08268                         /*
08269                         if ( pVehNPC->m_iArmor <= 50 )
08270                         {//FIXME: use as a proportion of max armor?
08271                                 VectorMA( cent->lerpOrigin, 64, fwd, org );
08272                                 VectorScale( fwd, -1, fwd );
08273                                 
08274                                 trap_FX_PlayEffectID( cgs.effects.mBlackSmoke, org, fwd, -1, -1 );
08275                         }
08276                         if ( pVehNPC->m_iArmor <= 0 )
08277                         {//FIXME: should use something attached.. but want it to build up over time, so...
08278                                 if ( flrand( 0, cg.time - pVehNPC->m_iDieTime ) < 1000 )
08279                                 {//flaming!
08280                                         VectorMA( cent->lerpOrigin, flrand(-64, 64), fwd, org );
08281                                         VectorScale( fwd, -1, fwd );
08282                                         trap_FX_PlayEffectID( trap_FX_RegisterEffect("ships/fire"), org, fwd, -1, -1 );
08283                                         nextFXDelay = 50;
08284                                 }
08285                         }
08286                         */
08287                         pVehNPC->m_iLastFXTime = cg.time + nextFXDelay;
08288                 }
08289         }
08290 }
08291 
08292 /*
08293 ===============
08294 CG_Player
08295 ===============
08296 */
08297 #include "../namespace_begin.h"
08298 int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
08299 #include "../namespace_end.h"
08300 
08301 float CG_RadiusForCent( centity_t *cent )
08302 {
08303         if ( cent->currentState.eType == ET_NPC )
08304         {
08305                 if (cent->currentState.NPC_class == CLASS_VEHICLE &&
08306                         cent->m_pVehicle &&
08307                         cent->m_pVehicle->m_pVehicleInfo->g2radius)
08308                 { //has override
08309                         return cent->m_pVehicle->m_pVehicleInfo->g2radius;
08310                 }
08311                 else if ( cent->currentState.g2radius )
08312                 {
08313                         return cent->currentState.g2radius;
08314                 }
08315         }
08316         else if ( cent->currentState.g2radius )
08317         {
08318                 return cent->currentState.g2radius;
08319         }
08320         return 64.0f;
08321 }
08322 
08323 static float cg_vehThirdPersonAlpha = 1.0f;
08324 extern vec3_t   cg_crosshairPos;
08325 extern vec3_t   cameraCurLoc;
08326 void CG_CheckThirdPersonAlpha( centity_t *cent, refEntity_t *legs )
08327 {
08328         float alpha = 1.0f;             
08329         int     setFlags = 0;
08330 
08331         if ( cent->m_pVehicle )
08332         {//a vehicle 
08333                 if ( cg.predictedPlayerState.m_iVehicleNum != cent->currentState.clientNum//not mine
08334                         && cent->m_pVehicle->m_pVehicleInfo
08335                         && cent->m_pVehicle->m_pVehicleInfo->cameraOverride
08336                         && cent->m_pVehicle->m_pVehicleInfo->cameraAlpha )//it has alpha
08337                 {//make sure it's not using any alpha
08338                         legs->renderfx |= RF_FORCE_ENT_ALPHA;
08339                         legs->shaderRGBA[3] = 255;
08340                         return;
08341                 }
08342         }
08343 
08344         if ( !cg.renderingThirdPerson )
08345         {
08346                 return;
08347         }
08348 
08349         if ( cg.predictedPlayerState.m_iVehicleNum )
08350         {//in a vehicle
08351                 if ( cg.predictedPlayerState.m_iVehicleNum == cent->currentState.clientNum )
08352                 {//this is my vehicle
08353                         if ( cent->m_pVehicle
08354                                 && cent->m_pVehicle->m_pVehicleInfo
08355                                 && cent->m_pVehicle->m_pVehicleInfo->cameraOverride
08356                                 && cent->m_pVehicle->m_pVehicleInfo->cameraAlpha )
08357                         {//vehicle has auto third-person alpha on
08358                                 trace_t trace;
08359                                 vec3_t  dir2Crosshair, end;
08360                                 VectorSubtract( cg_crosshairPos, cameraCurLoc, dir2Crosshair );
08361                                 VectorNormalize( dir2Crosshair );
08362                                 VectorMA( cameraCurLoc, cent->m_pVehicle->m_pVehicleInfo->cameraRange*2.0f, dir2Crosshair, end );
08363                                 CG_G2Trace( &trace, cameraCurLoc, vec3_origin, vec3_origin, end, ENTITYNUM_NONE, CONTENTS_BODY );
08364                                 if ( trace.entityNum == cent->currentState.clientNum 
08365                                         || trace.entityNum == cg.predictedPlayerState.clientNum)
08366                                 {//hit me or the vehicle I'm in
08367                                         cg_vehThirdPersonAlpha -= 0.1f*cg.frametime/50.0f;
08368                                         if ( cg_vehThirdPersonAlpha < cent->m_pVehicle->m_pVehicleInfo->cameraAlpha )
08369                                         {
08370                                                 cg_vehThirdPersonAlpha = cent->m_pVehicle->m_pVehicleInfo->cameraAlpha;
08371                                         }
08372                                 }
08373                                 else
08374                                 {
08375                                         cg_vehThirdPersonAlpha += 0.1f*cg.frametime/50.0f;
08376                                         if ( cg_vehThirdPersonAlpha > 1.0f )
08377                                         {
08378                                                 cg_vehThirdPersonAlpha = 1.0f;
08379                                         }
08380                                 }
08381                                 alpha = cg_vehThirdPersonAlpha;
08382                         }
08383                         else
08384                         {//use the cvar
08385                                 //reset this
08386                                 cg_vehThirdPersonAlpha = 1.0f;
08387                                 //use the cvar
08388                                 alpha = cg_thirdPersonAlpha.value;
08389                         }
08390                 }
08391         }
08392         else if ( cg.predictedPlayerState.clientNum == cent->currentState.clientNum )
08393         {//it's me
08394                 //reset this
08395                 cg_vehThirdPersonAlpha = 1.0f;
08396                 //use the cvar
08397                 setFlags = RF_FORCE_ENT_ALPHA;
08398                 alpha = cg_thirdPersonAlpha.value;
08399         }
08400         
08401         if ( alpha < 1.0f )
08402         {
08403                 legs->renderfx |= setFlags;
08404                 legs->shaderRGBA[3] = (unsigned char)(alpha * 255.0f);
08405         }
08406 }
08407 
08408 void CG_Player( centity_t *cent ) {
08409         clientInfo_t    *ci;
08410         refEntity_t             legs;
08411         refEntity_t             torso;
08412         int                             clientNum;
08413         int                             renderfx;
08414         qboolean                shadow = qfalse;
08415         float                   shadowPlane = 0;
08416         qboolean                dead = qfalse;
08417         vec3_t                  rootAngles;
08418         float                   angle;
08419         vec3_t                  angles, dir, elevated, enang, seekorg;
08420         int                             iwantout = 0, successchange = 0;
08421         int                             team;
08422         mdxaBone_t              boltMatrix, lHandMatrix;
08423         int                             doAlpha = 0;
08424         qboolean                gotLHandMatrix = qfalse;
08425         qboolean                g2HasWeapon = qfalse;
08426         qboolean                drawPlayerSaber = qfalse;
08427         qboolean                checkDroidShields = qfalse;
08428 
08429         //first if we are not an npc and we are using an emplaced gun then make sure our
08430         //angles are visually capped to the constraints (otherwise it's possible to lerp
08431         //a little outside and look kind of twitchy)
08432         if (cent->currentState.weapon == WP_EMPLACED_GUN &&
08433                 cent->currentState.otherEntityNum2)
08434         {
08435                 float empYaw;
08436 
08437                 if (BG_EmplacedView(cent->lerpAngles, cg_entities[cent->currentState.otherEntityNum2].currentState.angles, &empYaw, cg_entities[cent->currentState.otherEntityNum2].currentState.origin2[0]))
08438                 {
08439                         cent->lerpAngles[YAW] = empYaw;
08440                 }
08441         }
08442 
08443         if (cent->currentState.iModelScale)
08444         { //if the server says we have a custom scale then set it now.
08445                 cent->modelScale[0] = cent->modelScale[1] = cent->modelScale[2] = cent->currentState.iModelScale/100.0f;
08446                 if ( cent->currentState.NPC_class != CLASS_VEHICLE )
08447                 {
08448                         if (cent->modelScale[2] && cent->modelScale[2] != 1.0f)
08449                         {
08450                                 cent->lerpOrigin[2] += 24 * (cent->modelScale[2] - 1);
08451                         }
08452                 }
08453         }
08454         else
08455         {
08456                 VectorClear(cent->modelScale);
08457         }
08458 
08459         if ((cg_smoothClients.integer || cent->currentState.heldByClient) && (cent->currentState.groundEntityNum >= ENTITYNUM_WORLD || cent->currentState.eType == ET_TERRAIN) &&
08460                 !(cent->currentState.eFlags2 & EF2_HYPERSPACE) && cg.predictedPlayerState.m_iVehicleNum != cent->currentState.number)
08461         { //always smooth when being thrown
08462                 vec3_t                  posDif;
08463                 float                   smoothFactor;
08464                 int                             k = 0;
08465                 float                   fTolerance = 20000.0f;
08466 
08467                 if (cent->currentState.heldByClient)
08468                 { //smooth the origin more when in this state, because movement is origin-based on server.
08469                         smoothFactor = 0.2f;
08470                 }
08471                 else if ( (cent->currentState.powerups & (1 << PW_SPEED)) ||
08472                         (cent->currentState.forcePowersActive & (1 << FP_RAGE)) )
08473                 { //we're moving fast so don't smooth as much
08474                         smoothFactor = 0.6f;
08475                 }
08476                 else if (cent->currentState.eType == ET_NPC && cent->currentState.NPC_class == CLASS_VEHICLE &&
08477                         cent->m_pVehicle && cent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
08478                 { //greater smoothing for flying vehicles, since they move so fast
08479                         fTolerance = 6000000.0f;//500000.0f; //yeah, this is so wrong..but..
08480                         smoothFactor = 0.5f;
08481                 }
08482                 else
08483                 {
08484                         smoothFactor = 0.5f;
08485                 }
08486                 
08487                 if (DistanceSquared(cent->beamEnd,cent->lerpOrigin) > smoothFactor*fTolerance) //10000
08488                 {
08489                         VectorCopy(cent->lerpOrigin, cent->beamEnd);
08490                 }
08491 
08492                 VectorSubtract(cent->lerpOrigin, cent->beamEnd, posDif);
08493                 
08494                 for (k=0;k<3;k++)
08495                 {
08496                         cent->beamEnd[k]=(cent->beamEnd[k]+posDif[k]*smoothFactor);
08497                         cent->lerpOrigin[k]=cent->beamEnd[k];
08498                 }
08499         }
08500         else
08501         {
08502                 VectorCopy(cent->lerpOrigin, cent->beamEnd);
08503         }
08504 
08505         if (cent->currentState.m_iVehicleNum &&
08506                 cent->currentState.NPC_class != CLASS_VEHICLE)
08507         { //this player is riding a vehicle
08508                 centity_t *veh = &cg_entities[cent->currentState.m_iVehicleNum];
08509 
08510                 cent->lerpAngles[YAW] = veh->lerpAngles[YAW];
08511 
08512                 //Attach ourself to the vehicle
08513         if (veh->m_pVehicle &&
08514                         cent->playerState &&
08515                         veh->playerState &&
08516                         cent->ghoul2 &&
08517                         veh->ghoul2 )
08518                 {
08519                         if ( veh->currentState.owner != cent->currentState.clientNum )
08520                         {//FIXME: what about visible passengers?
08521                                 if ( CG_VehicleAttachDroidUnit( cent, &legs ) )
08522                                 {
08523                                         checkDroidShields = qtrue;
08524                                 }
08525                         }
08526                         else if ( veh->currentState.owner != ENTITYNUM_NONE)
08527                         {//has a pilot...???
08528                                 vec3_t oldPSOrg;
08529 
08530                                 //make sure it has its pilot and parent set
08531                                 veh->m_pVehicle->m_pPilot = (bgEntity_t *)&cg_entities[veh->currentState.owner];
08532                                 veh->m_pVehicle->m_pParentEntity = (bgEntity_t *)veh;
08533                     
08534                                 VectorCopy(veh->playerState->origin, oldPSOrg);
08535 
08536                                 //update the veh's playerstate org for getting the bolt
08537                                 VectorCopy(veh->lerpOrigin, veh->playerState->origin);
08538                                 VectorCopy(cent->lerpOrigin, cent->playerState->origin);
08539 
08540                                 //Now do the attach
08541                                 VectorCopy(veh->lerpAngles, veh->playerState->viewangles);
08542                                 veh->m_pVehicle->m_pVehicleInfo->AttachRiders(veh->m_pVehicle);
08543 
08544                                 //copy the "playerstate origin" to the lerpOrigin since that's what we use to display
08545                                 VectorCopy(cent->playerState->origin, cent->lerpOrigin);
08546 
08547                                 VectorCopy(oldPSOrg, veh->playerState->origin);
08548                         }
08549                 }
08550         }
08551 
08552         // the client number is stored in clientNum.  It can't be derived
08553         // from the entity number, because a single client may have
08554         // multiple corpses on the level using the same clientinfo
08555         if (cent->currentState.eType != ET_NPC)
08556         {
08557                 clientNum = cent->currentState.clientNum;
08558                 if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
08559                         CG_Error( "Bad clientNum on player entity");
08560                 }
08561                 ci = &cgs.clientinfo[ clientNum ];
08562         }
08563         else
08564         {
08565                 if (!cent->npcClient)
08566                 {
08567                         CG_CreateNPCClient(&cent->npcClient); //allocate memory for it
08568 
08569                         if (!cent->npcClient)
08570                         {
08571                                 assert(0);
08572                                 return;
08573                         }
08574 
08575                         memset(cent->npcClient, 0, sizeof(clientInfo_t));
08576                         cent->npcClient->ghoul2Model = NULL;
08577                 }
08578 
08579                 assert(cent->npcClient);
08580 
08581                 if (cent->npcClient->ghoul2Model != cent->ghoul2 && cent->ghoul2)
08582                 {
08583                         cent->npcClient->ghoul2Model = cent->ghoul2;
08584                         if (cent->localAnimIndex <= 1)
08585                         {
08586                                 cent->npcClient->bolt_rhand = trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "*r_hand");
08587                                 cent->npcClient->bolt_lhand = trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "*l_hand");
08588 
08589                                 //rhand must always be first bolt. lhand always second. Whichever you want the
08590                                 //jetpack bolted to must always be third.
08591                                 trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "*chestg");
08592 
08593                                 //claw bolts
08594                                 trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "*r_hand_cap_r_arm");
08595                                 trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "*l_hand_cap_l_arm");
08596 
08597                                 cent->npcClient->bolt_head = trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "*head_top");
08598                                 if (cent->npcClient->bolt_head == -1)
08599                                 {
08600                                         cent->npcClient->bolt_head = trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "ceyebrow");
08601                                 }
08602                                 cent->npcClient->bolt_motion = trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "Motion");
08603                                 cent->npcClient->bolt_llumbar = trap_G2API_AddBolt(cent->npcClient->ghoul2Model, 0, "lower_lumbar");
08604                         }
08605                         else
08606                         {
08607                                 cent->npcClient->bolt_rhand = -1;
08608                                 cent->npcClient->bolt_lhand = -1;
08609                                 cent->npcClient->bolt_head = -1;
08610                                 cent->npcClient->bolt_motion = -1;
08611                                 cent->npcClient->bolt_llumbar = -1;
08612                         }
08613                         cent->npcClient->team = TEAM_FREE;
08614                         cent->npcClient->infoValid = qtrue;
08615                 }
08616                 ci = cent->npcClient;
08617         }
08618 
08619         // it is possible to see corpses from disconnected players that may
08620         // not have valid clientinfo
08621         if ( !ci->infoValid ) {
08622                 return;
08623         }
08624 
08625         // Add the player to the radar if on the same team and its a team game
08626         if (cgs.gametype >= GT_TEAM)
08627         {
08628                 if ( cent->currentState.eType != ET_NPC &&
08629                         cg.snap->ps.clientNum != cent->currentState.number &&
08630                         ci->team == cg.snap->ps.persistant[PERS_TEAM] )
08631                 {
08632                         CG_AddRadarEnt(cent);
08633                 }
08634         }
08635 
08636         if (cent->currentState.eType == ET_NPC &&
08637                 cent->currentState.NPC_class == CLASS_VEHICLE)
08638         { //add vehicles
08639                 CG_AddRadarEnt(cent);
08640                 if ( CG_InFighter() )
08641                 {//this is a vehicle, bracket it
08642                         if ( cg.predictedPlayerState.m_iVehicleNum != cent->currentState.clientNum )
08643                         {//don't add the vehicle I'm in... :)
08644                                 CG_AddBracketedEnt(cent);
08645                         }
08646                 }
08647 
08648         }
08649 
08650         if (!cent->ghoul2)
08651         { //not ready yet?
08652 #ifdef _DEBUG
08653                 Com_Printf("WARNING: Client %i has a null ghoul2 instance\n", cent->currentState.number);
08654 #endif
08655                 trap_G2API_ClearAttachedInstance(cent->currentState.number);
08656 
08657                 if (ci->ghoul2Model &&
08658                         trap_G2_HaveWeGhoul2Models(ci->ghoul2Model))
08659                 {
08660 #ifdef _DEBUG
08661                         Com_Printf("Clientinfo instance was valid, duplicating for cent\n");
08662 #endif
08663                         trap_G2API_DuplicateGhoul2Instance(ci->ghoul2Model, &cent->ghoul2);
08664 
08665                         //Attach the instance to this entity num so we can make use of client-server
08666                         //shared operations if possible.
08667                         trap_G2API_AttachInstanceToEntNum(cent->ghoul2, cent->currentState.number, qfalse);
08668 
08669                         if (trap_G2API_AddBolt(cent->ghoul2, 0, "face") == -1)
08670                         { //check now to see if we have this bone for setting anims and such
08671                                 cent->noFace = qtrue;
08672                         }
08673 
08674                         cent->localAnimIndex = CG_G2SkelForModel(cent->ghoul2);
08675                         cent->eventAnimIndex = CG_G2EvIndexForModel(cent->ghoul2, cent->localAnimIndex);
08676                 }
08677                 return;
08678         }
08679 
08680         if (ci->superSmoothTime)
08681         { //do crazy smoothing
08682                 if (ci->superSmoothTime > cg.time)
08683                 { //do it
08684                         trap_G2API_AbsurdSmoothing(cent->ghoul2, qtrue);
08685                 }
08686                 else
08687                 { //turn it off
08688                         ci->superSmoothTime = 0;
08689                         trap_G2API_AbsurdSmoothing(cent->ghoul2, qfalse);
08690                 }
08691         }
08692 
08693         if (cg.predictedPlayerState.pm_type == PM_INTERMISSION)
08694         { //don't show all this shit during intermission
08695                 if ( cent->currentState.eType == ET_NPC
08696                         && cent->currentState.NPC_class != CLASS_VEHICLE )
08697                 {//NPC in intermission
08698                 }
08699                 else
08700                 {//don't render players or vehicles in intermissions, allow other NPCs for scripts
08701                         return;
08702                 }
08703         }
08704 
08705         CG_VehicleEffects(cent);
08706 
08707         if ((cent->currentState.eFlags & EF_JETPACK) && !(cent->currentState.eFlags & EF_DEAD) &&
08708                 cg_g2JetpackInstance)
08709         { //should have a jetpack attached
08710                 //1 is rhand weap, 2 is lhand weap (akimbo sabs), 3 is jetpack
08711                 if (!trap_G2API_HasGhoul2ModelOnIndex(&(cent->ghoul2), 3))
08712                 {
08713                         trap_G2API_CopySpecificGhoul2Model(cg_g2JetpackInstance, 0, cent->ghoul2, 3); 
08714                 }
08715 
08716                 if (cent->currentState.eFlags & EF_JETPACK_ACTIVE)
08717                 {
08718                         mdxaBone_t mat;
08719                         vec3_t flamePos, flameDir;
08720                         int n = 0;
08721 
08722                         while (n < 2)
08723                         {
08724                                 //Get the position/dir of the flame bolt on the jetpack model bolted to the player
08725                                 trap_G2API_GetBoltMatrix(cent->ghoul2, 3, n, &mat, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
08726                                 BG_GiveMeVectorFromMatrix(&mat, ORIGIN, flamePos);
08727 
08728                                 if (n == 0)
08729                                 {
08730                                         BG_GiveMeVectorFromMatrix(&mat, NEGATIVE_Y, flameDir);
08731                                         VectorMA(flamePos, -9.5f, flameDir, flamePos);
08732                                         BG_GiveMeVectorFromMatrix(&mat, POSITIVE_X, flameDir);
08733                                         VectorMA(flamePos, -13.5f, flameDir, flamePos);
08734                                 }
08735                                 else
08736                                 {
08737                                         BG_GiveMeVectorFromMatrix(&mat, POSITIVE_X, flameDir);
08738                                         VectorMA(flamePos, -9.5f, flameDir, flamePos);
08739                                         BG_GiveMeVectorFromMatrix(&mat, NEGATIVE_Y, flameDir);
08740                                         VectorMA(flamePos, -13.5f, flameDir, flamePos);
08741                                 }
08742 
08743                                 if (cent->currentState.eFlags & EF_JETPACK_FLAMING)
08744                                 { //create effects
08745                                         //FIXME: Just one big effect
08746                                         //Play the effect
08747                                         trap_FX_PlayEffectID(cgs.effects.mBobaJet, flamePos, flameDir, -1, -1);
08748                                         trap_FX_PlayEffectID(cgs.effects.mBobaJet, flamePos, flameDir, -1, -1);
08749 
08750                                         //Keep the jet fire sound looping
08751                                         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, 
08752                                                 trap_S_RegisterSound( "sound/effects/fire_lp" ) );
08753                                 }
08754                                 else
08755                                 { //just idling
08756                                         //FIXME: Different smaller effect for idle
08757                                         //Play the effect
08758                                         trap_FX_PlayEffectID(cgs.effects.mBobaJet, flamePos, flameDir, -1, -1);
08759                                 }
08760 
08761                                 n++;
08762                         }
08763 
08764                         trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, 
08765                                 trap_S_RegisterSound( "sound/boba/JETHOVER" ) );
08766                 }
08767         }
08768         else if (trap_G2API_HasGhoul2ModelOnIndex(&(cent->ghoul2), 3))
08769         { //fixme: would be good if this could be done not every frame
08770                 trap_G2API_RemoveGhoul2Model(&(cent->ghoul2), 3);
08771         }
08772 
08773         g2HasWeapon = trap_G2API_HasGhoul2ModelOnIndex(&(cent->ghoul2), 1);
08774 
08775         if (!g2HasWeapon)
08776         { //force a redup of the weapon instance onto the client instance
08777                 cent->ghoul2weapon = NULL;
08778                 cent->weapon = 0;
08779         }
08780 
08781         if (cent->torsoBolt && !(cent->currentState.eFlags & EF_DEAD))
08782         { //he's alive and has a limb missing still, reattach it and reset the weapon
08783                 CG_ReattachLimb(cent);
08784         }
08785 
08786         if (cent->isRagging && !(cent->currentState.eFlags & EF_DEAD) && !(cent->currentState.eFlags & EF_RAG))
08787         { //make sure we don't ragdoll ever while alive unless directly told to with eFlags
08788                 cent->isRagging = qfalse;
08789                 trap_G2API_SetRagDoll(cent->ghoul2, NULL); //calling with null parms resets to no ragdoll.
08790         }
08791 
08792         if (cent->ghoul2 && cent->torsoBolt && ((cent->torsoBolt & RARMBIT) || (cent->torsoBolt & RHANDBIT) || (cent->torsoBolt & WAISTBIT)) && g2HasWeapon)
08793         { //kill the weapon if the limb holding it is no longer on the model
08794                 trap_G2API_RemoveGhoul2Model(&(cent->ghoul2), 1);
08795                 g2HasWeapon = qfalse;
08796         }
08797 
08798         if (!cent->trickAlphaTime || (cg.time - cent->trickAlphaTime) > 1000)
08799         { //things got out of sync, perhaps a new client is trying to fill in this slot
08800                 cent->trickAlpha = 255;
08801                 cent->trickAlphaTime = cg.time;
08802         }
08803 
08804         if (cent->currentState.eFlags & EF_NODRAW)
08805         { //If nodraw, return here
08806                 return;
08807         }
08808         else if (cent->currentState.eFlags2 & EF2_SHIP_DEATH)
08809         { //died in ship, don't draw, we were "obliterated"
08810                 return;
08811         }
08812 
08813         //If this client has tricked you.
08814         if (CG_IsMindTricked(cent->currentState.trickedentindex,
08815                 cent->currentState.trickedentindex2,
08816                 cent->currentState.trickedentindex3,
08817                 cent->currentState.trickedentindex4,
08818                 cg.snap->ps.clientNum))
08819         {
08820                 if (cent->trickAlpha > 1)
08821                 {
08822                         cent->trickAlpha -= (cg.time - cent->trickAlphaTime)*0.5;
08823                         cent->trickAlphaTime = cg.time;
08824 
08825                         if (cent->trickAlpha < 0)
08826                         {
08827                                 cent->trickAlpha = 0;
08828                         }
08829 
08830                         doAlpha = 1;
08831                 }
08832                 else
08833                 {
08834                         doAlpha = 1;
08835                         cent->trickAlpha = 1;
08836                         cent->trickAlphaTime = cg.time;
08837                         iwantout = 1;
08838                 }
08839         }
08840         else
08841         {
08842                 if (cent->trickAlpha < 255)
08843                 {
08844                         cent->trickAlpha += (cg.time - cent->trickAlphaTime);
08845                         cent->trickAlphaTime = cg.time;
08846 
08847                         if (cent->trickAlpha > 255)
08848                         {
08849                                 cent->trickAlpha = 255;
08850                         }
08851 
08852                         doAlpha = 1;
08853                 }
08854                 else
08855                 {
08856                         cent->trickAlpha = 255;
08857                         cent->trickAlphaTime = cg.time;
08858                 }
08859         }
08860 
08861         // get the player model information
08862         renderfx = 0;
08863         if ( cent->currentState.number == cg.snap->ps.clientNum) {
08864                 if (!cg.renderingThirdPerson) {
08865 #if 0
08866                         if (!cg_fpls.integer || cent->currentState.weapon != WP_SABER)
08867 #else
08868                         if (cent->currentState.weapon != WP_SABER)
08869 #endif
08870                         {
08871                                 renderfx = RF_THIRD_PERSON;                     // only draw in mirrors
08872                         }
08873                 } else {
08874                         if (cg_cameraMode.integer) {
08875                                 iwantout = 1;
08876 
08877                                 
08878                                 // goto minimal_add;
08879                                 
08880                                 // NOTENOTE Temporary
08881                                 return;
08882                         }
08883                 }
08884         }
08885 
08886         // Update the player's client entity information regarding weapons.
08887         // Explanation:  The entitystate has a weapond defined on it.  The cliententity does as well.
08888         // The cliententity's weapon tells us what the ghoul2 instance on the cliententity has bolted to it.
08889         // If the entitystate and cliententity weapons differ, then the state's needs to be copied to the client.
08890         // Save the old weapon, to verify that it is or is not the same as the new weapon.
08891         // rww - Make sure weapons don't get set BEFORE cent->ghoul2 is initialized or else we'll have no
08892         // weapon bolted on
08893         if (cent->currentState.saberInFlight)
08894         {
08895                 cent->ghoul2weapon = CG_G2WeaponInstance(cent, WP_SABER);
08896         }
08897 
08898         if (cent->ghoul2 && 
08899                 (cent->currentState.eType != ET_NPC || (cent->currentState.NPC_class != CLASS_VEHICLE&&cent->currentState.NPC_class != CLASS_REMOTE&&cent->currentState.NPC_class != CLASS_SEEKER)) && //don't add weapon models to NPCs that have no bolt for them!
08900                 cent->ghoul2weapon != CG_G2WeaponInstance(cent, cent->currentState.weapon) &&
08901                 !(cent->currentState.eFlags & EF_DEAD) && !cent->torsoBolt &&
08902                 cg.snap && (cent->currentState.number != cg.snap->ps.clientNum || (cg.snap->ps.pm_flags & PMF_FOLLOW)))
08903         {
08904                 if (ci->team == TEAM_SPECTATOR)
08905                 {
08906                         cent->ghoul2weapon = NULL;
08907                         cent->weapon = 0;
08908                 }
08909                 else
08910                 {
08911                         CG_CopyG2WeaponInstance(cent, cent->currentState.weapon, cent->ghoul2);
08912 
08913                         if (cent->currentState.eType != ET_NPC)
08914                         {
08915                                 if (cent->weapon == WP_SABER 
08916                                         && cent->weapon != cent->currentState.weapon 
08917                                         && !cent->currentState.saberHolstered)
08918                                 { //switching away from the saber
08919                                         //trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/saber/saberoffquick.wav" ));
08920                                         if (ci->saber[0].soundOff 
08921                                                 && !cent->currentState.saberHolstered)
08922                                         {
08923                                                 trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, ci->saber[0].soundOff);
08924                                         }
08925 
08926                                         if (ci->saber[1].soundOff &&
08927                                                 ci->saber[1].model[0] &&
08928                                                 !cent->currentState.saberHolstered)
08929                                         {
08930                                                 trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, ci->saber[1].soundOff);
08931                                         }
08932 
08933                                 }
08934                                 else if (cent->currentState.weapon == WP_SABER
08935                                         && cent->weapon != cent->currentState.weapon 
08936                                         && !cent->saberWasInFlight)
08937                                 { //switching to the saber
08938                                         //trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound( "sound/weapons/saber/saberon.wav" ));
08939                                         if (ci->saber[0].soundOn)
08940                                         {
08941                                                 trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, ci->saber[0].soundOn);
08942                                         }
08943 
08944                                         if (ci->saber[1].soundOn)
08945                                         {
08946                                                 trap_S_StartSound(cent->lerpOrigin, cent->currentState.number, CHAN_AUTO, ci->saber[1].soundOn);
08947                                         }
08948 
08949                                         BG_SI_SetDesiredLength(&ci->saber[0], 0, -1);
08950                                         BG_SI_SetDesiredLength(&ci->saber[1], 0, -1);
08951                                 }
08952                         }
08953 
08954                         cent->weapon = cent->currentState.weapon;
08955                         cent->ghoul2weapon = CG_G2WeaponInstance(cent, cent->currentState.weapon);
08956                 }
08957         }
08958         else if ((cent->currentState.eFlags & EF_DEAD) || cent->torsoBolt)
08959         {
08960                 cent->ghoul2weapon = NULL; //be sure to update after respawning/getting limb regrown
08961         }
08962 
08963         
08964         if (cent->saberWasInFlight && g2HasWeapon)
08965         {
08966                  cent->saberWasInFlight = qfalse;
08967         }
08968 
08969         memset (&legs, 0, sizeof(legs));
08970 
08971         CG_SetGhoul2Info(&legs, cent);
08972 
08973         VectorCopy(cent->modelScale, legs.modelScale);
08974         legs.radius = CG_RadiusForCent( cent );
08975         VectorClear(legs.angles);
08976 
08977         if (ci->colorOverride[0] != 0.0f ||
08978                 ci->colorOverride[1] != 0.0f ||
08979                 ci->colorOverride[2] != 0.0f)
08980         {
08981                 legs.shaderRGBA[0] = ci->colorOverride[0]*255.0f;
08982                 legs.shaderRGBA[1] = ci->colorOverride[1]*255.0f;
08983                 legs.shaderRGBA[2] = ci->colorOverride[2]*255.0f;
08984                 legs.shaderRGBA[3] = cent->currentState.customRGBA[3];
08985         }
08986         else
08987         {
08988                 legs.shaderRGBA[0] = cent->currentState.customRGBA[0];
08989                 legs.shaderRGBA[1] = cent->currentState.customRGBA[1];
08990                 legs.shaderRGBA[2] = cent->currentState.customRGBA[2];
08991                 legs.shaderRGBA[3] = cent->currentState.customRGBA[3];
08992         }
08993 
08994 // minimal_add:
08995 
08996         team = ci->team;
08997 
08998         if (cgs.gametype >= GT_TEAM && cg_drawFriend.integer &&
08999                 cent->currentState.number != cg.snap->ps.clientNum &&
09000                 cent->currentState.eType != ET_NPC)
09001         {       // If the view is either a spectator or on the same team as this character, show a symbol above their head.
09002                 if ((cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR || cg.snap->ps.persistant[PERS_TEAM] == team) &&
09003                         !(cent->currentState.eFlags & EF_DEAD))
09004                 {
09005                         if (cgs.gametype == GT_SIEGE)
09006                         { //check for per-map team shaders
09007                                 if (team == SIEGETEAM_TEAM1)
09008                                 {
09009                                         if (cgSiegeTeam1PlShader)
09010                                         {
09011                                                 CG_PlayerFloatSprite( cent, cgSiegeTeam1PlShader);
09012                                         }
09013                                         else
09014                                         { //if there isn't one fallback to default
09015                                                 CG_PlayerFloatSprite( cent, cgs.media.teamRedShader);
09016                                         }
09017                                 }
09018                                 else
09019                                 {
09020                                         if (cgSiegeTeam2PlShader)
09021                                         {
09022                                                 CG_PlayerFloatSprite( cent, cgSiegeTeam2PlShader);
09023                                         }
09024                                         else
09025                                         { //if there isn't one fallback to default
09026                                                 CG_PlayerFloatSprite( cent, cgs.media.teamBlueShader);
09027                                         }
09028                                 }
09029                         }
09030                         else
09031                         { //generic teamplay
09032                                 if (team == TEAM_RED)
09033                                 {
09034                                         CG_PlayerFloatSprite( cent, cgs.media.teamRedShader);
09035                                 }
09036                                 else    // if (team == TEAM_BLUE)
09037                                 {
09038                                         CG_PlayerFloatSprite( cent, cgs.media.teamBlueShader);
09039                                 }
09040                         }
09041                 }
09042         }
09043         else if (cgs.gametype == GT_POWERDUEL && cg_drawFriend.integer &&
09044                 cent->currentState.number != cg.snap->ps.clientNum)
09045         {
09046                 if (cg.predictedPlayerState.persistant[PERS_TEAM] != TEAM_SPECTATOR &&
09047                         cent->currentState.number < MAX_CLIENTS &&
09048                         !(cent->currentState.eFlags & EF_DEAD) &&
09049                         ci &&
09050                         cgs.clientinfo[cg.snap->ps.clientNum].duelTeam == ci->duelTeam)
09051                 { //ally in powerduel, so draw the icon
09052                         CG_PlayerFloatSprite( cent, cgs.media.powerDuelAllyShader);
09053                 }
09054                 else if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_SPECTATOR &&
09055                         cent->currentState.number < MAX_CLIENTS &&
09056                         !(cent->currentState.eFlags & EF_DEAD) &&
09057                         ci->duelTeam == DUELTEAM_DOUBLE)
09058                 {
09059                         CG_PlayerFloatSprite( cent, cgs.media.powerDuelAllyShader);
09060                 }
09061         }
09062 
09063         if (cgs.gametype == GT_JEDIMASTER && cg_drawFriend.integer &&
09064                 cent->currentState.number != cg.snap->ps.clientNum)                     // Don't show a sprite above a player's own head in 3rd person.
09065         {       // If the view is either a spectator or on the same team as this character, show a symbol above their head.
09066                 if ((cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR || cg.snap->ps.persistant[PERS_TEAM] == team) &&
09067                         !(cent->currentState.eFlags & EF_DEAD))
09068                 {
09069                         if (CG_ThereIsAMaster())
09070                         {
09071                                 if (!cg.snap->ps.isJediMaster)
09072                                 {
09073                                         if (!cent->currentState.isJediMaster)
09074                                         {
09075                                                 CG_PlayerFloatSprite( cent, cgs.media.teamRedShader);
09076                                         }
09077                                 }
09078                         }
09079                 }
09080         }
09081 
09082         // add the shadow
09083         shadow = CG_PlayerShadow( cent, &shadowPlane );
09084 
09085         if ( ((cent->currentState.eFlags & EF_SEEKERDRONE) || cent->currentState.genericenemyindex != -1) && cent->currentState.eType != ET_NPC )
09086         {
09087                 refEntity_t             seeker;
09088 
09089                 memset( &seeker, 0, sizeof(seeker) );
09090 
09091                 VectorCopy(cent->lerpOrigin, elevated);
09092                 elevated[2] += 40;
09093 
09094                 VectorCopy( elevated, seeker.lightingOrigin );
09095                 seeker.shadowPlane = shadowPlane;
09096                 seeker.renderfx = 0; //renderfx;
09097                                                          //don't show in first person?
09098 
09099                 angle = ((cg.time / 12) & 255) * (M_PI * 2) / 255;
09100                 dir[0] = cos(angle) * 20;
09101                 dir[1] = sin(angle) * 20;
09102                 dir[2] = cos(angle) * 5;
09103                 VectorAdd(elevated, dir, seeker.origin);
09104 
09105                 VectorCopy(seeker.origin, seekorg);
09106 
09107                 if (cent->currentState.genericenemyindex > MAX_GENTITIES)
09108                 {
09109                         float prefig = (cent->currentState.genericenemyindex-cg.time)/80;
09110 
09111                         if (prefig > 55)
09112                         {
09113                                 prefig = 55;
09114                         }
09115                         else if (prefig < 1)
09116                         {
09117                                 prefig = 1;
09118                         }
09119 
09120                         elevated[2] -= 55-prefig;
09121 
09122                         angle = ((cg.time / 12) & 255) * (M_PI * 2) / 255;
09123                         dir[0] = cos(angle) * 20;
09124                         dir[1] = sin(angle) * 20;
09125                         dir[2] = cos(angle) * 5;
09126                         VectorAdd(elevated, dir, seeker.origin);
09127                 }
09128                 else if (cent->currentState.genericenemyindex != ENTITYNUM_NONE && cent->currentState.genericenemyindex != -1)
09129                 {
09130                         centity_t *enent = &cg_entities[cent->currentState.genericenemyindex];
09131 
09132                         if (enent)
09133                         {
09134                                 VectorSubtract(enent->lerpOrigin, seekorg, enang);
09135                                 VectorNormalize(enang);
09136                                 vectoangles(enang, angles);
09137                                 successchange = 1;
09138                         }
09139                 }
09140 
09141                 if (!successchange)
09142                 {
09143                         angles[0] = sin(angle) * 30;
09144                         angles[1] = (angle * 180 / M_PI) + 90;
09145                         if (angles[1] > 360)
09146                                 angles[1] -= 360;
09147                         angles[2] = 0;
09148                 }
09149 
09150                 AnglesToAxis( angles, seeker.axis );
09151 
09152                 seeker.hModel = trap_R_RegisterModel("models/items/remote.md3");
09153                 trap_R_AddRefEntityToScene( &seeker );
09154         }
09155 
09156         // add a water splash if partially in and out of water
09157         CG_PlayerSplash( cent );
09158 
09159         if ( (cg_shadows.integer == 3 || cg_shadows.integer == 2) && shadow ) {
09160                 renderfx |= RF_SHADOW_PLANE;
09161         }
09162         renderfx |= RF_LIGHTING_ORIGIN;                 // use the same origin for all
09163 
09164         // if we've been hit, display proper fullscreen fx
09165         CG_PlayerHitFX(cent);
09166 
09167         VectorCopy( cent->lerpOrigin, legs.origin );
09168 
09169         VectorCopy( cent->lerpOrigin, legs.lightingOrigin );
09170         legs.shadowPlane = shadowPlane;
09171         legs.renderfx = renderfx;
09172         if (cg_shadows.integer == 2 && (renderfx & RF_THIRD_PERSON))
09173         { //can see own shadow
09174                 legs.renderfx |= RF_SHADOW_ONLY;
09175         }
09176         VectorCopy (legs.origin, legs.oldorigin);       // don't positionally lerp at all
09177 
09178         CG_G2PlayerAngles( cent, legs.axis, rootAngles );
09179         CG_G2PlayerHeadAnims( cent );
09180 
09181         if ( (cent->currentState.eFlags2&EF2_HELD_BY_MONSTER) 
09182                 && cent->currentState.hasLookTarget )//NOTE: lookTarget is an entity number, so this presumes that client 0 is NOT a Rancor...
09183         {
09184                 centity_t       *rancor = &cg_entities[cent->currentState.lookTarget];
09185                 if ( rancor )
09186                 {
09187                         BG_AttachToRancor( rancor->ghoul2, //ghoul2 info
09188                                 rancor->lerpAngles[YAW],
09189                                 rancor->lerpOrigin,
09190                                 cg.time,
09191                                 cgs.gameModels,
09192                                 rancor->modelScale,
09193                                 (rancor->currentState.eFlags2&EF2_GENERIC_NPC_FLAG),
09194                                 legs.origin,
09195                                 legs.angles,
09196                                 NULL );
09197 
09198                         if ( cent->isRagging )
09199                         {//hack, ragdoll has you way at bottom of bounding box
09200                                 VectorMA( legs.origin, 32, legs.axis[2], legs.origin );
09201                         }
09202                         VectorCopy( legs.origin, legs.oldorigin );
09203                         VectorCopy( legs.origin, legs.lightingOrigin );
09204 
09205                         VectorCopy( legs.angles, cent->lerpAngles );
09206                         VectorCopy( cent->lerpAngles, rootAngles );//??? tempAngles );//tempAngles is needed a lot below
09207                         VectorCopy( cent->lerpAngles, cent->turAngles );
09208                         VectorCopy( legs.origin, cent->lerpOrigin );
09209                 }
09210         }
09211         //This call is mainly just to reconstruct the skeleton. But we'll get the left hand matrix while we're at it.
09212         //If we don't reconstruct the skeleton after setting the bone angles, we will get bad bolt points on the model
09213         //(e.g. the weapon model bolt will look "lagged") if there's no other GetBoltMatrix call for the rest of the
09214         //frame. Yes, this is stupid and needs to be fixed properly.
09215         //The current solution is to force it not to reconstruct the skeleton for the first GBM call in G2PlayerAngles.
09216         //It works and we end up only reconstructing it once, but it doesn't seem like the best solution.
09217         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &lHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09218         gotLHandMatrix = qtrue;
09219 #if 0
09220         if (cg.renderingThirdPerson)
09221         {
09222                 if (cgFPLSState != 0)
09223                 {
09224                         CG_ForceFPLSPlayerModel(cent, ci);
09225                         cgFPLSState = 0;
09226                         return;
09227                 }
09228         }
09229         else if (ci->team == TEAM_SPECTATOR || (cg.snap && (cg.snap->ps.pm_flags & PMF_FOLLOW)))
09230         { //don't allow this when spectating
09231                 if (cgFPLSState != 0)
09232                 {
09233                         trap_Cvar_Set("cg_fpls", "0");
09234                         cg_fpls.integer = 0;
09235 
09236                         CG_ForceFPLSPlayerModel(cent, ci);
09237                         cgFPLSState = 0;
09238                         return;
09239                 }
09240 
09241                 if (cg_fpls.integer)
09242                 {
09243                         trap_Cvar_Set("cg_fpls", "0");
09244                 }
09245         }
09246         else
09247         {
09248                 if (cg_fpls.integer && cent->currentState.weapon == WP_SABER && cg.snap && cent->currentState.number == cg.snap->ps.clientNum)
09249                 {
09250 
09251                         if (cgFPLSState != cg_fpls.integer)
09252                         {
09253                                 CG_ForceFPLSPlayerModel(cent, ci);
09254                                 cgFPLSState = cg_fpls.integer;
09255                                 return;
09256                         }
09257 
09258                         /*
09259                         mdxaBone_t              headMatrix;
09260                         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_head, &headMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09261                         BG_GiveMeVectorFromMatrix(&headMatrix, ORIGIN, cg.refdef.vieworg);
09262                         */
09263                 }
09264                 else if (!cg_fpls.integer && cgFPLSState)
09265                 {
09266                         if (cgFPLSState != cg_fpls.integer)
09267                         {
09268                                 CG_ForceFPLSPlayerModel(cent, ci);
09269                                 cgFPLSState = cg_fpls.integer;
09270                                 return;
09271                         }
09272                 }
09273         }
09274 #endif
09275 
09276         if (cent->currentState.eFlags & EF_DEAD)
09277         {
09278                 dead = qtrue;
09279                 //rww - since our angles are fixed when we're dead this shouldn't be an issue anyway
09280                 //we need to render the dying/dead player because we are now spawning the body on respawn instead of death
09281                 //return;
09282         }
09283 
09284         ScaleModelAxis(&legs);
09285 
09286         memset( &torso, 0, sizeof(torso) );
09287 
09288         //rww - force speed "trail" effect
09289         if (!(cent->currentState.powerups & (1 << PW_SPEED)) || doAlpha || !cg_speedTrail.integer)
09290         {
09291                 cent->frame_minus1_refreshed = 0;
09292                 cent->frame_minus2_refreshed = 0;
09293         }
09294 
09295         if (cent->frame_minus1_refreshed ||
09296                 cent->frame_minus2_refreshed)
09297         {
09298                 vec3_t                  tDir;
09299                 int                             distVelBase;
09300 
09301                 VectorCopy(cent->currentState.pos.trDelta, tDir);
09302                 distVelBase = SPEED_TRAIL_DISTANCE*(VectorNormalize(tDir)*0.004);
09303 
09304                 if (cent->frame_minus1_refreshed)
09305                 {
09306                         refEntity_t reframe_minus1 = legs;
09307                         reframe_minus1.renderfx |= RF_FORCE_ENT_ALPHA;
09308                         reframe_minus1.shaderRGBA[0] = legs.shaderRGBA[0];
09309                         reframe_minus1.shaderRGBA[1] = legs.shaderRGBA[1];
09310                         reframe_minus1.shaderRGBA[2] = legs.shaderRGBA[2];
09311                         reframe_minus1.shaderRGBA[3] = 100;
09312 
09313                         //rww - if the client gets a bad framerate we will only receive frame positions
09314                         //once per frame anyway, so we might end up with speed trails very spread out.
09315                         //in order to avoid that, we'll get the direction of the last trail from the player
09316                         //and place the trail refent a set distance from the player location this frame
09317                         VectorSubtract(cent->frame_minus1, legs.origin, tDir);
09318                         VectorNormalize(tDir);
09319 
09320                         cent->frame_minus1[0] = legs.origin[0]+tDir[0]*distVelBase;
09321                         cent->frame_minus1[1] = legs.origin[1]+tDir[1]*distVelBase;
09322                         cent->frame_minus1[2] = legs.origin[2]+tDir[2]*distVelBase;
09323 
09324                         VectorCopy(cent->frame_minus1, reframe_minus1.origin);
09325 
09326                         //reframe_minus1.customShader = 2;
09327 
09328                         trap_R_AddRefEntityToScene(&reframe_minus1);
09329                 }
09330 
09331                 if (cent->frame_minus2_refreshed)
09332                 {
09333                         refEntity_t reframe_minus2 = legs;
09334 
09335                         reframe_minus2.renderfx |= RF_FORCE_ENT_ALPHA;
09336                         reframe_minus2.shaderRGBA[0] = legs.shaderRGBA[0];
09337                         reframe_minus2.shaderRGBA[1] = legs.shaderRGBA[1];
09338                         reframe_minus2.shaderRGBA[2] = legs.shaderRGBA[2];
09339                         reframe_minus2.shaderRGBA[3] = 50;
09340 
09341                         //Same as above but do it between trail points instead of the player and first trail entry
09342                         VectorSubtract(cent->frame_minus2, cent->frame_minus1, tDir);
09343                         VectorNormalize(tDir);
09344 
09345                         cent->frame_minus2[0] = cent->frame_minus1[0]+tDir[0]*distVelBase;
09346                         cent->frame_minus2[1] = cent->frame_minus1[1]+tDir[1]*distVelBase;
09347                         cent->frame_minus2[2] = cent->frame_minus1[2]+tDir[2]*distVelBase;
09348 
09349                         VectorCopy(cent->frame_minus2, reframe_minus2.origin);
09350 
09351                         //reframe_minus2.customShader = 2;
09352 
09353                         trap_R_AddRefEntityToScene(&reframe_minus2);
09354                 }
09355         }
09356 
09357         //trigger animation-based sounds, done before next lerp frame.
09358         CG_TriggerAnimSounds(cent);
09359 
09360         // get the animation state (after rotation, to allow feet shuffle)
09361         CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp,
09362                  &torso.oldframe, &torso.frame, &torso.backlerp );
09363 
09364         // add the talk baloon or disconnect icon
09365         CG_PlayerSprites( cent );
09366 
09367         if (cent->currentState.eFlags & EF_DEAD)
09368         { //keep track of death anim frame for when we copy off the bodyqueue
09369                 ci->frame = cent->pe.torso.frame;
09370         }
09371 
09372         if (cent->currentState.activeForcePass > FORCE_LEVEL_3
09373                 && cent->currentState.NPC_class != CLASS_VEHICLE)
09374         {
09375                 vec3_t axis[3];
09376                 vec3_t tAng, fAng, fxDir;
09377                 vec3_t efOrg;
09378 
09379                 int realForceLev = (cent->currentState.activeForcePass - FORCE_LEVEL_3);
09380 
09381                 VectorSet( tAng, cent->turAngles[PITCH], cent->turAngles[YAW], cent->turAngles[ROLL] );
09382 
09383                 VectorSet( fAng, cent->pe.torso.pitchAngle, cent->pe.torso.yawAngle, 0 );
09384 
09385                 AngleVectors( fAng, fxDir, NULL, NULL );
09386 
09387                 if ( cent->currentState.torsoAnim == BOTH_FORCE_2HANDEDLIGHTNING_HOLD 
09388                         && Q_irand( 0, 1 ) )
09389                 {//alternate back and forth between left and right
09390                         mdxaBone_t      rHandMatrix;
09391                         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_rhand, &rHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09392                         efOrg[0] = rHandMatrix.matrix[0][3];
09393                         efOrg[1] = rHandMatrix.matrix[1][3];
09394                         efOrg[2] = rHandMatrix.matrix[2][3];
09395                 }
09396                 else
09397                 {
09398                         //trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09399                         if (!gotLHandMatrix)
09400                         {
09401                                 trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &lHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09402                                 gotLHandMatrix = qtrue;
09403                         }
09404                         efOrg[0] = lHandMatrix.matrix[0][3];
09405                         efOrg[1] = lHandMatrix.matrix[1][3];
09406                         efOrg[2] = lHandMatrix.matrix[2][3];
09407                 }
09408 
09409                 AnglesToAxis( fAng, axis );
09410         
09411                 if ( realForceLev > FORCE_LEVEL_2 )
09412                 {//arc
09413                         //trap_FX_PlayEffectID( cgs.effects.forceLightningWide, efOrg, fxDir );
09414                         //trap_FX_PlayEntityEffectID(cgs.effects.forceDrainWide, efOrg, axis, cent->boltInfo, cent->currentState.number, -1, -1);
09415                         trap_FX_PlayEntityEffectID(cgs.effects.forceDrainWide, efOrg, axis, -1, -1, -1, -1);
09416                 }
09417                 else
09418                 {//line
09419                         //trap_FX_PlayEffectID( cgs.effects.forceLightning, efOrg, fxDir );
09420                         //trap_FX_PlayEntityEffectID(cgs.effects.forceDrain, efOrg, axis, cent->boltInfo, cent->currentState.number, -1, -1);
09421                         trap_FX_PlayEntityEffectID(cgs.effects.forceDrain, efOrg, axis, -1, -1, -1, -1);
09422                 }
09423 
09424                 /*
09425                 if (cent->bolt4 < cg.time)
09426                 {
09427                         cent->bolt4 = cg.time + 100;
09428                         trap_S_StartSound(NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound("sound/weapons/force/drain.wav") );
09429                 }
09430                 */
09431         }
09432         else if ( cent->currentState.activeForcePass 
09433                 && cent->currentState.NPC_class != CLASS_VEHICLE)
09434         {//doing the electrocuting
09435                 vec3_t axis[3];
09436                 vec3_t tAng, fAng, fxDir;
09437                 vec3_t efOrg;
09438 
09439                 VectorSet( tAng, cent->turAngles[PITCH], cent->turAngles[YAW], cent->turAngles[ROLL] );
09440 
09441                 VectorSet( fAng, cent->pe.torso.pitchAngle, cent->pe.torso.yawAngle, 0 );
09442 
09443                 AngleVectors( fAng, fxDir, NULL, NULL );
09444 
09445                 //trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09446                 if (!gotLHandMatrix)
09447                 {
09448                         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &lHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09449                         gotLHandMatrix = qtrue;
09450                 }
09451 
09452                 efOrg[0] = lHandMatrix.matrix[0][3];
09453                 efOrg[1] = lHandMatrix.matrix[1][3];
09454                 efOrg[2] = lHandMatrix.matrix[2][3];
09455 
09456                 AnglesToAxis( fAng, axis );
09457         
09458                 if ( cent->currentState.activeForcePass > FORCE_LEVEL_2 )
09459                 {//arc
09460                         //trap_FX_PlayEffectID( cgs.effects.forceLightningWide, efOrg, fxDir );
09461                         //trap_FX_PlayEntityEffectID(cgs.effects.forceLightningWide, efOrg, axis, cent->boltInfo, cent->currentState.number, -1, -1);
09462                         trap_FX_PlayEntityEffectID(cgs.effects.forceLightningWide, efOrg, axis, -1, -1, -1, -1);
09463                 }
09464                 else
09465                 {//line
09466                         //trap_FX_PlayEffectID( cgs.effects.forceLightning, efOrg, fxDir );
09467                         //trap_FX_PlayEntityEffectID(cgs.effects.forceLightning, efOrg, axis, cent->boltInfo, cent->currentState.number, -1, -1);
09468                         trap_FX_PlayEntityEffectID(cgs.effects.forceLightning, efOrg, axis, -1, -1, -1, -1);
09469                 }
09470 
09471                 /*
09472                 if (cent->bolt4 < cg.time)
09473                 {
09474                         cent->bolt4 = cg.time + 100;
09475                         trap_S_StartSound(NULL, cent->currentState.number, CHAN_AUTO, trap_S_RegisterSound("sound/weapons/force/lightning.wav") );
09476                 }
09477                 */
09478         }
09479 
09480         //fullbody push effect
09481         if (cent->currentState.eFlags & EF_BODYPUSH)
09482         {
09483                 CG_ForcePushBodyBlur(cent);
09484         }
09485 
09486         if ( cent->currentState.powerups & (1 << PW_DISINT_4) )
09487         {
09488                 vec3_t tAng;
09489                 vec3_t efOrg;
09490 
09491                 //VectorSet( tAng, 0, cent->pe.torso.yawAngle, 0 );
09492                 VectorSet( tAng, cent->turAngles[PITCH], cent->turAngles[YAW], cent->turAngles[ROLL] );
09493 
09494                 //trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09495                 if (!gotLHandMatrix)
09496                 {
09497                         trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &lHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
09498                         gotLHandMatrix = qtrue;
09499                 }
09500 
09501                 efOrg[0] = lHandMatrix.matrix[0][3];
09502                 efOrg[1] = lHandMatrix.matrix[1][3];
09503                 efOrg[2] = lHandMatrix.matrix[2][3];
09504 
09505                 if ( (cent->currentState.forcePowersActive & (1 << FP_GRIP)) &&
09506                         (cg.renderingThirdPerson || cent->currentState.number != cg.snap->ps.clientNum) )
09507                 {
09508                         vec3_t boltDir;
09509                         vec3_t origBolt;
09510                         VectorCopy(efOrg, origBolt);
09511                         BG_GiveMeVectorFromMatrix( &lHandMatrix, NEGATIVE_Y, boltDir );
09512 
09513                         CG_ForceGripEffect( efOrg );
09514                         CG_ForceGripEffect( efOrg );
09515 
09516                         /*
09517                         //Render a scaled version of the model's hand with a n337 looking shader
09518                         {
09519                                 const char *rotateBone;
09520                                 char *limbName;
09521                                 char *limbCapName;
09522                                 vec3_t armAng;
09523                                 refEntity_t regrip_arm;
09524                                 float wv = sin( cg.time * 0.003f ) * 0.08f + 0.1f;
09525 
09526                                 //rotateBone = "lradius";
09527                                 rotateBone = "lradiusX";
09528                                 limbName = "l_arm";
09529                                 limbCapName = "l_arm_cap_torso";
09530 
09531                                 if (cent->grip_arm && trap_G2_HaveWeGhoul2Models(cent->grip_arm))
09532                                 {
09533                                         trap_G2API_CleanGhoul2Models(&(cent->grip_arm));
09534                                 }
09535 
09536                                 memset( &regrip_arm, 0, sizeof(regrip_arm) );
09537 
09538                                 VectorCopy(origBolt, efOrg);
09539 
09540 
09541                                 //efOrg[2] += 8;
09542                                 efOrg[2] -= 4;
09543 
09544                                 VectorCopy(efOrg, regrip_arm.origin);
09545                                 VectorCopy(regrip_arm.origin, regrip_arm.lightingOrigin);
09546 
09547                                 //VectorCopy(cent->lerpAngles, armAng);
09548                                 VectorAdd(vec3_origin, rootAngles, armAng);
09549                                 //armAng[ROLL] = -90;
09550                                 armAng[ROLL] = 0;
09551                                 armAng[PITCH] = 0;
09552                                 AnglesToAxis(armAng, regrip_arm.axis);
09553                                 
09554                                 trap_G2API_DuplicateGhoul2Instance(cent->ghoul2, &cent->grip_arm);
09555 
09556                                 //remove all other models
09557                                 if (trap_G2API_HasGhoul2ModelOnIndex(&(cent->grip_arm), 1))
09558                                 { //weapon right
09559                                         trap_G2API_RemoveGhoul2Model(&(cent->grip_arm), 1);
09560                                 }
09561                                 if (trap_G2API_HasGhoul2ModelOnIndex(&(cent->grip_arm), 2))
09562                                 { //weapon left
09563                                         trap_G2API_RemoveGhoul2Model(&(cent->grip_arm), 2);
09564                                 }
09565                                 if (trap_G2API_HasGhoul2ModelOnIndex(&(cent->grip_arm), 3))
09566                                 { //jetpack
09567                                         trap_G2API_RemoveGhoul2Model(&(cent->grip_arm), 3);
09568                                 }
09569 
09570                                 trap_G2API_SetRootSurface(cent->grip_arm, 0, limbName);
09571                                 trap_G2API_SetNewOrigin(cent->grip_arm, trap_G2API_AddBolt(cent->grip_arm, 0, rotateBone));
09572                                 trap_G2API_SetSurfaceOnOff(cent->grip_arm, limbCapName, 0);
09573 
09574                                 regrip_arm.modelScale[0] = 1;//+(wv*6);
09575                                 regrip_arm.modelScale[1] = 1;//+(wv*6);
09576                                 regrip_arm.modelScale[2] = 1;//+(wv*6);
09577                                 ScaleModelAxis(&regrip_arm);
09578 
09579                                 regrip_arm.radius = 64;
09580 
09581                                 regrip_arm.customShader = trap_R_RegisterShader( "gfx/misc/red_portashield" );
09582                                 
09583                                 regrip_arm.renderfx |= RF_RGB_TINT;
09584                                 regrip_arm.shaderRGBA[0] = 255 - (wv*900);
09585                                 if (regrip_arm.shaderRGBA[0] < 30)
09586                                 {
09587                                         regrip_arm.shaderRGBA[0] = 30;
09588                                 }
09589                                 if (regrip_arm.shaderRGBA[0] > 255)
09590                                 {
09591                                         regrip_arm.shaderRGBA[0] = 255;
09592                                 }
09593                                 regrip_arm.shaderRGBA[1] = regrip_arm.shaderRGBA[2] = regrip_arm.shaderRGBA[0];
09594                                 
09595                                 regrip_arm.ghoul2 = cent->grip_arm;
09596                                 trap_R_AddRefEntityToScene( &regrip_arm );
09597                         }
09598                         */
09599                 }
09600                 else if (!(cent->currentState.forcePowersActive & (1 << FP_GRIP)))
09601                 {
09602                         //use refractive effect
09603                         CG_ForcePushBlur( efOrg, cent );
09604                 }
09605         }
09606         else if (cent->bodyFadeTime)
09607         { //reset the counter for keeping track of push refraction effect state
09608                 cent->bodyFadeTime = 0;
09609         }
09610 
09611         if (cent->currentState.weapon == WP_STUN_BATON && cent->currentState.number == cg.snap->ps.clientNum)
09612         {
09613                 trap_S_AddLoopingSound( cent->currentState.number, cg.refdef.vieworg, vec3_origin, 
09614                         trap_S_RegisterSound( "sound/weapons/baton/idle.wav" ) );
09615         }
09616 
09617         //NOTE: All effects that should be visible during mindtrick should go above here
09618 
09619         if (iwantout)
09620         {
09621                 goto stillDoSaber;
09622                 //return;
09623         }
09624         else if (doAlpha)
09625         {
09626                 legs.renderfx |= RF_FORCE_ENT_ALPHA;
09627                 legs.shaderRGBA[3] = cent->trickAlpha;
09628 
09629                 if (legs.shaderRGBA[3] < 1)
09630                 { //don't cancel it out even if it's < 1
09631                         legs.shaderRGBA[3] = 1;
09632                 }
09633         }
09634 
09635         if (cent->teamPowerEffectTime > cg.time)
09636         {
09637                 if (cent->teamPowerType == 3)
09638                 { //absorb is a somewhat different effect entirely
09639                         //Guess I'll take care of it where it's always been, just checking these values instead.
09640                 }
09641                 else
09642                 {
09643                         vec4_t preCol;
09644                         int preRFX;
09645 
09646                         preRFX = legs.renderfx;
09647 
09648                         legs.renderfx |= RF_RGB_TINT;
09649                         legs.renderfx |= RF_FORCE_ENT_ALPHA;
09650 
09651                         preCol[0] = legs.shaderRGBA[0];
09652                         preCol[1] = legs.shaderRGBA[1];
09653                         preCol[2] = legs.shaderRGBA[2];
09654                         preCol[3] = legs.shaderRGBA[3];
09655 
09656                         if (cent->teamPowerType == 1)
09657                         { //heal
09658                                 legs.shaderRGBA[0] = 0;
09659                                 legs.shaderRGBA[1] = 255;
09660                                 legs.shaderRGBA[2] = 0;
09661                         }
09662                         else if (cent->teamPowerType == 0)
09663                         { //regen
09664                                 legs.shaderRGBA[0] = 0;
09665                                 legs.shaderRGBA[1] = 0;
09666                                 legs.shaderRGBA[2] = 255;
09667                         }
09668                         else
09669                         { //drain
09670                                 legs.shaderRGBA[0] = 255;
09671                                 legs.shaderRGBA[1] = 0;
09672                                 legs.shaderRGBA[2] = 0;
09673                         }
09674 
09675                         legs.