The Place to Start


Flash Bang Tutorial 
Gavin
Intermediate


Flash Bang Tutorial
Editor: YELLOW is original code. RED is new code.
In this tutorial you will learn how to make the flashbang like in
CStrike.
Goto ggrenade.cpp and somewhere in the file create the function

CGrenade * CGrenade:: ShootFlash( entvars_t *pevOwner, Vector vecStart,
Vector vecVelocity, float time )
{
 CGrenade *pGrenade = GetClassPtr( (CGrenade *)NULL );
 pGrenade->Spawn();
 UTIL_SetOrigin( pGrenade->pev, vecStart );
 pGrenade->pev->velocity = vecVelocity;
 pGrenade->pev->angles = UTIL_VecToAngles(pGrenade->pev->velocity);
 pGrenade->pev->owner = ENT(pevOwner);

 pGrenade->SetTouch( BounceTouch ); // Bounce if touched

 // Take one second off of the desired detonation time and set the think
to PreDetonate. PreDetonate
 // will insert a DANGER sound into the world sound list and delay
detonation for one second so that
 // the grenade explodes after the exact amount of time specified in the
call to ShootTimed().

 pGrenade->pev->dmgtime = gpGlobals->time + time;
 pGrenade->SetThink( FlashThink );
 pGrenade->pev->nextthink = gpGlobals->time + 0.1;
 if (time < 0.1)
 {
  pGrenade->pev->nextthink = gpGlobals->time;
  pGrenade->pev->velocity = Vector( 0, 0, 0 );
 }

 pGrenade->pev->sequence = RANDOM_LONG( 3, 6 );
 pGrenade->pev->framerate = 1.0;

 // Tumble through the air
 // pGrenade->pev->avelocity.x = -400;

 pGrenade->pev->gravity = 0.5;
 pGrenade->pev->friction = 0.8;

 SET_MODEL(ENT(pGrenade->pev), "models/w_grenade.mdl");
 pGrenade->pev->dmg = 0;

 return pGrenade;
}

and you also need a new think function so make this

void CGrenade :: FlashThink( void )
{
 if (!IsInWorld())
 {
  UTIL_Remove( this );
  return;
 }

 StudioFrameAdvance( );
 pev->nextthink = gpGlobals->time + 0.1;

 if (pev->dmgtime - 1 < gpGlobals->time)
 {
  CSoundEnt::InsertSound ( bits_SOUND_DANGER, pev->origin +
pev->velocity * (pev->dmgtime - gpGlobals->time), 400, 0.1 );
 }

 if (pev->dmgtime <= gpGlobals->time)
 {
  UTIL_MakeVectors( Vector( 0, pev->v_angle.y, 0 ) );
  Create("info_flash", pev->origin, pev->angles);
  UTIL_Remove( this );
 }
 if (pev->waterlevel != 0)
 {
  pev->velocity = pev->velocity * 0.5;
  pev->framerate = 0.2;
 }
}

The only thing that is changed from the ShootTimed in the ShootFlash
function is that it calls
FlashThink as its think. The FlashThink spawns a flash when the time
says it can. That will make
it so you can shoot the flashbang. Now you just have to say that the
function exist, go into
weapons.h and insert the lines

static CGrenade *ShootFlash( entvars_t *pevOwner, Vector vecStart,
Vector vecVelocity, float time );
void EXPORT FlashThink( void );

right above

static CGrenade *ShootTimed( entvars_t *pevOwner, Vector vecStart,
Vector vecVelocity, float time );

The next thing
to make is the actual flash function. Basing it on the code already in
items.cpp will making
coding the flash easier. So go into items.cpp and insert the lines

class CFlash : public CItem
{
 void Spawn( void )
 {
  Precache( );
  CItem::Spawn();
  UTIL_SetSize(pev, Vector(-400, -400, 0), Vector(400, 400, 400));
  SetThink( Think );
  pev->nextthink = gpGlobals->time + 0.1;
 }

 void Precache( void )
 {
 }

 BOOL MyTouch( CBasePlayer *pPlayer ) // was My Touch
 {
 UTIL_ScreenFade( pPlayer, Vector(255,255,255), 5.0, 0.5, 255, FFADE_IN
);
 pev->nextthink = gpGlobals->time + 0.1;
 return TRUE;
 }

 void Think( void )
 {
 UTIL_Remove( this );
 }
};
LINK_ENTITY_TO_CLASS(info_flash, CFlash);

That causes the actual flash and blinding of a person that is in the
400unit radius, to change
the radius just change all the 400s in the UTIL_SetSize to whatever
radius you want. If you are
in the radius you touch it and it does a screen fade for 5 seconds (the
number after the Vector(255,255,255)
is the duration and the Vector(255,255,255) is the color in RGB take a
look at BigGuys tutorial
about Screen Fading and Shaking for more inforation on screen fading).
It stays in the world for
0.1 seconds then it is removed. Also make sure to include the shake.h in
items.cpp.Now go into
weapons.cpp goto the W_Precache function and insert the line

UTIL_PrecacheOther( "info_flash" );

under the line UTIL_PrecacheOther( "item_longjump" ); to precache the
flash entity and it wont
give you a BadIndexOfEdict. That is everything to shoot a flashbang and
have it explode. Now
onto the actual weapon coding! Make a new weapon called flashbang.cpp
and insert all this code
into that file.

/***
*
* Flashbang by Gavin
*
****/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "player.h"


#define FLASHBANG_PRIMARY_VOLUME  450

enum handgrenade_e {
 FLASHBANG_IDLE = 0,
 FLASHBANG_FIDGET,
 FLASHBANG_PINPULL,
 FLASHBANG_THROW1, // toss
 FLASHBANG_THROW2, // medium
 FLASHBANG_THROW3, // hard
 FLASHBANG_HOLSTER,
 FLASHBANG_DRAW
};


class CFlashBang : public CBasePlayerWeapon
{
public:
 void Spawn( void );
 void Precache( void );
 int iItemSlot( void ) { return 5; }
 int GetItemInfo(ItemInfo *p);

 void PrimaryAttack( void );
 BOOL Deploy( void );
 BOOL CanHolster( void );
 void Holster( void );
 void WeaponIdle( void );
 float m_flStartThrow;
 float m_flReleaseThrow;
};
LINK_ENTITY_TO_CLASS( weapon_flashbang, CFlashBang );


void CFlashBang::Spawn( )
{
 Precache( );
 m_iId = WEAPON_FLASHBANG;
 SET_MODEL(ENT(pev), "models/w_grenade.mdl");

 m_iDefaultAmmo = HANDGRENADE_DEFAULT_GIVE;

 FallInit();// get ready to fall down.
}


void CFlashBang::Precache( void )
{
 PRECACHE_MODEL("models/w_grenade.mdl");
 PRECACHE_MODEL("models/v_grenade.mdl");
 PRECACHE_MODEL("models/p_grenade.mdl");
}

int CFlashBang::GetItemInfo(ItemInfo *p)
{
 p->pszName = STRING(pev->classname);
 p->pszAmmo1 = "Flashbang";
 p->iMaxAmmo1 = HANDGRENADE_MAX_CARRY;
 p->pszAmmo2 = NULL;
 p->iMaxAmmo2 = -1;
 p->iMaxClip = WEAPON_NOCLIP;
 p->iSlot = 4;
 p->iPosition = 0;
 p->iId = m_iId = WEAPON_FLASHBANG;
 p->iWeight = HANDGRENADE_WEIGHT;
 p->iFlags = ITEM_FLAG_LIMITINWORLD | ITEM_FLAG_EXHAUSTIBLE;

 return 1;
}


BOOL CFlashBang::Deploy( )
{
 m_flReleaseThrow = -1;
 return DefaultDeploy( "models/v_grenade.mdl", "models/p_grenade.mdl",
FLASHBANG_DRAW, "crowbar" );
}

BOOL CFlashBang::CanHolster( void )
{
 // can only holster hand grenades when not primed!
 return ( m_flStartThrow == 0 );
}

void CFlashBang::Holster( )
{
 m_pPlayer->m_flNextAttack = gpGlobals->time + 0.5;
 if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
 {
  SendWeaponAnim( FLASHBANG_HOLSTER );
 }
 else
 {
  // no more grenades!
  m_pPlayer->pev->weapons &= ~(1<< WEAPON_FLASHBANG);
  SetThink( DestroyItem );
  pev->nextthink = gpGlobals->time + 0.1;
 }

 EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "common/null.wav", 1.0,
ATTN_NORM);
}

void CFlashBang::PrimaryAttack()
{
 if (!m_flStartThrow && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
 {
  m_flStartThrow = gpGlobals->time;
  m_flReleaseThrow = 0;

  SendWeaponAnim( FLASHBANG_PINPULL );
  m_flTimeWeaponIdle = gpGlobals->time + 0.5;
 }
}


void CFlashBang::WeaponIdle( void )
{
 if (m_flReleaseThrow == 0)
  m_flReleaseThrow = gpGlobals->time;

 if (m_flTimeWeaponIdle > gpGlobals->time)
  return;

 if (m_flStartThrow)
 {
  Vector angThrow = m_pPlayer->pev->v_angle +
m_pPlayer->pev->punchangle;

  if (angThrow.x < 0)
   angThrow.x = -10 + angThrow.x * ((90 - 10) / 90.0);
  else
   angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0);

  float flVel = (90 - angThrow.x) * 4;
  if (flVel > 500)
   flVel = 500;

  UTIL_MakeVectors( angThrow );

  Vector vecSrc = m_pPlayer->pev->origin + m_pPlayer->pev->view_ofs +
gpGlobals->v_forward * 16;

  Vector vecThrow = gpGlobals->v_forward * flVel +
m_pPlayer->pev->velocity;

  // alway explode 3 seconds after the pin was pulled
  float time = m_flStartThrow - gpGlobals->time + 3.0;
  if (time < 0)
   time = 0;

  CGrenade::ShootFlash( m_pPlayer->pev, vecSrc, vecThrow, time );

  if (flVel < 500)
  {
   SendWeaponAnim( FLASHBANG_THROW1 );
  }
  else if (flVel < 1000)
  {
   SendWeaponAnim( FLASHBANG_THROW2 );
  }
  else
  {
   SendWeaponAnim( FLASHBANG_THROW3 );
  }

  // player "shoot" animation
  m_pPlayer->SetAnimation( PLAYER_ATTACK1 );

  m_flStartThrow = 0;
  m_flNextPrimaryAttack = gpGlobals->time + 0.5;
  m_flTimeWeaponIdle = gpGlobals->time + 0.5;

  m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;

  if ( !m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
  {
   // just threw last grenade
   // set attack times in the future, and weapon idle in the future so
we can see the //whole throw
   // animation, weapon idle will automatically retire the weapon for
us.
   m_flTimeWeaponIdle = m_flNextSecondaryAttack = m_flNextPrimaryAttack
= gpGlobals->time + 0.5;// ensure that the animation can finish playing
  }
  return;
 }
 else if (m_flReleaseThrow > 0)
 {
  // we've finished the throw, restart.
  m_flStartThrow = 0;

  if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
  {
   SendWeaponAnim( FLASHBANG_DRAW );
  }
  else
  {
   RetireWeapon();
   return;
  }

  m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );
  m_flReleaseThrow = -1;
  return;
 }

 if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType])
 {
  int iAnim;
  float flRand = RANDOM_FLOAT(0, 1);
  if (flRand <= 0.75)
  {
   iAnim = FLASHBANG_IDLE;
   m_flTimeWeaponIdle = gpGlobals->time + RANDOM_FLOAT ( 10, 15 );// how
long till we do this again.
  }
  else
  {
   iAnim = FLASHBANG_FIDGET;
   m_flTimeWeaponIdle = gpGlobals->time + 75.0 / 30.0;
  }

  SendWeaponAnim( iAnim );
 }
}

WOW! That was alot of code! Go into the weapons.h file under #define
WEAPON_SNARK 15 add

#define WEAPON_FLASHBANG 16

Now just goto weapons.cpp the W_Precache function and under the
info_flash we precached insert the line

UTIL_PrecacheOtherWeapon( "weapon_flashbang" );

Now thats it! Just start up a new game give yourself the
weapon_flashbang and have fun =).

Any questions or suggestions should be sent to me: british@epix.net