//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/weapon.cpp                       $
// $Revision:: 143                                                            $
//   $Author:: Jimdose                                                        $
//     $Date:: 12/16/98 5:43p                                                 $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/weapon.cpp                            $
// 
// 143   12/16/98 5:43p Jimdose
// infiniterockets no longer gives infinite mines
// 
// 142   12/14/98 7:00p Jimdose
// added sv_infinitesniper
// 
// 141   12/09/98 8:06p Jimdose
// added infinite ammo vars
// 
// 140   11/19/98 9:29p Jimdose
// fixed dropping weapons when on different gravaxis
// 
// 139   11/18/98 12:21a Jimdose
// Fixed gravaxis stuff.
// 
// 138   11/15/98 11:19p Jimdose
// Fixed bug where reloading only worked if you had less ammo in your clip than
// in your inventory
// 
// 137   11/13/98 1:47a Markd
// Put in FL_ROTATEDBOUNDS modification when picked up and then dropped
// 
// 136   11/07/98 10:46p Markd
// don't reload weapons while in cinematic
// 
// 135   10/22/98 7:57p Markd
// put in proper pre-caching in all the classes
// 
// 134   10/22/98 12:19a Jimdose
// Added aimanim and aimframe
// Added SetAimAnim
// 
// 133   10/21/98 5:29p Aldie
// Moved around mutant checks for picking up weapons
// 
// 132   10/21/98 1:30a Jimdose
// DoneReloading now sends a weapon ready event to sentient
// 
// 131   10/20/98 8:23p Jimdose
// no longer pickup weapons when you already have that weapon and enough ammo
// no longer switch to weapon when picking up if you already have it
// 
// 130   10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when
// possible)
// isSubclassOf no longer requires ::_classinfo()
// moved Magnum-specific Drop code to magnum.cpp
// 
// 129   10/18/98 6:42p Markd
// Added notdroppable event to weapons
// 
// 128   10/16/98 9:50p Aldie
// Added SecondaryAmmo command
// 
// 127   10/16/98 12:59a Aldie
// Fixed ammo types
// 
// 126   10/14/98 1:46a Markd
// Moved RandomGlobalSound to null out weaponidle before detach
// 
// 125   10/14/98 1:20a Jimdose
// Got cross-level persistant info working
// 
// 124   10/11/98 7:41p Aldie
// Mutate and restore commands for Richard
// 
// 123   10/10/98 5:58p Aldie
// More quantumdestab fixes
// 
// 122   10/09/98 2:07a Aldie
// Updated DMFLAGS
// 
// 121   10/08/98 5:47p Aldie
// Be able to pickup up dropped weapons in DF_WEAPONSSTAY mode
// 
// 120   10/08/98 12:36a Jimdose
// Added ForceIdle
// 
// 119   10/06/98 5:24p Markd
// put in bullet-proofing of owner variable
// 
// 118   10/05/98 10:43p Aldie
// Converted over to new silencer methods
// 
// 117   10/04/98 10:25p Markd
// Took out annoying warning, made the trace for gun position to muzzle
// position more bullet proof
// 
// 116   10/03/98 7:14p Markd
// fixed muzzle position stuff
// 
// 115   10/01/98 8:00p Aldie
// Don't pickup a weapon when mutant
// 
// 114   9/19/98 4:47p Markd
// fixed music stuff and added actionincrement to weapons
// 
// 113   9/12/98 11:10p Aldie
// Don't detach in putaway
// 
// 112   9/12/98 5:16p Jimdose
// Added call to DetachGun in PutAway
// 
// 111   9/09/98 6:24p Markd
// Added IsDroppable method
// 
// 110   9/07/98 7:49p Markd
// went back to previous attaching of weapons
// 
// 109   9/07/98 6:21p Markd
// don't attach if no worldmodel
// 
// 108   9/05/98 12:13p Aldie
// Fixed a bug that 2015 found.
// 
// 107   9/02/98 11:08a Markd
// Put in setModel into Sentient so that weapon could be properly detached and
// re-attached again.
// 
// 106   8/31/98 7:45p Aldie
// Updated surface data structure and removed surfinfo field
// 
// 105   8/31/98 4:19p Markd
// Made GetMuzzlePosition behave a bit more rationally.
// 
// 104   8/29/98 9:47p Jimdose
// Added call info to G_Trace
// 
// 103   8/29/98 5:27p Markd
// added specialfx, replaced misc with specialfx where appropriate
// 
// 102   8/19/98 4:56p Jimdose
// Added check for "gun" bone to MuzzleOffset
// 
// 101   8/18/98 8:12p Aldie
// Added dual mode weapons to base class
// 
// 100   8/18/98 7:39p Aldie
// Added dual weapon modes to base class
// 
// 99    8/14/98 5:48p Aldie
// Removed bloodsplat completely from weapon
// 
// 98    8/13/98 7:29p Aldie
// Fixed magnum dropping when it gets shot out of your hand.
// 
// 97    8/10/98 6:53p Aldie
// Added a better scaling for bloodsplats.
// 
// 96    8/06/98 10:53p Aldie
// Added weapon tweaks and kickback.  Also modified blast radius damage and
// rocket jumping.
// 
// 95    8/06/98 6:54p Jimdose
// Added min/max range, and projkectile speed
// 
// 94    7/26/98 2:13p Aldie
// Don't put bulletholes on hidden objects
// 
// 93    7/26/98 9:22a Jimdose
// Bulletholes are now bound to entities
// 
// 92    7/25/98 6:35p Aldie
// Bulletholes only in single player
// 
// 91    7/24/98 3:50p Aldie
// Fix for deathmatch flag weapons staying around
// 
// 90    7/22/98 9:58p Markd
// Added Raising, PuttingAway and Reloading functions
// 
// 89    7/22/98 8:18p Aldie
// Made sure reloading only happens when weapon is ready.
// 
// 88    7/22/98 7:03p Aldie
// Fixed reloading while reloading :-)
// 
// 87    7/22/98 5:16p Aldie
// Fixed NextAttack time
// 
// 86    7/20/98 6:43p Markd
// Put in new muzzle offset stuff
// 
// 85    7/20/98 3:54p Aldie
// Made weapons post the init commands in the future.
// 
// 84    7/19/98 5:41p Markd
// Added DetachFromOwner method
// 
// 83    7/17/98 4:04p Markd
// Added HasAnim support to putaway and ready
// 
// 82    7/17/98 11:32a Aldie
// Added clipammo and forcereload stuff
// 
// 81    7/15/98 9:59p Markd
// Made muzzle flashes into TEMP_DLIGHT's
// 
// 80    7/14/98 6:58p Aldie
// Made dropped weapons fade out
// 
// 79    7/14/98 5:52p Aldie
// Don't drop magnums in deathmatch.  Made the delay to pick up dropped weapons
// a little longer for the owner.
// 
// 78    7/14/98 3:54p Markd
// made weapons cycle through their models when spawning
// 
// 77    7/12/98 7:27p Jimdose
// Removed code setting scale and frame
// 
// 76    7/11/98 8:20p Jimdose
// Drop now returns true or false depending upon whether the item can be
// dropped or not
// 
// 75    7/10/98 5:39p Aldie
// Fixed printf of a warning
// 
// 74    7/10/98 12:06a Aldie
// Autochange override when you pickup a new weapon.  Currently only the
// sniperrifle will not be putaway if you are using it.
// 
// 73    7/09/98 10:41p Aldie
// If no barrel in weapon, look for barrel bone in owner
// 
// 72    7/01/98 7:04p Aldie
// Fixed assert with weapon reloading after death
// 
// 71    6/26/98 12:24p Markd
// Changed SetAmmoAmount fixed picking upweapons
// 
// 70    6/26/98 11:30a Markd
// Fixed weapon pickup bug
// 
// 69    6/25/98 8:47p Markd
// Added keyed items for Triggers, Rewrote Item class, rewrote every pickup
// method
// 
// 68    6/25/98 12:43p Aldie
// Changed bloodsplat scale and made a triggerdelay for dropping weapons
// 
// 67    6/24/98 1:38p Aldie
// Implementation of inventory system and picking stuff up
// 
// 66    6/19/98 9:28p Jimdose
// Added general weapon muzzle code
// 
// 65    6/17/98 1:20a Jimdose
// Moved setOwner to Item.  
// Added EV_Item_Pickup
// 
// 64    6/16/98 4:08p Jimdose
// Gave dropping weapons velocity
// 
// 63    6/15/98 10:39a Aldie
// Created MuzzleOffset derived from MuzzlePosition
// 
// 62    6/11/98 12:40a Markd
// Added BasePosition and MuzzlePosition
// 
// 61    6/10/98 9:49p Markd
// Made Finish attack set attack_finished to level.time not 0
// 
// 60    6/10/98 7:45p Aldie
// Updated some reload functions.
// 
// 59    6/10/98 3:46p Markd
// replaced instance of AmmoAvailable with HasAmmor to make monsters work right
// 
// 58    6/10/98 2:10p Aldie
// Updated damage function.
// 
// 57    6/10/98 1:18p Markd
// Added SetAmmoClipSize event removed NextAttack(0) from AttackDone
// 
// 56    6/09/98 7:37p Markd
// Put in "0" ammo_clip_size support
// 
// 55    6/09/98 3:58p Markd
// Fixed bug with double pickup of ammos from dropped guns
// 
// 54    6/09/98 12:54p Aldie
// Updated ReadyToFire function
// 
// 53    6/08/98 8:39p Markd
// Fixed some reload issues
// 
// 52    6/08/98 8:19p Markd
// Added reloading support and ReadyToUse function
// 
// 51    6/08/98 5:43p Markd
// removed "shoot" command from Fire function
// 
// 50    6/05/98 6:21p Markd
// When picking up weapons, ammo is given if you already have that weapon
// 
// 49    6/05/98 6:26p Aldie
// Added location to apply multi damage
// 
// 48    6/04/98 7:36p Jimdose
// Drop now posts EV_Item_DropToFloor instead of processing it.
// 
// 47    6/03/98 4:39p Markd
// Removed dummy weapon, there is now just a single weapon that gets sent over
// 
// 46    5/27/98 5:03a Aldie
// Added queue for bulletsplats
// 
// 45    5/27/98 3:20a Markd
// zeroed out animations for dropped weapons
// 
// 44    5/27/98 2:33a Markd
// When dropping weapons, made sure to zero out the anim and frame for the
// weapon
// 
// 43    5/25/98 8:51a Aldie
// Fixed sprayblood with masked textures
// 
// 42    5/25/98 6:52a Aldie
// Added maxbulletholes to stop from overflowing.
// 
// 41    5/24/98 8:46p Jimdose
// Made a lot of functions more str-friendly.
// Got rid of a lot of char * based strings
// Cleaned up get spawn arg functions and sound functions
// sound functions now use consistant syntax
// 
// 40    5/24/98 1:05a Jimdose
// Changed worldmodel, viewmodel, and ammotype to str objects
// Added sound events for ai
// 
// 39    5/23/98 10:20p Aldie
// Made some changes to bulletholes to try to get them to work with ents, but
// no luck.
// 
// 38    5/23/98 4:48p Aldie
// Added bulletholes
// 
// 37    5/14/98 4:27p Markd
// Fixed NULL weapon model issue
// 
// 36    5/13/98 4:55p Jimdose
// now uses SafePtrs
// got rid of weapon_owner.  replaced with owner which is a SafePtr.
// 
// 35    5/11/98 11:25a Markd
// Changed some worldmodel stuff
// 
// 34    5/03/98 4:34p Jimdose
// Changed Vector class
// 
// 33    4/14/98 9:58p Jimdose
// Made SetModels set the dummy model if it exists
// 
// 32    4/10/98 12:36a Jimdose
// Fire now cancels EV_Weapon_DoneFiring
// 
// 31    4/09/98 3:30p Jimdose
// Added gettype and settype
// added sound so that anim sounds play from the owner
// 
// 30    4/08/98 4:55p Jimdose
// Made ShowDummyOnClient and HideDummyOnClient check if dummy is NULL
// Fixed the bug with weapons not being shown on monsters.  The dummy's origin
// was non 0,0,0
// 
// 29    4/08/98 4:46p Jimdose
// Added ShowDummyOnClient and HideDummyOnClient
// 
// 28    4/07/98 8:19p Jimdose
// Made assaultrifle fire at normal speed
// 
// 27    4/07/98 8:00p Markd
// removed defhandle, changed all SINMDL calls to modelindex calls, removed
// SINMDL prefix
// 
// 26    4/07/98 6:45p Jimdose
// Rewrote weapon code.
// Fixed weapon switching bugs
// fixed crash from freeing weapons at level changes
// Added order to rank
// 
// 25    4/06/98 7:10p Aldie
// Added zooming for SniperRifle
// 
// 24    4/05/98 11:17p Jimdose
// No longer see the gun lerping around
// 
// 23    4/05/98 5:29p Jimdose
// Weapon attaching is back in (previous bug was probably related to not
// freeing the ents after a map change...go figure).
// 
// 22    4/04/98 7:28p Jimdose
// Can't spray blood against sky anymore
// 
// 21    4/04/98 6:11p Jimdose
// Made response from EV_Trigger_ActivateTargets to EV_Trigger_Effect
// Added attachable guns (currently disabled)
// 
// 20    4/02/98 4:53p Jimdose
// Added AmmoAvailable
// Made SprayBlood work
// Added check for valid owner in AnimateFlash
// 
// 19    3/31/98 1:05a Jimdose
// Made monsters always have ammo
// 
// 18    3/30/98 2:35p Jimdose
// Added Ammo
// Added world models
// 
// 17    3/27/98 11:03p Jimdose
// Added muzzle flash
// 
// 16    3/26/98 8:17p Jimdose
// Temporarily disabled spray blood function
// 
// 15    3/23/98 1:31p Jimdose
// Revamped event and command system
// 
// 14    3/18/98 7:24p Jimdose
// Added code for handling readying and putting away weapons
// Made work with new animation system
// 
// 13    3/02/98 8:49p Jimdose
// Changed the classid parameter of CLASS_DECLARATION to a quoted string so
// that you could have a NULL classid.
// 
// 12    3/02/98 5:36p Jimdose
// Fixed setOwner so that it handles NULL being passed in when entites are
// freed or when the weapon is dropped.
// 
// 11    2/19/98 2:35p Jimdose
// Updated to work with Q2 based progs
// 
// 9     12/15/97 12:04p Aldie
// Fixed error message from coming up when checking for reload animations when
// there was none (I.e. Melee weapons).
// 
// 8     12/14/97 7:49p Jimdose
// Fixed bug where if a weapon didn't have a "reload" animation, every other
// time the player tried to fire would fail.
// 
// 7     12/12/97 2:10p Markd
// Changed NextAttacks in CheckReload and Fire to 1 second instead of 5
// 
// 6     12/11/97 3:31p Markd
// Added SprayBlood function
// 
// 5     12/09/97 7:46p Markd
// Added range variable.  Added different behavior when firing...if there is no
// model, then go ahead and shoot right away
// 
// 4     11/18/97 5:28p Markd
// Put in CheckTwitch, CheckIdle, put in clip support and other goodies
// 
// 3     10/27/97 3:30p Jimdose
// Removed dependency on quakedef.h
// 
// 2     9/26/97 4:46p Jimdose
// Added standard Ritual header
//
// DESCRIPTION:
// Source file for Weapon class.  The weapon class is the base class for
// all weapons in Sin.  Any entity created from a class derived from the weapon
// class will be usable by any Sentient (players and monsters) as a weapon.
// 

#include "g_local.h"
#include "entity.h"
#include "item.h"
#include "weapon.h"
#include "scriptmaster.h"
#include "sentient.h"
#include "misc.h"
#include "specialfx.h"

#ifdef SIN_ARCADE
static ScriptVariablePtr sv_infinitebullets;
static ScriptVariablePtr sv_infiniterockets;
static ScriptVariablePtr sv_infiniteplasma;
static ScriptVariablePtr sv_infinitespears;
static ScriptVariablePtr sv_infinitesniper;
#endif

CLASS_DECLARATION( Item, Weapon, NULL );

Event EV_Weapon_Shoot( "shoot" );
Event EV_Weapon_FinishAttack( "attack_finished" );
Event EV_Weapon_DoneLowering( "putaway" );
Event EV_Weapon_DoneRaising( "ready" );
Event EV_Weapon_DoneFiring( "donefiring" );
Event EV_Weapon_Idle( "idle" );
Event	EV_Weapon_MuzzleFlash( "muzzleflash" );
Event	EV_Weapon_SecondaryUse( "secondaryuse" );
Event	EV_Weapon_DoneReloading( "donereloading" );
Event EV_Weapon_SetAmmoClipSize( "ammoclipsize" );
Event EV_Weapon_ProcessModelCommands( "process_mdl_cmds" );
Event EV_Weapon_SetMaxRange( "maxrange" );
Event EV_Weapon_SetMinRange( "minrange" );
Event EV_Weapon_SetProjectileSpeed( "projectilespeed" );
Event EV_Weapon_SetKick( "kick" );
Event EV_Weapon_PrimaryMode( "primarymode" );
Event EV_Weapon_SecondaryMode( "secondarymode" );
Event EV_Weapon_ActionIncrement( "actionincrement" );
Event EV_Weapon_PutAwayAndRaise( "putawaythenraise" );
Event EV_Weapon_Raise( "raise" );
Event EV_Weapon_NotDroppable( "notdroppable" );
Event EV_Weapon_SetAimAnim( "setaimanim" );

ResponseDef Weapon::Responses[] =
	{
	   { &EV_Item_Pickup,			         ( Response )Weapon::PickupWeapon },
	   { &EV_Weapon_FinishAttack,	         ( Response )Weapon::FinishAttack },
	   { &EV_Weapon_DoneLowering,	         ( Response )Weapon::DoneLowering },
	   { &EV_Weapon_DoneRaising,	         ( Response )Weapon::DoneRaising },
		{ &EV_Weapon_DoneFiring,	         ( Response )Weapon::DoneFiring },
	   { &EV_Weapon_Idle,			         ( Response )Weapon::Idle },
		{ &EV_Weapon_MuzzleFlash,           ( Response )Weapon::EventMuzzleFlash },
		{ &EV_WeaponSound,		         	( Response )Weapon::WeaponSound },
	   { &EV_Weapon_DoneReloading,         ( Response )Weapon::DoneReloading },
      { &EV_Weapon_SetAmmoClipSize,       ( Response )Weapon::SetAmmoClipSize },
      { &EV_Weapon_ProcessModelCommands,  ( Response )Weapon::ProcessWeaponCommandsEvent },
		{ &EV_Weapon_SetMaxRange,				( Response )Weapon::SetMaxRangeEvent },
		{ &EV_Weapon_SetMinRange,				( Response )Weapon::SetMinRangeEvent },
		{ &EV_Weapon_SetProjectileSpeed,		( Response )Weapon::SetProjectileSpeedEvent },
      { &EV_Weapon_SetKick,         		( Response )Weapon::SetKick },
      { &EV_Weapon_SecondaryUse,      		( Response )Weapon::SecondaryUse },
      { &EV_Weapon_PrimaryMode,      		( Response )Weapon::PrimaryMode },
      { &EV_Weapon_SecondaryMode,    		( Response )Weapon::SecondaryMode },
      { &EV_Weapon_ActionIncrement,    	( Response )Weapon::SetActionLevelIncrement },
      { &EV_Weapon_PutAwayAndRaise,    	( Response )Weapon::PutAwayAndRaise },
      { &EV_Weapon_Raise,    	            ( Response )Weapon::Raise },
      { &EV_Weapon_NotDroppable,    	   ( Response )Weapon::NotDroppableEvent },
      { &EV_Weapon_SetAimAnim,            ( Response )Weapon::SetAimAnim },

		{ NULL, NULL }
	};

static      int numBulletHoles = 0;
static      Queue queueBulletHoles;

void ResetBulletHoles(void)
   {
   numBulletHoles = 0;
   queueBulletHoles.Clear();
   }

Weapon::Weapon()
	{
	attack_finished	      = 0;
	owner					      = NULL;
	ammorequired		      = 0;
   secondary_ammorequired  = 0;
	startammo			      = 0;
	rank					      = 0;
   ammo_clip_size          = 0;
   ammo_in_clip            = G_GetIntArg( "ammo_in_clip", 0 );
   kick                    = 0;
   last_attack_time        = level.time-10;
	nextweaponsoundtime     = 0;
	weaponstate			      = WEAPON_HOLSTERED;
   weaponmode              = ( weaponmode_t )G_GetIntArg( "weaponmode", PRIMARY );
   dualmode                = false;
   silenced                = G_GetIntArg( "silenced", false );
   notdroppable            = false;

   aimanim = -1;
   aimframe = 0;

	// handles most cases
	weapontype			= WEAPON_2HANDED_HI;

   // start off unattached
   attached          = false;

	// maximum effective firing distance (for AI)
	maxrange = 8192 * 2; // FIXME grr... magic number...
	
	// minimum safe firing distance (for AI)
	minrange = 0;

	// speed of the projectile (0 == infinite speed)
	projectilespeed = 0;

   // default action_level_increment
   action_level_increment = 2;
	}

Weapon::~Weapon()
	{
   DetachGun();
	}

void Weapon::CreateSpawnArgs
   (
   void
   )

   {
   Item::CreateSpawnArgs();
   G_SetIntArg( "ammo_in_clip", ammo_in_clip );
   G_SetIntArg( "silenced", silenced );
   G_SetIntArg( "weaponmode", weaponmode );
   }

int Weapon::Rank
	(
	void
	)

	{
	return rank;
	}

int Weapon::Order
	(
	void
	)

	{
	return order;
	}

void Weapon::SetRank
	(
	int order,
	int rank
	)

	{
	this->order = order;
	this->rank = rank;
	}

void Weapon::SetType
	(
	weapontype_t type
	)

	{
	weapontype = type;
	}

weapontype_t Weapon::GetType
	(
	void
	)

	{
	return weapontype;
	}

void Weapon::SetAmmo
	(
	const char *type,
	int amount,
	int startamount
	)

	{
   if ( type )
		{
	   ammotype = type;
      primary_ammo_type = type;
		}
	else
		{
		ammotype = "";
      primary_ammo_type = "";
      }

	ammorequired = amount;
	startammo = startamount;
	}

void Weapon::SetSecondaryAmmo
   (
   const char *type,
	int amount,
	int startamount
	)

	{
   if ( type )
		{
	   secondary_ammo_type = type;
		}
	else
		{
      secondary_ammo_type = "";
      }

	secondary_ammorequired = amount;
	}

void Weapon::SetAmmoAmount
	(
	int amount
	)

	{
   if ( ammo_clip_size )
      ammo_in_clip = amount;
	}

void Weapon::UseAmmo
	(
	int amount
	)

	{
   if ( ammo_clip_size )
      {
      ammo_in_clip -= amount;
      if (ammo_in_clip < 0)
         {
         warning("UseAmmo","Used more ammo than in clip.\n");
         ammo_in_clip = 0;
         }
      }
   else
      {
      Ammo * ammo;
	   assert( owner );
	   if ( owner && owner->isClient() && !UnlimitedAmmo() )
		   {
         if ( ammotype.length() )
            {
		      ammo = ( Ammo * )owner->FindItem( ammotype.c_str() );
		      
            if ( weaponmode == PRIMARY )
               {
               if ( !ammo || !ammo->Use( ammorequired ) )
	   		      {
		   	      SetAmmoAmount( 0 );
			         return;
                  }
               }
            else
               {
               if ( !ammo || !ammo->Use( secondary_ammorequired ) )
	   		      {
		   	      SetAmmoAmount( 0 );
			         return;
                  }
               }
		      SetAmmoAmount( ammo->Amount() );
            }
		   }
      }
	}

Vector Weapon::MuzzleOffset
	(
   void
	)

   {
   vec3_t	trans[ 3 ];
   vec3_t   orient;
   int		groupindex;
   int		tri_num;
	Vector	offset = vec_zero;
   int      useanim;
   int      useframe;

   // get the bone information
   if ( ( !edict->s.gunmodelindex ) || ( owner && owner->isClient() ) )
      {
      if ( gi.GetBoneInfo( edict->s.modelindex, "barrel", &groupindex, &tri_num, orient ) )
		   {
         if ( aimanim == -1 )
            {
            useanim = edict->s.anim;
            useframe = edict->s.frame;
            }
         else
            {
            useanim = aimanim;
            useframe = aimframe;
            }
         if ( gi.GetBoneTransform( edict->s.modelindex, groupindex, tri_num, orient, useanim, useframe, 
			   edict->s.scale, trans, offset.vec3() ) )
		      {
            //
            // we scale the pos by 0.3 because of RF_DEPTHHACK 
            //
            offset *= 0.3f;
            if ( owner && owner->isClient() )
               {
               switch( owner->client->pers.hand )
                  {
                  case LEFT_HANDED:
                     offset[ 1 ] *= -1.0f;
                     break;
                  case CENTER_HANDED:
                     offset[ 1 ] = 0;
                     break;
                  }
               }
            }
         }
      }
   //
   // if it is a non-client, than get the information from the world model of the gun
   //
   else if ( gi.GetBoneInfo( edict->s.gunmodelindex, "barrel", &groupindex, &tri_num, orient ) )
      {
      gi.GetBoneTransform( edict->s.gunmodelindex, groupindex, tri_num, orient, 0, 0, 
			edict->s.scale, trans, offset.vec3() );
      }
   // Gun doesn't have a barrel, so search the owner for a barrel bone
   else if ( owner && gi.GetBoneInfo( owner->edict->s.modelindex,
                             "barrel",
                             &groupindex,
                             &tri_num,
                             orient ) )
		{
      gi.GetBoneTransform( owner->edict->s.modelindex, 
                           groupindex,
                           tri_num,
                           orient,
                           owner->edict->s.anim,
                           owner->edict->s.frame,
                           owner->edict->s.scale,
                           trans,
                           offset.vec3() );
		}

   return offset;
   }

void Weapon::GetMuzzlePosition
	(
   Vector *position,
	Vector *forward,
	Vector *right,
	Vector *up
	)

	{
   Vector	offset;
	Vector	f, r, u;
	Vector	pos;
	Vector	end;
   Vector   dir;
	Vector	gunpos;
	trace_t	trace;

	assert( owner );

	// technically, we should never not have an owner when firing.
	if ( !owner )
		{
		return;
		}

   //
   // get the position of the owners gun bone
   //
   gunpos = owner->GunPosition();
	pos = gunpos;

	owner->GetGunOrientation( pos, &f, &r, &u );

   // get the bone information
	offset = MuzzleOffset();

   pos += f * offset[ 0 ];
   pos -= r * offset[ 1 ];
	pos += u * offset[ 2 ];

	// prevent the creature from firing through walls
	trace = G_Trace( gunpos, vec_zero, vec_zero, pos, owner, MASK_PROJECTILE, "Weapon::GetMuzzlePosition" );
	if ( ( trace.fraction < 1 ) || ( trace.startsolid ) || ( trace.allsolid ) )
		{
      pos = gunpos;
      pos -= r * offset[ 1 ];
	   pos += u * offset[ 2 ];
		}

   //
   // calculate where this projectile is going to hit
   //
   end = gunpos + f*2048;
	trace = G_FullTrace( gunpos, vec_zero, vec_zero, end, 10, owner, MASK_SHOT, "Weapon::GetMuzzlePosition" );
   dir = trace.endpos - pos;
   dir.normalize();
   if ( dir*f < 0.707 )
      dir = f;

	if ( position )
		{
		*position = pos;
		}

	if ( forward )
		{
		*forward = dir;
		}

	if ( right )
		{
		*right = r;
		}

	if ( up )
		{
		*up = u;
		}
   }

void Weapon::SetAmmoClipSize
	(
   Event * ev
	)

	{
	ammo_clip_size = ev->GetInteger( 1 );
	}

void Weapon::SetModels
	(
	const char *world,
	const char *view
	)

	{
   Event *ev;
	assert( view );

	viewmodel = view;
   modelIndex( view );

   if ( world )
		{
	   worldmodel = world;
      modelIndex( world );
		}
   else
		{
      worldmodel = "";
		}

	if ( owner )
		{
   	setModel( viewmodel );
		}
	else if ( worldmodel.length() )
		{
		setModel( worldmodel );
		}
   else
      {
		setModel( viewmodel );
      }

   if ( worldmodel.length() )
      {
      ev = new Event( EV_Weapon_ProcessModelCommands );
      ev->AddInteger( modelIndex( worldmodel.c_str() ) );
      PostEvent( ev, 0 );
      }
   ev = new Event( EV_Weapon_ProcessModelCommands );
   ev->AddInteger( modelIndex( viewmodel.c_str() ) );
   PostEvent( ev, 0 );
	}

void Weapon::SetAimAnim
   (
   Event *ev
   )

   {
   str anim;

   anim = ev->GetString( 1 );
   aimanim = gi.Anim_NumForName( edict->s.modelindex, anim.c_str() );
   aimframe = ev->GetInteger( 2 );
   }

void Weapon::SetOwner
	(
	Sentient *ent
	)

	{
	assert( ent );
	if ( !ent )
		{
		// return to avoid any buggy behaviour
		return;
		}

	Item::SetOwner( ent );

	setOrigin( vec_zero );
   setAngles( vec_zero );

	if ( !viewmodel.length() )
		{
		error( "setOwner", "Weapon without viewmodel set" );
		}

	setModel( viewmodel );

	if ( ent->isClient() && ammotype.length() && startammo && !G_GetSpawnArg( "savegame" ) )
		{
      ent->giveItem( ammotype.c_str(), startammo );
		}
	}

int Weapon::AmmoAvailable
	(
	void
	)

	{
	Ammo *ammo;

	if ( owner )
		{
      if ( ammotype.length() )
         {
		   ammo = ( Ammo * )owner->FindItem( ammotype.c_str() );
		   if ( ammo )
			   {
			   return ammo->Amount();
			   }
         }
		}

	return 0;
	}

qboolean Weapon::UnlimitedAmmo
   (
   void
   )

   {
	if ( !owner )
		{
		return false;
		}
		
   if ( !owner->isClient() || ( owner->flags & FL_GODMODE ) || DM_FLAG( DF_INFINITE_AMMO ) )
		{
		return true;
		}

#ifdef SIN_ARCADE
   if ( !sv_infinitebullets || !sv_infiniterockets || !sv_infiniteplasma || !sv_infinitespears || !sv_infinitesniper )
      {
      sv_infinitebullets = gameVars.CreateVariable( "infinitebullets", 0 );
      sv_infiniterockets = gameVars.CreateVariable( "infiniterockets", 0 );
      sv_infiniteplasma  = gameVars.CreateVariable( "infiniteplasma", 0 );
      sv_infinitespears  = gameVars.CreateVariable( "infinitespears", 0 );
      sv_infinitesniper  = gameVars.CreateVariable( "infinitesniper", 0 );
      }

   if ( sv_infinitebullets->intValue() && ( ( !Q_strncasecmp( ammotype.c_str(), "bullet", 6 ) && ( ammotype != "BulletPulse" ) &&
      ( ammotype != "BulletSniper" ) ) || ( ammotype == "ShotgunClip" ) ) )
      {
      return true;
      }

   if ( sv_infiniterockets->intValue() && ( ammotype == "Rockets" ) )
      {
      return true;
      }

   if ( sv_infiniteplasma->intValue() && ( ammotype == "BulletPulse" ) )
      {
      return true;
      }

   if ( sv_infinitesniper->intValue() && ( ammotype == "BulletSniper" ) )
      {
      return true;
      }

   if ( sv_infinitespears->intValue() && ( ammotype == "Spears" ) )
      {
      return true;
      }
#endif

   return false;
   }

qboolean Weapon::HasAmmo
	(
	void
	)

	{
	if ( !owner )
		{
		return false;
		}
		
   if ( UnlimitedAmmo() )
		{
		return true;
		}

   if ( weaponmode == PRIMARY )
      {
      if ( ( ammo_clip_size && ammo_in_clip >= ammorequired ) || AmmoAvailable() >= ammorequired )
		   {
		   return true;
		   }
      }
   else
      {
      if ( ( ammo_clip_size && ammo_in_clip >= secondary_ammorequired ) || AmmoAvailable() >= secondary_ammorequired )
		   {
		   return true;
		   }
      }

	return false;
	}

qboolean Weapon::HasAmmoInClip
	(
	void
	)

	{
   if ( ammo_clip_size )
      {
      if ( weaponmode == PRIMARY )
         {
         if ( ammo_in_clip >= ammorequired )
	   	   {
		      return true;
		      }
         }
      else
         {
         if ( ammo_in_clip >= secondary_ammorequired )
	   	   {
		      return true;
		      }
         }
      }
   else
      {
      return HasAmmo();
      }

	return false;
	}

void Weapon::ForceState
   (
   weaponstate_t state
   )

   {
   weaponstate = state;
   }

qboolean Weapon::AttackDone
	(
	void
	)

	{
	return ( attack_finished <= level.time );
	}

qboolean Weapon::ReadyToFire
	(
	void
	)

	{
	return ( weaponstate == WEAPON_READY ) && AttackDone();
	}

qboolean Weapon::ReadyToChange
	(
	void
	)

	{
	return ( weaponstate == WEAPON_READY );
	}

qboolean Weapon::ReadyToUse
	(
	void
	)

	{
   return true;
	}

qboolean Weapon::ChangingWeapons
	(
	void
	)

	{
	return ( weaponstate == WEAPON_LOWERING ) || ( weaponstate == WEAPON_RAISING );
	}

qboolean Weapon::WeaponRaising
	(
	void
	)

	{
	return ( weaponstate == WEAPON_RAISING );
	}

qboolean Weapon::WeaponPuttingAway
	(
	void
	)

	{
	return ( weaponstate == WEAPON_LOWERING );
	}

qboolean Weapon::Reloading
	(
	void
	)

	{
	return ( weaponstate == WEAPON_RELOADING );
	}

void Weapon::PutAway
	(
	void
	)
	
	{
	if ( weaponstate != WEAPON_READY )
		{
		return;
		}

	weaponstate = WEAPON_LOWERING;

   if ( !HasAnim( "putaway" ) || ( deathmatch->value && ( ( int )dmflags->value & DF_FAST_WEAPONS ) ) )
		{
		ProcessEvent( EV_Weapon_DoneLowering );
		return;
		}

   if ( dualmode )
      {
      if ( weaponmode == PRIMARY )
         RandomAnimate( "primaryputaway", EV_Weapon_DoneLowering );
      else
         RandomAnimate( "secondaryputaway", EV_Weapon_DoneLowering );
      }
   else
      if ( ( owner ) && ( owner->flags & FL_SILENCER ) && ( silenced ) )
      	RandomAnimate( "silputaway", EV_Weapon_DoneLowering );
      else
        	RandomAnimate( "putaway", EV_Weapon_DoneLowering );

   //weaponmode = PRIMARY;
	}

void Weapon::ReadyWeapon
	(
	void
	)
	
	{
   str   animname;

	if ( weaponstate != WEAPON_HOLSTERED )
		{
		return;
		}

	weaponstate = WEAPON_RAISING;

	AttachGun();

   if ( ( owner ) && ( owner->flags & FL_SILENCER ) && ( silenced ) )
      animname = "silready";
   else
      {
      if ( weaponmode == SECONDARY )
         animname = "secondaryready";      
      else
         animname = "ready";      

      }

   if ( !HasAnim( animname.c_str() ) || ( deathmatch->value && ( ( int )dmflags->value & DF_FAST_WEAPONS ) ) )
	   {
		ProcessEvent( EV_Weapon_DoneRaising );
		return;
		}

	RandomAnimate( animname.c_str(), EV_Weapon_DoneRaising );
	}

void Weapon::DetachFromOwner
	(
	void
	)
	
	{
   StopAnimating();
   DetachGun();
 	weaponstate = WEAPON_HOLSTERED;
	}

void Weapon::AttachToOwner
	(
	void
	)
	
	{
   AttachGun();
   ForceIdle();
	}

qboolean Weapon::Drop
	(
	void
	)

	{
   float radius;
   Vector temp;

	if ( !owner )
		{
		return false;
		}

	if ( !IsDroppable() )
		{
		return false;
		}

   DetachGun();

   const gravityaxis_t &grav = gravity_axis[ gravaxis ];

   temp[ grav.z ] = 40 * grav.sign;
   if ( owner )
      {
	   setOrigin( owner->worldorigin + temp );
      }
   else
      {
	   setOrigin( worldorigin + temp );
      }
	setModel( worldmodel );

   // hack to fix the bounds when the gun is dropped

   //
   // once dropped reset the rotated bounds
   //
   flags |= FL_ROTATEDBOUNDS;

   gi.CalculateBounds( edict->s.modelindex, edict->s.scale, mins.vec3(), maxs.vec3() );
   radius = ( mins - maxs ).length() * 0.25f;
   mins.x = mins.y = -radius;
   maxs.x = maxs.y = radius;
   setSize( mins, maxs );

	StopAnimating();
   edict->s.frame = 0;
   edict->s.anim = 0;

	// drop the weapon
	PlaceItem();
   if ( owner )
      {
      temp[ grav.x ] = G_CRandom( 50 );
      temp[ grav.y ] = G_CRandom( 50 );
      temp[ grav.z ] = 100 * grav.sign;
	   velocity = owner->velocity * 0.5 + temp;
	   setAngles( owner->angles );
      }

	avelocity = Vector( 0, G_CRandom( 360 ), 0 );

	if ( owner && owner->isClient() )
		{
		spawnflags |= DROPPED_PLAYER_ITEM;
      if ( ammo_clip_size )
         startammo = ammo_in_clip;
      else
         startammo = 0;
      
      // If owner is dead, put all his ammo of that type in the gun.
      if ( owner->deadflag )
         {
         startammo = AmmoAvailable();
         }
		}
	else
		{
		spawnflags |= DROPPED_ITEM;
      if ( ammo_clip_size && ammo_in_clip )
         startammo = ammo_in_clip;
      else
         startammo >>= 2;

      if ( startammo == 0 )
         {
         startammo = 1;
         }
		}

   // Wait some time before the last owner can pickup this weapon
   last_owner = owner;
   last_owner_trigger_time = level.time + 2.5f;

   // Cancel reloading events
   CancelEventsOfType( EV_Weapon_DoneReloading );

   // Remove this from the owner's item list
   if ( owner )
      {
      owner->RemoveItem( this );
      }
   
   owner = NULL;
   
   // Fade out dropped weapons, to keep down the clutter
   PostEvent( EV_FadeOut, 30 );
	return true;
	}


void Weapon::Fire
	(
	void
	)

	{
	if ( !ReadyToFire() )
		{
		return;
		}

	if ( !HasAmmoInClip() )
		{
      CheckReload();
		return;
		}

   if ( weaponmode == PRIMARY )
      UseAmmo( ammorequired );
   else
      UseAmmo( secondary_ammorequired );

	weaponstate = WEAPON_FIRING;

	CancelEventsOfType( EV_Weapon_DoneFiring );
   // this is just a precaution that we can re-trigger
	NextAttack( 5 );

  	if ( dualmode )
      {
      if ( weaponmode == PRIMARY )
         {
         RandomAnimate( "primaryfire", EV_Weapon_DoneFiring );
         }
      else
         {
         RandomAnimate( "secondaryfire", EV_Weapon_DoneFiring );
         }
      }
   else
      {
      if ( ( owner ) && ( owner->flags & FL_SILENCER ) && ( silenced ) )
         RandomAnimate( "silfire", EV_Weapon_DoneFiring );
      else
         RandomAnimate( "fire", EV_Weapon_DoneFiring );
      }

   last_attack_time = level.time;
	}

//**********************************************************************************/
//
// Non-public Weapon functions
//
//**********************************************************************************/

void Weapon::DetachGun
	(
	void
	)

	{
	if ( attached )
		{
      RandomGlobalSound("null_sound", 1, CHAN_WEAPONIDLE );
      attached = false;
      detach();
		hideModel();
		edict->s.gunmodelindex	= 0;
      edict->s.gunanim        = 0;
		edict->s.gunframe		   = 0;
   	edict->s.effects &= ~EF_SMOOTHANGLES;
		}
	}

//
// attach and detach the gun from the owner
//

void Weapon::AttachGun
	(
	void
	)

	{
	int groupindex;
	int tri_num;
	Vector orient;

	if ( !owner )
		{
		return;
		}

   if (attached)
      DetachGun();

	if	( gi.GetBoneInfo( owner->edict->s.modelindex, "gun", &groupindex, &tri_num, orient.vec3() ) )
	   {
      attached = true;
		attach( owner->entnum, groupindex, tri_num, orient );
		showModel();
		setOrigin( vec_zero );
		edict->s.gunmodelindex	= modelIndex( worldmodel.c_str() );
      if ( edict->s.gunmodelindex )
         {
         edict->s.gunanim        = gi.Anim_Random( edict->s.gunmodelindex, "idle" );
         if ( edict->s.gunanim < 0 )
            edict->s.gunanim = 0;
		   edict->s.gunframe		   = 0;
         }
      else
         {
         edict->s.gunanim        = 0;
		   edict->s.gunframe		   = 0;
         }
   	edict->s.effects |= EF_SMOOTHANGLES;
		}
	else
		{
		gi.dprintf( "attach failed\n" );
		}
	}

void Weapon::PickupWeapon
	(
	Event *ev
	)
	
	{
	Sentient *sen;
	Entity   *other;
   Weapon   *weapon;
	Weapon   *current;
   qboolean hasweapon;
   qboolean giveammo;
   Ammo     *ammo;

	other = ev->GetEntity( 1 );
   
   assert( other );

	if ( !other->isSubclassOf( Sentient ) )
      {
		return;
      }

	sen = ( Sentient * )other;

   // If this is the last owner, check to see if he can pick it up
   if ( ( sen == last_owner ) && ( level.time < last_owner_trigger_time ) )
      {
      return;
      }

   hasweapon = sen->HasItem( getClassname() );
   giveammo = ( sen->isClient() && ammotype.length() && startammo );

   // if he already has the weapon, don't pick it up if he doesn't need the ammo
   if ( hasweapon )
      {
      if ( !giveammo )
         {
         return;
         }

      // check if he needs the ammo
      ammo = ( Ammo * )sen->FindItem( ammotype.c_str() );
      if ( ammo && ( ammo->Amount() >= ammo->MaxAmount() ) )
         {
         // doesn't need the ammo or the weapon, so return.
         return;
         }
      }

  	weapon = ( Weapon * )ItemPickup( other );
	if ( !weapon )
      {
      // Item Pickup failed, so don't give ammo either.
      return;
      }

   //
   // once picked up we don't want rotated bounds
   //
   flags &= ~FL_ROTATEDBOUNDS;

   //FIXME
   // Sentient should probably handle this when itempickup is called
   // Check if we should switch his weapon
   current = sen->CurrentWeapon();
	if ( !hasweapon && current && ( current != weapon ) && ( current->AutoChange() ) && 
		( ( current->Rank() < weapon->Rank() ) || ( !current->HasAmmo() && weapon->HasAmmo() ) ) )
		{
		sen->ChangeWeapon( weapon );
		}

   // check if we should give him ammo
	if ( giveammo )
		{
      sen->giveItem( ammotype.c_str(), startammo );
		}
	}

void Weapon::ForceIdle
   (
   void
   )

   {
	weaponstate = WEAPON_READY;
	   
   if ( dualmode )
      {
      if ( weaponmode == PRIMARY )
   	   RandomAnimate( "primaryidle", EV_Weapon_Idle );
      else
         RandomAnimate( "secondaryidle", EV_Weapon_Idle );
      }
   else
      {
      if ( owner && ( owner->flags & FL_SILENCER ) && ( silenced ) )
      	RandomAnimate( "silidle", EV_Weapon_Idle );
      else
         RandomAnimate( "idle", EV_Weapon_Idle );
      }
   }

void Weapon::DoneLowering
	(
	Event *ev
	)

	{
	weaponstate = WEAPON_HOLSTERED;

   DetachGun();

	if ( owner )
		{
		owner->ProcessEvent( EV_Sentient_WeaponPutAway );
		}
	StopAnimating();
	}

void Weapon::DoneRaising
	(
	Event *ev
	)

	{
	weaponstate = WEAPON_READY;

   if ( !ForceReload() )
      {
      ForceIdle();
      }

	if ( !owner )
		{
		PostEvent( EV_Remove, 0 );
		return;
		}

   // Set the ammo type
   if (weaponmode == PRIMARY)
      {
      ammotype = primary_ammo_type;
      }
   else if ( weaponmode == SECONDARY )
      {
      ammotype = secondary_ammo_type;
      }

   if ( owner )
	   owner->ProcessEvent( EV_Sentient_WeaponReady );
	}

void Weapon::DoneFiring
	(
	Event *ev
	)

	{
   if ( !CheckReload() )
      {
      ForceIdle();
      }

	if ( owner )
		{
		owner->ProcessEvent( EV_Sentient_WeaponDoneFiring );
		}
	}

void Weapon::DoneReloading
	(
	Event *ev
	)

	{
	Ammo *ammo;
   int amount;

   amount = ammo_clip_size - ammo_in_clip;
	assert( owner );
	if ( owner && owner->isClient() && !UnlimitedAmmo() )
		{
      if ( ammotype.length() )
         {
		   ammo = ( Ammo * )owner->FindItem( ammotype.c_str() );
         if ( ammo )
            {
            if ( ammo->Amount() < amount )
               amount = ammo->Amount();
            if ( !ammo->Use( amount ) )
               {
               amount = 0;
               }
            }
	      SetAmmoAmount( amount + ammo_in_clip );
         }
		}
   else
      {
      SetAmmoAmount( ammo_clip_size );
      }
   
   ForceIdle();

   if ( owner )
      {
	   owner->ProcessEvent( EV_Sentient_WeaponReady );
      }
   }

qboolean Weapon::ForceReload
   ( 
   void
   )

   {
   if ( weaponstate != WEAPON_READY )
      return false;

   if ( level.cinematic && owner && owner->isClient() )
      {
      return false;
      }

   // do a reload if the ammo in clip is not full, 
   // and there is some ammo available out of clip
   if ( 
      ( ammo_clip_size != ammo_in_clip ) && 
      ( AmmoAvailable() > 0 )
      )
      {
   	weaponstate = WEAPON_RELOADING;
      if ( ( owner ) && ( owner->flags & FL_SILENCER ) && ( silenced ) )
         RandomAnimate( "silreload", EV_Weapon_DoneReloading );
      else
         RandomAnimate( "reload", EV_Weapon_DoneReloading );
      return true;
      }
   return false;
   }

qboolean Weapon::CheckReload
	(
	void
	)

	{
   if ( weaponstate != WEAPON_READY )
      return false;

   if ( level.cinematic && owner && owner->isClient() )
      {
      return false;
      }

   if ( ammo_clip_size && !ammo_in_clip && HasAmmo() )
      {
   	weaponstate = WEAPON_RELOADING;
      if ( ( owner ) && ( owner->flags & FL_SILENCER ) && ( silenced ) )
         RandomAnimate( "silreload", EV_Weapon_DoneReloading );
      else
      	RandomAnimate( "reload", EV_Weapon_DoneReloading );
      return true;
      }
   return false;
	}

void Weapon::Idle
	(
	Event *ev
	)

	{
   if ( ammo_clip_size && ( !ammo_in_clip || level.time > last_attack_time + 2 ) )
      {
      if (CheckReload())
         return;
      }

   ForceIdle();
	}

void Weapon::FinishAttack
	(
	Event *ev
	)

	{
   attack_finished = level.time;
   }

void Weapon::NextAttack
	(
	double rate
	)

	{
	attack_finished = level.time + (float)rate;
	}

float	Weapon::GetMaxRange
	(
	void
	)

	{
	return maxrange;
	}

float	Weapon::GetMinRange
	(
	void
	)

	{
	return minrange;
	}

float	Weapon::GetProjectileSpeed
	(
	void
	)

	{
	return projectilespeed;
	}

void Weapon::SetMaxRangeEvent
	(
	Event *ev
	)
	
	{
	maxrange = ev->GetFloat( 1 );
	}

void Weapon::SetMinRangeEvent
	(
	Event *ev
	)

	{
	minrange = ev->GetFloat( 1 );
	}

void Weapon::SetProjectileSpeedEvent
	(
	Event *ev
	)

	{
	projectilespeed = ev->GetFloat( 1 );
	}

void Weapon::NotDroppableEvent
	(
	Event *ev
	)

	{
   notdroppable = true;
	}

void Weapon::SetMaxRange
	(
	float val
	)

	{
	maxrange = val;
	}

void Weapon::SetMinRange
	(
	float val
	)

	{
	minrange = val;
	}

void Weapon::SetProjectileSpeed
	(
	float val
	)

	{
	projectilespeed = val;
	}

void Weapon::EventMuzzleFlash
	(
	Event *ev
	)

	{
	if ( !owner )
		{
		return;
		}

   SpawnTempDlight
      (
      owner->worldorigin,
      ev->GetFloat( 1 ),
      ev->GetFloat( 2 ),
      ev->GetFloat( 3 ),
      ev->GetFloat( 4 ),
      ev->GetFloat( 5 ),
      ev->GetFloat( 6 )
      );
	}

void Weapon::MuzzleFlash
	(
   float r,
   float g,
   float b,
   float radius,
   float decay,
   float life
	)

	{
	if ( !owner )
		{
		return;
		}

   SpawnTempDlight
      (
      owner->worldorigin,
      r,
      g,
      b,
      radius,
      decay,
      life
      );
	}

void Weapon::BulletHole
   (
   trace_t *trace
   )
   {
   Entity *hole;
   Vector norm,norm2;
   Entity *hit;

   hit = trace->ent->entity;

   if ( !hit )
		{
      return;
		}

   if ( trace->surface &&
      ( trace->surface->flags & SURF_NODRAW ) )
      return;

   if ( hit->hidden() )
      return;

   // FIXME: Make Bullet holes client side.
   if ( deathmatch->value )
      return;

   hole = new Entity;
	hole->setMoveType( MOVETYPE_PUSH );
	hole->setSolidType( SOLID_NOT );
   hole->setModel( "sprites/bullethole.spr" );
	hole->setSize( "0 0 0", "0 0 0" );

   norm = trace->plane.normal;
   norm2.x = -norm.x;
   norm2.y = -norm.y;
   norm2.z = norm.z;

   hole->angles = norm2.toAngles();
   hole->setAngles( hole->angles );
   hole->setOrigin( Vector( trace->endpos ) + ( norm * 0.2 ) ) ;

	if ( ( trace->ent->solid == SOLID_BSP ) && ( hit != world ) )
		{
		hole->bind( hit );
		}

   queueBulletHoles.Enqueue(hole);
   numBulletHoles++;

   if (numBulletHoles > sv_maxbulletholes->value)
      {
      // Fade one out of the list.
      Entity *fadehole;
      fadehole = (Entity *) queueBulletHoles.Dequeue();
      fadehole->ProcessEvent(EV_Remove);
      numBulletHoles--;
      }
   }

EXPORT_FROM_DLL void Weapon::WeaponSound
	(
	Event *ev
	)

	{
	Event *e;

	// Broadcasting a sound can be time consuming.  Only do it once in a while on really fast guns.
	if ( nextweaponsoundtime > level.time )
		{
		if ( owner )
			{
			owner->BroadcastSound( ev, CHAN_WEAPON, NullEvent, 0 );
			}
		else
			{
			BroadcastSound( ev, CHAN_WEAPON, EV_HeardWeapon, 0 );
			}
		return;
		}

	if ( owner )
		{
		e = new Event( ev );
		owner->ProcessEvent( e );
		}
	else
		{
		Item::WeaponSound( ev );
		}

	// give us some breathing room
	nextweaponsoundtime = level.time + 0.4;
	}

qboolean Weapon::Removable
   (
   void
   )

   {
   if (
         ( ( int )( dmflags->value ) & DF_WEAPONS_STAY ) && 
         !( spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) ) 
      )
      return false;
   else
      return true;
   }

qboolean Weapon::Pickupable
   (
   Entity *other
   )

   {
   Sentient *sen;

   if ( !other->isSubclassOf( Sentient ) )
		{
		return false;
		}
   else if ( !other->isClient() )
      {
      return false;
      }

   sen = ( Sentient * )other;

   //FIXME
   // This should be in player

   // Mutants can't pickup weapons
   if ( other->flags & (FL_MUTANT|FL_SP_MUTANT) )
      {
      return false;
      }

   // If we have the weapon and weapons stay, then don't pick it up
   if ( ( ( int )( dmflags->value ) & DF_WEAPONS_STAY ) && !( spawnflags & ( DROPPED_ITEM | DROPPED_PLAYER_ITEM ) ) ) 
      {
      Weapon   *weapon;

      weapon = ( Weapon * )sen->FindItem( getClassname() );

      if ( weapon )
         return false;
      }

   return true;
   }

qboolean Weapon::AutoChange
   (
   void
   )

   {
   return true;
   }

int Weapon::ClipAmmo
   (
   void
   )

   {
   if (ammo_clip_size)
      return ammo_in_clip;
   else
      return -1;
   }

void Weapon::ProcessWeaponCommandsEvent
	(
	Event *ev
	)

	{
   int index;

   index = ev->GetInteger( 1 );
   ProcessInitCommands( index );
	}

void Weapon::SetKick
   (
   Event *ev
   )

   {
   kick = ev->GetInteger( 1 );
   }

void Weapon::SecondaryUse
   (
   Event *ev
   )

   {
   if ( !dualmode )
      return;

   // Switch to the secondary mode of the weapon

   if (weaponstate != WEAPON_READY)
      return;

   weaponstate = WEAPON_CHANGING;

   if (weaponmode == PRIMARY)
      {
      RandomAnimate( "primary2secondary", EV_Weapon_SecondaryMode );
      weaponmode = SECONDARY;
      }
   else
      {
      RandomAnimate( "secondary2primary", EV_Weapon_PrimaryMode );
      weaponmode = PRIMARY;
      }
   }

void Weapon::PrimaryMode
   (
   Event *ev
   )

   {
   RandomAnimate( "primaryidle", EV_Weapon_Idle );
   weaponstate = WEAPON_READY;
   ammotype = primary_ammo_type;
   }

void Weapon::SecondaryMode
   (
   Event *ev
   )
   
   {
   RandomAnimate( "secondaryidle", EV_Weapon_Idle );
   weaponstate = WEAPON_READY;
   ammotype = secondary_ammo_type;
   }

void Weapon::SetActionLevelIncrement
   (
   Event *ev
   )
   {
   action_level_increment = ev->GetInteger( 1 );
   }

int Weapon::ActionLevelIncrement
   (
   void
   )
   {
   return action_level_increment;
   }

qboolean Weapon::IsDroppable
	(
	void
	)
	{
	if ( notdroppable || !worldmodel.length() )
		{
		return false;
		}
   else
      {
      return true;
      }
   }

qboolean Weapon::IsSilenced
   (
   void
   )

   {
   return silenced;
   }

void Weapon::Raise
   (
   Event *ev 
   )

   {
   weaponstate = WEAPON_HOLSTERED;
   ReadyWeapon();
   }

void Weapon::PutAwayAndRaise
   (
   Event *ev
   )
   
   {
   weaponstate = WEAPON_LOWERING;

   if ( deathmatch->value && ( ( int )dmflags->value & DF_FAST_WEAPONS ) )
		{
		ProcessEvent( EV_Weapon_DoneRaising );
		return;
		}

	RandomAnimate( "putaway", EV_Weapon_Raise );
   }