Posted by: PowerSoul
Date posted: Oct 01 2004 User Rating: N/A | Number of views: 8767 Number of comments: 1 | Description: Tutorial on making a flashbang grenade, complete with CS-style screen fading & concussion effect. |
You must complete omega's orthotriangles tutorial before following this article.
I've seen horrible implementations of flashbangs in numerous smaller mods, and when I had to write one for my own mod, I wanted to do it right. This is my way of coding a flashbang, enjoy.
First, on the client dll, we're going to modify the TFC's concussion effect so it automatically slows down & goes off, without needing to send a message from the server every second. We will need to change gHud's m_iConcussion from private member to a public member, so we can modify it later. Open hud.h, and move int m_iConcussionEffect; from CHud's privates to publics (line 558 -> 561). Next, open tri.cpp, and add the following to HUD_DrawOrthoTriangles:
| | static float nextUpdate = gHUD.m_flTime + 0.2; if (gHUD.m_iConcussionEffect > 0) { if (gHUD.m_flTime > nextUpdate) { gHUD.m_iConcussionEffect -= 1; if (gHUD.m_iConcussionEffect < 0) { gHUD.m_iConcussionEffect = 0; } nextUpdate = gHUD.m_flTime+0.2; } }
|
This decreases m_iConcussionEffect (strength of the effect, 0 = off) by 1 every 0.2 seconds. You might be asking why we're adding this here - simply because HUD_DrawOrthoTriangles gets called every frame for sure. You could add this to numerous other places, but I like it there.
That's it for client side, compile & open the server workspace.
What we're going to do first is "re-enable" the concussion message on the server side, so we can use it later. Open player.cpp, and add this to line 186: then go to line 233, and add this:
| | gmsgConcussion = REG_USER_MSG( "Concuss", 1 );
|
Now the server dll recognizes the gmsgConcussion-message, which we're going to use as an added effect to the flash. Next we'll add two new functions to CGrenade, ShootFlashbang & Flash. So open up weapons.h, and add the following below static CGrenade *ShootSatchelCharge:
| | static CGrenade *ShootFlashbang( entvars_t *pevOwner, Vector vecStart, Vector vecVelocity, float time );
|
...and the following below void Explode:
| | void Flash( TraceResult *pTrace );
|
Those prototype the added functions. Next, open ggrenade.cpp, and scroll down around line 425, and our ShootFlashbang function there:
| | CGrenade * CGrenade::ShootFlashbang( 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 ); pGrenade->pev->dmgtime = gpGlobals->time + time; pGrenade->SetThink( TumbleThink ); pGrenade->pev->avelocity.y = RANDOM_FLOAT ( -5, -20 ); pGrenade->pev->nextthink = gpGlobals->time + 0.1; if (time < 0.1) { pGrenade->pev->nextthink = gpGlobals->time; pGrenade->pev->velocity = Vector( 0, 0, 0 ); } SET_MODEL(ENT(pGrenade->pev), "models/w_flashbang.mdl"); if ( RANDOM_LONG( 1, 2 ) == 1 ) { pGrenade->pev->sequence = 2; } else { pGrenade->pev->sequence = 3; } pGrenade->pev->animtime = gpGlobals->time; pGrenade->pev->framerate = 1.0; pGrenade->pev->gravity = 0.55; pGrenade->pev->friction = 0.6; pGrenade->m_bIsFlashbang = TRUE; pGrenade->pev->dmg = 0; return pGrenade; }
|
Next go to the Detonate() function, line 217, and modify it so it looks like this:
| | void CGrenade::Detonate( void ) { TraceResult tr; Vector vecSpot = pev->origin + Vector ( 0 , 0 , 8 ); UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -40 ), ignore_monsters, ENT(pev), & tr); if ( m_bIsFlashbang == TRUE ) { Flash( &tr ); } else { Explode( &tr, DMG_BLAST ); } }
|
This is just so it calls Flash() instead of handgrenade's Explode() if the granade is a flashbang. "Now what the heck is this m_bIsFlashbang?", you might be asking. Indeed, we haven't yet added it. So open up weapons.h again, and add
to the end of CGrenade's publics. It's a boolean we use to tell a flashbang from a normal grenade. We're almost done - only the most critical part is missing ;) Go to the start of ggrenade.cpp, and add these below the other includes:
| | #include "shake.h" #include "math.h"
|
We need shake.h to use UTIL_ScreenFade, and math.h, because we'll be using powf() to calculate the duration of the flash, based on player's distance. Go around line 129, and add our Flash() function there as follows:
| | void CGrenade::Flash( TraceResult *pTrace ) { pev->model = iStringNull; pev->solid = SOLID_NOT; pev->takedamage = DAMAGE_NO; if ( pTrace->flFraction != 1.0 ) { pev->origin = pTrace->vecEndPos + (pTrace->vecPlaneNormal * 100 * 0.1); } int iContents = UTIL_PointContents ( pev->origin ); if (iContents != CONTENTS_WATER) { EMIT_SOUND(ENT(pev), CHAN_VOICE, "weapons/flashbang/bang.wav", 0.9, ATTN_NORM); UTIL_DecalTrace( pTrace, DECAL_SCORCH1 ); CBaseEntity *pEntity = NULL; while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, 2000 )) != NULL) { if ( pEntity->IsPlayer() ) { float flDist = (pEntity->Center() - pev->origin).Length(); if ( FVisible( pEntity ) ) { float flFadeTime = 150000 / powf(flDist, 2); if (flFadeTime > 30) { flFadeTime = 30; } else if (flFadeTime <= 2.0) { flFadeTime = 2.0; } UTIL_ScreenFade( pEntity, Vector(255,255,255), flFadeTime, (flFadeTime / 4), 255, FFADE_IN ); extern int gmsgConcussion; MESSAGE_BEGIN( MSG_ONE, gmsgConcussion, NULL, pEntity->pev ); if (flFadeTime < 3) { WRITE_BYTE( 20 ); } else if (flFadeTime >= 3 && flFadeTime < 4) { WRITE_BYTE( 34 ); } else if (flFadeTime >= 4 && flFadeTime < 5.5) { WRITE_BYTE( 50 ); } else if (flFadeTime >= 5.5 && flFadeTime < 12) { WRITE_BYTE( 72 ); } else if (flFadeTime >= 12) { WRITE_BYTE( 90 ); } MESSAGE_END(); } } } int sparks = RANDOM_LONG(1,6); for ( int i = 0; i < sparks; i++ ) Create( "spark_shower", pev->origin, pTrace->vecPlaneNormal, NULL ); } pev->effects |= EF_NODRAW; SetThink( Smoke ); pev->velocity = g_vecZero; pev->nextthink = gpGlobals->time + 0.3; }
|
One last thing - If we leave the flashbang like it is now, it won't emit smoke - this is because it's "dmg" is 0. So, replace the CGrenade's Smoke-function with the following:
| | void CGrenade::Smoke( void ) { if (UTIL_PointContents ( pev->origin ) == CONTENTS_WATER) { UTIL_Bubbles( pev->origin - Vector( 64, 64, 64 ), pev->origin + Vector( 64, 64, 64 ), 100 ); } else { MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z ); WRITE_SHORT( g_sModelIndexSmoke ); WRITE_BYTE( 70 ); if (m_bIsFlashbang) { WRITE_BYTE( 80 ); } else { WRITE_BYTE( (pev->dmg - 50) * 0.80 ); } WRITE_BYTE( 12 ); MESSAGE_END(); } UTIL_Remove( this ); }
|
That's all - you now have a working flashbang in your mod. The easiest way to test it off is to change CHandGrenade to use CGrenade::ShootFlashbang instead of CGrenade::ShootTimed. Have fun ;) |
|
User Comments
Showing comments 1-1
hey PowerSoul, like to mention that your code here....
| | void CGrenade::Smoke( void ) { if (UTIL_PointContents ( pev->origin ) == CONTENTS_WATER) { UTIL_Bubbles( pev->origin - Vector( 64, 64, 64 ), pev->origin + Vector( 64, 64, 64 ), 100 ); } else { MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z ); WRITE_SHORT( g_sModelIndexSmoke ); WRITE_BYTE( 70 ); // scale * 10 // static amount if it's a flashbang if (m_bIsFlashbang) { WRITE_BYTE( 80 ); } else { WRITE_BYTE( (pev->dmg - 50) * 0.80 ); // scale * 10 } WRITE_BYTE( 12 ); // framerate MESSAGE_END(); } UTIL_Remove( this ); }
|
needs to look like this
| | void CGrenade::Smoke( void ) { if (UTIL_PointContents ( pev->origin ) == CONTENTS_WATER) { UTIL_Bubbles( pev->origin - Vector( 64, 64, 64 ), pev->origin + Vector( 64, 64, 64 ), 100 ); } else { MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_SMOKE ); WRITE_COORD( pev->origin.x ); WRITE_COORD( pev->origin.y ); WRITE_COORD( pev->origin.z ); WRITE_SHORT( g_sModelIndexSmoke ); // WRITE_BYTE( 70 ); // scale * 10 // FIX, THIS IS NULL/VOID unneeded // static amount if it's a flashbang if (m_bIsFlashbang) { WRITE_BYTE( 80 ); } else { WRITE_BYTE( (pev->dmg - 50) * 0.80 ); // scale * 10 } WRITE_BYTE( 12 ); // framerate MESSAGE_END(); } UTIL_Remove( this ); }
|
everything else is good.... |
|
You must register to post a comment. If you have already registered, you must login.
|
297 Approved Articless
8 Pending Articles
3940 Registered Members
0 People Online (7 guests)
|
|