void FX_GrenadeShrapnelExplode( vec3_t origin, vec3_t norm ) { localEntity_t *le; FXTrail *fx; vec3_t direction, org; //Orient the explosions to face the camera VectorSubtract( cg.refdef.vieworg, origin, direction ); VectorNormalize( direction ); VectorMA( origin, 12, direction, org ); // Add an explosion and tag a light to it le = CG_MakeExplosion( org, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 700, qfalse, 1.2f + (random()*0.3f) ); le->light = 150; le->refEntity.renderfx |= RF_NOSHADOW; VectorSet( le->lightColor, 1.0f, 0.6f, 0.6f ); for ( int i = 0; i < 6; i++) { fx = FX_AddTrail( origin, NULL, NULL, 16.0f, -15.0f, 1.5, -1.5, 1.0f, 1.0f, 0.2f, 1000.0f, cgs.media.orangeTrailShader, rand() & FXF_BOUNCE ); if ( fx == NULL ) return; FXE_Spray( norm, 500, 175, 0.8f, 512, (FXPrimitive *) fx ); } cgi_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.grenadeAltExplodeSnd ); CG_ImpactMark( cgs.media.compressionMarkShader, origin, norm, random()*360, 1,1,1,1.0, qfalse, random() * 16 + 48, qfalse ); CG_ExplosionEffects( origin, 2.0, 350 ); }
/* ================ CG_AddFireEffect ================ */ static void CG_AddFireEffect( localEntity_t *le ) { refEntity_t *re; trace_t trace; re = &le->refEntity; if ( le->pos.trType == TR_STATIONARY ) { if ( le->leFlags & LEF_LESSOVERDRAW ) { // avoid too much overdraw if ( CG_NearbyDrawnLeCount( le, re->origin, le->leType, 52 + !!cg_lowDetailEffects.integer * 24 ) > 0 ) return; } // add to refresh list trap_R_AddRefEntityToScene( re ); return; } // calculate position BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // check for water if ( trap_CM_PointContents( re->origin, 0 ) & CONTENTS_WATER ) { // do a trace to get water surface normals CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_WATER ); CG_FreeLocalEntity( le ); CG_MakeExplosion( trace.endpos, trace.plane.normal, cgs.media.ringFlashModel, cgs.media.vaporShader, 500, qfalse, qtrue ); return; } // do a trace sometimes if ( le->ti.trailTime++ > 5 ) { le->ti.trailTime = 0; CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT ); VectorCopy( trace.endpos, re->origin ); VectorCopy( trace.endpos, re->oldorigin ); // hit something if ( trace.fraction < 1.0 ) { // free le if another one is nearby, otherwise make it stationary if ( CG_CheckDistance( re->origin, le->leType, TR_STATIONARY, re->customShader, 200, 32 ) ) { CG_FreeLocalEntity( le ); return; } else { le->pos.trType = TR_STATIONARY; } } } if ( le->leFlags & LEF_LESSOVERDRAW ) { // avoid too much overdraw if ( CG_NearbyDrawnLeCount( le, re->origin, le->leType, 52 + !!cg_lowDetailEffects.integer * 24 ) > 0 ) return; } // add to refresh list trap_R_AddRefEntityToScene( re ); }
/* ================== CG_ObeliskExplode ================== */ void CG_ObeliskExplode( vec3_t org, int entityNum ) { localEntity_t *le; vec3_t origin; // create an explosion VectorCopy( org, origin ); origin[2] += 64; le = CG_MakeExplosion( origin, vec3_origin, cgs.media.dishFlashModel, cgs.media.rocketExplosionShader, 600, qtrue ); le->light = 300; le->lightColor[0] = 1; le->lightColor[1] = 0.75; le->lightColor[2] = 0.0; }
void FX_GrenadeExplode( vec3_t origin, vec3_t normal ) { localEntity_t *le; FXTrail *fx; vec3_t direction, org; VectorSet( direction, 0,0,1 ); // Add an explosion and tag a light to it le = CG_MakeExplosion( origin, direction, cgs.media.nukeModel, 5, NULL, 250, qfalse, 25.0f, LEF_FADE_RGB ); le->light = 150; le->refEntity.renderfx |= RF_NOSHADOW; VectorSet( le->lightColor, 1.0f, 0.6f, 0.2f ); // Ground ring FX_AddQuad( origin, normal, NULL, NULL, 5, 330, 1.0, 0.0, random() * 360, 0, 0, 300, cgs.media.bigShockShader ); // Flare VectorMA( origin, 12, direction, org ); FX_AddSprite( org, NULL, NULL, 160.0, -540.0, 1.0, 0.0, 0.0, 0.0, 200, cgs.media.sunnyFlareShader ); for ( int i = 0; i < 12; i++) { fx = FX_AddTrail( origin, NULL, NULL, 24.0f + random() * 12, -40.0f, 0.5 + random() * 2, -3.0, 1.0f, 1.0f, 0.5f, 1000.0f, cgs.media.orangeTrailShader, rand() & FXF_BOUNCE ); if ( fx == NULL ) return; FXE_Spray( normal, 470, 325, 0.5f, 700, (FXPrimitive *) fx ); } cgi_S_StartSound( origin, ENTITYNUM_WORLD, CHAN_AUTO, cgs.media.grenadeExplodeSnd ); // Smoke and impact CG_ImpactMark( cgs.media.compressionMarkShader, origin, normal, random()*360, 1,1,1,1.0, qfalse, random() * 16 + 48, qfalse ); CG_ExplosionEffects( origin, 3.0f, 400 ); }
void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shake_speed, qboolean smoke ) { localEntity_t *le; //FXTrail *particle; vec3_t direction, new_org; vec3_t velocity = { 0, 0, 0 }; vec3_t temp_org, temp_vel; float scale, dscale; int i, numSparks; //Sparks numSparks = 16 + (random() * 16.0f); for ( i = 0; i < numSparks; i++ ) { scale = 0.25f + (random() * 2.0f); dscale = -scale*0.5; /* particle = FX_AddTrail( origin, NULL, NULL, 32.0f, -64.0f, scale, -scale, 1.0f, 0.0f, 0.25f, 4000.0f, cgs.media.sparkShader, rand() & FXF_BOUNCE); if ( particle == NULL ) return; FXE_Spray( normal, 500, 150, 1.0f, 768 + (rand() & 255), (FXPrimitive *) particle );*/ } //Smoke //Move this out a little from the impact surface VectorMA( origin, 4, normal, new_org ); VectorSet( velocity, 0.0f, 0.0f, 16.0f ); for ( i = 0; i < 4; i++ ) { VectorSet( temp_org, new_org[0] + (crandom() * 16.0f), new_org[1] + (crandom() * 16.0f), new_org[2] + (random() * 4.0f) ); VectorSet( temp_vel, velocity[0] + (crandom() * 8.0f), velocity[1] + (crandom() * 8.0f), velocity[2] + (crandom() * 8.0f) ); /* FX_AddSprite( temp_org, temp_vel, NULL, 64.0f + (random() * 32.0f), 16.0f, 1.0f, 0.0f, 20.0f + (crandom() * 90.0f), 0.5f, 1500.0f, cgs.media.smokeShader, FXF_USE_ALPHA_CHAN );*/ } //Core of the explosion //Orient the explosions to face the camera VectorSubtract( cg.refdef.vieworg, origin, direction ); VectorNormalize( direction ); //Tag the last one with a light le = CG_MakeExplosion( origin, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 500, qfalse, radius * 0.02f + (random() * 0.3f), 0); le->light = 150; VectorSet( le->lightColor, 0.9f, 0.8f, 0.5f ); for ( i = 0; i < NUM_EXPLOSIONS-1; i ++) { VectorSet( new_org, (origin[0] + (16 + (crandom() * 8))*crandom()), (origin[1] + (16 + (crandom() * 8))*crandom()), (origin[2] + (16 + (crandom() * 8))*crandom()) ); le = CG_MakeExplosion( new_org, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 300 + (rand() & 99), qfalse, radius * 0.05f + (crandom() *0.3f), 0); } //Shake the camera CG_ExplosionEffects( origin, shake_speed, 350, 750 ); // The level designers wanted to be able to turn the smoke spawners off. The rationale is that they // want to blow up catwalks and such that fall down...when that happens, it shouldn't really leave a mark // and a smoke spewer at the explosion point... if ( smoke ) { VectorMA( origin, -8, normal, temp_org ); // FX_AddSpawner( temp_org, normal, NULL, NULL, 100, random()*25.0f, 5000.0f, (void *) CG_SmokeSpawn ); //Impact mark //FIXME: Replace mark //CG_ImpactMark( cgs.media.burnMarkShader, origin, normal, random()*360, 1,1,1,1, qfalse, 8, qfalse ); } }
/* ================ CG_AddMissile ================ */ static void CG_AddMissile( localEntity_t *le ) { refEntity_t *re; const weaponInfo_t *weapon; trace_t trace; centity_t *other; qboolean inWater; // just existing for server entity deletion if ( le->leFlags & LEF_FINISHED ) { return; } // get weapon info if ( le->ti.weapon > WP_NUM_WEAPONS ) { le->ti.weapon = 0; } weapon = &cg_weapons[le->ti.weapon]; re = &le->refEntity; // calculate position BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // special case for flames if ( re->reType == RT_SPRITE ) { int deltaTime; // check for water if ( trap_CM_PointContents( re->origin, 0 ) & CONTENTS_WATER ) { // do a trace to get water surface normals CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_WATER ); CG_FreeLocalEntity( le ); CG_MakeExplosion( trace.endpos, trace.plane.normal, cgs.media.ringFlashModel, cgs.media.vaporShader, 500, qfalse, qtrue ); return; } // change radius over time deltaTime = cg.time - le->startTime; re->radius = deltaTime * deltaTime * ( random()*0.4f + 0.8f ) / 2000.0f + 9; // do a trace sometimes if ( le->ti.trailTime++ > 5 ) { le->ti.trailTime = 0; CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT ); VectorCopy( re->origin, re->oldorigin ); // hit something if ( trace.fraction < 1.0 ) { CG_MissileHitWall( le->ti.weapon, 0, trace.endpos, trace.plane.normal, IMPACTSOUND_DEFAULT ); CG_FreeLocalEntity( le ); return; } } // add to refresh list trap_R_AddRefEntityToScene( re ); return; } // add trails if ( weapon->missileTrailFunc ) weapon->missileTrailFunc( &le->ti, cg.time ); // add dynamic light if ( weapon->missileDlight ) { trap_R_AddLightToScene( re->origin, weapon->missileDlight, weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] ); } // flicker between two skins re->skinNum = cg.clientFrame & 1; // convert direction of travel into axis if ( VectorNormalize2( le->pos.trDelta, re->axis[0] ) == 0 ) { re->axis[0][2] = 1; } // spin as it moves if ( le->pos.trType != TR_STATIONARY ) { if ( le->pos.trType == TR_GRAVITY ) { RotateAroundDirection( re->axis, cg.time / 4 ); } else if ( le->pos.trType == TR_WATER_GRAVITY ) { RotateAroundDirection( re->axis, cg.time / 8 ); } else { RotateAroundDirection( re->axis, cg.time ); } } else { RotateAroundDirection( re->axis, 0 ); } // trace a line from previous position to new position CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT ); VectorCopy( re->origin, re->oldorigin ); // draw BIG grenades if ( weLi[le->ti.weapon].category == CT_EXPLOSIVE ) { AxisScale( re->axis, GRENADE_SCALE, re->axis ); } if ( trace.fraction != 1.0 ) { // hit the sky or something like that if ( trace.surfaceFlags & SURF_NOIMPACT ) { le->leFlags |= LEF_FINISHED; le->endTime = cg.time + 500; return; } // impact other = &cg_entities[trace.entityNum]; if ( le->bounceFactor > 0 && ( le->bounceFactor == BOUNCE_FACTOR_HALF || other->currentState.eType != ET_PLAYER ) ) { // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); if ( cg.predictedImpacts < MAX_PREDICTED_IMPACTS ) { cg.predictedImpacts++; cg.predictedImpactsDecTime = cg.time; } // do bounce sound if ( rand() & 1 ) { trap_S_StartSound( le->pos.trBase, 0, CHAN_AUTO, cgs.media.hgrenb1aSound ); } else { trap_S_StartSound( le->pos.trBase, 0, CHAN_AUTO, cgs.media.hgrenb2aSound ); } } else { // explode missile if ( cg.predictedImpacts < MAX_PREDICTED_IMPACTS ) { cg.predictedImpacts++; cg.predictedImpactsDecTime = cg.time; } if ( other->currentState.eType == ET_PLAYER ) { CG_MissileHitPlayer( le->ti.weapon, 0, trace.endpos, trace.plane.normal, trace.entityNum ); } else if ( !(trace.surfaceFlags & SURF_NOIMPACT) ) { CG_MissileHitWall( le->ti.weapon, 0, trace.endpos, trace.plane.normal, (trace.surfaceFlags & SURF_METALSTEPS) ? IMPACTSOUND_METAL : IMPACTSOUND_DEFAULT ); } // store the entity for deleting the server entity le->leFlags |= LEF_FINISHED; le->endTime = cg.time + 500; return; } } // check for medium change if ( trap_CM_PointContents( re->origin, 0 ) & CONTENTS_WATER ) inWater = qtrue; else inWater = qfalse; if ( ( !inWater && le->pos.trType == TR_WATER_GRAVITY ) || ( inWater && le->pos.trType == TR_GRAVITY ) ) { // setup new tr vec3_t newDelta; BG_EvaluateTrajectoryDelta( &le->pos, cg.time, newDelta ); VectorCopy( re->origin, le->pos.trBase ); VectorCopy( newDelta, le->pos.trDelta ); le->pos.trTime = cg.time; if ( inWater ) le->pos.trType = TR_WATER_GRAVITY; else le->pos.trType = TR_GRAVITY; } // add to refresh list trap_R_AddRefEntityToScene( re ); }
/* ================== CG_AddMoveScale ================== */ static void CG_AddMoveScale( localEntity_t *le ) { refEntity_t *re; vec3_t delta; float len; re = &le->refEntity; if ( !( le->leFlags & (LEF_DONT_SCALE | LEF_ALT_SCALE) ) ) { re->radius = le->radius * ( cg.time - le->startTime ) * le->lifeRate + 8; } if ( le->leFlags & LEF_ALT_SCALE ) { if ( re->hModel ) { if ( (le->leFlags & LEF_AXIS_ALIGNED) && le->radius ) { // it has a variing scale AxisClear( re->axis ); AxisScale( re->axis, re->radius + le->radius * (cg.time - le->startTime) / (le->endTime - le->startTime), re->axis ); } if ( !(le->leFlags & LEF_AXIS_ALIGNED) ) { // model looks in flight direction vec3_t delta, angles; BG_EvaluateTrajectoryDelta( &le->pos, cg.time, delta ); vectoangles( delta, angles ); AnglesToAxis( angles, re->axis ); if ( re->radius || le->radius ) AxisScale( re->axis, re->radius + le->radius * (cg.time - le->startTime) / (le->endTime - le->startTime), re->axis ); } } else { // sprites scale like this re->radius += le->radius * cg.frametime / (le->endTime - le->startTime); } } BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // check for collisions if ( le->leFlags & LEF_COLLISIONS ) { // do a trace sometimes if ( le->ti.trailTime++ > 5 ) { trace_t trace; le->ti.trailTime = 0; CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT ); VectorCopy( re->origin, re->oldorigin ); // hit something if ( trace.fraction < 1.0 ) { entityState_t *s1; int life; // do impact effect s1 = &cg_entities[le->owner].currentState; life = s1->groundEntityNum; if ( !( trace.surfaceFlags & SURF_NOIMPACT ) ) { if ( s1->generic1 & PF_IMPACT_MODEL ) { CG_MakeExplosion( trace.endpos, trace.plane.normal, cgs.gameModels[s1->modelindex2], cgs.gameShaders[s1->otherEntityNum2], life, qfalse, qfalse ); } else if ( s1->modelindex2 ) { CG_MakeExplosion( trace.endpos, trace.plane.normal, cgs.media.dishFlashModel, cgs.gameShaders[s1->modelindex2], life, qtrue, qtrue ); } } CG_FreeLocalEntity( le ); return; } } } // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( re ); }