//	
//	Copyright (c) 1999, Valve LLC. All rights reserved.
//	
//	This product contains software technology licensed from Id 
//	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
//	All Rights Reserved.
//	
//	Use, distribution, and modification of this source code and/or resulting
//	object code is restricted to non-commercial enhancements to products from
//	Valve LLC.  All other use, distribution, or modification is prohibited
//	without written permission from Valve LLC.
//
//	===== weapons.cpp ========================================================
//
//  functions governing the selection/use of weapons for players
//
// $Workfile: weapons.cpp $
// $Date: 2002/11/22 21:13:52 $
//
//-------------------------------------------------------------------------------
// $Log: weapons.cpp,v $
// Revision 1.49  2002/11/22 21:13:52  Flayra
// - mp_consistency changes
//
// Revision 1.48  2002/11/12 02:19:43  Flayra
// - Fixed problem where friendly bulletfire was doing damage
//
// Revision 1.47  2002/10/24 21:19:11  Flayra
// - Reworked jetpack effects
// - Added a few extra sounds
//
// Revision 1.46  2002/10/16 00:41:15  Flayra
// - Removed unneeds sounds and events
// - Added distress beacon event
// - Added general purpose particle event
//
// Revision 1.45  2002/09/25 20:40:09  Flayra
// - Play different sound for aliens when they get weapons
//
// Revision 1.44  2002/09/23 22:04:00  Flayra
// - Regular update
//
// Revision 1.43  2002/08/31 18:01:18  Flayra
// - Work at VALVe
//
// Revision 1.42  2002/07/26 23:01:05  Flayra
// - Precache numerical feedback event
//
// Revision 1.41  2002/07/23 16:48:37  Flayra
// - Added distress beacon
//
// Revision 1.40  2002/07/08 16:35:17  Flayra
// - Added document header, updates for cheat protection, added constants
//
//===============================================================================
#include "../util/nowarnings.h"
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "player.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "soundent.h"
#include "decals.h"
#include "gamerules.h"
#include "../mod/AvHConstants.h"
#include "../mod/AvHMarineEquipmentConstants.h"
#include "../mod/AvHAlienWeaponConstants.h"
#include "../mod/AvHAlienEquipmentConstants.h"
#include "../mod/AvHPlayer.h"
#include "../mod/AvHGamerules.h"
#include "../mod/AvHNetworkMessages.h"

extern CGraph	WorldGraph;
extern int gEvilImpulse101;

int gJetpackEventID;
int gStartOverwatchEventID;
int gEndOverwatchEventID;
int gTeleportEventID;
int gBlinkEffectSuccessEventID;
int gPhaseInEventID;
int gSiegeHitEventID;
int gSiegeViewHitEventID;
int gCommanderPointsAwardedEventID;
int gAlienSightOnEventID;
int gAlienSightOffEventID;
//int gParalysisStartEventID;
int gRegenerationEventID;
int gStartCloakEventID;
int gEndCloakEventID;
int gSporeCloudEventID;
int gUmbraCloudEventID;
int gStopScreamEventID;

int gWelderEventID;
int gWelderConstEventID;
//int gWallJumpEventID;
//int gFlightEventID;
int gEmptySoundEventID;
int gNumericalInfoEventID;
int gInvalidActionEventID;
int gParticleEventID;
int gDistressBeaconEventID;
int gWeaponAnimationEventID;
int gLevelUpEventID;
int gMetabolizeSuccessEventID;

#define NOT_USED 255

DLL_GLOBAL	short	g_sModelIndexLaser;// holds the index for the laser beam
DLL_GLOBAL  const char *g_pModelNameLaser = "sprites/laserbeam.spr";
DLL_GLOBAL	short	g_sModelIndexLaserDot;// holds the index for the laser beam dot
DLL_GLOBAL	short	g_sModelIndexFireball;// holds the index for the fireball
DLL_GLOBAL	short	g_sModelIndexSmoke;// holds the index for the smoke cloud
DLL_GLOBAL	short	g_sModelIndexWExplosion;// holds the index for the underwater explosion
DLL_GLOBAL	short	g_sModelIndexBubbles;// holds the index for the bubbles model
DLL_GLOBAL	short	g_sModelIndexBloodDrop;// holds the sprite index for the initial blood
DLL_GLOBAL	short	g_sModelIndexBloodSpray;// holds the sprite index for splattered blood

ItemInfo CBasePlayerItem::ItemInfoArray[MAX_WEAPONS];
AmmoInfo CBasePlayerItem::AmmoInfoArray[MAX_AMMO_SLOTS];

MULTIDAMAGE gMultiDamage;

#define TRACER_FREQ		4			// Tracers fire every fourth bullet

extern bool gCanMove[];

//=========================================================
// MaxAmmoCarry - pass in a name and this function will tell
// you the maximum amount of that type of ammunition that a 
// player can carry.
//=========================================================
int MaxAmmoCarry( int iszName )
{
	for ( int i = 0;  i < MAX_WEAPONS; i++ )
	{
		if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo1 ) )
			return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo1;
		if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo2 ) )
			return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2;
	}

	ALERT( at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) );
	return -1;
}

	
/*
==============================================================================

MULTI-DAMAGE

Collects multiple small damages into a single damage

==============================================================================
*/

//
// ClearMultiDamage - resets the global multi damage accumulator
//
void ClearMultiDamage(void)
{
	gMultiDamage.pEntity = NULL;
	gMultiDamage.amount	= 0;
	gMultiDamage.type = 0;
}

//
// ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity
//
// GLOBALS USED:
//		gMultiDamage

void ApplyMultiDamage(entvars_t *pevInflictor, entvars_t *pevAttacker )
{
	Vector		vecSpot1;//where blood comes from
	Vector		vecDir;//direction blood should go
	TraceResult	tr;
	
	if ( !gMultiDamage.pEntity )
		return;

	float theDamage = gMultiDamage.amount;
	gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, theDamage, gMultiDamage.type );
}


// GLOBALS USED:
//		gMultiDamage

void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType)
{
	if ( !pEntity )
		return;
	
	gMultiDamage.type |= bitsDamageType;

	if ( pEntity != gMultiDamage.pEntity )
	{
		ApplyMultiDamage(pevInflictor,pevInflictor); // UNDONE: wrong attacker!
		gMultiDamage.pEntity	= pEntity;
		gMultiDamage.amount		= 0;
	}

	gMultiDamage.amount += flDamage;
}

/*
================
SpawnBlood
================
*/
void SpawnBlood(Vector vecSpot, int bloodColor, float flDamage)
{
	if(flDamage >= 0.0f)
	{
		if(bloodColor == DONT_BLEED)
		{
			UTIL_Sparks(vecSpot);
		}
		else
		{
			UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage );
		}
	}
}


int DamageDecal( CBaseEntity *pEntity, int bitsDamageType )
{
	if ( !pEntity )
		return (DECAL_GUNSHOT1 + RANDOM_LONG(0,4));
	
	return pEntity->DamageDecal( bitsDamageType );
}

void DecalGunshot( TraceResult *pTrace, int iBulletType )
{
	// Is the entity valid
	if ( !UTIL_IsValidEntity( pTrace->pHit ) )
		return;

	if ( VARS(pTrace->pHit)->solid == SOLID_BSP || VARS(pTrace->pHit)->movetype == MOVETYPE_PUSHSTEP )
	{
		CBaseEntity *pEntity = NULL;
		// Decal the wall with a gunshot
		if ( !FNullEnt(pTrace->pHit) )
			pEntity = CBaseEntity::Instance(pTrace->pHit);

//		switch( iBulletType )
//		{
//		case BULLET_PLAYER_9MM:
//		case BULLET_MONSTER_9MM:
//		case BULLET_PLAYER_MP5:
//		case BULLET_MONSTER_MP5:
//		case BULLET_PLAYER_BUCKSHOT:
//		case BULLET_PLAYER_357:
//		default:
			// smoke and decal
			UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
//			break;
//		case BULLET_MONSTER_12MM:
//			// smoke and decal
//			UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
//			break;
//		case BULLET_PLAYER_CROWBAR:
//			// wall decal
//			UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
//			break;
//		}
	}
}



//
// EjectBrass - tosses a brass shell from passed origin at passed velocity
//
void EjectBrass ( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype )
{
	// FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see.

	MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin );
		WRITE_BYTE( TE_MODEL);
		WRITE_COORD( vecOrigin.x);
		WRITE_COORD( vecOrigin.y);
		WRITE_COORD( vecOrigin.z);
		WRITE_COORD( vecVelocity.x);
		WRITE_COORD( vecVelocity.y);
		WRITE_COORD( vecVelocity.z);
		WRITE_ANGLE( rotation );
		WRITE_SHORT( model );
		WRITE_BYTE ( soundtype);
		WRITE_BYTE ( 25 );// 2.5 seconds
	MESSAGE_END();
}


#if 0
// UNDONE: This is no longer used?
void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count )
{
	MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin );
		WRITE_BYTE ( TE_EXPLODEMODEL );
		WRITE_COORD( vecOrigin.x );
		WRITE_COORD( vecOrigin.y );
		WRITE_COORD( vecOrigin.z );
		WRITE_COORD( speed );
		WRITE_SHORT( model );
		WRITE_SHORT( count );
		WRITE_BYTE ( 15 );// 1.5 seconds
	MESSAGE_END();
}
#endif


int giAmmoIndex = 0;

// Precaches the ammo and queues the ammo info for sending to clients
void AddAmmoNameToAmmoRegistry( const char *szAmmoname )
{
	// make sure it's not already in the registry
	for ( int i = 0; i < MAX_AMMO_SLOTS; i++ )
	{
		if ( !CBasePlayerItem::AmmoInfoArray[i].pszName)
			continue;

		if ( stricmp( CBasePlayerItem::AmmoInfoArray[i].pszName, szAmmoname ) == 0 )
			return; // ammo already in registry, just quite
	}


	giAmmoIndex++;
	ASSERT( giAmmoIndex < MAX_AMMO_SLOTS );
	if ( giAmmoIndex >= MAX_AMMO_SLOTS )
		giAmmoIndex = 0;

	CBasePlayerItem::AmmoInfoArray[giAmmoIndex].pszName = szAmmoname;
	CBasePlayerItem::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex;   // yes, this info is redundant
}


// Precaches the weapon and queues the weapon info for sending to clients
void UTIL_PrecacheOtherWeapon( const char *szClassname )
{
	edict_t	*pent;

	pent = CREATE_NAMED_ENTITY( MAKE_STRING( szClassname ) );
	if ( FNullEnt( pent ) )
	{
		ALERT ( at_console, "NULL Ent in UTIL_PrecacheOtherWeapon\n" );
		return;
	}
	
	CBaseEntity *pEntity = CBaseEntity::Instance (VARS( pent ));

	if (pEntity)
	{
		ItemInfo II;
		pEntity->Precache( );
		memset( &II, 0, sizeof II );
		if ( ((CBasePlayerItem*)pEntity)->GetItemInfo( &II ) )
		{
			CBasePlayerItem::ItemInfoArray[II.iId] = II;

			if ( II.pszAmmo1 && *II.pszAmmo1 )
			{
				AddAmmoNameToAmmoRegistry( II.pszAmmo1 );
			}

			if ( II.pszAmmo2 && *II.pszAmmo2 )
			{
				AddAmmoNameToAmmoRegistry( II.pszAmmo2 );
			}

			memset( &II, 0, sizeof II );
		}
	}

	REMOVE_ENTITY(pent);
}

// called by worldspawn
void W_Precache(void)
{
	memset( CBasePlayerItem::ItemInfoArray, 0, sizeof(CBasePlayerItem::ItemInfoArray) );
	memset( CBasePlayerItem::AmmoInfoArray, 0, sizeof(CBasePlayerItem::AmmoInfoArray) );
	giAmmoIndex = 0;

	// Marine weapons
	//UTIL_PrecacheOtherWeapon("weapon_9mmhandgun");
    //UTIL_PrecacheOtherWeapon("weapon_glock");
	//UTIL_PrecacheOther("ammo_9mmclip");

	// Player assets
	PRECACHE_UNMODIFIED_MODEL(kReadyRoomModel);
	PRECACHE_UNMODIFIED_MODEL(kMarineSoldierModel);
	PRECACHE_UNMODIFIED_MODEL(kHeavySoldierModel);
	PRECACHE_UNMODIFIED_MODEL(kAlienLevelOneModel);
	PRECACHE_UNMODIFIED_SOUND(kDistressBeaconSound);
	PRECACHE_UNMODIFIED_SOUND(kLevelUpMarineSound);
	PRECACHE_UNMODIFIED_SOUND(kLevelUpAlienSound);
	PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound1);
	PRECACHE_UNMODIFIED_SOUND(kAlienBuildingSound2);
	PRECACHE_SOUND(kMyHiveEasterEgg);
	
	//PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesGrantedSound);
	//PRECACHE_UNMODIFIED_SOUND(kAlienAbilitiesLostSound);
	
	PRECACHE_UNMODIFIED_MODEL(kAlienLevelTwoModel);
	PRECACHE_UNMODIFIED_MODEL(kAlienLevelThreeModel);
	PRECACHE_UNMODIFIED_MODEL(kAlienLevelFourModel);
	PRECACHE_UNMODIFIED_MODEL(kAlienLevelFiveModel);

    PRECACHE_UNMODIFIED_MODEL(kMarineCommanderModel);
    PRECACHE_UNMODIFIED_MODEL(kAlienGestateModel);
	// : 1072
	// Added some client side consistency checks.
	PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash1.spr");
	PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash2.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/muzzleflash3.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/digesting.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/membrane.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/hera_fog.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/spore.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/spore2.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/umbra.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/umbra2.spr"); 
	PRECACHE_UNMODIFIED_MODEL("sprites/webstrand.spr"); 

	UTIL_PrecacheOtherWeapon(kwsMine);
    UTIL_PrecacheOtherWeapon(kwsKnife);
    UTIL_PrecacheOtherWeapon(kwsMachineGun);
    UTIL_PrecacheOtherWeapon(kwsPistol);
    UTIL_PrecacheOtherWeapon(kwsShotGun);
    UTIL_PrecacheOtherWeapon(kwsHeavyMachineGun);
    UTIL_PrecacheOtherWeapon(kwsGrenadeGun);
    UTIL_PrecacheOtherWeapon(kwsGrenade);

	// Alien weapons
	UTIL_PrecacheOtherWeapon(kwsSpitGun);
	UTIL_PrecacheOther(kwsSpitProjectile);
	UTIL_PrecacheOther(kwsWebProjectile);
	UTIL_PrecacheOtherWeapon(kwsClaws);
	UTIL_PrecacheOtherWeapon(kwsSwipe);
	UTIL_PrecacheOtherWeapon(kwsSporeGun);
	UTIL_PrecacheOther(kwsSporeProjectile);
//	UTIL_PrecacheOtherWeapon(kwsParalysisGun);
	UTIL_PrecacheOtherWeapon(kwsSpikeGun);
	UTIL_PrecacheOtherWeapon(kwsBiteGun);
	UTIL_PrecacheOtherWeapon(kwsBite2Gun);
	UTIL_PrecacheOtherWeapon(kwsHealingSpray);
	UTIL_PrecacheOtherWeapon(kwsWebSpinner);
//	UTIL_PrecacheOtherWeapon(kwsBabblerGun);
	UTIL_PrecacheOtherWeapon(kwsPrimalScream);
	UTIL_PrecacheOtherWeapon(kwsParasiteGun);
	UTIL_PrecacheOtherWeapon(kwsMetabolize);
	UTIL_PrecacheOtherWeapon(kwsUmbraGun);
	UTIL_PrecacheOtherWeapon(kwsBlinkGun);
	UTIL_PrecacheOtherWeapon(kwsDivineWind);
	UTIL_PrecacheOtherWeapon(kwsBileBombGun);
	UTIL_PrecacheOtherWeapon(kwsAcidRocketGun);
	UTIL_PrecacheOtherWeapon(kwsStomp);
	UTIL_PrecacheOtherWeapon(kwsDevour);
//	UTIL_PrecacheOtherWeapon(kwsAmplify);
	UTIL_PrecacheOther(kwsBileBomb);

	// Alien abilities
	UTIL_PrecacheOtherWeapon(kwsLeap);
	UTIL_PrecacheOtherWeapon(kwsCharge);

	// Alien buildings
	UTIL_PrecacheOther(kwsAlienResourceTower);
	UTIL_PrecacheOther(kwsOffenseChamber);
	UTIL_PrecacheOther(kwsDefenseChamber);
	UTIL_PrecacheOther(kwsSensoryChamber);
	UTIL_PrecacheOther(kwsMovementChamber);
	UTIL_PrecacheOther(kesTeamWebStrand);
	
	// Equipment
	//UTIL_PrecacheOtherWeapon("weapon_tripmine");
	UTIL_PrecacheOther(kwsScan);
	UTIL_PrecacheOther(kwsPhaseGate);
	//UTIL_PrecacheOther(kwsNuke);

	// Marine buildings
	UTIL_PrecacheOther(kwsTeamCommand);
	UTIL_PrecacheOther(kwsResourceTower);
	UTIL_PrecacheOther(kwsInfantryPortal);
	UTIL_PrecacheOther(kwsTurretFactory);
	UTIL_PrecacheOther(kwsArmory);
	UTIL_PrecacheOther(kwsAdvancedArmory);
	UTIL_PrecacheOther(kwsArmsLab);
	UTIL_PrecacheOther(kwsPrototypeLab);
	UTIL_PrecacheOther(kwsObservatory);
	//UTIL_PrecacheOther(kwsChemlab);
	//UTIL_PrecacheOther(kwsMedlab);
	//UTIL_PrecacheOther(kwsNukePlant);
	UTIL_PrecacheOther(kwsDeployedTurret);
	UTIL_PrecacheOther(kwsSiegeTurret);

	// container for dropped deathmatch weapons
	UTIL_PrecacheOther("weaponbox");

	UTIL_PrecacheOtherWeapon(kwsWelder);
	UTIL_PrecacheOther(kwsDeployedMine);
	UTIL_PrecacheOther(kwsHealth);
    UTIL_PrecacheOther(kwsCatalyst);
	UTIL_PrecacheOther(kwsGenericAmmo);
	UTIL_PrecacheOther(kwsHeavyArmor);
	UTIL_PrecacheOther(kwsJetpack);
	UTIL_PrecacheOther(kwsAmmoPack);
	
	UTIL_PrecacheOther(kwsDebugEntity);

	// Precache other events
	gJetpackEventID = PRECACHE_EVENT(1, kJetpackEvent);
	//gStartOverwatchEventID = PRECACHE_EVENT(1, kStartOverwatchEvent);
	//gEndOverwatchEventID = PRECACHE_EVENT(1, kEndOverwatchEvent);
	
	// Alien upgrade events
	gRegenerationEventID = PRECACHE_EVENT(1, kRegenerationEvent);
	gStartCloakEventID = PRECACHE_EVENT(1, kStartCloakEvent);
	gEndCloakEventID = PRECACHE_EVENT(1, kEndCloakEvent);

	// Extra alien weapon events
	//gEnsnareHitEventID = PRECACHE_EVENT(1, kEnsnareHitEventName);
	gSporeCloudEventID = PRECACHE_EVENT(1, kSporeCloudEventName);
	gUmbraCloudEventID = PRECACHE_EVENT(1, kUmbraCloudEventName);
	gStopScreamEventID = PRECACHE_EVENT(1, kStopPrimalScreamSoundEvent);
	
	// Extra marine events
	gTeleportEventID = PRECACHE_EVENT(1, kTeleportEvent);
	gBlinkEffectSuccessEventID = PRECACHE_EVENT(1, kBlinkEffectSuccessEventName);
	gPhaseInEventID = PRECACHE_EVENT(1, kPhaseInEvent);
	gSiegeHitEventID = PRECACHE_EVENT(1, kSiegeHitEvent);
	gSiegeViewHitEventID = PRECACHE_EVENT(1, kSiegeViewHitEvent);
	gCommanderPointsAwardedEventID = PRECACHE_EVENT(1, kCommanderPointsAwardedEvent);
	gAlienSightOnEventID = PRECACHE_EVENT(1, kAlienSightOnEvent);
	gAlienSightOffEventID = PRECACHE_EVENT(1, kAlienSightOffEvent);
//	gParalysisStartEventID = PRECACHE_EVENT(1, kParalysisStartEventName);

	//gWallJumpEventID = PRECACHE_EVENT(1, kWallJumpEvent);
	//gFlightEventID = PRECACHE_EVENT(1, kFlightEvent);
	PRECACHE_UNMODIFIED_SOUND(kConnectSound);
	//PRECACHE_UNMODIFIED_SOUND(kDisconnectSound);
	PRECACHE_UNMODIFIED_MODEL(kNullModel);

	UTIL_PrecacheOther("monster_sentry");
	
	// Allow welder events in mapper build
	gWelderEventID = PRECACHE_EVENT(1, kWelderEventName);
	gWelderConstEventID = PRECACHE_EVENT(1, kWelderConstEventName);
	PRECACHE_EVENT(1, kWelderStartEventName);
	PRECACHE_EVENT(1, kWelderEndEventName);
	
	gEmptySoundEventID = PRECACHE_EVENT(1, kEmptySoundEvent);
	gNumericalInfoEventID = PRECACHE_EVENT(1, kNumericalInfoEvent);
	gInvalidActionEventID = PRECACHE_EVENT(1, kInvalidActionEvent);
	gParticleEventID = PRECACHE_EVENT(1, kParticleEvent);
	gDistressBeaconEventID = PRECACHE_EVENT(1, kDistressBeaconEvent);
	gWeaponAnimationEventID= PRECACHE_EVENT(1, kWeaponAnimationEvent);
	gLevelUpEventID = PRECACHE_EVENT(1, kLevelUpEvent);
	gMetabolizeSuccessEventID = PRECACHE_EVENT(1, kMetabolizeSuccessEventName);
	
	PRECACHE_UNMODIFIED_SOUND(kPhaseInSound);
	
	// Precache reload sound that is hardcoded deep in HL
	PRECACHE_UNMODIFIED_SOUND(kEmptySound);
	PRECACHE_UNMODIFIED_SOUND(kInvalidSound);

	// Not sure who's using these, but I keep getting errors that they're not precached.  Buttons?
	PRECACHE_UNMODIFIED_SOUND("buttons/spark1.wav");
	PRECACHE_UNMODIFIED_SOUND("buttons/spark2.wav");
	PRECACHE_UNMODIFIED_SOUND("buttons/spark3.wav");
	PRECACHE_UNMODIFIED_SOUND("buttons/spark4.wav");
	PRECACHE_UNMODIFIED_SOUND("buttons/spark5.wav");
	PRECACHE_UNMODIFIED_SOUND("buttons/spark6.wav");

	// For grunts.  Careful, this uses the same weapon id that the grenade gun uses
	//UTIL_PrecacheOtherWeapon("weapon_9mmAR");

	// common world objects
//	UTIL_PrecacheOther( "item_suit" );
//	UTIL_PrecacheOther( "item_battery" );
//	UTIL_PrecacheOther( "item_antidote" );
//	UTIL_PrecacheOther( "item_security" );
//	UTIL_PrecacheOther( "item_longjump" );

	// shotgun
//	UTIL_PrecacheOtherWeapon( "weapon_shotgun" );
//	UTIL_PrecacheOther( "ammo_buckshot" );
//
//	// crowbar
//	UTIL_PrecacheOtherWeapon( "weapon_rowbar" );
//
//	// glock
//	UTIL_PrecacheOtherWeapon( "weapon_9mmhandgun" );
//	UTIL_PrecacheOther( "ammo_9mmclip" );
//
//	// mp5
//	UTIL_PrecacheOtherWeapon( "weapon_9mmAR" );
//	UTIL_PrecacheOther( "ammo_9mmAR" );
//	UTIL_PrecacheOther( "ammo_ARgrenades" );

//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// python
//	UTIL_PrecacheOtherWeapon( "weapon_357" );
//	UTIL_PrecacheOther( "ammo_357" );
//#endif
//	
//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// gauss
//	UTIL_PrecacheOtherWeapon( "weapon_gauss" );
//	UTIL_PrecacheOther( "ammo_gaussclip" );
//#endif
//
//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// rpg
//	UTIL_PrecacheOtherWeapon( "weapon_rpg" );
//	UTIL_PrecacheOther( "ammo_rpgclip" );
//#endif
//
//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// crossbow
//	UTIL_PrecacheOtherWeapon( "weapon_crossbow" );
//	UTIL_PrecacheOther( "ammo_crossbow" );
//	UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons
//#endif
//
//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// egon
//	UTIL_PrecacheOtherWeapon( "weapon_egon" );
//#endif
//
//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// satchel charge
//	UTIL_PrecacheOtherWeapon( "weapon_satchel" );
//#endif
//
//	// hand grenade
//	UTIL_PrecacheOtherWeapon("weapon_handgrenade");
//
//
//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// squeak grenade
//	UTIL_PrecacheOtherWeapon( "weapon_snark" );
//#endif
//
//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	// hornetgun
//	UTIL_PrecacheOtherWeapon( "weapon_hornetgun" );
//#endif


//#if !defined( OEM_BUILD ) && !defined( HLDEMO_BUILD )
//	if ( g_pGameRules->IsDeathmatch() )
//	{
//		UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons
//	}
//#endif

	g_sModelIndexFireball = PRECACHE_UNMODIFIED_MODEL ("sprites/zerogxplode.spr");// fireball
	g_sModelIndexWExplosion = PRECACHE_UNMODIFIED_MODEL ("sprites/WXplo1.spr");// underwater fireball
	g_sModelIndexSmoke = PRECACHE_UNMODIFIED_MODEL ("sprites/steam1.spr");// smoke
	g_sModelIndexBubbles = PRECACHE_UNMODIFIED_MODEL ("sprites/bubble2.spr");//bubbles
	g_sModelIndexBloodSpray = PRECACHE_UNMODIFIED_MODEL ("sprites/bloodspray.spr"); // initial blood
	g_sModelIndexBloodDrop = PRECACHE_UNMODIFIED_MODEL ("sprites/blood.spr"); // splattered blood 

	g_sModelIndexLaser = PRECACHE_UNMODIFIED_MODEL( (char *)g_pModelNameLaser );
	g_sModelIndexLaserDot = PRECACHE_UNMODIFIED_MODEL("sprites/laserdot.spr");


	// used by explosions
	PRECACHE_UNMODIFIED_MODEL ("models/grenade.mdl");
	PRECACHE_UNMODIFIED_MODEL ("sprites/explode1.spr");

	PRECACHE_SOUND ("weapons/debris1.wav");// explosion aftermaths
	PRECACHE_SOUND ("weapons/debris2.wav");// explosion aftermaths
	PRECACHE_SOUND ("weapons/debris3.wav");// explosion aftermaths

	PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound1);
	PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound2);
	PRECACHE_UNMODIFIED_SOUND (kGrenadeBounceSound3);
    PRECACHE_UNMODIFIED_SOUND (kGRHitSound);

	PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit1.wav");	// hit by bullet
	PRECACHE_UNMODIFIED_SOUND ("weapons/bullet_hit2.wav");	// hit by bullet
	
	PRECACHE_UNMODIFIED_SOUND ("items/weapondrop1.wav");// weapon falls to the ground

}


 

TYPEDESCRIPTION	CBasePlayerItem::m_SaveData[] = 
{
	DEFINE_FIELD( CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR ),
	DEFINE_FIELD( CBasePlayerItem, m_pNext, FIELD_CLASSPTR ),
	DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating );


TYPEDESCRIPTION	CBasePlayerWeapon::m_SaveData[] = 
{
	DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME ),
	DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME ),
	DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME ),
	DEFINE_FIELD( CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER ),
	DEFINE_FIELD( CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER ),
	DEFINE_FIELD( CBasePlayerWeapon, m_iClip, FIELD_INTEGER ),
	DEFINE_FIELD( CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER ),
};

IMPLEMENT_SAVERESTORE( CBasePlayerWeapon, CBasePlayerItem );


void CBasePlayerItem :: SetObjectCollisionBox( void )
{
	pev->absmin = pev->origin + Vector(-24, -24, 0);
	pev->absmax = pev->origin + Vector(24, 24, 16); 
}

BOOL
CBasePlayerItem::CanDeploy( void ) 
{ 
	return TRUE; 
}

// can this weapon be put away right now?
BOOL CBasePlayerItem::CanHolster( void ) 
{ 
	return TRUE; 
};

// returns is deploy was successful
BOOL CBasePlayerItem::Deploy( )								
{ 
	return TRUE; 
}

BOOL
CBasePlayerItem::IsUseable( void ) 
{ 
	return TRUE; 
}


//=========================================================
// Sets up movetype, size, solidtype for a new weapon. 
//=========================================================
void CBasePlayerItem :: FallInit( void )
{
	pev->movetype = MOVETYPE_TOSS;
	//SOLID_BBOX caused weapons to get stuck on eachother and float in air
	pev->solid = SOLID_BSP;

	UTIL_SetOrigin( pev, pev->origin );
	UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0) );//pointsize until it lands on the ground.
	
	SetTouch(&CBasePlayerItem::DefaultTouch );
	SetThink(&CBasePlayerItem::FallThink );

	pev->nextthink = gpGlobals->time + 0.1;
}

//=========================================================
// FallThink - Items that have just spawned run this think
// to catch them when they hit the ground. Once we're sure
// that the object is grounded, we change its solid type
// to trigger and set it in a large box that helps the
// player get it.
//=========================================================
void CBasePlayerItem::FallThink ( void )
{
	pev->nextthink = gpGlobals->time + 0.1;
	//timer for weapons stuck floating in air
	pev->fuser4 += 0.1;

	if ( pev->flags & FL_ONGROUND )
	{
		// clatter if we have an owner (i.e., dropped by someone)
		// don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!)
		if ( !FNullEnt( pev->owner ) )
		{
			int pitch = 95 + RANDOM_LONG(0,29);
			EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "items/weapondrop1.wav", 1, ATTN_NORM, 0, pitch);	
		}

		// lie flat
		pev->angles.x = 0;
		pev->angles.z = 0;

		Materialize();
		pev->fuser4 = 0;
	}
	//weapons in air for too long from collision issues with other entities change to SOLID_TRIGGER to fall to ground
	if ((pev->fuser4 > 0.7))
	{
		pev->solid = SOLID_TRIGGER;
		pev->fuser4 = 0;
	}
}

//=========================================================
// Materialize - make a CBasePlayerItem visible and tangible
//=========================================================
void CBasePlayerItem::Materialize( void )
{
	if ( pev->effects & EF_NODRAW )
	{
		// changing from invisible state to visible.
		EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 );
		pev->effects &= ~EF_NODRAW;
		pev->effects |= EF_MUZZLEFLASH;
	}

	pev->solid = SOLID_TRIGGER;

	UTIL_SetOrigin( pev, pev->origin );// link into world.
	SetTouch (&CBasePlayerItem::DefaultTouch);
	SetThink (NULL);

	this->VirtualMaterialize();
}

//=========================================================
// AttemptToMaterialize - the item is trying to rematerialize,
// should it do so now or wait longer?
//=========================================================
void CBasePlayerItem::AttemptToMaterialize( void )
{
	float time = g_pGameRules->FlWeaponTryRespawn( this );

	if ( time == 0 )
	{
		Materialize();
		return;
	}

	pev->nextthink = gpGlobals->time + time;
}

//=========================================================
// CheckRespawn - a player is taking this weapon, should 
// it respawn?
//=========================================================
void CBasePlayerItem :: CheckRespawn ( void )
{
	switch ( g_pGameRules->WeaponShouldRespawn( this ) )
	{
	case GR_WEAPON_RESPAWN_YES:
		Respawn();
		break;
	case GR_WEAPON_RESPAWN_NO:
		return;
		break;
	}
}

//=========================================================
// Respawn- this item is already in the world, but it is
// invisible and intangible. Make it visible and tangible.
//=========================================================
CBaseEntity* CBasePlayerItem::Respawn( void )
{
	// make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
	// will decide when to make the weapon visible and touchable.
	CBaseEntity *pNewWeapon = CBaseEntity::Create( (char *)STRING( pev->classname ), g_pGameRules->VecWeaponRespawnSpot( this ), pev->angles, pev->owner );

	if ( pNewWeapon )
	{
		pNewWeapon->pev->effects |= EF_NODRAW;// invisible for now
		pNewWeapon->SetTouch( NULL );// no touch
		pNewWeapon->SetThink(&CBasePlayerItem::AttemptToMaterialize );

		DROP_TO_FLOOR ( ENT(pev) );

		// not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
		// but when it should respawn is based on conditions belonging to the weapon that was taken.
		pNewWeapon->pev->nextthink = g_pGameRules->FlWeaponRespawnTime( this );
	}
	else
	{
		ALERT ( at_console, "Respawn failed to create %s!\n", STRING( pev->classname ) );
	}

	return pNewWeapon;
}

void CBasePlayerItem::DefaultTouch( CBaseEntity *pOther )
{
	// if it's not a player, ignore
	if ( !pOther->IsPlayer() )
		return;

	CBasePlayer *pPlayer = (CBasePlayer *)pOther;

	// can I have this?
	if ( !g_pGameRules->CanHavePlayerItem( pPlayer, this ) )
	{
		if ( gEvilImpulse101 )
		{
			UTIL_Remove( this );
		}
		return;
	}

	if (pOther->AddPlayerItem( this ))
	{
		AttachToPlayer( pPlayer );

		AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(pPlayer);
		if(thePlayer && thePlayer->GetIsAlien())
		{
			EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2-a.wav", 1, ATTN_NORM);
		}
		else
		{
			EMIT_SOUND(ENT(pPlayer->pev), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM);
		}
	}

	SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen?

	// Haven't had the crash but adding this in case due to weapons being able to turn to SOLID_TRIGGER before touching the ground with 2021 weapon collision fix.
	// https://github.com/ValveSoftware/halflife/pull/1599
	// If the item is falling and its Think remains FallItem after the player picks it up,
	// then after the item touches the ground its Touch will be set back to DefaultTouch,
	// so the player will pick it up again, this time Kill-ing the item (since we already have it in the inventory),
	// which will make the pointer bad and crash the game.
	if (m_pfnThink == &CBasePlayerItem::FallThink)
		SetThink(NULL);
}

BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted )
{
	if ( !isPredicted )
	{
		return ( attack_time <= curtime ) ? TRUE : FALSE;
	}
	else
	{
		return ( attack_time <= 0.0 ) ? TRUE : FALSE;
	}
}


void CBasePlayerWeapon::ItemPostFrame( void )
{
	bool theAttackPressed = (m_pPlayer->pev->button & IN_ATTACK) && !(m_pPlayer->pev->button & IN_ATTACK2);

    bool theWeaponPrimes = (this->GetWeaponPrimeTime() > 0.0f);
    bool theWeaponIsPriming = this->GetIsWeaponPriming();
    bool theWeaponIsPrimed = this->GetIsWeaponPrimed();

	if ((m_fInReload) && ( m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase() ) )
	{
		// complete the reload. 
		int j = min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]);	

		// Add them to the clip
		m_iClip += j;
		m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j;

		m_pPlayer->TabulateAmmo();

		m_fInReload = FALSE;
	}

/*	// +movement: Removed case for +attack2 since it's used for movement abilities
	if ((m_pPlayer->pev->button & IN_ATTACK2) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) )
	{
        if (m_pPlayer->GetCanUseWeapon())
        {
        
		    if ( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] )
		    {
			    m_fFireOnEmpty = TRUE;
		    }

		    m_pPlayer->TabulateAmmo();
		    SecondaryAttack();
		    m_pPlayer->pev->button &= ~IN_ATTACK2;
        }
	}
    else 
*/

	if ( theAttackPressed && m_pPlayer->GetCanUseWeapon())
	{
		if ((m_fInSpecialReload == 1 || m_fInSpecialReload == 2) && m_iClip != 0 && (CVAR_GET_FLOAT("sv_nsversion") > 322.0f))
		{
			m_fInSpecialReload = 3;
			Reload();
		}
		else if (CanAttack(m_flNextPrimaryAttack, gpGlobals->time, UseDecrement()))
        {
            if ( (m_iClip == 0 && pszAmmo1()) || (iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) )
		    {
			    m_fFireOnEmpty = TRUE;
		    }

			m_pPlayer->TabulateAmmo();
			PrimaryAttack();
        }
	}
	else if ( m_pPlayer->pev->button & IN_RELOAD && iMaxClip() != WEAPON_NOCLIP && !m_fInReload ) 
	{
        if (m_pPlayer->GetCanUseWeapon())
        {
            // reload when reload is pressed, or if no buttons are down and weapon is empty.
	        Reload();
        }
	}
	// +movement: Removed case for +attack2
	else if ( !(m_pPlayer->pev->button & (IN_ATTACK /* |IN_ATTACK2 */) ) )
	{
        if (m_pPlayer->GetCanUseWeapon())
        {

		    // no fire buttons down

		    m_fFireOnEmpty = FALSE;

		    if ( !IsUseable() && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) ) 
		    {
			    // weapon isn't useable, switch.
			    if ( !(iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) )
			    {
				    m_flNextPrimaryAttack = ( UseDecrement() ? 0.0 : gpGlobals->time ) + 0.3;
				    return;
			    }
		    }
		    else
		    {
			    // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
			    if ( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0 : gpGlobals->time ) )
			    {
                    if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0)
			        {
    				    Reload();
	    			    return;
		    	    }
                }
		    }

		    WeaponIdle( );
        }
		return;
	}
	
	// catch all
	if ( ShouldWeaponIdle() )
	{
		WeaponIdle();
	}
}

void CBasePlayerItem::DestroyItem( void )
{
	//KGP: this is a virtual function call for a reason...
	// it had been replaced with the contents of
	// CBasePlayerItem::VirtualDestroyItem, ignoring
	// AvHBasePlayerWeapon::VirtualDestroyItem in 3.01
	this->VirtualDestroyItem();
}

void CBasePlayerItem::VirtualDestroyItem( void )
{
	if ( m_pPlayer )
	{
		// if attached to a player, remove. 
		m_pPlayer->RemovePlayerItem( this );
	}
	
	Kill( );
}

int CBasePlayerItem::AddToPlayer( CBasePlayer *pPlayer )
{
	m_pPlayer = pPlayer;
	pPlayer->m_iHideHUD &= ~HIDEHUD_WEAPONS;
	return TRUE;
}

void CBasePlayerItem::Drop( void )
{
	SetTouch( NULL );
	SetThink(&CBasePlayerItem::SUB_Remove);
	pev->nextthink = gpGlobals->time + .1;
}

void CBasePlayerItem::Kill( void )
{
	SetTouch( NULL );
	SetThink(&CBasePlayerItem::SUB_Remove);
	pev->nextthink = gpGlobals->time + .1;
}

void CBasePlayerItem::Holster( int skiplocal /* = 0 */ )
{ 
	m_pPlayer->pev->viewmodel = 0; 
	m_pPlayer->pev->weaponmodel = 0;
}

void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer )
{
	pev->movetype = MOVETYPE_FOLLOW;
	pev->solid = SOLID_NOT;
	pev->aiment = pPlayer->edict();
	pev->effects = EF_NODRAW; // ??
	pev->modelindex = 0;// server won't send down to clients if modelindex == 0
	pev->model = iStringNull;
	pev->owner = pPlayer->edict();
	pev->nextthink = gpGlobals->time + .1;
	SetTouch( NULL );
}

void CBasePlayerItem::Spawn()
{
    CBaseAnimating::Spawn();
}

// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal
int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal )
{
	if ( m_iDefaultAmmo )
	{
		return ExtractAmmo( (CBasePlayerWeapon *)pOriginal );
	}
	else
	{
		// a dead player dropped this.
		return ExtractClipAmmo( (CBasePlayerWeapon *)pOriginal );
	}
}


int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer )
{
	int bResult = CBasePlayerItem::AddToPlayer( pPlayer );

	pPlayer->pev->weapons |= (1<<m_iId);

	if ( !m_iPrimaryAmmoType )
	{
		m_iPrimaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo1() );
		m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() );
	}


	if (bResult)
		return AddWeapon( );
	return FALSE;
}

int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer )
{
	BOOL bSend = FALSE;
	int state = 0;

	// KGP: folded m_iEnabled into the state value and converted it to a proper bitfield.
	if( pPlayer->m_pActiveItem == this )
	{ state |= WEAPON_IS_CURRENT; }
	if( pPlayer->m_fOnTarget )
	{ state |= WEAPON_ON_TARGET; }
	if( m_iEnabled )
	{ state |= WEAPON_IS_ENABLED; }
	
	// Forcing send of all data!
	if ( !pPlayer->m_fWeapon )
	{
		bSend = TRUE; 
	}
	
	// This is the current or last weapon, so the state will need to be updated
	if ( this == pPlayer->m_pActiveItem ||
		 this == pPlayer->m_pClientActiveItem )
	{
		if ( pPlayer->m_pActiveItem != pPlayer->m_pClientActiveItem )
		{
			bSend = TRUE; 
		}
	}

	// If the ammo, state, or fov has changed, update the weapon
	if ( m_iClip != m_iClientClip || 
		 state != m_iClientWeaponState || 
		 pPlayer->m_iFOV != pPlayer->m_iClientFOV )
	{
		bSend = TRUE;
	}

	if (m_iId == 22 || m_iId == 11 || m_iId == 21)
		gCanMove[pPlayer->entindex() - 1] = m_iEnabled;

	if ( bSend )
	{
		NetMsg_CurWeapon( pPlayer->pev, state, m_iId, m_iClip );
	    m_iClientClip = m_iClip;
	    m_iClientWeaponState = state;
	    pPlayer->m_fWeapon = TRUE;
	}

	if ( m_pNext )
		m_pNext->UpdateClientData( pPlayer );

	return 1;
}

void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body )
{
	if(iAnim >= 0)
	{
		if ( UseDecrement() )
			skiplocal = 1;
		else
			skiplocal = 0;
	
		m_pPlayer->pev->weaponanim = iAnim;
	
		if ( skiplocal && ENGINE_CANSKIP( m_pPlayer->edict() ) )
			return;
	
		MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pPlayer->pev );
			WRITE_BYTE( iAnim );						// sequence number
			WRITE_BYTE( pev->body );					// weaponmodel bodygroup.
		MESSAGE_END();
	}
}

BOOL CBasePlayerWeapon :: AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry )
{
	int iIdAmmo;

	if (iMaxClip < 1)
	{
		m_iClip = -1;
		iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry );
	}
	else if (m_iClip == 0)
	{
		int i;
		i = min( m_iClip + iCount, iMaxClip ) - m_iClip;
		m_iClip += i;
		iIdAmmo = m_pPlayer->GiveAmmo( iCount - i, szName, iMaxCarry );
	}
	else
	{
		iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry );
	}
	
	// m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = iMaxCarry; // hack for testing

	if (iIdAmmo > 0)
	{
		m_iPrimaryAmmoType = iIdAmmo;
		if (m_pPlayer->HasPlayerItem( this ) )
		{
			// play the "got ammo" sound only if we gave some ammo to a player that already had this gun.
			// if the player is just getting this gun for the first time, DefaultTouch will play the "picked up gun" sound for us.
			EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
		}
	}

	return iIdAmmo > 0 ? TRUE : FALSE;
}


BOOL CBasePlayerWeapon :: AddSecondaryAmmo( int iCount, char *szName, int iMax )
{
	int iIdAmmo;

	iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMax );

	//m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] = iMax; // hack for testing

	if (iIdAmmo > 0)
	{
		m_iSecondaryAmmoType = iIdAmmo;
		EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
	}
	return iIdAmmo > 0 ? TRUE : FALSE;
}

//=========================================================
// IsUseable - this function determines whether or not a 
// weapon is useable by the player in its current state. 
// (does it have ammo loaded? do I have any ammo for the 
// weapon?, etc)
//=========================================================
BOOL CBasePlayerWeapon :: IsUseable( void )
{
	if ( m_iClip <= 0 )
	{
		// This looks like a nasty bug Valve didn't notice
		ASSERT(PrimaryAmmoIndex() >= 0);

		if ( m_pPlayer->m_rgAmmo[ PrimaryAmmoIndex() ] <= 0 && iMaxAmmo1() != -1 )			
		{
			// clip is empty (or nonexistant) and the player has no more ammo of this type. 
			return FALSE;
		}
	}

	return TRUE;
}

BOOL CBasePlayerWeapon :: CanDeploy( void )
{
	BOOL bHasAmmo = 0;

	// All weapons can always deploy.  Once fire is hit, it checks if it's out of ammo.  If so, the weapon switches.
	// This is needed so ammo packs function correctly, and it also feels more responsive.

//	if ( !pszAmmo1() )
//	{
//		// this weapon doesn't use ammo, can always deploy.
//		return TRUE;
//	}
//
//	if ( pszAmmo1() )
//	{
//		bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0);
//	}
//	if ( pszAmmo2() )
//	{
//		bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0);
//	}
//	if (m_iClip > 0)
//	{
//		bHasAmmo |= 1;
//	}
//	if (!bHasAmmo)
//	{
//		return FALSE;
//	}

	return TRUE;
}

BOOL CBasePlayerWeapon :: DefaultDeploy( char *szViewModel, char *szWeaponModel, int iAnim, char *szAnimExt, int skiplocal, int body)
{
	if (!CanDeploy( ))
		return FALSE;

	m_pPlayer->TabulateAmmo();
	m_pPlayer->pev->viewmodel = MAKE_STRING(szViewModel);
	m_pPlayer->pev->weaponmodel = MAKE_STRING(szWeaponModel);
	strcpy( m_pPlayer->m_szAnimExtention, szAnimExt );
	SendWeaponAnim( iAnim, skiplocal, body );

	// Set the player animation as well
	//this->m_pPlayer->SetAnimation(PLAYER_ANIM(iAnim));

	m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + this->GetDeployTime();
	m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + this->GetDeployTime() + kDeployIdleInterval;

	return TRUE;
}


BOOL CBasePlayerWeapon :: DefaultReload( int iClipSize, int iAnim, float fDelay, int body)
{
	if (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)
		return FALSE;

	int j = min(iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]);	

	if (j == 0)
		return FALSE;

	m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay;

	//!!UNDONE -- reload sound goes here !!!
	//SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 );

	m_fInReload = TRUE;

	m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + kDeployIdleInterval;

	return TRUE;
}

BOOL CBasePlayerWeapon :: PlayEmptySound( void )
{
	if (m_iPlayEmptySound)
	{
//		EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM);

		int flags = FEV_NOTHOST;
		
		PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), gEmptySoundEventID, 0.0, (float *)&(m_pPlayer->pev->origin), (float *)&g_vecZero, 0.0, 0.0, 0, 0, 0, 0 );

		m_iPlayEmptySound = 0;
		return 0;
	}
	return 0;
}

void CBasePlayerWeapon :: ResetEmptySound( void )
{
	m_iPlayEmptySound = 1;
}

//=========================================================
//=========================================================
int CBasePlayerWeapon::PrimaryAmmoIndex( void )
{
	return m_iPrimaryAmmoType;
}

//=========================================================
//=========================================================
int CBasePlayerWeapon::SecondaryAmmoIndex( void )
{
	return -1;
}

void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ )
{ 
	m_fInReload = FALSE; // cancel any reload in progress.
	m_pPlayer->pev->viewmodel = 0; 
	m_pPlayer->pev->weaponmodel = 0;
}

void CBasePlayerAmmo::Spawn( void )
{
	pev->movetype = MOVETYPE_TOSS;
	pev->solid = SOLID_TRIGGER;
	UTIL_SetSize(pev, Vector(-16, -16, 0), Vector(16, 16, 16));
	UTIL_SetOrigin( pev, pev->origin );

	SetTouch(&CBasePlayerAmmo::DefaultTouch );
}

CBaseEntity* CBasePlayerAmmo::Respawn( void )
{
	pev->effects |= EF_NODRAW;
	SetTouch( NULL );

	UTIL_SetOrigin( pev, g_pGameRules->VecAmmoRespawnSpot( this ) );// move to wherever I'm supposed to repawn.

	SetThink(&CBasePlayerAmmo::Materialize );
	pev->nextthink = g_pGameRules->FlAmmoRespawnTime( this );

	return this;
}

void CBasePlayerAmmo::Materialize( void )
{
	if ( pev->effects & EF_NODRAW )
	{
		// changing from invisible state to visible.
		EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 );
		pev->effects &= ~EF_NODRAW;
		pev->effects |= EF_MUZZLEFLASH;
	}

	SetTouch(&CBasePlayerAmmo::DefaultTouch );
}

void CBasePlayerAmmo :: DefaultTouch( CBaseEntity *pOther )
{
	if ( !pOther->IsPlayer() )
	{
		return;
	}

	if (AddAmmo( pOther ))
	{
		if ( g_pGameRules->AmmoShouldRespawn( this ) == GR_AMMO_RESPAWN_YES )
		{
			Respawn();
		}
		else
		{
			SetTouch( NULL );
			SetThink(&CBasePlayerAmmo::SUB_Remove);
			pev->nextthink = gpGlobals->time + .1;
		}
	}
	else if (gEvilImpulse101)
	{
		// evil impulse 101 hack, kill always
		SetTouch( NULL );
		SetThink(&CBasePlayerAmmo::SUB_Remove);
		pev->nextthink = gpGlobals->time + .1;
	}
}

//=========================================================
// called by the new item with the existing item as parameter
//
// if we call ExtractAmmo(), it's because the player is picking up this type of weapon for 
// the first time. If it is spawned by the world, m_iDefaultAmmo will have a default ammo amount in it.
// if  this is a weapon dropped by a dying player, has 0 m_iDefaultAmmo, which means only the ammo in 
// the weapon clip comes along. 
//=========================================================
int CBasePlayerWeapon::ExtractAmmo( CBasePlayerWeapon *pWeapon )
{
	int			iReturn;

	if ( pszAmmo1() != NULL )
	{
		// blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero,
		// we only get the ammo in the weapon's clip, which is what we want. 
		iReturn = pWeapon->AddPrimaryAmmo( m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1() );
		m_iDefaultAmmo = 0;
	}

	if ( pszAmmo2() != NULL )
	{
		iReturn = pWeapon->AddSecondaryAmmo( 0, (char *)pszAmmo2(), iMaxAmmo2() );
	}

	return iReturn;
}

//=========================================================
// called by the new item's class with the existing item as parameter
//=========================================================
int CBasePlayerWeapon::ExtractClipAmmo( CBasePlayerWeapon *pWeapon )
{
	int			iAmmo;

	if ( m_iClip == WEAPON_NOCLIP )
	{
		iAmmo = 0;// guns with no clips always come empty if they are second-hand
	}
	else
	{
		iAmmo = m_iClip;
	}
	
	return pWeapon->m_pPlayer->GiveAmmo( iAmmo, (char *)pszAmmo1(), iMaxAmmo1() ); // , &m_iPrimaryAmmoType
}
	
//=========================================================
// RetireWeapon - no more ammo for this gun, put it away.
//=========================================================
void CBasePlayerWeapon::RetireWeapon( void )
{
	// first, no viewmodel at all.
	m_pPlayer->pev->viewmodel = iStringNull;
	m_pPlayer->pev->weaponmodel = iStringNull;
	//m_pPlayer->pev->viewmodelindex = NULL;

	g_pGameRules->GetNextBestWeapon( m_pPlayer, this );
}

//*********************************************************
// weaponbox code:
//*********************************************************

LINK_ENTITY_TO_CLASS( weaponbox, CWeaponBox );

TYPEDESCRIPTION	CWeaponBox::m_SaveData[] = 
{
	DEFINE_ARRAY( CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ),
	DEFINE_ARRAY( CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ),
	DEFINE_ARRAY( CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ),
	DEFINE_FIELD( CWeaponBox, m_cAmmoTypes, FIELD_INTEGER ),
};

IMPLEMENT_SAVERESTORE( CWeaponBox, CBaseEntity );

//=========================================================
//
//=========================================================
void CWeaponBox::Precache( void )
{
	PRECACHE_UNMODIFIED_MODEL("models/w_weaponbox.mdl");
}

//=========================================================
//=========================================================
void CWeaponBox :: KeyValue( KeyValueData *pkvd )
{
	if ( m_cAmmoTypes < MAX_AMMO_SLOTS )
	{
		PackAmmo( ALLOC_STRING(pkvd->szKeyName), atoi(pkvd->szValue) );
		m_cAmmoTypes++;// count this new ammo type.

		pkvd->fHandled = TRUE;
	}
	else
	{
		ALERT ( at_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS );
	}
}

//=========================================================
// CWeaponBox - Spawn 
//=========================================================
void CWeaponBox::Spawn( void )
{
	Precache( );

	pev->movetype = MOVETYPE_TOSS;
	pev->solid = SOLID_TRIGGER;

	UTIL_SetSize( pev, g_vecZero, g_vecZero );

	SET_MODEL( ENT(pev), "models/w_weaponbox.mdl");
}

//=========================================================
// CWeaponBox - Kill - the think function that removes the
// box from the world.
//=========================================================
void CWeaponBox::Kill( void )
{
	CBasePlayerItem *pWeapon;
	int i;

	// destroy the weapons
	for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
	{
		pWeapon = m_rgpPlayerItems[ i ];

		while ( pWeapon )
		{
			pWeapon->SetThink(&CWeaponBox::SUB_Remove);
			pWeapon->pev->nextthink = gpGlobals->time + 0.1;
			pWeapon = pWeapon->m_pNext;
		}
	}

	// remove the box
	UTIL_Remove( this );
}

//=========================================================
// CWeaponBox - Touch: try to add my contents to the toucher
// if the toucher is a player.
//=========================================================
void CWeaponBox::Touch( CBaseEntity *pOther )
{
	if ( !(pev->flags & FL_ONGROUND ) )
	{
		return;
	}

	if ( !pOther->IsPlayer() )
	{
		// only players may touch a weaponbox.
		return;
	}

	if ( !pOther->IsAlive() )
	{
		// no dead guys.
		return;
	}

	CBasePlayer *pPlayer = (CBasePlayer *)pOther;
	int i;

// dole out ammo
	for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
	{
		if ( !FStringNull( m_rgiszAmmo[ i ] ) )
		{
			// there's some ammo of this type. 
			pPlayer->GiveAmmo( m_rgAmmo[ i ], (char *)STRING( m_rgiszAmmo[ i ] ), MaxAmmoCarry( m_rgiszAmmo[ i ] ) );

			//ALERT ( at_console, "Gave %d rounds of %s\n", m_rgAmmo[i], STRING(m_rgiszAmmo[i]) );

			// now empty the ammo from the weaponbox since we just gave it to the player
			m_rgiszAmmo[ i ] = iStringNull;
			m_rgAmmo[ i ] = 0;
		}
	}

// go through my weapons and try to give the usable ones to the player. 
// it's important the the player be given ammo first, so the weapons code doesn't refuse 
// to deploy a better weapon that the player may pick up because he has no ammo for it.
	for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
	{
		if ( m_rgpPlayerItems[ i ] )
		{
			CBasePlayerItem *pItem;

			// have at least one weapon in this slot
			while ( m_rgpPlayerItems[ i ] )
			{
				//ALERT ( at_console, "trying to give %s\n", STRING( m_rgpPlayerItems[ i ]->pev->classname ) );

				pItem = m_rgpPlayerItems[ i ];
				m_rgpPlayerItems[ i ] = m_rgpPlayerItems[ i ]->m_pNext;// unlink this weapon from the box

				if ( pPlayer->AddPlayerItem( pItem ) )
				{
					pItem->AttachToPlayer( pPlayer );
				}
			}
		}
	}

	EMIT_SOUND( pOther->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
	SetTouch(NULL);
	UTIL_Remove(this);
}

//=========================================================
// CWeaponBox - PackWeapon: Add this weapon to the box
//=========================================================
BOOL CWeaponBox::PackWeapon( CBasePlayerItem *pWeapon )
{
	// is one of these weapons already packed in this box?
	if ( HasWeapon( pWeapon ) )
	{
		return FALSE;// box can only hold one of each weapon type
	}

	if ( pWeapon->m_pPlayer )
	{
		if ( !pWeapon->m_pPlayer->RemovePlayerItem( pWeapon ) )
		{
			// failed to unhook the weapon from the player!
			return FALSE;
		}
	}

	int iWeaponSlot = pWeapon->iItemSlot();
	
	if ( m_rgpPlayerItems[ iWeaponSlot ] )
	{
		// there's already one weapon in this slot, so link this into the slot's column
		pWeapon->m_pNext = m_rgpPlayerItems[ iWeaponSlot ];	
		m_rgpPlayerItems[ iWeaponSlot ] = pWeapon;
	}
	else
	{
		// first weapon we have for this slot
		m_rgpPlayerItems[ iWeaponSlot ] = pWeapon;
		pWeapon->m_pNext = NULL;	
	}

	pWeapon->pev->spawnflags |= SF_NORESPAWN;// never respawn
	pWeapon->pev->movetype = MOVETYPE_NONE;
	pWeapon->pev->solid = SOLID_NOT;
	pWeapon->pev->effects = EF_NODRAW;
	pWeapon->pev->modelindex = 0;
	pWeapon->pev->model = iStringNull;
	pWeapon->pev->owner = edict();
	pWeapon->SetThink( NULL );// crowbar may be trying to swing again, etc.
	pWeapon->SetTouch( NULL );
	pWeapon->m_pPlayer = NULL;

	//ALERT ( at_console, "packed %s\n", STRING(pWeapon->pev->classname) );

	return TRUE;
}

//=========================================================
// CWeaponBox - PackAmmo
//=========================================================
BOOL CWeaponBox::PackAmmo( int iszName, int iCount )
{
	int iMaxCarry;

	if ( FStringNull( iszName ) )
	{
		// error here
		ALERT ( at_console, "NULL String in PackAmmo!\n" );
		return FALSE;
	}
	
	iMaxCarry = MaxAmmoCarry( iszName );

	if ( iMaxCarry != -1 && iCount > 0 )
	{
		//ALERT ( at_console, "Packed %d rounds of %s\n", iCount, STRING(iszName) );
		GiveAmmo( iCount, (char *)STRING( iszName ), iMaxCarry );
		return TRUE;
	}

	return FALSE;
}

//=========================================================
// CWeaponBox - GiveAmmo
//=========================================================
int CWeaponBox::GiveAmmo( int iCount, char *szName, int iMax, int *pIndex/* = NULL*/ )
{
	int i;

	for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull( m_rgiszAmmo[i] ); i++)
	{
		if (stricmp( szName, STRING( m_rgiszAmmo[i])) == 0)
		{
			if (pIndex)
				*pIndex = i;

			int iAdd = min( iCount, iMax - m_rgAmmo[i]);
			if (iCount == 0 || iAdd > 0)
			{
				m_rgAmmo[i] += iAdd;

				return i;
			}
			return -1;
		}
	}
	if (i < MAX_AMMO_SLOTS)
	{
		if (pIndex)
			*pIndex = i;

		m_rgiszAmmo[i] = MAKE_STRING( szName );
		m_rgAmmo[i] = iCount;

		return i;
	}
	ALERT( at_console, "out of named ammo slots\n");
	return i;
}

//=========================================================
// CWeaponBox::HasWeapon - is a weapon of this type already
// packed in this box?
//=========================================================
BOOL CWeaponBox::HasWeapon( CBasePlayerItem *pCheckItem )
{
	CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()];

	while (pItem)
	{
		if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) ))
		{
			return TRUE;
		}
		pItem = pItem->m_pNext;
	}

	return FALSE;
}

//=========================================================
// CWeaponBox::IsEmpty - is there anything in this box?
//=========================================================
BOOL CWeaponBox::IsEmpty( void )
{
	int i;

	for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
	{
		if ( m_rgpPlayerItems[ i ] )
		{
			return FALSE;
		}
	}

	for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
	{
		if ( !FStringNull( m_rgiszAmmo[ i ] ) )
		{
			// still have a bit of this type of ammo
			return FALSE;
		}
	}

	return TRUE;
}

//=========================================================
//=========================================================
void CWeaponBox::SetObjectCollisionBox( void )
{
	pev->absmin = pev->origin + Vector(-16, -16, 0);
	pev->absmax = pev->origin + Vector(16, 16, 16); 
}


void CBasePlayerWeapon::PrintState( void )
{
	ALERT( at_console, "primary:  %f\n", m_flNextPrimaryAttack );
	ALERT( at_console, "idle   :  %f\n", m_flTimeWeaponIdle );

//	ALERT( at_console, "nextrl :  %f\n", m_flNextReload );
//	ALERT( at_console, "nextpum:  %f\n", m_flPumpTime );

//	ALERT( at_console, "m_frt  :  %f\n", m_fReloadTime );
	ALERT( at_console, "m_finre:  %i\n", m_fInReload );
//	ALERT( at_console, "m_finsr:  %i\n", m_fInSpecialReload );

	ALERT( at_console, "m_iclip:  %i\n", m_iClip );
}


TYPEDESCRIPTION	CRpg::m_SaveData[] = 
{
	DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ),
	DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon );

TYPEDESCRIPTION	CRpgRocket::m_SaveData[] = 
{
	DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ),
	DEFINE_FIELD( CRpgRocket, m_pLauncher, FIELD_CLASSPTR ),
};
IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade );

TYPEDESCRIPTION	CShotgun::m_SaveData[] = 
{
	DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ),
	DEFINE_FIELD( CShotgun, m_fInSpecialReload, FIELD_INTEGER ),
	DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ),
	// DEFINE_FIELD( CShotgun, m_iShell, FIELD_INTEGER ),
	DEFINE_FIELD( CShotgun, m_flPumpTime, FIELD_TIME ),
};
IMPLEMENT_SAVERESTORE( CShotgun, CBasePlayerWeapon );

TYPEDESCRIPTION	CGauss::m_SaveData[] = 
{
	DEFINE_FIELD( CGauss, m_fInAttack, FIELD_INTEGER ),
//	DEFINE_FIELD( CGauss, m_flStartCharge, FIELD_TIME ),
//	DEFINE_FIELD( CGauss, m_flPlayAftershock, FIELD_TIME ),
//	DEFINE_FIELD( CGauss, m_flNextAmmoBurn, FIELD_TIME ),
	DEFINE_FIELD( CGauss, m_fPrimaryFire, FIELD_BOOLEAN ),
};
IMPLEMENT_SAVERESTORE( CGauss, CBasePlayerWeapon );

TYPEDESCRIPTION	CEgon::m_SaveData[] = 
{
//	DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ),
//	DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ),
//	DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ),
	DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ),
	DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ),
	DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ),
	DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ),
	DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ),
};
IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon );

TYPEDESCRIPTION	CSatchel::m_SaveData[] = 
{
	DEFINE_FIELD( CSatchel, m_chargeReady, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon );