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 Quake III Arena source code; 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 */
CG_InitLocalEntities(void)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 */
CG_FreeLocalEntity(localEntity_t * le)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 */
CG_AllocLocalEntity(void)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 */
CG_BloodTrail(localEntity_t * le)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 */
CG_FragmentBounceMark(localEntity_t * le,trace_t * trace)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 */
CG_FragmentBounceSound(localEntity_t * le,trace_t * trace)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 */
CG_ReflectVelocity(localEntity_t * le,trace_t * trace)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 */
CG_AddFragment(localEntity_t * le)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 */
CG_AddFadeRGB(localEntity_t * le)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 */
CG_AddMoveScaleFade(localEntity_t * le)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 */
CG_AddScaleFade(localEntity_t * le)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 */
CG_AddFallScaleFade(localEntity_t * le)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 */
CG_AddExplosion(localEntity_t * ex)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 */
CG_AddSpriteExplosion(localEntity_t * le)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 */
CG_AddKamikaze(localEntity_t * le)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 */
CG_AddInvulnerabilityImpact(localEntity_t * le)673 void CG_AddInvulnerabilityImpact( localEntity_t *le ) {
674 	trap_R_AddRefEntityToScene( &le->refEntity );
675 }
676 
677 /*
678 ===================
679 CG_AddInvulnerabilityJuiced
680 ===================
681 */
CG_AddInvulnerabilityJuiced(localEntity_t * le)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 */
CG_AddRefEntity(localEntity_t * le)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 
CG_AddScorePlum(localEntity_t * le)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 */
CG_AddLocalEntities(void)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 
887