1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”).
8 
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW SP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 
30 // cg_localents.c -- every frame, generate renderer commands for locally
31 // processed entities, like smoke puffs, gibs, shells, etc.
32 
33 #include "cg_local.h"
34 
35 // Ridah, increased this
36 //#define	MAX_LOCAL_ENTITIES	512
37 #define MAX_LOCAL_ENTITIES  768     // renderer can only handle 1024 entities max, so we should avoid
38 									// overwriting game entities
39 // done.
40 
41 localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES];
42 localEntity_t cg_activeLocalEntities;       // double linked list
43 localEntity_t   *cg_freeLocalEntities;      // single linked list
44 
45 // Ridah, debugging
46 int localEntCount = 0;
47 
48 /*
49 ===================
50 CG_InitLocalEntities
51 
52 This is called at startup and for tournement restarts
53 ===================
54 */
CG_InitLocalEntities(void)55 void    CG_InitLocalEntities( void ) {
56 	int i;
57 
58 	memset( cg_localEntities, 0, sizeof( cg_localEntities ) );
59 	cg_activeLocalEntities.next = &cg_activeLocalEntities;
60 	cg_activeLocalEntities.prev = &cg_activeLocalEntities;
61 	cg_freeLocalEntities = cg_localEntities;
62 	for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) {
63 		cg_localEntities[i].next = &cg_localEntities[i + 1];
64 	}
65 
66 	// Ridah, debugging
67 	localEntCount = 0;
68 }
69 
70 
71 /*
72 ==================
73 CG_FreeLocalEntity
74 ==================
75 */
CG_FreeLocalEntity(localEntity_t * le)76 void CG_FreeLocalEntity( localEntity_t *le ) {
77 	if ( !le->prev ) {
78 		CG_Error( "CG_FreeLocalEntity: not active" );
79 	}
80 
81 	// Ridah, debugging
82 	localEntCount--;
83 //	trap_Print( va("FreeLocalEntity: locelEntCount = %d\n", localEntCount) );
84 	// done.
85 
86 	// remove from the doubly linked active list
87 	le->prev->next = le->next;
88 	le->next->prev = le->prev;
89 
90 	// the free list is only singly linked
91 	le->next = cg_freeLocalEntities;
92 	cg_freeLocalEntities = le;
93 }
94 
95 /*
96 ===================
97 CG_AllocLocalEntity
98 
99 Will always succeed, even if it requires freeing an old active entity
100 ===================
101 */
CG_AllocLocalEntity(void)102 localEntity_t   *CG_AllocLocalEntity( void ) {
103 	localEntity_t   *le;
104 
105 	if ( !cg_freeLocalEntities ) {
106 		// no free entities, so free the one at the end of the chain
107 		// remove the oldest active entity
108 		CG_FreeLocalEntity( cg_activeLocalEntities.prev );
109 	}
110 
111 	// Ridah, debugging
112 	localEntCount++;
113 //	trap_Print( va("AllocLocalEntity: locelEntCount = %d\n", localEntCount) );
114 	// done.
115 
116 	le = cg_freeLocalEntities;
117 	cg_freeLocalEntities = cg_freeLocalEntities->next;
118 
119 	memset( le, 0, sizeof( *le ) );
120 
121 	// link into the active list
122 	le->next = cg_activeLocalEntities.next;
123 	le->prev = &cg_activeLocalEntities;
124 	cg_activeLocalEntities.next->prev = le;
125 	cg_activeLocalEntities.next = le;
126 	return le;
127 }
128 
129 
130 /*
131 ====================================================================================
132 
133 FRAGMENT PROCESSING
134 
135 A fragment localentity interacts with the environment in some way (hitting walls),
136 or generates more localentities along a trail.
137 
138 ====================================================================================
139 */
140 
141 /*
142 ================
143 CG_BloodTrail
144 
145 Leave expanding blood puffs behind gibs
146 ================
147 */
148 // use this to change between particle and trail code
149 //#define BLOOD_PARTICLE_TRAIL
CG_BloodTrail(localEntity_t * le)150 void CG_BloodTrail( localEntity_t *le ) {
151 	int t;
152 	int t2;
153 	int step;
154 	vec3_t newOrigin;
155 
156 #ifndef BLOOD_PARTICLE_TRAIL
157 	static vec3_t col = {1,1,1};
158 #endif
159 
160 	centity_t   *cent;
161 	cent = &cg_entities[le->ownerNum];
162 
163 	if ( !cg_blood.integer ) {
164 		return;
165 	}
166 
167 	// step = 150;
168 #ifdef BLOOD_PARTICLE_TRAIL
169 	step = 10;
170 #else
171 	// time it takes to move 3 units
172 	step = ( 1000 * 3 ) / VectorLength( le->pos.trDelta );
173 #endif
174 
175 	if ( cent && cent->currentState.aiChar == AICHAR_ZOMBIE ) {
176 		step = 30;
177 	}
178 
179 	t = step * ( ( cg.time - cg.frametime + step ) / step );
180 	t2 = step * ( cg.time / step );
181 
182 	for ( ; t <= t2; t += step ) {
183 		BG_EvaluateTrajectory( &le->pos, t, newOrigin );
184 
185 #ifdef BLOOD_PARTICLE_TRAIL
186 		CG_Particle_Bleed( cgs.media.smokePuffShader, newOrigin, vec3_origin, 0, 500 + rand() % 200 );
187 #else
188 
189 
190 		if ( cent && cent->currentState.aiChar == AICHAR_ZOMBIE ) {
191 			CG_Particle_Bleed( cgs.media.smokePuffShader, newOrigin, vec3_origin, 1, 500 + rand() % 200 );
192 		} else {
193 			// Ridah, blood trail using trail code (should be faster since we don't have to spawn as many)
194 			le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex,
195 												 cgs.media.bloodTrailShader,
196 												 t,
197 												 STYPE_STRETCH,
198 												 newOrigin,
199 												 180,
200 												 1.0, // start alpha
201 												 0.0, // end alpha
202 												 12.0,
203 												 12.0,
204 												 TJFL_NOCULL,
205 												 col, col,
206 												 0, 0 );
207 		}
208 #endif
209 
210 	}
211 }
212 
213 
214 /*
215 ================
216 CG_FragmentBounceMark
217 ================
218 */
CG_FragmentBounceMark(localEntity_t * le,trace_t * trace)219 void CG_FragmentBounceMark( localEntity_t *le, trace_t *trace ) {
220 	int radius;
221 
222 	if ( le->leMarkType == LEMT_BLOOD ) {
223 		static int lastBloodMark;
224 
225 		// don't drop too many blood marks
226 		if ( !( lastBloodMark > cg.time || lastBloodMark > cg.time - 100 ) ) {
227 			radius = 16 + ( rand() & 31 );
228 			CG_ImpactMark( cgs.media.bloodDotShaders[rand() % 5], trace->endpos, trace->plane.normal, random() * 360,
229 						   1,1,1,1, qtrue, radius, qfalse, cg_bloodTime.integer * 1000 );
230 
231 			lastBloodMark = cg.time;
232 		}
233 	}
234 
235 	// don't allow a fragment to make multiple marks, or they
236 	// pile up while settling
237 	le->leMarkType = LEMT_NONE;
238 }
239 
240 /*
241 ================
242 CG_FragmentBounceSound
243 ================
244 */
CG_FragmentBounceSound(localEntity_t * le,trace_t * trace)245 void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace ) {
246 	if ( le->leBounceSoundType == LEBS_BLOOD ) {
247 		// half the gibs will make splat sounds
248 		if ( rand() & 1 ) {
249 			int r = rand() & 3;
250 			sfxHandle_t s;
251 
252 			if ( r < 2 ) {
253 				s = cgs.media.gibBounce1Sound;
254 			} else if ( r == 2 ) {
255 				s = cgs.media.gibBounce2Sound;
256 			} else {
257 				s = cgs.media.gibBounce3Sound;
258 			}
259 			trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s );
260 		}
261 	} else if ( le->leBounceSoundType == LEBS_BRASS ) {
262 
263 //----(SA) added
264 	} else if ( le->leBounceSoundType == LEBS_ROCK ) {
265 		// half the hits will make thunk sounds	(this is just to start since we don't even have the sound yet... (SA))
266 		if ( rand() & 1 ) {
267 			int r = rand() & 3;
268 			sfxHandle_t s;
269 
270 			if ( r < 2 ) {
271 				s = cgs.media.debBounce1Sound;
272 			} else if ( r == 2 ) {
273 				s = cgs.media.debBounce2Sound;
274 			} else {
275 				s = cgs.media.debBounce3Sound;
276 			}
277 			trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s );
278 		}
279 //----(SA) end
280 
281 	} else if ( le->leBounceSoundType == LEBS_BONE ) {
282 
283 		trap_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.boneBounceSound );
284 
285 	}
286 
287 	// don't allow a fragment to make multiple bounce sounds,
288 	// or it gets too noisy as they settle
289 	le->leBounceSoundType = LEBS_NONE;
290 }
291 
292 
293 /*
294 ================
295 CG_ReflectVelocity
296 ================
297 */
CG_ReflectVelocity(localEntity_t * le,trace_t * trace)298 void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) {
299 	vec3_t velocity;
300 	float dot;
301 	int hitTime;
302 
303 	// reflect the velocity on the trace plane
304 	hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
305 	BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
306 	dot = DotProduct( velocity, trace->plane.normal );
307 	VectorMA( velocity, -2 * dot, trace->plane.normal, le->pos.trDelta );
308 
309 	VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );
310 
311 	VectorCopy( trace->endpos, le->pos.trBase );
312 	le->pos.trTime = cg.time;
313 
314 
315 	// check for stop, making sure that even on low FPS systems it doesn't bobble
316 
317 	if ( le->leMarkType == LEMT_BLOOD && trace->startsolid ) {
318 		//centity_t *cent;
319 		//cent = &cg_entities[trace->entityNum];
320 		//if (cent && cent->currentState.apos.trType != TR_STATIONARY)
321 		//	le->pos.trType = TR_STATIONARY;
322 	} else if ( trace->allsolid ||
323 				( trace->plane.normal[2] > 0 &&
324 				  ( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) {
325 
326 //----(SA)	if it's a fragment and it's not resting on the world...
327 //			if(le->leType == LE_DEBRIS && trace->entityNum < (MAX_ENTITIES - 1))
328 		if ( le->leType == LE_FRAGMENT && trace->entityNum < ( MAX_REFENTITIES - 1 ) ) {
329 			le->pos.trType = TR_GRAVITY_PAUSED;
330 		} else
331 		{
332 			le->pos.trType = TR_STATIONARY;
333 		}
334 	} else {
335 
336 	}
337 }
338 
339 
340 //----(SA)	added
341 
342 /*
343 ==============
344 CG_AddEmitter
345 ==============
346 */
CG_AddEmitter(localEntity_t * le)347 void CG_AddEmitter( localEntity_t *le ) {
348 	vec3_t dir;
349 	int nextTime = 50;
350 
351 	if ( le->breakCount > cg.time ) {  // using 'breakCount' for 'wait'
352 		return;
353 	}
354 
355 	if ( cg_paused.integer ) { // don't add while paused
356 		return;
357 	}
358 
359 	// TODO: look up particle script and use proper effect rather than this check
360 	//if(water){}
361 	//else if(oil) {}
362 	//else if(steam) {}
363 	//else if(wine) {}
364 
365 	switch ( le->headJuncIndex ) {
366 	case 1:     // oil
367 		VectorScale( le->angles.trBase, le->radius, dir );
368 		CG_Particle_OilParticle( cgs.media.oilParticle, le->pos.trBase, dir,  10000, le->ownerNum );
369 		break;
370 	case 2:     // water
371 		VectorScale( le->angles.trBase, le->radius, dir );
372 		CG_Particle_OilParticle( cgs.media.oilParticle, le->pos.trBase, dir,  10000, le->ownerNum );
373 		break;
374 	case 3:     // steam
375 		nextTime = 100;
376 		CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, le->pos.trBase, le->angles.trBase, 8, 1000, 8, le->radius, 20, 0.25f );
377 		break;
378 	case 4:     // wine
379 		VectorScale( le->angles.trBase, le->radius, dir );
380 		CG_Particle_OilParticle( cgs.media.oilParticle, le->pos.trBase, dir,  10000, le->ownerNum );
381 		break;
382 	case 5:     // smoke
383 		nextTime = 100;
384 		CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, le->pos.trBase, dir, 8, 1000, 8, 20, 20, 0.25f );
385 		break;
386 	case 6:     // electrical
387 		nextTime = 100;
388 		CG_AddBulletParticles( le->pos.trBase, dir, 2, 800, 4, 16.0f );
389 		break;
390 
391 	case 0:
392 	default:
393 		nextTime = 100;
394 		CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, le->pos.trBase, dir, 8, 1000, 8, 20, 20, 0.25f );
395 		break;
396 	}
397 
398 	le->breakCount = cg.time + nextTime;
399 }
400 
401 //----(SA)	end
402 
403 
404 /*
405 ================
406 CG_AddFragment
407 ================
408 */
CG_AddFragment(localEntity_t * le)409 void CG_AddFragment( localEntity_t *le ) {
410 	vec3_t newOrigin;
411 	trace_t trace;
412 	refEntity_t     *re;
413 	float flameAlpha = 0.0f;   // TTimo: init
414 	vec3_t flameDir;
415 	qboolean hasFlame = qfalse;
416 	int i;
417 	int contents;
418 
419 	// Ridah
420 	re = &le->refEntity;
421 	if ( !re->fadeStartTime || re->fadeEndTime < le->endTime ) {
422 		if ( le->endTime - cg.time > 5000 ) {
423 			re->fadeStartTime = le->endTime - 5000;
424 		} else {
425 			re->fadeStartTime = le->endTime - 1000;
426 		}
427 		re->fadeEndTime = le->endTime;
428 	}
429 
430 	// Ridah, flaming gibs
431 	if ( le->onFireStart && ( le->onFireStart < cg.time && le->onFireEnd > cg.time ) ) {
432 		hasFlame = qtrue;
433 		// calc the alpha
434 		flameAlpha = 1.0 - ( (float)( cg.time - le->onFireStart ) / (float)( le->onFireEnd - le->onFireStart ) );
435 		if ( flameAlpha < 0.0 ) {
436 			flameAlpha = 0.0;
437 		}
438 		if ( flameAlpha > 1.0 ) {
439 			flameAlpha = 1.0;
440 		}
441 		CG_S_AddLoopingSound( -1, le->refEntity.origin, vec3_origin, cgs.media.flameCrackSound, (int)( 5.0 * flameAlpha ) );
442 	}
443 
444 //----(SA)	added
445 	if ( le->leFlags & LEF_SMOKING ) {
446 		float alpha;
447 		refEntity_t flash;
448 
449 		// create a little less smoke
450 
451 		//	TODO: FIXME: this is not quite right, because it'll become fps dependant - in a bad way.
452 		//		the slower the fps, the /more/ smoke there'll be, probably driving the fps lower.
453 		if ( !cg_paused.integer ) {    // don't add while paused
454 			if ( !( rand() % 5 ) ) {
455 				alpha = 1.0 - ( (float)( cg.time - le->startTime ) / (float)( le->endTime - le->startTime ) );
456 				alpha *= 0.25f;
457 				memset( &flash, 0, sizeof( flash ) );
458 				CG_PositionEntityOnTag( &flash, &le->refEntity, "tag_flash", 0, NULL );
459 				CG_ParticleImpactSmokePuffExtended( cgs.media.smokeParticleShader, flash.origin, tv( 0,0,1 ), 8, 1000, 8, 20, 20, alpha );
460 			}
461 		}
462 	}
463 //----(SA)	end
464 
465 	if ( le->pos.trType == TR_STATIONARY ) {
466 		// Ridah, add the flame
467 		if ( hasFlame ) {
468 			refEntity_t backupEnt;
469 
470 			backupEnt = le->refEntity;
471 
472 			VectorClear( flameDir );
473 			flameDir[2] = 1;
474 
475 			le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha );
476 			VectorCopy( flameDir, le->refEntity.fireRiseDir );
477 			le->refEntity.customShader = cgs.media.onFireShader;
478 			trap_R_AddRefEntityToScene( &le->refEntity );
479 			le->refEntity.customShader = cgs.media.onFireShader2;
480 			trap_R_AddRefEntityToScene( &le->refEntity );
481 
482 			le->refEntity = backupEnt;
483 		}
484 
485 		trap_R_AddRefEntityToScene( &le->refEntity );
486 
487 		return;
488 
489 	} else if ( le->pos.trType == TR_GRAVITY_PAUSED ) {
490 		// Ridah, add the flame
491 		if ( hasFlame ) {
492 			refEntity_t backupEnt;
493 
494 			backupEnt = le->refEntity;
495 
496 			VectorClear( flameDir );
497 			flameDir[2] = 1;
498 
499 			le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha );
500 			VectorCopy( flameDir, le->refEntity.fireRiseDir );
501 			le->refEntity.customShader = cgs.media.onFireShader;
502 			trap_R_AddRefEntityToScene( &le->refEntity );
503 			le->refEntity.customShader = cgs.media.onFireShader2;
504 			trap_R_AddRefEntityToScene( &le->refEntity );
505 
506 			le->refEntity = backupEnt;
507 		}
508 
509 		trap_R_AddRefEntityToScene( &le->refEntity );
510 
511 
512 		// trace a line from previous position down, to see if I should start falling again
513 
514 		VectorCopy( le->refEntity.origin, newOrigin );
515 		newOrigin [2] -= 5;
516 		CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MISSILECLIP );
517 
518 		if ( trace.fraction == 1.0 ) { // it's clear, start moving again
519 			VectorClear( le->pos.trDelta );
520 			VectorClear( le->angles.trDelta );
521 			le->pos.trType = TR_GRAVITY;    // nothing below me, start falling again
522 		} else {
523 			return;
524 		}
525 	}
526 
527 	// calculate new position
528 	BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin );
529 
530 	if ( hasFlame ) {
531 		// calc the flame dir
532 		VectorSubtract( le->refEntity.origin, newOrigin, flameDir );
533 		if ( VectorLength( flameDir ) == 0 ) {
534 			flameDir[2] = 1;
535 			// play a burning sound when not moving
536 			CG_S_AddLoopingSound( 0, newOrigin, vec3_origin, cgs.media.flameSound, (int)( 0.3 * 255.0 * flameAlpha ) );
537 		} else {
538 			VectorNormalize( flameDir );
539 			// play a flame blow sound when moving
540 			CG_S_AddLoopingSound( 0, newOrigin, vec3_origin, cgs.media.flameBlowSound, (int)( 0.3 * 255.0 * flameAlpha ) );
541 		}
542 	}
543 
544 	contents = CONTENTS_SOLID;
545 
546 	// trace a line from previous position to new position
547 	if ( ( le->leFlags & LEF_NOTOUCHPARENT ) && le->ownerNum ) {
548 		CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, le->ownerNum, contents );
549 	} else {
550 		CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, contents );
551 	}
552 
553 	// did we hit someone?
554 	if ( le->leFlags & LEF_PLAYER_DAMAGE ) {
555 		// only do damage if travelling at a fast enough velocity
556 		if ( newOrigin[2] < le->refEntity.origin[2] ) {
557 			vec3_t pmin, pmax, dmin = {-12,-12,-12}, dmax = {12,12,12};
558 			// debris bounds
559 			VectorAdd( dmin, newOrigin, dmin );
560 			VectorAdd( dmax, newOrigin, dmax );
561 			dmax[2] += le->refEntity.origin[2] - newOrigin[2];  // add falling distance to box
562 			// are we touching the player?
563 			VectorAdd( cg.snap->ps.mins, cg.snap->ps.origin, pmin );
564 			VectorAdd( cg.snap->ps.maxs, cg.snap->ps.origin, pmax );
565 			pmin[2] = pmax[2] - 32; // only hit on the head
566 			for ( i = 0; i < 3; i++ ) {
567 				if ( ( dmax[i] < pmin[i] ) || ( dmin[i] > pmax[i] ) ) {
568 					break;
569 				}
570 			}
571 			if ( i == 3 ) {
572 				trap_S_StartSound( cg.snap->ps.origin, cg.snap->ps.clientNum, CHAN_VOICE, cgs.media.debrisHitSound );
573 				CG_ClientDamage( cg.snap->ps.clientNum, ENTITYNUM_WORLD, CLDMG_DEBRIS );
574 				// disable damage now for this debris
575 				le->leFlags &= ~LEF_PLAYER_DAMAGE;
576 			}
577 		}
578 	}
579 
580 	if ( trace.fraction == 1.0 ) {
581 		// still in free fall
582 		VectorCopy( newOrigin, le->refEntity.origin );
583 
584 		if ( le->leFlags & LEF_TUMBLE || le->angles.trType == TR_LINEAR ) {
585 			vec3_t angles;
586 
587 			BG_EvaluateTrajectory( &le->angles, cg.time, angles );
588 			AnglesToAxis( angles, le->refEntity.axis );
589 			if ( le->sizeScale && le->sizeScale != 1.0 ) {
590 				for ( i = 0; i < 3; i++ ) VectorScale( le->refEntity.axis[i], le->sizeScale, le->refEntity.axis[i] );
591 			}
592 		}
593 
594 		// Ridah, add the flame
595 		if ( hasFlame ) {
596 			refEntity_t backupEnt;
597 
598 			backupEnt = le->refEntity;
599 
600 			le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha );
601 			VectorCopy( flameDir, le->refEntity.fireRiseDir );
602 			le->refEntity.customShader = cgs.media.onFireShader;
603 			trap_R_AddRefEntityToScene( &le->refEntity );
604 			le->refEntity.customShader = cgs.media.onFireShader2;
605 			trap_R_AddRefEntityToScene( &le->refEntity );
606 
607 			le->refEntity = backupEnt;
608 		}
609 
610 		trap_R_AddRefEntityToScene( &le->refEntity );
611 
612 		// add a blood trail
613 		if ( le->leBounceSoundType == LEBS_BLOOD ) {
614 			CG_BloodTrail( le );
615 		}
616 
617 		return;
618 	}
619 
620 	// if it is in a nodrop zone, remove it
621 	// this keeps gibs from waiting at the bottom of pits of death
622 	// and floating levels
623 	if ( CG_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) {
624 		CG_FreeLocalEntity( le );
625 		return;
626 	}
627 
628 	// do a bouncy sound
629 	CG_FragmentBounceSound( le, &trace );
630 
631 	// reflect the velocity on the trace plane
632 	CG_ReflectVelocity( le, &trace );
633 
634 	// break on contact?
635 	if ( le->breakCount ) {
636 		clientInfo_t    *ci;
637 		int clientNum;
638 		localEntity_t   *nle;
639 		vec3_t dir;
640 
641 		clientNum = le->ownerNum;
642 		if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) {
643 			CG_Error( "Bad clientNum on player entity" );
644 		}
645 		ci = &cgs.clientinfo[ clientNum ];
646 
647 		// spawn some new fragments
648 		for ( i = 0; i <= le->breakCount; i++ ) {
649 			nle = CG_AllocLocalEntity();
650 			memcpy( &( nle->leType ), &( le->leType ), sizeof( localEntity_t ) - 2 * sizeof( localEntity_t * ) );
651 			if ( nle->breakCount-- < 2 ) {
652 				nle->refEntity.hModel = ci->gibModels[rand() % 2];
653 			} else {
654 				nle->refEntity.hModel = ci->gibModels[rand() % 4];
655 			}
656 			// make it smaller
657 			nle->endTime = le->endTime + ( cg.time - le->startTime );
658 			nle->sizeScale *= 0.8;
659 			if ( nle->sizeScale < 0.7 ) {
660 				nle->sizeScale = 0.7;
661 				nle->leBounceSoundType = 0;
662 			}
663 			// move us a bit
664 			VectorNormalize2( nle->pos.trDelta, dir );
665 			VectorMA( trace.endpos, 4.0 * le->sizeScale * i, dir, nle->pos.trBase );
666 			// randomize vel a bit
667 			VectorMA( nle->pos.trDelta, VectorLength( nle->pos.trDelta ) * 0.3, bytedirs[rand() % NUMVERTEXNORMALS], nle->pos.trDelta );
668 		}
669 		// we're done
670 		CG_FreeLocalEntity( le );
671 		return;
672 	}
673 
674 	if ( le->pos.trType == TR_STATIONARY && le->leMarkType == LEMT_BLOOD ) {
675 		// RF, disabled for performance reasons in boss1
676 		//if (le->leBounceSoundType)
677 		//	CG_BloodPool (le, cgs.media.bloodPool, &trace);
678 
679 		// leave a mark
680 		if ( le->leMarkType ) {
681 			CG_FragmentBounceMark( le, &trace );
682 		}
683 	}
684 
685 	// Ridah, add the flame
686 	if ( hasFlame ) {
687 		refEntity_t backupEnt;
688 
689 		backupEnt = le->refEntity;
690 
691 		le->refEntity.shaderRGBA[3] = ( unsigned char )( 255.0 * flameAlpha );
692 		VectorCopy( flameDir, le->refEntity.fireRiseDir );
693 		le->refEntity.customShader = cgs.media.onFireShader;
694 		trap_R_AddRefEntityToScene( &le->refEntity );
695 		le->refEntity.customShader = cgs.media.onFireShader2;
696 		trap_R_AddRefEntityToScene( &le->refEntity );
697 
698 		le->refEntity = backupEnt;
699 	}
700 
701 	trap_R_AddRefEntityToScene( &le->refEntity );
702 }
703 
704 // Ridah
705 /*
706 ================
707 CG_AddMovingTracer
708 ================
709 */
CG_AddMovingTracer(localEntity_t * le)710 void CG_AddMovingTracer( localEntity_t *le ) {
711 	vec3_t start, end, dir;
712 
713 	BG_EvaluateTrajectory( &le->pos, cg.time, start );
714 	VectorNormalize2( le->pos.trDelta, dir );
715 	VectorMA( start, cg_tracerLength.value, dir, end );
716 
717 	CG_DrawTracer( start, end );
718 }
719 
720 /*
721 ================
722 CG_AddSparkElements
723 ================
724 */
CG_AddSparkElements(localEntity_t * le)725 void CG_AddSparkElements( localEntity_t *le ) {
726 	vec3_t newOrigin;
727 	trace_t trace;
728 	float time;
729 	float lifeFrac;
730 
731 	time = (float)( cg.time - cg.frametime );
732 
733 	while ( 1 ) {
734 		// calculate new position
735 		BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin );
736 
737 //		if ((le->endTime - le->startTime) > 500) {
738 
739 		// trace a line from previous position to new position
740 		CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT );
741 
742 		// if stuck, kill it
743 		if ( trace.startsolid ) {
744 			// HACK, some walls screw up, so just pass through if starting in a solid
745 			VectorCopy( newOrigin, trace.endpos );
746 			trace.fraction = 1.0;
747 		}
748 
749 		// moved some distance
750 		VectorCopy( trace.endpos, le->refEntity.origin );
751 /*
752 		} else
753 		{	// just move it there
754 
755 			VectorCopy( newOrigin, le->refEntity.origin );
756 			trace.fraction = 1.0;
757 
758 		}
759 */
760 		time += cg.frametime * trace.fraction;
761 
762 		lifeFrac = (float)( cg.time - le->startTime ) / (float)( le->endTime - le->startTime );
763 
764 		// add a trail
765 		le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex,
766 											 le->refEntity.customShader,
767 											 le->refEntity.origin,
768 											 200,
769 											 1.0 - lifeFrac, // start alpha
770 											 0.0, //1.0 - lifeFrac,	// end alpha
771 											 lifeFrac * 2.0 * ( ( ( le->endTime - le->startTime ) > 400 ) + 1 ) * 1.5,
772 											 lifeFrac * 2.0 * ( ( ( le->endTime - le->startTime ) > 400 ) + 1 ) * 1.5 );
773 
774 		// if it is in a nodrop zone, remove it
775 		// this keeps gibs from waiting at the bottom of pits of death
776 		// and floating levels
777 // for some reason SFM1.BSP is one big NODROP zone
778 //		if ( trap_CM_PointContents( le->refEntity.origin, 0 ) & CONTENTS_NODROP ) {
779 //			CG_FreeLocalEntity( le );
780 //			return;
781 //		}
782 
783 		if ( trace.fraction < 1.0 ) {
784 			// just kill it
785 			CG_FreeLocalEntity( le );
786 			return;
787 /*
788 			// reflect the velocity on the trace plane
789 			CG_ReflectVelocity( le, &trace );
790 			// the intersection is a fraction of the frametime
791 			le->pos.trTime = (int)time;
792 */
793 		}
794 
795 		if ( trace.fraction == 1.0 || time >= (float)cg.time ) {
796 			return;
797 		}
798 	}
799 
800 }
801 
802 /*
803 ================
804 CG_AddFuseSparkElements
805 ================
806 */
CG_AddFuseSparkElements(localEntity_t * le)807 void CG_AddFuseSparkElements( localEntity_t *le ) {
808 
809 	float FUSE_SPARK_WIDTH      = 1.0;
810 
811 	int step = 10;
812 	float time;
813 	float lifeFrac;
814 	static vec3_t whiteColor = {1,1,1};
815 
816 	time = (float)( le->lastTrailTime );
817 
818 	while ( time < cg.time ) {
819 
820 		// calculate new position
821 		BG_EvaluateTrajectory( &le->pos, time, le->refEntity.origin );
822 
823 		lifeFrac = (float)( time - le->startTime ) / (float)( le->endTime - le->startTime );
824 
825 		//if (lifeFrac > 0.2) {
826 		// add a trail
827 		le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, cgs.media.sparkParticleShader, time, STYPE_STRETCH, le->refEntity.origin, (int)( lifeFrac * (float)( le->endTime - le->startTime ) / 2.0 ),
828 											 1.0 /*(1.0 - lifeFrac)*/, 0.0, FUSE_SPARK_WIDTH * ( 1.0 - lifeFrac ), FUSE_SPARK_WIDTH * ( 1.0 - lifeFrac ), TJFL_SPARKHEADFLARE, whiteColor, whiteColor, 0, 0 );
829 		//}
830 
831 		time += step;
832 
833 		le->lastTrailTime = time;
834 	}
835 
836 }
837 
838 /*
839 ================
840 CG_AddBloodElements
841 ================
842 */
CG_AddBloodElements(localEntity_t * le)843 void CG_AddBloodElements( localEntity_t *le ) {
844 	vec3_t newOrigin;
845 	trace_t trace;
846 	float time;
847 	float lifeFrac;
848 
849 	time = (float)( cg.time - cg.frametime );
850 
851 	while ( 1 ) {
852 		// calculate new position
853 		BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin );
854 
855 		// trace a line from previous position to new position
856 		CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT );
857 
858 		// if stuck, kill it
859 		if ( trace.startsolid ) {
860 			// HACK, some walls screw up, so just pass through if starting in a solid
861 			VectorCopy( newOrigin, trace.endpos );
862 			trace.fraction = 1.0;
863 		}
864 
865 		// moved some distance
866 		VectorCopy( trace.endpos, le->refEntity.origin );
867 		time += cg.frametime * trace.fraction;
868 
869 		lifeFrac = (float)( cg.time - le->startTime ) / (float)( le->endTime - le->startTime );
870 
871 		// add a trail
872 		le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex,
873 											 cgs.media.bloodTrailShader,
874 											 le->refEntity.origin,
875 											 200,
876 											 1.0 - lifeFrac, // start alpha
877 											 1.0 - lifeFrac, // end alpha
878 											 3.0,
879 											 5.0 );
880 
881 		if ( trace.fraction < 1.0 ) {
882 			// reflect the velocity on the trace plane
883 			CG_ReflectVelocity( le, &trace );
884 
885 			// TODO: spawn a blood decal here?
886 
887 			// the intersection is a fraction of the frametime
888 			le->pos.trTime = (int)time;
889 		}
890 
891 		if ( trace.fraction == 1.0 || time >= (float)cg.time ) {
892 			return;
893 		}
894 	}
895 
896 }
897 
898 /*
899 ===================
900 CG_AddSpiritViewflash
901 ===================
902 */
CG_AddSpiritViewflash(localEntity_t * le)903 void CG_AddSpiritViewflash( localEntity_t *le ) {
904 	float alpha;
905 #define SPIRIT_FLASH_FADEIN     50
906 #define SPIRIT_FLASH_DURATION   400
907 #define SPIRIT_FLASH_FADEOUT    2000
908 
909 	if ( cg.viewFade > 1.0 ) {
910 		return;
911 	}
912 
913 	if ( cg.time < le->startTime + SPIRIT_FLASH_FADEIN ) {
914 		alpha = (float)( cg.time - le->startTime ) / (float)SPIRIT_FLASH_FADEIN;
915 	} else if ( cg.time < le->startTime + SPIRIT_FLASH_FADEIN + SPIRIT_FLASH_DURATION )     {
916 		alpha = 1.0;
917 	} else if ( cg.time < le->startTime + SPIRIT_FLASH_FADEIN + SPIRIT_FLASH_DURATION + SPIRIT_FLASH_FADEOUT )     {
918 		alpha = 1.0 - (float)( cg.time - ( le->startTime + SPIRIT_FLASH_FADEIN + SPIRIT_FLASH_DURATION ) ) / (float)SPIRIT_FLASH_FADEOUT;
919 	} else {
920 		return;
921 	}
922 
923 	if ( alpha < 0 ) {
924 		return;
925 	}
926 
927 	// only ever use the highest fade
928 	if ( cg.viewFade < alpha ) {
929 		cg.viewFade = alpha;
930 	}
931 }
932 
933 /*
934 ================
935 CG_AddClientCritter
936 ================
937 */
CG_AddClientCritter(localEntity_t * le)938 void CG_AddClientCritter( localEntity_t *le ) {
939 	vec3_t newOrigin;
940 	trace_t trace;
941 	int time, step = 25, i;
942 	vec3_t v, ang, v2, oDelta;
943 	localEntity_t backup;
944 	float oldSpeed, enemyDist, of;
945 	vec3_t enemyPos;
946 	float alpha = 0.0f, fadeRatio; // TTimo: init
947 
948 	if ( cg_entities[le->ownerNum].currentState.otherEntityNum2 == cg.snap->ps.clientNum ) {
949 		VectorCopy( cg.snap->ps.origin, enemyPos );
950 		enemyPos[2] += cg.snap->ps.viewheight;
951 	} else {
952 		VectorCopy( cg_entities[le->ownerNum].currentState.origin2, enemyPos );
953 	}
954 
955 	VectorCopy( le->pos.trDelta, oDelta );
956 
957 	// vary the enemyPos to create a psuedo-randomness
958 	of = (float)cg.time + le->startTime;
959 	enemyPos[0] += 12 * ( sin( of / 100 ) * cos( of / 78 ) );
960 	enemyPos[1] += 12 * ( sin( of / 70 ) * cos( of / 82 ) );
961 	enemyPos[2] += 12 * ( sin( of / 67 ) * cos( of / 98 ) );
962 
963 	time = le->lastTrailTime + step;
964 
965 	fadeRatio = (float)( cg.time - le->startTime ) / 2000.0;
966 	if ( fadeRatio < 0.0 ) {
967 		fadeRatio = 0.0;
968 	}
969 	if ( fadeRatio > 1.0 ) {
970 		fadeRatio = 1.0;
971 	}
972 
973 	while ( time <= cg.time ) {
974 		if ( time > le->refEntity.fadeStartTime ) {
975 			alpha = (float)( time - le->refEntity.fadeStartTime ) / (float)( le->refEntity.fadeEndTime - le->refEntity.fadeStartTime );
976 			if ( alpha < 0 ) {
977 				alpha = 0;
978 			} else if ( alpha > 1 ) {
979 				alpha = 1;
980 			}
981 		} else {
982 			alpha = fadeRatio;
983 		}
984 
985 		// calculate new position
986 		BG_EvaluateTrajectory( &le->pos, time, newOrigin );
987 
988 		// trace a line from previous position to new position
989 		CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, le->ownerNum, MASK_SHOT );
990 
991 		// if stuck, kill it
992 		if ( trace.startsolid || ( trace.fraction < 1.0 ) ) {
993 			// let heinrich spirits pass through geometry
994 			if ( !( le->leType == LE_HELGA_SPIRIT && ( le->refEntity.hModel == cgs.media.ssSpiritSkullModel ) ) ) {
995 				// kill it
996 				CG_FreeLocalEntity( le );
997 				return;
998 			} else {
999 				VectorCopy( newOrigin, trace.endpos );
1000 			}
1001 		}
1002 
1003 		// moved some distance
1004 		VectorCopy( trace.endpos, le->refEntity.origin );
1005 
1006 		// record this pos
1007 		le->validOldPos[le->oldPosHead] = 1;
1008 		VectorCopy( le->refEntity.origin, le->oldPos[le->oldPosHead] );
1009 		if ( ++le->oldPosHead >= MAX_OLD_POS ) {
1010 			le->oldPosHead = 0;
1011 		}
1012 
1013 		if ( le->leType == LE_ZOMBIE_SPIRIT ) {
1014 			le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex,
1015 												 cgs.media.zombieSpiritTrailShader,
1016 												 time,
1017 												 STYPE_STRETCH,
1018 												 le->refEntity.origin,
1019 												 (int)le->effectWidth,  // trail life
1020 												 0.3 * alpha,
1021 												 0.0,
1022 												 le->radius,
1023 												 0,
1024 												 0, //TJFL_FIXDISTORT,
1025 												 colorWhite,
1026 												 colorWhite,
1027 												 1.0, 1 );
1028 		}
1029 
1030 		if ( le->leType == LE_HELGA_SPIRIT && le->refEntity.hModel != cgs.media.ssSpiritSkullModel ) {
1031 			le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex,
1032 												 cgs.media.helgaSpiritTrailShader,
1033 												 time,
1034 												 STYPE_STRETCH,
1035 												 le->refEntity.origin,
1036 												 (int)le->effectWidth,  // trail life
1037 												 0.3 * alpha,
1038 												 0.0,
1039 												 le->radius,
1040 												 0,
1041 												 0, //TJFL_FIXDISTORT,
1042 												 colorWhite,
1043 												 colorWhite,
1044 												 1.0, 1 );
1045 		}
1046 
1047 		// tracking factor
1048 		if ( le->leType == LE_ZOMBIE_BAT ) {
1049 			le->bounceFactor = 3.0 * (float)step / 1000.0;
1050 		} else {
1051 			le->bounceFactor = 5.0 * (float)step / 1000.0;
1052 		}
1053 		oldSpeed = VectorLength( le->pos.trDelta );
1054 
1055 		// track the enemy
1056 		VectorSubtract( enemyPos, le->refEntity.origin, v );
1057 		enemyDist = VectorNormalize( v );
1058 
1059 		if ( alpha > 0.5 && ( le->lastSpiritDmgTime < time - 100 ) && enemyDist < 24 ) {
1060 			localEntity_t *fb;
1061 
1062 			// if dead, ignore
1063 			if ( !( le->ownerNum != cg.snap->ps.clientNum ? cg_entities[le->ownerNum].currentState.eFlags & EF_DEAD : cg.snap->ps.pm_type == PM_DEAD ) ) {
1064 				// inflict the damage!
1065 				CG_ClientDamage( cg_entities[le->ownerNum].currentState.otherEntityNum2, le->ownerNum, CLDMG_SPIRIT );
1066 				le->lastSpiritDmgTime = time;
1067 
1068 				if ( le->leType == LE_HELGA_SPIRIT ) {
1069 					// spawn a "flashbang" thinker
1070 					fb = CG_AllocLocalEntity();
1071 					fb->leType = LE_SPIRIT_VIEWFLASH;
1072 					fb->startTime = cg.time + 50;
1073 					fb->endTime = fb->startTime + SPIRIT_FLASH_FADEIN + SPIRIT_FLASH_DURATION + SPIRIT_FLASH_FADEOUT;
1074 					// gasp!
1075 					CG_SoundPlayIndexedScript( cgs.media.helgaGaspSound, NULL, cg_entities[le->ownerNum].currentState.otherEntityNum2 );
1076 				}
1077 			}
1078 		}
1079 
1080 		VectorMA( le->pos.trDelta, le->bounceFactor * oldSpeed, v, le->pos.trDelta );
1081 		//VectorCopy( v, le->pos.trDelta );
1082 		if ( VectorLength( le->pos.trDelta ) < 1 ) {
1083 			CG_FreeLocalEntity( le );
1084 			return;
1085 		}
1086 
1087 		le->bounceFactor = 5.0 * (float)step / 1000.0;  // avoidance factor
1088 
1089 		// the intersection is a fraction of the frametime
1090 		le->pos.trTime = time;
1091 		VectorCopy( le->refEntity.origin, le->pos.trBase );
1092 		VectorNormalize( le->pos.trDelta );
1093 		VectorScale( le->pos.trDelta, oldSpeed, le->pos.trDelta );
1094 
1095 		// now trace ahead of time, if we're going to hit something, then avoid it
1096 		// only avoid dangers if we don't have direct sight to the enemy
1097 		trap_CM_BoxTrace( &trace, le->refEntity.origin, enemyPos, NULL, NULL, 0, MASK_SOLID );
1098 		if ( trace.fraction < 1.0 ) {
1099 			BG_EvaluateTrajectory( &le->pos, time + 1000, newOrigin );
1100 
1101 			// if we would go passed the enemy, don't bother
1102 			if ( VectorDistance( le->refEntity.origin, enemyPos ) > VectorDistance( le->refEntity.origin, newOrigin ) ) {
1103 
1104 				trap_CM_BoxTrace( &trace, le->refEntity.origin, newOrigin, NULL, NULL, 0, MASK_SOLID );
1105 
1106 				if ( trace.fraction < 1.0 ) {
1107 					// make sure we are not heading away from the enemy too much
1108 					VectorNormalize2( le->pos.trDelta, v2 );
1109 					if ( DotProduct( v, v2 ) > 0.7 ) {
1110 						// avoid world geometry
1111 						backup = *le;
1112 						le->bounceFactor = ( 1.0 - trace.fraction ) * 10.0 * (float)step / 1000.0;  // tracking and avoidance factor
1113 						// reflect the velocity on the trace plane
1114 						VectorMA( le->pos.trDelta, le->bounceFactor * oldSpeed, trace.plane.normal, le->pos.trDelta );
1115 						if ( VectorLength( le->pos.trDelta ) < 1 ) {
1116 							CG_FreeLocalEntity( le );
1117 							return;
1118 						}
1119 						// the intersection is a fraction of the frametime
1120 						le->pos.trTime = time;
1121 						VectorCopy( le->refEntity.origin, le->pos.trBase );
1122 						VectorNormalize( le->pos.trDelta );
1123 						VectorScale( le->pos.trDelta, oldSpeed, le->pos.trDelta );
1124 						//
1125 						// double check end velocity
1126 						VectorNormalize2( le->pos.trDelta, v2 );
1127 						if ( DotProduct( v, v2 ) <= 0.2 ) {
1128 							// restore
1129 							*le = backup;
1130 						}
1131 					}
1132 				}
1133 			}
1134 		}
1135 
1136 		// set the angles
1137 		VectorNormalize2( oDelta, v );
1138 		// HACK!!! skull model is back-to-front, need to fix
1139 		if ( le->leType == LE_ZOMBIE_SPIRIT ) {
1140 			VectorInverse( v );
1141 		}
1142 		vectoangles( v, ang );
1143 		AnglesToAxis( ang, le->refEntity.axis );
1144 		// lean when turning
1145 		if ( le->leType == LE_ZOMBIE_BAT || le->leType == LE_HELGA_SPIRIT ) {
1146 			VectorSubtract( le->pos.trDelta, oDelta, v2 );
1147 			ang[ROLL] = -0.5 * DotProduct( le->refEntity.axis[1], v2 );
1148 			if ( fabs( ang[ROLL] ) < 20 ) {
1149 				ang[ROLL] = 0;
1150 			} else {
1151 				if ( ang[ROLL] < 0 ) {
1152 					ang[ROLL] += 20;
1153 				} else {
1154 					ang[ROLL] -= 20;
1155 				}
1156 			}
1157 			if ( fabs( ang[ROLL] ) > 80 ) {
1158 				if ( ang[ROLL] > 80 ) {
1159 					ang[ROLL] = 80;
1160 				} else { ang[ROLL] = -80;}
1161 			}
1162 			AnglesToAxis( ang, le->refEntity.axis );
1163 		}
1164 
1165 		// HACK: the skull is slightly higher than the origin
1166 		if ( le->leType == LE_ZOMBIE_SPIRIT ) {
1167 			// set the size scale
1168 			for ( i = 0; i < 3; i++ )
1169 				VectorScale( le->refEntity.axis[i], 0.35, le->refEntity.axis[i] );
1170 			VectorMA( le->refEntity.origin, -10, le->refEntity.axis[2], le->refEntity.origin );
1171 		}
1172 
1173 		le->lastTrailTime = time;
1174 		time += step;
1175 	}
1176 
1177 	// Bats, set the frame
1178 	if ( le->leType == LE_ZOMBIE_BAT ) {
1179 		#define BAT_ANIM_FRAMETIME  30
1180 		le->refEntity.frame = ( cg.time / BAT_ANIM_FRAMETIME + 1 ) % 19;
1181 		le->refEntity.oldframe = ( cg.time / BAT_ANIM_FRAMETIME ) % 19;
1182 		le->refEntity.backlerp = 1.0 - ( (float)( cg.time % BAT_ANIM_FRAMETIME ) / (float)BAT_ANIM_FRAMETIME );
1183 	}
1184 
1185 	// add the sound
1186 	if ( le->loopingSound ) {
1187 		if ( cg.time > le->refEntity.fadeStartTime ) {
1188 			CG_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, 255 - (int)( 255.0 * (float)( cg.time - le->refEntity.fadeStartTime ) / (float)( le->refEntity.fadeEndTime - le->refEntity.fadeStartTime ) ) );
1189 		} else {
1190 			CG_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, 255 - (int)( 255.0 * ( 1.0 - alpha ) ) );
1191 		}
1192 	}
1193 
1194 	trap_R_AddRefEntityToScene( &le->refEntity );
1195 
1196 	// Bats, add the flame
1197 	if ( le->leType == LE_ZOMBIE_BAT ) {
1198 		//
1199 		le->refEntity.shaderRGBA[3] = 255;
1200 		VectorNormalize2( le->pos.trDelta, v );
1201 		VectorInverse( v );
1202 		v[2] += 1;
1203 		VectorNormalize2( v, le->refEntity.fireRiseDir );
1204 
1205 		le->refEntity.customShader = cgs.media.onFireShader2;
1206 		trap_R_AddRefEntityToScene( &le->refEntity );
1207 		le->refEntity.shaderTime = 1434;
1208 		trap_R_AddRefEntityToScene( &le->refEntity );
1209 
1210 		le->refEntity.customShader = 0;
1211 		le->refEntity.shaderTime = 0;
1212 	}
1213 }
1214 
1215 /*
1216 ================
1217 CG_AddDebrisElements
1218 ================
1219 */
CG_AddDebrisElements(localEntity_t * le)1220 void CG_AddDebrisElements( localEntity_t *le ) {
1221 	vec3_t newOrigin;
1222 	trace_t trace;
1223 	float lifeFrac;
1224 	int t, step = 50;
1225 
1226 	for ( t = le->lastTrailTime + step; t < cg.time; t += step ) {
1227 		// calculate new position
1228 		BG_EvaluateTrajectory( &le->pos, t, newOrigin );
1229 
1230 		// trace a line from previous position to new position
1231 		CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT );
1232 
1233 		// if stuck, kill it
1234 		if ( trace.startsolid ) {
1235 			// HACK, some walls screw up, so just pass through if starting in a solid
1236 			VectorCopy( newOrigin, trace.endpos );
1237 			trace.fraction = 1.0;
1238 		}
1239 
1240 		// moved some distance
1241 		VectorCopy( trace.endpos, le->refEntity.origin );
1242 
1243 		// add a trail
1244 		lifeFrac = (float)( t - le->startTime ) / (float)( le->endTime - le->startTime );
1245 
1246 #if 0
1247 		// fire
1248 #if 1   // flame
1249 		if ( le->effectWidth > 0 ) {
1250 			le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex,
1251 												 cgs.media.fireTrailShader,
1252 												 le->refEntity.origin,
1253 												 (int)( 500.0 * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ) ), // trail life
1254 												 1.0, // alpha
1255 												 0.5, // end alpha
1256 												 3, // start width
1257 												 le->effectWidth ); // end width
1258 #else   // spark line
1259 		if ( le->effectWidth > 0 ) {
1260 			le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex,
1261 												 cgs.media.sparkParticleShader,
1262 												 le->refEntity.origin,
1263 												 (int)( 600.0 * ( 0.5 + 0.5 * ( 0.5 - lifeFrac ) ) ), // trail life
1264 												 1.0 - lifeFrac * 2, // alpha
1265 												 0.5 * ( 1.0 - lifeFrac ), // end alpha
1266 												 5.0 * ( 1.0 - lifeFrac ), // start width
1267 												 5.0 * ( 1.0 - lifeFrac ) ); // end width
1268 #endif
1269 		}
1270 #endif
1271 
1272 		// smoke
1273 		if ( le->effectFlags & 1 ) {
1274 			le->headJuncIndex2 = CG_AddSmokeJunc( le->headJuncIndex2,
1275 												  cgs.media.smokeTrailShader,
1276 												  le->refEntity.origin,
1277 												  (int)( 2000.0 * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ) ), // trail life
1278 												  1.0 * ( trace.fraction == 1.0 ) * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ), // alpha
1279 												  1, // start width
1280 												  (int)( 60.0 * ( 0.5 + 0.5 * ( 1.0 - lifeFrac ) ) ) ); // end width
1281 		}
1282 
1283 		// if it is in a nodrop zone, remove it
1284 		// this keeps gibs from waiting at the bottom of pits of death
1285 		// and floating levels
1286 //		if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) {
1287 //			CG_FreeLocalEntity( le );
1288 //			return;
1289 //		}
1290 
1291 		if ( trace.fraction < 1.0 ) {
1292 			// reflect the velocity on the trace plane
1293 			CG_ReflectVelocity( le, &trace );
1294 			if ( VectorLength( le->pos.trDelta ) < 1 ) {
1295 				CG_FreeLocalEntity( le );
1296 				return;
1297 			}
1298 			// the intersection is a fraction of the frametime
1299 			le->pos.trTime = t;
1300 		}
1301 
1302 		le->lastTrailTime = t;
1303 	}
1304 
1305 }
1306 
1307 // Rafael Shrapnel
1308 /*
1309 ===============
1310 CG_AddShrapnel
1311 ===============
1312 */
1313 void CG_AddShrapnel( localEntity_t *le ) {
1314 	vec3_t newOrigin;
1315 	trace_t trace;
1316 
1317 	if ( le->pos.trType == TR_STATIONARY ) {
1318 		// sink into the ground if near the removal time
1319 		int t;
1320 		float oldZ;
1321 
1322 		t = le->endTime - cg.time;
1323 		if ( t < SINK_TIME ) {
1324 			// we must use an explicit lighting origin, otherwise the
1325 			// lighting would be lost as soon as the origin went
1326 			// into the ground
1327 			VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin );
1328 			le->refEntity.renderfx |= RF_LIGHTING_ORIGIN;
1329 			oldZ = le->refEntity.origin[2];
1330 			le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME );
1331 			trap_R_AddRefEntityToScene( &le->refEntity );
1332 			le->refEntity.origin[2] = oldZ;
1333 		} else {
1334 			trap_R_AddRefEntityToScene( &le->refEntity );
1335 			CG_AddParticleShrapnel( le );
1336 		}
1337 
1338 		return;
1339 	}
1340 
1341 	// calculate new position
1342 	BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin );
1343 
1344 	// trace a line from previous position to new position
1345 	CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID );
1346 	if ( trace.fraction == 1.0 ) {
1347 		// still in free fall
1348 		VectorCopy( newOrigin, le->refEntity.origin );
1349 
1350 		if ( le->leFlags & LEF_TUMBLE ) {
1351 			vec3_t angles;
1352 
1353 			BG_EvaluateTrajectory( &le->angles, cg.time, angles );
1354 			AnglesToAxis( angles, le->refEntity.axis );
1355 		}
1356 
1357 		trap_R_AddRefEntityToScene( &le->refEntity );
1358 		CG_AddParticleShrapnel( le );
1359 		return;
1360 	}
1361 
1362 	// if it is in a nodrop zone, remove it
1363 	// this keeps gibs from waiting at the bottom of pits of death
1364 	// and floating levels
1365 	if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) {
1366 		CG_FreeLocalEntity( le );
1367 		return;
1368 	}
1369 
1370 	// leave a mark
1371 	CG_FragmentBounceMark( le, &trace );
1372 
1373 	// do a bouncy sound
1374 	CG_FragmentBounceSound( le, &trace );
1375 
1376 	// reflect the velocity on the trace plane
1377 	CG_ReflectVelocity( le, &trace );
1378 
1379 	trap_R_AddRefEntityToScene( &le->refEntity );
1380 	CG_AddParticleShrapnel( le );
1381 }
1382 // done.
1383 
1384 /*
1385 =====================================================================
1386 
1387 TRIVIAL LOCAL ENTITIES
1388 
1389 These only do simple scaling or modulation before passing to the renderer
1390 =====================================================================
1391 */
1392 
1393 /*
1394 ====================
1395 CG_AddFadeRGB
1396 ====================
1397 */
1398 void CG_AddFadeRGB( localEntity_t *le ) {
1399 	refEntity_t *re;
1400 	float c;
1401 
1402 	re = &le->refEntity;
1403 
1404 	c = ( le->endTime - cg.time ) * le->lifeRate;
1405 	c *= 0xff;
1406 
1407 	re->shaderRGBA[0] = le->color[0] * c;
1408 	re->shaderRGBA[1] = le->color[1] * c;
1409 	re->shaderRGBA[2] = le->color[2] * c;
1410 	re->shaderRGBA[3] = le->color[3] * c;
1411 
1412 	trap_R_AddRefEntityToScene( re );
1413 }
1414 
1415 /*
1416 ==================
1417 CG_AddMoveScaleFade
1418 ==================
1419 */
1420 static void CG_AddMoveScaleFade( localEntity_t *le ) {
1421 	refEntity_t *re;
1422 	float c;
1423 	vec3_t delta;
1424 	float len;
1425 
1426 	re = &le->refEntity;
1427 
1428 	// fade / grow time
1429 //	c = ( le->endTime - cg.time ) * le->lifeRate;
1430 	if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) {
1431 		// fade / grow time
1432 		c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime );
1433 	} else {
1434 		// fade / grow time
1435 		c = ( le->endTime - cg.time ) * le->lifeRate;
1436 	}
1437 
1438 	// Ridah, spark
1439 	if ( !( le->leFlags & LEF_NOFADEALPHA ) ) {
1440 		// done.
1441 		re->shaderRGBA[3] = 0xff * c * le->color[3];
1442 	}
1443 
1444 	if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) {
1445 		c = ( le->endTime - cg.time ) * le->lifeRate;
1446 		re->radius = le->radius * ( 1.0 - c ) + 8;
1447 	}
1448 
1449 	BG_EvaluateTrajectory( &le->pos, cg.time, re->origin );
1450 
1451 	// if the view would be "inside" the sprite, kill the sprite
1452 	// so it doesn't add too much overdraw
1453 	VectorSubtract( re->origin, cg.refdef.vieworg, delta );
1454 	len = VectorLength( delta );
1455 	if ( len < le->radius ) {
1456 		CG_FreeLocalEntity( le );
1457 		return;
1458 	}
1459 
1460 	trap_R_AddRefEntityToScene( re );
1461 }
1462 
1463 
1464 /*
1465 ===================
1466 CG_AddScaleFade
1467 
1468 For rocket smokes that hang in place, fade out, and are
1469 removed if the view passes through them.
1470 There are often many of these, so it needs to be simple.
1471 ===================
1472 */
1473 static void CG_AddScaleFade( localEntity_t *le ) {
1474 	refEntity_t *re;
1475 	float c;
1476 	vec3_t delta;
1477 	float len;
1478 
1479 	re = &le->refEntity;
1480 
1481 	// fade / grow time
1482 	c = ( le->endTime - cg.time ) * le->lifeRate;
1483 
1484 	re->shaderRGBA[3] = 0xff * c * le->color[3];
1485 	if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) {
1486 		re->radius = le->radius * ( 1.0 - c ) + 8;
1487 	}
1488 
1489 	// if the view would be "inside" the sprite, kill the sprite
1490 	// so it doesn't add too much overdraw
1491 	VectorSubtract( re->origin, cg.refdef.vieworg, delta );
1492 	len = VectorLength( delta );
1493 	if ( len < le->radius ) {
1494 		CG_FreeLocalEntity( le );
1495 		return;
1496 	}
1497 
1498 	trap_R_AddRefEntityToScene( re );
1499 }
1500 
1501 
1502 /*
1503 =================
1504 CG_AddFallScaleFade
1505 
1506 This is just an optimized CG_AddMoveScaleFade
1507 For blood mists that drift down, fade out, and are
1508 removed if the view passes through them.
1509 There are often 100+ of these, so it needs to be simple.
1510 =================
1511 */
1512 static void CG_AddFallScaleFade( localEntity_t *le ) {
1513 	refEntity_t *re;
1514 	float c;
1515 	vec3_t delta;
1516 	float len;
1517 
1518 	re = &le->refEntity;
1519 
1520 	// fade time
1521 	c = ( le->endTime - cg.time ) * le->lifeRate;
1522 
1523 	re->shaderRGBA[3] = 0xff * c * le->color[3];
1524 
1525 	re->origin[2] = le->pos.trBase[2] - ( 1.0 - c ) * le->pos.trDelta[2];
1526 
1527 	re->radius = le->radius * ( 1.0 - c ) + 16;
1528 
1529 	// if the view would be "inside" the sprite, kill the sprite
1530 	// so it doesn't add too much overdraw
1531 	VectorSubtract( re->origin, cg.refdef.vieworg, delta );
1532 	len = VectorLength( delta );
1533 	if ( len < le->radius ) {
1534 		CG_FreeLocalEntity( le );
1535 		return;
1536 	}
1537 
1538 	trap_R_AddRefEntityToScene( re );
1539 }
1540 
1541 
1542 
1543 /*
1544 ================
1545 CG_AddExplosion
1546 ================
1547 */
1548 static void CG_AddExplosion( localEntity_t *ex ) {
1549 	refEntity_t *ent;
1550 
1551 	ent = &ex->refEntity;
1552 
1553 	// add the entity
1554 	// RF, don't add if shader is invalid
1555 	if ( ent->customShader >= 0 ) {
1556 		trap_R_AddRefEntityToScene( ent );
1557 	}
1558 
1559 	// add the dlight
1560 	if ( ex->light ) {
1561 		float light;
1562 
1563 		light = (float)( cg.time - ex->startTime ) / ( ex->endTime - ex->startTime );
1564 		if ( light < 0.5 ) {
1565 			light = 1.0;
1566 		} else {
1567 			light = 1.0 - ( light - 0.5 ) * 2;
1568 		}
1569 		light = ex->light * light;
1570 		trap_R_AddLightToScene( ent->origin, light, ex->lightColor[0], ex->lightColor[1], ex->lightColor[2], 0 );
1571 	}
1572 }
1573 
1574 /*
1575 ================
1576 CG_AddSpriteExplosion
1577 ================
1578 */
1579 static void CG_AddSpriteExplosion( localEntity_t *le ) {
1580 	refEntity_t re;
1581 	float c;
1582 
1583 	re = le->refEntity;
1584 
1585 	c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime );
1586 	if ( c > 1 ) {
1587 		c = 1.0;    // can happen during connection problems
1588 	}
1589 
1590 	re.shaderRGBA[0] = 0xff;
1591 	re.shaderRGBA[1] = 0xff;
1592 	re.shaderRGBA[2] = 0xff;
1593 	re.shaderRGBA[3] = 0xff * c * 0.33;
1594 
1595 	re.reType = RT_SPRITE;
1596 	re.radius = 42 * ( 1.0 - c ) + 30;
1597 
1598 	// Ridah, move away from surface
1599 	VectorMA( le->pos.trBase, ( 1.0 - c ), le->pos.trDelta, re.origin );
1600 	// done.
1601 
1602 	// RF, don't add if shader is invalid
1603 	if ( re.customShader >= 0 ) {
1604 		trap_R_AddRefEntityToScene( &re );
1605 	}
1606 
1607 	// add the dlight
1608 	if ( le->light ) {
1609 		float light;
1610 
1611 		// Ridah, modified this so the light fades out rather than shrinking
1612 		/*
1613 		light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime );
1614 		if ( light < 0.5 ) {
1615 			light = 1.0;
1616 		} else {
1617 			light = 1.0 - ( light - 0.5 ) * 2;
1618 		}
1619 		light = le->light * light;
1620 		trap_R_AddLightToScene(re.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2], 0 );
1621 		*/
1622 		light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime );
1623 		if ( light < 0.5 ) {
1624 			light = 1.0;
1625 		} else {
1626 			light = 1.0 - ( light - 0.5 ) * 2;
1627 		}
1628 		trap_R_AddLightToScene( re.origin, le->light, light * le->lightColor[0], light * le->lightColor[1], light * le->lightColor[2], 0 );
1629 		// done.
1630 	}
1631 }
1632 
1633 
1634 //==============================================================================
1635 
1636 /*
1637 ===================
1638 CG_AddLocalEntities
1639 
1640 ===================
1641 */
1642 void CG_AddLocalEntities( void ) {
1643 	localEntity_t   *le, *next;
1644 
1645 	cg.viewFade = 0.0;
1646 
1647 	// walk the list backwards, so any new local entities generated
1648 	// (trails, marks, etc) will be present this frame
1649 	le = cg_activeLocalEntities.prev;
1650 	for ( ; le != &cg_activeLocalEntities ; le = next ) {
1651 		// grab next now, so if the local entity is freed we
1652 		// still have it
1653 		next = le->prev;
1654 
1655 		if ( cg.time >= le->endTime ) {
1656 			CG_FreeLocalEntity( le );
1657 			continue;
1658 		}
1659 		switch ( le->leType ) {
1660 		default:
1661 			CG_Error( "Bad leType: %i", le->leType );
1662 			break;
1663 
1664 			// Ridah
1665 		case LE_MOVING_TRACER:
1666 			CG_AddMovingTracer( le );
1667 			break;
1668 		case LE_SPARK:
1669 			CG_AddSparkElements( le );
1670 			break;
1671 		case LE_FUSE_SPARK:
1672 			CG_AddFuseSparkElements( le );
1673 			break;
1674 		case LE_DEBRIS:
1675 			CG_AddDebrisElements( le );
1676 			break;
1677 		case LE_BLOOD:
1678 			CG_AddBloodElements( le );
1679 			break;
1680 		case LE_HELGA_SPIRIT:
1681 		case LE_ZOMBIE_SPIRIT:
1682 		case LE_ZOMBIE_BAT:
1683 			CG_AddClientCritter( le );
1684 			break;
1685 		case LE_SPIRIT_VIEWFLASH:
1686 			CG_AddSpiritViewflash( le );
1687 			// done.
1688 
1689 		case LE_MARK:
1690 			break;
1691 
1692 		case LE_SPRITE_EXPLOSION:
1693 			CG_AddSpriteExplosion( le );
1694 			break;
1695 
1696 		case LE_EXPLOSION:
1697 			CG_AddExplosion( le );
1698 			break;
1699 
1700 		case LE_FRAGMENT:           // gibs and brass
1701 			CG_AddFragment( le );
1702 			break;
1703 
1704 		case LE_MOVE_SCALE_FADE:        // water bubbles
1705 			CG_AddMoveScaleFade( le );
1706 			break;
1707 
1708 		case LE_FADE_RGB:               // teleporters, railtrails
1709 			CG_AddFadeRGB( le );
1710 			break;
1711 
1712 		case LE_FALL_SCALE_FADE: // gib blood trails
1713 			CG_AddFallScaleFade( le );
1714 			break;
1715 
1716 		case LE_SCALE_FADE:     // rocket trails
1717 			CG_AddScaleFade( le );
1718 			break;
1719 
1720 		case LE_EMITTER:
1721 			CG_AddEmitter( le );
1722 			break;
1723 
1724 		}
1725 	}
1726 }
1727 
1728