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