codemp/cgame/cg_effects.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // cg_effects.c -- these functions generate localentities, usually as a result
00004 // of event processing
00005 
00006 #include "cg_local.h"
00007 
00008 /*
00009 ==================
00010 CG_BubbleTrail
00011 
00012 Bullets shot underwater
00013 ==================
00014 */
00015 void CG_BubbleTrail( vec3_t start, vec3_t end, float spacing ) {
00016         vec3_t          move;
00017         vec3_t          vec;
00018         float           len;
00019         int                     i;
00020 
00021         if ( cg_noProjectileTrail.integer ) {
00022                 return;
00023         }
00024 
00025         VectorCopy (start, move);
00026         VectorSubtract (end, start, vec);
00027         len = VectorNormalize (vec);
00028 
00029         // advance a random amount first
00030         i = rand() % (int)spacing;
00031         VectorMA( move, i, vec, move );
00032 
00033         VectorScale (vec, spacing, vec);
00034 
00035         for ( ; i < len; i += spacing ) {
00036                 localEntity_t   *le;
00037                 refEntity_t             *re;
00038 
00039                 le = CG_AllocLocalEntity();
00040                 le->leFlags = LEF_PUFF_DONT_SCALE;
00041                 le->leType = LE_MOVE_SCALE_FADE;
00042                 le->startTime = cg.time;
00043                 le->endTime = cg.time + 1000 + random() * 250;
00044                 le->lifeRate = 1.0 / ( le->endTime - le->startTime );
00045 
00046                 re = &le->refEntity;
00047                 re->shaderTime = cg.time / 1000.0f;
00048 
00049                 re->reType = RT_SPRITE;
00050                 re->rotation = 0;
00051                 re->radius = 3;
00052                 re->customShader = 0;//cgs.media.waterBubbleShader;
00053                 re->shaderRGBA[0] = 0xff;
00054                 re->shaderRGBA[1] = 0xff;
00055                 re->shaderRGBA[2] = 0xff;
00056                 re->shaderRGBA[3] = 0xff;
00057 
00058                 le->color[3] = 1.0;
00059 
00060                 le->pos.trType = TR_LINEAR;
00061                 le->pos.trTime = cg.time;
00062                 VectorCopy( move, le->pos.trBase );
00063                 le->pos.trDelta[0] = crandom()*5;
00064                 le->pos.trDelta[1] = crandom()*5;
00065                 le->pos.trDelta[2] = crandom()*5 + 6;
00066 
00067                 VectorAdd (move, vec, move);
00068         }
00069 }
00070 
00071 /*
00072 =====================
00073 CG_SmokePuff
00074 
00075 Adds a smoke puff or blood trail localEntity.
00076 =====================
00077 */
00078 localEntity_t *CG_SmokePuff( const vec3_t p, const vec3_t vel, 
00079                                    float radius,
00080                                    float r, float g, float b, float a,
00081                                    float duration,
00082                                    int startTime,
00083                                    int fadeInTime,
00084                                    int leFlags,
00085                                    qhandle_t hShader ) {
00086         static int      seed = 0x92;
00087         localEntity_t   *le;
00088         refEntity_t             *re;
00089 //      int fadeInTime = startTime + duration / 2;
00090 
00091         le = CG_AllocLocalEntity();
00092         le->leFlags = leFlags;
00093         le->radius = radius;
00094 
00095         re = &le->refEntity;
00096         re->rotation = Q_random( &seed ) * 360;
00097         re->radius = radius;
00098         re->shaderTime = startTime / 1000.0f;
00099 
00100         le->leType = LE_MOVE_SCALE_FADE;
00101         le->startTime = startTime;
00102         le->fadeInTime = fadeInTime;
00103         le->endTime = startTime + duration;
00104         if ( fadeInTime > startTime ) {
00105                 le->lifeRate = 1.0 / ( le->endTime - le->fadeInTime );
00106         }
00107         else {
00108                 le->lifeRate = 1.0 / ( le->endTime - le->startTime );
00109         }
00110         le->color[0] = r;
00111         le->color[1] = g; 
00112         le->color[2] = b;
00113         le->color[3] = a;
00114 
00115 
00116         le->pos.trType = TR_LINEAR;
00117         le->pos.trTime = startTime;
00118         VectorCopy( vel, le->pos.trDelta );
00119         VectorCopy( p, le->pos.trBase );
00120 
00121         VectorCopy( p, re->origin );
00122         re->customShader = hShader;
00123 
00124         re->shaderRGBA[0] = le->color[0] * 0xff;
00125         re->shaderRGBA[1] = le->color[1] * 0xff;
00126         re->shaderRGBA[2] = le->color[2] * 0xff;
00127         re->shaderRGBA[3] = 0xff;
00128 
00129         re->reType = RT_SPRITE;
00130         re->radius = le->radius;
00131 
00132         return le;
00133 }
00134 
00135 int CGDEBUG_SaberColor( int saberColor )
00136 {
00137         switch( (int)(saberColor) )
00138         {
00139                 case SABER_RED:
00140                         return 0x000000ff;
00141                         break;
00142                 case SABER_ORANGE:
00143                         return 0x000088ff;
00144                         break;
00145                 case SABER_YELLOW:
00146                         return 0x0000ffff;
00147                         break;
00148                 case SABER_GREEN:
00149                         return 0x0000ff00;
00150                         break;
00151                 case SABER_BLUE:
00152                         return 0x00ff0000;
00153                         break;
00154                 case SABER_PURPLE:
00155                         return 0x00ff00ff;
00156                         break;
00157                 default:
00158                         return saberColor;
00159                         break;
00160         }
00161 }
00162 
00163 void CG_TestLine( vec3_t start, vec3_t end, int time, unsigned int color, int radius) {
00164         localEntity_t   *le;
00165         refEntity_t             *re;
00166 
00167         le = CG_AllocLocalEntity();
00168         le->leType = LE_LINE;
00169         le->startTime = cg.time;
00170         le->endTime = cg.time + time;
00171         le->lifeRate = 1.0 / ( le->endTime - le->startTime );
00172 
00173         re = &le->refEntity;
00174         VectorCopy( start, re->origin );
00175         VectorCopy( end, re->oldorigin);
00176         re->shaderTime = cg.time / 1000.0f;
00177 
00178         re->reType = RT_LINE;
00179         re->radius = 0.5*radius;
00180         re->customShader = cgs.media.whiteShader; //trap_R_RegisterShaderNoMip("textures/colombia/canvas_doublesided");
00181 
00182         re->shaderTexCoord[0] = re->shaderTexCoord[1] = 1.0f;
00183 
00184         if (color==0)
00185         {
00186                 re->shaderRGBA[0] = re->shaderRGBA[1] = re->shaderRGBA[2] = re->shaderRGBA[3] = 0xff;
00187         }
00188         else
00189         {
00190                 color = CGDEBUG_SaberColor( color );
00191                 re->shaderRGBA[0] = color & 0xff;
00192                 color >>= 8;
00193                 re->shaderRGBA[1] = color & 0xff;
00194                 color >>= 8;
00195                 re->shaderRGBA[2] = color & 0xff;
00196 //              color >>= 8;
00197 //              re->shaderRGBA[3] = color & 0xff;
00198                 re->shaderRGBA[3] = 0xff;
00199         }
00200 
00201         le->color[3] = 1.0;
00202 
00203         //re->renderfx |= RF_DEPTHHACK;
00204 }
00205 
00206 /*
00207 ==================
00208 CG_ThrowChunk
00209 ==================
00210 */
00211 void CG_ThrowChunk( vec3_t origin, vec3_t velocity, qhandle_t hModel, int optionalSound, int startalpha ) {
00212         localEntity_t   *le;
00213         refEntity_t             *re;
00214 
00215         le = CG_AllocLocalEntity();
00216         re = &le->refEntity;
00217 
00218         le->leType = LE_FRAGMENT;
00219         le->startTime = cg.time;
00220         le->endTime = le->startTime + 5000 + random() * 3000;
00221 
00222         VectorCopy( origin, re->origin );
00223         AxisCopy( axisDefault, re->axis );
00224         re->hModel = hModel;
00225 
00226         le->pos.trType = TR_GRAVITY;
00227         le->angles.trType = TR_GRAVITY;
00228         VectorCopy( origin, le->pos.trBase );
00229         VectorCopy( velocity, le->pos.trDelta );
00230         VectorSet(le->angles.trBase, 20, 20, 20);
00231         VectorCopy( velocity, le->angles.trDelta );
00232         le->pos.trTime = cg.time;
00233         le->angles.trTime = cg.time;
00234 
00235         le->leFlags = LEF_TUMBLE;
00236 
00237         le->angles.trBase[YAW] = 180;
00238 
00239         le->bounceFactor = 0.3f;
00240         le->bounceSound = optionalSound;
00241 
00242         le->forceAlpha = startalpha;
00243 }
00244 
00245 //----------------------------
00246 //
00247 // Breaking Glass Technology
00248 //
00249 //----------------------------
00250 
00251 // Since we have shared verts when we tesselate the glass sheet, it helps to have a 
00252 //      random offset table set up up front.
00253 
00254 static float offX[20][20],
00255                         offZ[20][20];
00256 
00257 #define FX_ALPHA_NONLINEAR      0x00000004
00258 #define FX_APPLY_PHYSICS        0x02000000
00259 #define FX_USE_ALPHA            0x08000000
00260 
00261 static void CG_DoGlassQuad( vec3_t p[4], vec2_t uv[4], qboolean stick, int time, vec3_t dmgDir )
00262 {
00263         float   bounce;
00264         vec3_t  rotDelta;
00265         vec3_t  vel, accel;
00266         vec3_t  rgb1;
00267         addpolyArgStruct_t apArgs;
00268         int             i, i_2;
00269 
00270         VectorSet( vel, crandom() * 12, crandom() * 12, -1 );
00271 
00272         if ( !stick )
00273         {
00274                 // We aren't a motion delayed chunk, so let us move quickly
00275                 VectorMA( vel, 0.3f, dmgDir, vel );
00276         }
00277 
00278         // Set up acceleration due to gravity, 800 is standard QuakeIII gravity, so let's use something close
00279         VectorSet( accel, 0.0f, 0.0f, -(600.0f + random() * 100.0f ) );
00280 
00281         // We are using an additive shader, so let's set the RGB low so we look more like transparent glass
00282 //      VectorSet( rgb1, 0.1f, 0.1f, 0.1f );
00283         VectorSet( rgb1, 1.0f, 1.0f, 1.0f );
00284 
00285         // Being glass, we don't want to bounce much
00286         bounce = random() * 0.2f + 0.15f;
00287 
00288         // Set up our random rotate, we only do PITCH and YAW, not ROLL.  This is something like degrees per second
00289         VectorSet( rotDelta, crandom() * 40.0f, crandom() * 40.0f, 0.0f );
00290 
00291         //In an ideal world, this might actually work.
00292         /*
00293         CPoly *pol = FX_AddPoly(p, uv, 4,                       // verts, ST, vertCount
00294                         vel, accel,                             // motion
00295                         0.15f, 0.0f, 85.0f,             // alpha start, alpha end, alpha parm ( begin alpha fade when 85% of life is complete )
00296                         rgb1, rgb1, 0.0f,               // rgb start, rgb end, rgb parm ( not used )
00297                         rotDelta, bounce, time, // rotation amount, bounce, and time to delay motion for ( zero if no delay );
00298                         6000,                                   // life
00299                         cgi_R_RegisterShader( "gfx/misc/test_crackle" ), 
00300                         FX_APPLY_PHYSICS | FX_ALPHA_NONLINEAR | FX_USE_ALPHA );
00301 
00302         if ( random() > 0.95f && pol )
00303         {
00304                 pol->AddFlags( FX_IMPACT_RUNS_FX | FX_KILL_ON_IMPACT );
00305                 pol->SetImpactFxID( theFxScheduler.RegisterEffect( "glass_impact" ));
00306         }
00307         */
00308 
00309         //rww - this is dirty.
00310 
00311         i = 0;
00312         i_2 = 0;
00313 
00314         while (i < 4)
00315         {
00316                 while (i_2 < 3)
00317                 {
00318                         apArgs.p[i][i_2] = p[i][i_2];
00319 
00320                         i_2++;
00321                 }
00322 
00323                 i_2 = 0;
00324                 i++;
00325         }
00326 
00327         i = 0;
00328         i_2 = 0;
00329 
00330         while (i < 4)
00331         {
00332                 while (i_2 < 2)
00333                 {
00334                         apArgs.ev[i][i_2] = uv[i][i_2];
00335 
00336                         i_2++;
00337                 }
00338 
00339                 i_2 = 0;
00340                 i++;
00341         }
00342 
00343         apArgs.numVerts = 4;
00344         VectorCopy(vel, apArgs.vel);
00345         VectorCopy(accel, apArgs.accel);
00346 
00347         apArgs.alpha1 = 0.15f;
00348         apArgs.alpha2 = 0.0f;
00349         apArgs.alphaParm = 85.0f;
00350 
00351         VectorCopy(rgb1, apArgs.rgb1);
00352         VectorCopy(rgb1, apArgs.rgb2);
00353 
00354         apArgs.rgbParm = 0.0f;
00355 
00356         VectorCopy(rotDelta, apArgs.rotationDelta);
00357 
00358         apArgs.bounce = bounce;
00359         apArgs.motionDelay = time;
00360         apArgs.killTime = 6000;
00361         apArgs.shader = cgs.media.glassShardShader;
00362         apArgs.flags = (FX_APPLY_PHYSICS | FX_ALPHA_NONLINEAR | FX_USE_ALPHA);
00363 
00364         trap_FX_AddPoly(&apArgs);
00365 }
00366 
00367 static void CG_CalcBiLerp( vec3_t verts[4], vec3_t subVerts[4], vec2_t uv[4] )
00368 {
00369         vec3_t  temp;
00370 
00371         // Nasty crap
00372         VectorScale( verts[0],  1.0f - uv[0][0],        subVerts[0] );
00373         VectorMA( subVerts[0],  uv[0][0],                       verts[1], subVerts[0] );
00374         VectorScale( subVerts[0], 1.0f - uv[0][1],      temp );
00375         VectorScale( verts[3],  1.0f - uv[0][0],        subVerts[0] );
00376         VectorMA( subVerts[0],  uv[0][0],                       verts[2], subVerts[0] );
00377         VectorMA( temp,                 uv[0][1],                       subVerts[0], subVerts[0] );
00378 
00379         VectorScale( verts[0],  1.0f - uv[1][0],        subVerts[1] );
00380         VectorMA( subVerts[1],  uv[1][0],                       verts[1], subVerts[1] );
00381         VectorScale( subVerts[1], 1.0f - uv[1][1],      temp );
00382         VectorScale( verts[3],  1.0f - uv[1][0],        subVerts[1] );
00383         VectorMA( subVerts[1],  uv[1][0],                       verts[2], subVerts[1] );
00384         VectorMA( temp,                 uv[1][1],                       subVerts[1], subVerts[1] );
00385 
00386         VectorScale( verts[0],  1.0f - uv[2][0],        subVerts[2] );
00387         VectorMA( subVerts[2],  uv[2][0],                       verts[1], subVerts[2] );
00388         VectorScale( subVerts[2], 1.0f - uv[2][1],              temp );
00389         VectorScale( verts[3],  1.0f - uv[2][0],        subVerts[2] );
00390         VectorMA( subVerts[2],  uv[2][0],                       verts[2], subVerts[2] );
00391         VectorMA( temp,                 uv[2][1],                       subVerts[2], subVerts[2] );
00392 
00393         VectorScale( verts[0],  1.0f - uv[3][0],        subVerts[3] );
00394         VectorMA( subVerts[3],  uv[3][0],                       verts[1], subVerts[3] );
00395         VectorScale( subVerts[3], 1.0f - uv[3][1],      temp );
00396         VectorScale( verts[3],  1.0f - uv[3][0],        subVerts[3] );
00397         VectorMA( subVerts[3],  uv[3][0],                       verts[2], subVerts[3] );
00398         VectorMA( temp,                 uv[3][1],                       subVerts[3], subVerts[3] );
00399 }
00400 // bilinear
00401 //f(p',q') = (1 - y)  {[(1 - x)  f(p,q)] + [x  f(p,q+1)]} + y  {[(1 - x)  f(p+1,q)] + [x  f(p+1,q+1)]}. 
00402 
00403 
00404 static void CG_CalcHeightWidth( vec3_t verts[4], float *height, float *width )
00405 {
00406         vec3_t  dir1, dir2, cross;
00407 
00408         VectorSubtract( verts[3], verts[0], dir1 ); // v
00409         VectorSubtract( verts[1], verts[0], dir2 ); // p-a
00410         CrossProduct( dir1, dir2, cross );
00411         *width = VectorNormalize( cross ) / VectorNormalize( dir1 ); // v 
00412         VectorSubtract( verts[2], verts[0], dir2 ); // p-a
00413         CrossProduct( dir1, dir2, cross );
00414         *width += VectorNormalize( cross ) / VectorNormalize( dir1 ); // v 
00415         *width *= 0.5f;
00416 
00417         VectorSubtract( verts[1], verts[0], dir1 ); // v
00418         VectorSubtract( verts[2], verts[0], dir2 ); // p-a
00419         CrossProduct( dir1, dir2, cross );
00420         *height = VectorNormalize( cross ) / VectorNormalize( dir1 ); // v 
00421         VectorSubtract( verts[3], verts[0], dir2 ); // p-a
00422         CrossProduct( dir1, dir2, cross );
00423         *height += VectorNormalize( cross ) / VectorNormalize( dir1 ); // v 
00424         *height *= 0.5f;
00425 }
00426 //Consider a line in 3D with position vector "a" and direction vector "v" and 
00427 // let "p" be the position vector of an arbitrary point in 3D
00428 //dist = len( crossprod(p-a,v) ) / len(v); 
00429 
00430 void CG_InitGlass( void )
00431 {
00432         int i, t;
00433 
00434         // Build a table first, so that we can do a more unpredictable crack scheme
00435         //      do it once, up front to save a bit of time.
00436         for ( i = 0; i < 20; i++ )
00437         {
00438                 for ( t = 0; t < 20; t++ )
00439                 {
00440                         offX[t][i] = crandom() * 0.03f;
00441                         offZ[i][t] = crandom() * 0.03f;
00442                 }
00443         }
00444 }
00445 
00446 void Vector2Set(vec2_t a,float b,float c)
00447 {
00448         a[0] = b;
00449         a[1] = c;
00450 }
00451 
00452 #define TIME_DECAY_SLOW         0.1f
00453 #define TIME_DECAY_MED          0.04f
00454 #define TIME_DECAY_FAST         0.009f
00455 
00456 void CG_DoGlass( vec3_t verts[4], vec3_t normal, vec3_t dmgPt, vec3_t dmgDir, float dmgRadius, int maxShards )
00457 {
00458         int                     i, t;
00459         int                     mxHeight, mxWidth;
00460         float           height, width;
00461         float           stepWidth, stepHeight;
00462         float           timeDecay;
00463         float           x, z;
00464         float           xx, zz;
00465         float           dif;
00466         int                     time = 0;
00467         int                     glassShards = 0;
00468         qboolean        stick = qtrue;
00469         vec3_t          subVerts[4];
00470         vec2_t          biPoints[4];
00471 
00472         // To do a smarter tesselation, we should figure out the relative height and width of the brush face,
00473         //      then use this to pick a lod value from 1-3 in each axis.  This will give us 1-9 lod levels, which will
00474         //      hopefully be sufficient.
00475         CG_CalcHeightWidth( verts, &height, &width );
00476 
00477         trap_S_StartSound( dmgPt, -1, CHAN_AUTO, trap_S_RegisterSound("sound/effects/glassbreak1.wav"));
00478 
00479         // Pick "LOD" for height
00480         if ( height < 100 )
00481         {
00482                 stepHeight = 0.2f;
00483                 mxHeight = 5;
00484                 timeDecay = TIME_DECAY_SLOW;
00485         }
00486         else if ( height > 220 )
00487         {
00488                 stepHeight = 0.05f;
00489                 mxHeight = 20;
00490                 timeDecay = TIME_DECAY_FAST;
00491         }
00492         else
00493         {
00494                 stepHeight = 0.1f;
00495                 mxHeight = 10;
00496                 timeDecay = TIME_DECAY_MED;
00497         }
00498 
00499         // Pick "LOD" for width
00500         /*
00501         if ( width < 100 )
00502         {
00503                 stepWidth = 0.2f;
00504                 mxWidth = 5;
00505                 timeDecay = ( timeDecay + TIME_DECAY_SLOW ) * 0.5f;
00506         }
00507         else if ( width > 220 )
00508         {
00509                 stepWidth = 0.05f;
00510                 mxWidth = 20;
00511                 timeDecay = ( timeDecay + TIME_DECAY_FAST ) * 0.5f;
00512         }
00513         else
00514         {
00515                 stepWidth = 0.1f;
00516                 mxWidth = 10;
00517                 timeDecay = ( timeDecay + TIME_DECAY_MED ) * 0.5f;
00518         }
00519         */
00520 
00521         //Attempt to scale the glass directly to the size of the window
00522 
00523         stepWidth = (0.25f - (width*0.0002)); //(width*0.0005));
00524         mxWidth = width*0.2;
00525         timeDecay = ( timeDecay + TIME_DECAY_FAST ) * 0.5f;
00526 
00527         if (stepWidth < 0.01f)
00528         {
00529                 stepWidth = 0.01f;
00530         }
00531         if (mxWidth < 5)
00532         {
00533                 mxWidth = 5;
00534         }
00535 
00536         for ( z = 0.0f, i = 0; z < 1.0f; z += stepHeight, i++ )
00537         {
00538                 for ( x = 0.0f, t = 0; x < 1.0f; x += stepWidth, t++ )
00539                 {
00540                         // This is nasty..
00541                         if ( t > 0 && t < mxWidth )
00542                         {
00543                                 xx = x - offX[i][t];
00544                         }
00545                         else
00546                         {
00547                                 xx = x;
00548                         }
00549 
00550                         if ( i > 0 && i < mxHeight )
00551                         {
00552                                 zz = z - offZ[t][i];
00553                         }
00554                         else
00555                         {
00556                                 zz = z;
00557                         }
00558 
00559                         Vector2Set( biPoints[0], xx, zz );
00560 
00561                         if ( t + 1 > 0 && t + 1 < mxWidth )
00562                         {
00563                                 xx = x - offX[i][t + 1];
00564                         }
00565                         else
00566                         {
00567                                 xx = x;
00568                         }
00569 
00570                         if ( i > 0 && i < mxHeight )
00571                         {
00572                                 zz = z - offZ[t + 1][i];
00573                         }
00574                         else
00575                         {
00576                                 zz = z;
00577                         }
00578 
00579                         Vector2Set( biPoints[1], xx + stepWidth, zz );
00580 
00581                         if ( t + 1 > 0 && t + 1 < mxWidth )
00582                         {
00583                                 xx = x - offX[i + 1][t + 1];
00584                         }
00585                         else
00586                         {
00587                                 xx = x;
00588                         }
00589 
00590                         if ( i + 1 > 0 && i + 1 < mxHeight )
00591                         {
00592                                 zz = z - offZ[t + 1][i + 1];
00593                         }
00594                         else
00595                         {
00596                                 zz = z;
00597                         }
00598 
00599                         Vector2Set( biPoints[2], xx + stepWidth, zz + stepHeight);
00600 
00601                         if ( t > 0 && t < mxWidth )
00602                         {
00603                                 xx = x - offX[i + 1][t];
00604                         }
00605                         else
00606                         {
00607                                 xx = x;
00608                         }
00609 
00610                         if ( i + 1 > 0 && i + 1 < mxHeight )
00611                         {
00612                                 zz = z - offZ[t][i + 1];
00613                         }
00614                         else
00615                         {
00616                                 zz = z;
00617                         }
00618 
00619                         Vector2Set( biPoints[3], xx, zz + stepHeight );
00620 
00621                         CG_CalcBiLerp( verts, subVerts, biPoints );
00622                         
00623                         dif = DistanceSquared( subVerts[0], dmgPt ) * timeDecay - random() * 32;
00624                         
00625                         // If we decrease dif, we are increasing the impact area, making it more likely to blow out large holes
00626                         dif -= dmgRadius * dmgRadius;
00627 
00628                         if ( dif > 1 )
00629                         {
00630                                 stick = qtrue;
00631                                 time = dif + random() * 200;
00632                         }
00633                         else
00634                         {
00635                                 stick = qfalse;
00636                                 time = 0;
00637                         }
00638 
00639                         CG_DoGlassQuad( subVerts, biPoints, stick, time, dmgDir );
00640                         glassShards++;
00641 
00642                         if (maxShards && glassShards >= maxShards)
00643                         {
00644                                 return;
00645                         }
00646                 }
00647         }
00648 }
00649 
00650 /*
00651 ==================
00652 CG_GlassShatter
00653 Break glass with fancy method
00654 ==================
00655 */
00656 void CG_GlassShatter(int entnum, vec3_t dmgPt, vec3_t dmgDir, float dmgRadius, int maxShards)
00657 {
00658         vec3_t          verts[4], normal;
00659 
00660         if (cgs.inlineDrawModel[cg_entities[entnum].currentState.modelindex])
00661         {
00662                 trap_R_GetBModelVerts(cgs.inlineDrawModel[cg_entities[entnum].currentState.modelindex], verts, normal);
00663                 CG_DoGlass(verts, normal, dmgPt, dmgDir, dmgRadius, maxShards);
00664         }
00665         //otherwise something awful has happened.
00666 }
00667 
00668 /*
00669 ==================
00670 CG_GlassShatter_Old
00671 Throws glass shards from within a given bounding box in the world
00672 ==================
00673 */
00674 void CG_GlassShatter_Old(int entnum, vec3_t org, vec3_t mins, vec3_t maxs)
00675 {
00676         vec3_t velocity, a, shardorg, dif, difx;
00677         float windowmass;
00678         float shardsthrow = 0;
00679         char chunkname[256];
00680 
00681         trap_S_StartSound(org, entnum, CHAN_BODY, trap_S_RegisterSound("sound/effects/glassbreak1.wav"));
00682 
00683         VectorSubtract(maxs, mins, a);
00684 
00685         windowmass = VectorLength(a); //should give us some idea of how big the chunk of glass is
00686 
00687         while (shardsthrow < windowmass)
00688         {
00689                 velocity[0] = crandom()*150;
00690                 velocity[1] = crandom()*150;
00691                 velocity[2] = 150 + crandom()*75;
00692 
00693                 Com_sprintf(chunkname, sizeof(chunkname), "models/chunks/glass/glchunks_%i.md3", Q_irand(1, 6));
00694                 VectorCopy(org, shardorg);
00695         
00696                 dif[0] = (maxs[0]-mins[0])/2;
00697                 dif[1] = (maxs[1]-mins[1])/2;
00698                 dif[2] = (maxs[2]-mins[2])/2;
00699 
00700                 if (dif[0] < 2)
00701                 {
00702                         dif[0] = 2;
00703                 }
00704                 if (dif[1] < 2)
00705                 {
00706                         dif[1] = 2;
00707                 }
00708                 if (dif[2] < 2)
00709                 {
00710                         dif[2] = 2;
00711                 }
00712 
00713                 difx[0] = Q_irand(1, (dif[0]*0.9)*2);
00714                 difx[1] = Q_irand(1, (dif[1]*0.9)*2);
00715                 difx[2] = Q_irand(1, (dif[2]*0.9)*2);
00716 
00717                 if (difx[0] > dif[0])
00718                 {
00719                         shardorg[0] += difx[0]-(dif[0]);
00720                 }
00721                 else
00722                 {
00723                         shardorg[0] -= difx[0];
00724                 }
00725                 if (difx[1] > dif[1])
00726                 {
00727                         shardorg[1] += difx[1]-(dif[1]);
00728                 }
00729                 else
00730                 {
00731                         shardorg[1] -= difx[1];
00732                 }
00733                 if (difx[2] > dif[2])
00734                 {
00735                         shardorg[2] += difx[2]-(dif[2]);
00736                 }
00737                 else
00738                 {
00739                         shardorg[2] -= difx[2];
00740                 }
00741 
00742                 //CG_TestLine(org, shardorg, 5000, 0x0000ff, 3);
00743 
00744                 CG_ThrowChunk( shardorg, velocity, trap_R_RegisterModel( chunkname ), 0, 254 );
00745 
00746                 shardsthrow += 10;
00747         }
00748 }
00749 
00750 /*
00751 ==================
00752 CG_CreateDebris
00753 Throws specified debris from within a given bounding box in the world
00754 ==================
00755 */
00756 #define DEBRIS_SPECIALCASE_ROCK                 -1
00757 #define DEBRIS_SPECIALCASE_CHUNKS               -2
00758 #define DEBRIS_SPECIALCASE_WOOD                 -3
00759 #define DEBRIS_SPECIALCASE_GLASS                -4
00760 
00761 #define NUM_DEBRIS_MODELS_GLASS                         8
00762 #define NUM_DEBRIS_MODELS_WOOD                          8
00763 #define NUM_DEBRIS_MODELS_CHUNKS                        3
00764 #define NUM_DEBRIS_MODELS_ROCKS                         4 //12
00765 
00766 int dbModels_Glass[NUM_DEBRIS_MODELS_GLASS];
00767 int dbModels_Wood[NUM_DEBRIS_MODELS_WOOD];
00768 int dbModels_Chunks[NUM_DEBRIS_MODELS_CHUNKS];
00769 int dbModels_Rocks[NUM_DEBRIS_MODELS_ROCKS];
00770 
00771 void CG_CreateDebris(int entnum, vec3_t org, vec3_t mins, vec3_t maxs, int debrissound, int debrismodel)
00772 {
00773         vec3_t velocity, a, shardorg, dif, difx;
00774         float windowmass;
00775         float shardsthrow = 0;
00776         int omodel = debrismodel;
00777 
00778         if (omodel == DEBRIS_SPECIALCASE_GLASS && !dbModels_Glass[0])
00779         { //glass no longer exists, using it for metal.
00780                 dbModels_Glass[0] = trap_R_RegisterModel("models/chunks/metal/metal1_1.md3");
00781                 dbModels_Glass[1] = trap_R_RegisterModel("models/chunks/metal/metal1_2.md3");
00782                 dbModels_Glass[2] = trap_R_RegisterModel("models/chunks/metal/metal1_3.md3");
00783                 dbModels_Glass[3] = trap_R_RegisterModel("models/chunks/metal/metal1_4.md3");
00784                 dbModels_Glass[4] = trap_R_RegisterModel("models/chunks/metal/metal2_1.md3");
00785                 dbModels_Glass[5] = trap_R_RegisterModel("models/chunks/metal/metal2_2.md3");
00786                 dbModels_Glass[6] = trap_R_RegisterModel("models/chunks/metal/metal2_3.md3");
00787                 dbModels_Glass[7] = trap_R_RegisterModel("models/chunks/metal/metal2_4.md3");
00788         }
00789         if (omodel == DEBRIS_SPECIALCASE_WOOD && !dbModels_Wood[0])
00790         {
00791                 dbModels_Wood[0] = trap_R_RegisterModel("models/chunks/crate/crate1_1.md3");
00792                 dbModels_Wood[1] = trap_R_RegisterModel("models/chunks/crate/crate1_2.md3");
00793                 dbModels_Wood[2] = trap_R_RegisterModel("models/chunks/crate/crate1_3.md3");
00794                 dbModels_Wood[3] = trap_R_RegisterModel("models/chunks/crate/crate1_4.md3");
00795                 dbModels_Wood[4] = trap_R_RegisterModel("models/chunks/crate/crate2_1.md3");
00796                 dbModels_Wood[5] = trap_R_RegisterModel("models/chunks/crate/crate2_2.md3");
00797                 dbModels_Wood[6] = trap_R_RegisterModel("models/chunks/crate/crate2_3.md3");
00798                 dbModels_Wood[7] = trap_R_RegisterModel("models/chunks/crate/crate2_4.md3");
00799         }
00800         if (omodel == DEBRIS_SPECIALCASE_CHUNKS && !dbModels_Chunks[0])
00801         {
00802                 dbModels_Chunks[0] = trap_R_RegisterModel("models/chunks/generic/chunks_1.md3");
00803                 dbModels_Chunks[1] = trap_R_RegisterModel("models/chunks/generic/chunks_2.md3");
00804         }
00805         if (omodel == DEBRIS_SPECIALCASE_ROCK && !dbModels_Rocks[0])
00806         {
00807                 dbModels_Rocks[0] = trap_R_RegisterModel("models/chunks/rock/rock1_1.md3");
00808                 dbModels_Rocks[1] = trap_R_RegisterModel("models/chunks/rock/rock1_2.md3");
00809                 dbModels_Rocks[2] = trap_R_RegisterModel("models/chunks/rock/rock1_3.md3");
00810                 dbModels_Rocks[3] = trap_R_RegisterModel("models/chunks/rock/rock1_4.md3");
00811                 /*
00812                 dbModels_Rocks[4] = trap_R_RegisterModel("models/chunks/rock/rock2_1.md3");
00813                 dbModels_Rocks[5] = trap_R_RegisterModel("models/chunks/rock/rock2_2.md3");
00814                 dbModels_Rocks[6] = trap_R_RegisterModel("models/chunks/rock/rock2_3.md3");
00815                 dbModels_Rocks[7] = trap_R_RegisterModel("models/chunks/rock/rock2_4.md3");
00816                 dbModels_Rocks[8] = trap_R_RegisterModel("models/chunks/rock/rock3_1.md3");
00817                 dbModels_Rocks[9] = trap_R_RegisterModel("models/chunks/rock/rock3_2.md3");
00818                 dbModels_Rocks[10] = trap_R_RegisterModel("models/chunks/rock/rock3_3.md3");
00819                 dbModels_Rocks[11] = trap_R_RegisterModel("models/chunks/rock/rock3_4.md3");
00820                 */
00821         }
00822 
00823         VectorSubtract(maxs, mins, a);
00824 
00825         windowmass = VectorLength(a); //should give us some idea of how big the chunk of glass is
00826 
00827         while (shardsthrow < windowmass)
00828         {
00829                 velocity[0] = crandom()*150;
00830                 velocity[1] = crandom()*150;
00831                 velocity[2] = 150 + crandom()*75;
00832 
00833                 if (omodel == DEBRIS_SPECIALCASE_GLASS)
00834                 {
00835                         debrismodel = dbModels_Glass[Q_irand(0, NUM_DEBRIS_MODELS_GLASS-1)];
00836                 }
00837                 else if (omodel == DEBRIS_SPECIALCASE_WOOD)
00838                 {
00839                         debrismodel = dbModels_Wood[Q_irand(0, NUM_DEBRIS_MODELS_WOOD-1)];
00840                 }
00841                 else if (omodel == DEBRIS_SPECIALCASE_CHUNKS)
00842                 {
00843                         debrismodel = dbModels_Chunks[Q_irand(0, NUM_DEBRIS_MODELS_CHUNKS-1)];
00844                 }
00845                 else if (omodel == DEBRIS_SPECIALCASE_ROCK)
00846                 {
00847                         debrismodel = dbModels_Rocks[Q_irand(0, NUM_DEBRIS_MODELS_ROCKS-1)];
00848                 }
00849 
00850                 VectorCopy(org, shardorg);
00851         
00852                 dif[0] = (maxs[0]-mins[0])/2;
00853                 dif[1] = (maxs[1]-mins[1])/2;
00854                 dif[2] = (maxs[2]-mins[2])/2;
00855 
00856                 if (dif[0] < 2)
00857                 {
00858                         dif[0] = 2;
00859                 }
00860                 if (dif[1] < 2)
00861                 {
00862                         dif[1] = 2;
00863                 }
00864                 if (dif[2] < 2)
00865                 {
00866                         dif[2] = 2;
00867                 }
00868 
00869                 difx[0] = Q_irand(1, (dif[0]*0.9)*2);
00870                 difx[1] = Q_irand(1, (dif[1]*0.9)*2);
00871                 difx[2] = Q_irand(1, (dif[2]*0.9)*2);
00872 
00873                 if (difx[0] > dif[0])
00874                 {
00875                         shardorg[0] += difx[0]-(dif[0]);
00876                 }
00877                 else
00878                 {
00879                         shardorg[0] -= difx[0];
00880                 }
00881                 if (difx[1] > dif[1])
00882                 {
00883                         shardorg[1] += difx[1]-(dif[1]);
00884                 }
00885                 else
00886                 {
00887                         shardorg[1] -= difx[1];
00888                 }
00889                 if (difx[2] > dif[2])
00890                 {
00891                         shardorg[2] += difx[2]-(dif[2]);
00892                 }
00893                 else
00894                 {
00895                         shardorg[2] -= difx[2];
00896                 }
00897 
00898                 //CG_TestLine(org, shardorg, 5000, 0x0000ff, 3);
00899 
00900                 CG_ThrowChunk( shardorg, velocity, debrismodel, debrissound, 0 );
00901 
00902                 shardsthrow += 10;
00903         }
00904 }
00905 
00906 //==========================================================
00907 //SP-style chunks
00908 //==========================================================
00909 
00910 /*
00911 -------------------------
00912 CG_ExplosionEffects
00913 
00914 Used to find the player and shake the camera if close enough
00915 intensity ranges from 1 (minor tremble) to 16 (major quake)
00916 -------------------------
00917 */
00918 
00919 void CG_ExplosionEffects( vec3_t origin, float intensity, int radius, int time )
00920 {
00921         //FIXME: When exactly is the vieworg calculated in relation to the rest of the frame?s
00922 
00923         vec3_t  dir;
00924         float   dist, intensityScale;
00925         float   realIntensity;
00926 
00927         VectorSubtract( cg.refdef.vieworg, origin, dir );
00928         dist = VectorNormalize( dir );
00929 
00930         //Use the dir to add kick to the explosion
00931 
00932         if ( dist > radius )
00933                 return;
00934 
00935         intensityScale = 1 - ( dist / (float) radius );
00936         realIntensity = intensity * intensityScale;
00937 
00938         CGCam_Shake( realIntensity, time );
00939 }
00940 
00941 /*
00942 -------------------------
00943 CG_MiscModelExplosion
00944 
00945 Adds an explosion to a misc model breakables
00946 -------------------------
00947 */
00948 
00949 void CG_MiscModelExplosion( vec3_t mins, vec3_t maxs, int size, material_t chunkType )
00950 {
00951         int             ct = 13;
00952         float   r;
00953         vec3_t  org, mid, dir;
00954         char    *effect = NULL, *effect2 = NULL;
00955         int             eID1, eID2 = 0;
00956         int             i;
00957 
00958         VectorAdd( mins, maxs, mid );
00959         VectorScale( mid, 0.5f, mid );
00960 
00961         switch( chunkType )
00962         {
00963         case MAT_GLASS:
00964                 effect = "chunks/glassbreak";
00965                 ct = 5;
00966                 break;
00967         case MAT_GLASS_METAL:
00968                 effect = "chunks/glassbreak";
00969                 effect2 = "chunks/metalexplode";
00970                 ct = 5;
00971                 break;
00972         case MAT_ELECTRICAL:
00973         case MAT_ELEC_METAL:
00974                 effect = "chunks/sparkexplode";
00975                 ct = 5;
00976                 break;
00977         case MAT_METAL:
00978         case MAT_METAL2:
00979         case MAT_METAL3:
00980         case MAT_CRATE1:
00981         case MAT_CRATE2:
00982                 effect = "chunks/metalexplode";
00983                 ct = 2;
00984                 break;
00985         case MAT_GRATE1:
00986                 effect = "chunks/grateexplode";
00987                 ct = 8;
00988                 break;
00989         case MAT_ROPE:
00990                 ct = 20;
00991                 effect = "chunks/ropebreak";
00992                 break;
00993         case MAT_WHITE_METAL: //not sure what this crap is really supposed to be..
00994         case MAT_DRK_STONE:
00995         case MAT_LT_STONE:
00996         case MAT_GREY_STONE:
00997         case MAT_SNOWY_ROCK:
00998                 switch( size )
00999                 {
01000                 case 2:
01001                         effect = "chunks/rockbreaklg";
01002                         break;
01003                 case 1:
01004                 default:
01005                         effect = "chunks/rockbreakmed";
01006                         break;
01007                 }
01008         }
01009 
01010         if ( !effect )
01011         {
01012                 return;
01013         }
01014 
01015         ct += 7 * size;
01016 
01017         // FIXME: real precache .. VERify that these need to be here...don't think they would because the effects should be registered in g_breakable
01018         //rww - No they don't.. indexed effects gameside get precached on load clientside, as server objects are setup before client asset load time.
01019         //However, we need to index them, so..
01020         eID1 = trap_FX_RegisterEffect( effect );
01021 
01022         if ( effect2 && effect2[0] )
01023         {
01024                 // FIXME: real precache
01025                 eID2 = trap_FX_RegisterEffect( effect2 );
01026         }
01027 
01028         // spawn chunk roughly in the bbox of the thing..
01029         for ( i = 0; i < ct; i++ )
01030         {
01031                 int j;
01032                 for( j = 0; j < 3; j++ )
01033                 {
01034                         r = random() * 0.8f + 0.1f;
01035                         org[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] );
01036                 }
01037 
01038                 // shoot effect away from center
01039                 VectorSubtract( org, mid, dir );
01040                 VectorNormalize( dir );
01041 
01042                 if ( effect2 && effect2[0] && ( rand() & 1 ))
01043                 {
01044                         trap_FX_PlayEffectID( eID2, org, dir, -1, -1 );
01045                 }
01046                 else
01047                 {
01048                         trap_FX_PlayEffectID( eID1, org, dir, -1, -1 );
01049                 }
01050         }
01051 }
01052 
01053 /*
01054 -------------------------
01055 CG_Chunks
01056 
01057 Fun chunk spewer
01058 -------------------------
01059 */
01060 
01061 void CG_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins, const vec3_t maxs, 
01062                                                 float speed, int numChunks, material_t chunkType, int customChunk, float baseScale )
01063 {
01064         localEntity_t   *le;
01065         refEntity_t             *re;
01066         vec3_t                  dir;
01067         int                             i, j, k;
01068         int                             chunkModel = 0;
01069         leBounceSoundType_t     bounce = LEBS_NONE;
01070         float                   r, speedMod = 1.0f;
01071         qboolean                chunk = qfalse;
01072 
01073         if ( chunkType == MAT_NONE )
01074         {
01075                 // Well, we should do nothing
01076                 return;
01077         }
01078 
01079         // Set up our chunk sound info...breaking sounds are done here so they are done once on breaking..some return instantly because the chunks are done with effects instead of models
01080         switch( chunkType )
01081         {
01082         case MAT_GLASS:
01083                 trap_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound );
01084                 return;
01085                 break;
01086         case MAT_GRATE1:
01087                 trap_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.grateSound );
01088                 return;
01089                 break;
01090         case MAT_ELECTRICAL:// (sparks)
01091                 trap_S_StartSound( NULL, owner, CHAN_BODY, trap_S_RegisterSound (va("sound/ambience/spark%d.wav", Q_irand(1, 6))) );
01092                 return;
01093                 break;
01094         case MAT_DRK_STONE:
01095         case MAT_LT_STONE:
01096         case MAT_GREY_STONE:
01097         case MAT_WHITE_METAL:  // not quite sure what this stuff is supposed to be...it's for Stu
01098         case MAT_SNOWY_ROCK:
01099                 trap_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.rockBreakSound );
01100                 bounce = LEBS_ROCK;
01101                 speedMod = 0.5f; // rock blows up less
01102                 break;
01103         case MAT_GLASS_METAL:
01104                 trap_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound ); // FIXME: should probably have a custom sound
01105                 bounce = LEBS_METAL;
01106                 break;
01107         case MAT_CRATE1:
01108         case MAT_CRATE2:
01109                 trap_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.crateBreakSound[Q_irand(0,1)] );
01110                 break;
01111         case MAT_METAL:
01112         case MAT_METAL2:
01113         case MAT_METAL3:
01114         case MAT_ELEC_METAL:// FIXME: maybe have its own sound?
01115                 trap_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.chunkSound );
01116                 bounce = LEBS_METAL;
01117                 speedMod = 0.8f; // metal blows up a bit more
01118                 break;
01119         case MAT_ROPE:
01120 //              trap_S_StartSound( NULL, owner, CHAN_BODY, cgi_S_RegisterSound( "" ));  FIXME:  needs a sound
01121                 return;
01122                 break;
01123         }
01124 
01125         if ( baseScale <= 0.0f )
01126         {
01127                 baseScale = 1.0f;
01128         }
01129 
01130         // Chunks
01131         for( i = 0; i < numChunks; i++ )
01132         {
01133                 if ( customChunk > 0 )
01134                 {
01135                         // Try to use a custom chunk.
01136                         if ( cgs.gameModels[customChunk] )
01137                         {
01138                                 chunk = qtrue;
01139                                 chunkModel = cgs.gameModels[customChunk];
01140                         }
01141                 }
01142 
01143                 if ( !chunk )
01144                 {
01145                         // No custom chunk.  Pick a random chunk type at run-time so we don't get the same chunks
01146                         switch( chunkType )
01147                         {
01148                         case MAT_METAL2: //bluegrey
01149                                 chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)];
01150                                 break;
01151                         case MAT_GREY_STONE://gray
01152                                 chunkModel = cgs.media.chunkModels[CHUNK_ROCK1][Q_irand(0, 3)];
01153                                 break;
01154                         case MAT_LT_STONE: //tan
01155                                 chunkModel = cgs.media.chunkModels[CHUNK_ROCK2][Q_irand(0, 3)];
01156                                 break;
01157                         case MAT_DRK_STONE://brown
01158                                 chunkModel = cgs.media.chunkModels[CHUNK_ROCK3][Q_irand(0, 3)];
01159                                 break;
01160                         case MAT_SNOWY_ROCK://gray & brown
01161                                 if ( Q_irand( 0, 1 ) )
01162                                 {
01163                                         chunkModel = cgs.media.chunkModels[CHUNK_ROCK1][Q_irand(0, 3)];
01164                                 }
01165                                 else
01166                                 {
01167                                         chunkModel = cgs.media.chunkModels[CHUNK_ROCK3][Q_irand(0, 3)];
01168                                 }
01169                                 break;
01170                         case MAT_WHITE_METAL:
01171                                 chunkModel = cgs.media.chunkModels[CHUNK_WHITE_METAL][Q_irand(0, 3)];
01172                                 break;
01173                         case MAT_CRATE1://yellow multi-colored crate chunks
01174                                 chunkModel = cgs.media.chunkModels[CHUNK_CRATE1][Q_irand(0, 3)];
01175                                 break;
01176                         case MAT_CRATE2://red multi-colored crate chunks
01177                                 chunkModel = cgs.media.chunkModels[CHUNK_CRATE2][Q_irand(0, 3)];
01178                                 break;
01179                         case MAT_ELEC_METAL:
01180                         case MAT_GLASS_METAL:
01181                         case MAT_METAL://grey
01182                                 chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)];
01183                                 break;
01184                         case MAT_METAL3:
01185                                 if ( rand() & 1 )
01186                                 {
01187                                         chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)];
01188                                 }
01189                                 else
01190                                 {
01191                                         chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)];
01192                                 }
01193                                 break;
01194                         }
01195                 }
01196 
01197                 // It wouldn't look good to throw a bunch of RGB axis models...so make sure we have something to work with.
01198                 if ( chunkModel )
01199                 {
01200                         le = CG_AllocLocalEntity();
01201                         re = &le->refEntity;
01202 
01203                         re->hModel = chunkModel;
01204                         le->leType = LE_FRAGMENT;
01205                         le->endTime = cg.time + 1300 + random() * 900;
01206 
01207                         // spawn chunk roughly in the bbox of the thing...bias towards center in case thing blowing up doesn't complete fill its bbox.
01208                         for( j = 0; j < 3; j++ )
01209                         {
01210                                 r = random() * 0.8f + 0.1f;
01211                                 re->origin[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] );
01212                         }
01213                         VectorCopy( re->origin, le->pos.trBase );
01214 
01215                         // Move out from center of thing, otherwise you can end up things moving across the brush in an undesirable direction.  Visually looks wrong
01216                         VectorSubtract( re->origin, origin, dir );
01217                         VectorNormalize( dir );
01218                         VectorScale( dir, flrand( speed * 0.5f, speed * 1.25f ) * speedMod, le->pos.trDelta );
01219 
01220                         // Angular Velocity
01221                         VectorSet( le->angles.trBase, random() * 360, random() * 360, random() * 360 );
01222 
01223                         le->angles.trDelta[0] = crandom();
01224                         le->angles.trDelta[1] = crandom();
01225                         le->angles.trDelta[2] = 0; // don't do roll
01226 
01227                         VectorScale( le->angles.trDelta, random() * 600.0f + 200.0f, le->angles.trDelta );
01228 
01229                         le->pos.trType = TR_GRAVITY;
01230                         le->angles.trType = TR_LINEAR;
01231                         le->pos.trTime = le->angles.trTime = cg.time;
01232                         le->bounceFactor = 0.2f + random() * 0.2f;
01233                         le->leFlags |= LEF_TUMBLE;
01234                         //le->ownerGentNum = owner;
01235                         le->leBounceSoundType = bounce; 
01236 
01237                         // Make sure that we have the desired start size set
01238                         le->radius = flrand( baseScale * 0.75f, baseScale * 1.25f );
01239                         re->nonNormalizedAxes = qtrue;
01240                         AxisCopy( axisDefault, re->axis ); // could do an angles to axis, but this is cheaper and works ok
01241                         for( k = 0; k < 3; k++ )
01242                         {
01243                                 re->modelScale[k] = le->radius;
01244                         }
01245                         ScaleModelAxis(re);
01246                         /*
01247                         for( k = 0; k < 3; k++ )
01248                         {
01249                                 VectorScale( re->axis[k], le->radius, re->axis[k] );
01250                         }
01251                         */
01252                 }
01253         }
01254 }
01255 
01256 /*
01257 ==================
01258 CG_ScorePlum
01259 ==================
01260 */
01261 void CG_ScorePlum( int client, vec3_t org, int score ) {
01262         localEntity_t   *le;
01263         refEntity_t             *re;
01264         vec3_t                  angles;
01265         static vec3_t lastPos;
01266 
01267         // only visualize for the client that scored
01268         if (client != cg.predictedPlayerState.clientNum || cg_scorePlum.integer == 0) {
01269                 return;
01270         }
01271 
01272         le = CG_AllocLocalEntity();
01273         le->leFlags = 0;
01274         le->leType = LE_SCOREPLUM;
01275         le->startTime = cg.time;
01276         le->endTime = cg.time + 4000;
01277         le->lifeRate = 1.0 / ( le->endTime - le->startTime );
01278 
01279         
01280         le->color[0] = le->color[1] = le->color[2] = le->color[3] = 1.0;
01281         le->radius = score;
01282         
01283         VectorCopy( org, le->pos.trBase );
01284         if (org[2] >= lastPos[2] - 20 && org[2] <= lastPos[2] + 20) {
01285                 le->pos.trBase[2] -= 20;
01286         }
01287 
01288         //CG_Printf( "Plum origin %i %i %i -- %i\n", (int)org[0], (int)org[1], (int)org[2], (int)Distance(org, lastPos));
01289         VectorCopy(org, lastPos);
01290 
01291 
01292         re = &le->refEntity;
01293 
01294         re->reType = RT_SPRITE;
01295         re->radius = 16;
01296 
01297         VectorClear(angles);
01298         AnglesToAxis( angles, re->axis );
01299 }
01300 
01301 /*
01302 ====================
01303 CG_MakeExplosion
01304 ====================
01305 */
01306 localEntity_t *CG_MakeExplosion( vec3_t origin, vec3_t dir, 
01307                                                                 qhandle_t hModel, int numFrames, qhandle_t shader,
01308                                                                 int msec, qboolean isSprite, float scale, int flags )
01309 {
01310         float                   ang = 0;
01311         localEntity_t   *ex;
01312         int                             offset;
01313         vec3_t                  tmpVec, newOrigin;
01314 
01315         if ( msec <= 0 ) {
01316                 CG_Error( "CG_MakeExplosion: msec = %i", msec );
01317         }
01318 
01319         // skew the time a bit so they aren't all in sync
01320         offset = rand() & 63;
01321 
01322         ex = CG_AllocLocalEntity();
01323         if ( isSprite ) {
01324                 ex->leType = LE_SPRITE_EXPLOSION; 
01325                 ex->refEntity.rotation = rand() % 360;
01326                 ex->radius = scale;
01327                 VectorScale( dir, 16, tmpVec );
01328                 VectorAdd( tmpVec, origin, newOrigin );
01329         } else {
01330                 ex->leType = LE_EXPLOSION;
01331                 VectorCopy( origin, newOrigin );
01332 
01333                 // set axis with random rotate when necessary
01334                 if ( !dir )
01335                 {
01336                         AxisClear( ex->refEntity.axis );
01337                 }
01338                 else
01339                 {
01340                         if ( !(flags & LEF_NO_RANDOM_ROTATE) )
01341                                 ang = rand() % 360;
01342                         VectorCopy( dir, ex->refEntity.axis[0] );
01343                         RotateAroundDirection( ex->refEntity.axis, ang );
01344                 }
01345         }
01346 
01347         ex->startTime = cg.time - offset;
01348         ex->endTime = ex->startTime + msec;
01349         
01350         // bias the time so all shader effects start correctly
01351         ex->refEntity.shaderTime = ex->startTime / 1000.0f;
01352 
01353         ex->refEntity.hModel = hModel;
01354         ex->refEntity.customShader = shader;
01355         ex->lifeRate = (float)numFrames / msec;
01356         ex->leFlags = flags;
01357 
01358         //Scale the explosion
01359         if (scale != 1) {
01360                 ex->refEntity.nonNormalizedAxes = qtrue;
01361 
01362                 VectorScale( ex->refEntity.axis[0], scale, ex->refEntity.axis[0] );
01363                 VectorScale( ex->refEntity.axis[1], scale, ex->refEntity.axis[1] );
01364                 VectorScale( ex->refEntity.axis[2], scale, ex->refEntity.axis[2] );
01365         }
01366         // set origin
01367         VectorCopy ( newOrigin, ex->refEntity.origin);
01368         VectorCopy ( newOrigin, ex->refEntity.oldorigin );
01369 
01370         ex->color[0] = ex->color[1] = ex->color[2] = 1.0;
01371 
01372         return ex;
01373 }
01374 
01375 
01376 /*
01377 -------------------------
01378 CG_SurfaceExplosion
01379 
01380 Adds an explosion to a surface
01381 -------------------------
01382 */
01383 
01384 #define NUM_SPARKS              12
01385 #define NUM_PUFFS               1
01386 #define NUM_EXPLOSIONS  4
01387 
01388 void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shake_speed, qboolean smoke )
01389 {
01390         localEntity_t   *le;
01391         //FXTrail                       *particle;
01392         vec3_t                  direction, new_org;
01393         vec3_t                  velocity                = { 0, 0, 0 };
01394         vec3_t                  temp_org, temp_vel;
01395         float                   scale, dscale;
01396         int                             i, numSparks;
01397 
01398         //Sparks
01399         numSparks = 16 + (random() * 16.0f);
01400         
01401         for ( i = 0; i < numSparks; i++ )
01402         {       
01403                 scale = 0.25f + (random() * 2.0f);
01404                 dscale = -scale*0.5;
01405 
01406 /*              particle = FX_AddTrail( origin,
01407                                                                 NULL,
01408                                                                 NULL,
01409                                                                 32.0f,
01410                                                                 -64.0f,
01411                                                                 scale,
01412                                                                 -scale,
01413                                                                 1.0f,
01414                                                                 0.0f,
01415                                                                 0.25f,
01416                                                                 4000.0f,
01417                                                                 cgs.media.sparkShader,
01418                                                                 rand() & FXF_BOUNCE);
01419                 if ( particle == NULL )
01420                         return;
01421 
01422                 FXE_Spray( normal, 500, 150, 1.0f, 768 + (rand() & 255), (FXPrimitive *) particle );*/
01423         }
01424 
01425         //Smoke
01426         //Move this out a little from the impact surface
01427         VectorMA( origin, 4, normal, new_org );
01428         VectorSet( velocity, 0.0f, 0.0f, 16.0f );
01429 
01430         for ( i = 0; i < 4; i++ )
01431         {
01432                 VectorSet( temp_org, new_org[0] + (crandom() * 16.0f), new_org[1] + (crandom() * 16.0f), new_org[2] + (random() * 4.0f) );
01433                 VectorSet( temp_vel, velocity[0] + (crandom() * 8.0f), velocity[1] + (crandom() * 8.0f), velocity[2] + (crandom() * 8.0f) );
01434 
01435 /*              FX_AddSprite(   temp_org,
01436                                                 temp_vel, 
01437                                                 NULL, 
01438                                                 64.0f + (random() * 32.0f), 
01439                                                 16.0f, 
01440                                                 1.0f, 
01441                                                 0.0f,
01442                                                 20.0f + (crandom() * 90.0f),
01443                                                 0.5f,
01444                                                 1500.0f, 
01445                                                 cgs.media.smokeShader, FXF_USE_ALPHA_CHAN );*/
01446         }
01447 
01448         //Core of the explosion
01449 
01450         //Orient the explosions to face the camera
01451         VectorSubtract( cg.refdef.vieworg, origin, direction );
01452         VectorNormalize( direction );
01453 
01454         //Tag the last one with a light
01455         le = CG_MakeExplosion( origin, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 500, qfalse, radius * 0.02f + (random() * 0.3f), 0);
01456         le->light = 150;
01457         VectorSet( le->lightColor, 0.9f, 0.8f, 0.5f );
01458 
01459         for ( i = 0; i < NUM_EXPLOSIONS-1; i ++)
01460         {
01461                 VectorSet( new_org, (origin[0] + (16 + (crandom() * 8))*crandom()), (origin[1] + (16 + (crandom() * 8))*crandom()), (origin[2] + (16 + (crandom() * 8))*crandom()) );
01462                 le = CG_MakeExplosion( new_org, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 300 + (rand() & 99), qfalse, radius * 0.05f + (crandom() *0.3f), 0);
01463         }
01464 
01465         //Shake the camera
01466         CG_ExplosionEffects( origin, shake_speed, 350, 750 );
01467 
01468         // The level designers wanted to be able to turn the smoke spawners off.  The rationale is that they
01469         //      want to blow up catwalks and such that fall down...when that happens, it shouldn't really leave a mark
01470         //      and a smoke spewer at the explosion point...
01471         if ( smoke )
01472         {
01473                 VectorMA( origin, -8, normal, temp_org );
01474 //              FX_AddSpawner( temp_org, normal, NULL, NULL, 100, random()*25.0f, 5000.0f, (void *) CG_SmokeSpawn );
01475 
01476                 //Impact mark
01477                 //FIXME: Replace mark
01478                 //CG_ImpactMark( cgs.media.burnMarkShader, origin, normal, random()*360, 1,1,1,1, qfalse, 8, qfalse );
01479         }
01480 }
01481 
01482 /*
01483 =================
01484 CG_Bleed
01485 
01486 This is the spurt of blood when a character gets hit
01487 =================
01488 */
01489 void CG_Bleed( vec3_t origin, int entityNum ) {
01490         localEntity_t   *ex;
01491 
01492         if ( !cg_blood.integer ) {
01493                 return;
01494         }
01495 
01496         ex = CG_AllocLocalEntity();
01497         ex->leType = LE_EXPLOSION;
01498 
01499         ex->startTime = cg.time;
01500         ex->endTime = ex->startTime + 500;
01501         
01502         VectorCopy ( origin, ex->refEntity.origin);
01503         ex->refEntity.reType = RT_SPRITE;
01504         ex->refEntity.rotation = rand() % 360;
01505         ex->refEntity.radius = 24;
01506 
01507         ex->refEntity.customShader = 0;//cgs.media.bloodExplosionShader;
01508 
01509         // don't show player's own blood in view
01510         if ( entityNum == cg.snap->ps.clientNum ) {
01511                 ex->refEntity.renderfx |= RF_THIRD_PERSON;
01512         }
01513 }
01514 
01515 
01516 
01517 /*
01518 ==================
01519 CG_LaunchGib
01520 ==================
01521 */
01522 void CG_LaunchGib( vec3_t origin, vec3_t velocity, qhandle_t hModel ) {
01523         localEntity_t   *le;
01524         refEntity_t             *re;
01525 
01526         le = CG_AllocLocalEntity();
01527         re = &le->refEntity;
01528 
01529         le->leType = LE_FRAGMENT;
01530         le->startTime = cg.time;
01531         le->endTime = le->startTime + 5000 + random() * 3000;
01532 
01533         VectorCopy( origin, re->origin );
01534         AxisCopy( axisDefault, re->axis );
01535         re->hModel = hModel;
01536 
01537         le->pos.trType = TR_GRAVITY;
01538         VectorCopy( origin, le->pos.trBase );
01539         VectorCopy( velocity, le->pos.trDelta );
01540         le->pos.trTime = cg.time;
01541 
01542         le->bounceFactor = 0.6f;
01543 
01544         le->leBounceSoundType = LEBS_BLOOD;
01545         le->leMarkType = LEMT_BLOOD;
01546 }