//=============================================================================
// KFAIController_Hans
//=============================================================================
// Hans's AIController
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================

class KFAIController_Hans extends KFAIController_ZedBoss
	native(AI);

/*********************************************************************************************
* Initialization
********************************************************************************************* */
/** Cached copy of Hans pawn to avoid casting */
var transient KFPawn_ZedHansBase	MyHansPawn;

/** Destination navigation point Hans wants to throw a grenade from */
//var NavigationPoint RangedAttackMoveGoal;
var vector          RangedAttackMoveLoc;
var bool			bFoundGrenadeThrowLocation;
var bool			bWantsToThrowGrenade;

var vector          CalcedGreanadeTossVelForNavMeshLoc;
var float           TimeCalcedGreanadeTossVelWasFound;
var vector          EnemysLocationWhenTossVelWasFound;

/*********************************************************************************************
* Sprint behavior
**********************************************************************************************/

/** How long Hans should wait to start sprinting after losing sight of his enemy */
var             float           LostSightSprintDelay;

/*********************************************************************************************
* Gun shooting related variables brought over from AICommand_HansGunAttack.
* Put here because the shoot AICommand keeps getting pushed onto the stack and
* resetting these vars
**********************************************************************************************/

/** The time a new barrage started */
var             float           BarrageStartTime;
/** The last time we started firing our gun */
var             float           LastFireTime;
/** The time we started drawing our gun */
var             float			StartDrawGunsTime;
/** The time we started this gun attack */
var             float			StartGunAttackTime;
/** Cache our last target */
var             Actor           LastTarget;
/** The time we first saw our current target we are shooting at */
var             float			TimeFirstSawTarget;

/** Last time we changed weapon stances */
var             float			LastStanceChangeTime;
/** Can't change stances more often than this */
var()   	    float			StanceChangeCooldown;

/** Last time we finished a melee attack */
var             float			LastAttackMoveFinishTime;
/** Wait this long to do a gun attack after doing a melee attack */
var()   	    float			PostAttackMoveGunCooldown;

/*********************************************************************************************
* Enemy Tracking
**********************************************************************************************/

/**
 *	Struct to track AI knowledge of visible enemies
 */
struct native TrackedEnemyInfo
{
	/** Enemy we're tracking */
	var KFPawn         TrackedEnemy;
	/** Enemy we're tracking */
	var float          LastTimeVisible;
	/** Enemy we're tracking */
	var vector         LastVisibleLocation;
	/** The last time we fired on this enemy */
	var float          LastTimeFiredOn;
	/** The last time we tossed a grenade at this enemy */
	var float          LastTimeGrenadeAttacked;
	/** How often we've chosen this enemy as a target recently */
	var int            NumTimesEngagedRecently;
};

/** List of tracked enemies that have recently been seen */
var array<TrackedEnemyInfo> RecentlySeenEnemyList;

/** The last enemy we gunned at that was recently seen */
var KFPawn LastRecentlySeenEnemyGunned;

/** The last enemy we grenaded that was recently seen */
var KFPawn LastRecentlySeenEnemyGrenaded;

/** How often to update RecentlySeenEnemyList */
var() float RecentSeenEnemyListUpdateInterval;

/** Last time we updated the RecentlySeenEnemyList*/
var float LastRecentSeenEnemyListUpdateTime;

/** The last time we changed to a new target */
var float LastRetargetTime;

/** How long to wait before attempting to find a new target */
var vector2d RetargetWaitTimeRange;

/** The actual retarget wait time after last retarget time has been set */
var transient float ActualRetargetWaitTime;

/*********************************************************************************************
* Vars for handing the timing of firing, when to shoot, the shooting cadence, etc
**********************************************************************************************/
/** Min length of a burst for this weapon when used by AI. Randomly used to calculate the BurstAmount */
var(Firing)	    int				MinBurstAmount;
/** Max length of a burst for this weapon when used by AI. Randomly used to calculate the BurstAmount */
var(Firing)	    int				MaxBurstAmount;
/** How long to wait between bursts when this weapon is used by AI */
var(Firing)	    float			BurstWaitTime;
/** How long to wait between barrages (groups of bursts) when this weapon is used by AI */
var(Firing)	    float			BarrageWaitTime;

/** Interp curve to scale how long Hans waits between barrages for different ranges **/
var() InterpCurveFloat BarrageWaitDistanceCurve;

/** Interp curve to scale how long Hans waits between bursts for different ranges **/
var() InterpCurveFloat BurstWaitDistanceCurve;

/** How long a barrage (groups of bursts) will last */
var(Firing)	    float			BarrageLength;
/** Tracks the current burst and what size it is */
var     	    int				BurstAmount;
/** How long to wait to stop firing after losing sight of an enemy */
var(Firing)	    float			LostSightStopFireDelay;
/** The fire mode the last time we fired our gun */
var             float           LastFireMode;
/** How long to wait after starting to draw our guns to fire */
var(Firing)	    float			DrawGunFireDelay;
/** How long to wait after starting to see an enemy to fire */
var(Firing)	    float			TargetAquisitionDelay;
/** How long this gun attack should last at the max */
var(Firing)	    float			MaxGunAttackLength;
/** How long to wait after drawing the guns before allowing a grenade toss to interrupt them */
var(Firing)	    float			GrenadeGunInterruptDelay;

/** The range the zed needs to be within to start shooting bullets */
var float StartShootingRange;

/** The range the zed needs to be outside to disallow shooting bullets */
var float MinShootingRange;

/** min delay between shooting attacks */
var float ShootingCooldown;

enum EHansNadeType
{
    HNT_None,
    HNT_HEGrenade,
    HNT_NerveGas,
    HNT_Smoke,
    HNT_HEGrenadeBarrage,
    HNT_NerveGasBarrage,
    HNT_SmokeBarrage,
};

/* The grenade attack type we have queued up to do */
var EHansNadeType CurrentNadeAttackType;

/* How often to check if we want to do a grenade attack */
var() float GrenadeAttackEvalInterval;

/* Last time we evaluated if we wanted to do a grenade attack*/
var float LastGrenadeAttackEvalTime;

/** Whether we've already thrown a smoke grenade barrage this hunt and heal phase */
var bool bHasUsedSmokeScreenThisPhase;

/*********************************************************************************************
* Fleeing
**********************************************************************************************/

/** TRUE if flee is in progress */
var bool bFleeing;

/** TRUE if flee was interrupted (to attack, etc) */
var bool bWantsToFlee;

/** Set to true once the flee has been completed */
var bool bHasFledThisPhase;

/** How long we should flee */
var float MinFleeDuration, MaxFleeDuration;

/** Maximum flee distance */
var float MaxFleeDistance;

/** The start time of the last flee */
var float FleeStartTime;

/** The total cumulative time spent fleeing */
var float TotalFleeTime;

/** Whether the flee was interrupted by the targeting loop */
var bool bFleeInterrupted;

cpptext
{
	/** Overridden to allow grab attack check */
	virtual UBOOL TickMeleeCombatDecision( FLOAT DeltaTime );
}

/*********************************************************************************************
* Initialization, Pawn Possession, and Destruction
********************************************************************************************* */

/** Set MyHansPawn to avoid casting */
event Possess( Pawn inPawn, bool bVehicleTransition )
{
	if( KFPawn_ZedHansBase(inPawn) != none )
	{
		MyHansPawn = KFPawn_ZedHansBase( inPawn );
	}
	else
	{
		`warn( GetFuncName()$"() attempting to possess "$inPawn$", but it's not a KFPawn_ZedHansBase class! MyHansPawn variable will not be valid." );
	}

    // Initialize retarget time
    LastRetargetTime = WorldInfo.TimeSeconds;
    ActualRetargetWaitTime = RandRange( RetargetWaitTimeRange.X, RetargetWaitTimeRange.Y );

	super.Possess( inPawn, bVehicleTransition );
}

function PawnDied( Pawn InPawn )
{
	if( MyHansPawn != none )
	{
		`DialogManager.PlayBossDeathDialog( MyHansPawn );
		MyHansPawn = None;
	}
	Super.PawnDied( InPawn );
}

/** Clean up all internal objects and references when the AI is destroyed
	TBD: Look into using Unpossessed() to do this! */
simulated event Destroyed()
{
	MyHansPawn = None;

	super.Destroyed();
}

/** Overridden to allow grab attack check */
/*
function DoMeleeAttack( optional Pawn NewEnemy, optional Actor InTarget, optional byte AttackFlags )
{
	local float DistSq;

	if( CommandList == None || AICommand(CommandList).bAllowedToAttack )
	{
		DistSq = VSizeSq( Enemy.Location - Pawn.Location );

		if( DistSq < 32400.f || DistSq > 10000.f )
		{
			if( CanGrabAttack() )
			{
				//	DumpCommandStack();
				DoGrabAttack();
				return;
			}
		}
	}
	super.DoMeleeAttack( NewEnemy, inTarget, AttackFlags );
}
*/

/** Start the throw grenade command, optionally for the grenade barrage attack */
function DoGrenadeThrow( optional bool bGrenadeBarrage )
{
    if( MyHansPawn != none )
    {
        MyHansPawn.SetActiveGrenadeClassGrenade();
    }

	if( class'AICommand_ThrowGrenade'.static.ThrowGrenade(self, bGrenadeBarrage ? 2 : 0) )
	{
        if( MyHansPawn != none )
        {
            MyHansPawn.BarrageTossCount=0;
            MyHansPawn.bDoingBarrage=bGrenadeBarrage;
        }
	}
}

/** Start the throw grenade command with smoke, optionally for the grenade barrage attack */
function DoSmokeGrenadeThrow( optional bool bGrenadeBarrage, optional bool bIsHuntAndHeal )
{
    local bool bThrowSuccess;

    if( MyHansPawn != none )
    {
        MyHansPawn.SetActiveGrenadeClassSmoke();
    }

    // Spawn some zeds if we need to
    bThrowSuccess = class'AICommand_ThrowGrenade'.static.ThrowGrenade(self, bGrenadeBarrage ? 1 : 0, bIsHuntAndHeal);

	if( bThrowSuccess )
	{
        if( MyHansPawn != none )
        {
            MyHansPawn.bPendingSmokeGrenadeBarrage=false;
            MyHansPawn.BarrageTossCount=0;
            MyHansPawn.bDoingBarrage=bGrenadeBarrage;
        }
	}
}

/** Notification from the pawn that it has taken damage */
function NotifyTakeHit( Controller InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum )
{
    local float HealThreshold, HealthPct;

    super.NotifyTakeHit( InstigatedBy, HitLocation, Damage, damageType, Momentum );

    // When our health gets low, go hunt a player to draw life and enter the next battle phase
    if( !MyHansPawn.bHealedThisPhase && MyHansPawn.CurrentBattlePhase < 4 )
    {
        HealThreshold = MyHansPawn.BattlePhases[MyHansPawn.CurrentBattlePhase-1].HealThresholds[WorldInfo.Game.GetModifiedGameDifficulty()];
        HealthPct = GetHealthPercentage();

        // Summon minions if we haven't yet
        if( !bSummonedThisPhase && HealthPct < HealThreshold + 0.18f ) //0.16
        {
            MyHansPawn.SummonMinions();
            bSummonedThisPhase = true;

            // Fallback so zeds can't spawn forever
            SetTimer( 30.f, false, nameOf(Timer_StopSummoningZeds) );
        }

        // Enter hunt and heal mode if we've dropped below the health threshold
        if( HealthPct < HealThreshold )
        {
            MyHansPawn.SetHuntAndHealMode( true );
        }
    }
}

/** Special move has ended, whether cleanly or aborted */
function NotifySpecialMoveEnded(KFSpecialMove SM)
{
    super.NotifySpecialMoveEnded(SM);

    // Our guns are put away, toss some grenades
    if( !bWantsToFlee && !bFleeing && Enemy != None && MyHansPawn != none && MyHansPawn.bPendingSmokeGrenadeBarrage )
    {
        DoSmokeGrenadeThrow( true, true );
    }

    if( SM.Handle == 'KFSM_MeleeAttack'
        || SM.Handle == 'KFSM_Hans_GrenadeBarrage'
        || SM.Handle == 'KFSM_Hans_GrenadeHalfBarrage'
        || SM.Handle == 'KFSM_Hans_ThrowGrenade'
        || SM.Handle == 'KFSM_Taunt'
        || SM.Handle == 'KFSM_WalkingTaunt' )
    {
        LastAttackMoveFinishTime = WorldInfo.TimeSeconds;
    }

    // Handle flee interruptions
    if( !bWantsToFlee )
    {
        // Retarget if it's been enough time since we changed targets
        if( SM.Handle == 'KFSM_MeleeAttack' && `TimeSince(LastRetargetTime) > ActualRetargetWaitTime )
        {
            CheckForEnemiesInFOV( 3000.f, -1.f, 1.f, true );
        }
    }
    else if( MyHansPawn.bGunsEquipped )
    {
        class'AICommand_Hans_GunStance'.static.SetGunStance( self, 0 );
    }
    else if( PendingDoor == none && !bFleeing )
    {
        Flee();

        // Play dialog indicating the next phase has started
        if( !bFleeInterrupted )
        {
            `DialogManager.PlayHansSmokeDialog( MyHansPawn, true );
        }
    }

    // Reset flee interrupted flag in case this was an attack special move
    bFleeInterrupted = false;

    // Evaluate sprinting whenever we finish a special move so sprinting will be snappy!
    EvaluateSprinting();
}

/** Evaluate if we should start/stop sprinting, and then set the sprinting flag */
function EvaluateSprinting()
{
    if( MyKFPawn == none || !MyKFPawn.IsAliveAndWell() )
    {
        return;
    }

    if( bFleeing || bWantsToFlee )
    {
        MyKFPawn.SetSprinting( true );
    }
    else if( Enemy != none )
	{
		if( ShouldSprint() )
		{
			MyKFPawn.SetSprinting( true );
		}
		else
		{
			MyKFPawn.SetSprinting( false );
		}
	}
}

/** NPC has seen a player - use SeeMonster for similar notifications about seeing any pawns (currently not in use) */
event SeePlayer( Pawn Seen )
{
    local int EnemyListIndex;
    local TrackedEnemyInfo NewTrackedEnemy;
    local KFPawn KFP;

    Super.SeePlayer(Seen);

	// Evaluate sprinting when visibility changes
    EvaluateSprinting();

	// Reject potential enemy if it's invalid or not on our team, or if it's already my current enemy, or if my pawn is dead or invalid
	if( Seen == none || !Seen.IsAliveAndWell() || Pawn.IsSameTeam( Seen ) ||
		Pawn == none || !Pawn.IsAliveAndWell() || !Seen.CanAITargetThisPawn(self) )
	{
		return;
	}

    KFP = KFPawn(Seen);

    // Add or update recently seen enemy in the list for tracking ranged enemies
    if( KFP != none )
    {
        EnemyListIndex = RecentlySeenEnemyList.Find('TrackedEnemy', KFP);

        if( EnemyListIndex == INDEX_NONE )
        {
            NewTrackedEnemy.TrackedEnemy = KFP;
            NewTrackedEnemy.LastTimeVisible = WorldInfo.TimeSeconds;
            NewTrackedEnemy.LastVisibleLocation = Seen.Location;
            RecentlySeenEnemyList[RecentlySeenEnemyList.Length] = NewTrackedEnemy;
        }
        else
        {
            RecentlySeenEnemyList[EnemyListIndex].LastTimeVisible = WorldInfo.TimeSeconds;
            RecentlySeenEnemyList[EnemyListIndex].LastVisibleLocation = Seen.Location;
        }
    }

    // Throw down our smoke screen before going after a player to heal
    if( bHasFledThisPhase && MyHansPawn.bInHuntAndHealMode && !bHasUsedSmokeScreenThisPhase )
    {
        bHasUsedSmokeScreenThisPhase = true;        
        SetTimer( 2.f + fRand(), false, nameOf(Timer_DoHuntAndHealSmokeGrenadeThrow) );
    }
}

/** Throws a smoke grenade barrage after waiting a short time */
function Timer_DoHuntAndHealSmokeGrenadeThrow()
{
    // Toss some smoke grenades out and summon some minions to help
    // while I try and find someont to suck some life from. If Hans
    // has his guns out, set the flag to wait til his guns are put
    // away before trying to throw the smoke grenade barrage and
    // Spawn the minion zeds
    if( !MyHansPawn.bGunsEquipped && !MyHansPawn.IsImpaired() && !MyHansPawn.IsIncapacitated() )
    {
        DoSmokeGrenadeThrow( true );
    }
    else
    {
        MyHansPawn.bPendingSmokeGrenadeBarrage = true;
    }
}

/** Start the throw grenade command with nerve gas, optionally for the grenade barrage attack */
function DoNerveGasGrenadeThrow( optional bool bGrenadeBarrage )
{
    if( MyHansPawn != none )
    {
        MyHansPawn.SetActiveGrenadeClassNerveGas();
    }

	if( class'AICommand_ThrowGrenade'.static.ThrowGrenade(self, bGrenadeBarrage ? 1 : 0) )
	{
        if( MyHansPawn != none )
        {
            MyHansPawn.BarrageTossCount=0;
            MyHansPawn.bDoingBarrage=bGrenadeBarrage;
        }
	}
}

/** Timer function called during latent moves that determines whether NPC should sprint or stop sprinting */
function bool ShouldSprint()
{
    local float RangeToEnemy;

	if( Enemy != none && MyHansPawn != none && !MyHansPawn.bIsHeadless && !MyHansPawn.bEmpPanicked )
	{
		// Always sprint if we're trying to track down players to heal from
        if( MyHansPawn.bInHuntAndHealMode )
		{
			//`log(self@GetFuncName()$" bInHuntAndHealMode should sprint!");
            return true;
		}

		// Sprint if we can't see our enemy
		if( LastEnemySightedTime == 0 || `TimeSince(LastEnemySightedTime) > LostSightSprintDelay )
		{
			//`log(self@GetFuncName()$" don't see any enemy should sprint = true! LastEnemySightedTime: "$LastEnemySightedTime$" TimeSince(LastEnemySightedTime): "$`TimeSince(LastEnemySightedTime));
            return true;
		}

        // No sprinting with the guns out!
        if( MyHansPawn.bGunsEquipped )
        {
            RangeToEnemy = VSize( Enemy.Location - Pawn.Location );

            // Sprint if we're too close to shoot
            if( RangeToEnemy > StartShootingRange*MyHansPawn.GetAttackRangeScale() || rangeToEnemy < MinShootingRange )
            {
                //`log(self@GetFuncName()$" guns out but enemy not in gun range. Should sprint = true!");
                return true;
            }
            else
            {
                //`log(self@GetFuncName()$" guns out enemy not in gun range. Should sprint = false!");
                return false;
            }
        }

        //`log(self@GetFuncName()$" Generic should sprint depending on phase: "$MyHansPawn.DesireSprintingInThisPhase());
        return MyHansPawn.DesireSprintingInThisPhase();
	}
	//`log(self@GetFuncName()$" Generic should sprint = false!");
	return false;
}

/*********************************************************************************************
* Combat
**********************************************************************************************/

/*********************************************************************************************
* Shooting
********************************************************************************************* */

/**
 * Handle the firing of the pawn's weapon
 */
function FireTimer()
{
    local float UsedBarrageWaitTime, UsedBurstWaitTime;

	// if we no longer have an Enemy or a pawn return
	if ( Enemy == none || Pawn == none )
	{
		// We've got nothing to Fire at
		ClearFireTiming();
		return;
	}

	// Lost sight of enemy, stop firing
	if( !CanSee(Enemy) && (WorldInfo.TimeSeconds - LastEnemySightedTime >= LostSightStopFireDelay) )
	{
		//`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() Can't see the enemy, stopping shooting TimeDiff: "$`TimeSince(LastEnemySightedTime)$" LostSightStopFireDelay = "$LostSightStopFireDelay);
		ClearFireTiming();
		TimeFirstSawTarget=0;
        // Clear any previous barrage so we start firing again immediately after reacquiring the target
        BarrageStartTime = 0;
		return;
	}

    // Don't fire during a Melee attack!
    if( MyKFPawn.IsDoingMeleeAttack() )
    {
		ClearFireTiming();
		return;
    }

	// if there is a friendly in the way don't fire!
//	if( IsFriendlyBlockingFireLine() )
//	{
//		// `CombatLog(Self$" "$GetStateName()$" "$GetFuncName()$"() Friendly blocking fire line, not shooting");
//		SetTimer(0.5, false, 'FireTimer');
//		return;
//	}

    // Store some info so we can time target acquisition
    if( LastTarget == none || TimeFirstSawTarget == 0 )
    {
        LastTarget = Enemy;
        TimeFirstSawTarget = WorldInfo.TimeSeconds;
    }

    // Wait to fire until the TargetAquisitionDelay is over
    if( TimeFirstSawTarget > 0 && `TimeSince(TimeFirstSawTarget) < TargetAquisitionDelay )
    {
        //`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() Waiting to aquire target, TimeDiff: "$`TimeSince(TimeFirstSawTarget)$" TargetAquisitionDelay = "$TargetAquisitionDelay);
        SetTimer(TargetAquisitionDelay - `TimeSince(TimeFirstSawTarget), false, nameof(FireTimer), self);
        Pawn.StopFiring();
        // Clear any previous barrage so we start firing again immediately after reacquiring the target
        BarrageStartTime = 0;
        return;
    }

    // Pause between groups of bursts to give the firing a good cadence
    if( BarrageStartTime > 0 && `TimeSince(BarrageStartTime) > BarrageLength )
    {
        if( Enemy != none && Pawn != none  )
        {
            UsedBarrageWaitTime = BarrageWaitTime * EvalInterpCurveFloat(BarrageWaitDistanceCurve, VSize(Pawn.Location - Enemy.Location));
            //`log("Barrage Shooting at enemy that is "$VSize(Pawn.Location - Enemy.Location)/100.0$" meters away BarrageWait is "$UsedBarrageWaitTime$" scale is "$EvalInterpCurveFloat(BarrageWaitDistanceCurve, VSize(Pawn.Location - Enemy.Location)));
        }
        else
        {
            UsedBarrageWaitTime = BarrageWaitTime;

        }

        SetTimer(UsedBarrageWaitTime, false, nameof(FireTimer), self);

        BarrageStartTime = 0;
        Pawn.StopFiring();
        //`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() Pausing between baragges");

        return;
    }
    else if( BarrageStartTime == 0 )
    {
        BarrageStartTime = WorldInfo.TimeSeconds;
    }

	// Reset the burst settings when we finish a burst
	if( BurstAmount == 0 )
	{
		//`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() Burst amount is 0");

		BurstAmount = RandRange(MinBurstAmount,MaxBurstAmount);

        // See if we can no longer shoot at our target when a burst ends
        if( !CanPerformShotAttack() )
        {
            //`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() BurstAmount hit zero, and CanPerformShotAttack is false so stopping shooting");
            ClearFireTiming();
            // Clear any previous barrage so we start firing again immediately after reacquiring the target
            BarrageStartTime = 0;
        }
        // Set up the next burst
        else
        {

            UsedBurstWaitTime = BurstWaitTime + ((FRand() * BurstWaitTime * 0.15) - (BurstWaitTime * 0.3));

            if( Enemy != none && Pawn != none  )
            {
                UsedBurstWaitTime = UsedBurstWaitTime * EvalInterpCurveFloat(BurstWaitDistanceCurve, VSize(Pawn.Location - Enemy.Location));
                //`log("Burst Shooting at enemy that is "$VSize(Pawn.Location - Enemy.Location)/100.0$" meters away BurstWaitTime is "$UsedBurstWaitTime$" scale is "$EvalInterpCurveFloat(BurstWaitDistanceCurve, VSize(Pawn.Location - Enemy.Location)));
            }

        	//`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() BurstAmount hit zero, stopping firing. New BurstAmount is: "$BurstAmount);
        	SetTimer(UsedBurstWaitTime, false, nameof(FireTimer), self);
    		Pawn.StopFiring();

    		// Select a new enemy 50% of the time when a burst ends
            if( FRand() < 0.50 )
    		{
                SelectNewGunFireEnemy(KFPawn(Enemy));
    		}
		}
		return;
	}
	// if we can see our Enemy and we're facing our Enemy and we've waited long enough to shoot again
	if ( !Pawn.NeedToTurn(GetAimingLocation()) && WorldInfo.TimeSeconds - LastFireTime > Pawn.Weapon.FireInterval[LastFireMode] )
	{
		// Shoot at our Enemy and record the Fire Time
		Pawn.BotFire(true);
		LastFireMode = 0;
		// `Log("Time since last fire = "$(WorldInfo.TimeSeconds - LastFireTime));
		LastFireTime = WorldInfo.TimeSeconds;
		//`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() Shooting!!!!! BurstAmount is: "$BurstAmount);

		SetTimer(Pawn.Weapon.FireInterval[LastFireMode] * BurstAmount, false, nameof(FireTimer), self);
		BurstAmount = 0;

		`DialogManager.PlayBeingShotAtDialog( KFPawn_Human(Enemy), MyKFPawn );
	}
	else
	{
		//`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() Not Shooting NeedToTurn = "$Pawn.NeedToTurn(Enemy.Location)$" Last Fire Diff = "$(WorldInfo.TimeSeconds - LastFireTime)$" FireInterval = "$Pawn.Weapon.FireInterval[0]);
		SetTimer(0.1, false, nameof(FireTimer), self);
	}
}

/**
 * Clear firing cycle
 */
function ClearFireTiming()
{
    SetTimer(0.0, false, nameof(FireTimer), self);
	SetTimer(0.0, false, nameof(StartFireTiming), self);
	if( Pawn != none )
	{
		Pawn.StopFiring();
	}
}

/** Set a new enemy, returns false potential enemy is a player and AI is set to ignore players */
event bool SetEnemy( Pawn NewEnemy )
{
    // Don't let Hans switch enemies in the middle of draining his current enemy,
    // unless that enemy is dead
    if( !CanSwitchEnemies()
        || (MyKFPawn != none && MyKFPawn.IsDoingSpecialMove(SM_GrappleAttack) && Enemy != none
        && Enemy.IsAliveAndWell() && Enemy.CanAITargetThisPawn(self)) )
    {
        return false;
    }

    return Super.SetEnemy( NewEnemy );
}

/** Overridden to stop retargeting enemies when fleeing or cloaked */
event ChangeEnemy( Pawn NewEnemy, optional bool bCanTaunt = true )
{
    local Pawn OldEnemy;

    if( !CanSwitchEnemies() )
    {
        return;
    }

    OldEnemy = Enemy;

    super.ChangeEnemy( NewEnemy, bCanTaunt );

    if( OldEnemy != Enemy )
    {
        LastRetargetTime = WorldInfo.TimeSeconds;
        ActualRetargetWaitTime = RandRange( RetargetWaitTimeRange.X, RetargetWaitTimeRange.Y );
    }
}

/**
 * Pick a different visible enemy to fire at so we can cycle between enemies
 */
function SelectNewGunFireEnemy( KFPawn CurrentEnemy )
{
    local int RandIdx, i;
    local array<TrackedEnemyInfo> TargetCandidates;
//    local Vector	CamLoc;
//	local Rotator	CamRot;
//	local Vector	X, Y, Z;
	local int EnemyListIndex;

    // Randomly pick another visible enemy to shoot at
    if( RecentlySeenEnemyList.Length > 0 )
    {
    	// Validate targets to shoot
        for( i = RecentlySeenEnemyList.Length-1; i >= 0; i-- )
    	{
            if( RecentlySeenEnemyList[i].TrackedEnemy == none
                || !RecentlySeenEnemyList[i].TrackedEnemy.IsAliveAndWell()
                || !RecentlySeenEnemyList[i].TrackedEnemy.CanAITargetThisPawn(self) )
            {
                RecentlySeenEnemyList.Remove(i,1);
                continue;
            }
            else if( !MyHansPawn.NeedToTurnEx(RecentlySeenEnemyList[i].LastVisibleLocation, 0.0) )
            {
                TargetCandidates[TargetCandidates.Length] = RecentlySeenEnemyList[i];
            }
    	}

    	// Clear out this pawn if it was the last one we fired at and it was engaged recently
        for( i = TargetCandidates.Length-1; i >= 0; i-- )
    	{
            if( TargetCandidates.Length > 1 && TargetCandidates[i].TrackedEnemy == LastRecentlySeenEnemyGunned
                && `TimeSince(TargetCandidates[i].LastTimeFiredOn) < 3.0 )
            {
                TargetCandidates.Remove(i,1);
            }
    	}

    	// Target selection debugging
        // Get camera location/rotation
//    	GetPlayerViewPoint( CamLoc, CamRot );
//    	GetAxes( CamRot, X, Y, Z );
//      FlushPersistentDebugLines();
//    	DrawDebugCone(CamLoc,X,500.0, Acos(0.0), Acos(0.0),16,MakeColor(0,255,0,255),true);

        if( TargetCandidates.Length > 0 )
        {
            RandIdx = Rand(TargetCandidates.Length);
            //`log("Selected random gunfire target index "$RandIdx$" Pawn: "$TargetCandidates[RandIdx].TrackedEnemy);

            LastRecentlySeenEnemyGunned = TargetCandidates[RandIdx].TrackedEnemy;

            if( LastRecentlySeenEnemyGunned != none )
            {
                EnemyListIndex = RecentlySeenEnemyList.Find('TrackedEnemy', LastRecentlySeenEnemyGunned);

                if( EnemyListIndex != INDEX_NONE )
                {
                    RecentlySeenEnemyList[EnemyListIndex].LastTimeFiredOn = WorldInfo.TimeSeconds;
                }
            }
        }
    }
}

/**
 * Returns the location this AI should be aiming at to shoot its enemy.
 */
function vector GetAimingLocation()
{
	local KFPawn KFP;
	local vector AimingLocation;
	local int EnemyListIndex;

	KFP = KFPawn(Enemy);

    // Flag the last guy we selected to fire at and aim at him
    if( LastRecentlySeenEnemyGunned != none )
    {
        EnemyListIndex = RecentlySeenEnemyList.Find('TrackedEnemy', LastRecentlySeenEnemyGunned);

        if( EnemyListIndex != INDEX_NONE )
        {
            RecentlySeenEnemyList[EnemyListIndex].LastTimeFiredOn = WorldInfo.TimeSeconds;

            // We recently selected this guy to fire on
            if( `TimeSince(RecentlySeenEnemyList[EnemyListIndex].LastTimeFiredOn) < 1.0 )
            {
                KFP = LastRecentlySeenEnemyGunned;
            }
        }
    }

	if( KFP == none )
	{
		if( Focus != none )
		{
			AimingLocation = Focus.Location;
		}
		else if( Pawn != none )
		{
			AimingLocation = Pawn.Location + vector( Pawn.Rotation ) * 128;
		}
	}
	else
	{
		AimingLocation = KFP.Location + Vect(0,0,1) * KFP.BaseEyeHeight * 0.5;//KFP.Mesh.GetBoneLocation(KFP.HeadBoneName,0);
	}

	return AimingLocation;
}

/**
 * Start a firing cycle
 */
function StartFireTiming()
{
	// Check to see if its the first time we have fired, or the first time we've fired this attack.
	// If that is the case, fire now, then let the FireTimer handle any subsequent fires
	if( Pawn !=  none && Pawn.Weapon != none  )
	{
		if( `TimeSince( LastFireTime ) > (Pawn.Weapon.FireInterval[0] + BurstWaitTime + BurstWaitTime * 0.3) )
		{
			//`log(Self$" "$GetStateName()$" "$GetFuncName());
			BurstAmount = RandRange(MinBurstAmount,MaxBurstAmount);
			FireTimer();

		}
		else
		{
			//`log(Self$" "$GetStateName()$" "$GetFuncName()$" ?????????????????????????????? Not starting fire timing!!!");
		}
	}
}

/**
 * Validate the next gun shot attack
 */
function bool CanPerformShotAttack(optional bool bStart)
{
	local float rangeToEnemy;

    // No guns when fleeing
    if( bFleeing || bWantsToFlee )
    {
        return false;
    }

    // Don't start shooting if hans hasn't drawn his gun yet, or is already trying to fire
    if( bStart && ( Pawn.IsFiring()
                || IsTimerActive(nameof(FireTimer), self) || IsTimerActive(nameof(StartFireTiming), self)) )
    {
        //`log("bStart not shooting because: TimeSince(StartDrawGunsTime): "$`TimeSince(StartDrawGunsTime)$" <  DrawGunFireDelay: "$DrawGunFireDelay$" Pawn.IsFiring(): "$Pawn.IsFiring()$" FireTimer Active: "$IsTimerActive(nameof(FireTimer), self)$" StartFireTiming Active: "$IsTimerActive(nameof(StartFireTiming), self));
        return false;
    }

    // Don't start shooting if Hans just did a melee attack
    if( bStart && (`TimeSince(LastAttackMoveFinishTime) < PostAttackMoveGunCooldown) )
    {
        return false;
    }

    // Don't let us shoot if Hans hasn't had his guns out long enough
	if( `TimeSince(StartDrawGunsTime) < DrawGunFireDelay )
	{
		return false;
	}

    // Don't shoot while sprinting
    if( MyKFPawn != none && MyKFPawn.bIsSprinting )
    {
        return false;
    }

	if( Enemy != none )
	{
		if( bStart && LastShotTime > 0 && (`TimeSince(LastShotTime) < ShootingCooldown) )
		{
			return false;
		}

		if( MyKFPawn.IsImpaired() )
		{
			return false;
		}

		rangeToEnemy = VSize(Enemy.Location - MyKFPawn.Location);

		if( rangeToEnemy < StartShootingRange*MyHansPawn.GetAttackRangeScale() && rangeToEnemy > MinShootingRange && !MyKFPawn.IsDoingMeleeAttack() )
		{
			if( CanSee(Enemy) )
			{
				return true;
			}
			else if( (WorldInfo.TimeSeconds - LastEnemySightedTime) >= LostSightStopFireDelay )
        	{
        		//`Log(Self$" "$GetStateName()$" "$GetFuncName()$"() Can't see the enemy, can't shoot TimeDiff: "$`TimeSince(LastEnemySightedTime)$" LostSightStopFireDelay = "$LostSightStopFireDelay);
        		TimeFirstSawTarget=0;
        	}
		}
	}

	return false;
}

/**
 * Can I do a stance change right now
 */
function bool CanStanceChange()
{
    return (LastStanceChangeTime == 0 || (`TimeSince(LastStanceChangeTime) > StanceChangeCooldown));
}

/** Override to tick the ranged combat system */
simulated function Tick( FLOAT DeltaTime )
{
	Super.Tick(DeltaTime);

	if( Role == ROLE_Authority )
	{
	   TickRangedCombatDecision();
	}
}

/** Tick the ranged combat system */
function TickRangedCombatDecision()
{
    local int i;

    // Update the ranged enemy visibility tracking
    // TODO: update this slower if we aren't doing an attack!
    if( LastRecentSeenEnemyListUpdateTime == 0 || `TimeSince(LastRecentSeenEnemyListUpdateTime) > RecentSeenEnemyListUpdateInterval )
    {
        LastRecentSeenEnemyListUpdateTime = WorldInfo.TimeSeconds;

    	for( i = RecentlySeenEnemyList.Length-1; i >= 0; i-- )
    	{
    	   if( RecentlySeenEnemyList[i].TrackedEnemy == none
            || !RecentlySeenEnemyList[i].TrackedEnemy.IsAliveAndWell()
            || !RecentlySeenEnemyList[i].TrackedEnemy.CanAITargetThisPawn(self)
            || `TimeSince(RecentlySeenEnemyList[i].LastTimeVisible) > 5.0 )
    	   {
    	       RecentlySeenEnemyList.Remove(i,1);
    	   }
    	   else if( CanSee(RecentlySeenEnemyList[i].TrackedEnemy) )
    	   {
    	       RecentlySeenEnemyList[i].LastVisibleLocation = RecentlySeenEnemyList[i].TrackedEnemy.Location;
               RecentlySeenEnemyList[i].LastTimeVisible = WorldInfo.TimeSeconds;
    	   }
    	}
    }

    // No grenades or guns when fleeing
    if( bFleeing || bWantsToFlee )
    {
        return;
    }

    //Don't attack while we're in theatrics
    if (CommandList != none && CommandList.class == class'AICommand_BossTheatrics')
    {
        return;
    }

    if( Enemy != None && (LastGrenadeAttackEvalTime == 0 || `TimeSince(LastGrenadeAttackEvalTime) > GrenadeAttackEvalInterval) )
    {
        LastGrenadeAttackEvalTime = WorldInfo.TimeSeconds;

        if( SetupGrenadeAttack() )
        {
            if( CurrentNadeAttackType == HNT_HEGrenade )
            {
                DoGrenadeThrow( false );
            }
            else if( CurrentNadeAttackType == HNT_HEGrenadeBarrage )
            {
                DoGrenadeThrow( true );
            }
            else if( CurrentNadeAttackType == HNT_NerveGas )
            {
                DoNerveGasGrenadeThrow( false );
            }
            else if( CurrentNadeAttackType == HNT_NerveGasBarrage )
            {
                DoNerveGasGrenadeThrow( true );
            }
            else if( CurrentNadeAttackType == HNT_Smoke )
            {
                DoSmokeGrenadeThrow( false );
            }
        }
    }

    TickGunSystem();
}

/** Tick the gun attack system which handles drawing/putting away the weapon, starting the shooting cycle, etc */
function TickGunSystem()
{
	local KFPawn_ZedHansBase HansPawn;
	local KFSpecialMove curMove;
    local float rangeToEnemy;
    //local vector EyeLocation;
    local bool bGrenadeAttackInterrupt;

	if( MyKFPawn.SpecialMoves.Length > MyKFPawn.SpecialMove )
	{
		curMove = MyKFPawn.SpecialMoves[MyKFPawn.SpecialMove];
	}
	else
	{
		curMove = none;
	}

    if( Pawn.Physics == PHYS_Walking )
    {
        HansPawn = KFPawn_ZedHansBase(MyKFPawn);

		// Aiming debugging
        /*EyeLocation = MyKFPawn.Location + vect(0,0,1) * MyKFPawn.BaseEyeHeight;

        if( Focus == none )
        {
            if( IsZero(GetFocalPoint()) )
            {
                DrawDebugLine( EyeLocation, EyeLocation + normal( GetFocalPoint()) * 5000.f, 255, 0, 0, false );
            }
            else
            {
                DrawDebugLine( EyeLocation, EyeLocation + normal( GetFocalPoint() - EyeLocation ) * 5000.f, 0, 255, 0, false );
            }
        }
        else
        {
            DrawDebugLine( EyeLocation, Focus.Location, 255, 255, 0, false );
        }*/

        // Don't shoot if we're performing a special move that prevents it
        if( curMove != none && curMove.bDisablesWeaponFiring )
        {
            //`log("Not shooting because special move disallows it");
            if( Pawn.IsFiring() || IsTimerActive(nameof(FireTimer), self) || IsTimerActive(nameof(StartFireTiming), self) )
            {
                ClearFireTiming();
                // Clear any previous barrage so we are eligible to firing again right after special move is over
                BarrageStartTime = 0;
            }

            return;
        }

		// Make sure we're not in the middle of switching stances with the weapon, or doing boss theatrics
        if( HansPawn != none && (curMove == none || !MyKFPawn.IsDoingSpecialMove(SM_ChangeStance) && !MyKFPawn.IsDoingSpecialMove(SM_BossTheatrics)) )
		{
            // If we're firing at our target, aim at that location
            if( Pawn.IsFiring() || IsTimerActive(nameof(FireTimer), self) || IsTimerActive(nameof(StartFireTiming), self) )
            {
        		Focus = none;
        		SetFocalPoint( GetAimingLocation() );
            }
            // Switch back to aiming at our enemy if we're not moving to a pathnode
            else if( Focus == none )
            {
                Focus = Enemy;
            }

            if( Enemy != none )
            {
                rangeToEnemy = VSize(Enemy.Location - MyKFPawn.Location);
            }
            else
            {
                // If there is not enemy just act like they are out of range
                rangeToEnemy = MaxInt;
            }

            bGrenadeAttackInterrupt = GrenadeAttackInterruptGuns();

            // Put the guns away if the enemy is out of range
            if( HansPawn.bGunsEquipped && ((rangeToEnemy > StartShootingRange*MyHansPawn.GetAttackRangeScale() )
                || HansPawn.bInHuntAndHealMode || !HansPawn.CanUseGunsInThisPhase() || HansPawn.bPendingSmokeGrenadeBarrage
                || bGrenadeAttackInterrupt )
                && CanStanceChange() )
            {
                ClearFireTiming();
                class'AICommand_Hans_GunStance'.static.SetGunStance( self, 0 );
                //`log("Putting the guns away because there is an enemy too close");
            }
            // Draw the guns if the enemy is in range, and we're not waiting for a cooldown
            else if( !HansPawn.bGunsEquipped && (LastShotTime == 0 || (`TimeSince(LastShotTime) > ShootingCooldown))
                && rangeToEnemy < StartShootingRange*MyHansPawn.GetAttackRangeScale() && !HansPawn.bInHuntAndHealMode
                && HansPawn.CanUseGunsInThisPhase() && CanStanceChange() && !bGrenadeAttackInterrupt )
            {
                class'AICommand_Hans_GunStance'.static.SetGunStance( self, 1 );

                StartDrawGunsTime = WorldInfo.TimeSeconds;
                //`log("Drawing our guns StartDrawGunsTime = "$StartDrawGunsTime);

                // Clear any previous barrage so we are eligible to firing again right after special move is over
                BarrageStartTime = 0;

                // Don't start the cooldown again if we haven't already crossed the cooldown
                if( StartGunAttackTime == 0 || (`TimeSince(StartGunAttackTime) > MaxGunAttackLength) )
                {
                    StartGunAttackTime = WorldInfo.TimeSeconds;
                    //`log("Setting StartGunAttackTime = "$StartGunAttackTime);
                }
            }
            // Put the guns away if its time to cooldown or we can't use guns in this phase
            else if( StartGunAttackTime > 0 && (`TimeSince(StartGunAttackTime) > MaxGunAttackLength//StartDrawGunsTime > 0 && (`TimeSince(StartDrawGunsTime) > MaxGunAttackLength
                || !HansPawn.CanUseGunsInThisPhase() ) && CanStanceChange() )
            {
                //`log("Not shooting because we're in cooldown");
                if(  HansPawn.bGunsEquipped )
                {
                    ClearFireTiming();
                    LastShotTime = WorldInfo.TimeSeconds;
                    // Reset the draw guns time
                    StartDrawGunsTime = 0;
                    //`log("Putting our guns away StartDrawGunsTime = "$StartDrawGunsTime);

                    //`log("Putting Guns Away due to cooldown StartDrawGunsTime = "$StartDrawGunsTime$" LastShotTime = "$LastShotTime);

                    class'AICommand_Hans_GunStance'.static.SetGunStance( self, 0 );
                }
            }
            // All clear, start shooting!
            else if( HansPawn.bGunsEquipped && HansPawn.CanUseGunsInThisPhase()
                && CanPerformShotAttack(true) )
            {
            	//`DialogManager.PlayBeingShotAtDialog( KFPawn_Human(Enemy), HansPawn );
                StartFireTiming();
            }
        }
    }
}

/** Shows this KFAIController's RandgedAttack info on screen (see ShowAIInfo cheat) */
function DrawRangedAttackInfo( HUD Hud )
{
	local Canvas		Canvas;
    local float UsedShootCooldown;
	local KFSpecialMove curMove;
    local bool bMoveDisablesFiring;
    local float UsedGunAttackCooldown;
    local float UsedGlobalNadeAttackCooldown;
    local float UsedHENadeTossCooldown;
    local float UsedHENadeBarrageCooldown;
    local float UsedNerveGasTossCooldown;
    local float UsedNerveGasBarrageCooldown;
    local float UsedSmokeTossCooldown;
    local float UsedDrawGunsCooldown;
    local float UsedAttackMoveGunsCooldown;
    local float UsedGunTargetAquisitionCooldown;
    local int i;
    local vector EyeLocation;

	if( MyKFPawn == none )
	{
		return;
	}

	if( MyKFPawn.SpecialMoves.Length > MyKFPawn.SpecialMove )
	{
		curMove = MyKFPawn.SpecialMoves[MyKFPawn.SpecialMove];
	}
	else
	{
		curMove = none;
	}

    // Don't shoot if we're performing a special move that prevents it
    if( curMove != none && curMove.bDisablesWeaponFiring )
    {
        bMoveDisablesFiring = true;
    }

	Canvas = HUD.Canvas;
	Canvas.Font = class'Engine'.Static.GetTinyFont();
	Canvas.SetPos(Canvas.SizeX * 0.7f, Canvas.SizeY * 0.25f);

	Canvas.SetDrawColor(0,200,50);

    DrawDebugText( HUD, "Ranged Combat for "$string(MyKFPawn.Name));
    DrawDebugText( HUD, "--------------------------------------" );
    DrawDebugText( HUD, "Battle Phase: "$MyHansPawn.CurrentBattlePhase );
    DrawDebugText( HUD, "--Guns--" );
    DrawDebugText( HUD, "Guns Equipped: "$MyHansPawn.bGunsEquipped$" Can Use Guns In This Phase: "$MyHansPawn.CanUseGunsInThisPhase() );
    DrawDebugText( HUD, "Stance Changing: "$MyKFPawn.IsDoingSpecialMove(SM_ChangeStance)$" CurrentMove: "$curMove$" bDisablesWeaponFiring: "$bMoveDisablesFiring );

    if(`TimeSince(StartDrawGunsTime) > DrawGunFireDelay)
    {
        UsedDrawGunsCooldown = 0;
    }
    else
    {
        UsedDrawGunsCooldown = DrawGunFireDelay - `TimeSince(StartDrawGunsTime);
    }

    if(`TimeSince(LastAttackMoveFinishTime) > PostAttackMoveGunCooldown)
    {
        UsedAttackMoveGunsCooldown = 0;
    }
    else
    {
        UsedAttackMoveGunsCooldown = PostAttackMoveGunCooldown - `TimeSince(LastAttackMoveFinishTime);
    }

    if(`TimeSince(LastShotTime) > ShootingCooldown)
    {
        UsedShootCooldown = 0;
    }
    else
    {
        UsedShootCooldown = ShootingCooldown - `TimeSince(LastShotTime);
    }

    if(`TimeSince(StartGunAttackTime) > MaxGunAttackLength)
    {
        UsedGunAttackCooldown = 0;
    }
    else
    {
        UsedGunAttackCooldown = MaxGunAttackLength - `TimeSince(StartGunAttackTime);
    }

    if(`TimeSince(TimeFirstSawTarget) > TargetAquisitionDelay)
    {
        UsedGunTargetAquisitionCooldown = 0;
    }
    else
    {
        UsedGunTargetAquisitionCooldown = TargetAquisitionDelay - `TimeSince(TimeFirstSawTarget);
    }

    DrawDebugText( HUD, "Time Til We Can Shoot : "$UsedShootCooldown$" Time Til We Must Stop Shooting: "$UsedGunAttackCooldown, (UsedShootCooldown <= 0) ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255)  );
    DrawDebugText( HUD, "Draw Guns Delay Til We Can Shoot : "$UsedDrawGunsCooldown, (UsedDrawGunsCooldown <= 0) ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "Attack Move Delay Til We Can Shoot : "$UsedAttackMoveGunsCooldown, (UsedAttackMoveGunsCooldown <= 0) ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "Target Aquisition Delay Til We Can Shoot : "$UsedGunTargetAquisitionCooldown, (UsedGunTargetAquisitionCooldown <= 0) ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "FireTimer : "$GetRemainingTimeForTimer(nameof(FireTimer))$" StartFireTiming: "$GetRemainingTimeForTimer(nameof(StartFireTiming)), MakeColor(0,255,0,255));


    DrawDebugText( HUD, "--Grenades--" );
    DrawDebugText( HUD, "CurrentNadeAttackType: "$CurrentNadeAttackType$" IsThrowingGrenade: "$MyHansPawn.IsThrowingGrenade() );
    if( Enemy != none )
    {
        DrawDebugText( HUD, "Good Line Of Site: "$CanSeeByPoints( Pawn.GetPawnViewLocation(), Enemy.Location, Rotator(Enemy.Location - Pawn.GetPawnViewLocation()))$" Too Close To Regular Nade: "$IsWithinAttackRange() );
    }
    else
    {
        DrawDebugText( HUD, "Good Line Of Site: No Enemy, Too Close To Regular Nade: "$IsWithinAttackRange() );
    }

    if( MyHansPawn.LastOffensiveNadeTime == 0 || `TimeSince(MyHansPawn.LastOffensiveNadeTime) > MyHansPawn.GlobalOffensiveNadeCooldown )
    {
        UsedGlobalNadeAttackCooldown = 0;
    }
    else
    {
        UsedGlobalNadeAttackCooldown =  MyHansPawn.GlobalOffensiveNadeCooldown - `TimeSince(MyHansPawn.LastOffensiveNadeTime);
    }

    if( MyHansPawn.LastHENadeTossTime == 0 || `TimeSince(MyHansPawn.LastHENadeTossTime) > MyHansPawn.HENadeTossCooldown )
    {
        UsedHENadeTossCooldown = 0;
    }
    else
    {
        UsedHENadeTossCooldown =  MyHansPawn.HENadeTossCooldown - `TimeSince(MyHansPawn.LastHENadeTossTime);
    }

    if( MyHansPawn.LastHENadeBarrageTime ==0 || `TimeSince(MyHansPawn.LastHENadeBarrageTime) > MyHansPawn.HENadeBarrageCooldown )
    {
        UsedHENadeBarrageCooldown = 0;
    }
    else
    {
        UsedHENadeBarrageCooldown =  MyHansPawn.HENadeBarrageCooldown - `TimeSince(MyHansPawn.LastHENadeBarrageTime);
    }

    if( MyHansPawn.LastNerveGasTossTime ==0 || `TimeSince(MyHansPawn.LastNerveGasTossTime) > MyHansPawn.NerveGasTossCooldown )
    {
        UsedNerveGasTossCooldown = 0;
    }
    else
    {
        UsedNerveGasTossCooldown =  MyHansPawn.NerveGasTossCooldown - `TimeSince(MyHansPawn.LastNerveGasTossTime);
    }

    if( MyHansPawn.LastNerveGasBarrageTime ==0 || `TimeSince(MyHansPawn.LastNerveGasBarrageTime) > MyHansPawn.NerveGasBarrageCooldown )
    {
        UsedNerveGasBarrageCooldown = 0;
    }
    else
    {
        UsedNerveGasBarrageCooldown =  MyHansPawn.NerveGasBarrageCooldown - `TimeSince(MyHansPawn.LastNerveGasBarrageTime);
    }

    if( MyHansPawn.LastSmokeTossTime ==0 || `TimeSince(MyHansPawn.LastSmokeTossTime) > MyHansPawn.SmokeTossCooldown )
    {
        UsedSmokeTossCooldown = 0;
    }
    else
    {
        UsedSmokeTossCooldown =  MyHansPawn.SmokeTossCooldown - `TimeSince(MyHansPawn.LastSmokeTossTime);
    }

    DrawDebugText( HUD, "Global Nade Cooldown Complete: "$MyHansPawn.OffensiveGrenadeCooldownComplete()$" GlobalNadeAttackCooldown: "$UsedGlobalNadeAttackCooldown, MyHansPawn.OffensiveGrenadeCooldownComplete() ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "Can Smoke Toss: "$MyHansPawn.CanSmokeTossInThisPhase()$" SmokeTossCooldown: "$UsedSmokeTossCooldown, MyHansPawn.CanSmokeTossInThisPhase() ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "Can Nerve Gas Toss: "$MyHansPawn.CanTossNerveGasInThisPhase()$" NerveGasTossCooldown: "$UsedNerveGasTossCooldown, MyHansPawn.CanTossNerveGasInThisPhase() ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "Can HE Grenade Toss: "$MyHansPawn.CanTossGrenadeInThisPhase()$" HENadeTossCooldown: "$UsedHENadeTossCooldown, MyHansPawn.CanTossGrenadeInThisPhase() ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "Can Nerve Gas Barrage: "$MyHansPawn.CanBarrageNerveGasInThisPhase()$" NerveGasBarrageCooldown: "$UsedNerveGasBarrageCooldown, MyHansPawn.CanBarrageNerveGasInThisPhase() ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );
    DrawDebugText( HUD, "Can HE Grenade Barrage: "$MyHansPawn.CanGrenadeBarrageInThisPhase()$" HENadeBarrageCooldown: "$UsedHENadeBarrageCooldown, MyHansPawn.CanGrenadeBarrageInThisPhase() ? MakeColor(0,255,0,255) : MakeColor(255,0,0,255) );

    DrawDebugText( HUD, "--RecentlySeenEnemyList--" );
	for( i = 0; i < RecentlySeenEnemyList.Length; i++ )
	{
        DrawDebugText( HUD, "TrackedEnemy #"$i$": "$RecentlySeenEnemyList[i].TrackedEnemy);
	    EyeLocation = MyKFPawn.Location + vect(0,0,1) * MyKFPawn.BaseEyeHeight;
        DrawDebugLine( EyeLocation, RecentlySeenEnemyList[i].LastVisibleLocation, 254, 89, 18, false );
	}
}


/*********************************************************************************************
* Grenades
**********************************************************************************************/

/** Return true if one of the standard (not smoke) grenade attack cooldowns is reached and we can attempt a nade throw*/
function bool GrenadeAttackInterruptGuns()
{
    // Don't interrupt the gun attack with the grenade attack if we just pulled the guns
    if( MyHansPawn.bGunsEquipped && `TimeSince(StartDrawGunsTime) < GrenadeGunInterruptDelay )
    {
        return false;
    }

    if( MyHansPawn.CanGrenadeBarrageInThisPhase() )
    {
        return true;
    }
    else if( MyHansPawn.CanBarrageNerveGasInThisPhase() )
    {
        return true;
    }
    else if( MyHansPawn.CanTossGrenadeInThisPhase() )
    {
        return true;
    }
    else if( MyHansPawn.CanTossNerveGasInThisPhase() )
    {
        return true;
    }

    return false;
}

/** Returns true if we can successfully set up a grenade attack */
function bool SetupGrenadeAttack()
{
	/** See if Hans can/should do a grenade attack right now*/
	if( MyHansPawn != none && Enemy != none && !MyHansPawn.IsDoingSpecialMove(SM_ChangeStance)
        && !MyHansPawn.IsThrowingGrenade() && !MyHansPawn.bGunsEquipped
        && CanSeeByPoints( Pawn.GetPawnViewLocation(), Enemy.Location, Rotator(Enemy.Location - Pawn.GetPawnViewLocation()) )
        && MyHansPawn.CacheGrenadeThrowLocation())
	{
        // Clear the last grenade attack type
        CurrentNadeAttackType = HNT_None;

        // Don't throw grenades up close, unless it's a smoke grenade during hunt and heal
        if( !IsWithinAttackRange() )
        {
            // Pick what kind of nade attack Hans should do
            if( MyHansPawn.CanGrenadeBarrageInThisPhase() )
            {
                CurrentNadeAttackType=HNT_HEGrenadeBarrage;
            }
            else if( MyHansPawn.CanBarrageNerveGasInThisPhase() )
            {
                CurrentNadeAttackType=HNT_NerveGasBarrage;
            }
            else if( MyHansPawn.CanTossGrenadeInThisPhase() )
            {
                CurrentNadeAttackType=HNT_HEGrenade;
            }
            else if( MyHansPawn.CanTossNerveGasInThisPhase() )
            {
                CurrentNadeAttackType=HNT_NerveGas;
            }
        }
        //Hans keeps tossing out smoke grenades occasionally when he's hunting a
        // player to heal from
        else
        {
            if( MyHansPawn.CanSmokeTossInThisPhase() )
            {
                CurrentNadeAttackType=HNT_Smoke;
            }
        }

        // Return true if we found a valid nade attack
        if( CurrentNadeAttackType != HNT_None )
        {
            return true;
        }
        else
        {
            return false;
        }
	}
	else
	{
	   return false;
	}
}

function DoStrike()
{
	local name AttackName;

	if( MyHansPawn != none && MyHansPawn.PawnAnimInfo != none )
	{
		AttackName = MyHansPawn.PawnAnimInfo.Attacks[PendingAnimStrikeIndex].Tag;

		// @todo: figure out a way to make this less hard-coded? (see also KFDialogManager::PlayHansKilledDialog)
		if( AttackName == 'Frenzy_Lunge' )
		{
			`DialogManager.PlayHansFrenzyDialog( MyHansPawn );
		}
        else if( AttackName == 'AOE' )
        {
            `DialogManager.PlayHansAOEDialog( MyHansPawn );
        }
	}

	super.DoStrike();
}

/**
 *  Used by movement commands to determine if NPC is close enough consider a strike
 *  Overridden so that Hans will move all the way to grab range and not get stuck.
 */
function bool IsWithinAttackRange()
{
	local float DistSqToEnemy;

    // Don't try to attack if our enemy isn't visible
    if( MyHansPawn == none || !bEnemyIsVisible )
    {
        return false;
    }

	// Don't grab players if you aren't in hunt and heal mode or are waiting for the smoke grenade barrage
    if( !MyHansPawn.bInHuntAndHealMode )
	{
	   return Super.IsWithinAttackRange();
	}

	// Check distance from enemy versus my grab range value.
	DistSqToEnemy = VSizeSq( Enemy.Location - Pawn.Location );
	if( DistSqToEnemy <= Square(MinDistanceToPerformGrabAttack) )
	{
		return true;
	}

	return false;
}

function bool CanAttackDestructibles()
{
    return !MyHansPawn.bGunsEquipped && bCanDoHeavyBump;
}

/*********************************************************************************************
* Battle-phase switching
**********************************************************************************************/

/** Overridden to handle door usage in flee state */
function NotifyAttackDoor( KFDoorActor Door )
{
    // We need to count up the total flee time so infinite fleeing isn't possible
    if( bFleeing )
    {
        TotalFleeTime = TotalFleeTime + (WorldInfo.TimeSeconds - FleeStartTime);
        bWantsToFlee = true;
        bFleeInterrupted = true;
        bFleeing = false;
        
        // Kill our flee and move commands
        AbortCommand( CommandList );

        // Allow melee again
        EnableMeleeRangeEventProbing();

        // Restart default command
        BeginCombatCommand( GetDefaultCommand(), "Restarting default command" );
    }

    super.NotifyAttackDoor( Door );
}

/** Overridden to handle door usage in flee state */
function bool DoorFinished()
{
    local bool bSuperFinished;

    bSuperFinished = super.DoorFinished();

    if( bWantsToFlee && !bFleeing )
    {
        if( MyHansPawn.IsDoingSpecialMove() )
        {
            MyHansPawn.EndSpecialMove();
        }
        Flee();
    }

    return bSuperFinished;
}

/**
 * Set Hans to the next phase in the battle
 */
function NextBattlePhase()
{
    // Set flee flags
    bHasFledThisPhase = false;
    MaxFleeDuration = RandRange( MinFleeDuration, MaxFleeDuration );
    bWantsToFlee = true;
    DisableMeleeRangeEventProbing();

    // Set smoke grenade barrage flag
    bHasUsedSmokeScreenThisPhase = false;
}

/** Whether enemy switch commands can be run */
function bool CanSwitchEnemies()
{
    return !bWantsToFlee && !bFleeing;
}

/* Starts Flee AICommand, with optional duration and distance */
function DoFleeFrom( actor FleeFrom,
    optional float FleeDuration,
    optional float FleeDistance,
    optional bool bShouldStopAtGoal=false,
    optional bool bFromFear=false )
{
    if( !bFromFear || !MyHansPawn.bInHuntAndHealMode )
    {
        super.DoFleeFrom( FleeFrom, FleeDuration, FleeDistance, bShouldStopAtGoal, bFromFear );
    }
}

/** Sets flee target if there is no enemy, starts flee command */
function Flee()
{
    local Actor FleeFromTarget;
    local float FleeDuration;
    local AICommand_SpecialMove AICSM;

    // Reset flee state
    bFleeing = false;
    bWantsToFlee = false;
    bFleeInterrupted = false;

    // We need a target to flee from
    Enemy = none;
    CheckForEnemiesInFOV( 3000.f, 0.2f, 1.f, true, false );
    if( Enemy == None )
    {
        SetEnemy( GetClosestEnemy() );
    }

    // Try to get an enemy, if not just choose a nearby navigation point (always flee!)
    if( Enemy != None )
    {
        FleeFromTarget = Enemy;
    }
    else
    {
        FleeFromTarget = class'NavigationPoint'.static.GetNearestNavToActor( MyHansPawn );
    }

    // Prevent timeout from interrupting flee
    AICSM = FindCommandOfClass( class'AICommand_SpecialMove' );
    if( AICSM != none )
    {
        AICSM.ClearTimeout();
    }

    // Abort all commands
    AbortCommand( CommandList );

    // Perform flee
    bFleeing = true;
    MyHansPawn.SetSprinting( true );
    DisableMeleeRangeEventProbing();

    FleeDuration = fMax( MaxFleeDuration - TotalFleeTime, 6.f );
    //`log("[FLEE] FleeDuration:"@FleeDuration);
    //`log("[FLEE] FleeStartTime:"@WorldInfo.TimeSeconds);
    FleeStartTime = WorldInfo.TimeSeconds;
    DoFleeFrom( FleeFromTarget, FleeDuration, MaxFleeDistance + Rand(MaxFleeDistance * 0.25f), true );
    EvaluateSprinting();

    // Constantly make sure we don't have a player trying to block us
    if( !IsTimerActive(nameOf(Timer_SearchForFleeObstructions)) )
    {
        SetTimer( 1.5f, false, nameOf(Timer_SearchForFleeObstructions) );
    }
}

/** Custom target searching when fleeing -- we only want to attack targets that are blocking our flee path */
function Timer_SearchForFleeObstructions()
{
    local KFPawn ObstructingEnemy;

    if( !bFleeing || bWantsToFlee || MyHansPawn.IsDoingSpecialMove() )
    {
        SetTimer( 0.25f, false, nameOf(Timer_SearchForFleeObstructions) );
        return;
    }

    // See if there's someone blocking us
    ObstructingEnemy = CheckForEnemiesInFOV( AttackRange * 1.1f, 0.5f, 1.f, false, false );
    if( ObstructingEnemy != none )
    {
        // We need to count up the total flee time so infinite fleeing isn't possible
        TotalFleeTime = TotalFleeTime + (WorldInfo.TimeSeconds - FleeStartTime);
        bFleeInterrupted = true;
        bFleeing = false;

        // Kill our flee and move commands
        AbortCommand( CommandList );

        // Set our new enemy
        ChangeEnemy( ObstructingEnemy, false );

        // Set our pending flee
        bWantsToFlee = true;

        // Sprint to new enemy
        MyHansPawn.SetSprinting( true );
        SetEnemyMoveGoal( self, true );
        EnableMeleeRangeEventProbing();

        // Restart default command
        BeginCombatCommand( GetDefaultCommand(), "Restarting default command" );

        // Give hans a little bit of time to flee after this attack
        SetTimer( 2.0f, false, nameOf(Timer_SearchForFleeObstructions) );
    }
    else
    {
        SetTimer( 0.25f, false, nameOf(Timer_SearchForFleeObstructions) );
    }
}


/** Command finished. Used to catch instances where the flee command is interrupted by another command */
function NotifyCommandFinished( AICommand FinishedCommand )
{
    if( !bWantsToFlee && bFleeing && PendingDoor == none && (ActorEnemy == none || ActorEnemy.bPendingDelete) && AICommand_Flee(FinishedCommand) != none )
    {
        // Add to our total flee time
        TotalFleeTime = TotalFleeTime + (WorldInfo.TimeSeconds - FleeStartTime);

        // Abort the flee command
        AbortCommand( FinishedCommand );

        // Cancel any special moves
        if( MyHansPawn.IsDoingSpecialMove() )
        {
            MyHansPawn.EndSpecialMove();
        }

        // Delay flee by a tiny bit to allow command to finish up
        SetTimer( 0.06f, false, nameOf(Flee), self );
    }
}

/** We have finished fleeing for one reason or another, notify pawn to heal */
function NotifyFleeFinished( optional bool bAcquireNewEnemy=true )
{
    local KFAISpawnManager SpawnManager;

    // Don't do additional cleanup if this wasn't a hunt and heal flee
    if( !MyHansPawn.bInHuntAndHealMode )
    {
        return;
    }

    // Restore flee flags to default
    bFleeing = false;
    bWantsToFlee = false;

    // Stop searching for targets
    ClearTimer( nameOf(Timer_SearchForFleeObstructions) );

    // Stop summoning minions
    SpawnManager = KFGameInfo(WorldInfo.Game).SpawnManager; 
    if( SpawnManager != none )
    {
        SpawnManager.StopSummoningBossMinions();
    }

    // Prevent infinite fleeing
    bHasFledThisPhase = true;

    // Find a new enemy and move to them
    if( bAcquireNewEnemy )
    {
        ChangeEnemy( GetClosestEnemy(), true );
    }

    // Allow melee again
    EnableMeleeRangeEventProbing();

    // Restart default command
    BeginCombatCommand( GetDefaultCommand(), "Restarting default command" );
}

/** Aborts the flee (shield was destroyed, etc) */
function CancelFlee( optional bool bAcquireNewEnemy=true )
{
    if( bFleeing )
    {
        bFleeing = false;
        bWantsToFlee = false;

        // Kill our flee command
        AbortCommand( FindCommandOfClass(class'AICommand_Flee') );

        // End flee as normal
        NotifyFleeFinished( bAcquireNewEnemy );
    }
    else
    {
        // Make sure hans doesn't try to flee again
        bWantsToFlee = false;
        bHasFledThisPhase = true;
    }
}

/**
 * Hans's prefers using health drain (grab) on players when he's wounded.  Currently he
 *	won't attempt this attack if he's already at full health
 */
event bool CanGrabAttack()
{
	local KFPawn KFPawnEnemy;
	local float DistSq;
	local vector HitLocation, HitNormal;
	local Actor HitActor;

    /** Don't try to grab while still fleeing */
    if( bFleeing || bWantsToFlee )
    {
        return false;
    }

	/** Hans shouldn't do this attack if at full health */
	if( Enemy == none || MyKFPawn == none || MyKFPawn.Health <= 0 || GetHealthPercentage() >= 1.f )
	{
		return false;
	}

	// Don't grab players if you aren't in hunt and heal mode or are waiting for the smoke grenade barrage
    if( MyHansPawn != none && (!MyHansPawn.bInHuntAndHealMode || MyHansPawn.bPendingSmokeGrenadeBarrage) )
	{
	   return false;
	}

	// If I'm dead, incapable of grabbing, or have no enemy, or my enemy is a player, or I'm busy doing a melee attack, refuse.
	if( (MyKFPawn == none || !MyKFPawn.bCanGrabAttack || MyKFPawn.Health <= 0) || (Enemy == none) || (Enemy != none && Pawn.IsSameTeam(Enemy)) )
	{
		return false;
	}

	KFPawnEnemy = KFPawn( Enemy );

	if( KFPawnEnemy != none && KFPawnEnemy.IsDoingSpecialMove(SM_GrappleVictim)
        && VSizeSq(MyHansPawn.Location - Enemy.Location) < Square(MinDistanceToPerformGrabAttack*1.5f) )
	{
        // End the move immediately so other zed stops grabbing our target!
        KFPawnEnemy.InteractionPawn.EndSpecialMove();
	}

	if( KFPawnEnemy == none || !KFPawnEnemy.CanBeGrabbed(MyKFPawn) )
	{
		return false;
	}

	// If I'm crippled, falling, busy doing an attack, or incapacitated, refuse.
	if( MyKFPawn.bIsHeadless || (MyKFPawn.Physics == PHYS_Falling) || IsDoingAttackSpecialMove() || MyKFPawn.IsImpaired() || MyKFPawn.IsIncapacitated() )
	{
		return false;
	}

	if( LastAttackTime_Grab == 0.f || (`TimeSince(LastAttackTime_Grab) > MinTimeBetweenGrabAttacks) )
	{
        // Make sure the enemy's center of mass (location) is within my collision cylinder
		if( Abs(Enemy.Location.Z - Pawn.Location.Z) > Pawn.CylinderComponent.CollisionHeight )
		{
			return false;
		}

		DistSq = VSizeSq(Enemy.Location - Pawn.Location);
		if( DistSq > Square(MinDistanceToPerformGrabAttack) )
		{
			return false;
		}
        // Do the same kind of trace we do in KFSM_GrappleStart
		HitActor = Trace(HitLocation, HitNormal, Enemy.Location, Pawn.Location, true);
		if ( HitActor != None && HitActor != Enemy )
		{
            return false;
		}
		if( !CanTargetBeGrabbed(KFPawn(Enemy)) )
		{
			return false;
		}
		/** Makes Zed have high desire to grab as initial attack */
		if( !MyKFPawn.IsDoingMeleeAttack() )
		{
			return true;
		}
	}
	`AILog( GetFuncName()$"() returning FALSE", 'GrabAttack' );
	return false;

}

function bool CanTargetBeGrabbed( KFPawn TargetKFP )
{
	local KFAIController OtherKFAIC;

	// Disallow if target is invalid, already being grabbed, or currently falling
	if( TargetKFP == none || (TargetKFP.Health <= 0) || (TargetKFP.IsDoingSpecialMove(SM_GrappleVictim)) || TargetKFP.Physics == PHYS_Falling )
	{
		return false;
	}

	// Need to check that no other clots are performing initial grab move on player since SM_GRAB is run without the player in a special move
	foreach WorldInfo.AllControllers( class'KFAIController', OtherKFAIC )
	{
		if( OtherKFAIC == self )
		{
			continue;
		}
		// If the the other Zed is doing a grab start or grab attack special move, and TargetKFP is also his enemy, disallow
		// this grab. Already checked if player is doing a grapplevictim special move above, but the player isn't executing
		// the victim special move until the other Zed's initial grab anim is complete (see KFSM_Clot_Grab - this is the move that determines
		// whether the clot's grab should miss or succeed based on when the initial sequence is interrupted)...  so instead check
		// whether the starting-to-grab Zed has the same enemy.
		// The grab special move assumes that the grab victim is also the grabber's enemy.
		if( OtherKFAIC.MyKFPawn != none && OtherKFAIC.Enemy == TargetKFP && OtherKFAIC.IsDoingGrabSpecialMove() )
		{
            if( VSizeSq(MyHansPawn.Location - TargetKFP.Location) < 250000 ) // 5 Meters
            {
                // End the move immediately so other zed stops grabbing our target!
                OtherKFAIC.MyKFPawn.EndSpecialMove();
            }
		}
	}

	return true;
}

/** Launch a grab attack */
event DoGrabAttack( optional Pawn NewEnemy, optional float InPostSpecialMoveSleepTime=0.f )
{
	if( CommandList == None || AICommand(CommandList).bAllowedToAttack )
	{
		if( NewEnemy != None && NewEnemy != Enemy )
		{
			ChangeEnemy( NewEnemy, false );
		}
		/** Abort qany movement commands */
		ClearMovementInfo();
		`AILog( GetFuncName()$"() Init AICommand_Attack_Grab", 'InitAICommand' );
		class'AICommand_Attack_Grab'.static.Grab( self, InPostSpecialMoveSleepTime );
	}
	else if( CommandList != none && !AICommand(CommandList).bAllowedToAttack )
	{
		`AILog( GetFuncName()$"() not doing grab attack because current command ("$CommandList$") will not allow it", 'GrabAttack' );
	}
}

/** Effects and damage from a zed sprinting and bumping other monsters */
function bool DoHeavyZedBump( Actor Other, vector HitNormal )
{
	local int BumpEffectDamage;
	local KFPawn_Monster BumpedMonster;

	/** If we bumped into a glass window, break it */
	if( Other.bCanBeDamaged && KFFracturedMeshGlass(Other) != none )
	{
		KFFracturedMeshGlass(Other).BreakOffAllFragments();
		return true;
	}

	BumpedMonster = KFPawn_Monster(Other);
	if( BumpedMonster == none || !BumpedMonster.IsAliveAndWell() || BumpedMonster.ZedBumpDamageScale <= 0 )
	{
		return false;
	}

	if( MyKFPawn == none || !MyKFPawn.IsAliveAndWell() )
	{
		return false;
	}

	// Hans knocks guys out of the way always if he is in hunt and heal
    if( (MyHansPawn.bInHuntAndHealMode && !IsZero(MyKFPawn.Velocity)) || (MyKFPawn.bIsSprinting && !MyKFPawn.IsDoingSpecialMove()) )
	{
		BumpEffectDamage = ZedBumpEffectThreshold * BumpedMonster.ZedBumpDamageScale;

		// If the Bumped Zed is near death, play either a knockdown or an immediate obliteration
		if( BumpedMonster.Health - BumpEffectDamage <= 0 )
		{
			// Chance to obliterate if at low health
			if( FRand() < ZedBumpObliterationEffectChance )
			{
				BumpedMonster.TakeDamage(BumpEffectDamage, self, BumpedMonster.Location, vect(0,0,0), MyKFPawn.GetBumpAttackDamageType());
			}
			else
			{
				BumpedMonster.Knockdown( , vect(1,1,1), Pawn.Location, 1000, 100 );
			}
			return true;
		}
		else
		{
			// otherwise deal damage and stumble the zed
			BumpedMonster.TakeDamage(BumpEffectDamage, self, BumpedMonster.Location, vect(0,0,0), MyKFPawn.GetBumpAttackDamageType());
			BumpedMonster.DoSpecialMove(SM_Stumble,,, class'KFSM_Stumble'.static.PackBodyHitSMFlags(BumpedMonster, HitNormal));
			return true;
		}
	}
	return false;
}

function bool CanDoStrike()
{
    local bool bInGrabRange;
    local float DistSq;

    if( !bFleeing && !bWantsToFlee )
    {
    	/** See if we want to grab an enemy, and they are in grab range */
    	if( Enemy != none && MyHansPawn != none && MyHansPawn.bInHuntAndHealMode &&
            MyKFPawn != none && MyKFPawn.Health > 0 )
    	{
        	DistSq = VSizeSq(Enemy.Location - Pawn.Location);
    		if( DistSq < Square(MinDistanceToPerformGrabAttack) )
    		{
                bInGrabRange = true;
    		}
    	}

    	// Never do melee strikes when in bInHuntAndHealMode unless we can't grab attack, and the enemy is in grab range
    	if( MyHansPawn == none || (MyHansPawn.bInHuntAndHealMode && (!bInGrabRange || CanGrabAttack())) )
    	{
    		return false;
    	}
    }

	return super.CanDoStrike();
}

event EdgeAndPolySubRegionRejectedDueToDist( vector EdgeCenterRejected, ENavmeshEdgeType EdgeTypeRecjted, Color EdgeTypeColorRejected, vector SubRegionLocation, vector PolyCenterRejected, float Dist )
{
 	local KFDebugLines KFDL;

	if( class'KFGameEngine'.default.bEnableAdvDebugLines )
	{
//
 		KFDL = class'KFDebugLines'.static.GetDebugLines();
	//
 		KFDL.AddDebugSphere( SubRegionLocation, 32, 8, 255, 0, 0, true);
 		KFDL.AddDebugText3D( SubRegionLocation + vect(0,0,1) * 72.f, "FAILED DUE TO Dist:"$Dist, true, 100, 100, 0, true );

	}
}

event EdgeAndPolySubRegionRejectedDueToRating( vector EdgeCenterRejected, ENavmeshEdgeType EdgeTypeRecjted, Color EdgeTypeColorRejected, vector SubRegionLocation, vector PolyCenterRejected, int Rating )
{
 	local KFDebugLines KFDL;
//
	if( class'KFGameEngine'.default.bEnableAdvDebugLines )
	{
 		KFDL = class'KFDebugLines'.static.GetDebugLines();
	//
 		KFDL.AddDebugSphere( SubRegionLocation, 32, 8, 255, 255, 0, true );
 		KFDL.AddDebugText3D( SubRegionLocation + vect(0,0,1) * 72.f, "FAILED DUE TO RATING:"$Rating, true, 100, 200, 0, true );
	}
}

event EntireEdgeAndPolyRejectedDueToRating( vector EdgeCenterRejected, ENavmeshEdgeType EdgeTypeRecjted, Color EdgeTypeColorRejected, vector PolyCenterRejected, int Rating )
{
 	local KFDebugLines KFDL;
	local vector centerOfLineFromEdgeToPolyCenter;

	if( class'KFGameEngine'.default.bEnableAdvDebugLines )
	{
		centerOfLineFromEdgeToPolyCenter = 0.5 * (PolyCenterRejected - EdgeCenterRejected);
	//
 		KFDL = class'KFDebugLines'.static.GetDebugLines();
	//
 		//KFDL.AddDebugSphere( PossibleGoal.Location, 32, 8, 255, 255, 0, true );
		KFDL.AddDebugLine( EdgeCenterRejected, PolyCenterRejected, 255, 0, 0, true );
 		KFDL.AddDebugText3D( EdgeCenterRejected + centerOfLineFromEdgeToPolyCenter + vect(0,0,1) * 72.f, "Entire Edge FAILED DUE TO RATING:"$Rating, true, 100, 200, 0, true );

	}
}

event EdgeAndPolySubRegionRejectedDueToLOS( vector EdgeCenterRejected, ENavmeshEdgeType EdgeTypeRecjted, Color EdgeTypeColorRejected, vector SubRegionLocation, vector PolyCenterRejected, vector HitLocation, Actor HitActor )
{
 	local KFDebugLines KFDL;

	if( class'KFGameEngine'.default.bEnableAdvDebugLines )
	{
	//
 		KFDL = class'KFDebugLines'.static.GetDebugLines();
	//
 		KFDL.AddDebugSphere( SubRegionLocation, 32, 8, 255, 0, 0, true );
 		KFDL.AddDebugText3D( SubRegionLocation+ vect(0,0,1) * 72.f, "FAILED DUE TO LOS", true, 100, 100, 0, true );

	}
}

event EdgeAndPolySubRegionRejectedDueToAdjustToss( vector EdgeCenterRejected, ENavmeshEdgeType EdgeTypeRecjted, Color EdgeTypeColorRejected, vector SubRegionLocation, vector PolyCenterRejected, vector HitLocation )
{
 	local KFDebugLines KFDL;

	if( class'KFGameEngine'.default.bEnableAdvDebugLines )
	{
	//
 		KFDL = class'KFDebugLines'.static.GetDebugLines();
	//
 		KFDL.AddDebugSphere( SubRegionLocation, 32, 8, 255, 0, 0, true );
 		KFDL.AddDebugText3D( SubRegionLocation + vect(0,0,1) * 72.f, "FAILED DUE TO AdjustToss", true, 100, 100, 0, true );

	}
}

event EdgeAndPolySubRegionRejectedDueToProximityToTarget( vector EdgeCenterRejected, ENavmeshEdgeType EdgeTypeRecjted, Color EdgeTypeColorRejected, vector SubRegionLocation, vector PolyCenterRejected, float Range, Actor Target )
{
}

`if(`notdefined(ShippingPC))
/** Debug command to advance battle phase */
function DebugNextPhase()
{
    MyHansPawn.SetHuntAndHealMode( true );
    MyHansPawn.Health = MyHansPawn.HealthMax * 0.34;
}
`endif

/*********************************************************************************************
* Pathfinding
********************************************************************************************* */

function bool AmIAllowedToSuicideWhenStuck()
{
	return false;
}


function InitalizeBaseCommand( class<AICommand> CmdClass )
{

	CmdClass.static.InitCommand( self );
}

/*********************************************************************************************
* Dialog
********************************************************************************************* */

function NotifyKilled(Controller Killer, Controller Killed, pawn KilledPawn, class<DamageType> damageType)
{
	if( self == Killer )
	{
		`DialogManager.PlayHansKilledDialog( MyHansPawn, damageType );
	}
}

function PlayDamagePlayerDialog( class<DamageType> DmgType )
{
	`DialogManager.PlayHansDamagePlayerDialog( MyHansPawn, DmgType );
}

DefaultProperties
{
	Steering=none
	DefaultCommandClass=class'KFGame.AICommand_Base_Hans'
	MeleeCommandClass=class'KFGame.AICommand_Base_Hans'
    MinDistanceToPerformGrabAttack=350.f
    MinTimeBetweenGrabAttacks=2.5f
    bRepathOnInvalidStrike=true

	//DefaultBehavior="Hans_BT"
	//UsedETQQueries[ENQ_EnemySelection]="BaseZedEnemySelection"

    AggroEnemySwitchWaitTime=7.0f //7
    RetargetWaitTimeRange=(X=4.4, Y=5.0)  //(X=5.0, Y=7.0)  (X=5.0, Y=8.0)feels good but he finishs people off to much

	bCanDoHeavyBump=true
	LostSightSprintDelay=0.5

    StartShootingRange=4000
    MinShootingRange=300

    LostSightStopFireDelay=1.25
	BurstWaitTime=0.5
	MinBurstAmount=3
	MaxBurstAmount=8
	BarrageLength=3.0
	BarrageWaitTime=1.5
	GrenadeGunInterruptDelay=5.0

	BarrageWaitDistanceCurve=(Points=((InVal=0.f,OutVal=1.25f),(InVal=2500.0f, OutVal=1.f)))
	BurstWaitDistanceCurve=(Points=((InVal=0.f,OutVal=4.f),(InVal=2500.0f, OutVal=1.f)))

	TargetAquisitionDelay=0.25
	DrawGunFireDelay=1.0

	StanceChangeCooldown=0.3
	PostAttackMoveGunCooldown=0.3

	CurrentNadeAttackType=HNT_None
	GrenadeAttackEvalInterval=0.1
	LastRecentSeenEnemyListUpdateTime=0.1

    MinFleeDuration=5.f //10 //3 //2
    MaxFleeDuration=6.f //15  //5  //3
    MaxFleeDistance=3000.f //10000. //5000

	TeleportCooldown=15.0
	HiddenRelocateTeleportThreshold=12.0

    // ---------------------------------------------
    // Danger Evasion Settings
    DangerEvadeSettings.Empty

    DangerEvadeSettings(0)={(ClassName="KFProj_Bullet_Pellet",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.85, 0.95), //0.1, 0.3, 0.5, 0.6
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(1)={(ClassName="KFProj_Nail_Nailgun",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.85, 0.95),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(2)={(ClassName="KFProj_Bullet_DragonsBreath",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.85, 0.95),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(3)={(ClassName="KFProj_HighExplosive_M79",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(4)={(ClassName="KFProj_HighExplosive_M32",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(5)={(ClassName="KFProj_Rocket_RPG7",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(6)={(ClassName="KFDT_Explosive_M16M203",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(7)={(ClassName="KFDT_Explosive_HRGIncendiaryRifle",
                                Cooldowns=(4.0, 3.0, 2.0, 1.0), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}


    //shooting fire
    DangerEvadeSettings(8)={(ClassName="KFProj_CaulkNBurn_GroundFire",
                                Cooldowns=(3.0, 3.0, 2.5, 1.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.0, 0.3, 0.5, 0.8),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(9)={(ClassName="KFProj_FlameThrower_GroundFire",
                                Cooldowns=(3.0, 3.0, 2.5, 1.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.0, 0.3, 0.5, 0.8),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(10)={(ClassName="KFWeap_Flame_CaulkBurn",
                                Cooldowns=(3.0, 3.0, 2.5, 1.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.0, 0.3, 0.5, 0.8),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(11)={(ClassName="KFWeap_Flame_Flamethrower",
                                Cooldowns=(3.0, 3.0, 2.5, 1.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.8),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(12)={(ClassName="KFWeap_Beam_Microwave",
                                Cooldowns=(3.0, 3.0, 2.5,  1.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.8),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(13)={(ClassName="KFProj_Flame_HRGIncendiaryRifle",
                                Cooldowns=(3.0, 3.0, 2.5, 1.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.0, 0.3, 0.5, 0.8),
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.00))},
                                SoloChanceMultiplier=1.0)}

    //Aimed weapons it dodges //sharpshooter
    DangerEvadeSettings(14)={(ClassName="KFWeap_Bow_Crossbow",
                                Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.08, 0.1, 0.2, 0.35),
                                ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
                                SoloChanceMultiplier=1.0)}  
    DangerEvadeSettings(15)={(ClassName="KFWeap_Bow_CompoundBow",
                                Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.08, 0.1, 0.2, 0.35),
                                ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(16)={(ClassName="KFWeap_Rifle_M14EBR",
                                Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.08, 0.1, 0.2, 0.35),
                                ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(17)={(ClassName="KFWeap_Rifle_Winchester1894",
                                Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.08, 0.1, 0.2, 0.35),
                                ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
                                SoloChanceMultiplier=1.0)}
    DangerEvadeSettings(18)={(ClassName="KFWeap_Rifle_RailGun",
                                Cooldowns=(2.3, 2.3, 2.3, 1.3), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.08, 0.1, 0.2, 0.35),
                                ForcedEvadeChances={((FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15), (X=0.0, Y=0.15))},
                                SoloChanceMultiplier=1.0)}

    //Grenades
    DangerEvadeSettings(19)={(ClassName="KFProj_FragGrenade",
                                Cooldowns=(6.0, 5.0, 3.0, 2.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}  
    DangerEvadeSettings(20)={(ClassName="KFProj_MolotovGrenade",
                                Cooldowns=(6.0, 5.0, 3.0, 2.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}  
    DangerEvadeSettings(21)={(ClassName="KFProj_DynamiteGrenade",
                                Cooldowns=(6.0, 5.0, 3.0, 2.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}  
    DangerEvadeSettings(22)={(ClassName="KFProj_NailBombGrenade",
                                Cooldowns=(6.0, 5.0, 3.0, 2.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}  
    DangerEvadeSettings(23)={(ClassName="KFProj_HEGrenade",
                                Cooldowns=(6.0, 5.0, 3.0, 2.5), // Normal, Hard, Suicidal, HoE
                                EvadeChances=(0.1, 0.3, 0.5, 0.6),
                                ForcedEvadeChances={((FL=0.0, FR=0.0), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5), (FL=0.5, FR=0.5))},
                                ReactionDelayRanges={((X=0.0, Y=0.2), (X=0.0, Y=0.0), (X=0.0, Y=0.15), (X=0.0, Y=0.05))},
                                SoloChanceMultiplier=1.0)}

    //Aim Blocks
    DangerEvadeSettings(24)={(ClassName="KFWeap_Rifle_Winchester1894",
                                Cooldowns=(0.5, 0.4, 0.3, 0.2), // Normal, Hard, Suicidal, HoE
                                BlockChances=(0.1, 0.2, 0.7, 0.85))}
    DangerEvadeSettings(25)={(ClassName="KFWeap_Bow_Crossbow",
                                Cooldowns=(0.5, 0.4, 0.3, 0.2), // Normal, Hard, Suicidal, HoE
                                BlockChances=(0.1, 0.2, 0.7, 0.85))}
    DangerEvadeSettings(26)={(ClassName="KFWeap_Rifle_M14EBR",
                                Cooldowns=(0.5, 0.4, 0.3, 0.2), // Normal, Hard, Suicidal, HoE
                                BlockChances=(0.1, 0.2, 0.7, 0.85))}
    DangerEvadeSettings(27)={(ClassName="KFWeap_Rifle_RailGun",
                                Cooldowns=(0.5, 0.4, 0.3, 0.2), // Normal, Hard, Suicidal, HoE
                                BlockChances=(0.1, 0.2, 0.7, 0.85))}
    DangerEvadeSettings(28)={(ClassName="KFWeap_Bow_CompoundBow",
                                Cooldowns=(0.5, 0.4, 0.3, 0.2), // Normal, Hard, Suicidal, HoE
                                BlockChances=(0.1, 0.2, 0.7, 0.85))}   
}