Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

cg_players.c (70197B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 // cg_players.c -- handle the media and animation for player entities
     24 #include "cg_local.h"
     25 
     26 char	*cg_customSoundNames[MAX_CUSTOM_SOUNDS] = {
     27 	"*death1.wav",
     28 	"*death2.wav",
     29 	"*death3.wav",
     30 	"*jump1.wav",
     31 	"*pain25_1.wav",
     32 	"*pain50_1.wav",
     33 	"*pain75_1.wav",
     34 	"*pain100_1.wav",
     35 	"*falling1.wav",
     36 	"*gasp.wav",
     37 	"*drown.wav",
     38 	"*fall1.wav",
     39 	"*taunt.wav"
     40 };
     41 
     42 
     43 /*
     44 ================
     45 CG_CustomSound
     46 
     47 ================
     48 */
     49 sfxHandle_t	CG_CustomSound( int clientNum, const char *soundName ) {
     50 	clientInfo_t *ci;
     51 	int			i;
     52 
     53 	if ( soundName[0] != '*' ) {
     54 		return trap_S_RegisterSound( soundName, qfalse );
     55 	}
     56 
     57 	if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
     58 		clientNum = 0;
     59 	}
     60 	ci = &cgs.clientinfo[ clientNum ];
     61 
     62 	for ( i = 0 ; i < MAX_CUSTOM_SOUNDS && cg_customSoundNames[i] ; i++ ) {
     63 		if ( !strcmp( soundName, cg_customSoundNames[i] ) ) {
     64 			return ci->sounds[i];
     65 		}
     66 	}
     67 
     68 	CG_Error( "Unknown custom sound: %s", soundName );
     69 	return 0;
     70 }
     71 
     72 
     73 
     74 /*
     75 =============================================================================
     76 
     77 CLIENT INFO
     78 
     79 =============================================================================
     80 */
     81 
     82 /*
     83 ======================
     84 CG_ParseAnimationFile
     85 
     86 Read a configuration file containing animation coutns and rates
     87 models/players/visor/animation.cfg, etc
     88 ======================
     89 */
     90 static qboolean	CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) {
     91 	char		*text_p, *prev;
     92 	int			len;
     93 	int			i;
     94 	char		*token;
     95 	float		fps;
     96 	int			skip;
     97 	char		text[20000];
     98 	fileHandle_t	f;
     99 	animation_t *animations;
    100 
    101 	animations = ci->animations;
    102 
    103 	// load the file
    104 	len = trap_FS_FOpenFile( filename, &f, FS_READ );
    105 	if ( len <= 0 ) {
    106 		return qfalse;
    107 	}
    108 	if ( len >= sizeof( text ) - 1 ) {
    109 		CG_Printf( "File %s too long\n", filename );
    110 		return qfalse;
    111 	}
    112 	trap_FS_Read( text, len, f );
    113 	text[len] = 0;
    114 	trap_FS_FCloseFile( f );
    115 
    116 	// parse the text
    117 	text_p = text;
    118 	skip = 0;	// quite the compiler warning
    119 
    120 	ci->footsteps = FOOTSTEP_NORMAL;
    121 	VectorClear( ci->headOffset );
    122 	ci->gender = GENDER_MALE;
    123 	ci->fixedlegs = qfalse;
    124 	ci->fixedtorso = qfalse;
    125 
    126 	// read optional parameters
    127 	while ( 1 ) {
    128 		prev = text_p;	// so we can unget
    129 		token = COM_Parse( &text_p );
    130 		if ( !token ) {
    131 			break;
    132 		}
    133 		if ( !Q_stricmp( token, "footsteps" ) ) {
    134 			token = COM_Parse( &text_p );
    135 			if ( !token ) {
    136 				break;
    137 			}
    138 			if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) {
    139 				ci->footsteps = FOOTSTEP_NORMAL;
    140 			} else if ( !Q_stricmp( token, "boot" ) ) {
    141 				ci->footsteps = FOOTSTEP_BOOT;
    142 			} else if ( !Q_stricmp( token, "flesh" ) ) {
    143 				ci->footsteps = FOOTSTEP_FLESH;
    144 			} else if ( !Q_stricmp( token, "mech" ) ) {
    145 				ci->footsteps = FOOTSTEP_MECH;
    146 			} else if ( !Q_stricmp( token, "energy" ) ) {
    147 				ci->footsteps = FOOTSTEP_ENERGY;
    148 			} else {
    149 				CG_Printf( "Bad footsteps parm in %s: %s\n", filename, token );
    150 			}
    151 			continue;
    152 		} else if ( !Q_stricmp( token, "headoffset" ) ) {
    153 			for ( i = 0 ; i < 3 ; i++ ) {
    154 				token = COM_Parse( &text_p );
    155 				if ( !token ) {
    156 					break;
    157 				}
    158 				ci->headOffset[i] = atof( token );
    159 			}
    160 			continue;
    161 		} else if ( !Q_stricmp( token, "sex" ) ) {
    162 			token = COM_Parse( &text_p );
    163 			if ( !token ) {
    164 				break;
    165 			}
    166 			if ( token[0] == 'f' || token[0] == 'F' ) {
    167 				ci->gender = GENDER_FEMALE;
    168 			} else if ( token[0] == 'n' || token[0] == 'N' ) {
    169 				ci->gender = GENDER_NEUTER;
    170 			} else {
    171 				ci->gender = GENDER_MALE;
    172 			}
    173 			continue;
    174 		} else if ( !Q_stricmp( token, "fixedlegs" ) ) {
    175 			ci->fixedlegs = qtrue;
    176 			continue;
    177 		} else if ( !Q_stricmp( token, "fixedtorso" ) ) {
    178 			ci->fixedtorso = qtrue;
    179 			continue;
    180 		}
    181 
    182 		// if it is a number, start parsing animations
    183 		if ( token[0] >= '0' && token[0] <= '9' ) {
    184 			text_p = prev;	// unget the token
    185 			break;
    186 		}
    187 		Com_Printf( "unknown token '%s' is %s\n", token, filename );
    188 	}
    189 
    190 	// read information for each frame
    191 	for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
    192 
    193 		token = COM_Parse( &text_p );
    194 		if ( !*token ) {
    195 			if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) {
    196 				animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame;
    197 				animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp;
    198 				animations[i].initialLerp = animations[TORSO_GESTURE].initialLerp;
    199 				animations[i].loopFrames = animations[TORSO_GESTURE].loopFrames;
    200 				animations[i].numFrames = animations[TORSO_GESTURE].numFrames;
    201 				animations[i].reversed = qfalse;
    202 				animations[i].flipflop = qfalse;
    203 				continue;
    204 			}
    205 			break;
    206 		}
    207 		animations[i].firstFrame = atoi( token );
    208 		// leg only frames are adjusted to not count the upper body only frames
    209 		if ( i == LEGS_WALKCR ) {
    210 			skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
    211 		}
    212 		if ( i >= LEGS_WALKCR && i<TORSO_GETFLAG) {
    213 			animations[i].firstFrame -= skip;
    214 		}
    215 
    216 		token = COM_Parse( &text_p );
    217 		if ( !*token ) {
    218 			break;
    219 		}
    220 		animations[i].numFrames = atoi( token );
    221 
    222 		animations[i].reversed = qfalse;
    223 		animations[i].flipflop = qfalse;
    224 		// if numFrames is negative the animation is reversed
    225 		if (animations[i].numFrames < 0) {
    226 			animations[i].numFrames = -animations[i].numFrames;
    227 			animations[i].reversed = qtrue;
    228 		}
    229 
    230 		token = COM_Parse( &text_p );
    231 		if ( !*token ) {
    232 			break;
    233 		}
    234 		animations[i].loopFrames = atoi( token );
    235 
    236 		token = COM_Parse( &text_p );
    237 		if ( !*token ) {
    238 			break;
    239 		}
    240 		fps = atof( token );
    241 		if ( fps == 0 ) {
    242 			fps = 1;
    243 		}
    244 		animations[i].frameLerp = 1000 / fps;
    245 		animations[i].initialLerp = 1000 / fps;
    246 	}
    247 
    248 	if ( i != MAX_ANIMATIONS ) {
    249 		CG_Printf( "Error parsing animation file: %s", filename );
    250 		return qfalse;
    251 	}
    252 
    253 	// crouch backward animation
    254 	memcpy(&animations[LEGS_BACKCR], &animations[LEGS_WALKCR], sizeof(animation_t));
    255 	animations[LEGS_BACKCR].reversed = qtrue;
    256 	// walk backward animation
    257 	memcpy(&animations[LEGS_BACKWALK], &animations[LEGS_WALK], sizeof(animation_t));
    258 	animations[LEGS_BACKWALK].reversed = qtrue;
    259 	// flag moving fast
    260 	animations[FLAG_RUN].firstFrame = 0;
    261 	animations[FLAG_RUN].numFrames = 16;
    262 	animations[FLAG_RUN].loopFrames = 16;
    263 	animations[FLAG_RUN].frameLerp = 1000 / 15;
    264 	animations[FLAG_RUN].initialLerp = 1000 / 15;
    265 	animations[FLAG_RUN].reversed = qfalse;
    266 	// flag not moving or moving slowly
    267 	animations[FLAG_STAND].firstFrame = 16;
    268 	animations[FLAG_STAND].numFrames = 5;
    269 	animations[FLAG_STAND].loopFrames = 0;
    270 	animations[FLAG_STAND].frameLerp = 1000 / 20;
    271 	animations[FLAG_STAND].initialLerp = 1000 / 20;
    272 	animations[FLAG_STAND].reversed = qfalse;
    273 	// flag speeding up
    274 	animations[FLAG_STAND2RUN].firstFrame = 16;
    275 	animations[FLAG_STAND2RUN].numFrames = 5;
    276 	animations[FLAG_STAND2RUN].loopFrames = 1;
    277 	animations[FLAG_STAND2RUN].frameLerp = 1000 / 15;
    278 	animations[FLAG_STAND2RUN].initialLerp = 1000 / 15;
    279 	animations[FLAG_STAND2RUN].reversed = qtrue;
    280 	//
    281 	// new anims changes
    282 	//
    283 //	animations[TORSO_GETFLAG].flipflop = qtrue;
    284 //	animations[TORSO_GUARDBASE].flipflop = qtrue;
    285 //	animations[TORSO_PATROL].flipflop = qtrue;
    286 //	animations[TORSO_AFFIRMATIVE].flipflop = qtrue;
    287 //	animations[TORSO_NEGATIVE].flipflop = qtrue;
    288 	//
    289 	return qtrue;
    290 }
    291 
    292 /*
    293 ==========================
    294 CG_FileExists
    295 ==========================
    296 */
    297 static qboolean	CG_FileExists(const char *filename) {
    298 	int len;
    299 
    300 	len = trap_FS_FOpenFile( filename, 0, FS_READ );
    301 	if (len>0) {
    302 		return qtrue;
    303 	}
    304 	return qfalse;
    305 }
    306 
    307 /*
    308 ==========================
    309 CG_FindClientModelFile
    310 ==========================
    311 */
    312 static qboolean	CG_FindClientModelFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *base, const char *ext ) {
    313 	char *team, *charactersFolder;
    314 	int i;
    315 
    316 	if ( cgs.gametype >= GT_TEAM ) {
    317 		switch ( ci->team ) {
    318 			case TEAM_BLUE: {
    319 				team = "blue";
    320 				break;
    321 			}
    322 			default: {
    323 				team = "red";
    324 				break;
    325 			}
    326 		}
    327 	}
    328 	else {
    329 		team = "default";
    330 	}
    331 	charactersFolder = "";
    332 	while(1) {
    333 		for ( i = 0; i < 2; i++ ) {
    334 			if ( i == 0 && teamName && *teamName ) {
    335 				//								"models/players/characters/james/stroggs/lower_lily_red.skin"
    336 				Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, team, ext );
    337 			}
    338 			else {
    339 				//								"models/players/characters/james/lower_lily_red.skin"
    340 				Com_sprintf( filename, length, "models/players/%s%s/%s_%s_%s.%s", charactersFolder, modelName, base, skinName, team, ext );
    341 			}
    342 			if ( CG_FileExists( filename ) ) {
    343 				return qtrue;
    344 			}
    345 			if ( cgs.gametype >= GT_TEAM ) {
    346 				if ( i == 0 && teamName && *teamName ) {
    347 					//								"models/players/characters/james/stroggs/lower_red.skin"
    348 					Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, team, ext );
    349 				}
    350 				else {
    351 					//								"models/players/characters/james/lower_red.skin"
    352 					Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, team, ext );
    353 				}
    354 			}
    355 			else {
    356 				if ( i == 0 && teamName && *teamName ) {
    357 					//								"models/players/characters/james/stroggs/lower_lily.skin"
    358 					Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", charactersFolder, modelName, teamName, base, skinName, ext );
    359 				}
    360 				else {
    361 					//								"models/players/characters/james/lower_lily.skin"
    362 					Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", charactersFolder, modelName, base, skinName, ext );
    363 				}
    364 			}
    365 			if ( CG_FileExists( filename ) ) {
    366 				return qtrue;
    367 			}
    368 			if ( !teamName || !*teamName ) {
    369 				break;
    370 			}
    371 		}
    372 		// if tried the heads folder first
    373 		if ( charactersFolder[0] ) {
    374 			break;
    375 		}
    376 		charactersFolder = "characters/";
    377 	}
    378 
    379 	return qfalse;
    380 }
    381 
    382 /*
    383 ==========================
    384 CG_FindClientHeadFile
    385 ==========================
    386 */
    387 static qboolean	CG_FindClientHeadFile( char *filename, int length, clientInfo_t *ci, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) {
    388 	char *team, *headsFolder;
    389 	int i;
    390 
    391 	if ( cgs.gametype >= GT_TEAM ) {
    392 		switch ( ci->team ) {
    393 			case TEAM_BLUE: {
    394 				team = "blue";
    395 				break;
    396 			}
    397 			default: {
    398 				team = "red";
    399 				break;
    400 			}
    401 		}
    402 	}
    403 	else {
    404 		team = "default";
    405 	}
    406 
    407 	if ( headModelName[0] == '*' ) {
    408 		headsFolder = "heads/";
    409 		headModelName++;
    410 	}
    411 	else {
    412 		headsFolder = "";
    413 	}
    414 	while(1) {
    415 		for ( i = 0; i < 2; i++ ) {
    416 			if ( i == 0 && teamName && *teamName ) {
    417 				Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext );
    418 			}
    419 			else {
    420 				Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext );
    421 			}
    422 			if ( CG_FileExists( filename ) ) {
    423 				return qtrue;
    424 			}
    425 			if ( cgs.gametype >= GT_TEAM ) {
    426 				if ( i == 0 &&  teamName && *teamName ) {
    427 					Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, team, ext );
    428 				}
    429 				else {
    430 					Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, team, ext );
    431 				}
    432 			}
    433 			else {
    434 				if ( i == 0 && teamName && *teamName ) {
    435 					Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext );
    436 				}
    437 				else {
    438 					Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext );
    439 				}
    440 			}
    441 			if ( CG_FileExists( filename ) ) {
    442 				return qtrue;
    443 			}
    444 			if ( !teamName || !*teamName ) {
    445 				break;
    446 			}
    447 		}
    448 		// if tried the heads folder first
    449 		if ( headsFolder[0] ) {
    450 			break;
    451 		}
    452 		headsFolder = "heads/";
    453 	}
    454 
    455 	return qfalse;
    456 }
    457 
    458 /*
    459 ==========================
    460 CG_RegisterClientSkin
    461 ==========================
    462 */
    463 static qboolean	CG_RegisterClientSkin( clientInfo_t *ci, const char *teamName, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName ) {
    464 	char filename[MAX_QPATH];
    465 
    466 	/*
    467 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/%slower_%s.skin", modelName, teamName, skinName );
    468 	ci->legsSkin = trap_R_RegisterSkin( filename );
    469 	if (!ci->legsSkin) {
    470 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%slower_%s.skin", modelName, teamName, skinName );
    471 		ci->legsSkin = trap_R_RegisterSkin( filename );
    472 		if (!ci->legsSkin) {
    473 			Com_Printf( "Leg skin load failure: %s\n", filename );
    474 		}
    475 	}
    476 
    477 
    478 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/%supper_%s.skin", modelName, teamName, skinName );
    479 	ci->torsoSkin = trap_R_RegisterSkin( filename );
    480 	if (!ci->torsoSkin) {
    481 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%supper_%s.skin", modelName, teamName, skinName );
    482 		ci->torsoSkin = trap_R_RegisterSkin( filename );
    483 		if (!ci->torsoSkin) {
    484 			Com_Printf( "Torso skin load failure: %s\n", filename );
    485 		}
    486 	}
    487 	*/
    488 	if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "lower", "skin" ) ) {
    489 		ci->legsSkin = trap_R_RegisterSkin( filename );
    490 	}
    491 	if (!ci->legsSkin) {
    492 		Com_Printf( "Leg skin load failure: %s\n", filename );
    493 	}
    494 
    495 	if ( CG_FindClientModelFile( filename, sizeof(filename), ci, teamName, modelName, skinName, "upper", "skin" ) ) {
    496 		ci->torsoSkin = trap_R_RegisterSkin( filename );
    497 	}
    498 	if (!ci->torsoSkin) {
    499 		Com_Printf( "Torso skin load failure: %s\n", filename );
    500 	}
    501 
    502 	if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headModelName, headSkinName, "head", "skin" ) ) {
    503 		ci->headSkin = trap_R_RegisterSkin( filename );
    504 	}
    505 	if (!ci->headSkin) {
    506 		Com_Printf( "Head skin load failure: %s\n", filename );
    507 	}
    508 
    509 	// if any skins failed to load
    510 	if ( !ci->legsSkin || !ci->torsoSkin || !ci->headSkin ) {
    511 		return qfalse;
    512 	}
    513 	return qtrue;
    514 }
    515 
    516 /*
    517 ==========================
    518 CG_RegisterClientModelname
    519 ==========================
    520 */
    521 static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName, const char *teamName ) {
    522 	char	filename[MAX_QPATH*2];
    523 	const char		*headName;
    524 	char newTeamName[MAX_QPATH*2];
    525 
    526 	if ( headModelName[0] == '\0' ) {
    527 		headName = modelName;
    528 	}
    529 	else {
    530 		headName = headModelName;
    531 	}
    532 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
    533 	ci->legsModel = trap_R_RegisterModel( filename );
    534 	if ( !ci->legsModel ) {
    535 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower.md3", modelName );
    536 		ci->legsModel = trap_R_RegisterModel( filename );
    537 		if ( !ci->legsModel ) {
    538 			Com_Printf( "Failed to load model file %s\n", filename );
    539 			return qfalse;
    540 		}
    541 	}
    542 
    543 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
    544 	ci->torsoModel = trap_R_RegisterModel( filename );
    545 	if ( !ci->torsoModel ) {
    546 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper.md3", modelName );
    547 		ci->torsoModel = trap_R_RegisterModel( filename );
    548 		if ( !ci->torsoModel ) {
    549 			Com_Printf( "Failed to load model file %s\n", filename );
    550 			return qfalse;
    551 		}
    552 	}
    553 
    554 	if( headName[0] == '*' ) {
    555 		Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] );
    556 	}
    557 	else {
    558 		Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headName );
    559 	}
    560 	ci->headModel = trap_R_RegisterModel( filename );
    561 	// if the head model could not be found and we didn't load from the heads folder try to load from there
    562 	if ( !ci->headModel && headName[0] != '*' ) {
    563 		Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName );
    564 		ci->headModel = trap_R_RegisterModel( filename );
    565 	}
    566 	if ( !ci->headModel ) {
    567 		Com_Printf( "Failed to load model file %s\n", filename );
    568 		return qfalse;
    569 	}
    570 
    571 	// if any skins failed to load, return failure
    572 	if ( !CG_RegisterClientSkin( ci, teamName, modelName, skinName, headName, headSkinName ) ) {
    573 		if ( teamName && *teamName) {
    574 			Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", teamName, modelName, skinName, headName, headSkinName );
    575 			if( ci->team == TEAM_BLUE ) {
    576 				Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_BLUETEAM_NAME);
    577 			}
    578 			else {
    579 				Com_sprintf(newTeamName, sizeof(newTeamName), "%s/", DEFAULT_REDTEAM_NAME);
    580 			}
    581 			if ( !CG_RegisterClientSkin( ci, newTeamName, modelName, skinName, headName, headSkinName ) ) {
    582 				Com_Printf( "Failed to load skin file: %s : %s : %s, %s : %s\n", newTeamName, modelName, skinName, headName, headSkinName );
    583 				return qfalse;
    584 			}
    585 		} else {
    586 			Com_Printf( "Failed to load skin file: %s : %s, %s : %s\n", modelName, skinName, headName, headSkinName );
    587 			return qfalse;
    588 		}
    589 	}
    590 
    591 	// load the animations
    592 	Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
    593 	if ( !CG_ParseAnimationFile( filename, ci ) ) {
    594 		Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName );
    595 		if ( !CG_ParseAnimationFile( filename, ci ) ) {
    596 			Com_Printf( "Failed to load animation file %s\n", filename );
    597 			return qfalse;
    598 		}
    599 	}
    600 
    601 	if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "skin" ) ) {
    602 		ci->modelIcon = trap_R_RegisterShaderNoMip( filename );
    603 	}
    604 	else if ( CG_FindClientHeadFile( filename, sizeof(filename), ci, teamName, headName, headSkinName, "icon", "tga" ) ) {
    605 		ci->modelIcon = trap_R_RegisterShaderNoMip( filename );
    606 	}
    607 
    608 	if ( !ci->modelIcon ) {
    609 		return qfalse;
    610 	}
    611 
    612 	return qtrue;
    613 }
    614 
    615 /*
    616 ====================
    617 CG_ColorFromString
    618 ====================
    619 */
    620 static void CG_ColorFromString( const char *v, vec3_t color ) {
    621 	int val;
    622 
    623 	VectorClear( color );
    624 
    625 	val = atoi( v );
    626 
    627 	if ( val < 1 || val > 7 ) {
    628 		VectorSet( color, 1, 1, 1 );
    629 		return;
    630 	}
    631 
    632 	if ( val & 1 ) {
    633 		color[2] = 1.0f;
    634 	}
    635 	if ( val & 2 ) {
    636 		color[1] = 1.0f;
    637 	}
    638 	if ( val & 4 ) {
    639 		color[0] = 1.0f;
    640 	}
    641 }
    642 
    643 /*
    644 ===================
    645 CG_LoadClientInfo
    646 
    647 Load it now, taking the disk hits.
    648 This will usually be deferred to a safe time
    649 ===================
    650 */
    651 static void CG_LoadClientInfo( clientInfo_t *ci ) {
    652 	const char	*dir, *fallback;
    653 	int			i, modelloaded;
    654 	const char	*s;
    655 	int			clientNum;
    656 	char		teamname[MAX_QPATH];
    657 
    658 	teamname[0] = 0;
    659 #ifdef MISSIONPACK
    660 	if( cgs.gametype >= GT_TEAM) {
    661 		if( ci->team == TEAM_BLUE ) {
    662 			Q_strncpyz(teamname, cg_blueTeamName.string, sizeof(teamname) );
    663 		} else {
    664 			Q_strncpyz(teamname, cg_redTeamName.string, sizeof(teamname) );
    665 		}
    666 	}
    667 	if( teamname[0] ) {
    668 		strcat( teamname, "/" );
    669 	}
    670 #endif
    671 	modelloaded = qtrue;
    672 	if ( !CG_RegisterClientModelname( ci, ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname ) ) {
    673 		if ( cg_buildScript.integer ) {
    674 			CG_Error( "CG_RegisterClientModelname( %s, %s, %s, %s %s ) failed", ci->modelName, ci->skinName, ci->headModelName, ci->headSkinName, teamname );
    675 		}
    676 
    677 		// fall back to default team name
    678 		if( cgs.gametype >= GT_TEAM) {
    679 			// keep skin name
    680 			if( ci->team == TEAM_BLUE ) {
    681 				Q_strncpyz(teamname, DEFAULT_BLUETEAM_NAME, sizeof(teamname) );
    682 			} else {
    683 				Q_strncpyz(teamname, DEFAULT_REDTEAM_NAME, sizeof(teamname) );
    684 			}
    685 			if ( !CG_RegisterClientModelname( ci, DEFAULT_TEAM_MODEL, ci->skinName, DEFAULT_TEAM_HEAD, ci->skinName, teamname ) ) {
    686 				CG_Error( "DEFAULT_TEAM_MODEL / skin (%s/%s) failed to register", DEFAULT_TEAM_MODEL, ci->skinName );
    687 			}
    688 		} else {
    689 			if ( !CG_RegisterClientModelname( ci, DEFAULT_MODEL, "default", DEFAULT_MODEL, "default", teamname ) ) {
    690 				CG_Error( "DEFAULT_MODEL (%s) failed to register", DEFAULT_MODEL );
    691 			}
    692 		}
    693 		modelloaded = qfalse;
    694 	}
    695 
    696 	ci->newAnims = qfalse;
    697 	if ( ci->torsoModel ) {
    698 		orientation_t tag;
    699 		// if the torso model has the "tag_flag"
    700 		if ( trap_R_LerpTag( &tag, ci->torsoModel, 0, 0, 1, "tag_flag" ) ) {
    701 			ci->newAnims = qtrue;
    702 		}
    703 	}
    704 
    705 	// sounds
    706 	dir = ci->modelName;
    707 	fallback = (cgs.gametype >= GT_TEAM) ? DEFAULT_TEAM_MODEL : DEFAULT_MODEL;
    708 
    709 	for ( i = 0 ; i < MAX_CUSTOM_SOUNDS ; i++ ) {
    710 		s = cg_customSoundNames[i];
    711 		if ( !s ) {
    712 			break;
    713 		}
    714 		ci->sounds[i] = 0;
    715 		// if the model didn't load use the sounds of the default model
    716 		if (modelloaded) {
    717 			ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", dir, s + 1), qfalse );
    718 		}
    719 		if ( !ci->sounds[i] ) {
    720 			ci->sounds[i] = trap_S_RegisterSound( va("sound/player/%s/%s", fallback, s + 1), qfalse );
    721 		}
    722 	}
    723 
    724 	ci->deferred = qfalse;
    725 
    726 	// reset any existing players and bodies, because they might be in bad
    727 	// frames for this new model
    728 	clientNum = ci - cgs.clientinfo;
    729 	for ( i = 0 ; i < MAX_GENTITIES ; i++ ) {
    730 		if ( cg_entities[i].currentState.clientNum == clientNum
    731 			&& cg_entities[i].currentState.eType == ET_PLAYER ) {
    732 			CG_ResetPlayerEntity( &cg_entities[i] );
    733 		}
    734 	}
    735 }
    736 
    737 /*
    738 ======================
    739 CG_CopyClientInfoModel
    740 ======================
    741 */
    742 static void CG_CopyClientInfoModel( clientInfo_t *from, clientInfo_t *to ) {
    743 	VectorCopy( from->headOffset, to->headOffset );
    744 	to->footsteps = from->footsteps;
    745 	to->gender = from->gender;
    746 
    747 	to->legsModel = from->legsModel;
    748 	to->legsSkin = from->legsSkin;
    749 	to->torsoModel = from->torsoModel;
    750 	to->torsoSkin = from->torsoSkin;
    751 	to->headModel = from->headModel;
    752 	to->headSkin = from->headSkin;
    753 	to->modelIcon = from->modelIcon;
    754 
    755 	to->newAnims = from->newAnims;
    756 
    757 	memcpy( to->animations, from->animations, sizeof( to->animations ) );
    758 	memcpy( to->sounds, from->sounds, sizeof( to->sounds ) );
    759 }
    760 
    761 /*
    762 ======================
    763 CG_ScanForExistingClientInfo
    764 ======================
    765 */
    766 static qboolean CG_ScanForExistingClientInfo( clientInfo_t *ci ) {
    767 	int		i;
    768 	clientInfo_t	*match;
    769 
    770 	for ( i = 0 ; i < cgs.maxclients ; i++ ) {
    771 		match = &cgs.clientinfo[ i ];
    772 		if ( !match->infoValid ) {
    773 			continue;
    774 		}
    775 		if ( match->deferred ) {
    776 			continue;
    777 		}
    778 		if ( !Q_stricmp( ci->modelName, match->modelName )
    779 			&& !Q_stricmp( ci->skinName, match->skinName )
    780 			&& !Q_stricmp( ci->headModelName, match->headModelName )
    781 			&& !Q_stricmp( ci->headSkinName, match->headSkinName ) 
    782 			&& !Q_stricmp( ci->blueTeam, match->blueTeam ) 
    783 			&& !Q_stricmp( ci->redTeam, match->redTeam )
    784 			&& (cgs.gametype < GT_TEAM || ci->team == match->team) ) {
    785 			// this clientinfo is identical, so use it's handles
    786 
    787 			ci->deferred = qfalse;
    788 
    789 			CG_CopyClientInfoModel( match, ci );
    790 
    791 			return qtrue;
    792 		}
    793 	}
    794 
    795 	// nothing matches, so defer the load
    796 	return qfalse;
    797 }
    798 
    799 /*
    800 ======================
    801 CG_SetDeferredClientInfo
    802 
    803 We aren't going to load it now, so grab some other
    804 client's info to use until we have some spare time.
    805 ======================
    806 */
    807 static void CG_SetDeferredClientInfo( clientInfo_t *ci ) {
    808 	int		i;
    809 	clientInfo_t	*match;
    810 
    811 	// if someone else is already the same models and skins we
    812 	// can just load the client info
    813 	for ( i = 0 ; i < cgs.maxclients ; i++ ) {
    814 		match = &cgs.clientinfo[ i ];
    815 		if ( !match->infoValid || match->deferred ) {
    816 			continue;
    817 		}
    818 		if ( Q_stricmp( ci->skinName, match->skinName ) ||
    819 			 Q_stricmp( ci->modelName, match->modelName ) ||
    820 //			 Q_stricmp( ci->headModelName, match->headModelName ) ||
    821 //			 Q_stricmp( ci->headSkinName, match->headSkinName ) ||
    822 			 (cgs.gametype >= GT_TEAM && ci->team != match->team) ) {
    823 			continue;
    824 		}
    825 		// just load the real info cause it uses the same models and skins
    826 		CG_LoadClientInfo( ci );
    827 		return;
    828 	}
    829 
    830 	// if we are in teamplay, only grab a model if the skin is correct
    831 	if ( cgs.gametype >= GT_TEAM ) {
    832 		for ( i = 0 ; i < cgs.maxclients ; i++ ) {
    833 			match = &cgs.clientinfo[ i ];
    834 			if ( !match->infoValid || match->deferred ) {
    835 				continue;
    836 			}
    837 			if ( Q_stricmp( ci->skinName, match->skinName ) ||
    838 				(cgs.gametype >= GT_TEAM && ci->team != match->team) ) {
    839 				continue;
    840 			}
    841 			ci->deferred = qtrue;
    842 			CG_CopyClientInfoModel( match, ci );
    843 			return;
    844 		}
    845 		// load the full model, because we don't ever want to show
    846 		// an improper team skin.  This will cause a hitch for the first
    847 		// player, when the second enters.  Combat shouldn't be going on
    848 		// yet, so it shouldn't matter
    849 		CG_LoadClientInfo( ci );
    850 		return;
    851 	}
    852 
    853 	// find the first valid clientinfo and grab its stuff
    854 	for ( i = 0 ; i < cgs.maxclients ; i++ ) {
    855 		match = &cgs.clientinfo[ i ];
    856 		if ( !match->infoValid ) {
    857 			continue;
    858 		}
    859 
    860 		ci->deferred = qtrue;
    861 		CG_CopyClientInfoModel( match, ci );
    862 		return;
    863 	}
    864 
    865 	// we should never get here...
    866 	CG_Printf( "CG_SetDeferredClientInfo: no valid clients!\n" );
    867 
    868 	CG_LoadClientInfo( ci );
    869 }
    870 
    871 
    872 /*
    873 ======================
    874 CG_NewClientInfo
    875 ======================
    876 */
    877 void CG_NewClientInfo( int clientNum ) {
    878 	clientInfo_t *ci;
    879 	clientInfo_t newInfo;
    880 	const char	*configstring;
    881 	const char	*v;
    882 	char		*slash;
    883 
    884 	ci = &cgs.clientinfo[clientNum];
    885 
    886 	configstring = CG_ConfigString( clientNum + CS_PLAYERS );
    887 	if ( !configstring[0] ) {
    888 		memset( ci, 0, sizeof( *ci ) );
    889 		return;		// player just left
    890 	}
    891 
    892 	// build into a temp buffer so the defer checks can use
    893 	// the old value
    894 	memset( &newInfo, 0, sizeof( newInfo ) );
    895 
    896 	// isolate the player's name
    897 	v = Info_ValueForKey(configstring, "n");
    898 	Q_strncpyz( newInfo.name, v, sizeof( newInfo.name ) );
    899 
    900 	// colors
    901 	v = Info_ValueForKey( configstring, "c1" );
    902 	CG_ColorFromString( v, newInfo.color1 );
    903 
    904 	v = Info_ValueForKey( configstring, "c2" );
    905 	CG_ColorFromString( v, newInfo.color2 );
    906 
    907 	// bot skill
    908 	v = Info_ValueForKey( configstring, "skill" );
    909 	newInfo.botSkill = atoi( v );
    910 
    911 	// handicap
    912 	v = Info_ValueForKey( configstring, "hc" );
    913 	newInfo.handicap = atoi( v );
    914 
    915 	// wins
    916 	v = Info_ValueForKey( configstring, "w" );
    917 	newInfo.wins = atoi( v );
    918 
    919 	// losses
    920 	v = Info_ValueForKey( configstring, "l" );
    921 	newInfo.losses = atoi( v );
    922 
    923 	// team
    924 	v = Info_ValueForKey( configstring, "t" );
    925 	newInfo.team = atoi( v );
    926 
    927 	// team task
    928 	v = Info_ValueForKey( configstring, "tt" );
    929 	newInfo.teamTask = atoi(v);
    930 
    931 	// team leader
    932 	v = Info_ValueForKey( configstring, "tl" );
    933 	newInfo.teamLeader = atoi(v);
    934 
    935 	v = Info_ValueForKey( configstring, "g_redteam" );
    936 	Q_strncpyz(newInfo.redTeam, v, MAX_TEAMNAME);
    937 
    938 	v = Info_ValueForKey( configstring, "g_blueteam" );
    939 	Q_strncpyz(newInfo.blueTeam, v, MAX_TEAMNAME);
    940 
    941 	// model
    942 	v = Info_ValueForKey( configstring, "model" );
    943 	if ( cg_forceModel.integer ) {
    944 		// forcemodel makes everyone use a single model
    945 		// to prevent load hitches
    946 		char modelStr[MAX_QPATH];
    947 		char *skin;
    948 
    949 		if( cgs.gametype >= GT_TEAM ) {
    950 			Q_strncpyz( newInfo.modelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.modelName ) );
    951 			Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
    952 		} else {
    953 			trap_Cvar_VariableStringBuffer( "model", modelStr, sizeof( modelStr ) );
    954 			if ( ( skin = strchr( modelStr, '/' ) ) == NULL) {
    955 				skin = "default";
    956 			} else {
    957 				*skin++ = 0;
    958 			}
    959 
    960 			Q_strncpyz( newInfo.skinName, skin, sizeof( newInfo.skinName ) );
    961 			Q_strncpyz( newInfo.modelName, modelStr, sizeof( newInfo.modelName ) );
    962 		}
    963 
    964 		if ( cgs.gametype >= GT_TEAM ) {
    965 			// keep skin name
    966 			slash = strchr( v, '/' );
    967 			if ( slash ) {
    968 				Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
    969 			}
    970 		}
    971 	} else {
    972 		Q_strncpyz( newInfo.modelName, v, sizeof( newInfo.modelName ) );
    973 
    974 		slash = strchr( newInfo.modelName, '/' );
    975 		if ( !slash ) {
    976 			// modelName didn not include a skin name
    977 			Q_strncpyz( newInfo.skinName, "default", sizeof( newInfo.skinName ) );
    978 		} else {
    979 			Q_strncpyz( newInfo.skinName, slash + 1, sizeof( newInfo.skinName ) );
    980 			// truncate modelName
    981 			*slash = 0;
    982 		}
    983 	}
    984 
    985 	// head model
    986 	v = Info_ValueForKey( configstring, "hmodel" );
    987 	if ( cg_forceModel.integer ) {
    988 		// forcemodel makes everyone use a single model
    989 		// to prevent load hitches
    990 		char modelStr[MAX_QPATH];
    991 		char *skin;
    992 
    993 		if( cgs.gametype >= GT_TEAM ) {
    994 			Q_strncpyz( newInfo.headModelName, DEFAULT_TEAM_MODEL, sizeof( newInfo.headModelName ) );
    995 			Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
    996 		} else {
    997 			trap_Cvar_VariableStringBuffer( "headmodel", modelStr, sizeof( modelStr ) );
    998 			if ( ( skin = strchr( modelStr, '/' ) ) == NULL) {
    999 				skin = "default";
   1000 			} else {
   1001 				*skin++ = 0;
   1002 			}
   1003 
   1004 			Q_strncpyz( newInfo.headSkinName, skin, sizeof( newInfo.headSkinName ) );
   1005 			Q_strncpyz( newInfo.headModelName, modelStr, sizeof( newInfo.headModelName ) );
   1006 		}
   1007 
   1008 		if ( cgs.gametype >= GT_TEAM ) {
   1009 			// keep skin name
   1010 			slash = strchr( v, '/' );
   1011 			if ( slash ) {
   1012 				Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
   1013 			}
   1014 		}
   1015 	} else {
   1016 		Q_strncpyz( newInfo.headModelName, v, sizeof( newInfo.headModelName ) );
   1017 
   1018 		slash = strchr( newInfo.headModelName, '/' );
   1019 		if ( !slash ) {
   1020 			// modelName didn not include a skin name
   1021 			Q_strncpyz( newInfo.headSkinName, "default", sizeof( newInfo.headSkinName ) );
   1022 		} else {
   1023 			Q_strncpyz( newInfo.headSkinName, slash + 1, sizeof( newInfo.headSkinName ) );
   1024 			// truncate modelName
   1025 			*slash = 0;
   1026 		}
   1027 	}
   1028 
   1029 	// scan for an existing clientinfo that matches this modelname
   1030 	// so we can avoid loading checks if possible
   1031 	if ( !CG_ScanForExistingClientInfo( &newInfo ) ) {
   1032 		qboolean	forceDefer;
   1033 
   1034 		forceDefer = trap_MemoryRemaining() < 4000000;
   1035 
   1036 		// if we are defering loads, just have it pick the first valid
   1037 		if ( forceDefer || (cg_deferPlayers.integer && !cg_buildScript.integer && !cg.loading ) ) {
   1038 			// keep whatever they had if it won't violate team skins
   1039 			CG_SetDeferredClientInfo( &newInfo );
   1040 			// if we are low on memory, leave them with this model
   1041 			if ( forceDefer ) {
   1042 				CG_Printf( "Memory is low.  Using deferred model.\n" );
   1043 				newInfo.deferred = qfalse;
   1044 			}
   1045 		} else {
   1046 			CG_LoadClientInfo( &newInfo );
   1047 		}
   1048 	}
   1049 
   1050 	// replace whatever was there with the new one
   1051 	newInfo.infoValid = qtrue;
   1052 	*ci = newInfo;
   1053 }
   1054 
   1055 
   1056 
   1057 /*
   1058 ======================
   1059 CG_LoadDeferredPlayers
   1060 
   1061 Called each frame when a player is dead
   1062 and the scoreboard is up
   1063 so deferred players can be loaded
   1064 ======================
   1065 */
   1066 void CG_LoadDeferredPlayers( void ) {
   1067 	int		i;
   1068 	clientInfo_t	*ci;
   1069 
   1070 	// scan for a deferred player to load
   1071 	for ( i = 0, ci = cgs.clientinfo ; i < cgs.maxclients ; i++, ci++ ) {
   1072 		if ( ci->infoValid && ci->deferred ) {
   1073 			// if we are low on memory, leave it deferred
   1074 			if ( trap_MemoryRemaining() < 4000000 ) {
   1075 				CG_Printf( "Memory is low.  Using deferred model.\n" );
   1076 				ci->deferred = qfalse;
   1077 				continue;
   1078 			}
   1079 			CG_LoadClientInfo( ci );
   1080 //			break;
   1081 		}
   1082 	}
   1083 }
   1084 
   1085 /*
   1086 =============================================================================
   1087 
   1088 PLAYER ANIMATION
   1089 
   1090 =============================================================================
   1091 */
   1092 
   1093 
   1094 /*
   1095 ===============
   1096 CG_SetLerpFrameAnimation
   1097 
   1098 may include ANIM_TOGGLEBIT
   1099 ===============
   1100 */
   1101 static void CG_SetLerpFrameAnimation( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
   1102 	animation_t	*anim;
   1103 
   1104 	lf->animationNumber = newAnimation;
   1105 	newAnimation &= ~ANIM_TOGGLEBIT;
   1106 
   1107 	if ( newAnimation < 0 || newAnimation >= MAX_TOTALANIMATIONS ) {
   1108 		CG_Error( "Bad animation number: %i", newAnimation );
   1109 	}
   1110 
   1111 	anim = &ci->animations[ newAnimation ];
   1112 
   1113 	lf->animation = anim;
   1114 	lf->animationTime = lf->frameTime + anim->initialLerp;
   1115 
   1116 	if ( cg_debugAnim.integer ) {
   1117 		CG_Printf( "Anim: %i\n", newAnimation );
   1118 	}
   1119 }
   1120 
   1121 /*
   1122 ===============
   1123 CG_RunLerpFrame
   1124 
   1125 Sets cg.snap, cg.oldFrame, and cg.backlerp
   1126 cg.time should be between oldFrameTime and frameTime after exit
   1127 ===============
   1128 */
   1129 static void CG_RunLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int newAnimation, float speedScale ) {
   1130 	int			f, numFrames;
   1131 	animation_t	*anim;
   1132 
   1133 	// debugging tool to get no animations
   1134 	if ( cg_animSpeed.integer == 0 ) {
   1135 		lf->oldFrame = lf->frame = lf->backlerp = 0;
   1136 		return;
   1137 	}
   1138 
   1139 	// see if the animation sequence is switching
   1140 	if ( newAnimation != lf->animationNumber || !lf->animation ) {
   1141 		CG_SetLerpFrameAnimation( ci, lf, newAnimation );
   1142 	}
   1143 
   1144 	// if we have passed the current frame, move it to
   1145 	// oldFrame and calculate a new frame
   1146 	if ( cg.time >= lf->frameTime ) {
   1147 		lf->oldFrame = lf->frame;
   1148 		lf->oldFrameTime = lf->frameTime;
   1149 
   1150 		// get the next frame based on the animation
   1151 		anim = lf->animation;
   1152 		if ( !anim->frameLerp ) {
   1153 			return;		// shouldn't happen
   1154 		}
   1155 		if ( cg.time < lf->animationTime ) {
   1156 			lf->frameTime = lf->animationTime;		// initial lerp
   1157 		} else {
   1158 			lf->frameTime = lf->oldFrameTime + anim->frameLerp;
   1159 		}
   1160 		f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
   1161 		f *= speedScale;		// adjust for haste, etc
   1162 
   1163 		numFrames = anim->numFrames;
   1164 		if (anim->flipflop) {
   1165 			numFrames *= 2;
   1166 		}
   1167 		if ( f >= numFrames ) {
   1168 			f -= numFrames;
   1169 			if ( anim->loopFrames ) {
   1170 				f %= anim->loopFrames;
   1171 				f += anim->numFrames - anim->loopFrames;
   1172 			} else {
   1173 				f = numFrames - 1;
   1174 				// the animation is stuck at the end, so it
   1175 				// can immediately transition to another sequence
   1176 				lf->frameTime = cg.time;
   1177 			}
   1178 		}
   1179 		if ( anim->reversed ) {
   1180 			lf->frame = anim->firstFrame + anim->numFrames - 1 - f;
   1181 		}
   1182 		else if (anim->flipflop && f>=anim->numFrames) {
   1183 			lf->frame = anim->firstFrame + anim->numFrames - 1 - (f%anim->numFrames);
   1184 		}
   1185 		else {
   1186 			lf->frame = anim->firstFrame + f;
   1187 		}
   1188 		if ( cg.time > lf->frameTime ) {
   1189 			lf->frameTime = cg.time;
   1190 			if ( cg_debugAnim.integer ) {
   1191 				CG_Printf( "Clamp lf->frameTime\n");
   1192 			}
   1193 		}
   1194 	}
   1195 
   1196 	if ( lf->frameTime > cg.time + 200 ) {
   1197 		lf->frameTime = cg.time;
   1198 	}
   1199 
   1200 	if ( lf->oldFrameTime > cg.time ) {
   1201 		lf->oldFrameTime = cg.time;
   1202 	}
   1203 	// calculate current lerp value
   1204 	if ( lf->frameTime == lf->oldFrameTime ) {
   1205 		lf->backlerp = 0;
   1206 	} else {
   1207 		lf->backlerp = 1.0 - (float)( cg.time - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
   1208 	}
   1209 }
   1210 
   1211 
   1212 /*
   1213 ===============
   1214 CG_ClearLerpFrame
   1215 ===============
   1216 */
   1217 static void CG_ClearLerpFrame( clientInfo_t *ci, lerpFrame_t *lf, int animationNumber ) {
   1218 	lf->frameTime = lf->oldFrameTime = cg.time;
   1219 	CG_SetLerpFrameAnimation( ci, lf, animationNumber );
   1220 	lf->oldFrame = lf->frame = lf->animation->firstFrame;
   1221 }
   1222 
   1223 
   1224 /*
   1225 ===============
   1226 CG_PlayerAnimation
   1227 ===============
   1228 */
   1229 static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float *legsBackLerp,
   1230 						int *torsoOld, int *torso, float *torsoBackLerp ) {
   1231 	clientInfo_t	*ci;
   1232 	int				clientNum;
   1233 	float			speedScale;
   1234 
   1235 	clientNum = cent->currentState.clientNum;
   1236 
   1237 	if ( cg_noPlayerAnims.integer ) {
   1238 		*legsOld = *legs = *torsoOld = *torso = 0;
   1239 		return;
   1240 	}
   1241 
   1242 	if ( cent->currentState.powerups & ( 1 << PW_HASTE ) ) {
   1243 		speedScale = 1.5;
   1244 	} else {
   1245 		speedScale = 1;
   1246 	}
   1247 
   1248 	ci = &cgs.clientinfo[ clientNum ];
   1249 
   1250 	// do the shuffle turn frames locally
   1251 	if ( cent->pe.legs.yawing && ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) {
   1252 		CG_RunLerpFrame( ci, &cent->pe.legs, LEGS_TURN, speedScale );
   1253 	} else {
   1254 		CG_RunLerpFrame( ci, &cent->pe.legs, cent->currentState.legsAnim, speedScale );
   1255 	}
   1256 
   1257 	*legsOld = cent->pe.legs.oldFrame;
   1258 	*legs = cent->pe.legs.frame;
   1259 	*legsBackLerp = cent->pe.legs.backlerp;
   1260 
   1261 	CG_RunLerpFrame( ci, &cent->pe.torso, cent->currentState.torsoAnim, speedScale );
   1262 
   1263 	*torsoOld = cent->pe.torso.oldFrame;
   1264 	*torso = cent->pe.torso.frame;
   1265 	*torsoBackLerp = cent->pe.torso.backlerp;
   1266 }
   1267 
   1268 /*
   1269 =============================================================================
   1270 
   1271 PLAYER ANGLES
   1272 
   1273 =============================================================================
   1274 */
   1275 
   1276 /*
   1277 ==================
   1278 CG_SwingAngles
   1279 ==================
   1280 */
   1281 static void CG_SwingAngles( float destination, float swingTolerance, float clampTolerance,
   1282 					float speed, float *angle, qboolean *swinging ) {
   1283 	float	swing;
   1284 	float	move;
   1285 	float	scale;
   1286 
   1287 	if ( !*swinging ) {
   1288 		// see if a swing should be started
   1289 		swing = AngleSubtract( *angle, destination );
   1290 		if ( swing > swingTolerance || swing < -swingTolerance ) {
   1291 			*swinging = qtrue;
   1292 		}
   1293 	}
   1294 
   1295 	if ( !*swinging ) {
   1296 		return;
   1297 	}
   1298 	
   1299 	// modify the speed depending on the delta
   1300 	// so it doesn't seem so linear
   1301 	swing = AngleSubtract( destination, *angle );
   1302 	scale = fabs( swing );
   1303 	if ( scale < swingTolerance * 0.5 ) {
   1304 		scale = 0.5;
   1305 	} else if ( scale < swingTolerance ) {
   1306 		scale = 1.0;
   1307 	} else {
   1308 		scale = 2.0;
   1309 	}
   1310 
   1311 	// swing towards the destination angle
   1312 	if ( swing >= 0 ) {
   1313 		move = cg.frametime * scale * speed;
   1314 		if ( move >= swing ) {
   1315 			move = swing;
   1316 			*swinging = qfalse;
   1317 		}
   1318 		*angle = AngleMod( *angle + move );
   1319 	} else if ( swing < 0 ) {
   1320 		move = cg.frametime * scale * -speed;
   1321 		if ( move <= swing ) {
   1322 			move = swing;
   1323 			*swinging = qfalse;
   1324 		}
   1325 		*angle = AngleMod( *angle + move );
   1326 	}
   1327 
   1328 	// clamp to no more than tolerance
   1329 	swing = AngleSubtract( destination, *angle );
   1330 	if ( swing > clampTolerance ) {
   1331 		*angle = AngleMod( destination - (clampTolerance - 1) );
   1332 	} else if ( swing < -clampTolerance ) {
   1333 		*angle = AngleMod( destination + (clampTolerance - 1) );
   1334 	}
   1335 }
   1336 
   1337 /*
   1338 =================
   1339 CG_AddPainTwitch
   1340 =================
   1341 */
   1342 static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) {
   1343 	int		t;
   1344 	float	f;
   1345 
   1346 	t = cg.time - cent->pe.painTime;
   1347 	if ( t >= PAIN_TWITCH_TIME ) {
   1348 		return;
   1349 	}
   1350 
   1351 	f = 1.0 - (float)t / PAIN_TWITCH_TIME;
   1352 
   1353 	if ( cent->pe.painDirection ) {
   1354 		torsoAngles[ROLL] += 20 * f;
   1355 	} else {
   1356 		torsoAngles[ROLL] -= 20 * f;
   1357 	}
   1358 }
   1359 
   1360 
   1361 /*
   1362 ===============
   1363 CG_PlayerAngles
   1364 
   1365 Handles seperate torso motion
   1366 
   1367   legs pivot based on direction of movement
   1368 
   1369   head always looks exactly at cent->lerpAngles
   1370 
   1371   if motion < 20 degrees, show in head only
   1372   if < 45 degrees, also show in torso
   1373 ===============
   1374 */
   1375 static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) {
   1376 	vec3_t		legsAngles, torsoAngles, headAngles;
   1377 	float		dest;
   1378 	static	int	movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 };
   1379 	vec3_t		velocity;
   1380 	float		speed;
   1381 	int			dir, clientNum;
   1382 	clientInfo_t	*ci;
   1383 
   1384 	VectorCopy( cent->lerpAngles, headAngles );
   1385 	headAngles[YAW] = AngleMod( headAngles[YAW] );
   1386 	VectorClear( legsAngles );
   1387 	VectorClear( torsoAngles );
   1388 
   1389 	// --------- yaw -------------
   1390 
   1391 	// allow yaw to drift a bit
   1392 	if ( ( cent->currentState.legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE 
   1393 		|| ( cent->currentState.torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND  ) {
   1394 		// if not standing still, always point all in the same direction
   1395 		cent->pe.torso.yawing = qtrue;	// always center
   1396 		cent->pe.torso.pitching = qtrue;	// always center
   1397 		cent->pe.legs.yawing = qtrue;	// always center
   1398 	}
   1399 
   1400 	// adjust legs for movement dir
   1401 	if ( cent->currentState.eFlags & EF_DEAD ) {
   1402 		// don't let dead bodies twitch
   1403 		dir = 0;
   1404 	} else {
   1405 		dir = cent->currentState.angles2[YAW];
   1406 		if ( dir < 0 || dir > 7 ) {
   1407 			CG_Error( "Bad player movement angle" );
   1408 		}
   1409 	}
   1410 	legsAngles[YAW] = headAngles[YAW] + movementOffsets[ dir ];
   1411 	torsoAngles[YAW] = headAngles[YAW] + 0.25 * movementOffsets[ dir ];
   1412 
   1413 	// torso
   1414 	CG_SwingAngles( torsoAngles[YAW], 25, 90, cg_swingSpeed.value, &cent->pe.torso.yawAngle, &cent->pe.torso.yawing );
   1415 	CG_SwingAngles( legsAngles[YAW], 40, 90, cg_swingSpeed.value, &cent->pe.legs.yawAngle, &cent->pe.legs.yawing );
   1416 
   1417 	torsoAngles[YAW] = cent->pe.torso.yawAngle;
   1418 	legsAngles[YAW] = cent->pe.legs.yawAngle;
   1419 
   1420 
   1421 	// --------- pitch -------------
   1422 
   1423 	// only show a fraction of the pitch angle in the torso
   1424 	if ( headAngles[PITCH] > 180 ) {
   1425 		dest = (-360 + headAngles[PITCH]) * 0.75f;
   1426 	} else {
   1427 		dest = headAngles[PITCH] * 0.75f;
   1428 	}
   1429 	CG_SwingAngles( dest, 15, 30, 0.1f, &cent->pe.torso.pitchAngle, &cent->pe.torso.pitching );
   1430 	torsoAngles[PITCH] = cent->pe.torso.pitchAngle;
   1431 
   1432 	//
   1433 	clientNum = cent->currentState.clientNum;
   1434 	if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
   1435 		ci = &cgs.clientinfo[ clientNum ];
   1436 		if ( ci->fixedtorso ) {
   1437 			torsoAngles[PITCH] = 0.0f;
   1438 		}
   1439 	}
   1440 
   1441 	// --------- roll -------------
   1442 
   1443 
   1444 	// lean towards the direction of travel
   1445 	VectorCopy( cent->currentState.pos.trDelta, velocity );
   1446 	speed = VectorNormalize( velocity );
   1447 	if ( speed ) {
   1448 		vec3_t	axis[3];
   1449 		float	side;
   1450 
   1451 		speed *= 0.05f;
   1452 
   1453 		AnglesToAxis( legsAngles, axis );
   1454 		side = speed * DotProduct( velocity, axis[1] );
   1455 		legsAngles[ROLL] -= side;
   1456 
   1457 		side = speed * DotProduct( velocity, axis[0] );
   1458 		legsAngles[PITCH] += side;
   1459 	}
   1460 
   1461 	//
   1462 	clientNum = cent->currentState.clientNum;
   1463 	if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
   1464 		ci = &cgs.clientinfo[ clientNum ];
   1465 		if ( ci->fixedlegs ) {
   1466 			legsAngles[YAW] = torsoAngles[YAW];
   1467 			legsAngles[PITCH] = 0.0f;
   1468 			legsAngles[ROLL] = 0.0f;
   1469 		}
   1470 	}
   1471 
   1472 	// pain twitch
   1473 	CG_AddPainTwitch( cent, torsoAngles );
   1474 
   1475 	// pull the angles back out of the hierarchial chain
   1476 	AnglesSubtract( headAngles, torsoAngles, headAngles );
   1477 	AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
   1478 	AnglesToAxis( legsAngles, legs );
   1479 	AnglesToAxis( torsoAngles, torso );
   1480 	AnglesToAxis( headAngles, head );
   1481 }
   1482 
   1483 
   1484 //==========================================================================
   1485 
   1486 /*
   1487 ===============
   1488 CG_HasteTrail
   1489 ===============
   1490 */
   1491 static void CG_HasteTrail( centity_t *cent ) {
   1492 	localEntity_t	*smoke;
   1493 	vec3_t			origin;
   1494 	int				anim;
   1495 
   1496 	if ( cent->trailTime > cg.time ) {
   1497 		return;
   1498 	}
   1499 	anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT;
   1500 	if ( anim != LEGS_RUN && anim != LEGS_BACK ) {
   1501 		return;
   1502 	}
   1503 
   1504 	cent->trailTime += 100;
   1505 	if ( cent->trailTime < cg.time ) {
   1506 		cent->trailTime = cg.time;
   1507 	}
   1508 
   1509 	VectorCopy( cent->lerpOrigin, origin );
   1510 	origin[2] -= 16;
   1511 
   1512 	smoke = CG_SmokePuff( origin, vec3_origin, 
   1513 				  8, 
   1514 				  1, 1, 1, 1,
   1515 				  500, 
   1516 				  cg.time,
   1517 				  0,
   1518 				  0,
   1519 				  cgs.media.hastePuffShader );
   1520 
   1521 	// use the optimized local entity add
   1522 	smoke->leType = LE_SCALE_FADE;
   1523 }
   1524 
   1525 #ifdef MISSIONPACK
   1526 /*
   1527 ===============
   1528 CG_BreathPuffs
   1529 ===============
   1530 */
   1531 static void CG_BreathPuffs( centity_t *cent, refEntity_t *head) {
   1532 	clientInfo_t *ci;
   1533 	vec3_t up, origin;
   1534 	int contents;
   1535 
   1536 	ci = &cgs.clientinfo[ cent->currentState.number ];
   1537 
   1538 	if (!cg_enableBreath.integer) {
   1539 		return;
   1540 	}
   1541 	if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson) {
   1542 		return;
   1543 	}
   1544 	if ( cent->currentState.eFlags & EF_DEAD ) {
   1545 		return;
   1546 	}
   1547 	contents = trap_CM_PointContents( head->origin, 0 );
   1548 	if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
   1549 		return;
   1550 	}
   1551 	if ( ci->breathPuffTime > cg.time ) {
   1552 		return;
   1553 	}
   1554 
   1555 	VectorSet( up, 0, 0, 8 );
   1556 	VectorMA(head->origin, 8, head->axis[0], origin);
   1557 	VectorMA(origin, -4, head->axis[2], origin);
   1558 	CG_SmokePuff( origin, up, 16, 1, 1, 1, 0.66f, 1500, cg.time, cg.time + 400, LEF_PUFF_DONT_SCALE, cgs.media.shotgunSmokePuffShader );
   1559 	ci->breathPuffTime = cg.time + 2000;
   1560 }
   1561 
   1562 /*
   1563 ===============
   1564 CG_DustTrail
   1565 ===============
   1566 */
   1567 static void CG_DustTrail( centity_t *cent ) {
   1568 	int				anim;
   1569 	localEntity_t	*dust;
   1570 	vec3_t end, vel;
   1571 	trace_t tr;
   1572 
   1573 	if (!cg_enableDust.integer)
   1574 		return;
   1575 
   1576 	if ( cent->dustTrailTime > cg.time ) {
   1577 		return;
   1578 	}
   1579 
   1580 	anim = cent->pe.legs.animationNumber & ~ANIM_TOGGLEBIT;
   1581 	if ( anim != LEGS_LANDB && anim != LEGS_LAND ) {
   1582 		return;
   1583 	}
   1584 
   1585 	cent->dustTrailTime += 40;
   1586 	if ( cent->dustTrailTime < cg.time ) {
   1587 		cent->dustTrailTime = cg.time;
   1588 	}
   1589 
   1590 	VectorCopy(cent->currentState.pos.trBase, end);
   1591 	end[2] -= 64;
   1592 	CG_Trace( &tr, cent->currentState.pos.trBase, NULL, NULL, end, cent->currentState.number, MASK_PLAYERSOLID );
   1593 
   1594 	if ( !(tr.surfaceFlags & SURF_DUST) )
   1595 		return;
   1596 
   1597 	VectorCopy( cent->currentState.pos.trBase, end );
   1598 	end[2] -= 16;
   1599 
   1600 	VectorSet(vel, 0, 0, -30);
   1601 	dust = CG_SmokePuff( end, vel,
   1602 				  24,
   1603 				  .8f, .8f, 0.7f, 0.33f,
   1604 				  500,
   1605 				  cg.time,
   1606 				  0,
   1607 				  0,
   1608 				  cgs.media.dustPuffShader );
   1609 }
   1610 
   1611 #endif
   1612 
   1613 /*
   1614 ===============
   1615 CG_TrailItem
   1616 ===============
   1617 */
   1618 static void CG_TrailItem( centity_t *cent, qhandle_t hModel ) {
   1619 	refEntity_t		ent;
   1620 	vec3_t			angles;
   1621 	vec3_t			axis[3];
   1622 
   1623 	VectorCopy( cent->lerpAngles, angles );
   1624 	angles[PITCH] = 0;
   1625 	angles[ROLL] = 0;
   1626 	AnglesToAxis( angles, axis );
   1627 
   1628 	memset( &ent, 0, sizeof( ent ) );
   1629 	VectorMA( cent->lerpOrigin, -16, axis[0], ent.origin );
   1630 	ent.origin[2] += 16;
   1631 	angles[YAW] += 90;
   1632 	AnglesToAxis( angles, ent.axis );
   1633 
   1634 	ent.hModel = hModel;
   1635 	trap_R_AddRefEntityToScene( &ent );
   1636 }
   1637 
   1638 
   1639 /*
   1640 ===============
   1641 CG_PlayerFlag
   1642 ===============
   1643 */
   1644 static void CG_PlayerFlag( centity_t *cent, qhandle_t hSkin, refEntity_t *torso ) {
   1645 	clientInfo_t	*ci;
   1646 	refEntity_t	pole;
   1647 	refEntity_t	flag;
   1648 	vec3_t		angles, dir;
   1649 	int			legsAnim, flagAnim, updateangles;
   1650 	float		angle, d;
   1651 
   1652 	// show the flag pole model
   1653 	memset( &pole, 0, sizeof(pole) );
   1654 	pole.hModel = cgs.media.flagPoleModel;
   1655 	VectorCopy( torso->lightingOrigin, pole.lightingOrigin );
   1656 	pole.shadowPlane = torso->shadowPlane;
   1657 	pole.renderfx = torso->renderfx;
   1658 	CG_PositionEntityOnTag( &pole, torso, torso->hModel, "tag_flag" );
   1659 	trap_R_AddRefEntityToScene( &pole );
   1660 
   1661 	// show the flag model
   1662 	memset( &flag, 0, sizeof(flag) );
   1663 	flag.hModel = cgs.media.flagFlapModel;
   1664 	flag.customSkin = hSkin;
   1665 	VectorCopy( torso->lightingOrigin, flag.lightingOrigin );
   1666 	flag.shadowPlane = torso->shadowPlane;
   1667 	flag.renderfx = torso->renderfx;
   1668 
   1669 	VectorClear(angles);
   1670 
   1671 	updateangles = qfalse;
   1672 	legsAnim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT;
   1673 	if( legsAnim == LEGS_IDLE || legsAnim == LEGS_IDLECR ) {
   1674 		flagAnim = FLAG_STAND;
   1675 	} else if ( legsAnim == LEGS_WALK || legsAnim == LEGS_WALKCR ) {
   1676 		flagAnim = FLAG_STAND;
   1677 		updateangles = qtrue;
   1678 	} else {
   1679 		flagAnim = FLAG_RUN;
   1680 		updateangles = qtrue;
   1681 	}
   1682 
   1683 	if ( updateangles ) {
   1684 
   1685 		VectorCopy( cent->currentState.pos.trDelta, dir );
   1686 		// add gravity
   1687 		dir[2] += 100;
   1688 		VectorNormalize( dir );
   1689 		d = DotProduct(pole.axis[2], dir);
   1690 		// if there is anough movement orthogonal to the flag pole
   1691 		if (fabs(d) < 0.9) {
   1692 			//
   1693 			d = DotProduct(pole.axis[0], dir);
   1694 			if (d > 1.0f) {
   1695 				d = 1.0f;
   1696 			}
   1697 			else if (d < -1.0f) {
   1698 				d = -1.0f;
   1699 			}
   1700 			angle = acos(d);
   1701 
   1702 			d = DotProduct(pole.axis[1], dir);
   1703 			if (d < 0) {
   1704 				angles[YAW] = 360 - angle * 180 / M_PI;
   1705 			}
   1706 			else {
   1707 				angles[YAW] = angle * 180 / M_PI;
   1708 			}
   1709 			if (angles[YAW] < 0)
   1710 				angles[YAW] += 360;
   1711 			if (angles[YAW] > 360)
   1712 				angles[YAW] -= 360;
   1713 
   1714 			//vectoangles( cent->currentState.pos.trDelta, tmpangles );
   1715 			//angles[YAW] = tmpangles[YAW] + 45 - cent->pe.torso.yawAngle;
   1716 			// change the yaw angle
   1717 			CG_SwingAngles( angles[YAW], 25, 90, 0.15f, &cent->pe.flag.yawAngle, &cent->pe.flag.yawing );
   1718 		}
   1719 
   1720 		/*
   1721 		d = DotProduct(pole.axis[2], dir);
   1722 		angle = Q_acos(d);
   1723 
   1724 		d = DotProduct(pole.axis[1], dir);
   1725 		if (d < 0) {
   1726 			angle = 360 - angle * 180 / M_PI;
   1727 		}
   1728 		else {
   1729 			angle = angle * 180 / M_PI;
   1730 		}
   1731 		if (angle > 340 && angle < 20) {
   1732 			flagAnim = FLAG_RUNUP;
   1733 		}
   1734 		if (angle > 160 && angle < 200) {
   1735 			flagAnim = FLAG_RUNDOWN;
   1736 		}
   1737 		*/
   1738 	}
   1739 
   1740 	// set the yaw angle
   1741 	angles[YAW] = cent->pe.flag.yawAngle;
   1742 	// lerp the flag animation frames
   1743 	ci = &cgs.clientinfo[ cent->currentState.clientNum ];
   1744 	CG_RunLerpFrame( ci, &cent->pe.flag, flagAnim, 1 );
   1745 	flag.oldframe = cent->pe.flag.oldFrame;
   1746 	flag.frame = cent->pe.flag.frame;
   1747 	flag.backlerp = cent->pe.flag.backlerp;
   1748 
   1749 	AnglesToAxis( angles, flag.axis );
   1750 	CG_PositionRotatedEntityOnTag( &flag, &pole, pole.hModel, "tag_flag" );
   1751 
   1752 	trap_R_AddRefEntityToScene( &flag );
   1753 }
   1754 
   1755 
   1756 #ifdef MISSIONPACK // bk001204
   1757 /*
   1758 ===============
   1759 CG_PlayerTokens
   1760 ===============
   1761 */
   1762 static void CG_PlayerTokens( centity_t *cent, int renderfx ) {
   1763 	int			tokens, i, j;
   1764 	float		angle;
   1765 	refEntity_t	ent;
   1766 	vec3_t		dir, origin;
   1767 	skulltrail_t *trail;
   1768 	trail = &cg.skulltrails[cent->currentState.number];
   1769 	tokens = cent->currentState.generic1;
   1770 	if ( !tokens ) {
   1771 		trail->numpositions = 0;
   1772 		return;
   1773 	}
   1774 
   1775 	if ( tokens > MAX_SKULLTRAIL ) {
   1776 		tokens = MAX_SKULLTRAIL;
   1777 	}
   1778 
   1779 	// add skulls if there are more than last time
   1780 	for (i = 0; i < tokens - trail->numpositions; i++) {
   1781 		for (j = trail->numpositions; j > 0; j--) {
   1782 			VectorCopy(trail->positions[j-1], trail->positions[j]);
   1783 		}
   1784 		VectorCopy(cent->lerpOrigin, trail->positions[0]);
   1785 	}
   1786 	trail->numpositions = tokens;
   1787 
   1788 	// move all the skulls along the trail
   1789 	VectorCopy(cent->lerpOrigin, origin);
   1790 	for (i = 0; i < trail->numpositions; i++) {
   1791 		VectorSubtract(trail->positions[i], origin, dir);
   1792 		if (VectorNormalize(dir) > 30) {
   1793 			VectorMA(origin, 30, dir, trail->positions[i]);
   1794 		}
   1795 		VectorCopy(trail->positions[i], origin);
   1796 	}
   1797 
   1798 	memset( &ent, 0, sizeof( ent ) );
   1799 	if( cgs.clientinfo[ cent->currentState.clientNum ].team == TEAM_BLUE ) {
   1800 		ent.hModel = cgs.media.redCubeModel;
   1801 	} else {
   1802 		ent.hModel = cgs.media.blueCubeModel;
   1803 	}
   1804 	ent.renderfx = renderfx;
   1805 
   1806 	VectorCopy(cent->lerpOrigin, origin);
   1807 	for (i = 0; i < trail->numpositions; i++) {
   1808 		VectorSubtract(origin, trail->positions[i], ent.axis[0]);
   1809 		ent.axis[0][2] = 0;
   1810 		VectorNormalize(ent.axis[0]);
   1811 		VectorSet(ent.axis[2], 0, 0, 1);
   1812 		CrossProduct(ent.axis[0], ent.axis[2], ent.axis[1]);
   1813 
   1814 		VectorCopy(trail->positions[i], ent.origin);
   1815 		angle = (((cg.time + 500 * MAX_SKULLTRAIL - 500 * i) / 16) & 255) * (M_PI * 2) / 255;
   1816 		ent.origin[2] += sin(angle) * 10;
   1817 		trap_R_AddRefEntityToScene( &ent );
   1818 		VectorCopy(trail->positions[i], origin);
   1819 	}
   1820 }
   1821 #endif
   1822 
   1823 
   1824 /*
   1825 ===============
   1826 CG_PlayerPowerups
   1827 ===============
   1828 */
   1829 static void CG_PlayerPowerups( centity_t *cent, refEntity_t *torso ) {
   1830 	int		powerups;
   1831 	clientInfo_t	*ci;
   1832 
   1833 	powerups = cent->currentState.powerups;
   1834 	if ( !powerups ) {
   1835 		return;
   1836 	}
   1837 
   1838 	// quad gives a dlight
   1839 	if ( powerups & ( 1 << PW_QUAD ) ) {
   1840 		trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1 );
   1841 	}
   1842 
   1843 	// flight plays a looped sound
   1844 	if ( powerups & ( 1 << PW_FLIGHT ) ) {
   1845 		trap_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.flightSound );
   1846 	}
   1847 
   1848 	ci = &cgs.clientinfo[ cent->currentState.clientNum ];
   1849 	// redflag
   1850 	if ( powerups & ( 1 << PW_REDFLAG ) ) {
   1851 		if (ci->newAnims) {
   1852 			CG_PlayerFlag( cent, cgs.media.redFlagFlapSkin, torso );
   1853 		}
   1854 		else {
   1855 			CG_TrailItem( cent, cgs.media.redFlagModel );
   1856 		}
   1857 		trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 0.2f, 0.2f );
   1858 	}
   1859 
   1860 	// blueflag
   1861 	if ( powerups & ( 1 << PW_BLUEFLAG ) ) {
   1862 		if (ci->newAnims){
   1863 			CG_PlayerFlag( cent, cgs.media.blueFlagFlapSkin, torso );
   1864 		}
   1865 		else {
   1866 			CG_TrailItem( cent, cgs.media.blueFlagModel );
   1867 		}
   1868 		trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 0.2f, 0.2f, 1.0 );
   1869 	}
   1870 
   1871 	// neutralflag
   1872 	if ( powerups & ( 1 << PW_NEUTRALFLAG ) ) {
   1873 		if (ci->newAnims) {
   1874 			CG_PlayerFlag( cent, cgs.media.neutralFlagFlapSkin, torso );
   1875 		}
   1876 		else {
   1877 			CG_TrailItem( cent, cgs.media.neutralFlagModel );
   1878 		}
   1879 		trap_R_AddLightToScene( cent->lerpOrigin, 200 + (rand()&31), 1.0, 1.0, 1.0 );
   1880 	}
   1881 
   1882 	// haste leaves smoke trails
   1883 	if ( powerups & ( 1 << PW_HASTE ) ) {
   1884 		CG_HasteTrail( cent );
   1885 	}
   1886 }
   1887 
   1888 
   1889 /*
   1890 ===============
   1891 CG_PlayerFloatSprite
   1892 
   1893 Float a sprite over the player's head
   1894 ===============
   1895 */
   1896 static void CG_PlayerFloatSprite( centity_t *cent, qhandle_t shader ) {
   1897 	int				rf;
   1898 	refEntity_t		ent;
   1899 
   1900 	if ( cent->currentState.number == cg.snap->ps.clientNum && !cg.renderingThirdPerson ) {
   1901 		rf = RF_THIRD_PERSON;		// only show in mirrors
   1902 	} else {
   1903 		rf = 0;
   1904 	}
   1905 
   1906 	memset( &ent, 0, sizeof( ent ) );
   1907 	VectorCopy( cent->lerpOrigin, ent.origin );
   1908 	ent.origin[2] += 48;
   1909 	ent.reType = RT_SPRITE;
   1910 	ent.customShader = shader;
   1911 	ent.radius = 10;
   1912 	ent.renderfx = rf;
   1913 	ent.shaderRGBA[0] = 255;
   1914 	ent.shaderRGBA[1] = 255;
   1915 	ent.shaderRGBA[2] = 255;
   1916 	ent.shaderRGBA[3] = 255;
   1917 	trap_R_AddRefEntityToScene( &ent );
   1918 }
   1919 
   1920 
   1921 
   1922 /*
   1923 ===============
   1924 CG_PlayerSprites
   1925 
   1926 Float sprites over the player's head
   1927 ===============
   1928 */
   1929 static void CG_PlayerSprites( centity_t *cent ) {
   1930 	int		team;
   1931 
   1932 	if ( cent->currentState.eFlags & EF_CONNECTION ) {
   1933 		CG_PlayerFloatSprite( cent, cgs.media.connectionShader );
   1934 		return;
   1935 	}
   1936 
   1937 	if ( cent->currentState.eFlags & EF_TALK ) {
   1938 		CG_PlayerFloatSprite( cent, cgs.media.balloonShader );
   1939 		return;
   1940 	}
   1941 
   1942 	if ( cent->currentState.eFlags & EF_AWARD_IMPRESSIVE ) {
   1943 		CG_PlayerFloatSprite( cent, cgs.media.medalImpressive );
   1944 		return;
   1945 	}
   1946 
   1947 	if ( cent->currentState.eFlags & EF_AWARD_EXCELLENT ) {
   1948 		CG_PlayerFloatSprite( cent, cgs.media.medalExcellent );
   1949 		return;
   1950 	}
   1951 
   1952 	if ( cent->currentState.eFlags & EF_AWARD_GAUNTLET ) {
   1953 		CG_PlayerFloatSprite( cent, cgs.media.medalGauntlet );
   1954 		return;
   1955 	}
   1956 
   1957 	if ( cent->currentState.eFlags & EF_AWARD_DEFEND ) {
   1958 		CG_PlayerFloatSprite( cent, cgs.media.medalDefend );
   1959 		return;
   1960 	}
   1961 
   1962 	if ( cent->currentState.eFlags & EF_AWARD_ASSIST ) {
   1963 		CG_PlayerFloatSprite( cent, cgs.media.medalAssist );
   1964 		return;
   1965 	}
   1966 
   1967 	if ( cent->currentState.eFlags & EF_AWARD_CAP ) {
   1968 		CG_PlayerFloatSprite( cent, cgs.media.medalCapture );
   1969 		return;
   1970 	}
   1971 
   1972 	team = cgs.clientinfo[ cent->currentState.clientNum ].team;
   1973 	if ( !(cent->currentState.eFlags & EF_DEAD) && 
   1974 		cg.snap->ps.persistant[PERS_TEAM] == team &&
   1975 		cgs.gametype >= GT_TEAM) {
   1976 		if (cg_drawFriend.integer) {
   1977 			CG_PlayerFloatSprite( cent, cgs.media.friendShader );
   1978 		}
   1979 		return;
   1980 	}
   1981 }
   1982 
   1983 /*
   1984 ===============
   1985 CG_PlayerShadow
   1986 
   1987 Returns the Z component of the surface being shadowed
   1988 
   1989   should it return a full plane instead of a Z?
   1990 ===============
   1991 */
   1992 #define	SHADOW_DISTANCE		128
   1993 static qboolean CG_PlayerShadow( centity_t *cent, float *shadowPlane ) {
   1994 	vec3_t		end, mins = {-15, -15, 0}, maxs = {15, 15, 2};
   1995 	trace_t		trace;
   1996 	float		alpha;
   1997 
   1998 	*shadowPlane = 0;
   1999 
   2000 	if ( cg_shadows.integer == 0 ) {
   2001 		return qfalse;
   2002 	}
   2003 
   2004 	// no shadows when invisible
   2005 	if ( cent->currentState.powerups & ( 1 << PW_INVIS ) ) {
   2006 		return qfalse;
   2007 	}
   2008 
   2009 	// send a trace down from the player to the ground
   2010 	VectorCopy( cent->lerpOrigin, end );
   2011 	end[2] -= SHADOW_DISTANCE;
   2012 
   2013 	trap_CM_BoxTrace( &trace, cent->lerpOrigin, end, mins, maxs, 0, MASK_PLAYERSOLID );
   2014 
   2015 	// no shadow if too high
   2016 	if ( trace.fraction == 1.0 || trace.startsolid || trace.allsolid ) {
   2017 		return qfalse;
   2018 	}
   2019 
   2020 	*shadowPlane = trace.endpos[2] + 1;
   2021 
   2022 	if ( cg_shadows.integer != 1 ) {	// no mark for stencil or projection shadows
   2023 		return qtrue;
   2024 	}
   2025 
   2026 	// fade the shadow out with height
   2027 	alpha = 1.0 - trace.fraction;
   2028 
   2029 	// bk0101022 - hack / FPE - bogus planes?
   2030 	//assert( DotProduct( trace.plane.normal, trace.plane.normal ) != 0.0f ) 
   2031 
   2032 	// add the mark as a temporary, so it goes directly to the renderer
   2033 	// without taking a spot in the cg_marks array
   2034 	CG_ImpactMark( cgs.media.shadowMarkShader, trace.endpos, trace.plane.normal, 
   2035 		cent->pe.legs.yawAngle, alpha,alpha,alpha,1, qfalse, 24, qtrue );
   2036 
   2037 	return qtrue;
   2038 }
   2039 
   2040 
   2041 /*
   2042 ===============
   2043 CG_PlayerSplash
   2044 
   2045 Draw a mark at the water surface
   2046 ===============
   2047 */
   2048 static void CG_PlayerSplash( centity_t *cent ) {
   2049 	vec3_t		start, end;
   2050 	trace_t		trace;
   2051 	int			contents;
   2052 	polyVert_t	verts[4];
   2053 
   2054 	if ( !cg_shadows.integer ) {
   2055 		return;
   2056 	}
   2057 
   2058 	VectorCopy( cent->lerpOrigin, end );
   2059 	end[2] -= 24;
   2060 
   2061 	// if the feet aren't in liquid, don't make a mark
   2062 	// this won't handle moving water brushes, but they wouldn't draw right anyway...
   2063 	contents = trap_CM_PointContents( end, 0 );
   2064 	if ( !( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) ) {
   2065 		return;
   2066 	}
   2067 
   2068 	VectorCopy( cent->lerpOrigin, start );
   2069 	start[2] += 32;
   2070 
   2071 	// if the head isn't out of liquid, don't make a mark
   2072 	contents = trap_CM_PointContents( start, 0 );
   2073 	if ( contents & ( CONTENTS_SOLID | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) {
   2074 		return;
   2075 	}
   2076 
   2077 	// trace down to find the surface
   2078 	trap_CM_BoxTrace( &trace, start, end, NULL, NULL, 0, ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) );
   2079 
   2080 	if ( trace.fraction == 1.0 ) {
   2081 		return;
   2082 	}
   2083 
   2084 	// create a mark polygon
   2085 	VectorCopy( trace.endpos, verts[0].xyz );
   2086 	verts[0].xyz[0] -= 32;
   2087 	verts[0].xyz[1] -= 32;
   2088 	verts[0].st[0] = 0;
   2089 	verts[0].st[1] = 0;
   2090 	verts[0].modulate[0] = 255;
   2091 	verts[0].modulate[1] = 255;
   2092 	verts[0].modulate[2] = 255;
   2093 	verts[0].modulate[3] = 255;
   2094 
   2095 	VectorCopy( trace.endpos, verts[1].xyz );
   2096 	verts[1].xyz[0] -= 32;
   2097 	verts[1].xyz[1] += 32;
   2098 	verts[1].st[0] = 0;
   2099 	verts[1].st[1] = 1;
   2100 	verts[1].modulate[0] = 255;
   2101 	verts[1].modulate[1] = 255;
   2102 	verts[1].modulate[2] = 255;
   2103 	verts[1].modulate[3] = 255;
   2104 
   2105 	VectorCopy( trace.endpos, verts[2].xyz );
   2106 	verts[2].xyz[0] += 32;
   2107 	verts[2].xyz[1] += 32;
   2108 	verts[2].st[0] = 1;
   2109 	verts[2].st[1] = 1;
   2110 	verts[2].modulate[0] = 255;
   2111 	verts[2].modulate[1] = 255;
   2112 	verts[2].modulate[2] = 255;
   2113 	verts[2].modulate[3] = 255;
   2114 
   2115 	VectorCopy( trace.endpos, verts[3].xyz );
   2116 	verts[3].xyz[0] += 32;
   2117 	verts[3].xyz[1] -= 32;
   2118 	verts[3].st[0] = 1;
   2119 	verts[3].st[1] = 0;
   2120 	verts[3].modulate[0] = 255;
   2121 	verts[3].modulate[1] = 255;
   2122 	verts[3].modulate[2] = 255;
   2123 	verts[3].modulate[3] = 255;
   2124 
   2125 	trap_R_AddPolyToScene( cgs.media.wakeMarkShader, 4, verts );
   2126 }
   2127 
   2128 
   2129 
   2130 /*
   2131 ===============
   2132 CG_AddRefEntityWithPowerups
   2133 
   2134 Adds a piece with modifications or duplications for powerups
   2135 Also called by CG_Missile for quad rockets, but nobody can tell...
   2136 ===============
   2137 */
   2138 void CG_AddRefEntityWithPowerups( refEntity_t *ent, entityState_t *state, int team ) {
   2139 
   2140 	if ( state->powerups & ( 1 << PW_INVIS ) ) {
   2141 		ent->customShader = cgs.media.invisShader;
   2142 		trap_R_AddRefEntityToScene( ent );
   2143 	} else {
   2144 		/*
   2145 		if ( state->eFlags & EF_KAMIKAZE ) {
   2146 			if (team == TEAM_BLUE)
   2147 				ent->customShader = cgs.media.blueKamikazeShader;
   2148 			else
   2149 				ent->customShader = cgs.media.redKamikazeShader;
   2150 			trap_R_AddRefEntityToScene( ent );
   2151 		}
   2152 		else {*/
   2153 			trap_R_AddRefEntityToScene( ent );
   2154 		//}
   2155 
   2156 		if ( state->powerups & ( 1 << PW_QUAD ) )
   2157 		{
   2158 			if (team == TEAM_RED)
   2159 				ent->customShader = cgs.media.redQuadShader;
   2160 			else
   2161 				ent->customShader = cgs.media.quadShader;
   2162 			trap_R_AddRefEntityToScene( ent );
   2163 		}
   2164 		if ( state->powerups & ( 1 << PW_REGEN ) ) {
   2165 			if ( ( ( cg.time / 100 ) % 10 ) == 1 ) {
   2166 				ent->customShader = cgs.media.regenShader;
   2167 				trap_R_AddRefEntityToScene( ent );
   2168 			}
   2169 		}
   2170 		if ( state->powerups & ( 1 << PW_BATTLESUIT ) ) {
   2171 			ent->customShader = cgs.media.battleSuitShader;
   2172 			trap_R_AddRefEntityToScene( ent );
   2173 		}
   2174 	}
   2175 }
   2176 
   2177 /*
   2178 =================
   2179 CG_LightVerts
   2180 =================
   2181 */
   2182 int CG_LightVerts( vec3_t normal, int numVerts, polyVert_t *verts )
   2183 {
   2184 	int				i, j;
   2185 	float			incoming;
   2186 	vec3_t			ambientLight;
   2187 	vec3_t			lightDir;
   2188 	vec3_t			directedLight;
   2189 
   2190 	trap_R_LightForPoint( verts[0].xyz, ambientLight, directedLight, lightDir );
   2191 
   2192 	for (i = 0; i < numVerts; i++) {
   2193 		incoming = DotProduct (normal, lightDir);
   2194 		if ( incoming <= 0 ) {
   2195 			verts[i].modulate[0] = ambientLight[0];
   2196 			verts[i].modulate[1] = ambientLight[1];
   2197 			verts[i].modulate[2] = ambientLight[2];
   2198 			verts[i].modulate[3] = 255;
   2199 			continue;
   2200 		} 
   2201 		j = ( ambientLight[0] + incoming * directedLight[0] );
   2202 		if ( j > 255 ) {
   2203 			j = 255;
   2204 		}
   2205 		verts[i].modulate[0] = j;
   2206 
   2207 		j = ( ambientLight[1] + incoming * directedLight[1] );
   2208 		if ( j > 255 ) {
   2209 			j = 255;
   2210 		}
   2211 		verts[i].modulate[1] = j;
   2212 
   2213 		j = ( ambientLight[2] + incoming * directedLight[2] );
   2214 		if ( j > 255 ) {
   2215 			j = 255;
   2216 		}
   2217 		verts[i].modulate[2] = j;
   2218 
   2219 		verts[i].modulate[3] = 255;
   2220 	}
   2221 	return qtrue;
   2222 }
   2223 
   2224 /*
   2225 ===============
   2226 CG_Player
   2227 ===============
   2228 */
   2229 void CG_Player( centity_t *cent ) {
   2230 	clientInfo_t	*ci;
   2231 	refEntity_t		legs;
   2232 	refEntity_t		torso;
   2233 	refEntity_t		head;
   2234 	int				clientNum;
   2235 	int				renderfx;
   2236 	qboolean		shadow;
   2237 	float			shadowPlane;
   2238 #ifdef MISSIONPACK
   2239 	refEntity_t		skull;
   2240 	refEntity_t		powerup;
   2241 	int				t;
   2242 	float			c;
   2243 	float			angle;
   2244 	vec3_t			dir, angles;
   2245 #endif
   2246 
   2247 	// the client number is stored in clientNum.  It can't be derived
   2248 	// from the entity number, because a single client may have
   2249 	// multiple corpses on the level using the same clientinfo
   2250 	clientNum = cent->currentState.clientNum;
   2251 	if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
   2252 		CG_Error( "Bad clientNum on player entity");
   2253 	}
   2254 	ci = &cgs.clientinfo[ clientNum ];
   2255 
   2256 	// it is possible to see corpses from disconnected players that may
   2257 	// not have valid clientinfo
   2258 	if ( !ci->infoValid ) {
   2259 		return;
   2260 	}
   2261 
   2262 	// get the player model information
   2263 	renderfx = 0;
   2264 	if ( cent->currentState.number == cg.snap->ps.clientNum) {
   2265 		if (!cg.renderingThirdPerson) {
   2266 			renderfx = RF_THIRD_PERSON;			// only draw in mirrors
   2267 		} else {
   2268 			if (cg_cameraMode.integer) {
   2269 				return;
   2270 			}
   2271 		}
   2272 	}
   2273 
   2274 
   2275 	memset( &legs, 0, sizeof(legs) );
   2276 	memset( &torso, 0, sizeof(torso) );
   2277 	memset( &head, 0, sizeof(head) );
   2278 
   2279 	// get the rotation information
   2280 	CG_PlayerAngles( cent, legs.axis, torso.axis, head.axis );
   2281 	
   2282 	// get the animation state (after rotation, to allow feet shuffle)
   2283 	CG_PlayerAnimation( cent, &legs.oldframe, &legs.frame, &legs.backlerp,
   2284 		 &torso.oldframe, &torso.frame, &torso.backlerp );
   2285 
   2286 	// add the talk baloon or disconnect icon
   2287 	CG_PlayerSprites( cent );
   2288 
   2289 	// add the shadow
   2290 	shadow = CG_PlayerShadow( cent, &shadowPlane );
   2291 
   2292 	// add a water splash if partially in and out of water
   2293 	CG_PlayerSplash( cent );
   2294 
   2295 	if ( cg_shadows.integer == 3 && shadow ) {
   2296 		renderfx |= RF_SHADOW_PLANE;
   2297 	}
   2298 	renderfx |= RF_LIGHTING_ORIGIN;			// use the same origin for all
   2299 #ifdef MISSIONPACK
   2300 	if( cgs.gametype == GT_HARVESTER ) {
   2301 		CG_PlayerTokens( cent, renderfx );
   2302 	}
   2303 #endif
   2304 	//
   2305 	// add the legs
   2306 	//
   2307 	legs.hModel = ci->legsModel;
   2308 	legs.customSkin = ci->legsSkin;
   2309 
   2310 	VectorCopy( cent->lerpOrigin, legs.origin );
   2311 
   2312 	VectorCopy( cent->lerpOrigin, legs.lightingOrigin );
   2313 	legs.shadowPlane = shadowPlane;
   2314 	legs.renderfx = renderfx;
   2315 	VectorCopy (legs.origin, legs.oldorigin);	// don't positionally lerp at all
   2316 
   2317 	CG_AddRefEntityWithPowerups( &legs, &cent->currentState, ci->team );
   2318 
   2319 	// if the model failed, allow the default nullmodel to be displayed
   2320 	if (!legs.hModel) {
   2321 		return;
   2322 	}
   2323 
   2324 	//
   2325 	// add the torso
   2326 	//
   2327 	torso.hModel = ci->torsoModel;
   2328 	if (!torso.hModel) {
   2329 		return;
   2330 	}
   2331 
   2332 	torso.customSkin = ci->torsoSkin;
   2333 
   2334 	VectorCopy( cent->lerpOrigin, torso.lightingOrigin );
   2335 
   2336 	CG_PositionRotatedEntityOnTag( &torso, &legs, ci->legsModel, "tag_torso");
   2337 
   2338 	torso.shadowPlane = shadowPlane;
   2339 	torso.renderfx = renderfx;
   2340 
   2341 	CG_AddRefEntityWithPowerups( &torso, &cent->currentState, ci->team );
   2342 
   2343 #ifdef MISSIONPACK
   2344 	if ( cent->currentState.eFlags & EF_KAMIKAZE ) {
   2345 
   2346 		memset( &skull, 0, sizeof(skull) );
   2347 
   2348 		VectorCopy( cent->lerpOrigin, skull.lightingOrigin );
   2349 		skull.shadowPlane = shadowPlane;
   2350 		skull.renderfx = renderfx;
   2351 
   2352 		if ( cent->currentState.eFlags & EF_DEAD ) {
   2353 			// one skull bobbing above the dead body
   2354 			angle = ((cg.time / 7) & 255) * (M_PI * 2) / 255;
   2355 			if (angle > M_PI * 2)
   2356 				angle -= (float)M_PI * 2;
   2357 			dir[0] = sin(angle) * 20;
   2358 			dir[1] = cos(angle) * 20;
   2359 			angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255;
   2360 			dir[2] = 15 + sin(angle) * 8;
   2361 			VectorAdd(torso.origin, dir, skull.origin);
   2362 			
   2363 			dir[2] = 0;
   2364 			VectorCopy(dir, skull.axis[1]);
   2365 			VectorNormalize(skull.axis[1]);
   2366 			VectorSet(skull.axis[2], 0, 0, 1);
   2367 			CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
   2368 
   2369 			skull.hModel = cgs.media.kamikazeHeadModel;
   2370 			trap_R_AddRefEntityToScene( &skull );
   2371 			skull.hModel = cgs.media.kamikazeHeadTrail;
   2372 			trap_R_AddRefEntityToScene( &skull );
   2373 		}
   2374 		else {
   2375 			// three skulls spinning around the player
   2376 			angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255;
   2377 			dir[0] = cos(angle) * 20;
   2378 			dir[1] = sin(angle) * 20;
   2379 			dir[2] = cos(angle) * 20;
   2380 			VectorAdd(torso.origin, dir, skull.origin);
   2381 
   2382 			angles[0] = sin(angle) * 30;
   2383 			angles[1] = (angle * 180 / M_PI) + 90;
   2384 			if (angles[1] > 360)
   2385 				angles[1] -= 360;
   2386 			angles[2] = 0;
   2387 			AnglesToAxis( angles, skull.axis );
   2388 
   2389 			/*
   2390 			dir[2] = 0;
   2391 			VectorInverse(dir);
   2392 			VectorCopy(dir, skull.axis[1]);
   2393 			VectorNormalize(skull.axis[1]);
   2394 			VectorSet(skull.axis[2], 0, 0, 1);
   2395 			CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
   2396 			*/
   2397 
   2398 			skull.hModel = cgs.media.kamikazeHeadModel;
   2399 			trap_R_AddRefEntityToScene( &skull );
   2400 			// flip the trail because this skull is spinning in the other direction
   2401 			VectorInverse(skull.axis[1]);
   2402 			skull.hModel = cgs.media.kamikazeHeadTrail;
   2403 			trap_R_AddRefEntityToScene( &skull );
   2404 
   2405 			angle = ((cg.time / 4) & 255) * (M_PI * 2) / 255 + M_PI;
   2406 			if (angle > M_PI * 2)
   2407 				angle -= (float)M_PI * 2;
   2408 			dir[0] = sin(angle) * 20;
   2409 			dir[1] = cos(angle) * 20;
   2410 			dir[2] = cos(angle) * 20;
   2411 			VectorAdd(torso.origin, dir, skull.origin);
   2412 
   2413 			angles[0] = cos(angle - 0.5 * M_PI) * 30;
   2414 			angles[1] = 360 - (angle * 180 / M_PI);
   2415 			if (angles[1] > 360)
   2416 				angles[1] -= 360;
   2417 			angles[2] = 0;
   2418 			AnglesToAxis( angles, skull.axis );
   2419 
   2420 			/*
   2421 			dir[2] = 0;
   2422 			VectorCopy(dir, skull.axis[1]);
   2423 			VectorNormalize(skull.axis[1]);
   2424 			VectorSet(skull.axis[2], 0, 0, 1);
   2425 			CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
   2426 			*/
   2427 
   2428 			skull.hModel = cgs.media.kamikazeHeadModel;
   2429 			trap_R_AddRefEntityToScene( &skull );
   2430 			skull.hModel = cgs.media.kamikazeHeadTrail;
   2431 			trap_R_AddRefEntityToScene( &skull );
   2432 
   2433 			angle = ((cg.time / 3) & 255) * (M_PI * 2) / 255 + 0.5 * M_PI;
   2434 			if (angle > M_PI * 2)
   2435 				angle -= (float)M_PI * 2;
   2436 			dir[0] = sin(angle) * 20;
   2437 			dir[1] = cos(angle) * 20;
   2438 			dir[2] = 0;
   2439 			VectorAdd(torso.origin, dir, skull.origin);
   2440 			
   2441 			VectorCopy(dir, skull.axis[1]);
   2442 			VectorNormalize(skull.axis[1]);
   2443 			VectorSet(skull.axis[2], 0, 0, 1);
   2444 			CrossProduct(skull.axis[1], skull.axis[2], skull.axis[0]);
   2445 
   2446 			skull.hModel = cgs.media.kamikazeHeadModel;
   2447 			trap_R_AddRefEntityToScene( &skull );
   2448 			skull.hModel = cgs.media.kamikazeHeadTrail;
   2449 			trap_R_AddRefEntityToScene( &skull );
   2450 		}
   2451 	}
   2452 
   2453 	if ( cent->currentState.powerups & ( 1 << PW_GUARD ) ) {
   2454 		memcpy(&powerup, &torso, sizeof(torso));
   2455 		powerup.hModel = cgs.media.guardPowerupModel;
   2456 		powerup.frame = 0;
   2457 		powerup.oldframe = 0;
   2458 		powerup.customSkin = 0;
   2459 		trap_R_AddRefEntityToScene( &powerup );
   2460 	}
   2461 	if ( cent->currentState.powerups & ( 1 << PW_SCOUT ) ) {
   2462 		memcpy(&powerup, &torso, sizeof(torso));
   2463 		powerup.hModel = cgs.media.scoutPowerupModel;
   2464 		powerup.frame = 0;
   2465 		powerup.oldframe = 0;
   2466 		powerup.customSkin = 0;
   2467 		trap_R_AddRefEntityToScene( &powerup );
   2468 	}
   2469 	if ( cent->currentState.powerups & ( 1 << PW_DOUBLER ) ) {
   2470 		memcpy(&powerup, &torso, sizeof(torso));
   2471 		powerup.hModel = cgs.media.doublerPowerupModel;
   2472 		powerup.frame = 0;
   2473 		powerup.oldframe = 0;
   2474 		powerup.customSkin = 0;
   2475 		trap_R_AddRefEntityToScene( &powerup );
   2476 	}
   2477 	if ( cent->currentState.powerups & ( 1 << PW_AMMOREGEN ) ) {
   2478 		memcpy(&powerup, &torso, sizeof(torso));
   2479 		powerup.hModel = cgs.media.ammoRegenPowerupModel;
   2480 		powerup.frame = 0;
   2481 		powerup.oldframe = 0;
   2482 		powerup.customSkin = 0;
   2483 		trap_R_AddRefEntityToScene( &powerup );
   2484 	}
   2485 	if ( cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) {
   2486 		if ( !ci->invulnerabilityStartTime ) {
   2487 			ci->invulnerabilityStartTime = cg.time;
   2488 		}
   2489 		ci->invulnerabilityStopTime = cg.time;
   2490 	}
   2491 	else {
   2492 		ci->invulnerabilityStartTime = 0;
   2493 	}
   2494 	if ( (cent->currentState.powerups & ( 1 << PW_INVULNERABILITY ) ) ||
   2495 		cg.time - ci->invulnerabilityStopTime < 250 ) {
   2496 
   2497 		memcpy(&powerup, &torso, sizeof(torso));
   2498 		powerup.hModel = cgs.media.invulnerabilityPowerupModel;
   2499 		powerup.customSkin = 0;
   2500 		// always draw
   2501 		powerup.renderfx &= ~RF_THIRD_PERSON;
   2502 		VectorCopy(cent->lerpOrigin, powerup.origin);
   2503 
   2504 		if ( cg.time - ci->invulnerabilityStartTime < 250 ) {
   2505 			c = (float) (cg.time - ci->invulnerabilityStartTime) / 250;
   2506 		}
   2507 		else if (cg.time - ci->invulnerabilityStopTime < 250 ) {
   2508 			c = (float) (250 - (cg.time - ci->invulnerabilityStopTime)) / 250;
   2509 		}
   2510 		else {
   2511 			c = 1;
   2512 		}
   2513 		VectorSet( powerup.axis[0], c, 0, 0 );
   2514 		VectorSet( powerup.axis[1], 0, c, 0 );
   2515 		VectorSet( powerup.axis[2], 0, 0, c );
   2516 		trap_R_AddRefEntityToScene( &powerup );
   2517 	}
   2518 
   2519 	t = cg.time - ci->medkitUsageTime;
   2520 	if ( ci->medkitUsageTime && t < 500 ) {
   2521 		memcpy(&powerup, &torso, sizeof(torso));
   2522 		powerup.hModel = cgs.media.medkitUsageModel;
   2523 		powerup.customSkin = 0;
   2524 		// always draw
   2525 		powerup.renderfx &= ~RF_THIRD_PERSON;
   2526 		VectorClear(angles);
   2527 		AnglesToAxis(angles, powerup.axis);
   2528 		VectorCopy(cent->lerpOrigin, powerup.origin);
   2529 		powerup.origin[2] += -24 + (float) t * 80 / 500;
   2530 		if ( t > 400 ) {
   2531 			c = (float) (t - 1000) * 0xff / 100;
   2532 			powerup.shaderRGBA[0] = 0xff - c;
   2533 			powerup.shaderRGBA[1] = 0xff - c;
   2534 			powerup.shaderRGBA[2] = 0xff - c;
   2535 			powerup.shaderRGBA[3] = 0xff - c;
   2536 		}
   2537 		else {
   2538 			powerup.shaderRGBA[0] = 0xff;
   2539 			powerup.shaderRGBA[1] = 0xff;
   2540 			powerup.shaderRGBA[2] = 0xff;
   2541 			powerup.shaderRGBA[3] = 0xff;
   2542 		}
   2543 		trap_R_AddRefEntityToScene( &powerup );
   2544 	}
   2545 #endif // MISSIONPACK
   2546 
   2547 	//
   2548 	// add the head
   2549 	//
   2550 	head.hModel = ci->headModel;
   2551 	if (!head.hModel) {
   2552 		return;
   2553 	}
   2554 	head.customSkin = ci->headSkin;
   2555 
   2556 	VectorCopy( cent->lerpOrigin, head.lightingOrigin );
   2557 
   2558 	CG_PositionRotatedEntityOnTag( &head, &torso, ci->torsoModel, "tag_head");
   2559 
   2560 	head.shadowPlane = shadowPlane;
   2561 	head.renderfx = renderfx;
   2562 
   2563 	CG_AddRefEntityWithPowerups( &head, &cent->currentState, ci->team );
   2564 
   2565 #ifdef MISSIONPACK
   2566 	CG_BreathPuffs(cent, &head);
   2567 
   2568 	CG_DustTrail(cent);
   2569 #endif
   2570 
   2571 	//
   2572 	// add the gun / barrel / flash
   2573 	//
   2574 	CG_AddPlayerWeapon( &torso, NULL, cent, ci->team );
   2575 
   2576 	// add powerups floating behind the player
   2577 	CG_PlayerPowerups( cent, &torso );
   2578 }
   2579 
   2580 
   2581 //=====================================================================
   2582 
   2583 /*
   2584 ===============
   2585 CG_ResetPlayerEntity
   2586 
   2587 A player just came into view or teleported, so reset all animation info
   2588 ===============
   2589 */
   2590 void CG_ResetPlayerEntity( centity_t *cent ) {
   2591 	cent->errorTime = -99999;		// guarantee no error decay added
   2592 	cent->extrapolated = qfalse;	
   2593 
   2594 	CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], &cent->pe.legs, cent->currentState.legsAnim );
   2595 	CG_ClearLerpFrame( &cgs.clientinfo[ cent->currentState.clientNum ], &cent->pe.torso, cent->currentState.torsoAnim );
   2596 
   2597 	BG_EvaluateTrajectory( &cent->currentState.pos, cg.time, cent->lerpOrigin );
   2598 	BG_EvaluateTrajectory( &cent->currentState.apos, cg.time, cent->lerpAngles );
   2599 
   2600 	VectorCopy( cent->lerpOrigin, cent->rawOrigin );
   2601 	VectorCopy( cent->lerpAngles, cent->rawAngles );
   2602 
   2603 	memset( &cent->pe.legs, 0, sizeof( cent->pe.legs ) );
   2604 	cent->pe.legs.yawAngle = cent->rawAngles[YAW];
   2605 	cent->pe.legs.yawing = qfalse;
   2606 	cent->pe.legs.pitchAngle = 0;
   2607 	cent->pe.legs.pitching = qfalse;
   2608 
   2609 	memset( &cent->pe.torso, 0, sizeof( cent->pe.legs ) );
   2610 	cent->pe.torso.yawAngle = cent->rawAngles[YAW];
   2611 	cent->pe.torso.yawing = qfalse;
   2612 	cent->pe.torso.pitchAngle = cent->rawAngles[PITCH];
   2613 	cent->pe.torso.pitching = qfalse;
   2614 
   2615 	if ( cg_debugPosition.integer ) {
   2616 		CG_Printf("%i ResetPlayerEntity yaw=%i\n", cent->currentState.number, cent->pe.torso.yawAngle );
   2617 	}
   2618 }
   2619