//-----------------------------------------------------------------------------
//
//  $Logfile:: /Code/DLLs/game/weapon.cpp                                    $
// $Revision:: 263                                                            $
//   $Author:: Steven                                                         $
//     $Date:: 10/13/03 9:43a                                                 $
//
// 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.
//
//
// DESCRIPTION:
// Source file for Weapon class.  The weapon class is the base class for
// all weapons in the game.  Any entity created from a class derived from the weapon
// class will be usable by any Sentient (players and monsters) as a weapon.
//

#include "_pch_cpp.h"
#include "entity.h"
#include "item.h"
#include "weapon.h"
#include "scriptmaster.h"
#include "sentient.h"
#include "misc.h"
#include "specialfx.h"
#include "actor.h"
#include "weaputils.h"
#include "player.h"
#include "decals.h"
#include "mp_manager.hpp"
#include <qcommon/gameplaymanager.h>

// for bot code
#include "g_local.h"
#include "botlib.h"
#include "be_aas.h"
#include "be_ea.h"
#include "be_ai_char.h"
#include "be_ai_chat.h"
#include "be_ai_gen.h"
#include "be_ai_goal.h"
#include "be_ai_move.h"
#include "be_ai_weap.h"
#include "ai_main.h"
extern bot_state_t	*botstates[MAX_CLIENTS];

#define TargetIdleTime 0.2

Event EV_Weapon_Shoot
(
	"shoot",
	EV_DEFAULT,
	"S",
	"mode",
	"Shoot the weapon"
 );
Event EV_Weapon_DoneRaising
(
	"ready",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Signals the end of the ready animation so the weapon can be used"
 );
Event EV_Weapon_DoneAnimating
(
	"weapon_done_animate",
	EV_CODEONLY,
	NULL,
	NULL,
	"Signals the end of the ready animation so the weapon can be used"
);
Event EV_Weapon_DoneFiring
(
	"donefiring",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Signals the end of the fire animation"
);
Event EV_Weapon_Idle
(
	"idle",
	EV_DEFAULT,
	NULL,
	NULL,
	"Puts the weapon into an idle state"
);
Event	EV_Weapon_SecondaryUse
(
	"secondaryuse",
	EV_DEFAULT,
	NULL,
	NULL,
	"Puts the weapon into its secondary mode of operation"
);
Event EV_Weapon_DoneReloading
(
	"donereloading",
	EV_CODEONLY,
	NULL,
	NULL,
	"Signals the end of the reload animation"
);
Event EV_Weapon_DoneReloadingBurst
(
	"doneReloadingBurst",
	EV_CODEONLY,
	NULL,
	NULL,
	"Signals the end of the reload animation for a burst"
);
Event EV_Weapon_SetAmmoClipSize
(
	"clipsize",
	EV_TIKIONLY,
	"i",
	"ammoClipSize",
	"Set the amount of rounds a clip of the weapon holds"
);
Event EV_Weapon_SetAmmoInClip
(
	"ammo_in_clip",
	EV_TIKIONLY,
	"i",
	"ammoInClip",
	"Set the amount of ammo in the clip"
);
Event EV_Weapon_ProcessModelCommands
(
	"process_mdl_cmds",
	EV_CODEONLY,
	NULL,
	NULL,
	"Forces a processing of the init commands for the model"
);
Event EV_Weapon_SetMaxRange
(
	"maxrange",
	EV_TIKIONLY,
	"f",
	"maxRange",
	"Set the maximum range of a weapon so the AI knows how to use it"
);
Event EV_Weapon_SetMinRange
(
	"minrange",
	EV_TIKIONLY,
	"f",
	"minRange",
	"Set the minimum range of a weapon so the AI knows how to use it"
);
Event EV_Weapon_ActionIncrement
(
	"actionincrement",
	EV_TIKIONLY,
	"i",
	"actionLevelIncrement",
	"Set the action level increment of the weapon.\n"
	"When the weapon is fired, it will raise the action level by this amount"
);
Event EV_Weapon_NotDroppable
(
	"notdroppable",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Makes a weapon not droppable"
);
Event EV_Weapon_SetAimAnim
(
	"setaimanim",
	EV_TIKIONLY,
	"si",
	"aimAnimation aimFrame",
	"Set the aim animation and frame for when a weapon fires"
);
Event EV_Weapon_SetFireType
(
	"firetype",
	EV_TIKIONLY,
	"s",
	"firingType",
	"Set the firing type of the weapon (projectile or bullet)"
);
Event EV_Weapon_SetProjectile
(
	"projectile",
	EV_TIKIONLY,
	"s",
	"projectileModel",
	"Set the model of the projectile that this weapon fires"
);
Event EV_Weapon_SetBulletDamage
(
	"bulletdamage",
	EV_TIKIONLY,
	"f",
	"bulletDamage",
	"Set the damage that the bullet causes"
);
Event EV_Weapon_SetBulletKnockback
(
	"bulletknockback",
	EV_TIKIONLY,
	"f",
	"bulletKnockback",
	"Set the knockback that the bullet causes"
);
Event EV_Weapon_SetBulletCount
(
	"bulletcount",
	EV_TIKIONLY,
	"f",
	"bulletCount",
	"Set the number of bullets this weapon shoots when fired"
);
Event EV_Weapon_SetBulletRange
(
	"bulletrange",
	EV_TIKIONLY,
	"f",
	"bulletRange",
	"Set the range of the bullets"
);
Event EV_Weapon_SetBulletSpread
(
	"bulletspread",
	EV_TIKIONLY,
	"ffFFF",
	"bulletSpreadX bulletSpreadY endSpreadX endSpreadY spreadTime",
	"Set the max spread of the bullet in the x and y axis, optionally with a different end spread and time interval"
);
Event EV_Weapon_SetRange
(
	"range",
	EV_TIKIONLY,
	"f",
	"range",
	"Set the range of the weapon"
);
Event EV_Weapon_Hand
(
	"hand",
	EV_TIKIONLY,
	"s",
	"weaponHand",
	"Set the hand the weapon can be wielded in (lefthand, righthand, both, or any)"
);
Event EV_Weapon_Mode
(
	"modeset",
	EV_TIKIONLY,
	"SSSSSSSSS",
	"arg1 arg2 arg3 arg4 arg5 arg6 arg7 arg8 arg9",
	"Set a value for a mode by passing commands through"
);
Event EV_Weapon_AmmoType
(
	"ammotype",
	EV_TIKIONLY,
	"s",
	"name",
	"Set the type of ammo this weapon uses"
);
Event EV_Weapon_StartAmmo
(
	"startammo",
	EV_TIKIONLY,
	"i",
	"amount",
	"Set the starting ammo of this weapon"
);
Event EV_Weapon_AmmoBoost
(
	"ammoBoost",
	EV_TIKIONLY,
	"i",
	"amount",
	"Set the ammo given to a sentient that picks up this weapon but already had a weapon of this type."
);
Event EV_Weapon_AmmoRequired
(
	"ammorequired",
	EV_TIKIONLY,
	"i",
	"amount",
	"Set the amount of ammo this weapon requires to fire"
);
Event EV_Weapon_MaxChargeTime
(
	"maxchargetime",
	EV_TIKIONLY,
	"i",
	"time",
	"Set the maximum time the weapon may be charged up"
);
Event EV_Weapon_GiveStartingAmmo
(
	"startingammotoowner",
	EV_CODEONLY,
	NULL,
	NULL,
	"Internal event used to give ammo to the owner of the weapon"
);
Event EV_Weapon_GiveAmmoBoost
(
	"giveAmmoBoost",
	EV_CODEONLY,
	NULL,
	NULL,
	"Internal event used to give ammo boost to the player that just picked up this weapon."
);
Event EV_Weapon_AutoAim
(
	"autoaim",
	EV_TIKIONLY,
	"FF",
	"selection_angle lockon_angle",
	"Turn on auto aiming for the weapon"
);
Event EV_Weapon_Crosshair
(
	"crosshair",
	EV_TIKIONLY,
	"b",
	"bool",
	"Turn on/off the crosshair for this weapon"
);
Event EV_Weapon_TorsoAim
(
	"torsoaim",
	EV_DEFAULT,
	"b",
	"bool",
	"Turn on/off the torsoaim for this weapon"
);
Event EV_Weapon_SetQuiet
(
	"quiet",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Makes the weapon make no noise."
);
Event EV_Weapon_SetLoopFire
(
	"loopfire",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Makes the weapon fire by looping the fire animation."
);
/*
Event EV_Weapon_FullAnimFire
(
	"fullanimfire",
	EV_DEFAULT,
	NULL,
	NULL,
	"Makes the weapon play the full fire animation even if key is released"
);
*/
Event EV_Weapon_LeftAttachToTag
(
	"leftattachtotag",
	EV_TIKIONLY,
	"s",
	"tagname",
	"Set the name of the tag to attach this to it's owner when wielded in the left hand."
);
Event EV_Weapon_RightAttachToTag
(
	"rightattachtotag",
	EV_TIKIONLY,
	"s",
	"tagname",
	"Set the name of the tag to attach this to it's owner when wielded in the right hand."
);
Event EV_Weapon_DualAttachToTag
(
	"dualattachtotag",
	EV_TIKIONLY,
	"s",
	"tagname",
	"Set the name of the tag to attach this to it's owner when wielded dual handed."
);
Event EV_Weapon_HolsterTagLeft
(
	"leftholstertag",
	EV_TIKIONLY,
	"s",
	"tagname",
	"Set the name of the tag to attach this to when the weapon is put away from the left hand."
);
Event EV_Weapon_HolsterTagRight
(
	"rightholstertag",
	EV_TIKIONLY,
	"s",
	"tagname",
	"Set the name of the tag to attach this to when the weapon is put away from the right hand."
);
Event EV_Weapon_HolsterTagDual
(
	"dualholstertag",
	EV_TIKIONLY,
	"s",
	"tagname",
	"Set the name of the tag to attach this to when the weapon is put away from dual handed"
);
Event EV_Weapon_LeftHolsterAngles
(
	"leftholsterangles",
	EV_TIKIONLY,
	"v",
	"angles",
	"Set the angles of this weapon when it's attached to the left holster"
);
Event EV_Weapon_RightHolsterAngles
(
	"rightholsterangles",
	EV_TIKIONLY,
	"v",
	"angles",
	"Set the angles of this weapon when it's attached to the right holster"
);
Event EV_Weapon_DualHolsterAngles
(
	"dualholsterangles",
	EV_TIKIONLY,
	"v",
	"angles",
	"Set the angles of this weapon when it's attached to the dual holster"
);
Event EV_Weapon_HolsterScale
(
	"holsterscale",
	EV_TIKIONLY,
	"f",
	"scale",
	"Set the scale of the weapon when it's attached to the holster"
);
Event EV_Weapon_WeildedScale
(
	"weildedScale",
	EV_TIKIONLY,
	"f",
	"scale",
	"Sets the scale of the weapon when it's weilded"
);
Event EV_Weapon_AutoPutaway
(
	"autoputaway",
	EV_TIKIONLY,
	"b",
	"bool",
	"Set the weapon to be automatically put away when out of ammo"
);
Event EV_Weapon_UseNoAmmo
(
	"usenoammo",
	EV_TIKIONLY,
	"b",
	"bool",
	"Set the weapon to be able to be used when it's out of ammo"
);
Event EV_Weapon_SetMeansOfDeath
(
	"meansofdeath",
	EV_TIKIONLY,
	"s",
	"meansOfDeath",
	"Set the meansOfDeath of the weapon."
);
Event EV_Weapon_SetWorldHitSpawn
(
	"worldhitspawn",
	EV_TIKIONLY,
	"s",
	"modelname",
	"Set a model to be spawned when the weapon strikes the world."
);
Event EV_Weapon_MakeNoise
(
	"makenoise",
	EV_TIKIONLY,
	"FB",
	"noise_radius force",
	"Makes the weapon make noise that actors can hear."
);
Event EV_Weapon_SetViewModel
(
	"weaponviewmodel",
	EV_TIKIONLY,
	"s",
	"filename",
	"Set the view model name"
);
Event EV_Weapon_DonePutaway
(
	"doneputaway",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Called when the weapon is done with it's putaway anim"
);
Event EV_Weapon_SetRegenAmmo
(
	"regenammo",
	EV_TIKIONLY,
	"ii",
	"regenamount regentime",
	"Specifics that a weapon regenerates ammo over time"
);
Event EV_Weapon_SetRegenOnlyWhenIdle
(
	"regenonlywhenidle",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Specifics that the weapon only regenerates ammo when idle, must still use regen ammo to set how much."
);
Event EV_Weapon_ChangeIdle
(
	"changeidle",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Changes the idle animation"
);
Event EV_Weapon_DrawBowStrain
(
	"drawbowstrain",
	EV_DEFAULT,
	NULL,
	NULL,
	"Starts the bow draw strain animation"
);
Event EV_Weapon_AltDrawBowStrain
(
	"altdrawbowstrain",
	EV_DEFAULT,
	NULL,
	NULL,
	"Starts the alternate bow draw strain animation"
);
Event EV_Weapon_SetAccuracy
(
	"setaccuracy",
	EV_TIKIONLY,
	"sfffff",
	"firemode stoppedac acchange walkac runac crouchac",
	"Sets the accuracy of the weapon"
);
Event EV_Weapon_SetReticuleTime
(
	"setreticuletime",
	EV_TIKIONLY,
	"f",
	"reticuletime",
	"Reticule time"
);
Event EV_Weapon_SetZoomFOV
(
	"setzoomfov",
	EV_TIKIONLY,
	"f",
	"zoomfov",
	"Zoom FOV"
);
Event EV_Weapon_IncrementZoomFOV
(
	"inczoomfov",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Increments the zoom fov"
);
Event EV_Weapon_SetZoomStage
(
	"setzoomstage",
	EV_TIKIONLY,
	"ff",
	"fov1 fov2",
	"Sets the fov stage"
);
Event EV_Weapon_SetStartZoom
(
	"setstartzoom",
	EV_TIKIONLY,
	"f",
	"startzoom",
	"Sets the start zoom fov"
);
Event EV_Weapon_SetEndZoom
(
	"setendzoom",
	EV_TIKIONLY,
	"f",
	"endzoom",
	"Sets the end zoom fov"
);
Event EV_Weapon_SetZoomTime
(
	"setzoomtime",
	EV_TIKIONLY,
	"f",
	"zoomtime",
	"Sets the time it takes to zoom from startzoom to endzoom"
);
Event EV_Weapon_SetAimType
(
	"setaim",
	EV_DEFAULT,
	"s",
	"aimtype",
	"Sets the aimtype of the weapon (changes according to player state)"
);
Event EV_Weapon_SetFireTimer
(
	"setfiretimer",
	EV_TIKIONLY,
	"sf",
	"mode mintime",
	"Specifies the minimum time between shots"
);
Event EV_Weapon_UseSameClip
(
	"usesameclip",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Specifies that ammo comes out of the same clip in all fire modes"
);
Event EV_Weapon_SetMaxModes
(
	"maxmode",
	EV_TIKIONLY,
	"s",
	"maxmode",
	"Overrides the maximum number of modes for this weapon (default is 2)"
);
Event EV_Weapon_SetSwitchMode
(
	"switchmode",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Specifies that this is a switch mode weapon (right button switches)"
);
Event	EV_Weapon_DoneSwitching
(
	"doneswitching",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Signals the end of the switching to mode animation"
);
Event	EV_Weapon_DoneSwitchToMiddle
(
	"doneswitchtomiddle",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Signals the end of the switching to 'neutral' animation"
);
Event	EV_Weapon_TargetIdle
(
	"targetidle",
	EV_CODEONLY,
	NULL,
	NULL,
	"This weapon have a specific on-target idle"
);
Event EV_Weapon_TargetIdleThink
(
	"targetidlethink",
	EV_DEFAULT,
	NULL,
	NULL,
	"Think event to check for target on target idle weapons"
);
Event EV_Weapon_SetBurstMode
(
	"burstmode",
	EV_TIKIONLY,
	"i",
	"burstcount",
	"Set this mode to be burst mode, that uses burstcount ammo"
);
Event EV_Weapon_StartFiring
(
	"startfiring",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Sets the time that the weapon starts firing (for spread computations)"
);
Event EV_Weapon_FinishedFiring
(
	"finishedfiring",
	EV_TIKIONLY,
	"i",
	"finished_firing",
	"Sets if the gun is finished firing"
);
Event EV_Weapon_Zoom
(
	"zoom",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Zooms in"
);
Event EV_Weapon_EndZoom
(
	"endZoom",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Ends the zoom"
);
Event EV_Weapon_Rezoom
(
	"rezoom",
	EV_TIKIONLY,
	NULL,
	NULL,
	"Rezooms to the last zoom fov"
);
Event EV_Weapon_SetTargetingSkin
(
	"targetingskin",
	EV_TIKIONLY,
	"i",
	"skinNum",
	"Sets the skin number to use when targeting something"
);
Event EV_Weapon_SetShootingSkin
(
	"shootingskin",
	EV_TIKIONLY,
	"i",
	"skinNum",
	"Sets the skin number to use when shooting"
);
Event EV_Weapon_SetFullAmmoSkin
(
	"fullAmmoSkin",
	EV_TIKIONLY,
	"ii",
	"skinNum modeNum",
	"Sets the skin number to use when the weapon has full ammo"
);
Event EV_Weapon_SetMoveSpeedModifier
(
	"moveSpeedModifier",
	EV_DEFAULT,
	"f",
	"modifier",
	"Sets the move speed modifier for this weapon (when not shooting)."
);
Event EV_Weapon_SetShootingMoveSpeedModifier
(
	"shootingMoveSpeedModifier",
	EV_DEFAULT,
	"f",
	"modifier",
	"Sets the move speed modifier for this weapon while shooting."
);
Event EV_Weapon_UseActorAiming
(
	"useactoraiming",
	EV_DEFAULT,
	"B",
	"flag",
	"This weapon aims with it's tag_barrel.  Regardless of who is holding it."
);
Event EV_Weapon_ViewShake
(
	"viewShake",
	EV_DEFAULT,
	"fF",
	"viewShakeMagnitude viewShakeDuration",
	"Sets the magnitude & duration of the view shaking while firing this weapon.\n"
	"Duration defaults to .05 if not set"
);
Event EV_Weapon_AdvancedViewShake
(
	"advancedViewShake",
	EV_DEFAULT,
	"vvFB",
	"minViewShake maxViewShake viewShakeDuration override",
	"Sets the min shake vector, max shake vector, & duration of the view shaking while firing this weapon.\n"
	"Duration defaults to .05 if not set"
);
Event EV_Weapon_ClearViewShake
(
	"clearViewShake",
	EV_CODEONLY,
	NULL,
	NULL,
	"Clears the current view shake."
);
Event EV_Weapon_ReduceViewShake
(
	"reduceViewShake",
	EV_CODEONLY,
	NULL,
	NULL,
	"Reduces the current view shake."
);
Event EV_Weapon_SetPowerRating
(
	"powerrating",
	EV_DEFAULT,
	"f",
	"rating",
	"Specifies how much damage is done per second by the weapon"
);
Event EV_Weapon_SetProjectileSpeed
(
	"projectilespeed",
	EV_DEFAULT,
	"f",
	"proj_speed",
	"Specifies the speed of the projectile -- Used by AI"
);
Event EV_Weapon_SetProjectileDamage
(
	"projectiledamage",
	EV_DEFAULT,
	"f",
	"proj_damage",
	"Specifies the damage of the projectile"
);
Event EV_Weapon_SetBurstModeDelay
(
	"burstModeDelay",
	EV_DEFAULT,
	"f",
	"burstModeDelay",
	"Specifies the length of time to use between bursts (do not use this if you want to use the animation timing)"
);
Event EV_Weapon_SetArcProjectile
(
	"arcprojectile",
	EV_DEFAULT,
	"b",
	"arc_bool",
	"Specifies for actors if the projectile should arc"
);
Event EV_Weapon_SetLowArcRange
(
	"lowarcrange",
	EV_DEFAULT,
	"f",
	"range",
	"Specifies for actors the range at which to change from high trajectory to normal"
);

Event EV_Weapon_SetPlayMissSound
(
	"playmisssound",
	EV_DEFAULT,
	"b",
	"playsound",
	"Sets whether or not to play a miss sound( snd_ricochet ) at the point of impact"
);

Event EV_Weapon_NoAmmoMode
(
	"noAmmoMode",
	EV_DEFAULT,
	NULL,
	NULL,
	"Specifies that the current mode also has a no ammo mode."
);
Event EV_Weapon_NoDelay
(
	"noDelay",
	EV_DEFAULT,
	NULL,
	NULL,
	"Specifies that this mode has no delay."
);
Event EV_Weapon_PauseRegen
(
	"pauseRegen",
	EV_DEFAULT,
	NULL,
	NULL,
	"Pauses the regen of ammo for a little bit."
);
Event EV_Weapon_SetChargedModels
(
	"chargedModels",
	EV_DEFAULT,
	"i",
	"numChargedModels",
	"Sets the number of charged models there are to use."
);
Event EV_Weapon_SetControllingProjectile
(
	"controllingProjectile",
	EV_DEFAULT,
	"B",
	"bool",
	"Sets whether or not the weapon is currently controlling a projectile or not."
);
Event EV_Weapon_SetCanInterruptFiringState
(
	"canInterruptFiringState",
	EV_DEFAULT,
	"B",
	"bool",
	"Sets whether or not the weapon can be interrupted during its firing state."
);
Event EV_Weapon_StartViewShake
(
	"startViewShake",
	EV_CODEONLY,
	NULL,
	NULL,
	"Starts the weapons view shake."
);
Event EV_Weapon_SpreadAnimData
(
	"spreadAnimData",
	EV_DEFAULT,
	"if",
	"numAnims totalTime",
	"Sets up the info for spread over time using different fire anims."
);
Event EV_Weapon_MaxViewShakeChange
(
	"maxViewShakeChange",
	EV_DEFAULT,
	"f",
	"maxViewChange",
	"Sets the max view change for this weapon."
);
Event EV_Weapon_ControlParms
(
	"controlProjParms",
	EV_DEFAULT,
	"ss",
	"emitterName soundName",
	"Sets the paramaters to use when controlling a projectile."
);
Event EV_Weapon_ProjectileControlHidden
(
	"controlProjHidden",
	EV_DEFAULT,
	"b",
	"hiddenBool",
	"Sets whether or not the projectile control is currently hidden."
);

Event EV_Weapon_MeleeParms
(
	"meleeParms",
	EV_DEFAULT,
	"fff",
	"width height length",
	"Sets the parms of the melee attack."
);
Event EV_Weapon_FireOffset
(
	"fireOffset",
	EV_DEFAULT,
	"v",
	"fireOffset",
	"Sets the offset where the weapon will fire from."
);
Event EV_Weapon_AutoReload
(
	"autoReload",
	EV_DEFAULT,
	"b",
	"autoReload",
	"Specifies whether or not the weapon automatically reloads."
);
Event EV_Weapon_AllowAutoSwitch
(
	"allowAutoSwitch",
	EV_DEFAULT,
	"b",
	"allowAutoSwitch",
	"Specifies whether or not the weapon automatically switches to another weapon when it's out of ammo."
);
Event EV_Weapon_ForceReload
(
	"forceReload",
	EV_DEFAULT,
	NULL,
	NULL,
	"Forces a reload to occur."
);
Event EV_Weapon_NextSwitchTime
(
	"nextSwitchTime",
	EV_DEFAULT,
	"f",
	"time",
	"Sets the next switch time."
);

CLASS_DECLARATION( Item, Weapon, NULL )
{
	{ &EV_Item_Pickup,							&Weapon::PickupWeapon },
	{ &EV_Weapon_DoneRaising,					&Weapon::DoneRaising },
	{ &EV_Weapon_DoneFiring,					&Weapon::DoneFiring },
	{ &EV_Weapon_DoneAnimating,					&Weapon::DoneAnimating },
	{ &EV_Weapon_Idle,							&Weapon::Idle },
	{ &EV_BroadcastSound,						&Weapon::WeaponSound },
	{ &EV_Weapon_DoneReloading,					&Weapon::DoneReloading },
	{ &EV_Weapon_DoneReloadingBurst,			&Weapon::DoneReloadingBurst },
	{ &EV_Weapon_SetAmmoClipSize,				&Weapon::SetAmmoClipSize },
	{ &EV_Weapon_SetAmmoInClip,					&Weapon::SetAmmoInClip },
	{ &EV_Weapon_ProcessModelCommands,			&Weapon::ProcessWeaponCommandsEvent },
	{ &EV_Weapon_SetMaxRange,					&Weapon::SetMaxRangeEvent },
	{ &EV_Weapon_SetMinRange,					&Weapon::SetMinRangeEvent },
	{ &EV_Weapon_ActionIncrement,				&Weapon::SetActionLevelIncrement },
	{ &EV_Weapon_NotDroppable,					&Weapon::NotDroppableEvent },
	{ &EV_Weapon_SetAimAnim,					&Weapon::SetAimAnim },
	{ &EV_Weapon_Shoot,							&Weapon::Shoot },
	{ &EV_Weapon_SetFireType,					&Weapon::SetFireType },
	{ &EV_Weapon_DonePutaway,					&Weapon::DonePutaway },
	{ &EV_Weapon_SetProjectile,					&Weapon::SetProjectile },
	{ &EV_Weapon_SetBulletDamage,				&Weapon::SetBulletDamage },
	{ &EV_Weapon_SetBulletCount,				&Weapon::SetBulletCount },
	{ &EV_Weapon_SetBulletKnockback,			&Weapon::SetBulletKnockback },
	{ &EV_Weapon_SetBulletRange,				&Weapon::SetBulletRange },
	{ &EV_Weapon_SetRange,						&Weapon::SetRange },
	{ &EV_Weapon_SetBulletSpread,				&Weapon::SetBulletSpread },
	{ &EV_Weapon_Hand,							&Weapon::SetHand },
	{ &EV_Weapon_Mode,							&Weapon::ModeSet },
	{ &EV_Weapon_AmmoType,						&Weapon::SetAmmoType },
	{ &EV_Weapon_StartAmmo,						&Weapon::SetStartAmmo },
	{ &EV_Weapon_AmmoBoost,						&Weapon::setAmmoBoost },
	{ &EV_Weapon_AmmoRequired,					&Weapon::SetAmmoRequired },
	{ &EV_Weapon_MaxChargeTime,					&Weapon::SetMaxChargeTime },
	{ &EV_Weapon_GiveStartingAmmo,				&Weapon::GiveStartingAmmo },
	{ &EV_Weapon_GiveAmmoBoost,					&Weapon::giveAmmoBoost },
	{ &EV_Weapon_AutoAim,						&Weapon::AutoAim },
	{ &EV_Weapon_Crosshair,						&Weapon::Crosshair },
	{ &EV_Weapon_TorsoAim,						&Weapon::TorsoAim },
	{ &EV_Weapon_LeftAttachToTag,				&Weapon::LeftAttachToTag },
	{ &EV_Weapon_RightAttachToTag,				&Weapon::RightAttachToTag },
	{ &EV_Weapon_DualAttachToTag,				&Weapon::DualAttachToTag },
	{ &EV_Weapon_HolsterTagLeft,				&Weapon::LeftHolsterAttachToTag },
	{ &EV_Weapon_HolsterTagRight,				&Weapon::RightHolsterAttachToTag },
	{ &EV_Weapon_HolsterTagDual,				&Weapon::DualHolsterAttachToTag },
	{ &EV_Weapon_RightHolsterAngles,			&Weapon::SetRightHolsterAngles },
	{ &EV_Weapon_LeftHolsterAngles,				&Weapon::SetLeftHolsterAngles },
	{ &EV_Weapon_DualHolsterAngles,				&Weapon::SetDualHolsterAngles },
	{ &EV_Weapon_HolsterScale,					&Weapon::SetHolsterScale },
	{ &EV_Weapon_WeildedScale,					&Weapon::setWeildedScale },
	{ &EV_Weapon_SetQuiet,						&Weapon::SetQuiet },
	{ &EV_Weapon_SetLoopFire,					&Weapon::SetLoopFire },
	//	{ &EV_Weapon_FullAnimFire,             SetFullAnimFire		},
	{ &EV_Weapon_AutoPutaway,					&Weapon::SetAutoPutaway },
	{ &EV_Weapon_UseNoAmmo,						&Weapon::SetUseNoAmmo },
	{ &EV_Weapon_SetMeansOfDeath,				&Weapon::SetMeansOfDeath },
	{ &EV_Weapon_SetWorldHitSpawn,				&Weapon::SetWorldHitSpawn },
	{ &EV_Weapon_MakeNoise,						&Weapon::MakeNoise },
	{ &EV_Weapon_SetViewModel,					&Weapon::SetViewModel },
	{ &EV_Weapon_SetRegenAmmo,					&Weapon::SetRegenAmmo },
	{ &EV_Weapon_SetRegenOnlyWhenIdle,			&Weapon::SetRegenOnlyWhenIdle },
	{ &EV_Weapon_ChangeIdle,					&Weapon::ChangeIdle },
	{ &EV_Weapon_DrawBowStrain,					&Weapon::DrawBowStrain },
	{ &EV_Weapon_AltDrawBowStrain,				&Weapon::AltDrawBowStrain },
	{ &EV_Weapon_SetAccuracy,					&Weapon::SetAccuracy },
	{ &EV_Weapon_SetReticuleTime,				&Weapon::SetReticuleTime },
	{ &EV_Weapon_SetZoomFOV,					&Weapon::SetZoomFOV },
	{ &EV_Weapon_IncrementZoomFOV,				&Weapon::IncrementZoom	},
	{ &EV_Weapon_SetZoomStage,					&Weapon::setZoomStage	},
	{ &EV_Weapon_SetStartZoom,					&Weapon::SetStartZoom },
	{ &EV_Weapon_SetEndZoom,					&Weapon::SetEndZoom },
	{ &EV_Weapon_SetZoomTime,					&Weapon::SetZoomTime },
	{ &EV_Weapon_SetAimType,					&Weapon::SetAimType },
	{ &EV_Weapon_SetFireTimer,					&Weapon::SetFireTimer },
	{ &EV_Weapon_UseSameClip,					&Weapon::UseSameClip },
	{ &EV_Weapon_SetMaxModes,					&Weapon::SetMaxModes },
	{ &EV_Weapon_SetSwitchMode,					&Weapon::SetSwitchMode },
	{ &EV_Weapon_DoneSwitchToMiddle,			&Weapon::DoneSwitchToMiddle },
	{ &EV_Weapon_DoneSwitching,					&Weapon::DoneSwitching },
	{ &EV_Weapon_TargetIdle,					&Weapon::TargetIdle },
	{ &EV_Weapon_TargetIdleThink,				&Weapon::TargetIdleThink },
	{ &EV_Weapon_SetBurstMode,					&Weapon::SetBurstMode },
	{ &EV_Weapon_FinishedFiring,				&Weapon::FinishedFiring		},
	{ &EV_Weapon_StartFiring,					&Weapon::StartFiring		},
	{ &EV_Weapon_Zoom,							&Weapon::Zoom },
	{ &EV_Weapon_EndZoom,						&Weapon::endZoom },
	{ &EV_Weapon_Rezoom,						&Weapon::rezoom },
	{ &EV_Weapon_SetTargetingSkin,				&Weapon::SetTargetingSkin		},
	{ &EV_Weapon_SetShootingSkin,				&Weapon::SetShootingSkin		},
	{ &EV_Weapon_SetFullAmmoSkin,				&Weapon::setFullAmmoSkin },
	{ &EV_Weapon_SetMoveSpeedModifier,			&Weapon::setMoveSpeedModifier },
	{ &EV_Weapon_SetShootingMoveSpeedModifier,	&Weapon::setShootingMoveSpeedModifier },
	{ &EV_Weapon_UseActorAiming,				&Weapon::UseActorAiming },
	{ &EV_Weapon_SetPowerRating,				&Weapon::SetPowerRating },
	{ &EV_Weapon_SetProjectileSpeed,			&Weapon::SetProjectileSpeed },
	{ &EV_Weapon_SetProjectileDamage,			&Weapon::SetProjectileDamage  },
	{ &EV_Weapon_ViewShake,						&Weapon::setViewShakeInfo },
	{ &EV_Weapon_AdvancedViewShake,				&Weapon::setAdvancedViewShakeInfo },
	{ &EV_Weapon_ReduceViewShake,				&Weapon::reduceViewShake },
	{ &EV_Weapon_ClearViewShake,				&Weapon::clearViewShake },
	{ &EV_ProcessGameplayData,					&Weapon::processGameplayData },

	{ &EV_Weapon_SetArcProjectile,				&Weapon::SetArcProjectile	},
	{ &EV_Weapon_SetLowArcRange,				&Weapon::SetLowArcRange		},

	{ &EV_Weapon_NoAmmoMode,					&Weapon::noAmmoMode },
	{ &EV_Weapon_NoDelay,						&Weapon::setNoDelay },
	{ &EV_Weapon_PauseRegen,					&Weapon::pauseRegen },

	{ &EV_Weapon_SetBurstModeDelay,				&Weapon::setBurstModeDelay },
	{ &EV_Weapon_SetChargedModels,				&Weapon::setChargedModels },
	{ &EV_Weapon_SetControllingProjectile,		&Weapon::setControllingProjectile },

	{ &EV_Weapon_SetCanInterruptFiringState,	&Weapon::setCanInterruptFiringState },

	{ &EV_Weapon_StartViewShake,				&Weapon::startViewShake },

	{ &EV_Weapon_SpreadAnimData,				&Weapon::setSpreadAnimData },

	{ &EV_Weapon_MaxViewShakeChange,			&Weapon::setMaxViewShakeChange },
	{ &EV_Weapon_ControlParms,					&Weapon::setControlParms },
	{ &EV_Weapon_ProjectileControlHidden,		&Weapon::setProjectileControlHidden },

	{ &EV_Weapon_MeleeParms,					&Weapon::setMeleeParms },
	{ &EV_Weapon_FireOffset,					&Weapon::setFireOffset },

	{ &EV_Weapon_AutoReload,					&Weapon::setAutoReload },
	{ &EV_Weapon_AllowAutoSwitch,				&Weapon::setAllowAutoSwitch },
	{ &EV_Weapon_ForceReload,					&Weapon::forceReload },
	{ &EV_Weapon_SetPlayMissSound,				&Weapon::SetPlayMissSound },

	{ &EV_Weapon_NextSwitchTime,				&Weapon::setNextSwitchTime },

	// These anim events are overridden from Entity
	{ &EV_Anim,									&Weapon::PassToAnimate },
	{ &EV_NewAnim,								&Weapon::PassToAnimate },

	{ NULL, NULL }
};

//======================
//Weapon::Weapon
//======================
Weapon::Weapon()
{
	if ( LoadingSavegame )
	{
		// Archive function will setup all necessary data
		return;
	}
	
	// Owner of the weapon
	owner					      = NULL;
	
	// Starting rank of the weapon
	rank					      = 0;
	
	// Amount of ammo required for weapon
	INITIALIZE_WEAPONMODE_VAR( ammorequired, 0 );
	
	// Starting ammo of the weapon
	INITIALIZE_WEAPONMODE_VAR( startammo, 0 );

	INITIALIZE_WEAPONMODE_VAR( _ammoBoost, 0 );
	
	// Amount of ammo the clip can hold
	INITIALIZE_WEAPONMODE_VAR( ammo_clip_size, 0 );
	
	// Amount of ammo in clip
	INITIALIZE_WEAPONMODE_VAR( ammo_in_clip, 0 );
	
	// Amount of time to pass before broadcasting a weapon sound again
	nextweaponsoundtime     = 0;
	
	// The initial state of the weapon
	weaponstate			      = WEAPON_HOLSTERED;
	
	// Is the weapon droppable when the owner is killed
	notdroppable            = false;
	
	// Aim animation for behavior of monsters
	aimanim                 = -1;
	aimframe                = 0;
	
	// start off unattached
	attached = false;
	
	// maximum effective firing distance (for autoaim)
	maxrange = 1000;
	
	// minimum safe firing distance (for AI)
	minrange = 0;
	
	// speed of the projectile (0 == infinite speed)
	memset( projectilespeed, 0, sizeof( projectilespeed ) );
	
	// default action_level_increment
	INITIALIZE_WEAPONMODE_VAR( action_level_increment, 2 );
	
	// Weapons don't move
	setMoveType( MOVETYPE_NONE );
	
	// What type of ammo this weapon fires
	INITIALIZE_WEAPONMODE_VAR( firetype, (firetype_t)0 );
	
	// Init the bullet specs
	INITIALIZE_WEAPONMODE_VAR( bulletdamage,    0 );
	INITIALIZE_WEAPONMODE_VAR( bulletcount,     1 );
	INITIALIZE_WEAPONMODE_VAR( bulletrange,     1024 );
	INITIALIZE_WEAPONMODE_VAR( bulletknockback, 0 );
	INITIALIZE_WEAPONMODE_VAR( ammo_type,       "" );
	INITIALIZE_WEAPONMODE_VAR( loopfire,        false );
	//INITIALIZE_WEAPONMODE_VAR( fullanimfire,    false );
	INITIALIZE_WEAPONMODE_VAR( burstmode,		  false);
	burstcount = 0;
	burstcountmax = 0;
	
	// Init the max amount of time a weapon may be charged (5 seconds)
	INITIALIZE_WEAPONMODE_VAR( max_charge_time,   5 );
	charge_fraction = 1.0f;
	
	// Tag to attach this weapon to on its owner when used in the left hand and in the right hand
	left_attachToTag  = "tag_lhand";
	right_attachToTag = "tag_rhand";
	dual_attachToTag  = "tag_weapon_dual";
	
	// putaway is flagged true when the weapon should be put away by state machine
	putaway = false;
	
	// Default to being able to use the weapon in any hand
	hand    = WEAPON_ANY;
	
	// This is used for setting mode functionality when initializing stuff
	firemodeindex = FIRE_MODE1;
	
	// Name and index
	setName( "Unnamed Weapon" );
	
	// do better lighting on all weapons
	edict->s.renderfx |= RF_EXTRALIGHT;
	
	// make all weapons RF_WEAPONMODELs
	edict->s.renderfx |= RF_WEAPONMODEL;
	
	// Weapons do not auto aim automatically
	autoAimTargetSelectionAngle = 0;
	autoAimLockonAngle = 0;
	// No crosshair visible
	crosshair = false;
	
	// Don't torsoaim
	torsoaim = false;
	
	// Weapons default to making noise
	quiet = false;
	next_noise_time = 0;
	next_noammo_time = 0;
	
	// Used to keep track of last angles and scale before holstering
	lastValid = false;
	lastScale = 1.0f;
	holsterScale = 1.0f;
	_weildedScale = 0.5f;
	
	// Weapon will not be putaway by default when out of ammo
	auto_putaway = false;
	
	// Weapon will be able to be used when it has no ammo
	use_no_ammo  = true;
	
	// Default accuracy value
	for ( int i=0; i<MAX_FIREMODES; i++ )
	{
		for ( int j=0; j<MAX_ACCURACYTYPES; j++ )
		{
			accuracy[i][j] = 0.0; // Defaults to perfect accuracy
		}
	}
		
	aimtype = ACCURACY_STOPPED;
	
	// Reticule and zoom fov defaults
	reticuletime = 0.0;
	zoomfov = 90.0;
	_lastZoomFov = 90.0f;
	
	usesameclip = false;
	
	curmode = FIRE_MODE1;
	maxmode = FIRE_MODE2;
	
	chargetime = 0.0;
	
	switchmode = false;
	targetidle = false;
	targetidleflag = false;
	
	chx = 0;
	chy = 0;
	
	donefiring = false;
	
	zoomed = false;
	useActorAiming = false;
	
	INITIALIZE_WEAPONMODE_VAR(next_fire_time, 0.0f);
	INITIALIZE_WEAPONMODE_VAR(fire_timer, 0.0f);
	
	INITIALIZE_WEAPONMODE_VAR( meansofdeath, MOD_SWORD );
	
	targetingSkin = 0;
	shootingSkin = 0;
	_fullAmmoSkin = 0;
	_fullAmmoMode = FIRE_MODE1;
	
	_powerRating	  = 0.0f;
	_projectileDamage = 0.0f;
	_projectileSpeed  = 0.0f;
	_arcProjectile = false;
	_lowArcRange = 256.0f;
	_playMissSound = false;
	
	defaultMoveSpeedModifier = 1.0f;
	INITIALIZE_WEAPONMODE_VAR( shootingMoveSpeedModifier, 1.0f );
	
	INITIALIZE_WEAPONMODE_VAR( _viewShakeMagnitude, 0.0f );
	INITIALIZE_WEAPONMODE_VAR( _viewShakeDuration, 0.0f );

	_maxViewShakeChange = 10.0f;
	
	INITIALIZE_WEAPONMODE_VAR( _burstModeDelay, 0.0f );
	
	INITIALIZE_WEAPONMODE_VAR( _noAmmoMode, false );
	
	INITIALIZE_WEAPONMODE_VAR( _noDelay, false );

	INITIALIZE_WEAPONMODE_VAR( _chargedModels, 0 );
	
	PostEvent( EV_Weapon_Idle, 0.0f );

	_mpItemType = MP_ITEM_TYPE_WEAPON;

	_controllingProjectile = false;
	_controllingProjectileHidden = false;

	startfiretime = 0.0f;

	INITIALIZE_WEAPONMODE_VAR( _spreadAnims, 0 );
	INITIALIZE_WEAPONMODE_VAR( _spreadTime, 0.0f );

	INITIALIZE_WEAPONMODE_VAR( _meleeWidth,   15.0f );
	INITIALIZE_WEAPONMODE_VAR( _meleeHeight , 45.0f );
	INITIALIZE_WEAPONMODE_VAR( _meleeLength,  96.0f );

	// Regen stuff

	INITIALIZE_WEAPONMODE_VAR( _regenAmount,  0 );
	INITIALIZE_WEAPONMODE_VAR( _regenTime,  0.0f );
	INITIALIZE_WEAPONMODE_VAR( _regenOnlyWhenIdle,  false );
	INITIALIZE_WEAPONMODE_VAR( _nextRegenTime,  0.0f );

	_zoomStage = ZOOM_STAGE_1;

	setRespawnTime( 10 );

	_autoReload = true;

	_allowAutoSwitch = true;

	edict->s.renderfx |= RF_CHILDREN_DONT_INHERIT_ALPHA;

	_nextSwitchTime = 0.0f;
}


//======================
//Weapon::Weapon
//======================
Weapon::Weapon( const char *file )
{
	// The tik file holds all the information available for a weapon
	Weapon();
}

//======================
//Weapon::~Weapon
//======================
Weapon::~Weapon()
{
	DetachGun();
}

//======================
//Weapon::GetRank
//======================
int Weapon::GetRank( void )
{
	return rank;
}

//======================
//Weapon::GetOrder
//======================
int Weapon::GetOrder( void )
{
	return order;
}

//======================
//Weapon::SetRank
//======================
void Weapon::SetRank( int order, int rank )
{
	this->order = order;
	this->rank = rank;
}

//======================
//Weapon::SetAutoPutaway
//======================
void Weapon::SetAutoPutaway( Event *ev )
{
	auto_putaway = ev->GetBoolean( 1 );
}

//======================
//Weapon::SetUseNoAmmo
//======================
void Weapon::SetUseNoAmmo( Event *ev )
{
	use_no_ammo = ev->GetBoolean( 1 );
}

//======================
//Weapon::SetStartAmmo
//======================
void Weapon::SetStartAmmo( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	startammo[firemodeindex] = ev->GetInteger( 1 );
}

void Weapon::setAmmoBoost( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );

	_ammoBoost[ firemodeindex ] = ev->GetInteger( 1 );
}

//======================
//Weapon::SetMaxChargeTime
//======================
void Weapon::SetMaxChargeTime( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	max_charge_time[firemodeindex] = ev->GetFloat( 1 );
}

//======================
//Weapon::SetAmmoRequired
//======================
void Weapon::SetAmmoRequired( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	ammorequired[firemodeindex] = ev->GetInteger( 1 );
}

//======================
//Weapon::GetStartAmmo
//======================
int Weapon::GetStartAmmo( firemode_t mode )
{
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
		return startammo[mode];
	else
	{
		warning( "Weapon::GetStartAmmo", "Invalid mode %d\n", mode );
		return 0;
	}
}

int Weapon::getAmmoBoost( firemode_t mode )
{
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
	{
		return _ammoBoost[ mode ];
	}
	else
	{
		warning( "Weapon::getAmmoBoost", "Invalid mode %d\n", mode );
		return 0;
	}
}

//======================
//Weapon::GetAmmoType
//======================
str Weapon::GetAmmoType( firemode_t mode )
{
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
		return ammo_type[mode];
	else
	{
		warning( "Weapon::GetAmmoType", "Invalid mode %d\n", mode );
		return "UnknownAmmo";
	}
}

//======================
//Weapon::SetAmmoType
//======================
void Weapon::SetAmmoType( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	
	if ( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) )
		ammo_type[firemodeindex] = ev->GetString( 1 );
	else
	{
		warning( "Weapon::SetAmmoType", "Invalid mode %d\n", firemodeindex );
		return;
	}
}

//======================
//Weapon::SetAmmoAmount
//======================
void Weapon::SetAmmoAmount( int amount, firemode_t mode )
{
	firemode_t clipToUse;
	
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;
	
	// If the clip can hold ammo, then set the amount in the clip to the specified amount
	if ( ( clipToUse >= 0 ) && ( clipToUse < MAX_FIREMODES ) )
	{
		if ( ammo_clip_size[clipToUse] )
			ammo_in_clip[clipToUse] = amount;
	}
	else
	{
		warning( "Weapon::SetAmmoAmount", "Invalid mode %d\n", clipToUse );
		return;
	}
}


//-----------------------------------------------------
//
// Name:		GetRequiredAmmo	
// Class:		Weapon
//
// Description:	Retrieves the required amount of ammo to fire one round
//
// Parameters:	mode - the firing mode.
//
// Returns:		None
//-----------------------------------------------------
int Weapon::GetRequiredAmmo( firemode_t mode )
{
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );

	return ammorequired[mode];
}


//======================
//Weapon::GetClipSize
//======================
int Weapon::GetClipSize( firemode_t mode )
{
	firemode_t clipToUse;
	
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;
	
	if ( ( clipToUse >= 0 ) && ( clipToUse < MAX_FIREMODES ) )
		return ammo_clip_size[clipToUse];
	else
	{
		warning( "Weapon::GetClipSize", "Invalid mode %d\n", clipToUse );
		return 0;
	}
}

//======================
//Weapon::UseAmmo
//======================
void Weapon::UseAmmo( int amount, firemode_t mode )
{
	firemode_t clipToUse;

	if ( UnlimitedAmmo( mode ) )
		return;

	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );

	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
		warning( "Weapon::UseAmmo", "Invalid mode %d\n", mode );

	if ( !HasAmmo( mode ) && hasNoAmmoMode( mode ) )
	{
		return;
	}

	// Determine which clip to use

	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;

	// Remove ammo from the clip if it's available

	if ( ammo_clip_size[clipToUse] )
	{
		ammo_in_clip[clipToUse] -= amount;
		if (ammo_in_clip[clipToUse] < 0)
		{
			warning("UseAmmo","Used more ammo than in clip.\n");
			ammo_in_clip[clipToUse] = 0;
		}
		owner->AmmoAmountInClipChanged( ammo_type[mode], ammo_in_clip[clipToUse] );
	}
	else
	{
		assert( owner );
		if ( owner && owner->isClient() && !UnlimitedAmmo( mode ) )
		{
			// Remove ammo from the player's inventory
			owner->UseAmmo( ammo_type[mode], ammorequired[mode] );
		}
	}

	if ( burstmode[mode] ) 
	{
		burstcount--;
	}
}

void Weapon::GetActorMuzzlePosition( Vector *position, Vector *forward, Vector *right, Vector *up, const char* tagname )
{
	int tag_num;
	
	// Using tag_name for convience since it's referenced several times
	str tag_name;
	if ( !tagname )
		tag_name = "tag_barrel";
	else
		tag_name = tagname;
	
	tag_num = gi.Tag_NumForName( edict->s.modelindex, tag_name );
	
	if ( tag_num < 0 )
		gi.Error( ERR_FATAL, "Weapon::GetActorMuzzlePosition, invalid tag name." );
	
	//Test Stuff
	setOrigin();
	setAngles();
	
	//GetTag( tag_num, &pos, &forward, &right, &up );
	
	orientation_t  weap_or, barrel_or, orn;
	Vector         pos;
	vec3_t         mat[3]={0,0,0,0,0,0,0,0,0};
	vec3_t         orient[3];
	int            i, mi, tagnum;
	Sentient *owner;   
	owner = this->owner;
	
	// Get the owner's weapon orientation ( this is custom code and doesn't use the GetTag function
	// because we need to use the saved off fire_frame and fire_animation indexes from the owner	
	mi = owner->edict->s.modelindex;
	
	tagnum = gi.Tag_NumForName( mi, current_attachToTag.c_str() );
	
	
	// Get the orientation based on the frame and anim stored off in the owner.
	// This is to prevent weird timing with getting orientations on different frames of firing
	// animations and the orientations will not be consistent.
	
	AnglesToAxis( owner->angles, owner->orientation );

	orn	= gi.Tag_OrientationEx( mi,
								owner->CurrentAnim( legs ),
								owner->CurrentFrame( legs ),
								tagnum & TAG_MASK,
								owner->edict->s.scale,
								owner->edict->s.bone_tag,
								owner->edict->s.bone_quat,
								0,
								0,
								1.0f,
								( owner->edict->s.anim & ANIM_BLEND ) != 0,
								( owner->edict->s.torso_anim & ANIM_BLEND ) != 0,
								owner->CurrentAnim( torso ),
								owner->CurrentFrame( torso ),
								0,
								0,
								1.0f
								);

	// Transform the weapon's orientation through the owner's orientation
	// Player orientation is normally based on the player's view, but we need
	// it to be based on the model's orientation, so we calculate it here.
	AnglesToAxis( owner->angles, orient );
	VectorCopy( owner->origin, weap_or.origin );
	for ( i=0;  i<3; i++ )
	{
		VectorMA( weap_or.origin, orn.origin[i], orient[i], weap_or.origin );
	}
	
	MatrixMultiply( orn.axis, orient, weap_or.axis );
	
	if ( !this->GetRawTag( tag_name, &barrel_or ) )
	{
		pos = owner->centroid;
		AnglesToAxis( owner->angles, mat );
		return;
	}
	
	// Translate the barrel's orientation through the weapon's orientation
	VectorCopy( weap_or.origin, pos );
	
	for ( i = 0 ; i < 3 ; i++ )
	{
		VectorMA( pos, barrel_or.origin[i], weap_or.axis[i], pos );
	}
	
	MatrixMultiply( barrel_or.axis, weap_or.axis, mat );
	
	if ( position )
		*position = pos;
	
	if ( forward )
		*forward = mat[0];
	
	if ( right )
		*right = mat[1];
	
	if ( up )
		*up = mat[2];
}

//======================
//Weapon::GetMuzzlePosition
//======================
void Weapon::GetMuzzlePosition( Vector *position, Vector *forward, Vector *right, Vector *up )
{
	Vector endpoint, vorg;
	orientation_t barrel_or;
	Vector f, r, u, viewWeapOrg, pview, pos;
	Sentient *owner;
	Player *player;
	int      tagnum;
	
	owner = this->owner;
	assert( owner );
	
	if ( owner->isSubclassOf( Player ) )
		player = ( Player * )owner;
	else
	{
		warning("GetMuzzlePosition called from Weapon class for a non-player character.",NULL);
		return;
	}
	
	// Assign player based variables

	vorg = player->origin;
	// Offset by the view since the origin is at the feet
	vorg.z += player->client->ps.viewheight;

	pview = player->GetVAngles();
	
/*	
	vec3_t new_vieworg;
	vec3_t left;

	if( player->client->ps.leanDelta != 0)
	{
		pview[2] -= player->client->ps.leanDelta / 2.0f;

		AngleVectors( pview, NULL, left, NULL );
		VectorMA( vorg, player->client->ps.leanDelta, left, new_vieworg );
		VectorCopy( new_vieworg, vorg );
	}
*/	
	// Assign player based variables
	
	// Get our f, r, u vectors and set an endpoint
	AngleVectors( pview, f, r, u );
	
	// Create our point in worldspace.  Lots of hardcoded crap here.  
	// This will only work correctly with the default FOV (since we don't have access any real numbers)
	// We also don't have access to the znear value.
	trace_t viewTrace;
	player->GetViewTrace(viewTrace, MASK_SHOT);
	endpoint = viewTrace.endpos;
	Vector dir = endpoint - vorg;
	float len = dir.length();
	dir.normalize();
	endpoint = vorg + dir * (len + 5.0f);
	/*Vector p,s;
	p.x = (float)chx * (-4.0f / 320.0f);
	p.y = (float)chy * (-3.0f / 240.0f);
	s = 4.0f * f + p.x * r + p.y * u;
	s.normalize();
	endpoint = vorg + (s * 3000);*/
	
	// Now we get the origin of the weapon that the CLIENT knows (first person)
	viewWeapOrg = vorg;
	//setScale(0.5f); // Client scales the weapon to half-size
	//VectorMA( viewWeapOrg, -9.0f, r, viewWeapOrg );
	//VectorMA( viewWeapOrg, -7.5f, u, viewWeapOrg );
	
	// Weapon is about at viewWeapOrg position, neglecting viewbob/pullback
	
	// Do a trace to get our endpoint
	
	trace_t trace;
	
	if ( !multiplayerManager.inMultiplayer() || multiplayerManager.fullCollision() )
		trace = G_FullTrace( vorg, vec_zero, vec_zero, endpoint, owner, MASK_SHOT, true, "Weapon::GetMuzzlePosition" );
	else
		trace = G_Trace( vorg, vec_zero, vec_zero, endpoint, owner, MASK_SHOT, true, "Weapon::GetMuzzlePosition" );
	
	// Get the barrel tag from the viewmodel
	
	tagnum = gi.Tag_NumForName( edict->s.viewmodelindex, "tag_barrel" );
	
	if ( tagnum >= 0 )
	{
		barrel_or = gi.Tag_Orientation( edict->s.viewmodelindex, 0, 0, tagnum & TAG_MASK, 1.0f, edict->s.bone_tag, edict->s.bone_quat );
	}
	else
	{
		VectorClear( barrel_or.origin );
	}
	
	// Get the tag_barrel of the gun, otherwise use the owners centroid
	/* if ( !this->GetRawTag( "tag_barrel", &barrel_or ) )
	{
	warning( "Weapon::GetMuzzlePosition", "Could not find tag \"%s\"", current_attachToTag.c_str() );
	*position = owner->centroid;		
	} 
	else
	*position = barrel_or.origin; */
	
	// Translate the barrel's orientation through the weapon's orientation

	pos = viewWeapOrg;

	// Offset the fire position if the weapon tells us to

	if ( _fireOffset[ curmode ] != vec_zero )
	{
		float viewOrientation[3][3];

		AnglesToAxis( pview, viewOrientation );
		MatrixTransformVector( _fireOffset[ curmode ], viewOrientation, pos );
		pos += viewWeapOrg;
	}
	
	// For now lets shoot from the viewpoint not the weapon
	//VectorMA( pos, barrel_or.origin[0], f, pos );
	//VectorMA( pos, barrel_or.origin[1], r, pos );
	//VectorMA( pos, barrel_or.origin[2] - 1.0f, u, pos );
	
	// Final barrel position
	*position = pos;
	
	// Now set a NEW f,r,u vector based on changed needed to hit the center of the screen
	Vector f2 = trace.endpos - *position;
	AngleVectors( f2.toAngles(), f, r, u );
	
	if ( forward ) 
		*forward = f;
	if ( right )
		*right = r;
	if ( up )
		*up = u;
	
	//G_DebugLine( *position, trace.endpos, 1,0,0,1 );
}


//======================
//Weapon::SetAmmoClipSize
//======================
void Weapon::SetAmmoClipSize( Event * ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	ammo_clip_size[firemodeindex] = ev->GetInteger( 1 );
}

//======================
//Weapon::SetAmmoInClip
//======================
void Weapon::SetAmmoInClip( Event * ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	ammo_in_clip[firemodeindex] = ev->GetInteger( 1 );
}

//======================
//Weapon::Shoot
//======================
// This function is called from the weapon tiki file
// With a loopfire weapon, this function may be called 
// multiple times without having called "Fire", so we need
// to also call UseAmmo here for loopfire weapons
void Weapon::Shoot( Event *ev )
{
	Vector      pos, forward, right, up, delta;
	firemode_t  mode = FIRE_MODE1;
	firemode_t  realmode = FIRE_MODE1;
	firemode_t  regenMode;
	
	if ( ev->NumArgs() > 0 )
	{
		mode = WeaponModeNameToNum( ev->GetString( 1 ) );
		realmode = mode;
		if ( mode == FIRE_ERROR )
			return;
	}
	
	// Override the firemode if we're a switch weapon
	if ( switchmode ) 
		mode = curmode;
	else 
		curmode = mode;
	
	/* if ( next_fire_time[mode] > level.time )
		return; */
	
	// Set the fire timer if we have one
	if ( fire_timer[mode] > 0.0 )
		next_fire_time[mode] = fire_timer[mode] + level.time;
	
	// Set accuracy spread
	//bulletspread[mode].x = 50.0f * accuracy[mode][aimtype];
	//bulletspread[mode].y = 50.0f * accuracy[mode][aimtype];
	
	//if ( usesameclip )
	//	mode = FIRE_MODE1;
	
	// If we are in loopfire, we need to keep checking ammo and using it up
	if ( loopfire[mode] )
	{
		if ( HasAmmo( mode ) || hasNoAmmoMode( mode ) ||UnlimitedAmmo( mode ) )
		{
			// Use up the appropriate amount of ammo, it's already been checked that we have enough
			UseAmmo( ammorequired[mode], mode );
			/* if ( mode != realmode )
			{
				if ( burstmode[realmode] )
					burstcount -= ammorequired[realmode];
			} */
			if ( switchmode ) 
				mode = curmode;
		}
		else
		{
			if ( switchmode ) 
				mode = curmode;
			ForceIdle();
			return;
		}
	}
	
	mode = realmode;
	
	if ( ( usesameclip ) || ( ammo_clip_size[ mode ] == 0 ) )
		regenMode = FIRE_MODE1;
	else
		regenMode = mode;

	if ( ( _regenOnlyWhenIdle[ regenMode ] ) && ( _regenAmount[ regenMode ] > 0 ) )
	{
		_nextRegenTime[ regenMode ] = level.time + _regenTime[ regenMode ] + 0.5f;
	}
	
	Sentient *owner;   
	owner = this->owner;
	
	// If I am owned by a player, I need to get the muzzle position, otherwise, I just
	// care about my barrel tag
	if ( useActorAiming || !owner->isSubclassOf( Player ) )
	{
		/*			
		Actor *actorOwner;
		actorOwner = (Actor*)owner;

		Vector gunPos, gunForward, gunRight, gunUp;
		actorOwner->combatSubsystem->AimWeaponTag(actorOwner->enemyManager->GetCurrentEnemy() );
		actorOwner->combatSubsystem->GetGunPositionData( &gunPos, &gunForward, &gunRight, &gunUp );
		forward = gunForward;
		*/
		owner->shotsFiredThisVolley++;
		GetActorMuzzlePosition( &pos , &forward, &right, &up );
	}
	else if ( owner->isSubclassOf( Player ) )
	{
		Player *player = (Player *)owner;

		GetMuzzlePosition( &pos, &forward, &right, &up );
		if (g_aimviewangles->integer || ( player->client->ps.pm_type == PM_SECRET_MOVE_MODE ) ) // use viewangles to aim instead of player muzzle orientation thing
		{
			vec3_t fwd;
			AngleVectors(owner->client->ps.viewangles,fwd,NULL,NULL);
			forward = fwd;
		}
		// Apply spread
	
		if ( firetype[mode] != FT_BULLET )
		{
			applySpread( &forward, &right, &up );
		}
	}
	
	PostEvent( EV_Weapon_StartViewShake, level.frametime * 2 );
	
	Vector testForward;
	pos.AngleVectors( &testForward );
	
	if ( firetype[mode] == FT_PROJECTILE )
	{
		// Temporary 
		// This is bad bad bad
		//
		/*if ( owner->isSubclassOf(Actor) )
		{
			Entity *target = NULL;
			Actor *actor = NULL;
			Vector actorToTarget;
			
			actor = (Actor*)owner;
			
			if ( actor )
			{
				target = actor->enemyManager->GetCurrentEnemy();
				
				if ( target )
				{
					actorToTarget = target->centroid - actor->centroid;
					actorToTarget.normalize();
					forward = actorToTarget;
				}
			}			
			
			applySpread( pos, &forward, &right, &up );
		}*/

		str projectileModelName;
		
		// Figure out the projectile name to shoot

		if ( _chargedModels[ mode ] )
		{
			int modelIndexToUse;

			// Build the projectile name - projectileModel + # + .tik

			projectileModelName = projectileModel[mode];

			modelIndexToUse = ( _chargedModels[ mode ] * charge_fraction ) + 1;

			if ( modelIndexToUse > _chargedModels[ mode ] )
				modelIndexToUse = _chargedModels[ mode ];

			projectileModelName.CapLength( projectileModelName.length() - 4 );
			projectileModelName += modelIndexToUse;
			projectileModelName += ".tik";
		}
		else
		{
			projectileModelName = projectileModel[mode];
		}

		ProjectileAttack( pos,
			forward,
			owner,
			projectileModelName,
			charge_fraction
			);
	}
	else if ( firetype[mode] == FT_BULLET )
	{
		Vector spread;
		float range;

		if ( owner->isSubclassOf( Player ) )
		{
			// Move the position to shoot from back a little ( 1 1/2 feet )
			
			//pos = pos - forward * 24.0f;

			spread = getSpread();
			//range = bulletrange[mode] + 24.0f;
			range = bulletrange[mode];
		}
		else
		{
			spread = bulletspread[ mode ];
			range = bulletrange[mode];
		}
		
		BulletAttack( pos,
			forward,
			right,
			up,
			range,
			bulletdamage[mode],
			bulletknockback[mode],
			0,
			GetMeansOfDeath( mode ),
			spread,
			bulletcount[mode],
			owner,
			this
			);
	}
	else if ( firetype[mode] == FT_EXPLOSION )
	{
		if ( owner && owner->isSubclassOf( Player ) )
		{
			Player *player = (Player *)owner;

			player->shotFired();
		}

		ExplosionAttack( pos, owner, projectileModel[mode], forward, owner );
	}
	// Apparently the special_projectile stuff is no longer used
	/* else if ( firetype[mode] == FT_SPECIAL_PROJECTILE )
	{
		this->SpecialFireProjectile( pos,
			forward,
			right,
			up,
			owner,
			projectileModel[mode],
			charge_fraction
			);
	} */
	else if ( firetype[mode] == FT_MELEE ) // this is a weapon that fires like a sword
	{
		Vector melee_pos, melee_end;
		Vector dir;
		float damage;
		meansOfDeath_t meansofdeath;
		float knockback;
		
		if ( owner->isSubclassOf( Player ) )
		{
			Vector forward;

			Player *player = (Player *)owner;
			melee_pos = player->origin + Vector( 0.0f, 0.0f, player->client->ps.viewheight );

			player->GetVAngles().AngleVectors( &forward );

			melee_end = melee_pos + forward * _meleeLength[ mode ];
		}
		else
		{
			melee_pos = owner->centroid;
			melee_end = owner->centroid + Vector( owner->orientation[0] ) * _meleeLength[ mode ];
		}
		
		damage = bulletdamage[mode];
		knockback = bulletknockback[mode];
		
		meansofdeath = GetMeansOfDeath( mode );
		
		if ( owner->isSubclassOf( Player ) )
		{
			Player *player = (Player *)(Sentient *)owner;
			
			meansofdeath = player->changetMeansOfDeath( meansofdeath );
			damage = player->getDamageDone( damage, meansofdeath, true );
			
			knockback = (knockback + player->GetPlayerKnockback()) * player->GetKnockbackMultiplier();
		}
		
		Container<EntityPtr>victimlist;
		
		if ( MeleeAttack( melee_pos, melee_end, damage, owner, meansofdeath, _meleeWidth[ mode ], -_meleeHeight[ mode ], 
			 _meleeHeight[ mode ], knockback, true, &victimlist ) )
		{
			// Hit something

			Sound( "impact_flesh", CHAN_VOICE );
		}
		else
		{
			// Didn't hit anything that took damage

			// Try to hit the world since we didn't do any damage to anything
			trace_t trace = G_Trace( melee_pos, Vector( -8.0f, -8.0f, -8.0f ), Vector( 8.0f, 8.0f, 8.0f ), melee_end, owner, MASK_MELEE, false, "Weapon::Shoot" );
			
			Entity *victim = G_GetEntity( trace.entityNum );
			
			if ( victim && ( ( victim == world ) || ( victim->takedamage == DAMAGE_NO ) ) )
			{
				vec3_t  newangles;
				vectoangles( trace.plane.normal, newangles );
				WorldHitSpawn( mode, trace.endpos, newangles, 0.1f );
				
				str realname = this->GetRandomAlias( "impact_world" );
				if ( realname.length() > 1 )
					this->Sound( realname, CHAN_VOICE );
			}
		}
	}
	
	if ( !quiet )
	{
		if ( next_noise_time <= level.time )
		{
			BroadcastSound();
			next_noise_time = level.time + 1.0f;
		}
	}
}

//======================
//Weapon::SetAimAnim
//======================
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 );
}

//======================
//Weapon::SetOwner
//======================
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 );
}

//======================
//Weapon::AmmoAvailable
//======================
int Weapon::AmmoAvailable( firemode_t mode )
{
	// Returns the amount of ammo the owner has that is available for use
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
	{
		warning( "Weapon::AmmoAvailable", "Invalid mode %d\n", mode );
		return 0;
	}
	
	// Make sure there is an owner before querying the amount of ammo
	if ( owner )
	{
		return owner->AmmoCount( ammo_type[mode] );
	}
	else
	{
		warning( "Weapon::AmmoAvailable", "Weapon does not have an owner.\n" );
		return 0;
	}
}

//======================
//Weapon::UnlimitedAmmo
//======================
qboolean Weapon::UnlimitedAmmo( firemode_t mode )
{
	if ( !owner )
	{
		return false;
	}
	
	if ( !ammorequired[mode] )
		return true;
	
	if ( !owner->isClient() || ( owner->flags & FL_GODMODE ) || multiplayerManager.checkFlag( MP_FLAG_INFINITE_AMMO ) )
	{
		return true;
	}
	else if ( !stricmp( ammo_type[mode], "None" ) )
	{
		return true;
	}
	
	return false;
}

//======================
//Weapon::HasInvAmmo
//======================
// Checks to see if we have any ammo in general
qboolean Weapon::HasInvAmmo( firemode_t mode )
{
	if ( !ammorequired[mode] )
		return false;
	
	return ( AmmoAvailable( mode ) >= ammorequired[mode] );
}

//======================
//Weapon::HasAmmo
//======================
qboolean Weapon::HasAmmo( firemode_t mode, int numShots )
{
	firemode_t clipToUse;

	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
	{
		warning( "Weapon::HasAmmo", "Invalid mode %d\n", mode );
		return false;
	}
	
	if ( !owner )
	{
		return false;
	}
	
	if ( UnlimitedAmmo( mode ) )
	{
		return true;
	}
	
	if ( burstmode[mode] && burstcount <= 0 ) 
	{
		return false;
	}

	if ( usesameclip )
	{
		clipToUse = FIRE_MODE1;
	}
	else
	{
		clipToUse = mode;
	}

	// If the weapon uses a clip, check for ammo in the right clip
	if ( ammo_clip_size[clipToUse] )
	{
		return HasAmmoInClip( mode, numShots );
	}
	else // Otherwise check if ammo is available in general
	{
		if ( !ammorequired[mode] )
			return false;
		
		return ( AmmoAvailable( mode ) >= ammorequired[mode] * numShots );
	}
}

//======================
//Weapon::HasAmmoInClip
//======================
qboolean Weapon::HasAmmoInClip( firemode_t mode, int numShots )
{
	firemode_t clipToUse;

	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
		warning( "Weapon::HasAmmoInClip", "Invalid mode %d\n", mode );

	if ( usesameclip )
	{
		clipToUse = FIRE_MODE1;
	}
	else
	{
		clipToUse = mode;
	}
	
	if ( ammo_clip_size[clipToUse] )
	{
		if ( ammo_in_clip[clipToUse] >= ammorequired[mode] * numShots )
		{
			return true;
		}
	}
	return false;
}

//======================
//Weapon::ForceState
//======================
void Weapon::ForceState( weaponstate_t state )
{
	weaponstate = state;
}

//======================
//Weapon::ReadyToFire
//======================
qboolean Weapon::ReadyToFire( firemode_t mode, qboolean playsound )
{
	// Make sure the weapon is in a good state

	if ( _canInterruptFiringState )
	{
		if ( weaponstate != WEAPON_READY && weaponstate != WEAPON_FIRING )
			return false;
	}
	else
	{
		if ( weaponstate != WEAPON_READY )
			return false;
	}

	// If the weapon doesn't take ammo, we're always ready.
	str ammotype = GetAmmoType( ( firemode_t )mode );
	if ( ammotype == "None" )
		return true;
	
	//if ( usesameclip )
	//	mode = FIRE_MODE1;
	
	if ( HasAmmo( mode ) )
	{
		return true;
	}
	
	if ( hasNoAmmoMode( mode ) )
	{
		return true;
	}
	
	if ( playsound && ( level.time > next_noammo_time ) && ( AmmoAvailable( mode ) < ammorequired[mode] ) )
	{
		Sound( "snd_noammo" );
		next_noammo_time = level.time + 0.25f;

		// If this is a bot, wipe out the rest of his useless ammo in the clip to prevent confusion

		if ( owner && owner->isSubclassOf( Player ) )
		{
			/* if ( player->edict->svflags & SVF_BOT )
			{
				ammo_in_clip[ mode ] = 0;
			}
			else */
			{
				// Auto switch to phaser

				if ( shouldAutoSwitch( mode ) )
				{
					autoSwitch();
				}

			}
		}
	}

	return false;
}

//======================
//Weapon::PutAway
//======================
void Weapon::PutAway( void )
{
	//	int i;
	//	int original_ammo_count;
	//	int new_ammo_count;
	
	if ( owner && owner->isSubclassOf( Actor ) )
	{
		Event *newEvent = new Event( EV_DisplayEffect );
		newEvent->AddString( "TransportOut" );
		newEvent->AddString( "FederationWeapons" );
		ProcessEvent( newEvent );
	}

	if ( _controllingProjectile )
	{
		toggleProjectileControl();
	}
	
	// set the putaway flag to true, so the state machine know to put this weapon away
	putaway = true;
	
	if ( !owner )
		return;

	Uninitialize();
	
	// Give ammo back to owner
	/*
	for( i = 0 ; i < MAX_FIREMODES ; i++ )
	{
		if ( ammo_in_clip[ i ] )
		{
			// Add ammo to owner
			original_ammo_count = owner->AmmoCount( ammo_type[ i ] );
			owner->GiveAmmo( ammo_type[ i ], ammo_in_clip[ i ] );
			new_ammo_count = owner->AmmoCount( ammo_type[ i ] );
	
			// Subtract ammo from weapon
	  
			ammo_in_clip[ i ] = ammo_in_clip[ i ] - (new_ammo_count - original_ammo_count);
		
			owner->AmmoAmountInClipChanged( ammo_type[ i ], ammo_in_clip[ i ] );
		}
	}*/
}

//======================
//Weapon::DetachFromOwner
//======================
void Weapon::DetachFromOwner( void )
{
	if ( edict->s.renderfx | RF_DONTDRAW )
		animate->StopAnimatingAtEnd();
	else
		animate->StopAnimating();

	DetachGun();
	weaponstate = WEAPON_HOLSTERED;
	current_attachToTag = "";

	Uninitialize();
}

//======================
//Weapon::AttachToOwner
//======================
void Weapon::AttachToOwner( weaponhand_t hand )
{
	AttachGun( hand , false );
	
	if ( targetidle )
		PostEvent(EV_Weapon_TargetIdleThink, TargetIdleTime);

	ForceIdle();
}

//======================
//Weapon::AttachToHolster
//======================
void Weapon::AttachToHolster( weaponhand_t hand )
{
	AttachGun( hand, true );
	animate->RandomAnimate( "holster", EV_Weapon_Idle );
}

//======================
//Weapon::Drop
//======================
qboolean Weapon::Drop( void )
{
	float radius;
	Vector temp;
	int i;
	
	if ( !owner )
	{
		return false;
	}
	
	if ( !IsDroppable() )
	{
		return false;
	}

	if ( multiplayerManager.inMultiplayer() && !multiplayerManager.checkRule( "weapon-candrop", true, NULL ) )
	{
		return false;
	}
	
	DetachGun();

	// Get rid of special effect stuff

	CancelEventsOfType( EV_DisplayEffect );
	CancelEventsOfType( EV_FadeIn );
	clearCustomShader();
	setAlpha( 1.0f );
	edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS;
	edict->s.renderfx &= ~RF_FORCE_ALPHA;

	if ( _controllingProjectile )
	{
		toggleProjectileControl();
	}

	temp[ 1 ] = 64.0;
	temp[ 2 ] = 48.0;
	if ( owner )
	{
		setOrigin( owner->origin + temp );
	}
	else
	{
		setOrigin( origin + temp );
	}
	
	// hack to fix the bounds when the gun is dropped
	// once dropped reset the rotated bounds
	flags |= FL_ROTATEDBOUNDS;
	
	if ( ( mins == vec_zero ) && ( maxs == vec_zero ) )
	{
		gi.CalculateBounds( edict->s.modelindex, edict->s.scale, mins, maxs );
		radius = ( mins - maxs ).length() * 2.0f;
		mins.x = mins.y = -radius;
		maxs.x = maxs.y = radius;
		setSize( mins, maxs );
	}
	
	// stop animating
	ForceIdle();
	animate->SetFrame( 0 );
	
	// drop the weapon
	PlaceItem();
	if ( owner )
	{
		temp[ 0 ] = G_CRandom( 50.0f );
		temp[ 1 ] = G_CRandom( 50.0f );
		temp[ 2 ] = 250.0f;
		velocity = owner->velocity * 0.5f + temp;
		setAngles( owner->angles );
	}
	
	avelocity = Vector( 0.0f, G_CRandom( 360.0f ), 0.0f );

	// Get rid of all of the ammo in the weapon

	for ( i = 0 ; i < MAX_FIREMODES ; i++ )
	{
		ammo_in_clip[ i ] = 0;
	}
	
	// FIXME - Make this work right if we need it
	/*
	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 );
	CancelEventsOfType( EV_Weapon_DoneReloadingBurst );
	
	// Remove this from the owner's item list
	if ( owner )
	{
		owner->RemoveItem( this );
	}
	
	owner = NULL;

	if ( multiplayerManager.inMultiplayer() && gi.Anim_NumForName( edict->s.modelindex, "idle_onground" ) >= 0 )
	{
		animate->RandomAnimate( "idle_onground" );
		edict->s.eType = ET_MODELANIM;
	}

	if ( multiplayerManager.inMultiplayer() )
	{
		edict->s.renderfx |= RF_FULLBRIGHT;
	}

	// Fade out dropped weapons, to keep down the clutter
	PostEvent( EV_FadeOut, 30.0f );

	Uninitialize();
	return true;
}

//======================
//Weapon::Charge
//======================
void Weapon::Charge( firemode_t mode )
{
}

//======================
//Weapon::ReleaseFire
//======================
void Weapon::ReleaseFire( firemode_t mode, float charge_time )
{
	// Calculate and store off the charge fraction to use when the weapon actually shoots
	
	// Clamp to max_charge_time
	if ( charge_time > max_charge_time[mode] )
		charge_fraction = 1.0f;
	else
		charge_fraction = charge_time / max_charge_time[mode];
	
	// Call regular fire function
	Fire( mode );
}

//======================
//Weapon::Fire
//======================
// This function is called from the state machine
// to tell the tiki which weapon animation to play
// The tiki file will most likely post the "shoot" event
// shortly after this function is called
void Weapon::Fire( firemode_t mode )
{
	Event *done_event=NULL;
	firemode_t realmode;
	Vector pos;
	//char anim[128];
	str fireAnimName;
	
	// Sanity check the mode
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
		warning( "Weapon::Fire", "Invalid mode %d\n", mode );
	
	realmode = mode;
	
	// Override the firemode if we're a switch weapon
	if ( switchmode )
		mode = curmode;
	else
		curmode = mode;
	
	if ( next_fire_time[mode] > level.time )
		return;
	
	// Set the fire timer if we have one
	if ( fire_timer[mode] > 0.0f )
		next_fire_time[mode] = fire_timer[mode] + level.time;
	
	// If we are in loopfire mode, then we don't pass a DoneFiring event
	if ( !loopfire[mode] )
	{
		// The DoneFiring event requires to know the firing mode so save that off in the event
		done_event = new Event( EV_Weapon_DoneFiring );
		done_event->AddInteger( mode );
	}
	
	/*
	if ( fullanimfire[mode] )
	{
		donefiring = false;
		done_event = new Event ( EV_Weapon_DoneFiring );
		done_event->AddInteger( mode );
	}
	*/
	
	// Set the state of the weapon to FIRING
	weaponstate = WEAPON_FIRING;
	
	if ( shootingSkin )
		ChangeSkin( shootingSkin, true );
	
	// Cancel any old done firing events
	CancelEventsOfType( EV_Weapon_DoneFiring );
	
	// Play the correct animation
	if ( !switchmode )
	{
		if ( mode == FIRE_MODE1 )
			fireAnimName = "fire";
		//animate->RandomAnimate( "fire", done_event );
		else if ( mode == FIRE_MODE2 )
			fireAnimName = "alternatefire";
		//animate->RandomAnimate( "alternatefire", done_event );
	}
	else
	{
		//sprintf(anim,"fire_mode%d",(int)mode+1);
		fireAnimName = "fire_mode";
		fireAnimName += (int)mode+1;
		//if ( animate->HasAnim( anim ) )
		//	animate->RandomAnimate( anim, done_event );
	}
	
	if ( world && world->isThingBroken( item_name.c_str() ) )
	{
		fireAnimName += "_broken";
		animate->RandomAnimate( fireAnimName.c_str(), done_event );
		return;
	}
	else if ( !HasAmmo( mode ) && hasNoAmmoMode( mode ) )
	{
		fireAnimName += "_noammo";
	}
	else if ( _spreadTime[ mode ] && _spreadAnims[ mode ] )
	{
		float firingTime;
		int animNum;

		firingTime = level.time - startfiretime;

		animNum = 1 + ( firingTime / _spreadTime[ mode ] ) * ( _spreadAnims[ mode ] + 1 );

		if ( animNum > _spreadAnims[ mode ] )
			animNum = _spreadAnims[ mode ];

		fireAnimName += "_spread";
		fireAnimName += animNum;
	}
	
	animate->RandomAnimate( fireAnimName.c_str(), done_event );
	
	// Use up the appropriate amount of ammo, it's already been checked that we have enough
	//if ( usesameclip )
	//	mode = FIRE_MODE1;
	
	if ( !loopfire[mode] )
	{
		if ( ammorequired[mode] != ammorequired[realmode] )
			UseAmmo(ammorequired[realmode], mode);
		else
			UseAmmo( ammorequired[mode], mode );
		if ( mode != realmode )
		{
			if ( burstmode[realmode] )
				burstcount -= ammorequired[realmode];
		}
	}
}

//======================
//Weapon::DetachGun
//======================
void Weapon::DetachGun( void )
{
	if ( attached )
	{
		StopSound( CHAN_WEAPONIDLE );
		attached = false;
		detach();
		hideModel();

		clearDisplayEffects();

		if ( lastValid )
		{
			// Restore the last scale

			setScale( lastScale );
			lastValid = false;
		}
	}
}

//======================
//Weapon::AttachGun
//======================
void Weapon::AttachGun( weaponhand_t hand, qboolean holstering )
{
	int tag_num = 0;
	
	if ( !owner )
	{
		current_attachToTag = "";
		return;
	}
	
	if ( attached )
	{
		DetachGun();
	}
	
	if ( holstering )
	{
		// Save off these values if we are holstering the weapon.  We will restore them when
		// the users raises the weapons again.
		lastAngles = this->angles;
		lastScale  = this->edict->s.scale;
		lastValid  = true;
	}
	else
	{
		lastScale  = this->edict->s.scale;
		lastValid  = true;

		if ( owner->isSubclassOf( Player ) )
		{
			if ( mp_bigGunMode->integer )
				setScale( _weildedScale * 2.0f );
			else
				setScale( _weildedScale );
		}
	}
	/* else if ( lastValid )
	{

		// Restore the last
		setScale( lastScale );
		setAngles( lastAngles );
		lastValid = false;
	} */

	
	switch( hand )
	{
	case WEAPON_LEFT:
		if ( holstering )
		{
			current_attachToTag = leftholster_attachToTag;
			setAngles( leftHolsterAngles );
			setScale( holsterScale );
		}
		else
		{
			current_attachToTag = left_attachToTag;
		}
		break;
	case WEAPON_RIGHT:
	case WEAPON_DUAL:
		if ( holstering )
		{
			current_attachToTag = rightholster_attachToTag;
			setAngles( rightHolsterAngles );
			setScale( holsterScale );
		}
		else
		{
			current_attachToTag = right_attachToTag;
		}
		break;
		
	default:
		warning( "Weapon::AttachGun", "Invalid hand for attachment of weapon specified" );
		break;
	}
	
	if ( !current_attachToTag.length() )
		return;
	
	tag_num = gi.Tag_NumForName( owner->edict->s.modelindex, this->current_attachToTag.c_str() );
	
	NoLerpThisFrame();
	if	( tag_num >= 0 )
	   {
		attached = true;
		
		attach( owner->entnum, tag_num, false );
		
		showModel();
		setOrigin();

		if ( owner && owner->isSubclassOf( Actor ) )
		{
			Event *newEvent = new Event( EV_DisplayEffect );
			newEvent->AddString( "TransportIn" );
			newEvent->AddString( "FederationWeapons" );
			ProcessEvent( newEvent );
		}
	}
	else
	{
		warning( "Weapon::AttachGun", "Attachment of weapon to tag \"%s\": Tag Not Found\n", this->current_attachToTag.c_str() );
	}
}

//======================
//Weapon::GiveStartingAmmo
//======================
void Weapon::GiveStartingAmmo( Event *ev )
{
	str   ammotype;
	int   mode;
	Entity *entityToGiveAmmo;
	Sentient *sentient;
	
	if ( ev->NumArgs() > 0 )
		entityToGiveAmmo = ev->GetEntity( 1 );
	else
		entityToGiveAmmo = owner;
	
	if ( !entityToGiveAmmo || !entityToGiveAmmo->isSubclassOf( Sentient ) )
	{
		assert( entityToGiveAmmo );
		
		warning( "Weapon::GiveStartingAmmo", "Could not give ammo\n" );
		return;
	}
	
	sentient = reinterpret_cast<Sentient *>(entityToGiveAmmo);
	
	// Give the player the starting ammo
	
	for ( mode=FIRE_MODE1; mode<MAX_FIREMODES; mode++ )
	{
		ammotype = GetAmmoType( ( firemode_t )mode );
		
		if ( ammotype.length() )
		{
			sentient->GiveAmmo( ammotype, this->GetStartAmmo( ( firemode_t )mode ), false );
		}
	}
}

void Weapon::giveAmmoBoost( Event *ev )
{
	str   ammotype;
	int   mode;
	Entity *entityToGiveAmmo;
	Sentient *sentient;
	
	
	// Get the sentient to give the ammo boost to

	entityToGiveAmmo = ev->GetEntity( 1 );
	
	if ( !entityToGiveAmmo || !entityToGiveAmmo->isSubclassOf( Sentient ) )
	{
		assert( entityToGiveAmmo );
		
		warning( "Weapon::GiveAmmoBoost", "Could not give ammo\n" );
		return;
	}
	
	sentient = reinterpret_cast<Sentient *>(entityToGiveAmmo);
	
	// Give the sentient the ammo boost
	
	for ( mode = FIRE_MODE1 ; mode < MAX_FIREMODES ; mode++ )
	{
		ammotype = GetAmmoType( ( firemode_t )mode );
		
		if ( ammotype.length() )
		{
			sentient->GiveAmmo( ammotype, this->getAmmoBoost( ( firemode_t )mode ), true );
		}
	}
}

//======================
//Weapon::PickupWeapon
//======================
void Weapon::PickupWeapon( Event *ev )
{
	Sentient       *sen;
	Entity         *other;
	Weapon         *weapon;
	qboolean       hasweapon;
	//qboolean       giveammo[MAX_FIREMODES];
	//int            mode;
	
	other = ev->GetEntity( 1 );
	
	if ( !other )
		return;
	
	if ( !other->isSubclassOf( Sentient ) )
	{
		return;
	}
	
	sen = ( Sentient * )other;

	if ( !Pickupable( other ) )
		return;
	
	// 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( item_name );
	
	
	if ( hasweapon && !multiplayerManager.checkFlag( MP_FLAG_WEAPONS_STAY ) )
	{
		Event *event;
		
		event = new Event( EV_Weapon_GiveAmmoBoost );
		event->AddEntity( other );
		ProcessEvent( event );
		
		ItemPickup( other, false );
	}
	else
	{
		weapon = ( Weapon * )ItemPickup( other );
		
		if ( !weapon )
		{
			// Item Pickup failed, so don't give ammo either.
			return;
		}
	}
	/*   else
	{
		for ( mode = FIRE_MODE1; mode < MAX_FIREMODES; mode++ )
		{
			giveammo[mode] = ( sen->isClient() && ammo_type[mode].length() && startammo[mode] );
	
			if ( !giveammo[mode] )
			{
				return;
			}
	  
			sen->GiveAmmo( ammo_type[mode].c_str(), startammo[mode] );
		}
	} */
}

//======================
//Weapon::ForceIdle
//======================
void Weapon::ForceIdle( void )
{
	// Force the weapon to the idle animation
	weaponstate = WEAPON_READY;
	
	// Clear our fire timer
	//INITIALIZE_WEAPONMODE_VAR(next_fire_time, 0.0);
	
	float r = G_Random(100.0f);
	char anim[128];
	
	
	int animNum = gi.Anim_NumForName( edict->s.modelindex, "idle_onground" );
	if ( multiplayerManager.inMultiplayer() && animNum >= 0	&& animate->CurrentAnim() == animNum )
	{
		return;
	}
	
	// If this is a switchmode weapon, we have a very different idle system
	if ( switchmode )
	{
		if ( r < 5.0f )
		{
			sprintf(anim,"idle_rare_mode%d",(int)curmode+1);
			if ( animate->HasAnim( anim ) )
			{
				animate->RandomAnimate( anim );
				return;
			}
		}
		
		if ( r < 15.0f )
		{
			sprintf(anim,"idle_uncommon_mode%d",(int)curmode+1);
			if ( animate->HasAnim( anim ) )
			{
				animate->RandomAnimate( anim );
				return;
			}
		}
		
		sprintf(anim,"idle_common_mode%d",(int)curmode+1);
		if ( animate->HasAnim( anim ) )
		{
			animate->RandomAnimate( anim );
			return;
		}
		
		// We shouldn't get here if the anims are set up correctly
		// for the switch mode weapon
		animate->RandomAnimate("idle");
		return;
	}
	
	if ( r < 5.0f && animate->HasAnim("idle_rare") )
	{
		animate->RandomAnimate( "idle_rare" );
		return;
	}
	
	if ( r < 15.0 && animate->HasAnim("idle_uncommon") )
	{
		animate->RandomAnimate("idle_uncommon");
		return;
	}
	
	if ( animate->HasAnim("idle_common") )
	{
		animate->RandomAnimate( "idle_common" );
		return;
	}
	
	// We shouldn't get here if the anims are set up correctly
	animate->RandomAnimate("idle");
}

//======================
//Weapon::DoneRaising
//======================
void Weapon::DoneRaising( Event *ev )
{
	Event *event;
	
	weaponstate = WEAPON_READY;
	ForceIdle();
	
	if ( !owner )
	{
		PostEvent( EV_Remove, 0.0f );
		return;
	}
	
	event = new Event( EV_Weapon_DoneReloading );
	event->AddInteger( FIRE_MODE1 );
	ProcessEvent( event );
	
	event = new Event( EV_Weapon_DoneReloading );
	event->AddInteger( FIRE_MODE2 );
	ProcessEvent( event );
	
	if ( targetidle )
		PostEvent(EV_Weapon_TargetIdleThink, TargetIdleTime);
}

//----------------------------------------------------------------
// Name:			playAnim
// Class:			Weapon
//
// Description:		Plays the specified animation and marks its weapon state as animating
//
// Parameters:		const str &animName			- name of the animation to play
//					bool animatingFlag			- defaults to true
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::playAnim( const str &animName, bool animatingFlag )
{ 
	if ( animate->HasAnim( animName.c_str() ) )
	{
		if ( animatingFlag )
			weaponstate = WEAPON_ANIMATING;

		SetAnim( animName, EV_Weapon_DoneAnimating );
	}
}



//===============================================================
// Name:		animName
// Class:		Weapon
//
// Description: Sets the weapons animation.
// 
// Parameters:	const str& -- 
//
// Returns:		None
// 
//===============================================================
void Weapon::SetAnim( const str	&animName, Event *endevent, bodypart_t bodypart )
{
	assert( animate );
	animate->RandomAnimate( animName.c_str(), endevent, bodypart );
	AddEffectsAnims();
}


//===============================================================
// Name:		animName
// Class:		Weapon
//
// Description: Sets the weapons animation.
// 
// Parameters:	const str& -- 
//
// Returns:		None
// 
//===============================================================
void Weapon::SetAnim( const str	&animName, const Event &endevent, bodypart_t bodypart )
{
	assert( animate );
	animate->RandomAnimate( animName.c_str(), endevent, bodypart );
	AddEffectsAnims();
}

//===============================================================
// Name:		PassToAnimate
// Class:		Weapon
//
// Description: Passes the event to the animation object, and gives
//				the weapon a chance to add additional special effects.
//				If the animation object doesn't exist, creates it.
// 
// Parameters:	Event* -- an animation related event.
//
// Returns:		None
// 
//===============================================================
void Weapon::PassToAnimate( Event *ev )
{
	if ( !animate )
	{
		animate = new Animate( this );
	}
	Event *new_event = new Event( ev );
	animate->ProcessEvent( new_event );
	AddEffectsAnims();
}

//===============================================================
// Name:		AddEffectsAnims
// Class:		Weapon
//
// Description: Adds the effect anims for a weapon.
// 
// Parameters:	None
//
// Returns:		None
// 
//===============================================================
void Weapon::AddEffectsAnims( void )
{
}

//======================
//Weapon::DoneAnimating
//======================
void Weapon::DoneAnimating( Event *ev )
{
	weaponstate = WEAPON_READY;
	
	if ( !owner )
		PostEvent( EV_Remove, 0.0f );
	
	return;
}

//======================
//Weapon::ClientFireDone
//======================
void Weapon::ClientFireDone( void )
{
	// This is called when the client's firing animation is done
}

//======================
//Weapon::DoneFiring
//======================
void Weapon::DoneFiring( Event *ev )
{
	/*
	firemode_t mode = (firemode_t)ev->GetInteger( 1 );

  
	if ( fullanimfire[mode] )
	{
		donefiring = true;	
		return;
	}
	*/
	
	if ( shootingSkin )
		ChangeSkin( shootingSkin, false );
	
	// This is called when the weapon's firing animation is done 
	// If this weapon has a fire_stop animation, play it (tiki will force us back to idle)
	if ( animate->HasAnim("fire_stop") )
		animate->RandomAnimate("fire_stop");
	else
		ForceIdle();
	
	// Check to see if the auto_putaway flag is set, and the weapon is out of ammo.  If so, then putaway the
	// weapon.
	if (
		( !HasAmmo( FIRE_MODE1 )   || ( !stricmp( ammo_type[ FIRE_MODE1 ], "None" ) ) ) &&
		( !HasAmmo( FIRE_MODE2 ) || ( !stricmp( ammo_type[ FIRE_MODE2 ], "None" ) ) ) &&
		auto_putaway
		)
	{
		PutAway();
	}
}

//======================
//Weapon::DoneReloading
//======================
void Weapon::DoneReloading( Event *ev )
{
	int         amount;
	int         amount_used;
	firemode_t  mode;
	firemode_t  clipToUse;
	
	// Get the mode from the passed in event
	mode = (firemode_t)ev->GetInteger( 1 );
	
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
		warning( "Weapon::DoneReloading", "Invalid mode %d\n", mode );
	
	/* if ( regenAmount > 0 )
	{
		int tmpamount = 0;
		Ammo *ammo;
		ammo = owner->FindAmmoByName( ammo_type[mode] );
		if ( ammo ) 
		{
			tmpamount = (ammo->getMaxAmount() / 2) - owner->AmmoCount( ammo_type[mode] );
			owner->GiveAmmo( ammo_type[ mode ],  tmpamount);
			ForceIdle();
			return;
		}
	} */
	
	// If we need to do a REAL reload (not a burst reload) we skip the test
	// altogether.
	/* firemode_t realmode = mode;
	if ( usesameclip )
		mode = FIRE_MODE1;
	if ( !(ammo_clip_size[mode] && !ammo_in_clip[mode] && AmmoAvailable(mode)) )
	{
		int tmp = 0;
		if ( switchmode )
			tmp = burstmode[curmode];
		else
			tmp = burstmode[realmode];
	
		if ( tmp )
		{
			int tmpamount = 0;
			Ammo *ammo;
			ammo = owner->FindAmmoByName( ammo_type[mode] );
			if ( ammo ) 
			{
				tmpamount = owner->AmmoCount( ammo_type[mode] );
	  
				// Try the clip ammo if we have none in our main inventory
				if ( tmpamount == 0 ) 
					tmpamount = ammo_in_clip[mode];
		
				if ( tmpamount > burstcountmax )
					burstcount = burstcountmax;
				else 
					burstcount = tmpamount;
		  
				// We're out of ammo
				if ( burstcount == 0 )
					burstcount = -1;
			
				ForceIdle();
				return;
			}
		}
	} */
	
	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;
	
	// Calc the amount the clip should get
	amount = ammo_clip_size[clipToUse] - ammo_in_clip[clipToUse];
	
	assert( owner );
	if ( owner && owner->isClient() && !UnlimitedAmmo( mode ) )
	{
		// use up the ammo from the player
		amount_used = owner->UseAmmo( ammo_type[mode], amount );
		
		// Stick it in the clip
		if ( ammo_clip_size[clipToUse] )
			ammo_in_clip[clipToUse] = amount_used + ammo_in_clip[clipToUse];
		
		assert( ammo_in_clip[clipToUse] <= ammo_clip_size[clipToUse] );
		if ( ammo_in_clip[clipToUse] > ammo_clip_size[clipToUse] )
			ammo_in_clip[clipToUse] = ammo_clip_size[clipToUse];
		
		if ( burstmode[curmode] && ( ammo_in_clip[clipToUse] >= burstcountmax ) )
			burstcount = burstcountmax;
	}
	owner->AmmoAmountInClipChanged( ammo_type[mode], ammo_in_clip[clipToUse] );
	
	ForceIdle();
}

void Weapon::DoneReloadingBurst( Event *ev )
{
	//int         amount;
	//int         amount_used;
	firemode_t  mode;
	int		inBurstMode;
	firemode_t clipToUse; 

	// Get the mode from the passed in event
	mode = (firemode_t)ev->GetInteger( 1 );

	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );

	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
		warning( "Weapon::DoneReloading", "Invalid mode %d\n", mode );

	// If we need to do a REAL reload (not a burst reload) we skip the test
	// altogether.
	firemode_t realmode = mode;
	//if ( usesameclip )
	//	mode = FIRE_MODE1;

	// Make sure we are actually in burst mode

	inBurstMode = false;

	if ( switchmode )
		inBurstMode = burstmode[curmode];
	else
		inBurstMode = burstmode[realmode];

	if ( !inBurstMode )
		return;

	// Add to the burst count

	int tmpamount = 0;
	Ammo *ammo;

	ammo = owner->FindAmmoByName( ammo_type[mode] );

	if ( ammo ) 
	{
		tmpamount = owner->AmmoCount( ammo_type[mode] );
		
		// Try the clip ammo if we have none in our main inventory

		if ( usesameclip )
			clipToUse = FIRE_MODE1;
		else
			clipToUse = mode;

		if ( tmpamount == 0 ) 
			tmpamount = ammo_in_clip[clipToUse];

		if ( tmpamount > burstcountmax )
			burstcount = burstcountmax;
		else 
			burstcount = tmpamount;
		
		// We're out of ammo

		if ( burstcount == 0 )
			burstcount = -1;

		ForceIdle();
		return;
	}
}

qboolean Weapon::CheckReload( void )
{
	return CheckReload( curmode );
}

//======================
//Weapon::CheckReload - Checks to see if we NEED to reload the weapon
//======================
qboolean Weapon::CheckReload( firemode_t mode )
{
	int inBurstMode;
	firemode_t clipToUse;

	// Check to see if the weapon needs to be reloaded
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );

	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
	{
		warning( "Weapon::CheckReload", "Invalid mode %d\n", mode );
		return false;
	}

	if ( !_autoReload )
	{
		return false;
	}

	if ( UnlimitedAmmo( mode ) )
	{
		return false;
	}

	if ( putaway )
	{
		return false;
	}

	if ( weaponstate != WEAPON_READY )
		return false;
	
	// If we're a burst mode weapon, see if we need to "reload"

	inBurstMode = false;

	if ( switchmode )
		inBurstMode = burstmode[curmode];
	else
		inBurstMode = burstmode[mode];

	firemode_t oldmode = mode;

	if ( inBurstMode && ( weaponstate != WEAPON_RELOADING ) && ( burstcount <= 0 ) )
	{
		Event *doneReloadingEvent;
		doneReloadingEvent = new Event( EV_Weapon_DoneReloadingBurst );
		doneReloadingEvent->AddInteger( oldmode );

		if ( _burstModeDelay[ mode ] > 0.0f )
		{
			animate->RandomAnimate( "reload_burst" );
			PostEvent( doneReloadingEvent, _burstModeDelay[ mode ] );
		}
		else
		{
			animate->RandomAnimate( "reload_burst", doneReloadingEvent );
		}

		weaponstate = WEAPON_RELOADING;
		return true;
	}

	mode = oldmode;

	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;

	if ( ammo_clip_size[clipToUse] && ( ammo_in_clip[clipToUse] < ammorequired[mode] ) && ( AmmoAvailable( mode ) >= ammorequired[mode] ) )
	{
		Event *ev1;

		ev1 = new Event( EV_Weapon_DoneReloading );
		ev1->AddInteger( mode );

		if ( animate->HasAnim( "reload_normal" ) && !multiplayerManager.skipWeaponReloads() )
		{
			weaponstate = WEAPON_RELOADING;
			animate->RandomAnimate( "reload_normal", ev1 );
		}
		else
		{
			ProcessEvent( ev1 );
		}
		return true;
	}

	// We're a regenerating weapon out of ammo
	/* if ( !AmmoAvailable(mode) && ( regenAmount > 0 ) )
	{
		Event *ev1;

		ev1 = new Event( EV_Weapon_DoneReloading );
		ev1->AddInteger( mode );

		if ( animate->HasAnim( "idle_special" ) )
		{
			weaponstate = WEAPON_RELOADING;
			animate->RandomAnimate( "idle_special", ev1 );
		}
		else
		{
			ProcessEvent( ev1 );
		}
		return true;
	} */

	return false;
}

//======================
//Weapon::ForceReload - Reload the clip if it's not full already
//======================
qboolean Weapon::ForceReload( void )
{
	// Only primary mode can reload
	firemode_t mode = FIRE_MODE1;
	firemode_t clipToUse;
	
	// Weapon has to be in the ready state

	if ( !canReload() )
		return false;
	
	// Make sure it's not put away
	if ( putaway )
		return false;
	
	// We can't reload regenerating weapons
	if ( _regenAmount[ FIRE_MODE1 ] > 0 )
		return false;
	
	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;
	
	// If our clip is not full and we have ammo available...
	// HACK: targetidle is currently only used on the TOW weapon,
	// so instead of making an entirely new event JUST for this, we use it here.
	if ( ( ( ammo_clip_size[clipToUse] != ammo_in_clip[clipToUse] ) && AmmoAvailable( mode ) ) || targetidle)
	{
		Event *ev1;
		
		ev1 = new Event( EV_Weapon_DoneReloading );
		ev1->AddInteger( mode );
		
		if ( animate->HasAnim( "reload_normal" ) )
		{
			weaponstate = WEAPON_RELOADING;
			animate->RandomAnimate( "reload_normal", ev1 );
		}
		else
		{
			ProcessEvent( ev1 );
		}
		return true;
	}
	
	return false;
}

//======================
//Weapon::Idle
//======================
void Weapon::Idle( Event *ev )
{
	ForceIdle();
}

//======================
//Weapon::GetMaxRange
//======================
float	Weapon::GetMaxRange( void )
{
	return maxrange;
}

//======================
//Weapon::GetMinRange
//======================
float	Weapon::GetMinRange( void )
{
	return minrange;
}

//======================
//Weapon::SetMaxRangeEvent
//======================
void Weapon::SetMaxRangeEvent( Event *ev )
{
	maxrange = ev->GetFloat( 1 );
}

//======================
//Weapon::SetMinRangeEvent
//======================
void Weapon::SetMinRangeEvent( Event *ev )
{
	minrange = ev->GetFloat( 1 );
}

//======================
//Weapon::NotDroppableEvent
//======================
void Weapon::NotDroppableEvent( Event *ev )
{
	notdroppable = true;
}

//======================
//Weapon::SetMaxRange
//======================
void Weapon::SetMaxRange( float val )
{
	maxrange = val;
}

//======================
//Weapon::SetMinRange
//======================
void Weapon::SetMinRange( float val )
{
	minrange = val;
}

//======================
//Weapon::WeaponSound
//======================
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(SOUND_RADIUS, SOUNDTYPE_WEAPONFIRE);;
		}
		else
		{
			BroadcastSound(SOUND_RADIUS, SOUNDTYPE_WEAPONFIRE);;
		}
		return;
	}
	
	if ( owner )
	{
		e = new Event( ev );
		owner->ProcessEvent( e );
	}
	else
	{
		Item::BroadcastSound();
	}
	
	// give us some breathing room
	nextweaponsoundtime = level.time + 0.4f;
}

//======================
//Weapon::Removable
//======================
qboolean Weapon::Removable( void )
{
	if ( multiplayerManager.checkFlag( MP_FLAG_WEAPONS_STAY ) && Respawnable() )
		return false;
	else
		return true;
}

//======================
//Weapon::Pickupable
//======================
qboolean Weapon::Pickupable( Entity *other )
{
	Sentient *sen;
	
	if ( !other->isSubclassOf( Sentient ) )
	{
		return false;
	}
	else if ( !other->isClient() )
	{
		return false;
	}

	if ( getSolidType() == SOLID_NOT )
	{
		return NULL;
	}

	if ( multiplayerManager.inMultiplayer() && other->isSubclassOf( Player ) )
	{
		if ( !multiplayerManager.canPickup( (Player *)other, getMultiplayerItemType(), getName().c_str() ) )
			return false;
	}
	
	sen = ( Sentient * )other;
	
	//FIXME
	// This should be in player
	
	// If we have the weapon and weapons stay, then don't pick it up
	if ( ( multiplayerManager.checkFlag( MP_FLAG_WEAPONS_STAY ) ) && Respawnable() )
	{
		Weapon   *weapon;
		
		weapon = ( Weapon * )sen->FindItem( getName() );
		
		if ( weapon )
			return false;
	}
	
	return true;
}

//======================
//Weapon::AutoChange
//======================
qboolean Weapon::AutoChange( void )
{
	return true;
}

//======================
//Weapon::ClipAmmo
//======================
int Weapon::ClipAmmo( firemode_t mode )
{
	firemode_t clipToUse;
	
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( !( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) ) )
		warning( "Weapon::ClipAmmo", "Invalid mode %d\n", mode );
	
	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;
	
	if (ammo_clip_size[clipToUse])
		return ammo_in_clip[clipToUse];
	else
		return -1;
}

//======================
//Weapon::ProcessWeaponCommandsEvent
//======================
void Weapon::ProcessWeaponCommandsEvent( Event *ev )
{
	int index;
	
	index = ev->GetInteger( 1 );
	ProcessInitCommands( index );
}

//======================
//Weapon::SetActionLevelIncrement
//======================
void Weapon::SetActionLevelIncrement( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	action_level_increment[firemodeindex] = ev->GetInteger( 1 );
}

//======================
//Weapon::ActionLevelIncrement
//======================
int Weapon::ActionLevelIncrement( firemode_t mode )
{
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
		return action_level_increment[mode];
	else
	{
		warning( "Weapon::ActionLevelIncrement", "Invalid mode %d\n", mode );
		return 0;
	}
}

//======================
//Weapon::IsDroppable
//======================
qboolean Weapon::IsDroppable( void )
{
	if ( notdroppable )
	{
		return false;
	}
	else
	{
		return true;
	}
}

//======================
//Weapon::SetFireType
//======================
void Weapon::SetFireType( Event *ev )
{
	str ftype;
	
	ftype = ev->GetString( 1 );
	
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	
	if ( !ftype.icmp( "projectile" ) )
		firetype[firemodeindex] = FT_PROJECTILE;
	else if ( !ftype.icmp( "bullet" ) )
		firetype[firemodeindex] = FT_BULLET;
	else if ( !ftype.icmp( "melee" ) )
		firetype[firemodeindex] = FT_MELEE;
	else if ( !ftype.icmp( "special_projectile" ) )
		firetype[firemodeindex] = FT_SPECIAL_PROJECTILE;
	else if ( !ftype.icmp( "none" ) )
		firetype[firemodeindex] = FT_NONE;
	else if ( !ftype.icmp( "explosion" ) )
		firetype[firemodeindex] = FT_EXPLOSION;
	else if ( !ftype.icmp( "triggerProjectile" ) )
		firetype[firemodeindex] = FT_TRIGGER_PROJECTILE;
	else if ( !ftype.icmp( "controlProjectile" ) )
		firetype[firemodeindex] = FT_CONTROL_PROJECTILE;
	else if ( !ftype.icmp( "controlZoom" ) )
		firetype[firemodeindex] = FT_CONTROL_ZOOM;
	else
		warning( "Weapon::SetFireType", "unknown firetype: %s\n", ftype.c_str() );
}

//======================
//Weapon::GetFireType
//======================
firetype_t Weapon::GetFireType( firemode_t mode )
{
	return firetype[mode];
}

//======================
//Weapon::SetProjectile
//======================
void Weapon::SetProjectile( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	projectileModel[firemodeindex] = ev->GetString( 1 );
	CacheResource( projectileModel[firemodeindex].c_str(), this );
}

//======================
//Weapon::SetBulletDamage
//======================
void Weapon::SetBulletDamage( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	bulletdamage[firemodeindex] = ev->GetFloat( 1 );
}

//======================
//Weapon::SetBulletKnockback
//======================
void Weapon::SetBulletKnockback( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	bulletknockback[firemodeindex] = ev->GetFloat( 1 );
}

//======================
//Weapon::SetBulletRange
//======================
void Weapon::SetBulletRange( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	bulletrange[firemodeindex] = ev->GetFloat( 1 );
}

//======================
//Weapon::SetRange
//======================
void Weapon::SetRange( Event *ev )
{
	SetBulletRange( ev );
}

//======================
//Weapon::SetBulletCount
//======================
void Weapon::SetBulletCount( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	bulletcount[firemodeindex] = ev->GetFloat( 1 );
}

void Weapon::SetBulletSpread( float spreadX , float spreadY )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	bulletspread[firemodeindex].x = spreadX;
	bulletspread[firemodeindex].y = spreadY;
}

//======================
//Weapon::SetBulletSpread
//======================
void Weapon::SetBulletSpread( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	bulletspread[firemodeindex].x = ev->GetFloat( 1 );
	if (ev->NumArgs() < 2) // this shouldn't happen, but there are assets that do it
		bulletspread[firemodeindex].y = bulletspread[firemodeindex].x;
	else
		bulletspread[firemodeindex].y = ev->GetFloat( 2 );
	if (ev->NumArgs() == 5) // expanding/shrinking spread
	   {
		endbulletspread[firemodeindex].x = ev->GetFloat(3);
		endbulletspread[firemodeindex].y = ev->GetFloat(4);
		endbulletspread[firemodeindex].z = ev->GetFloat(5); // time interval
	}
	else
	   {
		endbulletspread[firemodeindex].z = 0; // make sure spread is constant if it's not variable (duh)
	   }
}

//======================
//Weapon::SetHand
//======================
void Weapon::SetHand( Event *ev )
{
	str side;
	
	side = ev->GetString( 1 );
	
	if ( !stricmp( side.c_str(), "righthand" ) || !stricmp( side.c_str(), "right" ) )
		hand = WEAPON_RIGHT;
	else if ( !stricmp( side.c_str(), "lefthand" ) || !stricmp( side.c_str(), "left" ) )
		hand = WEAPON_LEFT;
	else if ( !stricmp( side.c_str(), "dualhand" ) || !stricmp( side.c_str(), "dual" ) )
		hand = WEAPON_DUAL;
	else if ( !stricmp( side.c_str(), "any" ) )
		hand = WEAPON_ANY;
	else
	{
		warning( "Weapon::SetHand", "Unknown side %s\n", side.c_str() );
		assert( 0 );
	}
}

//======================
//Weapon::ModeSet
//======================
void Weapon::ModeSet( Event *ev )
{
	int i;
	str modestr;
	
	Event *modeev = new Event( ev->GetToken( 2 ) );
	
	modestr = ev->GetToken( 1 );
	
	firemodeindex = WeaponModeNameToNum(modestr);
	
	for( i=3; i<=ev->NumArgs(); i++ )
	{
		modeev->AddToken( ev->GetToken( i ) );
	}
	
	ProcessEvent( modeev );
	firemodeindex = FIRE_MODE1;
}

//====================
//Weapon::AutoAim
//====================
void Weapon::AutoAim( Event *ev )
{
	if ( ev->NumArgs() > 0 )
	{
		autoAimTargetSelectionAngle = ev->GetInteger( 1 );
		autoAimLockonAngle = ev->GetInteger( 2 );
	}
	else
	{
		autoAimTargetSelectionAngle = 60;
		autoAimLockonAngle = 15;
	}
}

//====================
//Weapon::Crosshair
//====================
void Weapon::Crosshair( Event *ev )
{
	crosshair = ev->GetBoolean( 1 );
}

//====================
//Weapon::TorsoAim
//====================
void Weapon::TorsoAim( Event *ev )
{
	torsoaim = ev->GetBoolean( 1 );
}

//====================
//Weapon::LeftAttachToTag
//====================
void Weapon::LeftAttachToTag( Event *ev )
{
	left_attachToTag = ev->GetString( 1 );
}

//====================
//Weapon::RightAttachToTag
//====================
void Weapon::RightAttachToTag( Event *ev )
{
	right_attachToTag = ev->GetString( 1 );
}

//====================
//Weapon::DualAttachToTag
//====================
void Weapon::DualAttachToTag( Event *ev )
{
	dual_attachToTag = ev->GetString( 1 );
}

//====================
//Weapon::LeftHolsterAttachToTag
//====================
void Weapon::LeftHolsterAttachToTag( Event *ev )
{
	leftholster_attachToTag = ev->GetString( 1 );
}

//====================
//Weapon::RightHolsterAttachToTag
//====================
void Weapon::RightHolsterAttachToTag( Event *ev )
{
	rightholster_attachToTag = ev->GetString( 1 );
}

//====================
//Weapon::DualHolsterAttachToTag
//====================
void Weapon::DualHolsterAttachToTag( Event *ev )
{
	dualholster_attachToTag = ev->GetString( 1 );
}

//====================
//Weapon::SetLeftHolsterAngles
//====================
void Weapon::SetLeftHolsterAngles( Event *ev )
{
	leftHolsterAngles = ev->GetVector( 1 );
}

//====================
//Weapon::SetRightHolsterAngles
//====================
void Weapon::SetRightHolsterAngles( Event *ev )
{
	rightHolsterAngles = ev->GetVector( 1 );
}

//====================
//Weapon::SetDualHolsterAngles
//====================
void Weapon::SetDualHolsterAngles( Event *ev )
{
	dualHolsterAngles = ev->GetVector( 1 );
}

//====================
//Weapon::SetHolsterScale
//====================
void Weapon::SetHolsterScale( Event *ev )
{
	holsterScale = ev->GetFloat( 1 );
}

void Weapon::setWeildedScale( Event *ev )
{
	_weildedScale = ev->GetFloat( 1 );
}

//====================
//Weapon::SetQuiet
//====================
void Weapon::SetQuiet( Event *ev )
{
	quiet = true;
}

//====================
//Weapon::SetLoopFire
//====================
void Weapon::SetLoopFire( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	loopfire[firemodeindex] = true;
}
/*
void Weapon::SetFullAnimFire( Event *ev )
	{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
   fullanimfire[firemodeindex] = true;
	}
*/

//======================
//Weapon::SetMeansOfDeath
//======================
void Weapon::SetMeansOfDeath( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	meansofdeath[firemodeindex] = (meansOfDeath_t )MOD_NameToNum( ev->GetString( 1 ) );
}

//======================
//Weapon::GetMeansOfDeath
//======================
meansOfDeath_t Weapon::GetMeansOfDeath( firemode_t mode )
{
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) )
		return meansofdeath[mode];
	else
	{
		warning( "Weapon::GetMeansOfDeath", "Invalid mode %d\n", mode );
		return MOD_NONE;
	}
}

//======================
//Weapon::SetAimTarget
//======================
void Weapon::SetAimTarget( Entity *ent )
{
	aim_target = ent;
}

//======================
//Weapon::WorldHitSpawn
//======================
void Weapon::WorldHitSpawn( firemode_t mode, const Vector &origin, const Vector &angles, float life )
{
	if ( !worldhitspawn[mode].length() )
		return;
	
	Entity::SpawnEffect(worldhitspawn[mode], origin, angles, life);
}

//======================
//Weapon::SetWorldHitSpawn
//======================
void Weapon::SetWorldHitSpawn( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	worldhitspawn[firemodeindex] = ev->GetString( 1 );
}

void Weapon::MakeNoise( Event *ev )
{
	float radius = 500.0f;
	qboolean force = false;
	
	if ( ev->NumArgs() > 0 )
		radius = ev->GetFloat( 1 );
	
	if ( ev->NumArgs() > 1 )
		force = ev->GetBoolean( 2 );
	
	if ( attached && ( next_noise_time <= level.time || force ) )
	{
		BroadcastSound( radius , SOUNDTYPE_WEAPONFIRE );
		next_noise_time = level.time + 1.0f;
	}
}

void Weapon::SetViewModel( Event *ev )
{
	if ( ev->NumArgs() > 0 )
	{
		viewmodel = ev->GetString( 1 );
		
		gi.setviewmodel( edict, viewmodel );
		//setViewModel( viewmodel );
	}
}

void Weapon::DonePutaway( Event *ev )
{
	//int i;

	// Tell the state machine we're lowering the weapon
	// so it will continue to do it's normal deactivate code
	weaponstate = WEAPON_LOWERING;

	if ( zoomed )
	{
		CancelEventsOfType( EV_Weapon_Zoom );	
		ProcessEvent( EV_Weapon_Zoom );	
	}

	if ( _controllingProjectile )
	{
		toggleProjectileControl();
	}

	// No longer targeting anyone

	//SetTargetedEntity( NULL );

	if ( targetidle )
		CancelEventsOfType( EV_Weapon_TargetIdleThink );

	// Move ammo from weapon back to owner if possible

	/* if ( owner )
	{
		for ( i = 0 ; i < MAX_FIREMODES ; i++ )
		{
			if ( ammo_in_clip[ i ] )
			{
				int maxAmount;
				int amount;

				maxAmount = owner->MaxAmmoCount( ammo_type[ i ] ) - owner->AmmoCount( ammo_type[ i ] );

				if ( maxAmount > ammo_in_clip[ i ] )
					amount = ammo_in_clip[ i ];
				else
					amount = maxAmount;

				owner->GiveAmmo( ammo_type[ i ], amount, false );
				ammo_in_clip[ i ] -= amount;
				owner->AmmoAmountInClipChanged( ammo_type[ i ], ammo_in_clip[ i ] );
			}
		}
	} */
}

void Weapon::SetRegenAmmo( Event *ev )
{
	if ( ev->NumArgs() > 0 )
	{
		_regenAmount[ firemodeindex ] = ev->GetInteger( 1 );	

		_regenTime[ firemodeindex ] = 1.0; // default time
	}

	if ( ev->NumArgs() > 1 )
	{
		_regenTime[ firemodeindex ] = ev->GetFloat( 2 );
	}

	turnThinkOn();
}

//----------------------------------------------------------------
// Name:			SetRegenOnlyWhenIdle
// Class:			Weapon
//
// Description:		Specifies that this weapon only regenerates ammo when it is idle (not shooting)
//
// Parameters:		Event *ev
//
// Returns:			none
//----------------------------------------------------------------

void Weapon::SetRegenOnlyWhenIdle( Event *ev )
{
	_regenOnlyWhenIdle[ firemodeindex ] = true;
}

void Weapon::ChangeIdle( Event *ev )
{
	ForceIdle();
	
	if ( !owner )
	{
		return;
	}
}

void Weapon::DrawBowStrain( Event *ev )
{
	if ( animate->HasAnim( "draw_strain" ) )
		animate->RandomAnimate( "draw_strain" );
}

void Weapon::AltDrawBowStrain( Event *ev )
{
	if ( animate->HasAnim( "alternate_draw_strain" ) )
		animate->RandomAnimate( "alternate_draw_strain" );
}

void Weapon::SetAccuracy( Event *ev )
{
	if ( ev->NumArgs() < 6 )
	{
		gi.DPrintf("SetAccuracy: Too few parameters\n");
		return;
	}
	firemode_t mode = WeaponModeNameToNum( ev->GetToken( 1 ) );
	accuracy[mode][ACCURACY_STOPPED] = ev->GetFloat( 2 );
	accuracy[mode][ACCURACY_CHANGE] = ev->GetFloat( 3 );
	accuracy[mode][ACCURACY_WALK] = ev->GetFloat( 4 );
	accuracy[mode][ACCURACY_RUN] = ev->GetFloat( 5 );
	accuracy[mode][ACCURACY_CROUCH] = ev->GetFloat( 6 );
}

void Weapon::SetZoomFOV( Event *ev )
{
	if ( ev->NumArgs() > 0 )
	{
		zoomfov = ev->GetFloat( 1 );
	}
}

void Weapon::SetReticuleTime( Event *ev )
{
	if ( ev->NumArgs() > 0 )
		reticuletime = ev->GetFloat( 1 );
}

void Weapon::SetStartZoom( Event* ev )
{
	if(ev == 0)
		return;
	
	startzoom = ev->GetFloat(1);
}

void Weapon::SetEndZoom( Event* ev )
{
	if(ev == 0)
		return;
	
	endzoom = ev->GetFloat(1);
}

void Weapon::SetZoomTime( Event* ev )
{
	if(ev == 0)
		return;
	
	zoomtime = ev->GetFloat(1);
}

void Weapon::SetAimType( Event *ev )
{
	
	str aimstr;
	
	Sentient *owner;
	Player *player = NULL;
	owner = this->owner;
	assert( owner );
	if ( owner->isSubclassOf( Player ) )
		player = ( Player * )owner;
	
	if ( ev->NumArgs() > 0 )
		aimstr = ev->GetString( 1 );
	
	// Crouching is a special case... you'll always have the "crouch" accuracy
	// rating in your crouched, regardless of whether you're walking or not.
	if ( player && player->GetCrouch() || ( aimstr == "crouch" ) )
		aimtype = ACCURACY_CROUCH;
	else 
	{
		if ( aimstr == "stopped" )
			aimtype = ACCURACY_STOPPED;	
		if ( aimstr == "walk" )
			aimtype = ACCURACY_WALK;
		if ( aimstr == "run" )
			aimtype = ACCURACY_RUN;
		if ( aimstr == "change" )
			aimtype = ACCURACY_CHANGE;
	}
	
	if ( ( aimstr != "stopped" ) && ( aimstr != "walk" ) && ( aimstr != "run" ) && ( aimstr != "crouch" ) && ( aimstr != "change" ) )
	{
		warning("SetAimType: Invalid aimtype, %s, defaulting to stopped",aimstr);
		aimtype = ACCURACY_STOPPED;
	}
	
	if ( g_showaccuracymod->integer )
		gi.DPrintf("Accuracy Mod: %s = %f\n", aimstr.c_str(), accuracy[curmode][aimtype] );
}

void Weapon::SetFireTimer( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	
	fire_timer[firemodeindex] = ev->GetFloat( 1 );
}

void Weapon::UseSameClip( Event *ev )
{
	usesameclip = true;
}

qboolean Weapon::HasFullClip( void )
{
	firemode_t mode = FIRE_MODE1;
	firemode_t clipToUse;
	
	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;
	
	if ( ammo_in_clip[clipToUse] == ammo_clip_size[clipToUse] )
		return true;
	
	return false;
}

void Weapon::SetMaxModes( Event *ev )
{
	if ( ev->NumArgs() < 1 )
	{
		maxmode = FIRE_MODE2;
		return;
	}
	
	maxmode = WeaponModeNameToNum( ev->GetToken( 1 ) );
}

void Weapon::SetSwitchMode( Event *ev )
{
	switchmode = true;
}

// Target Idle Weapon functions
void Weapon::TargetIdle( Event *ev )
{
	targetidle = true;
}

void Weapon::TargetIdleThink( Event *ev )
{
	CancelEventsOfType( EV_Weapon_TargetIdleThink );
	
	// HACK sortof: If the weapon is playing it's put away animation,
	// we don't do this event.  For some reason a CancelEventsOfType
	// call in the Putaway function failed to cancel this event.
	if  ( animate->GetName() == "putaway" )
		return;
	
	PostEvent( EV_Weapon_TargetIdleThink, TargetIdleTime );
	
	Sentient *owner;
	Player *player;
	owner = this->owner;
	assert( owner );
	
	if ( owner->isSubclassOf( Player ) )
	{
		player = ( Player * )owner;
		Vector pos, forward, right, up, endpoint, vorg;   
		GetMuzzlePosition( &pos, &forward, &right, &up );
		vorg = player->origin;
		vorg.z += player->viewheight;
		
		endpoint = vorg + (forward * 4000.0f);
		trace_t trace = G_Trace( pos, vec_zero, vec_zero, endpoint, player, MASK_SHOT, true, "Weapon::TargetIdleThink" );
		if ( trace.ent && ( trace.entityNum != ENTITYNUM_WORLD ) && trace.ent->entity->isSubclassOf( Sentient ))
		{
			if ( !trace.ent->entity->deadflag )
			{
				if ( !strstr(animate->GetName(), "idle_target") )
					animate->RandomAnimate( "idle_target" );
				targetidleflag = true;
			}
			return;
		}
		else 
		{
			if ( targetidleflag )
			{
				targetidleflag = false;
				ForceIdle();
			}
		}
			
	}
}
// ****************************

// Weapon Mode switching functions
void Weapon::SwitchMode( void )
{
	char anim[128];
	
	Event *ev1;
	ev1 = new Event( EV_Weapon_DoneSwitchToMiddle );
	
	strcpy(anim,"");
	sprintf(anim,"switch_mode%d_neutral",(int)curmode+1);
	
	if ( animate->HasAnim( anim ) )
	{
		weaponstate = WEAPON_SWITCHINGMODE;
		animate->RandomAnimate( anim, ev1 );
	}
	else
	{
		ProcessEvent( ev1 );
	}
}

void Weapon::DoneSwitchToMiddle( Event *ev )
{
	int mode;
	char anim[128];
	
	Event *ev1;
	ev1 = new Event( EV_Weapon_DoneSwitching );
	
	if ( curmode == maxmode )
		mode = FIRE_MODE1;
	else
		mode = (int)curmode + 1;
	
	strcpy(anim,"");
	sprintf(anim,"switch_mode%d",mode);
	
	if ( animate->HasAnim( anim ) )
	{
		weaponstate = WEAPON_SWITCHINGMODE;
		animate->RandomAnimate( anim, ev1 );
	}
	else
	{
		ProcessEvent( ev1 );
	}
}

void Weapon::DoneSwitching( Event *ev )
{
	if ( curmode == maxmode )
		curmode = FIRE_MODE1;
	else
		curmode = (firemode_t)((int)curmode + 1);
	
	weaponstate = WEAPON_READY;
	
	// Clear our fire timer
	INITIALIZE_WEAPONMODE_VAR(next_fire_time, 0.0);
	
	ForceIdle();
}
// *************

void Weapon::SetBurstMode( Event *ev )
{
	burstmode[firemodeindex] = true;
	if ( ev->NumArgs() > 0 )
		burstcountmax = ev->GetInteger( 1 );
	else
		burstcountmax = 10; // Default to 10
	
	burstcount = burstcountmax;
}

void Weapon::setBurstModeDelay( Event *ev )
{
	_burstModeDelay[ firemodeindex ] = ev->GetFloat( 1 );
}

void Weapon::SetCHOffset(int chx, int chy)
{
	this->chx = chx;
	this->chy = chy;
}



// GetTargetedEntity
//
// Returns the entity that is targeted by the weapon.
// If there is not an entity, 0 is returned.
void Weapon::CheckForTargetedEntity( void )
{

}

void Weapon::SetRealViewOrigin( const Vector &rv )
{
	realvieworg = rv;
}

void Weapon::SetThirdPerson( qboolean tp )
{
	thirdperson = tp;
}

qboolean Weapon::IsDoneFiring()
{
	return donefiring;
}

//----------------------------------------------------------------
// Name:			StartFiring
// Class:			Weapon
//
// Description:		Sets the start time used for bullet spread (for repeating weapons, this should be set in a prefire state)
//
// Parameters:		Event *ev
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::StartFiring( Event *ev )
{
	startfiretime = level.time;
}

void Weapon::FinishedFiring( Event *ev )
{
	int oldDoneFiring;

	oldDoneFiring = donefiring;

	donefiring = ev->GetInteger( 1 );

	if ( shootingSkin )
		ChangeSkin( shootingSkin, false );

	if ( donefiring && !oldDoneFiring )
	{
		CheckReload();
	}
}

void Weapon::Zoom( Event *ev )
{
	Player *player = NULL;
	
	if ( owner && owner->isSubclassOf( Player ) )
		player = (Player *)(Sentient *)owner;
	
	if ( !player )
		return;
	
	if ( zoomed )
	{
		endZoom();
	}
	else
	{
		zoomfov = startzoom;
		startzoomtime = level.time;
		zoomed = true;
		
		player->SetFov( zoomfov, true );
		player->client->ps.pm_flags |= PMF_ZOOM;

	}
}

void Weapon::endZoom( Event *ev )
{
	if ( zoomed )
		_lastZoomFov = zoomfov;
	else
		_lastZoomFov = sv_defaultFov->value;

	endZoom();
}

void Weapon::rezoom( Event *ev )
{
	Player *player = NULL;
	
	if ( owner && owner->isSubclassOf( Player ) )
		player = (Player *)(Sentient *)owner;
	
	if ( !player )
		return;

	if ( _lastZoomFov < sv_defaultFov->value )
	{
		zoomed = true;
		zoomfov = _lastZoomFov;
			
		player->SetFov( zoomfov, true );
		player->client->ps.pm_flags |= PMF_ZOOM;
	}
}

void Weapon::endZoom( void )
{
	Player *player = NULL;
	float defaultFov;
	
	if ( owner && owner->isSubclassOf( Player ) )
		player = (Player *)(Sentient *)owner;
	
	if ( !player )
		return;

	defaultFov = player->getDefaultFov();

	player->SetFov( defaultFov, true );
		
	//remove the player client zoom flag
	player->client->ps.pm_flags &= ~PMF_ZOOM;

	//gi.SendServerCommand(player->entnum, "stufftext \"ui_removehud zoomhud\"\n");
	player->removeHud( "zoomhud" );

	_zoomStage = ZOOM_NORMAL_FOV;
	zoomed = false;
}

void Weapon::IncrementZoom( Event* ev )
{
	
	if(!zoomed)
		return;
	
	Player *player = NULL;
	
	if ( owner && owner->isSubclassOf( Player ) )
		player = (Player *)(Sentient *)owner;
	
	float zoomPercentage = ((float)level.time - startzoomtime) / zoomtime;
	zoomfov = ((endzoom - startzoom) * zoomPercentage) + startzoom;
	if(zoomfov < endzoom)
		zoomfov = endzoom;
	
	player->SetFov( zoomfov, true );
}


//-----------------------------------------------------
//
// Name:		zoomFov
// Class:		Weapon
//
// Description:	Changes the zoom fov from Normal FOV to FOV 1 to FOV 2 then back to normal fov
//
// Parameters:	
//
// Returns:		
//-----------------------------------------------------
void Weapon::setZoomStage( Event* ev )
{
	changeZoomStage( ev->GetFloat( 1 ), ev->GetFloat( 2 ) );
}

void Weapon::changeZoomStage( float firstZoom, float secondZoom )
{
	Player* player = NULL;

	if ( owner && owner->isSubclassOf( Player ) )
		player = (Player *)(Sentient *)owner;

	if(player == NULL)
		return;

	// Move to next zoom stage

	switch( _zoomStage )
	{
		case ZOOM_NORMAL_FOV:
			_zoomStage = ZOOM_STAGE_1;
			player->SetFov( firstZoom, true );
			player->addHud( "zoomhud" );
			break;

		case ZOOM_STAGE_1:
			_zoomStage = ZOOM_STAGE_2;
			player->SetFov( secondZoom, true );
			player->addHud( "zoomhud" );
			break;

		case ZOOM_STAGE_2:
			_zoomStage = ZOOM_NORMAL_FOV;
			endZoom();
			return;
	}

	player->client->ps.pm_flags |= PMF_ZOOM;
}


void Weapon::SetTargetingSkin( Event *ev )
{
	targetingSkin = ev->GetInteger( 1 );
}

void Weapon::SetShootingSkin( Event *ev )
{
	shootingSkin = ev->GetInteger( 1 );
}

void Weapon::setFullAmmoSkin( Event *ev )
{
	_fullAmmoSkin = ev->GetInteger( 1 );

	_fullAmmoMode = WeaponModeNameToNum( ev->GetString( 2 ) );

	turnThinkOn();
}


void Weapon::ProcessTargetedEntity( EntityPtr entity )
{
	
	Player* player;
	assert(owner);

	if ( !owner )
		return;

	player =  (Player*) (Sentient*)owner;
	if(player == 0)
		return;
	
	if ( targetingSkin )
	{
		ChangeSkin( targetingSkin, false );
		
		if ( entity && entity->isSubclassOf( Actor ) )
		{
			
			Actor *actor = (Actor *)entity.Pointer();
			
			if ( !actor->deadflag && ( actor->actortype == IS_ENEMY ) )
				ChangeSkin( targetingSkin, true );
				
		}
	}

	if(entity != 0)
	{
		entity->edict->s.eFlags |= EF_DISPLAY_DESC1;
	}

}

//----------------------------------------------------------------
// Name:			setMoveSpeedModifier
// Class:			Weapon
//
// Description:		Sets the move speed modifier (when not firing)
//
// Parameters:		Event *ev			- float, specifies modifier
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::Uninitialize(void)
{
	if( _zoomStage == ZOOM_STAGE_1 || _zoomStage == ZOOM_STAGE_2 )
	{
		endZoom();
	}
}



//----------------------------------------------------------------
// Name:			setMoveSpeedModifier
// Class:			Weapon
//
// Description:		Sets the move speed modifier (when not firing)
//
// Parameters:		Event *ev			- float, specifies modifier
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::setMoveSpeedModifier( Event *ev )
{
	defaultMoveSpeedModifier = ev->GetFloat( 1 );
}

//----------------------------------------------------------------
// Name:			setShootingMoveSpeedModifier
// Class:			Weapon
//
// Description:		Sets the move speed modified for a particular mode
//
// Parameters:		Event *ev			- float, specifies modifier
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::setShootingMoveSpeedModifier( Event *ev )
{
	shootingMoveSpeedModifier[ firemodeindex ] = ev->GetFloat( 1 );
}

//----------------------------------------------------------------
// Name:			getMoveSpeedModifier
// Class:			Weapon
//
// Description:		Gets the current move speed modifier
//
// Parameters:		none
//
// Returns:			float				- the move speed modifier
//----------------------------------------------------------------
float Weapon::getMoveSpeedModifier( void )
{
	if ( weaponstate == WEAPON_FIRING )
		return shootingMoveSpeedModifier[ curmode ];
	else
		return defaultMoveSpeedModifier;
}

//----------------------------------------------------------------
// Name:			UseActorAiming
// Class:			Weapon
//
// Description:		Tells the weapon to use the tag_barrel for aiming, no matter who is holding it.
//
// Parameters:		Event *ev
//
// Returns:			None
//----------------------------------------------------------------
void Weapon::UseActorAiming( Event *ev )
{
	if ( ev->NumArgs() > 0 )
		useActorAiming = ev->GetBoolean( 1 );
	else
		useActorAiming = true;
}

//----------------------------------------------------------------
// Name:			updateViewShake
// Class:			Weapon
//
// Description:		Updates the view shake angles
//
// Parameters:		none
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::updateViewShake( void )
{
	if ( _viewShakeMagnitude[ curmode ] > 0.0f )
	{
		// Apply simple view shake

		viewShake[ PITCH ] = G_CRandom() * _viewShakeMagnitude[ curmode ];
		viewShake[ YAW ]   = G_CRandom() * _viewShakeMagnitude[ curmode ];
		viewShake[ ROLL ]  = G_CRandom() * _viewShakeMagnitude[ curmode ] * 1.5;
	}
	else if ( ( _viewMinShake[ curmode ] != vec_zero ) || ( _viewMaxShake[ curmode ] != vec_zero ) )
	{
		Vector minShake;
		Vector maxShake;

		// Apply advanced view shake

		minShake = _viewMinShake[ curmode ];
		maxShake = _viewMaxShake[ curmode ];

		if ( _viewShakeOverride[ curmode ] )
		{
			viewShake[ PITCH ] = G_Random() * ( maxShake[ PITCH ] - minShake[ PITCH ] ) + minShake[ PITCH ];
			viewShake[ YAW   ] = G_Random() * ( maxShake[ YAW   ] - minShake[ YAW   ] ) + minShake[ YAW   ];
			viewShake[ ROLL  ] = G_Random() * ( maxShake[ ROLL  ] - minShake[ ROLL  ] ) + minShake[ ROLL  ];
		}
		else
		{
			viewShake[ PITCH ] += G_Random() * ( maxShake[ PITCH ] - minShake[ PITCH ] ) + minShake[ PITCH ];
			viewShake[ YAW   ] += G_Random() * ( maxShake[ YAW   ] - minShake[ YAW   ] ) + minShake[ YAW   ];
			viewShake[ ROLL  ] += G_Random() * ( maxShake[ ROLL  ] - minShake[ ROLL  ] ) + minShake[ ROLL  ];
		}

		for ( int i = 0 ; i < 3 ; i++ )
		{
			if ( viewShake[ i ] > _maxViewShakeChange )
				viewShake[ i ] = _maxViewShakeChange;
			else if ( viewShake[ i ] < -_maxViewShakeChange )
				viewShake[ i ] = -_maxViewShakeChange;

		}
	}
}

void Weapon::startViewShake( Event *ev )
{
	startViewShake();
}
void Weapon::startViewShake( void )
{
	if ( ( _viewShakeMagnitude[ curmode ] > 0.0f ) || ( _viewMinShake[ curmode ] != vec_zero ) || ( _viewMaxShake[ curmode ] != vec_zero ) )
	{
		//CancelEventsOfType( EV_Weapon_ClearViewShake );

		updateViewShake();

		//if ( _viewShakeDuration[ curmode ] > FRAMETIME )
		{
			CancelEventsOfType( EV_Weapon_ReduceViewShake );
			PostEvent( EV_Weapon_ReduceViewShake, FRAMETIME );
			//PostEvent( EV_Weapon_ReduceViewShake, 0.25 );
		}

		//PostEvent( EV_Weapon_ClearViewShake, _viewShakeDuration[ curmode ] );
	}
}

void Weapon::reduceViewShake( Event *ev )
{
	int i;
	bool repost;

	// Reduce the viewShake

	viewShake *= 0.75;

	// Determine if we should do reduce the viewShake next frame

	repost = false;

	for ( i = 0 ; i < 3 ; i++ )
	{
		if ( ( viewShake[ i ] > 0.1f ) || ( viewShake[ i ] < -0.1f ) )
		{
			repost = true;
		}
	}

	if ( repost )
	{
		PostEvent( EV_Weapon_ReduceViewShake, FRAMETIME );
	}
	else
	{
		viewShake = vec_zero;
	}
}

//----------------------------------------------------------------
// Name:			clearViewShake
// Class:			Weapon
//
// Description:		Clears the view shake angles
//
// Parameters:		none
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::clearViewShake( Event *ev )
{
	viewShake = vec_zero;
	CancelEventsOfType( EV_Weapon_ReduceViewShake );
}

//----------------------------------------------------------------
// Name:			getViewShake
// Class:			Weapon
//
// Description:		Gets the angles to shake the view
//
// Parameters:		none
//
// Returns:			Vector					- angles to shake the view by
//----------------------------------------------------------------
Vector Weapon::getViewShake( void )
{
	return viewShake;
}

//----------------------------------------------------------------
// Name:			setViewShakeInfo
// Class:			Weapon
//
// Description:		Sets the magnitude and duration that the view should shake when firing the weapon
//
// Parameters:		Event *ev			- float, specifies the magnitude
//										  float, specifies the duration (optional)
//
// Returns:			none
//----------------------------------------------------------------
void Weapon::setViewShakeInfo( Event *ev )
{
	_viewShakeMagnitude[ firemodeindex ] = ev->GetFloat( 1 );

	if ( ev->NumArgs() > 1 )
	{
		_viewShakeDuration[ firemodeindex ] = ev->GetFloat( 2 );
	}
	else
	{
		_viewShakeDuration[ firemodeindex ] = FRAMETIME;
	}
}

void Weapon::setAdvancedViewShakeInfo( Event *ev )
{
	_viewMinShake[ firemodeindex ] = ev->GetVector( 1 );
	_viewMaxShake[ firemodeindex ] = ev->GetVector( 2 );

	if ( ev->NumArgs() > 2 )
	{
		_viewShakeDuration[ firemodeindex ] = ev->GetFloat( 3 );
	}
	else
	{
		_viewShakeDuration[ firemodeindex ] = FRAMETIME;
	}

	if ( ev->NumArgs() > 3 )
	{
		_viewShakeOverride[ firemodeindex ] = ev->GetBoolean( 4 );
	}
	else
	{
		_viewShakeOverride[ firemodeindex ] = false;
	}
}


//--------------------------------------------------------------
// Name:		SetPowerRating()
// Class:		Weapon
//
// Description:	Calls SetPowerRating()
//
// Parameters:	Event *ev -- Event with the rating
//
// Returns:		None
//--------------------------------------------------------------
void Weapon::SetPowerRating( Event *ev )
{
	SetPowerRating( ev->GetFloat( 1 ) );
}

//--------------------------------------------------------------
// Name:		SetProjectileDamage()
// Class:		Weapon
//
// Description:	Calls SetProjectileDamage
//
// Parameters:	Event *ev -- Event with the damage
//
// Returns:		None
//--------------------------------------------------------------
void Weapon::SetProjectileDamage( Event *ev )
{
	SetProjectileDamage(ev->GetFloat( 1 ) );
}

//--------------------------------------------------------------
// Name:		SetProjectileSpeed()
// Class:		Weapon
//
// Description:	Calls SetProjectileSpeed
//
// Parameters:	Event *ev -- Event with the speed of the projectile
//
// Returns:		None
//--------------------------------------------------------------
void Weapon::SetProjectileSpeed( Event *ev )
{
	SetProjectileSpeed(ev->GetFloat( 1 ) );
}

//--------------------------------------------------------------
// Name:		AdvancedMeleeAttack
// Class:		Weapon
//
// Description:	Does some literal melee damage based on the weapon itself.
//				Weapon angles (except Z) are taken into account, and we
//				utilize the meleestart and meleeend events to apply damage
//				properly to enemies in a single swipe.  The melee trace goes
//				between the two WEAPON tags passed in.
//
// Parameters:	const char* tag1 -- The first tag to do the melee trace from
//				const char* tag2 -- The second tag to do the melee trace from
//
// Returns:		None
//--------------------------------------------------------------
void Weapon::AdvancedMeleeAttack(const char* tag1, const char* tag2, bool criticalHit)
{
	Vector startpoint, endpoint;
	Vector tagpos, tagpos2, vect;
	float damage, knockback;
	meansOfDeath_t meansofdeath;
	
	// New Melee Combat code
	GetActorMuzzlePosition(&tagpos, NULL, NULL, NULL, tag1); // Get the position of the first tag
	GetActorMuzzlePosition(&tagpos2, NULL, NULL, NULL, tag2); // Get the position of the second tag
	startpoint = tagpos2;
	vect = tagpos - tagpos2;
	vect.z = 0;
	vect.normalize();
	endpoint = startpoint + (vect*48);
	
	// Debug line
	//G_DebugLine(startpoint, endpoint, 0.5, 1.0, 0.5, 1.0);
	
	damage = bulletdamage[FIRE_MODE1];
	knockback = bulletknockback[FIRE_MODE1];
	
	meansofdeath = GetMeansOfDeath( FIRE_MODE1 );
	
	if ( owner->isSubclassOf( Player ) )
	{
		Player *player = (Player *)(Sentient *)owner;
		
		meansofdeath = player->changetMeansOfDeath( meansofdeath );
		
		damage = player->getDamageDone( damage, meansofdeath, true );
		
		knockback = (knockback + player->GetPlayerKnockback()) * player->GetKnockbackMultiplier();
	}
	
	Sentient *owner;
	Player *player;
	owner = this->owner;
	assert( owner );
	if ( owner->isSubclassOf( Player ) )
	{
		player = ( Player * )owner;
		if ( !player->in_melee_attack )
			meleeVictims.ClearObjectList();
	}
	
	if ( !MeleeAttack( startpoint, endpoint, damage, owner, meansofdeath, 15.0f, -45.0f, 45.0f, knockback, true, &meleeVictims, this, criticalHit ) )
	{
		// Try to hit the world since we didn't do any damage to anything
		trace_t trace = G_Trace( startpoint, Vector( -8.0f, -8.0f, -8.0f ), Vector( 8.0f, 8.0f, 8.0f ), endpoint, owner, MASK_MELEE, false, "Weapon::Shoot" );
		
		Entity *victim = G_GetEntity( trace.entityNum );
		
		if ( victim && ( ( victim == world ) || ( victim->takedamage == DAMAGE_NO ) ) )
		{
			vec3_t  newangles;
			vectoangles( trace.plane.normal, newangles );
			WorldHitSpawn( FIRE_MODE1, trace.endpos, newangles, 0.1f );
			str realname = this->GetRandomAlias( "impact_world" );
			if ( realname.length() > 1 )
				this->Sound( realname, CHAN_VOICE );
		}
	}
	
	if ( !quiet )
	{
		if ( next_noise_time <= level.time )
		{
			BroadcastSound();
			next_noise_time = level.time + 1.0f;
		}
	}
}

//----------------------------------------------------------------
// Name:			applySpread
// Class:			Weapon
//
// Description:		Applies the weapons spread to the angles
//
// Parameters:		const Vector &pos				- position of the muzzle
//					Vector *forward					- forward vector of the muzzle
//					Vector *right					- right vector of the muzzle
//					Vector *up						- up vector of the muzzle
//
// Returns:			none
//----------------------------------------------------------------

void Weapon::applySpread( Vector *forward, Vector *right, Vector *up )
{
	Vector spread;


	if (( !forward ) || (!up) || (!right))
		return;

	spread = getSpread();
	
	// figure the new projected impact point based upon computed spread
	*forward = ( *forward * bulletrange[curmode]) + 
		( *right * G_CRandom( spread.x ) ) +
		( *up * G_CRandom( spread.y ) );
	
	// after figuring spread location, re-normalize vectors
	forward->normalize();
	*right = Vector::Cross(*forward,*up);
	*up = Vector::Cross(*right,*forward);	
}

Vector Weapon::getSpread( void )
{
	Vector spread;
	
	// compute the spread if it's time-variant

	if ( endbulletspread[ curmode ].z )
	{
		float timemult = (level.time - startfiretime) / endbulletspread[curmode].z;

		if (timemult > 1) // cap it so we don't get silly spread
			timemult = 1;

		spread.x = timemult * (endbulletspread[curmode].x - bulletspread[curmode].x) + bulletspread[curmode].x;
		spread.y = timemult * (endbulletspread[curmode].y - bulletspread[curmode].y) + bulletspread[curmode].y;
	}
	else
	{
		spread.x = bulletspread[curmode].x;
		spread.y = bulletspread[curmode].y;
	}

	return spread;
}


//--------------------------------------------------------------
//
// Name:			processGameplayData
// Class:			Weapon
//
// Description:		Called usually from the tiki file after all other
//					server side events are called.
//
// Parameters:		Event *ev -- not used
//
// Returns:			None
//
//--------------------------------------------------------------
void Weapon::processGameplayData( Event *ev )
{
	GameplayManager *gpm = GameplayManager::getTheGameplayManager();
	if ( !gpm->hasObject(getArchetype()) )
		return;

	str objname = getArchetype();
	str useanim, usetype, usethread;
	if ( gpm->hasProperty(objname, "useanim") )
		useanim = gpm->getStringValue(objname, "useanim");
	if ( gpm->hasProperty(objname, "usethread") )
		usethread = gpm->getStringValue(objname, "usethread");
	if ( gpm->hasProperty(objname + ".Pickup", "icon") )
		usetype = gpm->getStringValue(objname + ".Pickup", "icon");

	// If any of these strings were set, add to our UseData object.
	if ( useanim.length() || usethread.length() || usetype.length() )
	{
		if ( !useData )
			useData = new UseData();
		
		useData->setUseAnim(useanim);
		useData->setUseThread(usethread);
		useData->setUseType(usetype);
	}
}

float Weapon::RespawnTime( void )
{
	if ( multiplayerManager.inMultiplayer() )
		return respawntime * multiplayerManager.getWeaponRespawnMultiplayer();
	else
		return respawntime;
}

void Weapon::SetArcProjectile( Event *ev )
{
	_arcProjectile = ev->GetBoolean( 1 );
}

void Weapon::SetLowArcRange( Event *ev )
{
	_lowArcRange = ev->GetFloat( 1 );
}

void Weapon::SetPlayMissSound( Event *ev )
{
	_playMissSound = ev->GetBoolean( 1 );
}

void Weapon::noAmmoMode( Event * )
{
	_noAmmoMode[ firemodeindex ] = true;
}

bool Weapon::hasNoAmmoMode( firemode_t mode )
{
	return _noAmmoMode[ mode ];
}

void Weapon::setNoDelay( Event *ev )
{
	_noDelay[ firemodeindex ] = true;
}

bool Weapon::isModeNoDelay( firemode_t mode )
{
	return _noDelay[ mode ];
}

void Weapon::pauseRegen( Event *ev )
{
	int i;

	for( i = 0 ; i < MAX_FIREMODES ; i++ )
	{
		if ( ( _regenAmount[ i ] > 0 ) )
		{
			_nextRegenTime[ i ] = level.time + _regenTime[ i ] + 0.5f;
		}
	}
}

void Weapon::setChargedModels( Event *ev )
{
	assert( ( firemodeindex >= 0 ) && ( firemodeindex < MAX_FIREMODES ) );
	_chargedModels[ firemodeindex ] = ev->GetInteger( 1 );
}

void Weapon::setControllingProjectile( Event *ev )
{
	if ( ev->NumArgs() > 0 )
	{
		_controllingProjectile = ev->GetBoolean( 1 );
	}
	else
	{
		_controllingProjectile = true;
	}
}

bool Weapon::getControllingProjectile( void )
{
	return _controllingProjectile;
}

void Weapon::setCanInterruptFiringState( Event *ev )
{
	if ( ev->NumArgs() > 0 )
		_canInterruptFiringState = ev->GetBoolean( 1 );
	else
		_canInterruptFiringState = true;
}

void Weapon::setSpreadAnimData( Event *ev )
{
	_spreadAnims[ firemodeindex ] = ev->GetInteger( 1 );
	_spreadTime[ firemodeindex ]  = ev->GetFloat( 2 );
}

bool Weapon::canReload( void )
{
	if ( weaponstate != WEAPON_READY )
		return false;

	return true;
}

int Weapon::getAmmoInClip( firemode_t mode )
{
	firemode_t clipToUse;
	
	assert( ( mode >= 0 ) && ( mode < MAX_FIREMODES ) );
	
	if ( usesameclip )
		clipToUse = FIRE_MODE1;
	else
		clipToUse = mode;

	if ( ( clipToUse >= 0 ) && ( clipToUse < MAX_FIREMODES ) )
	{
		if ( ammo_clip_size[ clipToUse ] )
		{
			return ammo_in_clip[ clipToUse ];
		}
	}

	return 0;
}

void Weapon::setMaxViewShakeChange( Event *ev )
{
	_maxViewShakeChange = ev->GetFloat( 1 );
}

void Weapon::setControlParms( Event *ev )
{
	_controlEmitterName = ev->GetString( 1 );
	_controlSoundName   = ev->GetString( 2 );
}

void Weapon::toggleProjectileControl( void )
{
	if ( _controllingProjectile )
	{
		// No longer control projectile

		_controllingProjectile = false;

		// Turn off sound

		StopLoopSound();

		// Turn off emitter

		clearCustomEmitter( _controlEmitterName );
	}
	else
	{
		// Control projectile now

		_controllingProjectile = true;

		// Turn on sound

		LoopSound( _controlSoundName );

		if ( !_controllingProjectileHidden )
		{
			// Turn on emitter

			setCustomEmitter( _controlEmitterName );
		}
	}
}

void Weapon::setProjectileControlHidden( Event *ev )
{
	_controllingProjectileHidden = ev->GetBoolean( 1 );

	if ( _controllingProjectileHidden && _controllingProjectile )
	{
		// Turn off emitter

		clearCustomEmitter( _controlEmitterName );
	}
	else if ( !_controllingProjectileHidden && _controllingProjectile )
	{
		// Turn on emitter

		setCustomEmitter( _controlEmitterName );
	}
}

void Weapon::setMeleeParms( Event *ev )
{
	_meleeWidth[ firemodeindex ] = ev->GetFloat( 1 );
	_meleeHeight[ firemodeindex ] = ev->GetFloat( 2 );
	_meleeLength[ firemodeindex ] = ev->GetFloat( 3 );
}

void Weapon::setFireOffset( Event *ev )
{
	_fireOffset[ firemodeindex ] = ev->GetVector( 1 );
}

void Weapon::cacheStrings( void )
{
	G_FindConfigstringIndex( va( "$$PickedUpThe$$ $$Weapon-%s$$\n", getName().c_str() ), CS_GENERAL_STRINGS, MAX_GENERAL_STRINGS, true );
}

void Weapon::setAutoReload( Event *ev )
{
	_autoReload = ev->GetBoolean( 1 );
}

void Weapon::setAllowAutoSwitch( Event *ev )
{
	_allowAutoSwitch = ev->GetBoolean( 1 );
}

void Weapon::forceReload( Event *ev )
{
	ForceReload();
}

void Weapon::Think( void )
{
	int i;

	for( i = 0 ; i < MAX_FIREMODES ; i++ )
	{
		if ( owner && ( _regenAmount[ i ] > 0 ) && ( _nextRegenTime[ i ] < level.time ) )
		//if ( ( attached ) && ( _regenAmount[ i ] > 0 ) && ( _nextRegenTime[ i ] < level.time ) )
		{
			owner->GiveAmmo( ammo_type[ i ], _regenAmount[ i ], false );

			_nextRegenTime[ i ] = level.time + _regenTime[ i ];
		}
	}

	if ( attached && _fullAmmoSkin )
	{
		bool full = false;
		firemode_t modeToCheck;

		if ( usesameclip )
			modeToCheck = FIRE_MODE1;
		else
			modeToCheck = _fullAmmoMode;

		if ( ammo_clip_size[ modeToCheck ] > 0 )
		{
			if ( ammo_in_clip[ modeToCheck ] == ammo_clip_size[ modeToCheck ] )
				full = true;
		}
		else
		{
			if ( ( owner ) && ( owner->AmmoCount( ammo_type[ modeToCheck ] ) == owner->MaxAmmoCount( ammo_type[ modeToCheck ] ) ) )
				full = true;
		}

		if ( full )
		{
			ChangeSkin( _fullAmmoSkin, true );
		}
		else
		{
			ChangeSkin( _fullAmmoSkin, false );
		}
	}
}

bool Weapon::shouldAutoSwitch( firemode_t mode )
{
	Player *player;
	int i;
	firemode_t modeToCheck;


	if ( !owner || !owner->isSubclassOf( Player ) )
		return false;

	player = (Player *)(Sentient *)owner;

	if ( HasAmmo( mode ) )
		return false;

	if ( !_allowAutoSwitch )
		return false;

	if ( player->edict->svflags & SVF_BOT )
		return true;

	if ( !player->getAutoSwitchWeapons() )
		return false;

	for ( i = 0 ; i < MAX_FIREMODES ; i++ )
	{
		if ( usesameclip )
			modeToCheck = FIRE_MODE1;
		else
			modeToCheck = (firemode_t)i;

		if ( HasAmmo( modeToCheck ) )
			return false;

		if ( firetype[ i ] == FT_TRIGGER_PROJECTILE )
		{
			if ( _nextSwitchTime > level.time )
			{
				return false;
			}
		}
	}

	return true;
}

int Weapon::getWeaponPriority( void )
{
	int priority;
	str currentWeaponName;

	for ( priority = 0 ; priority < 14 ; priority++ )
	{
		currentWeaponName = getWeaponByPriority( priority );

		if ( currentWeaponName == getName() )
			return priority;
	}

	return 100;
}

str Weapon::getWeaponByPriority( int priority )
{
	str weaponName;

	switch ( priority )
	{
	case 0 :
		weaponName = "PhotonBurst";
		break;
	case 1 :
		weaponName = "TetryonGatlingGun";
		break;
	case 2 :
		weaponName = "FederationSniperRifle";
		break;
	case 3 :
		weaponName = "RomulanRadGun";
		break;
	case 4 :
		weaponName = "AttrexianRifle";
		break;
	case 5 :
		weaponName = "BurstRifle";
		break;
	case 6 :
		weaponName = "GrenadeLauncher";
		break;
	case 7 :
		weaponName = "FieldAssaultRifle";
		break;
	case 8 :
		weaponName = "I-Mod";
		break;
	case 9 :
		weaponName = "CompressionRifle";
		break;
	case 10 :
		weaponName = "RomulanDisruptor";
		break;
	case 11 :
		weaponName = "DrullStaff";
		break;
	case 12 :
		weaponName = "Phaser-stx";
		break;
	case 13 :
		weaponName = "Phaser";
		break;
	}

	return weaponName;
}

void Weapon::autoSwitch()
{
	str weaponName;
	Weapon *weapon;
	Item *item;
	int i;
	Player *player;
	int ammoAvailable;

	if ( !owner || !owner->isSubclassOf( Player ) )
		return;

	player = (Player *)(Sentient *)owner;

	for ( i = 0 ; i < 14 ; i++ )
	{
		weaponName = getWeaponByPriority( i );

		if ( player->HasItem( weaponName ) )
		{
			item = player->FindItem( weaponName );

			if ( item && item->isSubclassOf( Weapon ) )
			{
				weapon = (Weapon *)item;

				ammoAvailable = weapon->getAmmoInClip( FIRE_MODE1 ) + weapon->AmmoAvailable( FIRE_MODE1 );

				if ( ammoAvailable > weapon->ammorequired[ FIRE_MODE1 ] )
				{
					Event *newEvent = new Event( EV_Player_UseItem );
					newEvent->AddString( weaponName );
					player->ProcessEvent( newEvent );

					return;
				}
			}
		}
	}
}

void Weapon::setNextSwitchTime( float time )
{
	_nextSwitchTime = level.time + time;
}

void Weapon::setNextSwitchTime( Event *ev )
{
	setNextSwitchTime( ev->GetFloat( 1 ) );
}
