Making a concussion grenade

From Valve Developer Community
Jump to: navigation, search


Creating a grenade that will blind someone.

void CGrenadeConc::Detonate( void )
{
	// An array for all the player pointers to chill
	CBaseEntity *list[1024];
	
	// grab all of them within 400 units
	int count = UTIL_EntitiesInSphere(list,1024,GetAbsOrigin()+Vector(0,10,0),400,MASK_PLAYERSOLID);
	
	// for each of them
	for ( int i = 0; i < count; i++ )
	{
		// make sure its a player
		if (list[i]->IsPlayer())
		{
			// trace line to make
			trace_t   tr;   
			UTIL_TraceLine(list[i]->GetAbsOrigin(),GetAbsOrigin()+Vector(0,10,0),MASK_PLAYERSOLID, NULL, COLLISION_GROUP_NONE, &tr );
			CBasePlayer *pPlayer = ToBasePlayer(list[i]);

			color32 white = { 255,255,255,255 };

			if(tr.fraction == 1.0f) {UTIL_ScreenFade(pPlayer,white,CONC_FALLOFF_TIME,CONC_FADEHOLD, FFADE_IN);}
		}
	}

	BaseClass::Detonate();
}

Drop that in your grenade .cpp and add this to the class definition

virtual void      Detonate( void );

and these defines into up top

#define CONC_FALLOFF_TIME 20.0f
#define CONC_FADEHOLD 3.0f
There was a bug in the original code I posted here, where the grenade was actually blocking the flash. the fix is added here.
GetAbsOrigin()+Vector(0,10,0)


There was a problem with the concussion effect not working when using it in the crossbow "BoltTouch" function and jumping while firing downwards. The problem was that the mask wasn't working properly. Fixed it by replacing MASK_SOLID with MASK_PLAYERSOLID. This fix has been implemented into the code on this page already.
MASK_SOLID replaced with MASK_PLAYERSOLID

Update 1

The question was posed... "How do you get the flash to deminish with distance?" The answer is simple in theory...

  1. Find the distance the player is from the grenade
  2. Consider..
    • ..the 100% flash distance
    • ..the 0% distance
  3. Calculate and set alpha

So let's do it really quick

Step 1

We are going to tweak CGrenadeConc::Detonate( void ) a bit.

Basically, since we are changing the alpha of the fade, all calculations will be added at the marked location

CBasePlayer *pPlayer = ToBasePlayer(list[i]);
// here is where our code will be going
color32 white = { 255,255,255,255 };

A simple line will take care of finding the distance...

float distancesq = (pPlayer->GetAbsOrigin() - GetAbsOrigin()+Vector(0,10,0)).LengthSqr()

Step 2

Part A

Considering the 100% flash distance is the next step. At what distance do we still want the character to be flashed? For this tutorial, the chosen distance is 200 inches. The sphere above makes that work out pretty well because 200 inches is about half the sphere's range.

Part B

Consider the 0% distance. If 200 inches is 100%, a good 0% distance may be 400 inches.

Step 3

For simplicitys sake, and code shortening, we start percent with 100%:

float ratio = 1.0;

Since we decided that 200 inches would be a good distance for 100%, let's make sure the distance is above or at that. We have a simple calculation to know what percentage of the flash to show. The distance is left squared to possibly avoid an unneeded square root...

if (distancesq >= 40000)

ratio = (200-(vec_t)FastSqrt(distancesq))/200.0;

Distance is taken between the point of explosion and the character's location.

We want a proportion of this flash, so we will use a ratio of a 100% opaque flash.

color32 white = { 255,255,255,(255*ratio) };

Update 2

It might also be better to have the flash hold change as well via the ratio. Here is the finished detonate function for comparison.

void CGrenadeConc::Detonate( void )
{
	// An array for all the player pointers to chill
	CBaseEntity *list[1024];
	
	// grab all of them within 350 units
	int count = UTIL_EntitiesInSphere(list,1024,GetAbsOrigin()+Vector(0,10,0),350,MASK_PLAYERSOLID);
	
	// for each of them
	for ( int i = 0; i &lt; count; i++ )
	{
		// make sure its a player
		if (list[i]->IsPlayer())
		{
			// trace line to make
			trace_t   tr;   
			UTIL_TraceLine(list[i]->GetAbsOrigin(),GetAbsOrigin()+Vector(0,10,0),MASK_PLAYERSOLID, NULL, COLLISION_GROUP_NONE, &amp;tr );
			CBasePlayer *pPlayer = ToBasePlayer(list[i]);
			
			float distancesq = (pPlayer->GetAbsOrigin() - GetAbsOrigin()+Vector(0,10,0)).LengthSqr()
			
			float ratio = 1.0;
			
			if (distancesq >= 40000)
				ratio = (200-(vec_t)FastSqrt(distancesq))/200.0;
			
			color32 white = { 255,255,255,(255*ratio) };
			
			int fadehold = random->RandomInt( 0, 4 );
			
			if(tr.fraction == 1.0f)
			{
				UTIL_ScreenFade(pPlayer, white, CONC_FALLOFF_TIME+(.5f*fadehold*ratio), fadehold*ratio, FFADE_IN);
				
				int effect = 0 ? random->RandomInt( 35, 37 ) : random->RandomInt( 32, 34 );
				
				CSingleUserRecipientFilter user( pPlayer );
				enginesound->SetPlayerDSP( user, effect, false );
			}
		}
	}
	
	BaseClass::Detonate();
}

Update 3

if(tr.fraction == 1.0f)
{
	if ( pPlayer->FInViewCone( this ) )
	UTIL_ScreenFade(pPlayer, white, CONC_FALLOFF_TIME+(.5f*fadehold*ratio), fadehold*ratio, FFADE_IN);
		  
	int effect = 0 ? random->RandomInt( 35, 37 ) : random->RandomInt( 32, 34 );
	
	CSingleUserRecipientFilter user( pPlayer );
	enginesound->SetPlayerDSP( user, effect, false );
}

The above shows how to skip the screenfade if the player can't actually see the grenade.