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->trickede