class Weapon extends Inventory
    abstract
    native
    nativereplication
    HideDropDown
	CacheExempt;

#exec Texture Import File=Textures\Weapon.tga Name=S_Weapon Mips=Off Alpha=1

replication
{
    // Things the server should send to the client.
    reliable if( Role==ROLE_Authority )
        Ammo, AmmoCharge;

//if _RO_
    // Things the server should send to the client.
    reliable if( bNetDirty && Role==ROLE_Authority )
        bBayonetMounted, bUsingSights;
// end _RO_

    // Functions called by server on client
    reliable if( Role==ROLE_Authority )
        ClientWeaponSet, ClientWeaponThrown, ClientForceAmmoUpdate, ClientWriteStats, ClientWriteFire;

    // functions called by client on server
    reliable if( Role<ROLE_Authority )
        ServerStartFire, ServerStopFire;
}

const NUM_FIRE_MODES = 2;

var() class<WeaponFire> FireModeClass[NUM_FIRE_MODES];
var() protected WeaponFire FireMode[NUM_FIRE_MODES];
var() protected Ammunition Ammo[NUM_FIRE_MODES];

// animation //
var() Name IdleAnim;
var() Name RestAnim;
var() Name AimAnim;
var() Name RunAnim;
var() Name SelectAnim;
var() Name PutDownAnim;

var() float IdleAnimRate;
var() float RestAnimRate;
var() float AimAnimRate;
var() float RunAnimRate;
var() float SelectAnimRate;
var() float PutDownAnimRate;
var float PutDownTime;
var float BringUpTime;

var() Sound SelectSound;
var() String SelectForce;  // jdf

// AI //
var()	int		BotMode; // the fire Mode currently being used for bots
var()	float	AIRating;
var		float	CurrentRating;	// rating result from most recent RateSelf()
var()	bool	bMeleeWeapon;
var()	bool	bSniping;

// other useful stuff //
var	  bool bShowChargingBar;
var	  bool bMatchWeapons;	// OBSOLETE - see WeaponAttachment
var() bool bCanThrow;
var() bool bForceSwitch; // if true, this weapon will prevent any other weapon from delaying the switch to it (bomb launcher)
var() deprecated bool bNotInPriorityList; // Should be displayed in a GUI weapon list	-	refer to 'Description' documentation
var	  bool bNotInDemo;
var	  bool bNoVoluntarySwitch;
var	  bool bSpectated;
var	  bool bDebugging;
var	  bool bNoInstagibReplace;
var	  bool bInitOldMesh;
var config bool bUseOldWeaponMesh;
var   bool	bEndOfRound;	// don't allow firing
var bool bNoAmmoInstances;	// if true, replicated ammocount using the Weapons AmmoCharge property - true by default, included to allow mod authors to fallback to old style
var bool bBerserk;

// properties needed if no instantiated ammunition
var int AmmoCharge[2];
var class<Ammunition> AmmoClass[2];

var Mesh OldMesh;
var string OldPickup;
var(OldFirstPerson) float OldDrawScale, OldCenteredOffsetY;
var(OldFirstPerson) vector OldPlayerViewOffset, OldSmallViewOffset;
var(OldFirstPerson) rotator OldPlayerViewPivot;
var(OldFirstPerson) int	OldCenteredRoll, OldCenteredYaw;

/*
	A note about weapons & the caching system:
	You must now perform two commands on your mod's final package file:

	'ucc dumpint <PackageFileName.u>' - this exports the localized text to a localization file, which is used by the caching system to load the ItemName and Description
	'ucc exportcache <PackageName.u>' - this exports the information that will be used to load the weapon into the caching system.  Type 'ucc help exportcache' at the command-line for more info.

	Ex: (ACoolWeaponMod.u)

	ucc dumpint ACoolWeaponMod.u
	- creates 'ACoolWeaponMod.int' file, (file extension will vary if a different language is specified in the UT2004.ini file)

	ucc exportcache ACoolWeaponMod.u
	- creates an entry in the 'CacheRecords.ucl' file, containing caching information for your package

	Weapons must have values for both ItemName & Description in order to appear in the game.  The caching system will not recognize inherited values for these properties.
	If creating a custom crosshair for your weapon, it will FIXME
	-- rjp
*/
var() localized cache string Description;

var class<Weapon> DemoReplacement;
var transient bool bPendingSwitch;
var(FirstPerson) vector EffectOffset; // where muzzle flashes and smoke appear. replace by bone reference eventually
var() Localized string MessageNoAmmo;
var(FirstPerson) float DisplayFOV;
var() enum EWeaponClientState
{
    WS_None,
    WS_Hidden,
    WS_BringUp,
    WS_PutDown,
    WS_ReadyToFire
} ClientState; // this will always be None on the server

var() config byte ExchangeFireModes;
var() config byte Priority;

var() deprecated byte DefaultPriority;

var float Hand;
var float RenderedHand;
var Color HudColor;
var Weapon OldWeapon;
var(FirstPerson)	vector      SmallViewOffset;   // Offset from view center with small weapons option.
var(FirstPerson) vector SmallEffectOffset;
var(FirstPerson) float CenteredOffsetY;
var(FirstPerson) int CenteredRoll, CenteredYaw;
var config int CustomCrosshair;
var config color CustomCrossHairColor;
var config float CustomCrossHairScale;
var config string CustomCrossHairTextureName;
var texture CustomCrossHairTexture;

var float DownDelay, MinReloadPct;		// Used to delay putting down weapons which have jsut been fired

// ROVariables - put here to avoid casting
// if _RO_
var	bool bBayonetMounted; // If true this weapon has a bayonet mounted
var bool bCanAttachOnBack;// True if this weapon can be placed on the pawns back
var bool bCanRestDeploy;// True if this weapon can be deployed by resting it on something
var bool bCanBipodDeploy;// True if this weapon can be deployed using a bipod
var	bool bBipodDeployed; // True if the weapon is deployed on its bipod
var	float LastStartCrawlingTime; // Used in smoothing out transitions to/from crawling forward/back
var	bool bUsingSights; // True if the weapon is zoomed in using the sights
var	bool bUsesFreeAim; // Whether or not this weapon uses free-aim
var	bool bCanSway; // This weapon can sway in ironsights (false for binocs)
// end _RO_

native final function InitWeaponFires();

simulated function float ChargeBar();

// ROFunctions - put here to avoid casting
// if _RO_
simulated function bool WeaponCanSwitch() {return true;}
simulated function bool WeaponCanBusySwitch() {return false;}
simulated function bool WeaponAllowSprint() {return true;}
simulated function bool WeaponAllowProneChange() {return true;}
simulated function bool WeaponAllowCrouchChange() {return true;}
simulated function bool IsMounted() {return false;}
simulated function bool IsCrawling() {return false;}
simulated function ROIronSights();
simulated function int GetHudAmmoCount();
simulated function NotifyOwnerJumped();
function SetServerOrientation(rotator NewRotation);
function coords GetMuzzleCoords();
simulated function bool ShouldUseFreeAim() {return false;}
simulated function PostFire();
// end _RO_

// Called by physics when the player is crawling
simulated event NotifyCrawlMoving();
simulated event NotifyStopCrawlMoving();

//=========================================================================
// Ammunition Interface (to remove the need for instantiated ammunition)

simulated function class<Ammunition> GetAmmoClass(int mode)
{
	return AmmoClass[mode];
}

simulated function MaxOutAmmo()
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] != None )
			AmmoCharge[0] = MaxAmmo(0);
		if ( (AmmoClass[1] != None) && (AmmoClass[0] != AmmoClass[1]) )
			AmmoCharge[1] = MaxAmmo(1);
		return;
	}
	if ( Ammo[0] != None )
		Ammo[0].AmmoAmount = Ammo[0].MaxAmmo;
	if ( Ammo[1] != None )
		Ammo[1].AmmoAmount = Ammo[1].MaxAmmo;
}

simulated function SuperMaxOutAmmo()
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] != None )
			AmmoCharge[0] = 999;
		if ( (AmmoClass[1] != None) && (AmmoClass[0] != AmmoClass[1]) )
			AmmoCharge[1] = 999;
		return;
	}
	if ( Ammo[0] != None )
		Ammo[0].AmmoAmount = 999;
	if ( Ammo[1] != None )
		Ammo[1].AmmoAmount = 999;
}
simulated function int MaxAmmo(int mode)
{
	if ( AmmoClass[mode] != None )
		return AmmoClass[mode].Default.MaxAmmo;

	return 0;
}

simulated function FillToInitialAmmo()
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] != None )
			AmmoCharge[0] = Max(AmmoCharge[0], AmmoClass[0].Default.InitialAmount);
		if ( (AmmoClass[1] != None) && (AmmoClass[0] != AmmoClass[1]) )
			AmmoCharge[1] = Max(AmmoCharge[1], AmmoClass[1].Default.InitialAmount);
		return;
	}

	if ( Ammo[0] != None )
		Ammo[0].AmmoAmount = Max(Ammo[0].AmmoAmount,Ammo[0].InitialAmount);
	if ( Ammo[1] != None )
		Ammo[1].AmmoAmount = Max(Ammo[1].AmmoAmount,Ammo[1].InitialAmount);
}

simulated function int AmmoAmount(int mode)
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] == AmmoClass[mode] )
			return AmmoCharge[0];
		return AmmoCharge[mode];
	}
	if ( Ammo[mode] != None )
		return Ammo[mode].AmmoAmount;

	return 0;
}

simulated function class<Pickup> AmmoPickupClass(int mode)
{
	if ( AmmoClass[mode] != None )
		return FireMode[mode].AmmoClass.Default.PickupClass;

	return None;
}

simulated function bool AmmoMaxed(int mode)
{
	if ( AmmoClass[mode] == None )
		return false;

	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] == AmmoClass[mode] )
			mode = 0;
		return AmmoCharge[mode] >= MaxAmmo(mode);
	}
	if ( Ammo[mode] == None )
		return false;
	return (Ammo[mode].AmmoAmount >= MaxAmmo(mode));
}

simulated function GetAmmoCount(out float MaxAmmoPrimary, out float CurAmmoPrimary)
{
	if ( AmmoClass[0] == None )
		return;

	if ( bNoAmmoInstances )
	{
		MaxAmmoPrimary = MaxAmmo(0);
		CurAmmoPrimary = AmmoCharge[0];
		return;
	}

	if ( Ammo[0] == None )
		return;
	MaxAmmoPrimary = Ammo[0].MaxAmmo;
	CurAmmoPrimary = Ammo[0].AmmoAmount;
}

simulated function float AmmoStatus(optional int Mode) // returns float value for ammo amount
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[Mode] == None )
			return 0;
		if ( AmmoClass[0] == AmmoClass[mode] )
			mode = 0;

		return float(AmmoCharge[Mode])/float(MaxAmmo(Mode));
	}
    if (Ammo[Mode] == None)
        return 0.0;
    else
	    return float(Ammo[Mode].AmmoAmount) / float(Ammo[Mode].MaxAmmo);
}

simulated function bool ConsumeAmmo(int Mode, float load, optional bool bAmountNeededIsMax)
{
	local int AmountNeeded;

	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] == AmmoClass[mode] )
			mode = 0;
		AmountNeeded = int(load);
		if (bAmountNeededIsMax && AmmoCharge[mode] < AmountNeeded)
			AmountNeeded = AmmoCharge[mode];

		if (AmmoCharge[mode] < AmountNeeded)
		{
			CheckOutOfAmmo();
			return false;   // Can't do it
		}

		AmmoCharge[mode] -= AmountNeeded;
		NetUpdateTime = Level.TimeSeconds - 1;

		if (Level.NetMode == NM_StandAlone || Level.NetMode == NM_ListenServer)
			CheckOutOfAmmo();

		return true;
	}
    if (Ammo[Mode] != None)
        return Ammo[Mode].UseAmmo(int(load), bAmountNeededIsMax);

    return true;
}

function bool AddAmmo(int AmmoToAdd, int Mode)
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] == AmmoClass[mode] )
			mode = 0;
		if ( Level.GRI.WeaponBerserk > 1.0 )
			AmmoCharge[mode] = MaxAmmo(Mode);
		else if ( AmmoCharge[mode] < MaxAmmo(mode) )
			AmmoCharge[mode] = Min(MaxAmmo(mode), AmmoCharge[mode]+AmmoToAdd);
		NetUpdateTime = Level.TimeSeconds - 1;
		return true;
	}
    if (Ammo[Mode] != None)
		return Ammo[Mode].AddAmmo(AmmoToAdd);
}

simulated function bool HasAmmo()
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] == AmmoClass[1] )
			return ( (AmmoClass[0] != None && FireMode[0] != None && AmmoCharge[0] >= FireMode[0].AmmoPerFire) );

		return ( (AmmoClass[0] != None && FireMode[0] != None && AmmoCharge[0] >= FireMode[0].AmmoPerFire)
			|| (AmmoClass[1] != None && FireMode[1] != None && AmmoCharge[1] >= FireMode[1].AmmoPerFire) );
	}
    return ( (Ammo[0] != None && FireMode[0] != None && Ammo[0].AmmoAmount >= FireMode[0].AmmoPerFire)
          || (Ammo[1] != None && FireMode[1] != None && Ammo[1].AmmoAmount >= FireMode[1].AmmoPerFire) );
}

// for AI
simulated function bool NeedAmmo(int mode)
{
	if ( bNoAmmoInstances )
	{
		if ( AmmoClass[0] == AmmoClass[mode] )
			mode = 0;
		if ( AmmoClass[mode] == None )
			return false;

		return ( AmmoCharge[Mode] < AmmoClass[mode].default.InitialAmount );
	}
	if ( Ammo[mode] != None )
		 return (Ammo[mode].AmmoAmount < Ammo[mode].InitialAmount);

	return false;
}

simulated function float DesireAmmo(class<Inventory> NewAmmoClass, bool bDetour)
{
	local int i;
	local float curr, max;

	for ( i=0; i<2; i++ )
		if ( NewAmmoClass == AmmoClass[i] )
		{
			if ( AmmoMaxed(i) )
				return -100;
			curr = AmmoAmount(i);
			if ( curr == 0 )
				return 1;
			max = MaxAmmo(i);

			return ( FMin(0.5*(max-curr),AmmoClass[i].Default.AmmoAmount)/max );
		}
	return 0;
}

simulated function CheckOutOfAmmo()
{
	if (Instigator != None && Instigator.Weapon == self)
	{
		if ( bNoAmmoInstances )
		{
			if ( (AmmoCharge[0] <= 0) && (AmmoCharge[1] <= 0) )
				OutOfAmmo();
			return;
		}

		if ( Ammo[0] != None )
			Ammo[0].CheckOutOfAmmo();
		if ( Ammo[1] != None )
			Ammo[1].CheckOutOfAmmo();
	}
}

simulated function PostNetReceive()
{
    CheckOutOfAmmo();
}

//=========================================================================

simulated function DrawWeaponInfo(Canvas C);
simulated function NewDrawWeaponInfo(Canvas C, float YPos);

function StartDebugging()
{
}

simulated function ClientWriteStats(byte Mode, bool bMatch, bool bAllowFire, bool bDelay, bool bAlt, float wait)
{
	log(self$" Same weapon "$bMatch$" Mode "$Mode$" Allow fire "$bAllowFire$" delay start fire "$bDelay$" alt firing "$bAlt$" next fire wait "$wait);
}

function class<DamageType> GetDamageType();

function HackPlayFireSound()
{
	if ( (FireMode[0] != None) && (FireMode[0].FireSound != None) )
		PlaySound(FireMode[0].FireSound, SLOT_None, 1.0);
}

//=================================================================
// AI functions

function float RangedAttackTime()
{
	return 0;
}

function bool RecommendRangedAttack()
{
	return false;
}

function bool RecommendLongRangedAttack()
{
	return false;
}

function bool FocusOnLeader(bool bLeaderFiring)
{
	return false;
}

function FireHack(byte Mode);

// return true if weapon effect has splash damage (if significant)
// use by bot to avoid hurting self
// should be based on current firing Mode if active
function bool SplashDamage()
{
    return FireMode[BotMode].bSplashDamage;
}

// return true if weapon should be fired to take advantage of splash damage
// For example, rockets should be fired at enemy feet
function bool RecommendSplashDamage()
{
    return FireMode[BotMode].bRecommendSplashDamage;
}

function float GetDamageRadius()
{
    if (FireMode[BotMode].ProjectileClass == None)
        return 0;
    else
        return FireMode[BotMode].ProjectileClass.default.DamageRadius;
}

// Repeater weapons like minigun should be 0.99, other weapons based on likelihood
// of firing again right away
function float RefireRate()
{
    return FireMode[BotMode].BotRefireRate;
}

// tells AI that it needs to release the fire button for this weapon to do anything
function bool FireOnRelease()
{
	return FireMode[BotMode].bFireOnRelease;
}

simulated function Loaded();

simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
    local int i;
    local string T;
    local name Anim;
    local float frame,rate;

    Canvas.SetDrawColor(0,255,0);
    if ( (Pawn(Owner) != None) && (Pawn(Owner).PlayerReplicationInfo != None) )
		Canvas.DrawText("WEAPON "$GetItemName(string(self))$" Owner "$Pawn(Owner).PlayerReplicationInfo.PlayerName);
    else
		Canvas.DrawText("WEAPON "$GetItemName(string(self))$" Owner "$Owner);
    YPos += YL;
    Canvas.SetPos(4,YPos);

    T = "     STATE: "$GetStateName()$" Timer: "$TimerCounter$" Client State ";

	Switch( ClientState )
	{
		case WS_None: T=T$"None"; break;
		case WS_Hidden: T=T$"Hidden"; break;
		case WS_BringUp: T=T$"BringUp"; break;
		case WS_PutDown: T=T$"PutDown"; break;
		case WS_ReadyToFire: T=T$"ReadyToFire"; break;
	}

    Canvas.DrawText(T, false);
    YPos += YL;
    Canvas.SetPos(4,YPos);

    if ( DrawType == DT_StaticMesh )
        Canvas.DrawText("     StaticMesh "$GetItemName(string(StaticMesh))$" AmbientSound "$AmbientSound, false);
    else
        Canvas.DrawText("     Mesh "$GetItemName(string(Mesh))$" AmbientSound "$AmbientSound, false);
    YPos += YL;
    Canvas.SetPos(4,YPos);
    if ( Mesh != None )
    {
        // mesh animation
        GetAnimParams(0,Anim,frame,rate);
        T = "     AnimSequence "$Anim$" Frame "$frame$" Rate "$rate;
        if ( bAnimByOwner )
            T= T$" Anim by Owner";

        Canvas.DrawText(T, false);
        YPos += YL;
        Canvas.SetPos(4,YPos);

		T = "Eyeheight "$Instigator.EyeHeight$" base "$Instigator.BaseEyeheight$" landbob "$Instigator.Landbob$" just landed "$Instigator.bJustLanded$" land recover "$Instigator.bLandRecovery;
        Canvas.DrawText(T, false);
        YPos += YL;
        Canvas.SetPos(4,YPos);
    }

    for ( i=0; i<NUM_FIRE_MODES; i++ )
    {
        if ( FireMode[i] == None )
        {
            Canvas.DrawText("NO FIREMODE "$i);
            YPos += YL;
            Canvas.SetPos(4,YPos);
        }
        else
            FireMode[i].DisplayDebug(Canvas,YL,YPos);

        Canvas.DrawText("Ammunition "$i$" amount "$AmmoAmount(i));
		YPos += YL;
		Canvas.SetPos(4,YPos);
    }
}

simulated function Weapon RecommendWeapon( out float rating )
{
    local Weapon Recommended;
    local float oldRating;

    if ( (Instigator == None) || (Instigator.Controller == None) )
        rating = -2;
    else
        rating = RateSelf() + Instigator.Controller.WeaponPreference(self);

    if ( inventory != None )
    {
        Recommended = inventory.RecommendWeapon(oldRating);
        if ( (Recommended != None) && (oldRating > rating) )
        {
            rating = oldRating;
            return Recommended;
        }
    }
    return self;
}

function SetAITarget(Actor T);

/* BestMode()
choose between regular or alt-fire
*/
function byte BestMode()
{
	if ( Instigator.Controller.bFire != 0 )
		return 0;
	else if ( Instigator.Controller.bAltFire != 0 )
		return 1;
	if ( FRand() < 0.5 )
		return 1;
	return 0;
}

/* BotFire()
called by NPC firing weapon. Weapon chooses appropriate firing Mode to use (typically no change)
bFinished should only be true if called from the Finished() function
FiringMode can be passed in to specify a firing Mode (used by scripted sequences)
*/
function bool BotFire(bool bFinished, optional name FiringMode)
{
    local int newmode;
    local Controller C;

    C = Instigator.Controller;
	newMode = BestMode();

	if ( newMode == 0 )
	{
		C.bFire = 1;
		C.bAltFire = 0;
	}
	else
	{
		C.bFire = 0;
		C.bAltFire = 1;
	}

	if ( bFinished )
		return true;

    if ( FireMode[BotMode].IsFiring() )
    {
    	if (BotMode == newMode)
    		return true;
    	else
			StopFire(BotMode);
    }

    if ( !ReadyToFire(newMode) || ClientState != WS_ReadyToFire )
		return false;

    BotMode = NewMode;
    StartFire(NewMode);
    return true;
}

// this returns the actual projectile spawn location or trace start
simulated function vector GetFireStart(vector X, vector Y, vector Z)
{
    return FireMode[BotMode].GetFireStart(X,Y,Z);
}

// need to figure out modified rating based on enemy/tactical situation
simulated function float RateSelf()
{
    if ( !HasAmmo() )
        CurrentRating = -2;
	else if ( Instigator.Controller == None )
		return 0;
	else
		CurrentRating = Instigator.Controller.RateWeapon(self);
	return CurrentRating;
}

function float GetAIRating()
{
	return AIRating;
}

// tells bot whether to charge or back off while using this weapon
function float SuggestAttackStyle()
{
    return 0.0;
}

// tells bot whether to charge or back off while defending against this weapon
function float SuggestDefenseStyle()
{
    return 0.0;
}

// return true if recommend jumping while firing to improve splash damage (by shooting at feet)
// true for R.L., for example
function bool SplashJump()
{
    return FireMode[BotMode].bSplashJump;
}

// return false if out of range, can't see target, etc.
function bool CanAttack(Actor Other)
{
    local float Dist, CheckDist;
    local vector HitLocation, HitNormal,X,Y,Z, projStart;
    local actor HitActor;
    local int m;
	local bool bInstantHit;
	local bool bShouldAttack;

	// if _RO_
	// Extra friendly fire checking
	local pawn Victims;
	local vector dir, lookdir;
	local float DiffAngle, FriendlyDist, MinAngle;
	// end _RO_

    if ( (Instigator == None) || (Instigator.Controller == None) )
        return false;

    // check that target is within range
    Dist = VSize(Instigator.Location - Other.Location);
    if ( (Dist > FireMode[0].MaxRange()) && (Dist > FireMode[1].MaxRange()) )
        return false;

    // check that can see target
    if ( !Instigator.Controller.LineOfSightTo(Other) )
        return false;

    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
		if ( FireMode[m].bInstantHit )
			bInstantHit = true;
		else
		{
			CheckDist = FMax(CheckDist, 0.5 * FireMode[m].ProjectileClass.Default.Speed);
	        CheckDist = FMax(CheckDist, 300);
	        CheckDist = FMin(CheckDist, VSize(Other.Location - Location));
		}
	}
    // check that would hit target, and not a friendly
    GetAxes(Instigator.Controller.Rotation, X,Y,Z);
    projStart = GetFireStart(X,Y,Z);
    if ( bInstantHit )
        HitActor = Instigator.Trace(HitLocation, HitNormal, Other.Location + Other.CollisionHeight * vect(0,0,0.8), projStart, true);
    else
    {
        // for non-instant hit, only check partial path (since others may move out of the way)
        HitActor = Instigator.Trace(HitLocation, HitNormal,
                projStart + CheckDist * Normal(Other.Location + Other.CollisionHeight * vect(0,0,0.8) - Location),
                projStart, true);
    }

//    if( (HitActor != None) && HitActor.IsA('ROCollisionAttachment'))
//    {
//    	log(self$"'s trace hit "$HitActor.Base$" Collision attachment");
//    }

	// if _RO_
	// Extra friendly fire checking
	if (!Instigator.IsHumanControlled() )
	{
		foreach VisibleCollidingActors( class 'Pawn', Victims, 3000 ) //, RadiusHitLocation
		{
			if( Victims != Instigator && Instigator.Controller.SameTeamAs(Victims.Controller))
			{
				FriendlyDist = VSizeSquared(Instigator.Location - Victims.Location);

				if( FriendlyDist < 22500 ) //2.5 meters
				{
					MinAngle = 0.85;
				}
				else if( FriendlyDist < 90000 ) //5.0 meters
				{
					MinAngle = 0.95;
				}
				else if( FriendlyDist < 360000 ) //10.0 meters
				{
					MinAngle = 0.98;
				}
				else
				{
					MinAngle = 0.99;
				}

	  			lookdir = Normal((Other.Location + Other.CollisionHeight * vect(0,0,0.8))-projStart);
				dir = Normal(Victims.Location - Instigator.Location);

	           	DiffAngle = lookdir dot dir;

	           	if(  DiffAngle > MinAngle )
	           	{
	           		//log("Not firing because we might hit a friendly with a DiffAngle of "$DiffAngle);
	           		return false;
	           	}

			}
		}
	}

    if ( (HitActor == None) || (HitActor == Other) )
		bShouldAttack = true;//return true;
	else if ( Pawn(HitActor) == None )
		bShouldAttack = !HitActor.BlocksShotAt(Other);//return !HitActor.BlocksShotAt(Other);
	else if ( (Pawn(HitActor).Controller == None) || !Instigator.Controller.SameTeamAs(Pawn(HitActor).Controller) )
        bShouldAttack = true;//return true;

//	if( Pawn(HitActor) != none && Instigator.Controller.SameTeamAs(Pawn(HitActor).Controller))
//	{
//		log("Weapon "$Instigator$"'s shot would hit "$Other$" who is on the same team and should attack is "$bShouldAttack);
//	}

    return bShouldAttack;//false;
}


//=================================================================

simulated function PostBeginPlay()
{
    local int m;
    Super.PostBeginPlay();
    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
        if (FireModeClass[m] != None)
        {
            FireMode[m] = new(self) FireModeClass[m];
            if ( FireMode[m] != None )
				AmmoClass[m] = FireMode[m].AmmoClass;
        }
     }
     InitWeaponFires();

     for (m = 0; m < NUM_FIRE_MODES; m++)
    {
        if (FireMode[m] != None)
        {
            FireMode[m].ThisModeNum = m;
            FireMode[m].Weapon = self;
            FireMode[m].Instigator = Instigator;
            FireMode[m].Level = Level;
            FireMode[m].Owner = self;
			FireMode[m].PreBeginPlay();
			FireMode[m].BeginPlay();
			FireMode[m].PostBeginPlay();
			FireMode[m].SetInitialState();
			FireMode[m].PostNetBeginPlay();
		}
    }

	if ( Level.bDropDetail || (Level.DetailMode == DM_Low) )
		MaxLights = Min(4,MaxLights);

	if ( SmallViewOffset == vect(0,0,0) )
		SmallViewOffset = Default.PlayerviewOffset;

	if ( SmallEffectOffset == vect(0,0,0) )
		SmallEffectOffset = EffectOffset + Default.PlayerViewOffset - SmallViewOffset;

	if ( bUseOldWeaponMesh && (OldMesh != None) )
	{
		bInitOldMesh = true;
		LinkMesh(OldMesh);
	}
	if ( Level.GRI != None )
		CheckSuperBerserk();
}

simulated function SetGRI(GameReplicationInfo G)
{
	CheckSuperBerserk();
}

simulated function Destroyed()
{
    local int m;

    AmbientSound = None;

    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
		if ( FireMode[m] != None )
			FireMode[m].DestroyEffects();
        if (Ammo[m] != None)
        {
            Ammo[m].Destroy();
            Ammo[m] = None;
        }
    }
    Super.Destroyed();
}

simulated function Reselect()
{
}

simulated function bool WeaponCentered()
{
	return ( bSpectated || (Hand > 1) );
}

simulated event RenderOverlays( Canvas Canvas )
{
    local int m;
	local vector NewScale3D;
	local rotator CenteredRotation;
	local name AnimSeq;
	local float frame,rate;

    if (Instigator == None)
        return;

	if ( Instigator.Controller != None )
		Hand = Instigator.Controller.Handedness;

    if ((Hand < -1.0) || (Hand > 1.0))
        return;

    // draw muzzleflashes/smoke for all fire modes so idle state won't
    // cause emitters to just disappear
    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
        if (FireMode[m] != None)
        {
            FireMode[m].DrawMuzzleFlash(Canvas);
        }
    }

	if ( (OldMesh != None) && (bUseOldWeaponMesh != (OldMesh == Mesh)) )
	{
		GetAnimParams(0,AnimSeq,frame,rate);
		bInitOldMesh = true;
		if ( bUseOldWeaponMesh )
			LinkMesh(OldMesh);
		else
			LinkMesh(Default.Mesh);
		PlayAnim(AnimSeq,rate,0.0);
	}

    if ( (Hand != RenderedHand) || bInitOldMesh )
    {
		newScale3D = Default.DrawScale3D;
		if ( Hand != 0 )
			newScale3D.Y *= Hand;
		SetDrawScale3D(newScale3D);
		SetDrawScale(Default.DrawScale);
		CenteredRoll = Default.CenteredRoll;
		CenteredYaw = Default.CenteredYaw;
		CenteredOffsetY = Default.CenteredOffsetY;
		PlayerViewPivot = Default.PlayerViewPivot;
		SmallViewOffset = Default.SmallViewOffset;
		if ( SmallViewOffset == vect(0,0,0) )
			SmallViewOffset = Default.PlayerviewOffset;
		bInitOldMesh = false;
		if ( Default.SmallEffectOffset == vect(0,0,0) )
			SmallEffectOffset = EffectOffset + Default.PlayerViewOffset - SmallViewOffset;
		else
			SmallEffectOffset = Default.SmallEffectOffset;
		if ( Mesh == OldMesh )
		{
			SmallEffectOffset = EffectOffset + OldPlayerViewOffset - OldSmallViewOffset;
			PlayerViewPivot = OldPlayerViewPivot;
			SmallViewOffset = OldSmallViewOffset;
			if ( Hand != 0 )
			{
				PlayerViewPivot.Roll *= Hand;
				PlayerViewPivot.Yaw *= Hand;
			}
			CenteredRoll = OldCenteredRoll;
			CenteredYaw = OldCenteredYaw;
			CenteredOffsetY = OldCenteredOffsetY;
			SetDrawScale(OldDrawScale);
		}
		else if ( Hand == 0 )
		{
			PlayerViewPivot.Roll = Default.PlayerViewPivot.Roll;
			PlayerViewPivot.Yaw = Default.PlayerViewPivot.Yaw;
		}
		else
		{
			PlayerViewPivot.Roll = Default.PlayerViewPivot.Roll * Hand;
			PlayerViewPivot.Yaw = Default.PlayerViewPivot.Yaw * Hand;
		}
		RenderedHand = Hand;
	}
	if ( class'PlayerController'.Default.bSmallWeapons )
		PlayerViewOffset = SmallViewOffset;
	else if ( Mesh == OldMesh )
		PlayerViewOffset = OldPlayerViewOffset;
	else
		PlayerViewOffset = Default.PlayerViewOffset;
	if ( Hand == 0 )
		PlayerViewOffset.Y = CenteredOffsetY;
	else
		PlayerViewOffset.Y *= Hand;

    SetLocation( Instigator.Location + Instigator.CalcDrawOffset(self) );
    if ( Hand == 0 )
    {
		CenteredRotation = Instigator.GetViewRotation();
		CenteredRotation.Yaw += CenteredYaw;
		CenteredRotation.Roll = CenteredRoll;
	    SetRotation(CenteredRotation);
    }
    else
	    SetRotation( Instigator.GetViewRotation() );

	PreDrawFPWeapon();	// Laurent -- Hook to override things before render (like rotation if using a staticmesh)

    bDrawingFirstPerson = true;
    Canvas.DrawActor(self, false, false, DisplayFOV);
    bDrawingFirstPerson = false;
	if ( Hand == 0 )
		PlayerViewOffset.Y = 0;
}

simulated function PreDrawFPWeapon();

simulated function SetHand(float InHand)
{
    Hand = InHand;
}

simulated function GetViewAxes( out vector xaxis, out vector yaxis, out vector zaxis )
{
    if ( Instigator.Controller == None )
        GetAxes( Instigator.Rotation, xaxis, yaxis, zaxis );
    else
        GetAxes( Instigator.Controller.Rotation, xaxis, yaxis, zaxis );
}

simulated function vector CenteredEffectStart()
{
	return Instigator.Location;
}

simulated function vector GetEffectStart()
{
    local Vector X,Y,Z;

    // jjs - this function should actually never be called in third person views
    // any effect that needs a 3rdp weapon offset should figure it out itself

    // 1st person
    if (Instigator.IsFirstPerson())
    {
        if ( WeaponCentered() )
			return CenteredEffectStart();

        GetViewAxes(X, Y, Z);
        if ( class'PlayerController'.Default.bSmallWeapons )
			return (Instigator.Location +
				Instigator.CalcDrawOffset(self) +
				SmallEffectOffset.X * X  +
				SmallEffectOffset.Y * Y * Hand +
				SmallEffectOffset.Z * Z);
        else
			return (Instigator.Location +
				Instigator.CalcDrawOffset(self) +
				EffectOffset.X * X +
				EffectOffset.Y * Y * Hand +
				EffectOffset.Z * Z);
    }
    // 3rd person
    else
    {
        return (Instigator.Location +
            Instigator.EyeHeight*Vect(0,0,0.5) +
            Vector(Instigator.Rotation) * 40.0);
    }
}

simulated function IncrementFlashCount(int Mode)
{
    if ( WeaponAttachment(ThirdPersonActor) != None )
    {
        if (Mode == 0)
            WeaponAttachment(ThirdPersonActor).FiringMode = 0;
        else
            WeaponAttachment(ThirdPersonActor).FiringMode = 1;
        ThirdPersonActor.NetUpdateTime = Level.TimeSeconds - 1;
        WeaponAttachment(ThirdPersonActor).FlashCount++;
        WeaponAttachment(ThirdPersonActor).ThirdPersonEffects();
    }
}

simulated function ZeroFlashCount(int Mode)
{
    if ( WeaponAttachment(ThirdPersonActor) != None )
    {
        if (Mode == 0)
            WeaponAttachment(ThirdPersonActor).FiringMode = 0;
        else
            WeaponAttachment(ThirdPersonActor).FiringMode = 1;
        ThirdPersonActor.NetUpdateTime = Level.TimeSeconds - 1;
        WeaponAttachment(ThirdPersonActor).FlashCount = 0;
        WeaponAttachment(ThirdPersonActor).ThirdPersonEffects();
    }
}

simulated function Weapon WeaponChange( byte F, bool bSilent )
{
    local Weapon newWeapon;

    if ( InventoryGroup == F )
    {
        if ( !HasAmmo() )
        {
            if ( Inventory == None )
                newWeapon = None;
            else
                newWeapon = Inventory.WeaponChange(F,bSilent);

            if ( !bSilent && (newWeapon == None) && Instigator.IsHumanControlled() )
                Instigator.ClientMessage( ItemName$MessageNoAmmo );

            return newWeapon;
        }
        else
            return self;
    }
    else if ( Inventory == None )
        return None;
    else
        return Inventory.WeaponChange(F,bSilent);
}

simulated function Weapon PrevWeapon(Weapon CurrentChoice, Weapon CurrentWeapon)
{
    if ( HasAmmo() )
    {
        if ( (CurrentChoice == None) )
        {
            if ( CurrentWeapon != self )
                CurrentChoice = self;
        }
        else if ( InventoryGroup == CurrentWeapon.InventoryGroup )
        {
            if ( (GroupOffset < CurrentWeapon.GroupOffset)
                && ((CurrentChoice.InventoryGroup != InventoryGroup) || (GroupOffset > CurrentChoice.GroupOffset)) )
                CurrentChoice = self;
		}
        else if ( InventoryGroup == CurrentChoice.InventoryGroup )
        {
            if ( GroupOffset > CurrentChoice.GroupOffset )
                CurrentChoice = self;
        }
        else if ( InventoryGroup > CurrentChoice.InventoryGroup )
        {
			if ( (InventoryGroup < CurrentWeapon.InventoryGroup)
                || (CurrentChoice.InventoryGroup > CurrentWeapon.InventoryGroup) )
                CurrentChoice = self;
        }
        else if ( (CurrentChoice.InventoryGroup > CurrentWeapon.InventoryGroup)
                && (InventoryGroup < CurrentWeapon.InventoryGroup) )
            CurrentChoice = self;
    }
    if ( Inventory == None )
        return CurrentChoice;
    else
        return Inventory.PrevWeapon(CurrentChoice,CurrentWeapon);
}

simulated function Weapon NextWeapon(Weapon CurrentChoice, Weapon CurrentWeapon)
{
    if ( HasAmmo() )
    {
        if ( (CurrentChoice == None) )
        {
            if ( CurrentWeapon != self )
                CurrentChoice = self;
        }
        else if ( InventoryGroup == CurrentWeapon.InventoryGroup )
        {
            if ( (GroupOffset > CurrentWeapon.GroupOffset)
                && ((CurrentChoice.InventoryGroup != InventoryGroup) || (GroupOffset < CurrentChoice.GroupOffset)) )
                CurrentChoice = self;
        }
        else if ( InventoryGroup == CurrentChoice.InventoryGroup )
        {
			if ( GroupOffset < CurrentChoice.GroupOffset )
                CurrentChoice = self;
        }

        else if ( InventoryGroup < CurrentChoice.InventoryGroup )
        {
            if ( (InventoryGroup > CurrentWeapon.InventoryGroup)
                || (CurrentChoice.InventoryGroup < CurrentWeapon.InventoryGroup) )
                CurrentChoice = self;
        }
        else if ( (CurrentChoice.InventoryGroup < CurrentWeapon.InventoryGroup)
                && (InventoryGroup > CurrentWeapon.InventoryGroup) )
            CurrentChoice = self;
    }
    if ( Inventory == None )
        return CurrentChoice;
    else
        return Inventory.NextWeapon(CurrentChoice,CurrentWeapon);
}


function HolderDied()
{
    local int m;

    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
		// if _RO_
		if( FireMode[m] == none )
			continue;
		// End _RO_

        if (FireMode[m].bIsFiring)
        {
            StopFire(m);
            if (FireMode[m].bFireOnRelease)
                FireMode[m].ModeDoFire();
        }
    }
}

simulated function bool CanThrow()
{
	local int Mode;

    for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
    {
    	// if _RO_
		if( FireMode[Mode] == none )
			continue;
		// End _RO_

	    if ( FireMode[Mode].bFireOnRelease && FireMode[Mode].bIsFiring )
            return false;
        if ( FireMode[Mode].NextFireTime > Level.TimeSeconds)
			return false;
    }
    return (bCanThrow && (ClientState == WS_ReadyToFire || (Level.NetMode == NM_DedicatedServer) || (Level.NetMode == NM_ListenServer))
			&& HasAmmo() );
}

function DropFrom(vector StartLocation)
{
    local int m;
	local Pickup Pickup;

    if (!bCanThrow || !HasAmmo())
        return;

    ClientWeaponThrown();

    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
    	// if _RO_
		if( FireMode[m] == none )
			continue;
		// End _RO_

        if (FireMode[m].bIsFiring)
            StopFire(m);
    }

	if ( Instigator != None )
	{
		DetachFromPawn(Instigator);
	}

	Pickup = Spawn(PickupClass,,, StartLocation);
	if ( Pickup != None )
	{
    	Pickup.InitDroppedPickupFor(self);
	    Pickup.Velocity = Velocity;
        if (Instigator.Health > 0)
            WeaponPickup(Pickup).bThrown = true;
    }

    Destroy();
}

simulated function DetachFromPawn(Pawn P)
{
	Super.DetachFromPawn(P);
	P.AmbientSound = None;
}

simulated function ClientWeaponThrown()
{
    local int m;

    AmbientSound = None;
    Instigator.AmbientSound = None;

    if( Level.NetMode != NM_Client )
        return;

    Instigator.DeleteInventory(self);
    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
        if (Ammo[m] != None)
            Instigator.DeleteInventory(Ammo[m]);
    }
}

function GiveTo(Pawn Other, optional Pickup Pickup)
{
    local int m;
    local weapon w;
    local bool bPossiblySwitch, bJustSpawned;

    Instigator = Other;
    W = Weapon(Instigator.FindInventoryType(class));
    if ( W == None || W.Class != Class ) // added class check because somebody made FindInventoryType() return subclasses for some reason
    {
		bJustSpawned = true;
        Super.GiveTo(Other);
        bPossiblySwitch = true;
        W = self;
    }
    else if ( !W.HasAmmo() )
	    bPossiblySwitch = true;

    if ( Pickup == None )
        bPossiblySwitch = true;

    for (m = 0; m < NUM_FIRE_MODES; m++)
    {
        if ( FireMode[m] != None )
        {
            FireMode[m].Instigator = Instigator;
            W.GiveAmmo(m,WeaponPickup(Pickup),bJustSpawned);
        }
    }

	if ( Instigator.Weapon != W )
		W.ClientWeaponSet(bPossiblySwitch);

    if ( !bJustSpawned )
	{
        for (m = 0; m < NUM_FIRE_MODES; m++)
            Ammo[m] = None;
		Destroy();
	}
}

function GiveAmmo(int m, WeaponPickup WP, bool bJustSpawned)
{
    local bool bJustSpawnedAmmo;
    local int addAmount, InitialAmount;

    if ( FireMode[m] != None && FireMode[m].AmmoClass != None )
    {
        Ammo[m] = Ammunition(Instigator.FindInventoryType(FireMode[m].AmmoClass));
		bJustSpawnedAmmo = false;

		if ( bNoAmmoInstances )
		{
			if ( (FireMode[m].AmmoClass == None) || ((m != 0) && (FireMode[m].AmmoClass == FireMode[0].AmmoClass)) )
				return;

			InitialAmount = FireMode[m].AmmoClass.Default.InitialAmount;
			if ( (WP != None) && ((WP.AmmoAmount[0] > 0) || (WP.AmmoAmount[1] > 0))  )
			{
				InitialAmount = WP.AmmoAmount[m];
			}

			if ( Ammo[m] != None )
			{
				addamount = InitialAmount + Ammo[m].AmmoAmount;
				Ammo[m].Destroy();
			}
			else
				addAmount = InitialAmount;

			AddAmmo(addAmount,m);
		}
		else
		{
			if ( (Ammo[m] == None) && (FireMode[m].AmmoClass != None) )
			{
				Ammo[m] = Spawn(FireMode[m].AmmoClass, Instigator);
				Instigator.AddInventory(Ammo[m]);
				bJustSpawnedAmmo = true;
			}
			else if ( (m == 0) || (FireMode[m].AmmoClass != FireMode[0].AmmoClass) )
				bJustSpawnedAmmo = ( bJustSpawned || ((WP != None) && !WP.bWeaponStay) );

			if ( (WP != None) && ((WP.AmmoAmount[0] > 0) || (WP.AmmoAmount[1] > 0))  )
			{
				addAmount = WP.AmmoAmount[m];
			}
			else if ( bJustSpawnedAmmo )
			{
				addAmount = Ammo[m].InitialAmount;
			}

			Ammo[m].AddAmmo(addAmount);
			Ammo[m].GotoState('');
		}
    }
}

simulated function ClientWeaponSet(bool bPossiblySwitch)
{
    local int Mode;

    Instigator = Pawn(Owner);

    bPendingSwitch = bPossiblySwitch;

    if( Instigator == None )
    {
        GotoState('PendingClientWeaponSet');
        return;
    }

    for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
    {
        if( FireModeClass[Mode] != None )
        {
			// laurent -- added check for vehicles (ammo not replicated but unlimited)
            if( ( FireMode[Mode] == None ) || ( FireMode[Mode].AmmoClass != None ) && !bNoAmmoInstances && Ammo[Mode] == None && FireMode[Mode].AmmoPerFire > 0 )
            {
                GotoState('PendingClientWeaponSet');
                return;
            }
        }

        FireMode[Mode].Instigator = Instigator;
        FireMode[Mode].Level = Level;
    }

    ClientState = WS_Hidden;
    GotoState('Hidden');

    if( Level.NetMode == NM_DedicatedServer || !Instigator.IsHumanControlled() )
        return;

    if( Instigator.Weapon == self || Instigator.PendingWeapon == self ) // this weapon was switched to while waiting for replication, switch to it now
    {
		if (Instigator.PendingWeapon != None)
            Instigator.ChangedWeapon();
        else
            BringUp();
        return;
    }

    if( Instigator.PendingWeapon != None && Instigator.PendingWeapon.bForceSwitch )
        return;

    if( Instigator.Weapon == None )
    {
        Instigator.PendingWeapon = self;
        Instigator.ChangedWeapon();
    }
    else if ( bPossiblySwitch && !Instigator.Weapon.IsFiring() )
    {
		if ( PlayerController(Instigator.Controller) != None && PlayerController(Instigator.Controller).bNeverSwitchOnPickup )
			return;
        if ( Instigator.PendingWeapon != None )
        {
            if ( RateSelf() > Instigator.PendingWeapon.RateSelf() )
            {
                Instigator.PendingWeapon = self;
                Instigator.Weapon.PutDown();
            }
        }
        else if ( RateSelf() > Instigator.Weapon.RateSelf() )
        {
            Instigator.PendingWeapon = self;
            Instigator.Weapon.PutDown();
        }
    }
}

// jdf ---
simulated function ClientPlayForceFeedback( String EffectName )
{
    local PlayerController PC;

    PC = PlayerController(Instigator.Controller);
    if ( PC != None && PC.bEnableWeaponForceFeedback )
    {
        PC.ClientPlayForceFeedback( EffectName );
    }
}

simulated function StopForceFeedback( String EffectName )
{
    local PlayerController PC;

    PC = PlayerController(Instigator.Controller);
    if ( PC != None && PC.bEnableWeaponForceFeedback )
    {
        PC.StopForceFeedback( EffectName );
    }
}
// --- jdf

simulated function BringUp(optional Weapon PrevWeapon)
{
   local int Mode;

    if ( ClientState == WS_Hidden )
    {
        PlayOwnedSound(SelectSound, SLOT_Interact,,,,, false);
		ClientPlayForceFeedback(SelectForce);  // jdf

        if ( Instigator.IsLocallyControlled() )
        {
            if ( (Mesh!=None) && HasAnim(SelectAnim) )
                PlayAnim(SelectAnim, SelectAnimRate, 0.0);
        }

        ClientState = WS_BringUp;
        SetTimer(BringUpTime, false);
    }
    for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
	{
		FireMode[Mode].bIsFiring = false;
		FireMode[Mode].HoldTime = 0.0;
		FireMode[Mode].bServerDelayStartFire = false;
		FireMode[Mode].bServerDelayStopFire = false;
		FireMode[Mode].bInstantStop = false;
	}
	   if ( (PrevWeapon != None) && PrevWeapon.HasAmmo() && !PrevWeapon.bNoVoluntarySwitch )
		OldWeapon = PrevWeapon;
	else
		OldWeapon = None;

}

simulated function bool PutDown()
{
    local int Mode;

    if (ClientState == WS_BringUp || ClientState == WS_ReadyToFire)
    {
        if ( (Instigator.PendingWeapon != None) && !Instigator.PendingWeapon.bForceSwitch )
        {
            for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
            {
		    	// if _RO_
				if( FireMode[Mode] == none )
					continue;
				// End _RO_

                if ( FireMode[Mode].bFireOnRelease && FireMode[Mode].bIsFiring )
                    return false;
                if ( FireMode[Mode].NextFireTime > Level.TimeSeconds + FireMode[Mode].FireRate*(1.f - MinReloadPct))
					DownDelay = FMax(DownDelay, FireMode[Mode].NextFireTime - Level.TimeSeconds - FireMode[Mode].FireRate*(1.f - MinReloadPct));
            }
        }

        if (Instigator.IsLocallyControlled())
        {
            for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
            {
		    	// if _RO_
				if( FireMode[Mode] == none )
					continue;
				// End _RO_

                if ( FireMode[Mode].bIsFiring )
                    ClientStopFire(Mode);
            }

            if (  DownDelay <= 0 )
            {
				if ( ClientState == WS_BringUp )
					TweenAnim(SelectAnim,PutDownTime);
				else if ( HasAnim(PutDownAnim) )
					PlayAnim(PutDownAnim, PutDownAnimRate, 0.0);
			}
        }
        ClientState = WS_PutDown;
        if ( Level.GRI.bFastWeaponSwitching )
			DownDelay = 0;
        if ( DownDelay > 0 )
			SetTimer(DownDelay, false);
		else
			SetTimer(PutDownTime, false);
    }
    for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
    {
    	// if _RO_
		if( FireMode[Mode] == none )
			continue;
		// End _RO_

		FireMode[Mode].bServerDelayStartFire = false;
		FireMode[Mode].bServerDelayStopFire = false;
	}
    Instigator.AmbientSound = None;
    OldWeapon = None;
    return true; // return false if preventing weapon switch
}

simulated function Fire(float F)
{
}

simulated function AltFire(float F)
{
}

simulated event WeaponTick(float dt); // only called on currently held weapon

simulated function OutOfAmmo()
{
    if ( Instigator == None || !Instigator.IsLocallyControlled() || HasAmmo() )
        return;

    DoAutoSwitch();
}

simulated function DoAutoSwitch()
{
    Instigator.Controller.SwitchToBestWeapon();
}

//// client only ////
simulated event ClientStartFire(int Mode)
{
    if ( Pawn(Owner).Controller.IsInState('GameEnded') || Pawn(Owner).Controller.IsInState('RoundEnded') )
        return;
    if (Role < ROLE_Authority)
    {
        if (StartFire(Mode))
        {
            ServerStartFire(Mode);
        }
    }
    else
    {
        StartFire(Mode);
    }
}

simulated event ClientStopFire(int Mode)
{
    if (Role < ROLE_Authority)
    {
        //Log("ClientStopFire"@Level.TimeSeconds);
        StopFire(Mode);
    }
    ServerStopFire(Mode);
}

simulated function ClientWriteFire(string Result)
{
	log(self$" ServerStartFire! "$Result);
}

//// server only ////
event ServerStartFire(byte Mode)
{
	if ( (Instigator != None) && (Instigator.Weapon != self) )
	{
		if ( Instigator.Weapon == None )
			Instigator.ServerChangedWeapon(None,self);
		else
			Instigator.Weapon.SynchronizeWeapon(self);
		return;
	}

    if ( (FireMode[Mode].NextFireTime <= Level.TimeSeconds + FireMode[Mode].PreFireTime)
		&& StartFire(Mode) )
    {
        FireMode[Mode].ServerStartFireTime = Level.TimeSeconds;
        FireMode[Mode].bServerDelayStartFire = false;
    }
    else if ( FireMode[Mode].AllowFire() )
    {
        FireMode[Mode].bServerDelayStartFire = true;
	}
	else
		ClientForceAmmoUpdate(Mode, AmmoAmount(Mode));
}

simulated function ClientForceAmmoUpdate(int Mode, int NewAmount)
{
	//log(self$" ClientForceAmmoUpdate mode "$Mode$" newamount "$NewAmount);
	if ( bNoAmmoInstances )
		AmmoCharge[Mode] = NewAmount;
	else if ( Ammo[mode] != None )
		Ammo[mode].AmmoAmount = NewAmount;
}

function SynchronizeWeapon(Weapon ClientWeapon)
{
	Instigator.ServerChangedWeapon(self,ClientWeapon);
}

function ServerStopFire(byte Mode)
{
    // if a stop was received on the same frame as a start then we need to delay the stop for one frame
    if (FireMode[Mode].bServerDelayStartFire || FireMode[Mode].ServerStartFireTime == Level.TimeSeconds)
    {
        //log("Stop Delayed");
        FireMode[Mode].bServerDelayStopFire = true;
    }
    else
    {
        //Log("ServerStopFire"@Level.TimeSeconds);
        StopFire(Mode);
    }
}

simulated function bool ReadyToFire(int Mode)
{
    local int alt;

    if ( Mode == 0 )
        alt = 1;
    else
        alt = 0;

    if ( ((FireMode[alt] != FireMode[Mode]) && FireMode[alt].bModeExclusive && FireMode[alt].bIsFiring)
		|| !FireMode[Mode].AllowFire()
		|| (FireMode[Mode].NextFireTime > Level.TimeSeconds + FireMode[Mode].PreFireTime) )
    {
        return false;
    }

	return true;
}

//// client & server ////
simulated function bool StartFire(int Mode)
{
    local int alt;

    if (!ReadyToFire(Mode))
        return false;

    if (Mode == 0)
        alt = 1;
    else
        alt = 0;

    FireMode[Mode].bIsFiring = true;
    FireMode[Mode].NextFireTime = Level.TimeSeconds + FireMode[Mode].PreFireTime;

    if (FireMode[alt].bModeExclusive)
    {
        // prevents rapidly alternating fire modes
        FireMode[Mode].NextFireTime = FMax(FireMode[Mode].NextFireTime, FireMode[alt].NextFireTime);
    }

    if (Instigator.IsLocallyControlled())
    {
        if (FireMode[Mode].PreFireTime > 0.0 || FireMode[Mode].bFireOnRelease)
        {
            FireMode[Mode].PlayPreFire();
        }
        FireMode[Mode].FireCount = 0;
    }

    return true;
}

simulated event StopFire(int Mode)
{
	if ( FireMode[Mode].bIsFiring )
	    FireMode[Mode].bInstantStop = true;
    if (Instigator.IsLocallyControlled() && !FireMode[Mode].bFireOnRelease)
        FireMode[Mode].PlayFireEnd();

    FireMode[Mode].bIsFiring = false;
    FireMode[Mode].StopFiring();
    if (!FireMode[Mode].bFireOnRelease)
        ZeroFlashCount(Mode);
}

//hack to stop all firing and release any charging firemodes RIGHT THIS INSTANT
//used when getting into vehicles
simulated function ImmediateStopFire()
{
	local int i;

	for (i = 0; i < NUM_FIRE_MODES; i++)
	{
		ClientStopFire(i);
		if (FireMode[i].bFireOnRelease)
		{
        		if (Level.TimeSeconds > FireMode[i].NextFireTime && (FireMode[i].bInstantStop || !FireMode[i].bNowWaiting))
				FireMode[i].ModeDoFire();
			if (FireMode[i].bWaitForRelease)
				FireMode[i].bNowWaiting = true;
		}
	}
}

simulated function Timer()
{
	local int Mode;
	local float OldDownDelay;

	OldDownDelay = DownDelay;
	DownDelay = 0;

    if (ClientState == WS_BringUp)
    {
		for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
	       FireMode[Mode].InitEffects();
        PlayIdle();
        ClientState = WS_ReadyToFire;
    }
    else if (ClientState == WS_PutDown)
    {
        if ( OldDownDelay > 0 )
        {
            if ( HasAnim(PutDownAnim) )
                PlayAnim(PutDownAnim, PutDownAnimRate, 0.0);
			SetTimer(PutDownTime, false);
			return;
		}
		if ( Instigator.PendingWeapon == None )
		{
			PlayIdle();
			ClientState = WS_ReadyToFire;
		}
		else
		{
			ClientState = WS_Hidden;
			Instigator.ChangedWeapon();
			if ( Instigator.Weapon == self )
			{
				PlayIdle();
				ClientState = WS_ReadyToFire;
			}
			else
			{
				for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
					FireMode[Mode].DestroyEffects();
			}
		}
    }
}


simulated function bool IsFiring() // called by pawn animation, mostly
{
    if (Level.NetMode == NM_DedicatedServer || Level.NetMode == NM_ListenServer)
        return (FireMode[0].IsFiring() || FireMode[1].IsFiring());

    return  ( ClientState == WS_ReadyToFire && (FireMode[0].IsFiring() || FireMode[1].IsFiring()) );
}

function bool IsRapidFire() // called by pawn animation
{
    if (FireMode[1] != None && FireMode[1].bIsFiring)
        return FireMode[1].bPawnRapidFireAnim;
    else if (FireMode[0] != None)
        return FireMode[0].bPawnRapidFireAnim;
    else
        return false;
}

// called every time owner takes damage while holding this weapon - used by shield gun
function AdjustPlayerDamage( out int Damage, Pawn InstigatedBy, Vector HitLocation,
                             out Vector Momentum, class<DamageType> DamageType)
{
}


simulated function CheckSuperBerserk()
{
	if ( Level.GRI.WeaponBerserk > 1.0 )
	{
		if (FireMode[0] != None)
			FireMode[0].StartSuperBerserk();
		if (FireMode[1] != None)
			FireMode[1].StartSuperBerserk();
	}
}
simulated function StartBerserk()
{
	if ( (Level.GRI != None) && Level.GRI.WeaponBerserk > 1.0 )
		return;
	bBerserk = true;
    if (FireMode[0] != None)
        FireMode[0].StartBerserk();
    if (FireMode[1] != None)
        FireMode[1].StartBerserk();
}

simulated function StopBerserk()
{
	bBerserk = false;
	if ( (Level.GRI != None) && Level.GRI.WeaponBerserk > 1.0 )
		return;
    if (FireMode[0] != None)
        FireMode[0].StopBerserk();
    if (FireMode[1] != None)
        FireMode[1].StopBerserk();
}

simulated function AnimEnd(int channel)
{
    local name anim;
    local float frame, rate;

    GetAnimParams(0, anim, frame, rate);

    if (ClientState == WS_ReadyToFire)
    {
        if (anim == FireMode[0].FireAnim && HasAnim(FireMode[0].FireEndAnim)) // rocket hack
        {
            PlayAnim(FireMode[0].FireEndAnim, FireMode[0].FireEndAnimRate, 0.0);
        }
        else if (anim== FireMode[1].FireAnim && HasAnim(FireMode[1].FireEndAnim))
        {
            PlayAnim(FireMode[1].FireEndAnim, FireMode[1].FireEndAnimRate, 0.0);
        }
        else if ((FireMode[0] == None || !FireMode[0].bIsFiring) && (FireMode[1] == None || !FireMode[1].bIsFiring))
        {
            PlayIdle();
        }
    }
}

simulated function PlayIdle()
{
    LoopAnim(IdleAnim, IdleAnimRate, 0.2);
}

state PendingClientWeaponSet
{
    simulated function Timer()
    {
        if ( Pawn(Owner) != None )
            ClientWeaponSet(bPendingSwitch);
        if ( IsInState('PendingClientWeaponSet') )
			SetTimer(0.05, false);
    }

    simulated function BeginState()
    {
        SetTimer(0.05, false);
    }

    simulated function EndState()
    {
    }
}

state Hidden
{
}

function bool CheckReflect( Vector HitLocation, out Vector RefNormal, int AmmoDrain )
{
    return false;
}

function DoReflectEffect(int Drain)
{

}

function bool HandlePickupQuery( pickup Item )
{
    local WeaponPickup wpu;
	local int i;

	if ( bNoAmmoInstances )
	{
		// handle ammo pickups
		for ( i=0; i<2; i++ )
		{
			if ( (item.inventorytype == AmmoClass[i]) && (AmmoClass[i] != None) )
			{
				if ( AmmoCharge[i] >= MaxAmmo(i) )
					return true;
				item.AnnouncePickup(Pawn(Owner));
				AddAmmo(Ammo(item).AmmoAmount, i);
				item.SetRespawn();
				return true;
			}
		}
	}

	if (class == Item.InventoryType)
    {
        wpu = WeaponPickup(Item);
        if (wpu != None)
            return !wpu.AllowRepeatPickup();
        else
            return false;
    }

    if ( Inventory == None )
		return false;

	return Inventory.HandlePickupQuery(Item);
}

simulated function bool WantsZoomFade()
{
	return false;
}

/* CanHeal()
used by bot AI
should return true if this weapon is able to heal Other
*/
function bool CanHeal(Actor Other)
{
	return false;
}

//called by AI when camping/defending
//return true if it is useful to fire this weapon even though bot doesn't have a target
//for example, a weapon that launches turrets or mines
function bool ShouldFireWithoutTarget()
{
	return false;
}

// ugly hack for tutorial
function bool ShootHoop(Controller B, Vector ShootLoc)
{
	return false;
}

simulated function PawnUnpossessed();


// FIXME - hack to get classes building again after fixing the bug that was allowing all protected variables to
// be referenced in other classes - maybe there's already an accessor that does this and I just don't know about it
// ...in that case, just remove this function -- rjp
simulated function WeaponFire GetFireMode( byte Mode )
{
	if ( Mode < NUM_FIRE_MODES )
		return FireMode[Mode];

	return None;
}

defaultproperties
{
    DrawType=DT_Mesh
    Style=STY_Normal
    PlayerViewOffset=(X=0,Y=0,Z=0)
    InventoryGroup=1

    FireModeClass(0)=None
    FireModeClass(1)=None

    // animation //
    IdleAnim=Idle
    RestAnim=Rest
    AimAnim=Aim
    RunAnim=Run
    SelectAnim=Select
    PutDownAnim=Down

    IdleAnimRate=1.0
    RestAnimRate=1.0
    AimAnimRate=1.0
    RunAnimRate=1.0

    // other useful stuff //
    MessageNoAmmo=" has no ammo"
    DisplayFOV=90.0
    bCanThrow=true

    AIRating=0.5
    CurrentRating=0.5

    AttachmentClass=class'WeaponAttachment'

    NetPriority=3.0
    ScaleGlow=1.5
    AmbientGlow=20
    MaxLights=6
    SoundVolume=255
    HudColor=(r=255,g=255,b=0,a=255)

    CenteredOffsetY=-10.0
    CenteredRoll=2000
	CenteredYaw=0

	bUseOldWeaponMesh=False
	ExchangeFireModes=0
	CustomCrosshair=0
	CustomCrossHairColor=(R=255,G=255,B=255,A=255)
	CustomCrossHairScale=+1.0
	CustomCrossHairTexture=None
// if _RO_
	CustomCrossHairTextureName="InterfaceArt_tex.Cursors.Crosshair_Cross2"
// else
//	CustomCrossHairTextureName="Crosshairs.Hud.Crosshair_Cross1"
// end if _RO_

    OldCenteredOffsetY=-10.0
    OldCenteredRoll=2000
	OldCenteredYaw=0
	OldDrawScale=+1.0

    SelectAnimRate=1.3636
    PutDownAnimRate=1.3636
	BringUpTime=+0.33
	PutDownTime=+0.33

	MinReloadPct=+0.5
	NetUpdateFrequency=2

	bNoAmmoInstances=true
    bNetNotify=true
}
