/** * The original Half-Life version of the mp5 * Modified by for the Crack-Life Sven Co-op conversion * * Original Crack-Life mod by: Siemka321 * Sven Co-op Re-conversion: Rafael "R4to0" Alves */ namespace CLMP5 { enum Mp5Animation { MP5_LONGIDLE = 0, MP5_IDLE1, MP5_LAUNCH, MP5_RELOAD, MP5_DEPLOY, MP5_FIRE1, MP5_FIRE2, MP5_FIRE3 }; // Let's Crack-Life this // Confirmed IDA - 12 Feb 2019 -R4to0 const uint g_DefGivePri = 999; const uint g_MaxAmmoPri = 999; const uint g_MaxAmmoSec = 999; const uint g_MaxClip = 999; const uint g_Weight = 15; const uint g_PrimaryDmg = int( g_EngineFuncs.CVarGetFloat( "sk_plr_9mmAR_bullet" ) ); const uint g_SecondaryDmg = int( g_EngineFuncs.CVarGetFloat( "sk_plr_9mmAR_grenade" ) ); const uint g_DefGiveSec = 999; const float g_PriFireDelay = 0.001f; const float g_SecFireDelay = 0.1f; // Weapon Info uint g_Slot = 2; uint g_Position = 4; const string g_PriAmmoType = "cl_9mm"; //Default: 9mm const string g_SecAmmoType = "cl_ARgrenades"; //Default: ARgrenades const string g_AmmoName = "ammo_clmp5"; const string g_SecAmmoName = "ammo_clmp5grenades"; const string g_WeaponName = "weapon_clmp5"; const string g_ProjName = "proj_clgrenade"; // Models const string g_PeeMdl = "models/hlclassic/p_9mmAR.mdl"; // SC HL1 "classic mode" model const string g_VeeMdl = "models/cracklife/v_9mmar.mdl"; // v2: Edited reload events const string g_WeeMdl = "models/hlclassic/w_9mmAR.mdl"; // SC HL1 "classic mode" model const string g_ShellMdl = "models/hlclassic/shell.mdl"; // Shell const string g_GrenadeMdl = "models/hlclassic/grenade.mdl"; // Grenade const string g_WeeGrenadeMdl = "models/hlclassic/w_ARgrenade.mdl"; // Grenade const string g_WeeAmmoMdl = "models/hlclassic/w_9mmARclip.mdl"; // Ammo // Sounds const string g_ClipInsSnd = "hlclassic/items/clipinsert1.wav"; // Played by v2 model const string g_ClipRelSnd = "hlclassic/items/cliprelease1.wav"; // Played by v2 model const string g_Fire1Snd = "hlclassic/weapons/hks1.wav"; const string g_Fire2Snd = "hlclassic/weapons/hks2.wav"; const string g_Fire3Snd = "hlclassic/weapons/hks3.wav"; const string g_GL1Snd = "hlclassic/weapons/glauncher.wav"; const string g_GL2Snd = "hlclassic/weapons/glauncher2.wav"; const string g_EmptySnd = "cracklife/weapons/357_cock1.wav"; const string g_AmmoPickSnd = "hlclassic/items/9mmclip1.wav"; const string g_Debris1Snd = "hlclassic/weapons/debris1.wav"; const string g_Debris2Snd = "hlclassic/weapons/debris2.wav"; const string g_Debris3Snd = "hlclassic/weapons/debris3.wav"; // Sprites const string g_FireballSpr = "sprites/zerogxplode.spr"; const string g_ExplosionSpr = "sprites/WXplo1.spr"; const string g_SmokeSpr = "sprites/steam1.spr"; class weapon_clmp5 : ScriptBasePlayerWeaponEntity { private CBasePlayer@ m_pPlayer { get const { return cast( self.m_hPlayer.GetEntity() ); } set { self.m_hPlayer = EHandle( @value ); } } int m_iShell; int m_iSecondaryAmmo; /*int SecondaryAmmoIndex() { return self.m_iSecondaryAmmoType; }*/ void Spawn() { Precache(); g_EntityFuncs.SetModel( self, g_WeeMdl ); self.m_iDefaultAmmo = g_DefGivePri; self.m_iSecondaryAmmoType = 0; self.FallInit(); } void Precache() { // Models self.PrecacheCustomModels(); g_Game.PrecacheModel( g_VeeMdl ); g_Game.PrecacheModel( g_WeeMdl ); g_Game.PrecacheModel( g_PeeMdl ); m_iShell = g_Game.PrecacheModel( g_ShellMdl ); g_Game.PrecacheModel( g_GrenadeMdl ); // Sounds g_SoundSystem.PrecacheSound( g_ClipInsSnd ); g_SoundSystem.PrecacheSound( g_ClipRelSnd ); g_SoundSystem.PrecacheSound( g_Fire1Snd ); g_SoundSystem.PrecacheSound( g_Fire2Snd ); g_SoundSystem.PrecacheSound( g_Fire3Snd ); g_SoundSystem.PrecacheSound( g_GL1Snd ); g_SoundSystem.PrecacheSound( g_GL2Snd ); g_SoundSystem.PrecacheSound( g_EmptySnd ); } bool GetItemInfo( ItemInfo& out info ) { info.iMaxAmmo1 = g_MaxAmmoPri; info.iMaxAmmo2 = g_MaxAmmoSec; info.iMaxClip = g_MaxClip; info.iSlot = g_Slot; info.iPosition = g_Position; info.iId = g_ItemRegistry.GetIdForName( self.pev.classname ); info.iWeight = g_Weight; return true; } bool AddToPlayer( CBasePlayer@ pPlayer ) { if( BaseClass.AddToPlayer( pPlayer ) == true ) { @m_pPlayer = pPlayer; NetworkMessage clmp5( MSG_ONE, NetworkMessages::WeapPickup, pPlayer.edict() ); clmp5.WriteLong( g_ItemRegistry.GetIdForName( self.pev.classname ) ); clmp5.End(); return true; } return false; } bool Deploy() { return self.DefaultDeploy( self.GetV_Model( g_VeeMdl ), self.GetP_Model( g_PeeMdl ), MP5_DEPLOY, "mp5" ); } bool PlayEmptySound() { if( self.m_bPlayEmptySound ) { self.m_bPlayEmptySound = false; g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, g_EmptySnd, 0.8f, ATTN_NORM, 0, PITCH_NORM ); } return false; } void GetDefaultShellInfo( CBasePlayer@ pPlayer, Vector& out ShellVelocity, Vector& out ShellOrigin, float forwardScale, float rightScale, float upScale ) { Vector vecForward, vecRight, vecUp; g_EngineFuncs.AngleVectors( pPlayer.pev.v_angle, vecForward, vecRight, vecUp ); const float fR = Math.RandomFloat( 50, 70 ); const float fU = Math.RandomFloat( 100, 150 ); for( int i = 0; i < 3; ++i ) { ShellVelocity[i] = pPlayer.pev.velocity[i] + vecRight[i] * fR + vecUp[i] * fU + vecForward[i] * 25; ShellOrigin[i] = pPlayer.pev.origin[i] + pPlayer.pev.view_ofs[i] + vecUp[i] * upScale + vecForward[i] * forwardScale + vecRight[i] * rightScale; } } void PrimaryAttack() { // don't fire underwater if( m_pPlayer.pev.waterlevel == WATERLEVEL_HEAD ) { self.PlayEmptySound(); self.m_flNextPrimaryAttack = self.m_flNextSecondaryAttack = g_Engine.time + 0.15f; return; } if( self.m_iClip <= 0 ) { self.PlayEmptySound(); self.m_flNextSecondaryAttack = self.m_flNextPrimaryAttack = g_Engine.time + 0.15f; return; } m_pPlayer.m_iWeaponVolume = NORMAL_GUN_VOLUME; m_pPlayer.m_iWeaponFlash = NORMAL_GUN_FLASH; --self.m_iClip; // Fire anim switch ( g_PlayerFuncs.SharedRandomLong( m_pPlayer.random_seed, 0, 2 ) ) { case 0: self.SendWeaponAnim( MP5_FIRE1, 0, 0 ); break; case 1: self.SendWeaponAnim( MP5_FIRE2, 0, 0 ); break; case 2: self.SendWeaponAnim( MP5_FIRE3, 0, 0 ); break; } // Eject brass Vector vecShellOrigin, vecShellVelocity; GetDefaultShellInfo( m_pPlayer, vecShellVelocity, vecShellOrigin, 22, 6, -9 ); g_EntityFuncs.EjectBrass( vecShellOrigin, vecShellVelocity, m_pPlayer.pev.angles.y, m_iShell, TE_BOUNCE_SHELL ); // Fire sound switch ( g_PlayerFuncs.SharedRandomLong( m_pPlayer.random_seed, 0, 2 ) ) { case 0: g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, g_Fire1Snd, 1.0f, ATTN_NORM, 0, 95 + Math.RandomLong( 0, 10 ) ); break; case 1: g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, g_Fire2Snd, 1.0f, ATTN_NORM, 0, 95 + Math.RandomLong( 0, 10 ) ); break; case 2: g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, g_Fire3Snd, 1.0f, ATTN_NORM, 0, 95 + Math.RandomLong( 0, 10 ) ); break; } // player "shoot" animation m_pPlayer.SetAnimation( PLAYER_ATTACK1 ); Vector vecSrc = m_pPlayer.GetGunPosition(); Vector vecAiming = m_pPlayer.GetAutoaimVector( AUTOAIM_5DEGREES ); // VECTOR_CONE_6DEGREES = optimized multiplayer. Widened to make it easier to hit a moving player // VECTOR_CONE_3DEGREES = single player spread m_pPlayer.FireBullets( 1, vecSrc, vecAiming, ( g_WeaponMode ? VECTOR_CONE_6DEGREES : VECTOR_CONE_3DEGREES ), 8192, BULLET_PLAYER_MP5, 2, g_PrimaryDmg ); if( self.m_iClip == 0 && m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 ) // HEV suit - indicate out of ammo condition m_pPlayer.SetSuitUpdate( "!HEV_AMO0", false, 0 ); m_pPlayer.pev.punchangle.x = Math.RandomLong( -2, 2 ); self.m_flNextPrimaryAttack = self.m_flNextPrimaryAttack + g_PriFireDelay; // changed here if( self.m_flNextPrimaryAttack < g_Engine.time ) self.m_flNextPrimaryAttack = g_Engine.time + g_PriFireDelay; // changed here self.m_flTimeWeaponIdle = g_Engine.time + g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 10, 15 ); TraceResult tr; float x, y; g_Utility.GetCircularGaussianSpread( x, y ); Vector vecDir = vecAiming + x * ( g_WeaponMode ? VECTOR_CONE_6DEGREES : VECTOR_CONE_3DEGREES ).x * g_Engine.v_right + y * ( g_WeaponMode ? VECTOR_CONE_6DEGREES : VECTOR_CONE_3DEGREES ).y * g_Engine.v_up; Vector vecEnd = vecSrc + vecDir * 4096; g_Utility.TraceLine( vecSrc, vecEnd, dont_ignore_monsters, m_pPlayer.edict(), tr ); if( tr.flFraction < 1.0 ) { if( tr.pHit !is null ) { CBaseEntity@ pHit = g_EntityFuncs.Instance( tr.pHit ); if( pHit is null || pHit.IsBSPModel() ) g_WeaponFuncs.DecalGunshot( tr, BULLET_PLAYER_MP5 ); } } } void SecondaryAttack() { // don't fire underwater if( m_pPlayer.pev.waterlevel == WATERLEVEL_HEAD ) { self.PlayEmptySound(); self.m_flNextPrimaryAttack = g_Engine.time + 0.15f; return; } if( m_pPlayer.m_rgAmmo(self.m_iSecondaryAmmoType) <= 0 ) { self.PlayEmptySound(); return; } m_pPlayer.m_iWeaponVolume = NORMAL_GUN_VOLUME; m_pPlayer.m_iWeaponFlash = BRIGHT_GUN_FLASH; m_pPlayer.m_iExtraSoundTypes = bits_SOUND_DANGER; m_pPlayer.m_flStopExtraSoundTime = g_Engine.time + 0.2f; m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType, m_pPlayer.m_rgAmmo( self.m_iSecondaryAmmoType ) - 1 ); m_pPlayer.pev.punchangle.x = -10.0f; // Is this right? -R4to0; It is https://github.com/ValveSoftware/halflife/blob/master/cl_dll/ev_hldm.cpp#L731 -R4to0 self.SendWeaponAnim( MP5_LAUNCH ); // player "shoot" animation m_pPlayer.SetAnimation( PLAYER_ATTACK1 ); if ( g_PlayerFuncs.SharedRandomLong( m_pPlayer.random_seed, 0, 1 ) != 0 ) { // play this sound through BODY channel so we can hear it if player didn't stop firing MP5 g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, g_GL1Snd, 0.8f, ATTN_NORM, 0, PITCH_NORM ); } else { // play this sound through BODY channel so we can hear it if player didn't stop firing MP5 g_SoundSystem.EmitSoundDyn( m_pPlayer.edict(), CHAN_WEAPON, g_GL2Snd, 0.8f, ATTN_NORM, 0, PITCH_NORM ); } Math.MakeVectors( m_pPlayer.pev.v_angle + m_pPlayer.pev.punchangle ); // we don't add in player velocity anymore. if( ( m_pPlayer.pev.button & IN_DUCK ) != 0 ) ShootContact( m_pPlayer.pev, m_pPlayer.pev.origin + g_Engine.v_forward * 16 + g_Engine.v_right * 6, g_Engine.v_forward * 800 ); else ShootContact( m_pPlayer.pev, m_pPlayer.pev.origin + m_pPlayer.pev.view_ofs * 0.5f + g_Engine.v_forward * 16 + g_Engine.v_right * 6, g_Engine.v_forward * 800 ); self.m_flNextPrimaryAttack = g_Engine.time + g_SecFireDelay; // changed here self.m_flNextSecondaryAttack = g_Engine.time + g_SecFireDelay; // changed here self.m_flTimeWeaponIdle = g_Engine.time + 5;// idle pretty soon after shooting. if( m_pPlayer.m_rgAmmo(self.m_iSecondaryAmmoType) <= 0 ) // HEV suit - indicate out of ammo condition m_pPlayer.SetSuitUpdate( "!HEV_AMO0", false, 0 ); } void Reload() { if( m_pPlayer.m_rgAmmo( self.m_iPrimaryAmmoType ) <= 0 || self.m_iClip == g_MaxClip ) return; self.DefaultReload( g_MaxClip, MP5_RELOAD, 1.5f, 0 ); //Set 3rd person reloading animation -Sniper BaseClass.Reload(); } void WeaponIdle() { self.ResetEmptySound(); m_pPlayer.GetAutoaimVector( AUTOAIM_5DEGREES ); if( self.m_flTimeWeaponIdle > g_Engine.time ) return; int iAnim; switch( g_PlayerFuncs.SharedRandomLong( m_pPlayer.random_seed, 0, 1 ) ) { case 0: iAnim = MP5_LONGIDLE; break; case 1: iAnim = MP5_IDLE1; break; default: iAnim = MP5_IDLE1; break; } self.SendWeaponAnim( iAnim ); self.m_flTimeWeaponIdle = g_Engine.time + g_PlayerFuncs.SharedRandomFloat( m_pPlayer.random_seed, 10, 15 );// how long till we do this again. } } /** * Custom 9mm ammo entity */ class AmmoClip : ScriptBasePlayerAmmoEntity { void Spawn() { Precache(); g_EntityFuncs.SetModel( self, g_WeeAmmoMdl ); BaseClass.Spawn(); } void Precache() { g_Game.PrecacheModel( g_WeeAmmoMdl ); g_SoundSystem.PrecacheSound( g_AmmoPickSnd ); } bool AddAmmo( CBaseEntity@ pOther ) { if( pOther.GiveAmmo( g_MaxClip, g_PriAmmoType, g_MaxClip ) != -1 ) { g_SoundSystem.EmitSound( self.edict(), CHAN_ITEM, g_AmmoPickSnd, 1, ATTN_NORM ); return true; } return false; } } /** * Custom grenade ammo entity */ class AmmoGrenade : ScriptBasePlayerAmmoEntity { void Spawn() { Precache(); g_EntityFuncs.SetModel( self, g_WeeGrenadeMdl ); BaseClass.Spawn(); } void Precache() { g_Game.PrecacheModel( g_WeeGrenadeMdl ); g_SoundSystem.PrecacheSound( g_AmmoPickSnd ); } bool AddAmmo( CBaseEntity@ pOther ) { if( pOther.GiveAmmo( g_DefGiveSec, g_SecAmmoType, g_MaxAmmoSec ) != -1 ) { g_SoundSystem.EmitSound( self.edict(), CHAN_ITEM, g_AmmoPickSnd, 1, ATTN_NORM ); return true; } return false; } } /** * Custom grenade projectile */ class CCLGrenade : ScriptBaseMonsterEntity { int m_iModelIndexFireball; int m_iModelIndexWExplosion; int m_iModelIndexSmoke; void Spawn() { Precache(); self.pev.movetype = MOVETYPE_BOUNCE; self.pev.solid = SOLID_BBOX; g_EntityFuncs.SetModel( self, g_GrenadeMdl ); g_EntityFuncs.SetSize( self.pev, g_vecZero, g_vecZero ); self.pev.dmg = g_SecondaryDmg; // 100 } void Precache() { g_SoundSystem.PrecacheSound( g_Debris1Snd ); g_SoundSystem.PrecacheSound( g_Debris2Snd ); g_SoundSystem.PrecacheSound( g_Debris3Snd ); m_iModelIndexFireball = g_Game.PrecacheModel( g_FireballSpr ); m_iModelIndexWExplosion = g_Game.PrecacheModel( g_ExplosionSpr ); m_iModelIndexSmoke = g_Game.PrecacheModel( g_SmokeSpr ); } void Explode( TraceResult pTrace, int bitsDamageType ) { self.pev.model = string_t(); //invisible self.pev.solid = SOLID_NOT;// intangible self.pev.takedamage = DAMAGE_NO; // Pull out of the wall a bit if( pTrace.flFraction != 1.0f ) { self.pev.origin = pTrace.vecEndPos + ( pTrace.vecPlaneNormal * ( self.pev.dmg - 24 ) * 0.6f ); } int iContents = g_EngineFuncs.PointContents( self.pev.origin ); NetworkMessage expl( MSG_PAS, NetworkMessages::SVC_TEMPENTITY, self.pev.origin ); expl.WriteByte( TE_EXPLOSION ); // This makes a dynamic light and the explosion sprites/sound expl.WriteCoord( self.pev.origin.x ); // Send to PAS because of the sound expl.WriteCoord( self.pev.origin.y ); expl.WriteCoord( self.pev.origin.z ); if( iContents != CONTENTS_WATER ) expl.WriteShort( m_iModelIndexFireball ); else expl.WriteShort( m_iModelIndexWExplosion ); expl.WriteByte( int( ( self.pev.dmg - 50 ) * 0.6f ) ); // scale * 10 expl.WriteByte( 15 ); // framerate expl.WriteByte( TE_EXPLFLAG_NONE ); expl.End(); GetSoundEntInstance().InsertSound ( bits_SOUND_COMBAT, self.pev.origin, NORMAL_EXPLOSION_VOLUME, 3.0f, self ); entvars_t@ pevOwner; if( self.pev.owner !is null ) @pevOwner = self.pev.owner.vars; else @pevOwner = null; @self.pev.owner = null; // can't traceline attack owner if this is set g_WeaponFuncs.RadiusDamage( self.pev.origin, self.pev, pevOwner, self.pev.dmg, self.pev.dmg * 2.5f, CLASS_NONE, bitsDamageType ); if( Math.RandomFloat( 0, 1 ) < 0.5f ) g_Utility.DecalTrace( pTrace, DECAL_SCORCH1 ); else g_Utility.DecalTrace( pTrace, DECAL_SCORCH2 ); switch( Math.RandomLong( 0, 2 ) ) { case 0: g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, g_Debris1Snd, 0.55f, ATTN_NORM ); break; case 1: g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, g_Debris2Snd, 0.55f, ATTN_NORM ); break; case 2: g_SoundSystem.EmitSound( self.edict(), CHAN_VOICE, g_Debris3Snd, 0.55f, ATTN_NORM ); break; } self.pev.effects |= EF_NODRAW; SetThink( ThinkFunction( this.Smoke ) ); self.pev.velocity = g_vecZero; self.pev.nextthink = g_Engine.time + 0.3f; if( iContents != CONTENTS_WATER ) { int sparkCount = Math.RandomLong( 0, 3 ); for( int i = 0; i < sparkCount; i++ ) g_EntityFuncs.Create( "spark_shower", self.pev.origin, pTrace.vecPlaneNormal, false ); } } void Smoke() { if( g_EngineFuncs.PointContents( self.pev.origin ) == CONTENTS_WATER ) { g_Utility.Bubbles( self.pev.origin - Vector( 64, 64, 64 ), self.pev.origin + Vector( 64, 64, 64 ), 100 ); } else { NetworkMessage smoke( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, self.pev.origin ); smoke.WriteByte( TE_SMOKE ); smoke.WriteCoord( self.pev.origin.x ); smoke.WriteCoord( self.pev.origin.y ); smoke.WriteCoord( self.pev.origin.z ); smoke.WriteShort( m_iModelIndexSmoke ); smoke.WriteByte( int( ( self.pev.dmg - 50 ) * 0.80f ) ); // scale * 10 smoke.WriteByte( 12 ); // framerate smoke.End(); } g_EntityFuncs.Remove( self ); } void ExplodeTouch( CBaseEntity@ pOther ) { TraceResult tr; Vector vecSpot; // trace starts here! @self.pev.enemy = @pOther.edict(); vecSpot = self.pev.origin - self.pev.velocity.Normalize() * 32; g_Utility.TraceLine( vecSpot, vecSpot + self.pev.velocity.Normalize() * 64, ignore_monsters, self.edict(), tr ); Explode( tr, DMG_BLAST ); } void DangerSoundThink() { if( !self.IsInWorld() ) { g_EntityFuncs.Remove( self ); return; } GetSoundEntInstance().InsertSound( bits_SOUND_DANGER, ( self.pev.origin + self.pev.velocity ) * 0.5f, int ( self.pev.velocity.Length() ), 0.2f, self ); self.pev.nextthink = g_Engine.time + 0.2f; if( self.pev.waterlevel != WATERLEVEL_DRY ) { self.pev.velocity = self.pev.velocity * 0.5f; } } } CCLGrenade@ ShootContact( entvars_t@ pevOwner, Vector vecStart, Vector vecVelocity ) { CBaseEntity@ cbeCLGrenade = g_EntityFuncs.CreateEntity( g_ProjName ); CCLGrenade@ pGrenade = cast( CastToScriptClass( cbeCLGrenade ) ); g_EntityFuncs.DispatchSpawn( pGrenade.self.edict() ); // contact grenades arc lower pGrenade.pev.gravity = 0.5f;// lower gravity since grenade is aerodynamic and engine doesn't know it. g_EntityFuncs.SetOrigin( pGrenade.self, vecStart ); pGrenade.pev.velocity = vecVelocity; pGrenade.pev.angles = Math.VecToAngles( pGrenade.pev.velocity ); @pGrenade.pev.owner = pevOwner.get_pContainingEntity(); // make monsters afaid of it while in the air pGrenade.SetThink( ThinkFunction( pGrenade.DangerSoundThink ) ); pGrenade.pev.nextthink = g_Engine.time; // Tumble in air pGrenade.pev.avelocity.x = Math.RandomFloat( -100, -500 ); // Explode on contact pGrenade.SetTouch( TouchFunction( pGrenade.ExplodeTouch ) ); pGrenade.pev.dmg = g_SecondaryDmg; return pGrenade; } void Register() { g_CustomEntityFuncs.RegisterCustomEntity( "CLMP5::weapon_clmp5", g_WeaponName ); g_ItemRegistry.RegisterWeapon( g_WeaponName, "cracklife", g_PriAmmoType, g_SecAmmoType ); g_CustomEntityFuncs.RegisterCustomEntity( "CLMP5::AmmoClip", g_AmmoName ); g_CustomEntityFuncs.RegisterCustomEntity( "CLMP5::AmmoGrenade", g_SecAmmoName ); g_CustomEntityFuncs.RegisterCustomEntity( "CLMP5::CCLGrenade", g_ProjName ); g_Game.PrecacheOther( g_ProjName ); g_Game.PrecacheOther( g_AmmoName ); g_Game.PrecacheOther( g_SecAmmoName ); } } // End of namespace