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