Making a concussion grenade

From Valve Developer Community
Revision as of 10:34, 31 December 2006 by Johnyb (talk | contribs)
Jump to navigation Jump to search

Info:

This Tutorial was originally from gneu.org and moved here to combine all the tutorials into one location.

Purpose:

To Create a grenade that will blind someone.

Route:

Research UTIL_ functions and BaseGrenade

Solution:

One simple function does it all folks.

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_SOLID);
   
   // 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_SOLID, 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)


THANKS TO HELK FROM HOSTILE PLANET

Update 1

The question was posed... "how do you get the flash to deminish with distance?" The answer is simple... in pseudo code at least...

  1. Find the distance the player is from the grenade...
  2. consider...
    • the 100% flash distance
    • the 0% distance
  3. calculations
  4. alpha!

so lets do it really quick

1) in the same function as above... CGrenadeConc::Detonate( void ) we are going to tweak it a bit.

basically, since we are changing the color of the fade, we put all of our code in between

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

so first part is the distance. a simple utility function will take care of this...

UTIL_DistApprox2D(pPlayer->GetAbsOrigin(), GetAbsOrigin()+Vector(0,10,0))

this tid bit will return a float value of the distance of the character, but its not a magnitude, its got direction as well. What this means, is that the value can be + or negative. Since we dont really need to worry about .2 of a unit, and negative values do us no good, abs() is our new best friend. It will remove the decimal portion, truncation, and return the positive version of the number, All better.

int distance = abs(UTIL_DistApprox2D(pPlayer->GetAbsOrigin(), GetAbsOrigin()+Vector(0,10,0)));

okay, we have the distance.

2a) consider the 100% flash distance is the next question. At what distance do we still want the character to be flashed? I choose to have it at 200 units. my sphere above makes that work out pretty well. its about 1/2 the sphere, and if you think about it... 16 feet is a nice distance for this to happen...

2b) consider the 0% distance. if 16 is 100%, i think it should have 32 as 0. nothing too complex. 400 units Cool

3) calculations! For simplicitys sake, and code shortening, we start percent with 100%:

float percent = 1.0;

now we need a test. since we decided that 200 would be a good distance for 100%, lets make sure the distance is above that. and then we have a simple calculation to know what percentage of the flash to show...

if (distance >200)
    percent = (distance-200)/200.0;

but we have another problem. this is actually the opposite of the ratio we were looking for, and it also sends negative values to percent, like we had above... not good. so we need to invert the percent and abs it.

if (distance >200)
    percent = abs (1-((distance-200)/200.0));

Next problem to avert, we have nothing but 1 and 0 as values. as i said earlier, abs truncates the decimal values. simply make the value an integer. I figure 3 decimal points is good enough... how about it.

if (distance >200)
    percent = abs (1000*(1-((distance-200)/200.0)))/1000;

So lets go through this again.

distance is taken, from the point of explosion to the characters location. percent is 100% if the distance is more than 200, percent is changed to the correct value

one more statement to change and we are done, folks.

we want the alpha to change on this color to a percentage.

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

Done.

Update 2

On second thought, i thought it would be better to have the flashhold change as well via the percentage. here is my 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_SOLID);
   
   // 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_SOLID, NULL, COLLISION_GROUP_NONE, &tr );
         CBasePlayer *pPlayer = ToBasePlayer(list[i]);

       int distance = abs(UTIL_DistApprox2D(pPlayer->GetAbsOrigin(), GetAbsOrigin()+Vector(0,10,0)));

       float percent = 1.0;
      
       if (distance >200)
          percent = abs (1000*(1-((distance-200)/200.0)))/1000;
      
         color32 white = { 255,255,255,(255*percent) };

       int fadehold = random->RandomInt( 0, 4 );

         if(tr.fraction == 1.0f) {
         UTIL_ScreenFade(pPlayer,white,CONC_FALLOFF_TIME+(.5f*fadehold*percent),fadehold*percent, 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*percent),fadehold*percent, 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 cant see the grenade itself.