Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

cg_localents.c (22133B)


      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 
     24 // cg_localents.c -- every frame, generate renderer commands for locally
     25 // processed entities, like smoke puffs, gibs, shells, etc.
     26 
     27 #include "cg_local.h"
     28 
     29 #define	MAX_LOCAL_ENTITIES	512
     30 localEntity_t	cg_localEntities[MAX_LOCAL_ENTITIES];
     31 localEntity_t	cg_activeLocalEntities;		// double linked list
     32 localEntity_t	*cg_freeLocalEntities;		// single linked list
     33 
     34 /*
     35 ===================
     36 CG_InitLocalEntities
     37 
     38 This is called at startup and for tournement restarts
     39 ===================
     40 */
     41 void	CG_InitLocalEntities( void ) {
     42 	int		i;
     43 
     44 	memset( cg_localEntities, 0, sizeof( cg_localEntities ) );
     45 	cg_activeLocalEntities.next = &cg_activeLocalEntities;
     46 	cg_activeLocalEntities.prev = &cg_activeLocalEntities;
     47 	cg_freeLocalEntities = cg_localEntities;
     48 	for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) {
     49 		cg_localEntities[i].next = &cg_localEntities[i+1];
     50 	}
     51 }
     52 
     53 
     54 /*
     55 ==================
     56 CG_FreeLocalEntity
     57 ==================
     58 */
     59 void CG_FreeLocalEntity( localEntity_t *le ) {
     60 	if ( !le->prev ) {
     61 		CG_Error( "CG_FreeLocalEntity: not active" );
     62 	}
     63 
     64 	// remove from the doubly linked active list
     65 	le->prev->next = le->next;
     66 	le->next->prev = le->prev;
     67 
     68 	// the free list is only singly linked
     69 	le->next = cg_freeLocalEntities;
     70 	cg_freeLocalEntities = le;
     71 }
     72 
     73 /*
     74 ===================
     75 CG_AllocLocalEntity
     76 
     77 Will allways succeed, even if it requires freeing an old active entity
     78 ===================
     79 */
     80 localEntity_t	*CG_AllocLocalEntity( void ) {
     81 	localEntity_t	*le;
     82 
     83 	if ( !cg_freeLocalEntities ) {
     84 		// no free entities, so free the one at the end of the chain
     85 		// remove the oldest active entity
     86 		CG_FreeLocalEntity( cg_activeLocalEntities.prev );
     87 	}
     88 
     89 	le = cg_freeLocalEntities;
     90 	cg_freeLocalEntities = cg_freeLocalEntities->next;
     91 
     92 	memset( le, 0, sizeof( *le ) );
     93 
     94 	// link into the active list
     95 	le->next = cg_activeLocalEntities.next;
     96 	le->prev = &cg_activeLocalEntities;
     97 	cg_activeLocalEntities.next->prev = le;
     98 	cg_activeLocalEntities.next = le;
     99 	return le;
    100 }
    101 
    102 
    103 /*
    104 ====================================================================================
    105 
    106 FRAGMENT PROCESSING
    107 
    108 A fragment localentity interacts with the environment in some way (hitting walls),
    109 or generates more localentities along a trail.
    110 
    111 ====================================================================================
    112 */
    113 
    114 /*
    115 ================
    116 CG_BloodTrail
    117 
    118 Leave expanding blood puffs behind gibs
    119 ================
    120 */
    121 void CG_BloodTrail( localEntity_t *le ) {
    122 	int		t;
    123 	int		t2;
    124 	int		step;
    125 	vec3_t	newOrigin;
    126 	localEntity_t	*blood;
    127 
    128 	step = 150;
    129 	t = step * ( (cg.time - cg.frametime + step ) / step );
    130 	t2 = step * ( cg.time / step );
    131 
    132 	for ( ; t <= t2; t += step ) {
    133 		BG_EvaluateTrajectory( &le->pos, t, newOrigin );
    134 
    135 		blood = CG_SmokePuff( newOrigin, vec3_origin, 
    136 					  20,		// radius
    137 					  1, 1, 1, 1,	// color
    138 					  2000,		// trailTime
    139 					  t,		// startTime
    140 					  0,		// fadeInTime
    141 					  0,		// flags
    142 					  cgs.media.bloodTrailShader );
    143 		// use the optimized version
    144 		blood->leType = LE_FALL_SCALE_FADE;
    145 		// drop a total of 40 units over its lifetime
    146 		blood->pos.trDelta[2] = 40;
    147 	}
    148 }
    149 
    150 
    151 /*
    152 ================
    153 CG_FragmentBounceMark
    154 ================
    155 */
    156 void CG_FragmentBounceMark( localEntity_t *le, trace_t *trace ) {
    157 	int			radius;
    158 
    159 	if ( le->leMarkType == LEMT_BLOOD ) {
    160 
    161 		radius = 16 + (rand()&31);
    162 		CG_ImpactMark( cgs.media.bloodMarkShader, trace->endpos, trace->plane.normal, random()*360,
    163 			1,1,1,1, qtrue, radius, qfalse );
    164 	} else if ( le->leMarkType == LEMT_BURN ) {
    165 
    166 		radius = 8 + (rand()&15);
    167 		CG_ImpactMark( cgs.media.burnMarkShader, trace->endpos, trace->plane.normal, random()*360,
    168 			1,1,1,1, qtrue, radius, qfalse );
    169 	}
    170 
    171 
    172 	// don't allow a fragment to make multiple marks, or they
    173 	// pile up while settling
    174 	le->leMarkType = LEMT_NONE;
    175 }
    176 
    177 /*
    178 ================
    179 CG_FragmentBounceSound
    180 ================
    181 */
    182 void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace ) {
    183 	if ( le->leBounceSoundType == LEBS_BLOOD ) {
    184 		// half the gibs will make splat sounds
    185 		if ( rand() & 1 ) {
    186 			int r = rand()&3;
    187 			sfxHandle_t	s;
    188 
    189 			if ( r == 0 ) {
    190 				s = cgs.media.gibBounce1Sound;
    191 			} else if ( r == 1 ) {
    192 				s = cgs.media.gibBounce2Sound;
    193 			} else {
    194 				s = cgs.media.gibBounce3Sound;
    195 			}
    196 			trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s );
    197 		}
    198 	} else if ( le->leBounceSoundType == LEBS_BRASS ) {
    199 
    200 	}
    201 
    202 	// don't allow a fragment to make multiple bounce sounds,
    203 	// or it gets too noisy as they settle
    204 	le->leBounceSoundType = LEBS_NONE;
    205 }
    206 
    207 
    208 /*
    209 ================
    210 CG_ReflectVelocity
    211 ================
    212 */
    213 void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) {
    214 	vec3_t	velocity;
    215 	float	dot;
    216 	int		hitTime;
    217 
    218 	// reflect the velocity on the trace plane
    219 	hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
    220 	BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
    221 	dot = DotProduct( velocity, trace->plane.normal );
    222 	VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta );
    223 
    224 	VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );
    225 
    226 	VectorCopy( trace->endpos, le->pos.trBase );
    227 	le->pos.trTime = cg.time;
    228 
    229 
    230 	// check for stop, making sure that even on low FPS systems it doesn't bobble
    231 	if ( trace->allsolid || 
    232 		( trace->plane.normal[2] > 0 && 
    233 		( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) {
    234 		le->pos.trType = TR_STATIONARY;
    235 	} else {
    236 
    237 	}
    238 }
    239 
    240 /*
    241 ================
    242 CG_AddFragment
    243 ================
    244 */
    245 void CG_AddFragment( localEntity_t *le ) {
    246 	vec3_t	newOrigin;
    247 	trace_t	trace;
    248 
    249 	if ( le->pos.trType == TR_STATIONARY ) {
    250 		// sink into the ground if near the removal time
    251 		int		t;
    252 		float	oldZ;
    253 		
    254 		t = le->endTime - cg.time;
    255 		if ( t < SINK_TIME ) {
    256 			// we must use an explicit lighting origin, otherwise the
    257 			// lighting would be lost as soon as the origin went
    258 			// into the ground
    259 			VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin );
    260 			le->refEntity.renderfx |= RF_LIGHTING_ORIGIN;
    261 			oldZ = le->refEntity.origin[2];
    262 			le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME );
    263 			trap_R_AddRefEntityToScene( &le->refEntity );
    264 			le->refEntity.origin[2] = oldZ;
    265 		} else {
    266 			trap_R_AddRefEntityToScene( &le->refEntity );
    267 		}
    268 
    269 		return;
    270 	}
    271 
    272 	// calculate new position
    273 	BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin );
    274 
    275 	// trace a line from previous position to new position
    276 	CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID );
    277 	if ( trace.fraction == 1.0 ) {
    278 		// still in free fall
    279 		VectorCopy( newOrigin, le->refEntity.origin );
    280 
    281 		if ( le->leFlags & LEF_TUMBLE ) {
    282 			vec3_t angles;
    283 
    284 			BG_EvaluateTrajectory( &le->angles, cg.time, angles );
    285 			AnglesToAxis( angles, le->refEntity.axis );
    286 		}
    287 
    288 		trap_R_AddRefEntityToScene( &le->refEntity );
    289 
    290 		// add a blood trail
    291 		if ( le->leBounceSoundType == LEBS_BLOOD ) {
    292 			CG_BloodTrail( le );
    293 		}
    294 
    295 		return;
    296 	}
    297 
    298 	// if it is in a nodrop zone, remove it
    299 	// this keeps gibs from waiting at the bottom of pits of death
    300 	// and floating levels
    301 	if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) {
    302 		CG_FreeLocalEntity( le );
    303 		return;
    304 	}
    305 
    306 	// leave a mark
    307 	CG_FragmentBounceMark( le, &trace );
    308 
    309 	// do a bouncy sound
    310 	CG_FragmentBounceSound( le, &trace );
    311 
    312 	// reflect the velocity on the trace plane
    313 	CG_ReflectVelocity( le, &trace );
    314 
    315 	trap_R_AddRefEntityToScene( &le->refEntity );
    316 }
    317 
    318 /*
    319 =====================================================================
    320 
    321 TRIVIAL LOCAL ENTITIES
    322 
    323 These only do simple scaling or modulation before passing to the renderer
    324 =====================================================================
    325 */
    326 
    327 /*
    328 ====================
    329 CG_AddFadeRGB
    330 ====================
    331 */
    332 void CG_AddFadeRGB( localEntity_t *le ) {
    333 	refEntity_t *re;
    334 	float c;
    335 
    336 	re = &le->refEntity;
    337 
    338 	c = ( le->endTime - cg.time ) * le->lifeRate;
    339 	c *= 0xff;
    340 
    341 	re->shaderRGBA[0] = le->color[0] * c;
    342 	re->shaderRGBA[1] = le->color[1] * c;
    343 	re->shaderRGBA[2] = le->color[2] * c;
    344 	re->shaderRGBA[3] = le->color[3] * c;
    345 
    346 	trap_R_AddRefEntityToScene( re );
    347 }
    348 
    349 /*
    350 ==================
    351 CG_AddMoveScaleFade
    352 ==================
    353 */
    354 static void CG_AddMoveScaleFade( localEntity_t *le ) {
    355 	refEntity_t	*re;
    356 	float		c;
    357 	vec3_t		delta;
    358 	float		len;
    359 
    360 	re = &le->refEntity;
    361 
    362 	if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) {
    363 		// fade / grow time
    364 		c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime );
    365 	}
    366 	else {
    367 		// fade / grow time
    368 		c = ( le->endTime - cg.time ) * le->lifeRate;
    369 	}
    370 
    371 	re->shaderRGBA[3] = 0xff * c * le->color[3];
    372 
    373 	if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) {
    374 		re->radius = le->radius * ( 1.0 - c ) + 8;
    375 	}
    376 
    377 	BG_EvaluateTrajectory( &le->pos, cg.time, re->origin );
    378 
    379 	// if the view would be "inside" the sprite, kill the sprite
    380 	// so it doesn't add too much overdraw
    381 	VectorSubtract( re->origin, cg.refdef.vieworg, delta );
    382 	len = VectorLength( delta );
    383 	if ( len < le->radius ) {
    384 		CG_FreeLocalEntity( le );
    385 		return;
    386 	}
    387 
    388 	trap_R_AddRefEntityToScene( re );
    389 }
    390 
    391 
    392 /*
    393 ===================
    394 CG_AddScaleFade
    395 
    396 For rocket smokes that hang in place, fade out, and are
    397 removed if the view passes through them.
    398 There are often many of these, so it needs to be simple.
    399 ===================
    400 */
    401 static void CG_AddScaleFade( localEntity_t *le ) {
    402 	refEntity_t	*re;
    403 	float		c;
    404 	vec3_t		delta;
    405 	float		len;
    406 
    407 	re = &le->refEntity;
    408 
    409 	// fade / grow time
    410 	c = ( le->endTime - cg.time ) * le->lifeRate;
    411 
    412 	re->shaderRGBA[3] = 0xff * c * le->color[3];
    413 	re->radius = le->radius * ( 1.0 - c ) + 8;
    414 
    415 	// if the view would be "inside" the sprite, kill the sprite
    416 	// so it doesn't add too much overdraw
    417 	VectorSubtract( re->origin, cg.refdef.vieworg, delta );
    418 	len = VectorLength( delta );
    419 	if ( len < le->radius ) {
    420 		CG_FreeLocalEntity( le );
    421 		return;
    422 	}
    423 
    424 	trap_R_AddRefEntityToScene( re );
    425 }
    426 
    427 
    428 /*
    429 =================
    430 CG_AddFallScaleFade
    431 
    432 This is just an optimized CG_AddMoveScaleFade
    433 For blood mists that drift down, fade out, and are
    434 removed if the view passes through them.
    435 There are often 100+ of these, so it needs to be simple.
    436 =================
    437 */
    438 static void CG_AddFallScaleFade( localEntity_t *le ) {
    439 	refEntity_t	*re;
    440 	float		c;
    441 	vec3_t		delta;
    442 	float		len;
    443 
    444 	re = &le->refEntity;
    445 
    446 	// fade time
    447 	c = ( le->endTime - cg.time ) * le->lifeRate;
    448 
    449 	re->shaderRGBA[3] = 0xff * c * le->color[3];
    450 
    451 	re->origin[2] = le->pos.trBase[2] - ( 1.0 - c ) * le->pos.trDelta[2];
    452 
    453 	re->radius = le->radius * ( 1.0 - c ) + 16;
    454 
    455 	// if the view would be "inside" the sprite, kill the sprite
    456 	// so it doesn't add too much overdraw
    457 	VectorSubtract( re->origin, cg.refdef.vieworg, delta );
    458 	len = VectorLength( delta );
    459 	if ( len < le->radius ) {
    460 		CG_FreeLocalEntity( le );
    461 		return;
    462 	}
    463 
    464 	trap_R_AddRefEntityToScene( re );
    465 }
    466 
    467 
    468 
    469 /*
    470 ================
    471 CG_AddExplosion
    472 ================
    473 */
    474 static void CG_AddExplosion( localEntity_t *ex ) {
    475 	refEntity_t	*ent;
    476 
    477 	ent = &ex->refEntity;
    478 
    479 	// add the entity
    480 	trap_R_AddRefEntityToScene(ent);
    481 
    482 	// add the dlight
    483 	if ( ex->light ) {
    484 		float		light;
    485 
    486 		light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime );
    487 		if ( light < 0.5 ) {
    488 			light = 1.0;
    489 		} else {
    490 			light = 1.0 - ( light - 0.5 ) * 2;
    491 		}
    492 		light = ex->light * light;
    493 		trap_R_AddLightToScene(ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2] );
    494 	}
    495 }
    496 
    497 /*
    498 ================
    499 CG_AddSpriteExplosion
    500 ================
    501 */
    502 static void CG_AddSpriteExplosion( localEntity_t *le ) {
    503 	refEntity_t	re;
    504 	float c;
    505 
    506 	re = le->refEntity;
    507 
    508 	c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime );
    509 	if ( c > 1 ) {
    510 		c = 1.0;	// can happen during connection problems
    511 	}
    512 
    513 	re.shaderRGBA[0] = 0xff;
    514 	re.shaderRGBA[1] = 0xff;
    515 	re.shaderRGBA[2] = 0xff;
    516 	re.shaderRGBA[3] = 0xff * c * 0.33;
    517 
    518 	re.reType = RT_SPRITE;
    519 	re.radius = 42 * ( 1.0 - c ) + 30;
    520 
    521 	trap_R_AddRefEntityToScene( &re );
    522 
    523 	// add the dlight
    524 	if ( le->light ) {
    525 		float		light;
    526 
    527 		light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime );
    528 		if ( light < 0.5 ) {
    529 			light = 1.0;
    530 		} else {
    531 			light = 1.0 - ( light - 0.5 ) * 2;
    532 		}
    533 		light = le->light * light;
    534 		trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2] );
    535 	}
    536 }
    537 
    538 
    539 #ifdef MISSIONPACK
    540 /*
    541 ====================
    542 CG_AddKamikaze
    543 ====================
    544 */
    545 void CG_AddKamikaze( localEntity_t *le ) {
    546 	refEntity_t	*re;
    547 	refEntity_t shockwave;
    548 	float		c;
    549 	vec3_t		test, axis[3];
    550 	int			t;
    551 
    552 	re = &le->refEntity;
    553 
    554 	t = cg.time - le->startTime;
    555 	VectorClear( test );
    556 	AnglesToAxis( test, axis );
    557 
    558 	if (t > KAMI_SHOCKWAVE_STARTTIME && t < KAMI_SHOCKWAVE_ENDTIME) {
    559 
    560 		if (!(le->leFlags & LEF_SOUND1)) {
    561 //			trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeExplodeSound );
    562 			trap_S_StartLocalSound(cgs.media.kamikazeExplodeSound, CHAN_AUTO);
    563 			le->leFlags |= LEF_SOUND1;
    564 		}
    565 		// 1st kamikaze shockwave
    566 		memset(&shockwave, 0, sizeof(shockwave));
    567 		shockwave.hModel = cgs.media.kamikazeShockWave;
    568 		shockwave.reType = RT_MODEL;
    569 		shockwave.shaderTime = re->shaderTime;
    570 		VectorCopy(re->origin, shockwave.origin);
    571 
    572 		c = (float)(t - KAMI_SHOCKWAVE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVE_STARTTIME);
    573 		VectorScale( axis[0], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] );
    574 		VectorScale( axis[1], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] );
    575 		VectorScale( axis[2], c * KAMI_SHOCKWAVE_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] );
    576 		shockwave.nonNormalizedAxes = qtrue;
    577 
    578 		if (t > KAMI_SHOCKWAVEFADE_STARTTIME) {
    579 			c = (float)(t - KAMI_SHOCKWAVEFADE_STARTTIME) / (float)(KAMI_SHOCKWAVE_ENDTIME - KAMI_SHOCKWAVEFADE_STARTTIME);
    580 		}
    581 		else {
    582 			c = 0;
    583 		}
    584 		c *= 0xff;
    585 		shockwave.shaderRGBA[0] = 0xff - c;
    586 		shockwave.shaderRGBA[1] = 0xff - c;
    587 		shockwave.shaderRGBA[2] = 0xff - c;
    588 		shockwave.shaderRGBA[3] = 0xff - c;
    589 
    590 		trap_R_AddRefEntityToScene( &shockwave );
    591 	}
    592 
    593 	if (t > KAMI_EXPLODE_STARTTIME && t < KAMI_IMPLODE_ENDTIME) {
    594 		// explosion and implosion
    595 		c = ( le->endTime - cg.time ) * le->lifeRate;
    596 		c *= 0xff;
    597 		re->shaderRGBA[0] = le->color[0] * c;
    598 		re->shaderRGBA[1] = le->color[1] * c;
    599 		re->shaderRGBA[2] = le->color[2] * c;
    600 		re->shaderRGBA[3] = le->color[3] * c;
    601 
    602 		if( t < KAMI_IMPLODE_STARTTIME ) {
    603 			c = (float)(t - KAMI_EXPLODE_STARTTIME) / (float)(KAMI_IMPLODE_STARTTIME - KAMI_EXPLODE_STARTTIME);
    604 		}
    605 		else {
    606 			if (!(le->leFlags & LEF_SOUND2)) {
    607 //				trap_S_StartSound (re->origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.kamikazeImplodeSound );
    608 				trap_S_StartLocalSound(cgs.media.kamikazeImplodeSound, CHAN_AUTO);
    609 				le->leFlags |= LEF_SOUND2;
    610 			}
    611 			c = (float)(KAMI_IMPLODE_ENDTIME - t) / (float) (KAMI_IMPLODE_ENDTIME - KAMI_IMPLODE_STARTTIME);
    612 		}
    613 		VectorScale( axis[0], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[0] );
    614 		VectorScale( axis[1], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[1] );
    615 		VectorScale( axis[2], c * KAMI_BOOMSPHERE_MAXRADIUS / KAMI_BOOMSPHEREMODEL_RADIUS, re->axis[2] );
    616 		re->nonNormalizedAxes = qtrue;
    617 
    618 		trap_R_AddRefEntityToScene( re );
    619 		// add the dlight
    620 		trap_R_AddLightToScene( re->origin, c * 1000.0, 1.0, 1.0, c );
    621 	}
    622 
    623 	if (t > KAMI_SHOCKWAVE2_STARTTIME && t < KAMI_SHOCKWAVE2_ENDTIME) {
    624 		// 2nd kamikaze shockwave
    625 		if (le->angles.trBase[0] == 0 &&
    626 			le->angles.trBase[1] == 0 &&
    627 			le->angles.trBase[2] == 0) {
    628 			le->angles.trBase[0] = random() * 360;
    629 			le->angles.trBase[1] = random() * 360;
    630 			le->angles.trBase[2] = random() * 360;
    631 		}
    632 		else {
    633 			c = 0;
    634 		}
    635 		memset(&shockwave, 0, sizeof(shockwave));
    636 		shockwave.hModel = cgs.media.kamikazeShockWave;
    637 		shockwave.reType = RT_MODEL;
    638 		shockwave.shaderTime = re->shaderTime;
    639 		VectorCopy(re->origin, shockwave.origin);
    640 
    641 		test[0] = le->angles.trBase[0];
    642 		test[1] = le->angles.trBase[1];
    643 		test[2] = le->angles.trBase[2];
    644 		AnglesToAxis( test, axis );
    645 
    646 		c = (float)(t - KAMI_SHOCKWAVE2_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2_STARTTIME);
    647 		VectorScale( axis[0], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[0] );
    648 		VectorScale( axis[1], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[1] );
    649 		VectorScale( axis[2], c * KAMI_SHOCKWAVE2_MAXRADIUS / KAMI_SHOCKWAVEMODEL_RADIUS, shockwave.axis[2] );
    650 		shockwave.nonNormalizedAxes = qtrue;
    651 
    652 		if (t > KAMI_SHOCKWAVE2FADE_STARTTIME) {
    653 			c = (float)(t - KAMI_SHOCKWAVE2FADE_STARTTIME) / (float)(KAMI_SHOCKWAVE2_ENDTIME - KAMI_SHOCKWAVE2FADE_STARTTIME);
    654 		}
    655 		else {
    656 			c = 0;
    657 		}
    658 		c *= 0xff;
    659 		shockwave.shaderRGBA[0] = 0xff - c;
    660 		shockwave.shaderRGBA[1] = 0xff - c;
    661 		shockwave.shaderRGBA[2] = 0xff - c;
    662 		shockwave.shaderRGBA[3] = 0xff - c;
    663 
    664 		trap_R_AddRefEntityToScene( &shockwave );
    665 	}
    666 }
    667 
    668 /*
    669 ===================
    670 CG_AddInvulnerabilityImpact
    671 ===================
    672 */
    673 void CG_AddInvulnerabilityImpact( localEntity_t *le ) {
    674 	trap_R_AddRefEntityToScene( &le->refEntity );
    675 }
    676 
    677 /*
    678 ===================
    679 CG_AddInvulnerabilityJuiced
    680 ===================
    681 */
    682 void CG_AddInvulnerabilityJuiced( localEntity_t *le ) {
    683 	int t;
    684 
    685 	t = cg.time - le->startTime;
    686 	if ( t > 3000 ) {
    687 		le->refEntity.axis[0][0] = (float) 1.0 + 0.3 * (t - 3000) / 2000;
    688 		le->refEntity.axis[1][1] = (float) 1.0 + 0.3 * (t - 3000) / 2000;
    689 		le->refEntity.axis[2][2] = (float) 0.7 + 0.3 * (2000 - (t - 3000)) / 2000;
    690 	}
    691 	if ( t > 5000 ) {
    692 		le->endTime = 0;
    693 		CG_GibPlayer( le->refEntity.origin );
    694 	}
    695 	else {
    696 		trap_R_AddRefEntityToScene( &le->refEntity );
    697 	}
    698 }
    699 
    700 /*
    701 ===================
    702 CG_AddRefEntity
    703 ===================
    704 */
    705 void CG_AddRefEntity( localEntity_t *le ) {
    706 	if (le->endTime < cg.time) {
    707 		CG_FreeLocalEntity( le );
    708 		return;
    709 	}
    710 	trap_R_AddRefEntityToScene( &le->refEntity );
    711 }
    712 
    713 #endif
    714 /*
    715 ===================
    716 CG_AddScorePlum
    717 ===================
    718 */
    719 #define NUMBER_SIZE		8
    720 
    721 void CG_AddScorePlum( localEntity_t *le ) {
    722 	refEntity_t	*re;
    723 	vec3_t		origin, delta, dir, vec, up = {0, 0, 1};
    724 	float		c, len;
    725 	int			i, score, digits[10], numdigits, negative;
    726 
    727 	re = &le->refEntity;
    728 
    729 	c = ( le->endTime - cg.time ) * le->lifeRate;
    730 
    731 	score = le->radius;
    732 	if (score < 0) {
    733 		re->shaderRGBA[0] = 0xff;
    734 		re->shaderRGBA[1] = 0x11;
    735 		re->shaderRGBA[2] = 0x11;
    736 	}
    737 	else {
    738 		re->shaderRGBA[0] = 0xff;
    739 		re->shaderRGBA[1] = 0xff;
    740 		re->shaderRGBA[2] = 0xff;
    741 		if (score >= 50) {
    742 			re->shaderRGBA[1] = 0;
    743 		} else if (score >= 20) {
    744 			re->shaderRGBA[0] = re->shaderRGBA[1] = 0;
    745 		} else if (score >= 10) {
    746 			re->shaderRGBA[2] = 0;
    747 		} else if (score >= 2) {
    748 			re->shaderRGBA[0] = re->shaderRGBA[2] = 0;
    749 		}
    750 
    751 	}
    752 	if (c < 0.25)
    753 		re->shaderRGBA[3] = 0xff * 4 * c;
    754 	else
    755 		re->shaderRGBA[3] = 0xff;
    756 
    757 	re->radius = NUMBER_SIZE / 2;
    758 
    759 	VectorCopy(le->pos.trBase, origin);
    760 	origin[2] += 110 - c * 100;
    761 
    762 	VectorSubtract(cg.refdef.vieworg, origin, dir);
    763 	CrossProduct(dir, up, vec);
    764 	VectorNormalize(vec);
    765 
    766 	VectorMA(origin, -10 + 20 * sin(c * 2 * M_PI), vec, origin);
    767 
    768 	// if the view would be "inside" the sprite, kill the sprite
    769 	// so it doesn't add too much overdraw
    770 	VectorSubtract( origin, cg.refdef.vieworg, delta );
    771 	len = VectorLength( delta );
    772 	if ( len < 20 ) {
    773 		CG_FreeLocalEntity( le );
    774 		return;
    775 	}
    776 
    777 	negative = qfalse;
    778 	if (score < 0) {
    779 		negative = qtrue;
    780 		score = -score;
    781 	}
    782 
    783 	for (numdigits = 0; !(numdigits && !score); numdigits++) {
    784 		digits[numdigits] = score % 10;
    785 		score = score / 10;
    786 	}
    787 
    788 	if (negative) {
    789 		digits[numdigits] = 10;
    790 		numdigits++;
    791 	}
    792 
    793 	for (i = 0; i < numdigits; i++) {
    794 		VectorMA(origin, (float) (((float) numdigits / 2) - i) * NUMBER_SIZE, vec, re->origin);
    795 		re->customShader = cgs.media.numberShaders[digits[numdigits-1-i]];
    796 		trap_R_AddRefEntityToScene( re );
    797 	}
    798 }
    799 
    800 
    801 
    802 
    803 //==============================================================================
    804 
    805 /*
    806 ===================
    807 CG_AddLocalEntities
    808 
    809 ===================
    810 */
    811 void CG_AddLocalEntities( void ) {
    812 	localEntity_t	*le, *next;
    813 
    814 	// walk the list backwards, so any new local entities generated
    815 	// (trails, marks, etc) will be present this frame
    816 	le = cg_activeLocalEntities.prev;
    817 	for ( ; le != &cg_activeLocalEntities ; le = next ) {
    818 		// grab next now, so if the local entity is freed we
    819 		// still have it
    820 		next = le->prev;
    821 
    822 		if ( cg.time >= le->endTime ) {
    823 			CG_FreeLocalEntity( le );
    824 			continue;
    825 		}
    826 		switch ( le->leType ) {
    827 		default:
    828 			CG_Error( "Bad leType: %i", le->leType );
    829 			break;
    830 
    831 		case LE_MARK:
    832 			break;
    833 
    834 		case LE_SPRITE_EXPLOSION:
    835 			CG_AddSpriteExplosion( le );
    836 			break;
    837 
    838 		case LE_EXPLOSION:
    839 			CG_AddExplosion( le );
    840 			break;
    841 
    842 		case LE_FRAGMENT:			// gibs and brass
    843 			CG_AddFragment( le );
    844 			break;
    845 
    846 		case LE_MOVE_SCALE_FADE:		// water bubbles
    847 			CG_AddMoveScaleFade( le );
    848 			break;
    849 
    850 		case LE_FADE_RGB:				// teleporters, railtrails
    851 			CG_AddFadeRGB( le );
    852 			break;
    853 
    854 		case LE_FALL_SCALE_FADE: // gib blood trails
    855 			CG_AddFallScaleFade( le );
    856 			break;
    857 
    858 		case LE_SCALE_FADE:		// rocket trails
    859 			CG_AddScaleFade( le );
    860 			break;
    861 
    862 		case LE_SCOREPLUM:
    863 			CG_AddScorePlum( le );
    864 			break;
    865 
    866 #ifdef MISSIONPACK
    867 		case LE_KAMIKAZE:
    868 			CG_AddKamikaze( le );
    869 			break;
    870 		case LE_INVULIMPACT:
    871 			CG_AddInvulnerabilityImpact( le );
    872 			break;
    873 		case LE_INVULJUICED:
    874 			CG_AddInvulnerabilityJuiced( le );
    875 			break;
    876 		case LE_SHOWREFENTITY:
    877 			CG_AddRefEntity( le );
    878 			break;
    879 #endif
    880 		}
    881 	}
    882 }
    883 
    884 
    885 
    886