Shameless copy of myself @ modsonline:

IzNoGoD writes...
First: this tutorial is written only for the most experienced scripters in cod2. Others should steer clear of this explanation and hope someone writes them a proper implementation.


I got annoyed by all the non-working implementations of isthrowinggrenade() and decided to write my own function for it.

This was quite hard, as the cod2 engine has a lot(!) of limitations. Finally, all works

how it works:
As cod2 doesnt switch to the nadeweapon as cod1 does, we have to use the getcurrentoffhand() and switchtooffhand() to determine if the player is throwing a nade.
In essence, it is quite easy, but there are a lot of things you have to keep in mind.

First, if the player starts throwing a smokegrenade, getcurrentoffhand() will return the name of said nade, but the player will not switch back to any offhand. So we have to force it back to a grenade of our choice:
Code:
while(true)
{
     self switchtooffhand("grenade");
     wait 0.05;
}
This works fine, but it cannot detect any normal nadethrows, only the smokegrenade will show up.
If we force the player to the offhand smokegrenade, we can detect fraggrenades, but we lose the ability to detect any smokegrenades.

So what we need is a third offhand grenade. The weaponfile is pasted here:
Code:
WEAPONFILE\weaponType\grenade\weaponClass\grenade\offhandClass\Frag Grenade\weaponSlot\none\displayName\WEAPON_M2FRAGGRENADE\AIOverlayDescription\\modeName\\playerAnimType\other\gunModel\viewmodel_mk2\handModel\viewmodel_hands_cloth\isHandModelOverridable\1\idleAnim\viewmodel_mk2_idle\emptyIdleAnim\viewmodel_mk2_idle\fireAnim\viewmodel_mk2_throw\holdFireAnim\viewmodel_mk2_pullpin\lastShotAnim\viewmodel_mk2_throw\meleeAnim\viewmodel_mk2_idle\raiseAnim\viewmodel_mk2_idle\dropAnim\viewmodel_mk2_idle\altRaiseAnim\viewmodel_mk2_idle\altDropAnim\viewmodel_mk2_idle\autoAimRange\1600\slowdownAimRange\50000\slowdownAimRangeAds\50000\lockonAimRange\50000\lockonAimRangeAds\50000\moveSpeedScale\1\damage\0\meleeDamage\40\fireDelay\0.1\meleeDelay\0.1\fireTime\0.3\holdFireTime\0.6\meleeTime\0.66\reloadTime\2\dropTime\0\raiseTime\0\altDropTime\0\altRaiseTime\0\fuseTime\3.5\clipOnly\1\hipIdleAmount\80\idleCrouchFactor\0.5\idleProneFactor\0.15\parallelDefaultBounce\0.5\parallelBarkBounce\0.6\parallelBrickBounce\0.6\parallelCarpetBounce\0.6\parallelClothBounce\0.2\parallelConcreteBounce\0.6\parallelDirtBounce\0.45\parallelFleshBounce\0.2\parallelFoliageBounce\0.05\parallelGlassBounce\0.4\parallelGrassBounce\0.35\parallelGravelBounce\0.5\parallelIceBounce\0.6\parallelMetalBounce\0.6\parallelMudBounce\0.2\parallelPaperBounce\0.2\parallelPlasterBounce\0.5\parallelRockBounce\0.6\parallelSandBounce\0.3\parallelSnowBounce\0.2\parallelWaterBounce\0.2\parallelWoodBounce\0.6\parallelAsphaltBounce\0.6\perpendicularDefaultBounce\0.25\perpendicularBarkBounce\0.25\perpendicularBrickBounce\0.25\perpendicularCarpetBounce\0.25\perpendicularClothBounce\0.2\perpendicularConcreteBounce\0.25\perpendicularDirtBounce\0.225\perpendicularFleshBounce\0.2\perpendicularFoliageBounce\0.05\perpendicularGlassBounce\0.2\perpendicularGrassBounce\0.15\perpendicularGravelBounce\0.25\perpendicularIceBounce\0.3\perpendicularMetalBounce\0.25\perpendicularMudBounce\0.1\perpendicularPaperBounce\0.2\perpendicularPlasterBounce\0.25\perpendicularRockBounce\0.25\perpendicularSandBounce\0.1\perpendicularSnowBounce\0.1\perpendicularWaterBounce\0.2\perpendicularWoodBounce\0.25\perpendicularAsphaltBounce\0.25\swayMaxAngle\30\swayLerpSpeed\6\swayPitchScale\0.1\swayYawScale\0.1\swayHorizScale\0.1\swayVertScale\0.1\swayShellShockScale\5\worldModel\xmodel/weapon_mk2fraggrenade\hudIcon\gfx/icons/hud@us_grenade.tga\modeIcon\\ammoName\idlenade\maxAmmo\0\startAmmo\0\clipName\idlenade\clipSize\0\sharedAmmoCapName\WEAPON_IDLENADE\sharedAmmoCap\0\dropAmmoMin\0\dropAmmoMax\0\pickupSound\weap_pickup\ammoPickupSound\\pullbackSound\weap_fraggrenade_pin\fireSound\weap_fraggrenade_fire\lastShotSound\\reloadSound\weap_fraggrenade_reload\altSwitchSound\\raiseSound\\putawaySound\\noteTrackSoundA\\noteTrackSoundB\\noteTrackSoundC\\noteTrackSoundD\\reticleCenter\gfx/reticle/center_cross.tga\reticleSide\\reticleCenterSize\32\reticleSideSize\16\reticleMinOfs\4\altWeapon\\twoHanded\0\cookOffHold\0\wideListIcon\0\killIcon\gfx/icons/hud@us_grenade.tga\wideKillIcon\0\flipKillIcon\1\standMoveF\0\standMoveR\1\standMoveU\-1.5\standRotP\0\standRotY\0\standRotR\0\duckedOfsF\-2\duckedOfsR\2\duckedOfsU\-0.2\duckedMoveF\0\duckedMoveR\1\duckedMoveU\-1.5\duckedRotP\0\duckedRotY\0\duckedRotR\0\proneOfsF\-6\proneOfsR\3.5\proneOfsU\0.8\proneMoveF\0\proneMoveR\1\proneMoveU\-1.5\proneRotP\0\proneRotY\0\proneRotR\0\posMoveRate\4\posProneMoveRate\10\standMoveMinSpeed\110\duckedMoveMinSpeed\60\proneMoveMinSpeed\0\posRotRate\5\posProneRotRate\10\standRotMinSpeed\110\duckedRotMinSpeed\60\proneRotMinSpeed\0\explosionRadius\256\explosionInnerDamage\200\explosionOuterDamage\50\projectileSpeed\940\projectileSpeedUp\120\projectileModel\xmodel/projectile_mk2fraggrenade\projExplosionType\grenade\projExplosionEffect\\projExplosionSound\\projectileTrail\none\projectileTrailTime\1\projectileTrailRadius\32\projImpactExplode\0
as you can, or maybe cannot see, this nade has a max of 0 ammo, making it an addition to the player's loadout without any ammo. This is nice, because players will not have any additional nades.

We now need to put this code into a weaponfile, precache it and give it to all players on spawn. (warning: this WILL break the dropoffhand() function used to drop grenades. Write your own fix for this, like storing the last used grenade in a player var)

Now we can force the player to focus on this offhandtype, and catch any other offhands. I suggest you use something like this:

Code:
while(true)
{
     self switchtooffhand("fraggrenade_idle_mp");
     if(self getcurrentoffhand()!="fraggrenade_idle_mp")
     {
          //player is throwing a nade
     }
     wait 0.05;
}
And thats all.

Known bugs: If the smokegrenade is empty, but ppl press the smokegrenade button anyway, this system detects it as if a real nade is being thrown.