/* * CG_CartoonHitEffect */ void CG_CartoonHitEffect( const vec3_t origin, const vec3_t dir, int damage ) { lentity_t *le; int time = 6; float radius = 11.0f, alpha = cg_bloodTrailAlpha->value; struct shader_s *shader = CG_MediaShader( cgs.media.shaderCartoonHit ); struct shader_s *shader2 = CG_MediaShader( cgs.media.shaderCartoonHit2 ); struct shader_s *shader3 = CG_MediaShader( cgs.media.shaderCartoonHit3 ); vec3_t local_origin, local_dir; if( !cg_cartoonHitEffect->integer ) return; if ( damage < 39 ) return; if( !VectorLength( dir ) ) { VectorNegate( &cg.view.axis[AXIS_FORWARD], local_dir ); } else { VectorNormalize2( dir, local_dir ); } // Move effect a bit up from player VectorCopy( origin, local_origin ); local_origin[2] += 65; // small buff if ( damage < 64 ) { if( damage < 50 ) { // SPLITZOW! radius = 7.0f; le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, local_origin, radius, time, 1, 1, 1, alpha, 0, 0, 0, 0, shader2 ); } else { // POW! radius = 9.0f; le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, local_origin, radius, time, 1, 1, 1, alpha, 0, 0, 0, 0, shader ); } } else // big buff { // OUCH! le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, local_origin, radius, time, 1, 1, 1, alpha, 0, 0, 0, 0, shader3 ); } // randomize dir VectorSet( le->velocity, -local_dir[0] * 5 + crandom()*5, -local_dir[1] * 5 + crandom()*5, -local_dir[2] * 5 + crandom()*5 + 3); VectorMA( local_dir, 1, le->velocity, le->velocity ); }
void CG_Explosion_Puff( vec3_t pos, float radius, int frame ) { lentity_t *le; struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff1 ); switch( (int)floor( crandom()*3.0f ) ) { case 0: shader = CG_MediaShader( cgs.media.shaderSmokePuff1 ); break; case 1: shader = CG_MediaShader( cgs.media.shaderSmokePuff2 ); break; case 2: shader = CG_MediaShader( cgs.media.shaderSmokePuff3 ); break; } pos[0] += crandom()*4; pos[1] += crandom()*4; pos[2] += crandom()*4; le = CG_AllocSprite( LE_PUFF_SCALE_2, pos, radius, frame, 1.0f, 1.0f, 1.0f, 1.0f, 0, 0, 0, 0, shader ); le->ent.rotation = rand() % 360; }
/* * CG_ImpactSmokePuff */ void CG_ImpactSmokePuff( vec3_t origin, vec3_t dir, float radius, float alpha, int time, int speed ) { #define SMOKEPUFF_MAXVIEWDIST 700 lentity_t *le; struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff ); if( CG_PointContents( origin ) & MASK_WATER ) { return; } if( DistanceFast( origin, cg.view.origin ) * cg.view.fracDistFOV > SMOKEPUFF_MAXVIEWDIST ) return; if( !VectorLength( dir ) ) { VectorCopy( cg.view.axis[FORWARD], dir ); VectorInverse( dir ); } VectorNormalize( dir ); //offset the origin by half of the radius VectorMA( origin, radius*0.5f, dir, origin ); le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, origin, radius + crandom(), time, 1, 1, 1, alpha, 0, 0, 0, 0, shader ); le->ent.rotation = rand() % 360; VectorScale( dir, speed, le->velocity ); }
/* * CG_BubbleTrail */ void CG_BubbleTrail( vec3_t start, vec3_t end, int dist ) { int i; float len; vec3_t move, vec; lentity_t *le; struct shader_s *shader; VectorCopy( start, move ); VectorSubtract( end, start, vec ); len = VectorNormalize( vec ); if( !len ) return; VectorScale( vec, dist, vec ); shader = CG_MediaShader( cgs.media.shaderWaterBubble ); for( i = 0; i < len; i += dist ) { le = CG_AllocSprite( LE_ALPHA_FADE, move, 3, 10, 1, 1, 1, 1, 0, 0, 0, 0, shader ); VectorSet( le->velocity, crandom()*5, crandom()*5, crandom()*5 + 6 ); VectorAdd( move, vec, move ); } }
/* * CG_ElectroRings */ static void CG_ElectroRings( const vec3_t start, const vec3_t end, const vec4_t color ) { vec3_t dir, origin; int i, numrings; float len; float timeFrac; float space = 15.0f; lentity_t *le; struct shader_s *s = CG_MediaShader(cgs.media.shaderElectroBeamRing); VectorSubtract(end, start, dir); len = VectorNormalize(dir); if (!len) return; numrings = len / space + 1; timeFrac = 0.6f / (float)numrings; for (i = 0; i < numrings; i++) { float t = ( (float)i * timeFrac + 7.5f + (i * 0.20f) ) * (float)cg_ebbeam_time->value; float l = i * space; VectorMA(start, l, dir, origin); le = CG_AllocSprite(LE_ALPHA_FADE, origin, 4.25f, t, color[0], color[1], color[2], color[3], 0, 0, 0, 0, s); le->ent.rotation = rand() % 360; } }
void CG_Explosion_Puff_2( vec3_t pos, vec3_t vel, int radius ) { lentity_t *le; struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff3 ); /* switch( (int)floor( crandom()*3.0f ) ) { case 0: shader = CG_MediaShader( cgs.media.shaderSmokePuff1 ); break; case 1: shader = CG_MediaShader( cgs.media.shaderSmokePuff2 ); break; case 2: shader = CG_MediaShader( cgs.media.shaderSmokePuff3 ); break; } */ if( radius == 0 ) radius = (int)floor( 35.0f + crandom()*5 ); le = CG_AllocSprite( LE_PUFF_SHRINK, pos, radius, 7, 1.0f, 1.0f, 1.0f, 1.0f, 0, 0, 0, 0, shader ); VectorCopy( vel, le->velocity ); //le->ent.rotation = rand () % 360; }
/* * CG_ProjectileTrail */ void CG_ProjectileTrail( centity_t *cent ) { lentity_t *le; float len; vec3_t vec; int contents; int trailTime; float radius = 6.5f, alpha = 0.35f; #if 0 struct shader_s *shader = CG_MediaShader( cgs.media.shaderRocketTrailSmokePuff ); #else struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff ); #endif CG_ProjectileFireTrail( cent ); // add fire trail if( !cg_projectileTrail->integer ) return; // didn't move VectorSubtract( cent->ent.origin, cent->trailOrigin, vec ); len = VectorNormalize( vec ); if( !len ) return; // density is found by quantity per second trailTime = (int)(1000.0f / cg_projectileTrail->value ); if( trailTime < 1 ) trailTime = 1; // we don't add more than one sprite each frame. If frame // ratio is too slow, people will prefer having less sprites on screen if( cent->localEffects[LOCALEFFECT_ROCKETTRAIL_LAST_DROP] + trailTime < cg.time ) { cent->localEffects[LOCALEFFECT_ROCKETTRAIL_LAST_DROP] = cg.time; contents = ( CG_PointContents( cent->trailOrigin ) & CG_PointContents( cent->ent.origin ) ); if( contents & MASK_WATER ) { shader = CG_MediaShader( cgs.media.shaderWaterBubble ); radius = 3 + crandom(); alpha = 1.0f; } // racesow if( cg_raceGhosts->integer && (unsigned int)cent->current.ownerNum != cg.predictedPlayerState.POVnum ) alpha *= cg_raceGhostsAlpha->value; // !racesow clamp( alpha, 0.0f, 1.0f ); le = CG_AllocSprite( LE_PUFF_SHRINK, cent->trailOrigin, radius, 20, 1.0f, 1.0f, 1.0f, alpha, 0, 0, 0, 0, shader ); VectorSet( le->velocity, -vec[0] * 5 + crandom()*5, -vec[1] * 5 + crandom()*5, -vec[2] * 5 + crandom()*5 + 3 ); le->ent.rotation = rand () % 360; } }
/* * CG_PModel_SpawnTeleportEffect */ void CG_PModel_SpawnTeleportEffect( centity_t *cent ) { // the thing is, we must have a built skeleton, so we // can run all bones and spawn a polygon at their origin int i, j; cgs_skeleton_t *skel; orientation_t orient, ref; float radius = 5; lentity_t *le; vec3_t vec, teleportOrigin; skel = CG_SkeletonForModel( cent->ent.model ); if( !skel || !cent->ent.boneposes ) return; for( j = LOCALEFFECT_EV_PLAYER_TELEPORT_IN; j <= LOCALEFFECT_EV_PLAYER_TELEPORT_OUT; j++ ) { if( cent->localEffects[j] ) { cent->localEffects[j] = 0; if( j == LOCALEFFECT_EV_PLAYER_TELEPORT_OUT ) VectorCopy( cent->teleportedFrom, teleportOrigin ); else VectorCopy( cent->ent.origin, teleportOrigin ); for( i = 0; i < skel->numBones; i++ ) { DualQuat_ToMatrixAndVector( cent->ent.boneposes[i].dualquat, orient.axis, orient.origin ); VectorCopy( vec3_origin, ref.origin ); Matrix_Copy( axis_identity, ref.axis ); CG_MoveToTag( ref.origin, ref.axis, teleportOrigin, cent->ent.axis, orient.origin, orient.axis ); VectorSet( vec, 0.1f, 0.1f, 0.1f ); // spawn a sprite at each bone le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, ref.origin, radius, 15 + crandom()*5, 1, 1, 1, 0.5f, 0, 0, 0, 0, CG_MediaShader( cgs.media.shaderTeleporterSmokePuff ) ); VectorSet( le->velocity, -vec[0] * 5 + crandom()*5, -vec[1] * 5 + crandom()*5, -vec[2] * 5 + crandom()*5 + 3 ); le->ent.rotation = rand() % 360; // CG_ParticleEffect( ref.origin, ref.axis[2], 0.9f, 0.9f, 0.9f, 2 ); } } } }
/* * CG_NewBloodTrail */ void CG_NewBloodTrail( centity_t *cent ) { lentity_t *le; float len; vec3_t vec; int contents; int trailTime; float radius = 2.5f, alpha = cg_bloodTrailAlpha->value; struct shader_s *shader = CG_MediaShader( cgs.media.shaderBloodTrailPuff ); if( !cg_showBloodTrail->integer ) return; if( !cg_bloodTrail->integer ) return; // didn't move VectorSubtract( cent->ent.origin, cent->trailOrigin, vec ); len = VectorNormalize( vec ); if( !len ) return; // density is found by quantity per second trailTime = (int)( 1000.0f / cg_bloodTrail->value ); if( trailTime < 1 ) trailTime = 1; // we don't add more than one sprite each frame. If frame // ratio is too slow, people will prefer having less sprites on screen if( cent->localEffects[LOCALEFFECT_BLOODTRAIL_LAST_DROP] + trailTime < cg.time ) { cent->localEffects[LOCALEFFECT_BLOODTRAIL_LAST_DROP] = cg.time; contents = ( CG_PointContents( cent->trailOrigin ) & CG_PointContents( cent->ent.origin ) ); if( contents & MASK_WATER ) { shader = CG_MediaShader( cgs.media.shaderBloodTrailLiquidPuff ); radius = 4 + crandom(); alpha = 0.5f * cg_bloodTrailAlpha->value; } clamp( alpha, 0.0f, 1.0f ); le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, cent->trailOrigin, radius, 8, 1.0f, 1.0f, 1.0f, alpha, 0, 0, 0, 0, shader ); VectorSet( le->velocity, -vec[0] * 5 + crandom()*5, -vec[1] * 5 + crandom()*5, -vec[2] * 5 + crandom()*5 + 3 ); le->ent.rotation = rand() % 360; } }
/* * CG_BloodDamageEffect */ void CG_BloodDamageEffect( const vec3_t origin, const vec3_t dir, int damage ) { lentity_t *le; int count, i; float radius = 5.0f, alpha = cg_bloodTrailAlpha->value; int time = 8; struct shader_s *shader = CG_MediaShader( cgs.media.shaderBloodImpactPuff ); vec3_t local_dir; if( !cg_showBloodTrail->integer ) return; if( !cg_bloodTrail->integer ) return; count = (int)( damage * 0.25f ); clamp( count, 1, 10 ); if( CG_PointContents( origin ) & MASK_WATER ) { shader = CG_MediaShader( cgs.media.shaderBloodTrailLiquidPuff ); radius += ( 1 + crandom() ); alpha = 0.5f * cg_bloodTrailAlpha->value; } if( !VectorLength( dir ) ) { VectorNegate( &cg.view.axis[AXIS_FORWARD], local_dir ); } else { VectorNormalize2( dir, local_dir ); } for( i = 0; i < count; i++ ) { le = CG_AllocSprite( LE_PUFF_SHRINK, origin, radius + crandom(), time, 1, 1, 1, alpha, 0, 0, 0, 0, shader ); le->ent.rotation = rand() % 360; // randomize dir VectorSet( le->velocity, -local_dir[0] * 5 + crandom()*5, -local_dir[1] * 5 + crandom()*5, -local_dir[2] * 5 + crandom()*5 + 3 ); VectorMA( local_dir, min( 6, count ), le->velocity, le->velocity ); } }
static void CG_ProjectileFireTrail( centity_t *cent ) { lentity_t *le; float len; vec3_t vec; int trailTime; float radius = 8, alpha = cg_projectileFireTrailAlpha->value; struct shader_s *shader; if( !cg_projectileFireTrail->integer ) return; // didn't move VectorSubtract( cent->ent.origin, cent->trailOrigin, vec ); len = VectorNormalize( vec ); if( !len ) return; if( cent->effects & EF_STRONG_WEAPON ) shader = CG_MediaShader( cgs.media.shaderStrongRocketFireTrailPuff ); else shader = CG_MediaShader( cgs.media.shaderWeakRocketFireTrailPuff ); // density is found by quantity per second trailTime = (int)( 1000.0f / cg_projectileFireTrail->value ); if( trailTime < 1 ) trailTime = 1; // we don't add more than one sprite each frame. If frame // ratio is too slow, people will prefer having less sprites on screen if( cent->localEffects[LOCALEFFECT_ROCKETFIRE_LAST_DROP] + trailTime < cg.time ) { cent->localEffects[LOCALEFFECT_ROCKETFIRE_LAST_DROP] = cg.time; // racesow if( cg_raceGhosts->integer && (unsigned int)cent->current.ownerNum != cg.predictedPlayerState.POVnum ) alpha *= cg_raceGhostsAlpha->value; // !racesow clamp( alpha, 0.0f, 1.0f ); le = CG_AllocSprite( LE_INVERSESCALE_ALPHA_FADE, cent->trailOrigin, radius, 4, 1.0f, 1.0f, 1.0f, alpha, 0, 0, 0, 0, shader ); VectorSet( le->velocity, -vec[0] * 10 + crandom()*5, -vec[1] * 10 + crandom()*5, -vec[2] * 10 + crandom()*5 ); le->ent.rotation = rand() % 360; } }
void CG_SpawnTracer( vec3_t origin, vec3_t dir, vec3_t dir_per1, vec3_t dir_per2 ) { lentity_t *tracer; vec3_t dir_temp; VectorMA( dir, crandom()*2, dir_per1, dir_temp ); VectorMA( dir_temp, crandom()*2, dir_per2, dir_temp ); VectorScale( dir_temp, VectorNormalize( dir_temp ), dir_temp ); VectorScale( dir_temp, random()*400 + 420, dir_temp ); tracer = CG_AllocSprite( LE_EXPLOSION_TRACER, origin, 30, 7, 1, 1, 1, 1, 0, 0, 0, 0, CG_MediaShader( cgs.media.shaderSmokePuff3 ) ); VectorCopy( dir_temp, tracer->velocity ); VectorSet( tracer->accel, -0.2f, -0.2f, -9.8f*170 ); tracer->bounce = 500; tracer->ent.rotation = cg.time; }
/* * CG_FlagFlareTrail */ void CG_FlagTrail( vec3_t origin, vec3_t start, vec3_t end, float r, float g, float b ) { lentity_t *le; float len, mass = 20; vec3_t dir; VectorSubtract( end, start, dir ); len = VectorNormalize( dir ); if( !len ) return; le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, origin, 8, 50 + 50*random(), r, g, b, 0.7f, 0, 0, 0, 0, CG_MediaShader( cgs.media.shaderTeleporterSmokePuff ) ); VectorSet( le->velocity, -dir[0] * 5 + crandom()*5, -dir[1] * 5 + crandom()*5, -dir[2] * 5 + crandom()*5 + 3 ); le->ent.rotation = rand() % 360; //friction and gravity VectorSet( le->accel, -0.2f, -0.2f, -9.8f * mass ); le->bounce = 20; }
void CG_ExplosionsDust( vec3_t pos, vec3_t dir, float radius ) { const int count = 32; /* Number of sprites used to create the circle */ lentity_t *le; struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff3 ); vec3_t dir_per1; vec3_t dir_per2; vec3_t dir_temp = { 0.0f, 0.0f, 0.0f }; int i; float angle; if( CG_PointContents( pos ) & MASK_WATER ) return; // no smoke under water :) PerpendicularVector( dir_per2, dir ); CrossProduct( dir, dir_per2, dir_per1 ); //VectorScale( dir_per1, VectorNormalize( dir_per1 ), dir_per1 ); //VectorScale( dir_per2, VectorNormalize( dir_per2 ), dir_per2 ); // make a circle out of the specified number (int count) of sprites for( i = 0; i < count; i++ ) { angle = (float)( 6.2831f / count * i ); VectorSet( dir_temp, 0.0f, 0.0f, 0.0f ); VectorMA( dir_temp, sin( angle ), dir_per1, dir_temp ); VectorMA( dir_temp, cos( angle ), dir_per2, dir_temp ); //VectorScale(dir_temp, VectorNormalize(dir_temp),dir_temp ); VectorScale( dir_temp, crandom()*8 + radius + 16.0f, dir_temp ); // make the sprite smaller & alpha'd le = CG_AllocSprite( LE_ALPHA_FADE, pos, 10, 10, 1.0f, 1.0f, 1.0f, 1.0f, 0, 0, 0, 0, shader ); VectorCopy( dir_temp, le->velocity ); } }
/* * CG_GenericExplosion */ void CG_GenericExplosion( vec3_t pos, vec3_t dir, int fire_mode, float radius ) { lentity_t *le; vec3_t angles; vec3_t decaldir; vec3_t origin, vec; float expvelocity = 8.0f; VectorCopy( dir, decaldir ); VecToAngles( dir, angles ); //if( CG_PointContents( pos ) & MASK_WATER ) //jalfixme: (shouldn't we do the water sound variation?) if( fire_mode == FIRE_MODE_STRONG ) CG_SpawnDecal( pos, decaldir, random()*360, radius * 0.5, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderExplosionMark ) ); else CG_SpawnDecal( pos, decaldir, random()*360, radius * 0.25, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderExplosionMark ) ); // animmap shader of the explosion VectorMA( pos, radius*0.15f, dir, origin ); le = CG_AllocSprite( LE_ALPHA_FADE, origin, radius * 0.5f, 8, 1, 1, 1, 1, radius*4, 0.75f, 0.533f, 0, // yellow dlight CG_MediaShader( cgs.media.shaderRocketExplosion ) ); VectorSet( vec, crandom()*expvelocity, crandom()*expvelocity, crandom()*expvelocity ); VectorScale( dir, expvelocity, le->velocity ); VectorAdd( le->velocity, vec, le->velocity ); le->ent.rotation = rand() % 360; // use the rocket explosion sounds if( fire_mode == FIRE_MODE_STRONG ) trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxRocketLauncherStrongHit ), pos, CHAN_AUTO, cg_volume_effects->value, ATTN_DISTANT ); else trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxRocketLauncherWeakHit ), pos, CHAN_AUTO, cg_volume_effects->value, ATTN_DISTANT ); }
void CG_SpawnSprite( vec3_t origin, vec3_t velocity, vec3_t accel, float radius, int time, int bounce, qboolean expandEffect, qboolean shrinkEffect, float r, float g, float b, float a, float light, float lr, float lg, float lb, struct shader_s *shader ) { lentity_t *le; int numFrames; int type = LE_ALPHA_FADE; if( !radius || !shader || !origin ) return; numFrames = (int)( (float)( time * 1000 ) * 0.01f ); if( !numFrames ) return; if( expandEffect && !shrinkEffect ) type = LE_SCALE_ALPHA_FADE; if( !expandEffect && shrinkEffect ) type = LE_INVERSESCALE_ALPHA_FADE; le = CG_AllocSprite( type, origin, radius, numFrames, r, g, b, a, light, lr, lg, lb, shader ); if( velocity != NULL ) VectorCopy( velocity, le->velocity ); if( accel != NULL ) VectorCopy( accel, le->accel ); le->bounce = bounce; le->ent.rotation = rand() % 360; }
/* * CG_RocketExplosionMode */ void CG_RocketExplosionMode( vec3_t pos, vec3_t dir, int fire_mode, float radius ) { lentity_t *le; vec3_t angles, vec; vec3_t origin; float expvelocity = 8.0f; VecToAngles( dir, angles ); if( fire_mode == FIRE_MODE_STRONG ) { //trap_S_StartSound ( pos, 0, 0, CG_MediaSfx (cgs.media.sfxRocketLauncherStrongHit), cg_volume_effects->value, ATTN_NORM, 0 ); CG_SpawnDecal( pos, dir, random()*360, radius * 0.5, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderExplosionMark ) ); } else { //trap_S_StartSound ( pos, 0, 0, CG_MediaSfx (cgs.media.sfxRocketLauncherWeakHit), cg_volume_effects->value, ATTN_NORM, 0 ); CG_SpawnDecal( pos, dir, random()*360, radius * 0.25, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderExplosionMark ) ); } // animmap shader of the explosion VectorMA( pos, radius*0.12f, dir, origin ); le = CG_AllocSprite( LE_ALPHA_FADE, origin, radius * 0.5f, 8, 1, 1, 1, 1, radius*4, 0.75f, 0.533f, 0, // yellow dlight CG_MediaShader( cgs.media.shaderRocketExplosion ) ); VectorSet( vec, crandom()*expvelocity, crandom()*expvelocity, crandom()*expvelocity ); VectorScale( dir, expvelocity, le->velocity ); VectorAdd( le->velocity, vec, le->velocity ); le->ent.rotation = rand() % 360; if( cg_explosionsRing->integer ) { // explosion ring sprite VectorMA( pos, radius*0.20f, dir, origin ); le = CG_AllocSprite( LE_ALPHA_FADE, origin, radius, 3, 1, 1, 1, 1, 0, 0, 0, 0, // no dlight CG_MediaShader( cgs.media.shaderRocketExplosionRing ) ); le->ent.rotation = rand() % 360; } if( cg_explosionsDust->integer == 1 ) { // dust ring parallel to the contact surface CG_ExplosionsDust(pos, dir, radius); } // Explosion particles CG_ParticleExplosionEffect( pos, dir, 1, 0.5, 0, 32 ); if( fire_mode == FIRE_MODE_STRONG ) trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxRocketLauncherStrongHit ), pos, CHAN_AUTO, cg_volume_effects->value, ATTN_DISTANT ); else trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxRocketLauncherWeakHit ), pos, CHAN_AUTO, cg_volume_effects->value, ATTN_DISTANT ); //jalfixme: add sound at water? }
/* * CG_GrenadeExplosionMode */ void CG_GrenadeExplosionMode( vec3_t pos, vec3_t dir, int fire_mode, float radius ) { lentity_t *le; vec3_t angles; vec3_t decaldir; vec3_t origin, vec; float expvelocity = 8.0f; VectorCopy( dir, decaldir ); VecToAngles( dir, angles ); //if( CG_PointContents( pos ) & MASK_WATER ) //jalfixme: (shouldn't we do the water sound variation?) if( fire_mode == FIRE_MODE_STRONG ) { CG_SpawnDecal( pos, decaldir, random()*360, radius * 0.5, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderExplosionMark ) ); } else { CG_SpawnDecal( pos, decaldir, random()*360, radius * 0.25, 1, 1, 1, 1, 10, 1, qfalse, CG_MediaShader( cgs.media.shaderExplosionMark ) ); } // animmap shader of the explosion VectorMA( pos, radius*0.15f, dir, origin ); le = CG_AllocSprite( LE_ALPHA_FADE, origin, radius * 0.5f, 8, 1, 1, 1, 1, radius*4, 0.75f, 0.533f, 0, // yellow dlight CG_MediaShader( cgs.media.shaderRocketExplosion ) ); VectorSet( vec, crandom()*expvelocity, crandom()*expvelocity, crandom()*expvelocity ); VectorScale( dir, expvelocity, le->velocity ); VectorAdd( le->velocity, vec, le->velocity ); le->ent.rotation = rand() % 360; // explosion ring sprite if( cg_explosionsRing->integer ) { VectorMA( pos, radius*0.25f, dir, origin ); le = CG_AllocSprite( LE_ALPHA_FADE, origin, radius, 3, 1, 1, 1, 1, 0, 0, 0, 0, // no dlight CG_MediaShader( cgs.media.shaderRocketExplosionRing ) ); le->ent.rotation = rand() % 360; } if( cg_explosionsDust->integer == 1 ) { // dust ring parallel to the contact surface CG_ExplosionsDust(pos, dir, radius); } // Explosion particles CG_ParticleExplosionEffect( pos, dir, 1, 0.5, 0, 32 ); if( fire_mode == FIRE_MODE_STRONG ) trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxGrenadeStrongExplosion ), pos, CHAN_AUTO, cg_volume_effects->value, ATTN_DISTANT ); else trap_S_StartFixedSound( CG_MediaSfx( cgs.media.sfxGrenadeWeakExplosion ), pos, CHAN_AUTO, cg_volume_effects->value, ATTN_DISTANT ); }