cg_effects.c (17147B)
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 // cg_effects.c -- these functions generate localentities, usually as a result 24 // of event processing 25 26 #include "cg_local.h" 27 28 29 /* 30 ================== 31 CG_BubbleTrail 32 33 Bullets shot underwater 34 ================== 35 */ 36 void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ) { 37 vec3_t move; 38 vec3_t vec; 39 float len; 40 int i; 41 42 if ( cg_noProjectileTrail.integer ) { 43 return; 44 } 45 46 VectorCopy (start, move); 47 VectorSubtract (end, start, vec); 48 len = VectorNormalize (vec); 49 50 // advance a random amount first 51 i = rand() % (int)spacing; 52 VectorMA( move, i, vec, move ); 53 54 VectorScale (vec, spacing, vec); 55 56 for ( ; i < len; i += spacing ) { 57 localEntity_t *le; 58 refEntity_t *re; 59 60 le = CG_AllocLocalEntity(); 61 le->leFlags = LEF_PUFF_DONT_SCALE; 62 le->leType = LE_MOVE_SCALE_FADE; 63 le->startTime = cg.time; 64 le->endTime = cg.time + 1000 + random() * 250; 65 le->lifeRate = 1.0 / ( le->endTime - le->startTime ); 66 67 re = &le->refEntity; 68 re->shaderTime = cg.time / 1000.0f; 69 70 re->reType = RT_SPRITE; 71 re->rotation = 0; 72 re->radius = 3; 73 re->customShader = cgs.media.waterBubbleShader; 74 re->shaderRGBA[0] = 0xff; 75 re->shaderRGBA[1] = 0xff; 76 re->shaderRGBA[2] = 0xff; 77 re->shaderRGBA[3] = 0xff; 78 79 le->color[3] = 1.0; 80 81 le->pos.trType = TR_LINEAR; 82 le->pos.trTime = cg.time; 83 VectorCopy( move, le->pos.trBase ); 84 le->pos.trDelta[0] = crandom()*5; 85 le->pos.trDelta[1] = crandom()*5; 86 le->pos.trDelta[2] = crandom()*5 + 6; 87 88 VectorAdd (move, vec, move); 89 } 90 } 91 92 /* 93 ===================== 94 CG_SmokePuff 95 96 Adds a smoke puff or blood trail localEntity. 97 ===================== 98 */ 99 localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, 100 float radius, 101 float r, float g, float b, float a, 102 float duration, 103 int startTime, 104 int fadeInTime, 105 int leFlags, 106 qhandle_t hShader ) { 107 static int seed = 0x92; 108 localEntity_t *le; 109 refEntity_t *re; 110 // int fadeInTime = startTime + duration / 2; 111 112 le = CG_AllocLocalEntity(); 113 le->leFlags = leFlags; 114 le->radius = radius; 115 116 re = &le->refEntity; 117 re->rotation = Q_random( &seed ) * 360; 118 re->radius = radius; 119 re->shaderTime = startTime / 1000.0f; 120 121 le->leType = LE_MOVE_SCALE_FADE; 122 le->startTime = startTime; 123 le->fadeInTime = fadeInTime; 124 le->endTime = startTime + duration; 125 if ( fadeInTime > startTime ) { 126 le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime ); 127 } 128 else { 129 le->lifeRate = 1.0 / ( le->endTime - le->startTime ); 130 } 131 le->color[0] = r; 132 le->color[1] = g; 133 le->color[2] = b; 134 le->color[3] = a; 135 136 137 le->pos.trType = TR_LINEAR; 138 le->pos.trTime = startTime; 139 VectorCopy( vel, le->pos.trDelta ); 140 VectorCopy( p, le->pos.trBase ); 141 142 VectorCopy( p, re->origin ); 143 re->customShader = hShader; 144 145 // rage pro can't alpha fade, so use a different shader 146 if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) { 147 re->customShader = cgs.media.smokePuffRageProShader; 148 re->shaderRGBA[0] = 0xff; 149 re->shaderRGBA[1] = 0xff; 150 re->shaderRGBA[2] = 0xff; 151 re->shaderRGBA[3] = 0xff; 152 } else { 153 re->shaderRGBA[0] = le->color[0] * 0xff; 154 re->shaderRGBA[1] = le->color[1] * 0xff; 155 re->shaderRGBA[2] = le->color[2] * 0xff; 156 re->shaderRGBA[3] = 0xff; 157 } 158 159 re->reType = RT_SPRITE; 160 re->radius = le->radius; 161 162 return le; 163 } 164 165 /* 166 ================== 167 CG_SpawnEffect 168 169 Player teleporting in or out 170 ================== 171 */ 172 void CG_SpawnEffect( vec3_t org ) { 173 localEntity_t *le; 174 refEntity_t *re; 175 176 le = CG_AllocLocalEntity(); 177 le->leFlags = 0; 178 le->leType = LE_FADE_RGB; 179 le->startTime = cg.time; 180 le->endTime = cg.time + 500; 181 le->lifeRate = 1.0 / ( le->endTime - le->startTime ); 182 183 le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; 184 185 re = &le->refEntity; 186 187 re->reType = RT_MODEL; 188 re->shaderTime = cg.time / 1000.0f; 189 190 #ifndef MISSIONPACK 191 re->customShader = cgs.media.teleportEffectShader; 192 #endif 193 re->hModel = cgs.media.teleportEffectModel; 194 AxisClear( re->axis ); 195 196 VectorCopy( org, re->origin ); 197 #ifdef MISSIONPACK 198 re->origin[2] += 16; 199 #else 200 re->origin[2] -= 24; 201 #endif 202 } 203 204 205 #ifdef MISSIONPACK 206 /* 207 =============== 208 CG_LightningBoltBeam 209 =============== 210 */ 211 void CG_LightningBoltBeam( vec3_t start, vec3_t end ) { 212 localEntity_t *le; 213 refEntity_t *beam; 214 215 le = CG_AllocLocalEntity(); 216 le->leFlags = 0; 217 le->leType = LE_SHOWREFENTITY; 218 le->startTime = cg.time; 219 le->endTime = cg.time + 50; 220 221 beam = &le->refEntity; 222 223 VectorCopy( start, beam->origin ); 224 // this is the end point 225 VectorCopy( end, beam->oldorigin ); 226 227 beam->reType = RT_LIGHTNING; 228 beam->customShader = cgs.media.lightningShader; 229 } 230 231 /* 232 ================== 233 CG_KamikazeEffect 234 ================== 235 */ 236 void CG_KamikazeEffect( vec3_t org ) { 237 localEntity_t *le; 238 refEntity_t *re; 239 240 le = CG_AllocLocalEntity(); 241 le->leFlags = 0; 242 le->leType = LE_KAMIKAZE; 243 le->startTime = cg.time; 244 le->endTime = cg.time + 3000;//2250; 245 le->lifeRate = 1.0 / ( le->endTime - le->startTime ); 246 247 le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; 248 249 VectorClear(le->angles.trBase); 250 251 re = &le->refEntity; 252 253 re->reType = RT_MODEL; 254 re->shaderTime = cg.time / 1000.0f; 255 256 re->hModel = cgs.media.kamikazeEffectModel; 257 258 VectorCopy( org, re->origin ); 259 260 } 261 262 /* 263 ================== 264 CG_ObeliskExplode 265 ================== 266 */ 267 void CG_ObeliskExplode( vec3_t org, int entityNum ) { 268 localEntity_t *le; 269 vec3_t origin; 270 271 // create an explosion 272 VectorCopy( org, origin ); 273 origin[2] += 64; 274 le = CG_MakeExplosion( origin, vec3_origin, 275 cgs.media.dishFlashModel, 276 cgs.media.rocketExplosionShader, 277 600, qtrue ); 278 le->light = 300; 279 le->lightColor[0] = 1; 280 le->lightColor[1] = 0.75; 281 le->lightColor[2] = 0.0; 282 } 283 284 /* 285 ================== 286 CG_ObeliskPain 287 ================== 288 */ 289 void CG_ObeliskPain( vec3_t org ) { 290 float r; 291 sfxHandle_t sfx; 292 293 // hit sound 294 r = rand() & 3; 295 if ( r < 2 ) { 296 sfx = cgs.media.obeliskHitSound1; 297 } else if ( r == 2 ) { 298 sfx = cgs.media.obeliskHitSound2; 299 } else { 300 sfx = cgs.media.obeliskHitSound3; 301 } 302 trap_S_StartSound ( org, ENTITYNUM_NONE, CHAN_BODY, sfx ); 303 } 304 305 306 /* 307 ================== 308 CG_InvulnerabilityImpact 309 ================== 310 */ 311 void CG_InvulnerabilityImpact( vec3_t org, vec3_t angles ) { 312 localEntity_t *le; 313 refEntity_t *re; 314 int r; 315 sfxHandle_t sfx; 316 317 le = CG_AllocLocalEntity(); 318 le->leFlags = 0; 319 le->leType = LE_INVULIMPACT; 320 le->startTime = cg.time; 321 le->endTime = cg.time + 1000; 322 le->lifeRate = 1.0 / ( le->endTime - le->startTime ); 323 324 le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; 325 326 re = &le->refEntity; 327 328 re->reType = RT_MODEL; 329 re->shaderTime = cg.time / 1000.0f; 330 331 re->hModel = cgs.media.invulnerabilityImpactModel; 332 333 VectorCopy( org, re->origin ); 334 AnglesToAxis( angles, re->axis ); 335 336 r = rand() & 3; 337 if ( r < 2 ) { 338 sfx = cgs.media.invulnerabilityImpactSound1; 339 } else if ( r == 2 ) { 340 sfx = cgs.media.invulnerabilityImpactSound2; 341 } else { 342 sfx = cgs.media.invulnerabilityImpactSound3; 343 } 344 trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, sfx ); 345 } 346 347 /* 348 ================== 349 CG_InvulnerabilityJuiced 350 ================== 351 */ 352 void CG_InvulnerabilityJuiced( vec3_t org ) { 353 localEntity_t *le; 354 refEntity_t *re; 355 vec3_t angles; 356 357 le = CG_AllocLocalEntity(); 358 le->leFlags = 0; 359 le->leType = LE_INVULJUICED; 360 le->startTime = cg.time; 361 le->endTime = cg.time + 10000; 362 le->lifeRate = 1.0 / ( le->endTime - le->startTime ); 363 364 le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; 365 366 re = &le->refEntity; 367 368 re->reType = RT_MODEL; 369 re->shaderTime = cg.time / 1000.0f; 370 371 re->hModel = cgs.media.invulnerabilityJuicedModel; 372 373 VectorCopy( org, re->origin ); 374 VectorClear(angles); 375 AnglesToAxis( angles, re->axis ); 376 377 trap_S_StartSound (org, ENTITYNUM_NONE, CHAN_BODY, cgs.media.invulnerabilityJuicedSound ); 378 } 379 380 #endif 381 382 /* 383 ================== 384 CG_ScorePlum 385 ================== 386 */ 387 void CG_ScorePlum( int client, vec3_t org, int score ) { 388 localEntity_t *le; 389 refEntity_t *re; 390 vec3_t angles; 391 static vec3_t lastPos; 392 393 // only visualize for the client that scored 394 if (client != cg.predictedPlayerState.clientNum || cg_scorePlum.integer == 0) { 395 return; 396 } 397 398 le = CG_AllocLocalEntity(); 399 le->leFlags = 0; 400 le->leType = LE_SCOREPLUM; 401 le->startTime = cg.time; 402 le->endTime = cg.time + 4000; 403 le->lifeRate = 1.0 / ( le->endTime - le->startTime ); 404 405 406 le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0; 407 le->radius = score; 408 409 VectorCopy( org, le->pos.trBase ); 410 if (org[2] >= lastPos[2] - 20 && org[2] <= lastPos[2] + 20) { 411 le->pos.trBase[2] -= 20; 412 } 413 414 //CG_Printf( "Plum origin %i %i %i -- %i\n", (int)org[0], (int)org[1], (int)org[2], (int)Distance(org, lastPos)); 415 VectorCopy(org, lastPos); 416 417 418 re = &le->refEntity; 419 420 re->reType = RT_SPRITE; 421 re->radius = 16; 422 423 VectorClear(angles); 424 AnglesToAxis( angles, re->axis ); 425 } 426 427 428 /* 429 ==================== 430 CG_MakeExplosion 431 ==================== 432 */ 433 localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, 434 qhandle_t hModel, qhandle_t shader, 435 int msec, qboolean isSprite ) { 436 float ang; 437 localEntity_t *ex; 438 int offset; 439 vec3_t tmpVec, newOrigin; 440 441 if ( msec <= 0 ) { 442 CG_Error( "CG_MakeExplosion: msec = %i", msec ); 443 } 444 445 // skew the time a bit so they aren't all in sync 446 offset = rand() & 63; 447 448 ex = CG_AllocLocalEntity(); 449 if ( isSprite ) { 450 ex->leType = LE_SPRITE_EXPLOSION; 451 452 // randomly rotate sprite orientation 453 ex->refEntity.rotation = rand() % 360; 454 VectorScale( dir, 16, tmpVec ); 455 VectorAdd( tmpVec, origin, newOrigin ); 456 } else { 457 ex->leType = LE_EXPLOSION; 458 VectorCopy( origin, newOrigin ); 459 460 // set axis with random rotate 461 if ( !dir ) { 462 AxisClear( ex->refEntity.axis ); 463 } else { 464 ang = rand() % 360; 465 VectorCopy( dir, ex->refEntity.axis[0] ); 466 RotateAroundDirection( ex->refEntity.axis, ang ); 467 } 468 } 469 470 ex->startTime = cg.time - offset; 471 ex->endTime = ex->startTime + msec; 472 473 // bias the time so all shader effects start correctly 474 ex->refEntity.shaderTime = ex->startTime / 1000.0f; 475 476 ex->refEntity.hModel = hModel; 477 ex->refEntity.customShader = shader; 478 479 // set origin 480 VectorCopy( newOrigin, ex->refEntity.origin ); 481 VectorCopy( newOrigin, ex->refEntity.oldorigin ); 482 483 ex->color[0] = ex->color[1] = ex->color[2] = 1.0; 484 485 return ex; 486 } 487 488 489 /* 490 ================= 491 CG_Bleed 492 493 This is the spurt of blood when a character gets hit 494 ================= 495 */ 496 void CG_Bleed( vec3_t origin, int entityNum ) { 497 localEntity_t *ex; 498 499 if ( !cg_blood.integer ) { 500 return; 501 } 502 503 ex = CG_AllocLocalEntity(); 504 ex->leType = LE_EXPLOSION; 505 506 ex->startTime = cg.time; 507 ex->endTime = ex->startTime + 500; 508 509 VectorCopy ( origin, ex->refEntity.origin); 510 ex->refEntity.reType = RT_SPRITE; 511 ex->refEntity.rotation = rand() % 360; 512 ex->refEntity.radius = 24; 513 514 ex->refEntity.customShader = cgs.media.bloodExplosionShader; 515 516 // don't show player's own blood in view 517 if ( entityNum == cg.snap->ps.clientNum ) { 518 ex->refEntity.renderfx |= RF_THIRD_PERSON; 519 } 520 } 521 522 523 524 /* 525 ================== 526 CG_LaunchGib 527 ================== 528 */ 529 void CG_LaunchGib( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { 530 localEntity_t *le; 531 refEntity_t *re; 532 533 le = CG_AllocLocalEntity(); 534 re = &le->refEntity; 535 536 le->leType = LE_FRAGMENT; 537 le->startTime = cg.time; 538 le->endTime = le->startTime + 5000 + random() * 3000; 539 540 VectorCopy( origin, re->origin ); 541 AxisCopy( axisDefault, re->axis ); 542 re->hModel = hModel; 543 544 le->pos.trType = TR_GRAVITY; 545 VectorCopy( origin, le->pos.trBase ); 546 VectorCopy( velocity, le->pos.trDelta ); 547 le->pos.trTime = cg.time; 548 549 le->bounceFactor = 0.6f; 550 551 le->leBounceSoundType = LEBS_BLOOD; 552 le->leMarkType = LEMT_BLOOD; 553 } 554 555 /* 556 =================== 557 CG_GibPlayer 558 559 Generated a bunch of gibs launching out from the bodies location 560 =================== 561 */ 562 #define GIB_VELOCITY 250 563 #define GIB_JUMP 250 564 void CG_GibPlayer( vec3_t playerOrigin ) { 565 vec3_t origin, velocity; 566 567 if ( !cg_blood.integer ) { 568 return; 569 } 570 571 VectorCopy( playerOrigin, origin ); 572 velocity[0] = crandom()*GIB_VELOCITY; 573 velocity[1] = crandom()*GIB_VELOCITY; 574 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 575 if ( rand() & 1 ) { 576 CG_LaunchGib( origin, velocity, cgs.media.gibSkull ); 577 } else { 578 CG_LaunchGib( origin, velocity, cgs.media.gibBrain ); 579 } 580 581 // allow gibs to be turned off for speed 582 if ( !cg_gibs.integer ) { 583 return; 584 } 585 586 VectorCopy( playerOrigin, origin ); 587 velocity[0] = crandom()*GIB_VELOCITY; 588 velocity[1] = crandom()*GIB_VELOCITY; 589 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 590 CG_LaunchGib( origin, velocity, cgs.media.gibAbdomen ); 591 592 VectorCopy( playerOrigin, origin ); 593 velocity[0] = crandom()*GIB_VELOCITY; 594 velocity[1] = crandom()*GIB_VELOCITY; 595 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 596 CG_LaunchGib( origin, velocity, cgs.media.gibArm ); 597 598 VectorCopy( playerOrigin, origin ); 599 velocity[0] = crandom()*GIB_VELOCITY; 600 velocity[1] = crandom()*GIB_VELOCITY; 601 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 602 CG_LaunchGib( origin, velocity, cgs.media.gibChest ); 603 604 VectorCopy( playerOrigin, origin ); 605 velocity[0] = crandom()*GIB_VELOCITY; 606 velocity[1] = crandom()*GIB_VELOCITY; 607 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 608 CG_LaunchGib( origin, velocity, cgs.media.gibFist ); 609 610 VectorCopy( playerOrigin, origin ); 611 velocity[0] = crandom()*GIB_VELOCITY; 612 velocity[1] = crandom()*GIB_VELOCITY; 613 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 614 CG_LaunchGib( origin, velocity, cgs.media.gibFoot ); 615 616 VectorCopy( playerOrigin, origin ); 617 velocity[0] = crandom()*GIB_VELOCITY; 618 velocity[1] = crandom()*GIB_VELOCITY; 619 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 620 CG_LaunchGib( origin, velocity, cgs.media.gibForearm ); 621 622 VectorCopy( playerOrigin, origin ); 623 velocity[0] = crandom()*GIB_VELOCITY; 624 velocity[1] = crandom()*GIB_VELOCITY; 625 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 626 CG_LaunchGib( origin, velocity, cgs.media.gibIntestine ); 627 628 VectorCopy( playerOrigin, origin ); 629 velocity[0] = crandom()*GIB_VELOCITY; 630 velocity[1] = crandom()*GIB_VELOCITY; 631 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 632 CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); 633 634 VectorCopy( playerOrigin, origin ); 635 velocity[0] = crandom()*GIB_VELOCITY; 636 velocity[1] = crandom()*GIB_VELOCITY; 637 velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; 638 CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); 639 } 640 641 /* 642 ================== 643 CG_LaunchGib 644 ================== 645 */ 646 void CG_LaunchExplode( vec3_t origin, vec3_t velocity, qhandle_t hModel ) { 647 localEntity_t *le; 648 refEntity_t *re; 649 650 le = CG_AllocLocalEntity(); 651 re = &le->refEntity; 652 653 le->leType = LE_FRAGMENT; 654 le->startTime = cg.time; 655 le->endTime = le->startTime + 10000 + random() * 6000; 656 657 VectorCopy( origin, re->origin ); 658 AxisCopy( axisDefault, re->axis ); 659 re->hModel = hModel; 660 661 le->pos.trType = TR_GRAVITY; 662 VectorCopy( origin, le->pos.trBase ); 663 VectorCopy( velocity, le->pos.trDelta ); 664 le->pos.trTime = cg.time; 665 666 le->bounceFactor = 0.1f; 667 668 le->leBounceSoundType = LEBS_BRASS; 669 le->leMarkType = LEMT_NONE; 670 } 671 672 #define EXP_VELOCITY 100 673 #define EXP_JUMP 150 674 /* 675 =================== 676 CG_GibPlayer 677 678 Generated a bunch of gibs launching out from the bodies location 679 =================== 680 */ 681 void CG_BigExplode( vec3_t playerOrigin ) { 682 vec3_t origin, velocity; 683 684 if ( !cg_blood.integer ) { 685 return; 686 } 687 688 VectorCopy( playerOrigin, origin ); 689 velocity[0] = crandom()*EXP_VELOCITY; 690 velocity[1] = crandom()*EXP_VELOCITY; 691 velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; 692 CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); 693 694 VectorCopy( playerOrigin, origin ); 695 velocity[0] = crandom()*EXP_VELOCITY; 696 velocity[1] = crandom()*EXP_VELOCITY; 697 velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; 698 CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); 699 700 VectorCopy( playerOrigin, origin ); 701 velocity[0] = crandom()*EXP_VELOCITY*1.5; 702 velocity[1] = crandom()*EXP_VELOCITY*1.5; 703 velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; 704 CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); 705 706 VectorCopy( playerOrigin, origin ); 707 velocity[0] = crandom()*EXP_VELOCITY*2.0; 708 velocity[1] = crandom()*EXP_VELOCITY*2.0; 709 velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; 710 CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); 711 712 VectorCopy( playerOrigin, origin ); 713 velocity[0] = crandom()*EXP_VELOCITY*2.5; 714 velocity[1] = crandom()*EXP_VELOCITY*2.5; 715 velocity[2] = EXP_JUMP + crandom()*EXP_VELOCITY; 716 CG_LaunchExplode( origin, velocity, cgs.media.smoke2 ); 717 } 718