//=============================================================================
// KFPerk_Berserker
//=============================================================================
// The berserker perk class
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
// Christian "schneidzekk" Schneider
//=============================================================================

class KFPerk_Berserker extends KFPerk
	native;

//`include(KFOnlineStats.uci)

var	const				PerkSkill 	BerserkerDamage;
var const				PerkSkill	DamageResistance;                   // 0% resistance to all damage – Max 25%
var const				PerkSkill	NightVision;

var const 	private 	float 		NinjaSprintModifer;
var const 	private 	float		SmashStumbleModifier;
var const 	private 	int 		SmallRadiusSizeSQ;
var 		private		bool		bParryActive;
var const 	private  	float 		ParryDuration;
var const 	private  	float 		ParrySpeed;
var const 	private  	float 		FurySpeed;
var const 	private 	float 		SmashKnockdownMultiplier;
var const 	private 	float 		SpartanZedTimeResistance;
var const 	private 	float 		SpeedDamageModifier;
var const 	private 	float 		SmashHeadDamageModifier;
var const 	private 	float 		VampireAttackSpeedModifier;
var const	private		float		ParryDamageReduction;
var const	private		int			RageRadius;
var const	private		float		RageFleeDuration;
var const	private		int			RageFleeDistance;
var const	private		int			RageDialogEvent;

// Events to play when parry skill is activated / deactivated
var AkEvent ParrySkillSoundModeStart;
var AkEvent ParrySkillSoundModeStop;

enum EBerserkPerkSkills
{
	EBerserkerFortitude,
	EBerserkerNinja,
	EBerserkerVampire,
	EBerserkerSpeed,
	EBerserkerResistance,
	EBerserkerParry,
	EBerserkerSmash,
	EBerserkerFury,
	EBerserkerRage,
	EBerserkerSpartan
};

/** The last time a zed was bumped using battering ram */
var float LastBumpTime;
/** The unique list of actors that have been bumped before the last cooldown reset */
var array<Actor> CurrentBumpedActors;

cpptext
{
	// specific abilities
	virtual UBOOL UseBlueMeleeTrails();
	virtual UBOOL UseRedMeleeTrails();
}

/*********************************************************************************************
* @name	 Perk init and spawning
******************************************************************************************** */

/** (Server) Modify Instigator settings based on selected perk */
function ApplySkillsToPawn()
{
	super.ApplySkillsToPawn();

	if( OwnerPawn != none )
	{
		OwnerPawn.bMovesFastInZedTime = IsFastInZedTime() || IsSWATEnforcerActiveForWeekly();
	}
}

/**
 * @brief Modifies skill related attributes
 */
simulated protected event PostSkillUpdate()
{
	super.PostSkillUpdate();

	// enable tick if we have the regeneration skill
	SetTickIsDisabled( !IsNinjaActive() );
	//clear parry
	ParryTimer();
}

function ClearPerkEffects()
{
	ParryTimer();
	Super.ClearPerkEffects();
}

reliable client function ClientClearPerkEffects()
{
	if (Role != ROLE_Authority)
	{
		super.ClientClearPerkEffects();

		ParryTimer();
	}
}

/**
 * @brief script tick (server)
  *
 * @param DeltaTime time since the last tick
 */
event Tick( float DeltaTime )
{
	super.Tick( DeltaTime );
	if( IsNinjaActive() )
	{
		TickRegen( DeltaTime );
	}
}

simulated event float GetZedTimeSpeedScale()
{
	return GetZedTimeSpeedScaleForColliseum();
}

/*********************************************************************************************
* @name	 Passive skills functions
********************************************************************************************* */
/**
* @brief Modifies the damage dealt
*
* @param InDamage damage
* @param DamageCauser weapon or projectile (optional)
* @param MyKFPM the zed damaged (optional)
* @param DamageInstigator responsible controller (optional)
* @param class DamageType the damage type used (optional)
*/
simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCauser, optional KFPawn_Monster MyKFPM, optional KFPlayerController DamageInstigator, optional class<KFDamageType> DamageType, optional int HitZoneIdx )
{
	local KFWeapon MyKFWeapon;
	local float TempDamage;

	TempDamage = InDamage;

	if( DamageCauser != none )
	{
		if( DamageCauser.IsA( 'Weapon' ) )
		{
			MyKFWeapon = KFWeapon(DamageCauser);
		}
		else if( DamageCauser.IsA( 'Projectile' ) )
		{
			MyKFWeapon = KFWeapon(DamageCauser.Owner);
		}

		`QALog( GetFuncName() @ "Base damage:" @ InDamage , bLogPerk);
		if( ((MyKFWeapon != none && IsWeaponOnPerk( MyKFWeapon,, self.class )) || IsDamageTypeOnPerk( DamageType )) && !IsBlastBrawlers(MyKFWeapon) )
		{
			TempDamage += InDamage * GetPassiveValue( BerserkerDamage,  CurrentLevel );
			if( IsSpeedActive() )
			{
				TempDamage += InDamage * static.GetSpeedDamageModifier();
				`QALog( GetFuncName() @ "Speed extra damage:" @  InDamage * static.GetSpeedDamageModifier(), bLogPerk);
			}

			if( GetParryActive() )
			{
				TempDamage += InDamage * GetSkillValue( PerkSkills[EBerserkerParry] );
				`QALog( GetFuncName() @ "Parry extra damage:" @  InDamage * GetSkillValue( PerkSkills[EBerserkerParry] ), bLogPerk);
			}

			if( HitZoneIdx == HZI_HEAD && IsSmashActive() )
			{
				TempDamage += InDamage * static.GetSmashHeadDamageModifier();
				`QALog( GetFuncName() @ "Smash head hit extra damage:" @  InDamage * static.GetSmashHeadDamageModifier(), bLogPerk);
			}
		}
	}

	`QALog( "Total Damage Given" @ MyKFWeapon @ GetPercentage(InDamage, Round(TempDamage)) @ "Start/End" @ InDamage @ Round(TempDamage), bLogPerk );
	InDamage = Round( TempDamage );
}

/**
 * @brief Some perk skills modify the melee attack speed
 *
 * @param InDuration delay in between attacks
 */
simulated function ModifyMeleeAttackSpeed( out float InDuration, KFWeapon KFW )
{
	local float TempDuration;

	if( KFW == none || !KFW.IsMeleeWeapon() || IsBlastBrawlers(KFW) )
	{
		return;
	}

	TempDuration = InDuration;

	if( IsSpeedActive() )
	{
		`QALog("Speed reduced the attack delay duration by:" @ TempDuration * GetSkillValue( PerkSkills[EBerserkerSpeed] ), bLogPerk);
		TempDuration -= InDuration * GetSkillValue( PerkSkills[EBerserkerSpeed] );
		`QALog( "Speed Melee Attack Speed pct" @ GetSkillValue( PerkSkills[EBerserkerSpeed] ) @ InDuration @ TempDuration, bLogPerk );
	}

	if( GetParryActive() )
	{
		`QALog("Parry reduced the attack delay duration by:" @ TempDuration * ParrySpeed, bLogPerk);
		TempDuration -= InDuration * ParrySpeed;
		`QALog( "Parry Melee Attack Speed pct" @ ParrySpeed, bLogPerk );
	}

	if( IsFuryActive() && KFW.CurrentFireMode == 0 )
	{
		`QALog("Fury reduced the attack delay duration by:" @ TempDuration * GetSkillValue( PerkSkills[EBerserkerFury] ), bLogPerk);
		TempDuration -= InDuration * GetFurySpeed();
		`QALog( "Fury Melee Attack Speed pct" @ GetFurySpeed(), bLogPerk );
	}

	if( IsVampireActive() )
	{
		`QALog("Vampire reduced the attack delay duration by:" @ TempDuration * static.GetVampireAttackSpeedModifier(), bLogPerk);
		TempDuration -= InDuration * static.GetVampireAttackSpeedModifier();
		`QALog( "Vampire Melee Attack Speed pct" @ static.GetVampireAttackSpeedModifier(), bLogPerk );
	}

	`QALog( "Total, Melee Attack Speed" @ GetPercentage( InDuration, TempDuration ) @ "Start/End" @ InDuration @ TempDuration, bLogPerk );
	InDuration = TempDuration;
}

simulated final static function float GetFurySpeed()
{
	return default.FurySpeed;
}

/**
 * @brief Weapons and perk skills can affect the jog/sprint speed
 *
 * @param Speed jog speed
 */
simulated function ModifySpeed( out float Speed )
{
	local KFWeapon MyKFWeapon;
	local KFInventoryManager KFIM;

	if( !IsNinjaActive() )
	{
		return;
	}

	MyKFWeapon = GetOwnerWeapon();
	if( MyKFWeapon == none && CheckOwnerPawn() )
	{
		KFIM = KFInventoryManager(OwnerPawn.InvManager);
		if( KFIM != none && KFIM.PendingWeapon != none )
		{
			MyKFWeapon = KFWeapon(KFIM.PendingWeapon);
		}
	}

	if (MyKFWeapon != none &&
		((MyKFWeapon.IsMeleeWeapon() && !IsBlastBrawlers(MyKFWeapon)) || IsWeaponOnPerk(MyKFWeapon,, self.class)))
	{
		Speed += Speed * GetSkillValue( PerkSkills[EBerserkerNinja] );
	}
}

/**
 * @brief Weapons and perk skills can affect the jog/sprint speed
 *
 * @param Speed sprint speed
 */
simulated function ModifySprintSpeed( out float Speed )
{
	local KFWeapon MyKFWeapon;
	local KFInventoryManager KFIM;

	if( !IsNinjaActive() )
	{
		return;
	}

	MyKFWeapon = GetOwnerWeapon();
	if( MyKFWeapon == none && CheckOwnerPawn() )
	{
		KFIM = KFInventoryManager(OwnerPawn.InvManager);
		if( KFIM != none && KFIM.PendingWeapon != none )
		{
			MyKFWeapon = KFWeapon(KFIM.PendingWeapon);
		}
	}

	if (MyKFWeapon != none &&
		((MyKFWeapon.IsMeleeWeapon() && !IsBlastBrawlers(MyKFWeapon)) || IsWeaponOnPerk(MyKFWeapon,, self.class)))
	{
		Speed += Speed * static.GetNinjaSprintModifier();
	}
}

simulated static final function float GetNinjaSprintModifier()
{
	return default.NinjaSprintModifer;
}

/**
 * @brief Modifies the damage taken
 *
 * @param InDamage damage
 * @param DamageType the damage type used (optional)
 */
function ModifyDamageTaken( out int InDamage, optional class<DamageType> DamageType, optional Controller InstigatedBy )
{
	local float TempDamage;

	if( InDamage <= 0 )
	{
		return;
	}

	TempDamage = InDamage;

	if( IsResistanceActive() )
	{
		TempDamage -= InDamage * GetSkillValue( PerkSkills[EBerserkerResistance] );
		if( ClassIsChildOf( Damagetype,  class'KFDT_Toxic' ) ||
			ClassIsChildOf( Damagetype,  class'KFDT_Sonic' ) )
		{
			TempDamage -= InDamage * GetSkillValue( PerkSkills[EBerserkerResistance] );
		}
	}

	if( GetParryActive() )
	{
		TempDamage -= InDamage * static.GetParryDamageModifier();
		`QALog( "Parry DamageResistance =" @ InDamage * static.GetParryDamageModifier(), bLogPerk );
	}

	TempDamage -= InDamage * GetPassiveDamageResistance( CurrentLevel );

	`QALog( "Total DamageResistance" @ DamageType @ GetPercentage( InDamage, Round( TempDamage ) ) @ "Start/End" @ InDamage @ TempDamage, bLogPerk );
	InDamage = Round( TempDamage );
}

/**
 * @brief Calculates the additional ammo per perk level
 *
 * @param Level Current perk level
 * @return additional ammo
 */
simulated private final static function float GetPassiveDamageResistance( int Level )
{
	return default.DamageResistance.Increment * FFloor( float( Level ) / 5.f );
}

function ModifyBloatBileDoT( out float DoTScaler )
{
	super.ModifyBloatBileDoT( DoTScaler );

	`QALog( "BloatBile DotScaler" @ GetPercentage( DoTScaler, DoTScaler - GetSkillValue( PerkSkills[EBerserkerResistance] ) ),bLogPerk );
	DotScaler -= GetSkillValue( PerkSkills[EBerserkerResistance] );
}

simulated static function GetPassiveStrings( out array<string> PassiveValues, out array<string> Increments, byte Level )
{
	PassiveValues[0] = Round(GetPassiveValue( default.BerserkerDamage, Level ) * 100) $ "%";
	PassiveValues[1] = Round(GetPassiveDamageResistance( Level ) * 100) $ "%";
	PassiveValues[2] = "";
	PassiveValues[3] = "";

	Increments[0] = "[" @ Left( string( default.BerserkerDamage.Increment * 100 ), InStr(string(default.BerserkerDamage.Increment * 100), ".") + 2 )$"% /" @ default.LevelString @ "]";
	Increments[1] = "[" @ Left( string( default.DamageResistance.Increment * 100 ), InStr(string(default.DamageResistance.Increment * 100), ".") + 2 )$"% / 5" @ default.LevelString @ "]";
	Increments[2] = "";
	Increments[3] = "";
}

/*********************************************************************************************
* @name	 Selectable skills functions
********************************************************************************************* */

/**
 * @brief modifies the players health
 *
 * @param InHealth health
 */
function ModifyHealth( out int InHealth )
{
	local float TempHealth;
	TempHealth = InHealth;

	if( IsFortitudeActive() )
	{
		TempHealth += InHealth * GetSKillValue( PerkSkills[EBerserkerFortitude] );
		`QALog( "Health passive + Fortitude health" @ InHealth, bLogPerk );
	}

	InHealth = Round( TempHealth );
}

/**
 * @brief Adds health points earned by killing with the vampire skill
 *
 * @param KFPC Owning controller
 */
function AddVampireHealth( KFPlayerController KFPC, class<DamageType> DT )
{
	if( IsDamageTypeOnPerk( class<KFDamageType>(DT) ) && IsVampireActive() && KFPC.Pawn != none )
	{
		KFPC.Pawn.HealDamage( GetSkillValue( PerkSkills[EBerserkerVampire] ), KFPC, class'KFDT_Healing', false, false );
		`QALog( "Vampire, HealDamage" @ GetSkillValue( PerkSkills[EBerserkerVampire] ), bLogPerk );
	}
}

/**
 * @brief Skills can effect the hard (secondary) melee attack damage
 *
 * @param int The damage to modify
 */
function ModifyHardAttackDamage( out int InDamage )
{
	local float TempDamage;

	TempDamage = InDamage;

	if( IsSmashActive() )
	{
		TempDamage += InDamage * GetSkillValue( PerkSkills[EBerserkerSmash] );
		`QALog( GetFuncName() @ "Smash extra damage:" @  InDamage * GetSkillValue( PerkSkills[EBerserkerSmash] ), bLogPerk);
	}

	`QALog( "Total Hard Atk Dmg" @ GetPercentage( InDamage, TempDamage != InDamage ? Round( TempDamage ) : InDamage ) @ "Start/End" @ InDamage @ ( TempDamage != InDamage ? Round( TempDamage ) : InDamage ), bLogPerk );
	InDamage = Round( TempDamage );
}

/**
 * @brief Skills can effect the light melee attack damage
 *
 * @param int The damage to modify
 */
function ModifyLightAttackDamage( out int InDamage )
{
	local float TempDamage;

	if( IsFuryActive() )
	{
		TempDamage = InDamage;
		TempDamage += InDamage * GetSkillValue( PerkSkills[EBerserkerFury] );
		InDamage = Round( TempDamage );
	}
}

/**
 * @brief Called when the parry time ends
 */
simulated function ParryTimer()
{
	bParryActive = false;
	SetTickIsDisabled( !IsNinjaActive() );

	if( OwnerPC != none )
	{
		OwnerPC.SetPerkEffect( false );
	}

	OwnerPC.PlaySoundBase( ParrySkillSoundModeStop, true );
}

/**
 * @brief Change this to effect intensity change when we have it
 *
 * @param float InDuration The effects duration
 */
function ModifyScreamEffectDuration( out float InDuration )
{
	if( IsResistanceActive() )
	{
		InDuration *= 1 - GetSkillValue( PerkSkills[EBerserkerResistance]);
	}
}


/**
 * @brief skills and weapons can modify the stumbling power
 * @return stumbling power modifier
 */
function float GetStumblePowerModifier( optional KFPawn KFP, optional class<KFDamageType> DamageType, optional out float CooldownModifier, optional byte BodyPart )
{
	if( IsSmashActive() && IsDamageTypeOnPerk(DamageType) )
	{
        return static.GetSmashStumbleModifier();
	}

	return 0.f;
}

 final static function float GetSmashStumbleModifier()
 {
 	return default.SmashStumbleModifier;
 }

/**
 * @brief Gets the small radius kill xp based on the difficulty
 *
 * @param Difficulty game difficulty
 * @return XP
 */
static function int GetSmallRadiusKillXP( byte Difficulty )
{
	return default.SecondaryXPModifier[Difficulty];
}

/**
 * @brief Gets the squared small kill radius
 *
 * @return UUs squared
 */
static function int GetSmallRadiusKillDistanceSQ()
{
	return default.SmallRadiusSizeSQ;
}

/**
 * @brief can the perk earn small radius kill xp
 * @return perk == berserker && DT is on perk
 */
function bool CanEarnSmallRadiusKillXP( class<DamageType> DT )
{
	return IsDamageTypeOnPerk( class<KFDamageType>(DT) ) || IsBackupDamageTypeOnPerk( DT );
}

simulated function float GetSirenScreamStrength()
{
	if( IsResistanceActive() )
	{
		return 1 - GetSkillValue( PerkSkills[EBerserkerResistance] );
	}

	return super.GetSirenScreamStrength();
}

function NotifyZedTimeStarted()
{
	local Pawn OtherPawn;
	local KFPlayerController KFPC;
	local KFPowerUp PowerUp;
	local KFAIController KFAIC;
	local KFGameInfo GameInfo;
	local bool bScaredAI;
	local bool bCannotBeHealed;
	local KFGameReplicationInfo KFGRI;

	if( IsRageActive() && OwnerPawn != none )
	{
		KFPC = KFPlayerController(OwnerPawn.Controller);

		if( KFPC != none )
		{
			PowerUp = KFPC.GetPowerUp();
			bCannotBeHealed = (PowerUp != none && !PowerUp.CanBeHealedWhilePowerUpIsActive);

			GameInfo = KFGameInfo(WorldInfo.Game);
			if(GameInfo != none)
			{
				bCannotBeHealed = bCannotBeHealed ||(GameInfo.OutbreakEvent != none && GameInfo.OutbreakEvent.ActiveEvent.bCannotBeHealed);
			}

			// VIP cannot heal
			KFGRI = KFGameReplicationInfo(WorldInfo.GRI);
		    if (KFGRI != none
				&& KFGRI.VIPRepPlayer != none
				&& KFGRI.VIPRepPlayer == KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo))
    		{
				bCannotBeHealed = true;
			}
		}

		if( bCannotBeHealed == false )
		{
			OwnerPawn.Health += OwnerPawn.HealthMax * GetSkillValue( PerkSkills[EBerserkerRage] );
			OwnerPawn.Health = Min( OwnerPawn.Health, OwnerPawn.HealthMax );
		}

		foreach WorldInfo.AllPawns( class'Pawn', OtherPawn, OwnerPawn.Location, static.GetRageRadius() )
		{
			KFAIC = KFAIController(OtherPawn.Controller);
			if( KFAIC != none )
			{
				KFAIC.DoPauseAI( static.GetRageFleeDuration(), true, false, true);
				//KFAIC.DoFleeFrom( OwnerPawn, static.GetRageFleeDuration(), static.GetRageFleeDistance(),, true );
				bScaredAI = true;
			}
			else
			{
				continue;
			}
		}

		if( bScaredAI )
		{
			KFGameInfo(Owner.WorldInfo.Game).DialogManager.PlayDialogEvent( OwnerPawn, RageDialogEvent );
		}
	}
}

private static function int GetRageRadius()
{
	return default.RageRadius;
}

private static function float GetRageFleeDuration()
{
	return default.RageFleeDuration;
}

private static function int GetRageFleeDistance()
{
	return default.RageFleeDistance;
}

/**
 * @brief Skills can modify the zed time time dilation
 *
 * @param StateName used weapon's state
 * @return time dilation modifier
 */
simulated function float GetZedTimeModifier( KFWeapon W )
{
	local name StateName;

	StateName = W.GetStateName();

	if( IsWeaponOnPerk( W,, self.class ) && ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE )
	{
		if( CouldSpartanBeActive() )
		{
			return default.SpartanZedTimeResistance;
		}
	}

	return 0.f;
}

private static function float GetSpeedDamageModifier()
{
	return default.SpeedDamageModifier;
}

private static function float GetSmashHeadDamageModifier()
{
	return default.SmashHeadDamageModifier;
}

private static function float GetVampireAttackSpeedModifier()
{
	return default.VampireAttackSpeedModifier;
}

private static function float GetParryDamageModifier()
{
	return default.ParryDamageReduction;
}

/*********************************************************************************************
* @name	 Getters / Setters
********************************************************************************************* */

/**
 * @brief Checks if the fortitude skill is active
 *
 * @return true if we have the skill enabled
 */
final private function bool IsFortitudeActive()
{
	return PerkSkills[EBerserkerFortitude].bActive && IsPerkLevelAllowed(EBerserkerFortitude);
}

/**
 * @brief Checks if the ninja skill is active
 *
 * @return true if we have the skill enabled
 */
simulated final private function bool IsNinjaActive()
{
	return PerkSkills[EBerserkerNinja].bActive && IsPerkLevelAllowed(EBerserkerNinja);
}


/**
 * @brief Checks if the Vampire skill is active
 *
 * @return true if we have the skill enabled
 */
simulated private function bool IsVampireActive()
{
	return PerkSkills[EBerserkerVampire].bActive && IsPerkLevelAllowed(EBerserkerVampire);
}


/**
 * @brief Checks if the Speed skill is active
 *
 * @return true if we have the skill enabled
 */
simulated final private function bool IsSpeedActive()
{
	return PerkSkills[EBerserkerSpeed].bActive && IsPerkLevelAllowed(EBerserkerSpeed);
}

/**
 * @brief Do we need to tick the perk?
 *
 * @return true if perk needs to be ticked
 */
simulated function bool PerkNeedsTick()
{
	return IsNinjaActive();
}

/**
 * @brief Player successfully parried an attack, save it if Parry skill active
 */
simulated function SetSuccessfullParry()
{
	if( IsParryActive() )
	{
		bParryActive = true;
		SetTickIsDisabled( false );
		SetTimer( ParryDuration, false, nameOf( ParryTimer ));

		if( OwnerPC != none )
		{
			OwnerPC.SetPerkEffect( true );
		}

		OwnerPC.PlaySoundBase( ParrySkillSoundModeStart, true );
	}
}

/**
 * @brief Checks if the Parry skill is active
 *
 * @return true if we have the skill enabled
 */
simulated function bool IsParryActive()
{
	return PerkSkills[EBerserkerParry].bActive && IsPerkLevelAllowed(EBerserkerParry);
}

/**
 * @brief Checks if the resistance skill is active
 *
 * @return true if we have the skill enabled
 */
simulated function bool IsResistanceActive()
{
	return PerkSkills[EBerserkerResistance].bActive && IsPerkLevelAllowed(EBerserkerResistance);
}

/**
 * @brief Check if conditions for the Parry skill are true
 * @return true if parried attack successfully earlier and Parry skill active
 */
simulated event bool GetParryActive()
{
	return IsParryActive() && bParryActive;
}

/**
 * @brief Checks if the night vision skill is active
 * @return true if we have the skill enabled
 */
simulated function bool HasNightVision()
{
	return true;
}

/**
 * @brief Checks if the Smash skill is active
 *
 * @return true if we have the skill enabled
 */
final simulated protected event bool IsSmashActive()
{
	return PerkSkills[EBerserkerSmash].bActive && IsPerkLevelAllowed(EBerserkerSmash);
}

/**
 * @brief Checks if the fury skill is active
 *
 * @return true if we have the skill enabled
 */
final simulated private function bool IsFuryActive()
{
	return PerkSkills[EBerserkerFury].bActive && IsPerkLevelAllowed(EBerserkerFury);
}

/**
 * @brief Checks if the player can be grabbed by clots
 *
 * @return true if we are not grabbable
 */
function bool CanNotBeGrabbed()
{
	return true;
}

/**
 * @brief Checks if the menace skill is active and if we are in zed time
 *
 * @return true if we have the skill enabled
 */
simulated private event bool IsRageActive()
{
	return PerkSkills[EBerserkerRage].bActive && WorldInfo.TimeDilation < 1.f && IsPerkLevelAllowed(EBerserkerRage) && !ShouldDisableZedTimeSkillsForWeekly();
}

/**
 * @brief Checks if the Spartan skill is active and if we are in zed time
 *
 * @return true if we have the skill enabled
 */
simulated final private event bool IsSpartanActive()
{
	return PerkSkills[EBerserkerSpartan].bActive && WorldInfo.TimeDilation < 1.f && IsPerkLevelAllowed(EBerserkerSpartan) && !ShouldDisableZedTimeSkillsForWeekly();
}

simulated private event bool CouldSpartanBeActive()
{
	return PerkSkills[EBerserkerSpartan].bActive && IsPerkLevelAllowed(EBerserkerSpartan) && !ShouldDisableZedTimeSkillsForWeekly();
}

/**
 * @brief Spartan resists zed time. You move at normal speed.
 *
 * @return true if Spartan are active
 */
simulated function bool IsFastInZedTime()
{
	return PerkSkills[EBerserkerSpartan].bActive && IsPerkLevelAllowed(EBerserkerSpartan) && !ShouldDisableZedTimeSkillsForWeekly();
}

simulated function bool IsFastInZedTimeOutbreak()
{
	return ShouldDisableZedTimeSkillsForWeekly();
}

/**
 * @brief Checks if we should be using instigator for time dilation
 *      while in zed time.  Specific to Spartan mode for Berserker.
 *
 * @return true if Spartan is active
 */
simulated event bool ShouldUseFastInstigatorDilation(KFWeapon Weap)
{
    if (PerkSkills[EBerserkerSpartan].bActive && Weap != none && IsPerkLevelAllowed(EBerserkerSpartan) && !ShouldDisableZedTimeSkillsForWeekly())
    {
        return Weap.GetWeaponPerkClass(default.Class) == default.Class;
    }

    return false;
}

/*********************************************************************************************
* @name	 Special Weekly Modes
********************************************************************************************* */

simulated event float GetZedTimeSpeedScaleForColliseum()
{
	return IsSWATEnforcerActiveForWeekly() ? class'KFPerk_Swat'.default.SWATEnforcerZedTimeSpeedScale : 1.f;
}

function bool ShouldDisableZedTimeSkillsForWeekly()
{
	return MyKFGI != none && MyKFGI.OutbreakEvent != none && MyKFGI.OutbreakEvent.ActiveEvent.bColliseumSkillConditionsActive;
}

function bool IsSWATEnforcerActiveForWeekly()
{
	return MyKFGI != none && MyKFGI.OutbreakEvent != none && MyKFGI.OutbreakEvent.ActiveEvent.bColliseumSkillConditionsActive;
}

simulated function bool ShouldKnockDownOnBump()
{
	return IsSWATEnforcerActiveForWeekly() && WorldInfo.TimeDilation < 1.f;
}

simulated function OnBump(Actor BumpedActor, KFPawn_Human BumpInstigator, vector BumpedVelocity, rotator BumpedRotation)
{
	local KFPawn_Monster KFPM;
	local bool CanBump;

	if (ShouldKnockDownOnBump() && Normal(BumpedVelocity) dot Vector(BumpedRotation) > 0.7f)
	{
		KFPM = KFPawn_Monster(BumpedActor);
		if (KFPM != none)
		{
			// cooldown so that the same zed can't be bumped multiple frames back to back
			//	especially relevant if they can't be knocked down or stumbled so the player is always bumping them
			if (WorldInfo.TimeSeconds - LastBumpTime > class'KFPerk_Swat'.default.BumpCooldown)
			{
				CurrentBumpedActors.length = 0;
				CurrentBumpedActors.AddItem(BumpedActor);
				CanBump = true;
			}
			// if still within the cooldown time, can still bump the actor as long as it hasn't been bumped yet
			else if (CurrentBumpedActors.Find(BumpedActor) == INDEX_NONE)
			{
				CurrentBumpedActors.AddItem(BumpedActor);
				CanBump = true;
			}

			LastBumpTime = WorldInfo.TimeSeconds;

			if (CanBump)
			{
				if (KFPM.IsHeadless())
				{
					KFPM.TakeDamage(KFPM.HealthMax, BumpInstigator.Controller, BumpInstigator.Location,
						Normal(vector(BumpedRotation)) * class'KFPerk_Swat'.default.BumpMomentum, class'KFPerk_Swat'.default.BumpDamageType);
				}
				else
				{
					KFPM.TakeDamage(class'KFPerk_Swat'.default.BumpDamageAmount, BumpInstigator.Controller, BumpInstigator.Location,
						Normal(vector(BumpedRotation)) * class'KFPerk_Swat'.default.BumpMomentum, class'KFPerk_Swat'.default.BumpDamageType);
					KFPM.Knockdown(BumpedVelocity * 3, vect(1, 1, 1), KFPM.Location, 1000, 100);
				}
			}
		}
	}
}

/*********************************************************************************************
* @name	  UI / HUD
********************************************************************************************* */
simulated function class<EmitterCameraLensEffectBase> GetPerkLensEffect( class<KFDamageType> DmgType )
{
	if( ClassIsChildOf( DmgType,  class'KFDT_Toxic' ) )
	{
		return IsResistanceActive() ? DmgType.default.AltCameraLensEffectTemplate : super.GetPerkLensEffect( DmgType );
	}

	return super.GetPerkLensEffect( DmgType );
}

/*********************************************************************************************
* @name	 Logging / debug
********************************************************************************************* */

/** QA Logging - Report Perk Info */
simulated function LogPerkSkills()
{
	super.LogPerkSkills();

	if ( bLogPerk )
	{
	}
}

static function PrepareExplosive( Pawn ProjOwner, KFProjectile Proj, optional float AuxRadiusMod = 1.0f, optional float AuxDmgMod = 1.0f )
{
    local KFPlayerController KFPC;
    local KFPerk InstigatorPerk;

    if( ProjOwner != none )
    {
	    // Change the radius and damage based on the perk
	    if( ProjOwner.Role == ROLE_Authority )
	    {
	    	KFPC = KFPlayerController( ProjOwner.Controller );
	    	if( KFPC != none )
	    	{
		        InstigatorPerk = KFPC.GetPerk();
		        Proj.ExplosionTemplate.DamageRadius *= InstigatorPerk.GetAoERadiusModifier() * AuxRadiusMod;
		    }
	    }
	}
}


DefaultProperties
{
	ParryDuration=8.f //6.f //10.f
	ParrySpeed=0.05
	FurySpeed=0.05
	NinjaSprintModifer=0.25
	SmashStumbleModifier=2.f
	SpartanZedTimeResistance=1.f 			//Attack speed zed time resistance
	SpeedDamageModifier=0.2
	SmashHeadDamageModifier=0.25
	VampireAttackSpeedModifier=0.2f
	ParryDamageReduction=0.3f //0.25f //0.4f //0.75
	RageRadius=1000 //300
	RageFleeDuration=5.f //3
	RageFleeDistance=2500 //5000
	RageDialogEvent=229

	RegenerationInterval=1
	RegenerationAmount=1 //2

	ZedTimeModifyingStates(0)="MeleeChainAttacking"
	ZedTimeModifyingStates(1)="MeleeAttackBasic"
	ZedTimeModifyingStates(2)="MeleeHeavyAttacking"
	ZedTimeModifyingStates(3)="MeleeSustained"
	ZedTimeModifyingStates(4)="WeaponFiring"

	PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Berserker'

	ProgressStatID=STATID_Bsrk_Progress
	PerkBuildStatID=STATID_Bsrk_Build

	SecondaryXPModifier(0)=6
	SecondaryXPModifier(1)=8
	SecondaryXPModifier(2)=10
	SecondaryXPModifier(3)=14
	SmallRadiusSizeSQ=40000

	PrimaryWeaponDef=class'KFWeapDef_Crovel'
	KnifeWeaponDef=class'KFweapDef_Knife_Berserker'
	GrenadeWeaponDef=class'KFWeapDef_Grenade_Berserker'

	BerserkerDamage=(Name="Berserker Damage",Increment=0.01,Rank=0,StartingValue=0.f,MaxValue=0.25)
	DamageResistance=(Name="Damage Resistance",Increment=0.02f,Rank=0,StartingValue=0.f,MaxValue=0.1f) //0.03f, 0, 0.f, 0.15f
	NightVision=(Name="Night Vision",Increment=0.f,Rank=0,StartingValue=0.f,MaxValue=0.f)

	PerkSkills(EBerserkerFortitude)=(Name="Fortitude",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Fortitude",Increment=0.f,Rank=0,StartingValue=1.0,MaxValue=1.0) //0.75
	PerkSkills(EBerserkerNinja)=(Name="Ninja",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Ninja",Increment=0.f,Rank=0,StartingValue=0.2f,MaxValue=0.2f)
	PerkSkills(EBerserkerVampire)=(Name="Vampire",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Vampire",Increment=0.f,Rank=0,StartingValue=3.f,MaxValue=3.f) //4.f
	PerkSkills(EBerserkerSpeed)=(Name="Speed",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Speed",Increment=0.f,Rank=0,StartingValue=0.2f,MaxValue=0.2f)
	PerkSkills(EBerserkerResistance)=(Name="Resistance",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_PoisonResistance",Increment=0.f,Rank=0,StartingValue=0.15f,MaxValue=0.15f) //0.15 //0.2
	PerkSkills(EBerserkerParry)=(Name="Parry",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Parry",Increment=0.f,Rank=0,StartingValue=0.35,MaxValue=0.35)
	PerkSkills(EBerserkerSmash)=(Name="Smash",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Smash",Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f)
	PerkSkills(EBerserkerFury)=(Name="Fury",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Intimidate",Increment=0.f,Rank=0,StartingValue=0.3f,MaxValue=0.3f)
	PerkSkills(EBerserkerRage)=(Name="Rage",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Menace",Increment=0.f,Rank=0,StartingValue=0.5,MaxValue=0.5)
	PerkSkills(EBerserkerSpartan)=(Name="Spartan",IconPath="UI_PerkTalent_TEX.berserker.UI_Talents_Berserker_Flash",Increment=0.f,Rank=0,StartingValue=0.f,MaxValue=0.f)

	ParrySkillSoundModeStart=AkEvent'WW_GLO_Runtime.Play_Beserker_Parry_Mode'
	ParrySkillSoundModeStop=AkEvent'WW_GLO_Runtime.Stop_Beserker_Parry_Mode'

    // Skill tracking
	HitAccuracyHandicap=2.5
	HeadshotAccuracyHandicap=-2.0
	AutoBuyLoadOutPath=(class'KFWeapDef_Crovel', class'KFWeapDef_Katana', class'KFWeapDef_Pulverizer', class'KFWeapDef_Eviscerator', class'KFWeapDef_AbominationAxe')

	// Prestige Rewards
	PrestigeRewardItemIconPaths[0]="WEP_SkinSet_Prestige01_Item_TEX.knives.BerserkerKnife_PrestigePrecious_Mint_large"
	PrestigeRewardItemIconPaths[1]="WEP_SkinSet_Prestige02_Item_TEX.tier01.Crovel_PrestigePrecious_Mint_large"
	PrestigeRewardItemIconPaths[2]="WEP_skinset_prestige03_itemtex.tier02.Katana_PrestigePrecious_Mint_large"
	PrestigeRewardItemIconPaths[3]="wep_skinset_prestige04_itemtex.tier03.Pulverizer_PrestigePrecious_Mint_Large"
	PrestigeRewardItemIconPaths[4]="WEP_SkinSet_Prestige05_Item_TEX.tier04.Eviscerator_PrestigePrecious_Mint_large"
}
