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