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

class KFPerk_Demolitionist extends KFPerk
		native;

//`include(KFOnlineStats.uci)

var				const	PerkSkill 					ExplosiveDamage;			// 1% increased explosive damage per level (max 25%)
var 			const	PerkSkill					ExplosiveResistance;        // 10% explosive resistance, additional 2% resistance per level (max 60%)
var 			const	PerkSkill					ExplosiveAmmo;            	// 1 extra explosive ammo for every 5 levels. Weapons only

var						Array<KFPawn_Human>			SuppliedPawnList;

var 	private const   float 						SharedExplosiveResistance;
var 	private const 	class<Damagetype>			ExplosiveResistableDamageTypeSuperClass;
/** the radius in within the shared explosive resistance works */
var 	private const 	float 						ExplosiveResistanceRadius;

/** Template for explosion when you die with the Sacrifice perk skill*/
var 					KFGameExplosion				SacrificeExplosionTemplate;
/** Template for explosion when do an explosion with the Nuke perk skill*/
var 					KFGameExplosion				NukeExplosionTemplate;
/** Template for explosion when a door is opened by zeds that has been welded when you have the Door Trap perk skill*/
var 					KFGameExplosion				DoorTrapExplosionTemplate;
var 					class<KFExplosionActor>		NukeExplosionActorClass;
/** How much to modify a projectile's damage when the nuke skill is active */
var 	private const 	float 						NukeDamageModifier;
/** How much to modify a projectile's damage radius when the nuke skill is active */
var 	private const 	float 						NukeRadiusModifier;
var 					AkEvent 					ConcussiveExplosionSound;
var 	private	const	float 						AoeDamageModifier;
var 	private	const	int 						LingeringNukePoisonDamage;
var 	private const	array<name>					PassiveExtraAmmoIgnoredClassNames;
var 	private const	array<name>					ExtraAmmoIgnoredClassNames;
var		private const   array<name>					TacticalReloadAsReloadRateClassNames;
var 	private const 	array<name>					OnlySecondaryAmmoWeapons;
var 	private const 	array<name>					DamageIgnoredDTs;
var 	private	const	float 						DaZedEMPPower;
var 	private const   float 						ProfessionalAoEModifier;
var 	private			bool 						bUsedSacrifice;
var 	private const 	class<KFDamagetype>			LingeringNukeDamageType;

/** The last time an HX25 projectile spawned by our owner caused a nuke */
var 	private transient float LastHX25NukeTime;

enum EDemoSkills
{
	EDemoDamage,
	EDemoTacticalReload,
	EDemoDirectHit,
	EDemoAmmo,
	EDemoSirenResistance,
	EDemoAoE,
	EDemoCriticalHit,
	EDemoConcussiveForce,
	EDemoNuke,
	EDemoProfessional
};

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

/**
 * @brief(Server) Modify Instigator settings based on selected perk
 */
function ApplySkillsToPawn()
{
	local KFGameReplicationInfo KFGRI;

	Super.ApplySkillsToPawn();

	KFGRI = KFGameReplicationInfo(WorldInfo.GRI);

	if( MyPRI != none )
	{
		MyPRI.bNukeActive = IsNukeActive();
		MyPRI.bConcussiveActive = IsConcussiveForceActive();
	}
	if (KFGRI == none && KFGRI.bTraderIsOpen)
	{
		return;
	}
	//ResetSupplier();
}

/**
 * @brief Resets certain perk values on wave start/end
 */
function OnWaveEnded()
{
	Super.OnWaveEnded();
	bUsedSacrifice = false;
}

/**
* @brief Resets certain perk values on wave start/end
*/
function OnWaveStart()
{
	Super.OnWaveStart();
	ResetSupplier();
}

/*********************************************************************************************
* @name	 Passives
********************************************************************************************* */
/**
 * @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 KFW;
	local float TempDamage;
	local bool bIsCrossboom;

	if( DamageType != none && IsDamageIgnoredDT( DamageType ) )
	{
		return;
	}

	TempDamage = InDamage;
	`QAlog( "ModifyDamage() Start Damage:" @ InDamage, bLogPerk );

	if( DamageCauser != none )
	{
		KFW = GetWeaponFromDamageCauser( DamageCauser );
	}

	bIsCrossboom = IsHRGCrossboom(KFW);

	if( (KFW != none && (IsWeaponOnPerk( KFW,, self.class )) || bIsCrossboom) || (DamageType != none && IsDamageTypeOnPerk( DamageType )) )
	{
		`QALog( "Base Damage Given" @ DamageType @ KFW @ InDamage, bLogPerk );
		//Passive
		TempDamage +=  InDamage * GetPassiveValue( ExplosiveDamage, CurrentLevel );
		`QALog( "ExplosiveDamage Given" @ DamageType @ KFW @ InDamage * GetPassiveValue( ExplosiveDamage, CurrentLevel ), bLogPerk );
		//Damage skill
		if( IsDamageActive() )
		{
			TempDamage +=  InDamage * GetSkillValue( PerkSkills[EDemoDamage] );
			`QALog( "Bombadier Given" @ DamageType @ KFW @ InDamage * GetSkillValue( PerkSkills[EDemoDamage] ), bLogPerk );
		}

		if( IsDirectHitActive() && ((DamageType != none && IsDamageTypeOnPerk( DamageType )) || bIsCrossboom ))
		{
			if( class<KFDT_Ballistic_Shell>(DamageType) != none || class<KFDT_Bludgeon>(DamageType)!=none || (bIsCrossboom && class<KFDT_Piercing>(DamageType) != none))
			{
				TempDamage += InDamage * GetSkillValue( PerkSkills[EDemoDirectHit] );
				`QALog( "High Impact Damage Given" @ DamageType @ KFW @ InDamage * GetSkillValue( PerkSkills[EDemoDirectHit] ), bLogPerk );
			}
		}

		if( IsCriticalHitActive() && MyKFPM != none &&
			IsCriticalHitZone( MyKFPM, HitZoneIdx ) )
		{
			TempDamage += InDamage * GetSkillValue( PerkSkills[EDemoCriticalHit] );
			`QALog( "Armor Piercing Rounds Damage Given" @ DamageType @ KFW @ InDamage * GetSkillValue( PerkSkills[EDemoCriticalHit] ), bLogPerk );
		}

		if( IsAoEActive() )
		{
			TempDamage -= InDamage * GetAoEDamageModifier();
		}
	}

	`QALog( "Total Damage Given" @ DamageType @ KFW @ GetPercentage( InDamage, Round( TempDamage ) ), bLogPerk );
	InDamage = Round( TempDamage );
	`QAlog( "ModifyDamage() Total Damage:" @ InDamage, bLogPerk );
}

static protected function bool IsDamageIgnoredDT( class<KFDamageType> KFDT )
{
	return default.DamageIgnoredDTs.Find( KFDT.name ) != INDEX_NONE;
}

protected function bool IsCriticalHitZone( KFPawn TestPawn, int HitZoneIndex )
{
	if( TestPawn != none && HitzoneIndex >= 0 && HitzoneIndex < TestPawn.HitZones.length )
	{
		return TestPawn.HitZones[HitZoneIndex].DmgScale > 1.f;
	}

	return false;
}

/**
 * @brief DamageType on perk?
 *
 * @param KFDT The damage type
 * @return true/false
 */
static function bool IsDamageTypeOnPerk( class<KFDamageType> KFDT )
{
	if( KFDT != none && IsDamageIgnoredDT( KFDT ) )
	{
		return false;
	}

	return super.IsDamageTypeOnPerk( KFDT );
}

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

	if( InDamage <= 0 )
	{
		return;
	}

	TempDamage = InDamage;

	if( ClassIsChildOf( DamageType, class'KFDT_Explosive' ) )
	{
		TempDamage *= 1 - GetPassiveValue( ExplosiveResistance, CurrentLevel );

        if (InstigatedBy == OwnerPC && (IsNukeActive() || IsProfessionalActive()) && ShouldNeverDud())
        {
            TempDamage = 0;
        }
	}

	`QALog( "Total Damage Resistance" @ DamageType @ GetPercentage(InDamage, Round(TempDamage)), bLogPerk );
	InDamage = Round( TempDamage );
}

/**
 * @brief Modifies starting spare ammo
 *
 * @param KFW The weapon
 * @param PrimarySpareAmmo ammo amount
 * @param TraderItem the weapon's associated trader item info
 */
simulated function ModifySpareAmmoAmount( KFWeapon KFW, out int PrimarySpareAmmo, optional const out STraderItem TraderItem, optional bool bSecondary=false )
{
	local array< class<KFPerk> > WeaponPerkClass;
	local bool bUsesAmmo;
	local name WeaponClassName;

	if( KFW == none )
	{
		WeaponPerkClass = TraderItem.AssociatedPerkClasses;
		bUsesAmmo = TraderItem.WeaponDef.static.UsesAmmo();
		WeaponClassName = TraderItem.ClassName;
	}
	else
	{
		WeaponPerkClass = KFW.GetAssociatedPerkClasses();
		bUsesAmmo = KFW.UsesAmmo();
		WeaponClassName = KFW.class.Name;
	}

	if( bUsesAmmo )
	{
		GivePassiveExtraAmmo( PrimarySpareAmmo, KFW, WeaponPerkClass, WeaponClassName, bSecondary );
		GiveAmmoExtraAmmo( PrimarySpareAmmo, KFW, WeaponPerkClass, WeaponClassName, bSecondary );
	}
}

simulated private function GivePassiveExtraAmmo( out int PrimarySpareAmmo, KFWeapon KFW, array< class<KFPerk> > WeaponPerkClass, name WeaponClassName, optional bool bSecondary=false )
{
	if( ShouldGiveOnlySecondaryAmmo( WeaponClassName ) && !bSecondary )
	{
		return;
	}

	if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) &&
		PassiveExtraAmmoIgnoredClassNames.Find( WeaponClassName ) == INDEX_NONE )
	{
		PrimarySpareAmmo += GetExtraAmmo( CurrentLevel );
	}
}

simulated private function GiveAmmoExtraAmmo( out int PrimarySpareAmmo, KFWeapon KFW, array< class<KFPerk> > WeaponPerkClass, name WeaponClassName, optional bool bSecondary=false  )
{
	if( ShouldGiveOnlySecondaryAmmo( WeaponClassName ) && !bSecondary )
	{
		return;
	}

	if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) &&
		ExtraAmmoIgnoredClassNames.Find( WeaponClassName ) == INDEX_NONE )
	{
		PrimarySpareAmmo += GetAmmoExtraAmmo();
	}
}

simulated function bool ShouldGiveOnlySecondaryAmmo( name WeaponClassName )
{
	return OnlySecondaryAmmoWeapons.Find( WeaponClassName ) != INDEX_NONE;
}

/**
 * @brief Modifies the max spare ammo
 *
 * @param KFW The weapon
 * @param PrimarySpareAmmo ammo amount
 * @param TraderItem the weapon's associated trader item info
 */
simulated function ModifyMaxSpareAmmoAmount( KFWeapon KFW, out int MaxSpareAmmo, optional const out STraderItem TraderItem, optional bool bSecondary=false )
{
	local array< class<KFPerk> > WeaponPerkClass;
	local bool bUsesAmmo;
	local name WeaponClassName;

	if( KFW == none )
	{
		WeaponPerkClass = TraderItem.AssociatedPerkClasses;
		bUsesAmmo = TraderItem.WeaponDef.static.UsesAmmo();
		WeaponClassName = TraderItem.ClassName;
	}
	else
	{
		WeaponPerkClass = KFW.GetAssociatedPerkClasses();
		bUsesAmmo = KFW.UsesAmmo();
		WeaponClassName = KFW.class.Name;
	}

	if( bUsesAmmo )
	{
		GivePassiveExtraAmmo( MaxSpareAmmo, KFW, WeaponPerkClass, WeaponClassName, bSecondary );
		GiveAmmoExtraAmmo( MaxSpareAmmo, KFW, WeaponPerkClass, WeaponClassName, bSecondary );
	}
}

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

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

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

    if( ProjOwner != none )
    {
	    if( Proj.bWasTimeDilated )
	    {
	        InstigatorPRI = KFPlayerReplicationInfo( ProjOwner.PlayerReplicationInfo );
	        if( InstigatorPRI != none )
	        {
	            if( InstigatorPRI.bNukeActive && class'KFPerk_Demolitionist'.static.ProjectileShouldNuke(Proj) )
	            {
	                Proj.ExplosionTemplate = class'KFPerk_Demolitionist'.static.GetNukeExplosionTemplate();
	                Proj.ExplosionTemplate.Damage = Proj.default.ExplosionTemplate.Damage * class'KFPerk_Demolitionist'.static.GetNukeDamageModifier() * AuxDmgMod;
	                Proj.ExplosionTemplate.DamageRadius = Proj.default.ExplosionTemplate.DamageRadius * class'KFPerk_Demolitionist'.static.GetNukeRadiusModifier() * AuxRadiusMod;
	                Proj.ExplosionTemplate.DamageFalloffExponent = Proj.default.ExplosionTemplate.DamageFalloffExponent;
	            }
	            else if( InstigatorPRI.bConcussiveActive && Proj.AltExploEffects != none && class'KFPerk_Demolitionist'.static.ProjectileShouldConcussive(Proj) )
	            {
	                Proj.ExplosionTemplate.ExplosionEffects = Proj.AltExploEffects;
	                Proj.ExplosionTemplate.ExplosionSound = class'KFPerk_Demolitionist'.static.GetConcussiveExplosionSound();
	            }
	        }
	    }

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

				if (class'KFPerk_Demolitionist'.static.ProjectileShouldExplosionChangeRadius(Proj))
				{
					InstigatorPerk = KFPC.GetPerk();
		        	Proj.ExplosionTemplate.DamageRadius *= InstigatorPerk.GetAoERadiusModifier();
				}
		    }
	    }
	}
}

simulated function SetLastHX25NukeTime( float NewTime )
{
	LastHX25NukeTime = NewTime;
}
simulated function float GetLastHX25NukeTime()
{
	return LastHX25NukeTime;
}

simulated function float GetAoERadiusModifier()
{
	local float RadiusModifier;

	RadiusModifier = IsAoEActive() ? GetSkillValue( PerkSkills[EDemoAoE] ) : 1.f;
	RadiusModifier = (IsProfessionalActive() && WorldInfo.TimeDilation < 1.f) ?
		(RadiusModifier + default.ProfessionalAoEModifier) :
		RadiusModifier;

	return RadiusModifier;
}

simulated function float GetAoEDamageModifier()
{
	return default.AoeDamageModifier;
}

simulated protected function int GetAmmoExtraAmmo()
{
	return IsAmmoActive() ? GetSkillValue( PerkSkills[EDemoAmmo] ) : 0.f;
}

/**
 * @brief Should the tactical reload skill adjust the reload speed
 *
 * @param KFW weapon in use
 * @return true/false
 */
simulated function bool GetUsingTactialReload(KFWeapon KFW)
{
	return (IsTacticalReloadActive() && (IsWeaponOnPerk(KFW, , self.class) || IsBackupWeapon(KFW) || IsDual9mm( KFW )) && TacticalReloadAsReloadRateClassNames.Find(KFW.class.Name) == INDEX_NONE);
}

/**
*  @brief Modifies the reload speed for Demolitionist weapons
*/
simulated function float GetReloadRateScale(KFWeapon KFW)
{
	if (IsWeaponOnPerk(KFW, , self.class) && IsTacticalReloadActive() && TacticalReloadAsReloadRateClassNames.Find(KFW.class.Name) != INDEX_NONE)
	{
		return 0.8f;
	}

	return 1.f;
}

/**
 * @brief Sets up the supllier skill
 */
simulated final private function ResetSupplier()
{
	if( MyPRI != none )
	{
		if( SuppliedPawnList.Length > 0 )
		{
			SuppliedPawnList.Remove( 0, SuppliedPawnList.Length );
		}

		MyPRI.PerkSupplyLevel = 1;

		if( InteractionTrigger != none )
		{
			InteractionTrigger.Destroy();
			InteractionTrigger = none;
		}

		if( CheckOwnerPawn() )
		{
			InteractionTrigger = Spawn( class'KFUsablePerkTrigger', OwnerPawn,, OwnerPawn.Location, OwnerPawn.Rotation,, true );
			InteractionTrigger.SetBase( OwnerPawn );
			InteractionTrigger.SetInteractionIndex( IMT_ReceiveGrenades );
			OwnerPC.SetPendingInteractionMessage();
		}
	}
	else if( InteractionTrigger != none )
	{
		InteractionTrigger.Destroy();
	}
}

/**
 * @brief General interaction with another pawn, here: give grenades
 *
 * @param KFPH Pawn to interact with
 */
simulated function Interact( KFPawn_Human KFPH )
{
	local KFInventoryManager KFIM;
	local KFPlayerController KFPC;
	local KFPlayerReplicationInfo OwnerPRI, UserPRI;
	local bool bReceivedGrenades;

	if( SuppliedPawnList.Find( KFPH ) != INDEX_NONE )
	{
		return;
	}

	KFIM = KFInventoryManager(KFPH.InvManager);
	if( KFIM != None )
	{
		bReceivedGrenades = KFIM.AddGrenades( 1 );
	}

	if( bReceivedGrenades )
	{
		SuppliedPawnList.AddItem( KFPH );

		if( Role == ROLE_Authority )
		{
			KFPC = KFPlayerController(KFPH.Controller);
			if( KFPC != none )
			{
				OwnerPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_GaveGrenadesTo, KFPC.PlayerReplicationInfo );
				KFPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_ReceivedGrenadesFrom, OwnerPC.PlayerReplicationInfo );

				UserPRI = KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo);
				OwnerPRI = KFPlayerReplicationInfo(OwnerPC.PlayerReplicationInfo);
				if( UserPRI != none && OwnerPRI != none )
				{
					UserPRI.MarkSupplierOwnerUsed( OwnerPRI );
				}

				`QALog( "Grenade Supplier" @ KFPC.PlayerReplicationInfo.PlayerName, bLogPerk );
			}
		}
	}
	else
	{
		if( Role == ROLE_Authority )
		{
			KFPC = KFPlayerController(KFPH.Controller);
			if( KFPC != none )
			{
				KFPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_AmmoIsFull, OwnerPC.PlayerReplicationInfo );
			}
		}
	}
}

/**
 * @brief Can other pawns interact with us?
 *
 * @param MyKFPH the other pawn
 * @return true/false
 */
simulated function bool CanInteract( KFPawn_HUman MyKFPH )
{
	return SuppliedPawnList.Find( MyKFPH ) == INDEX_NONE;
}

/**
 * @brief Checks if we should blow up close to death
 *
 * @return kaboom or not.
 */
simulated function bool ShouldSacrifice()
{
	return IsSacrificeActive() && !bUsedSacrifice;
}

function NotifyPerkSacrificeExploded()
{
	bUsedSacrifice = true;
}

/**
 * @brief Modifies the damage if the shared explosive resistance skill is selected
 *
 * @param InDamage the damage to modify
 */
simulated static function ModifyExplosiveDamage( out float InDamage )
{
	InDamage -= Indamage * GetSharedExplosiveResistance();
}

simulated function bool CanExplosiveWeld()
{
	return IsDoorTrapsActive();
}

/**
 * @brief Checks if the projectile should be immune to the pesky siren effects
 *
 * @return Immune or not
 */
simulated function bool ShouldRandSirenResist()
{
	return IsSirenResistanceActive();
}

/**
 * @brief skills and weapons can modify the knockdown power chance
 * @return knockdown power multiplier
 */
function float GetKnockdownPowerModifier( optional class<DamageType> DamageType, optional byte BodyPart, optional bool bIsSprinting=false )
{
	local float KnockDownMultiplier;

	KnockDownMultiplier = 0.f;

	if( IsDamageTypeOnPerk( class<KFDamageType>(DamageType) ) )
	{
		if( IsConcussiveForceActive() )
		{
			KnockDownMultiplier += GetSkillValue( PerkSkills[EDemoConcussiveForce] );
		}

		if( IsTacticalReloadActive() )
		{
			KnockDownMultiplier += GetSkillValue( PerkSkills[EDemoTacticalReload] );
		}
	}

	`QALog( "KnockDownMultiplier" @ KnockDownMultiplier, bLogPerk );
	return KnockDownMultiplier;
}

/**
 * @brief skills and weapons can modify the stumbling power chance
 * @return stumbling power modifier
 */
function float GetStumblePowerModifier( optional KFPawn KFP, optional class<KFDamageType> DamageType, optional out float CooldownModifier, optional byte BodyPart )
{
	local float StumbleMultiplier;

	StumbleMultiplier = 0.f;

	if( IsConcussiveForceActive() && IsDamageTypeOnPerk( DamageType ) )
	{
		StumbleMultiplier += GetSkillValue( PerkSkills[EDemoConcussiveForce] );
	}

	`QALog( "StumbleMultiplier" @ StumbleMultiplier, bLogPerk );
	return StumbleMultiplier;
}

/**
 * @brief skills and weapons can modify the stun power chance
 *
 * @return stun power modifier
 */
function float GetStunPowerModifier( optional class<DamageType> DamageType, optional byte HitZoneIdx )
{
	local float StunMultiplier;

	StunMultiplier = 0.f;

	if( IsConcussiveForceActive() && IsDamageTypeOnPerk( class<KFDamageType>(DamageType) ) )
	{
		StunMultiplier += GetSkillValue( PerkSkills[EDemoConcussiveForce] );
	}

	`QALog( "StunMultiplier" @ StunMultiplier, bLogPerk );
	return StunMultiplier;
}

function float GetReactionModifier( optional class<KFDamageType> DamageType )
{
	local float ReactionMultiplier;

	ReactionMultiplier = 1.f;

	if( IsConcussiveForceActive() && IsDamageTypeOnPerk( DamageType ) )
	{
		ReactionMultiplier += GetSkillValue( PerkSkills[EDemoConcussiveForce] );
	}

	`QALog( "ReactionMultiplier" @ ReactionMultiplier, bLogPerk );
	return ReactionMultiplier;
}

simulated static function bool ProjectileShouldNuke( KFProjectile Proj )
{
	return Proj.AllowNuke();
}

simulated static function bool ProjectileShouldConcussive( KFProjectile Proj )
{
	return Proj.AllowDemolitionistConcussive();
}

simulated static function bool ProjectileShouldExplosionChangeRadius( KFProjectile Proj )
{
	return Proj.AllowDemolitionistExplosionChangeRadius();
}

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

simulated function bool ShouldNeverDud()
{
	return (IsNukeActive() || IsProfessionalActive()) && WorldInfo.TimeDilation < 1.f;
}

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

	StateName = W.GetStateName();
	if( IsProfessionalActive() && IsWeaponOnPerk( W,, self.class ) )
	{
		if( ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || W.HasAlwaysOnZedTimeResist() )
		{
			`QALog( "Professional Modifier" @ StateName @ GetSkillValue( PerkSkills[EDemoProfessional] ), bLogPerk );
			return GetSkillValue( PerkSkills[EDemoProfessional] );
		}
	}

	return 0.f;
}

/*********************************************************************************************
* @name	 Getters etc
********************************************************************************************* */

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

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

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

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

/**
 * @brief Checks if the Area of Effect skill is active
 *
 * @return true if we have the skill enabled
 */
simulated function bool IsAoEActive()
{
	return PerkSkills[EDemoAoE].bActive && IsPerkLevelAllowed(EDemoAoE);
}

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

/**
 * @brief Checks if the Heat wave skill is active and if we are in Zed time
 *
 * @return true/false
 */
simulated final private function bool IsProfessionalActive()
{
	return PerkSkills[EDemoProfessional].bActive && IsPerkLevelAllowed(EDemoProfessional);
}

/**
 * @brief Returns the shared explosive resistance
 * @return Percent in float (.3f default)
 */
simulated static private final function float GetSharedExplosiveResistance()
{
	return default.SharedExplosiveResistance;
}

/**
 * @brief Gets the radius in within the shared explosive restince works
 * @return radius in UU float
 */
simulated static function float GetExplosiveResistanceRadius()
{
	return default.ExplosiveResistanceRadius;
}

/**
 * @brief Shared explosive resistance obviously opnly works with explosives
 *
 * @param DmgType Damage type to test
 *
 * @return true if resistable
 */
static function bool IsDmgTypeExplosiveResistable( class<DamageType> DmgType )
{
	return ClassIsChildOf( DmgType, default.ExplosiveResistableDamageTypeSuperClass );
}

/**
 * @brief The Sacrifice skill can spawn an explosion, this function delivers the template
 *
 * @return A game explosion template
 */
static function GameExplosion GetSacrificeExplosionTemplate()
{
	return default.SacrificeExplosionTemplate;
}

 /**
 * @brief Checks if the Door Traps skill is active
 *
 * @return true if we have the skill enabled
 */
 simulated private final function bool IsDoorTrapsActive()
 {
 	return true;
 }

 /**
 * @brief Checks if the Sacrifice skill is active
 *
 * @return true if we have the skill enabled
 */
 simulated private final function bool IsSacrificeActive()
 {
 	return true;
 }

/**
 * @brief The Door Traps skill can spawn an explosion, this function delivers the template
 *
 * @return A game explosion template
 */
static function GameExplosion GetDoorTrapsExplosionTemplate()
{
	return default.DoorTrapExplosionTemplate;
}

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

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

/**
 * @brief The nuke skill spawns a radioactive cloud, this function delivers the template
 *
 * @return Nuke explosion template
 */
static simulated function KFGameExplosion GetNukeExplosionTemplate()
{
	return default.NukeExplosionTemplate;
}

static simulated function class<KFExplosionActor> GetNukeExplosionActorClass()
{
	return default.NukeExplosionActorClass;
}

/**
 * @brief Modifies a projectile's damage when the nuke skill is active
 * @return damage modifier
 */
static function float GetNukeDamageModifier()
{
	return default.NukeDamageModifier;
}

/**
 * @brief Modifies a projectile's damage radius when the nuke skill is avtive
 * @return damage radius modifier
 */
static function float GetNukeRadiusModifier()
{
	return default.NukeRadiusModifier;
}

static function int GetLingeringPoisonDamage()
{
	return default.LingeringNukePoisonDamage;
}

static function class<KFDamageType> GetLingeringDamageType()
{
	return default.LingeringNukeDamageType;
}

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

simulated final static function AkEvent GetConcussiveExplosionSound()
{
	return default.ConcussiveExplosionSound;
}


/*********************************************************************************************
* @name	 Stats/XP
********************************************************************************************* */

/**
 * @brief how much XP is earned by a crawler kill depending on the game's difficulty
 *
 * @param Difficulty current game difficulty
 * @return XP earned
 */
simulated static function int GetFleshpoundKillXP( byte Difficulty )
{
	return default.SecondaryXPModifier[Difficulty];
}

/*********************************************************************************************
* @name	 UI
********************************************************************************************* */
simulated static function GetPassiveStrings( out array<string> PassiveValues, out array<string> Increments, byte Level )
{
	PassiveValues[0] = Round( GetPassiveValue( default.ExplosiveDamage, Level ) * 100 ) $ "%";
	PassiveValues[1] = Round( GetPassiveValue( default.ExplosiveResistance, Level ) * 100 ) $ "%";
	PassiveValues[2] = string(GetExtraAmmo( Level ));
	PassiveValues[3] = "";
	PassiveValues[4] = "";
	PassiveValues[5] = "";

	Increments[0] = "[" @ Round(default.ExplosiveDamage.Increment * 100)  $ "% /" @ default.LevelString @ "]";
	Increments[1] = "[" @ Round(default.ExplosiveResistance.StartingValue * 100)  $ "%" @ "+" @ int(default.ExplosiveResistance.Increment * 100)  $ "% /" @ default.LevelString @ "]";
	Increments[2] = "[" @ Round(default.ExplosiveAmmo.Increment) @ "/ 5" @ default.LevelString @ "]";
	Increments[3] = "";
	Increments[4] = "";
	Increments[5] = "";
}

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

	if( bLogPerk )
	{
		`log( "PASSIVE PERKS" );
		`log( "-ExplosiveDamage:" @ GetPassiveValue( ExplosiveDamage, GetLevel() ) $ "%" );
		`log( "-ExplosiveResistance:" @ GetPassiveValue( ExplosiveResistance, GetLevel() ) $ "%" );
		`log( "-ExplosiveAmmo:" @ GetExtraAmmo( GetLevel() ) $ "%" );

/**	    `log( "Skill Tree" );
	    `log( "-GrenadeSupplier:" @ PerkSkills[EDemoGrenadeSupplier].bActive );
	    `log( "-OnContact:" @ PerkSkills[EDemoOnContact].bActive );
	    `log( "-ExplosiveResistance:" @ PerkSkills[EDemoExplosiveResistance].bActive );
	    `log( "-Sacrifice:" @ PerkSkills[EDemoSacrifice].bActive );
	    `log( "-DoorTraps:" @ PerkSkills[EDemoDoorTraps].bActive );
	    `log( "-SirenResistance:" @ PerkSkills[EDemoSirenResistance].bActive );
	    `log( "-OffPerk:" @ PerkSkills[EDemoOffPerk].bActive );
	    `log( "-OnPerk:" @ PerkSkills[EDemoOnPerk].bActive );
	    `log( "-Nuke:" @ PerkSkills[EDemoNuke].bActive );
	    `log( "-ConcussiveForce:" @ PerkSkills[EDemoConcussiveForce].bActive );*/
	}
}

DefaultProperties
{
	SharedExplosiveResistance=0.3f

	PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Demolition'
	InteractIcon=Texture2D'UI_World_TEX.Demolitionist_Supplier_HUD'

	PrimaryWeaponDef=class'KFWeapDef_HX25'
	KnifeWeaponDef=class'KFWeapDef_Knife_Demo'
	GrenadeWeaponDef=class'KFWeapDef_Grenade_Demo'

	ProgressStatID=STATID_Demo_Progress
   	PerkBuildStatID=STATID_Demo_Build

   	SecondaryXPModifier(0)=10
	SecondaryXPModifier(1)=17
	SecondaryXPModifier(2)=21
	SecondaryXPModifier(3)=30

	ExplosiveResistableDamageTypeSuperClass=class'KFDT_Explosive'
	ExplosiveResistanceRadius=500.f

	AoeDamageModifier=0 //0.3f
	DaZedEMPPower=0 //100

	ProfessionalAoEModifier=0.25

  	ExplosiveDamage=(Name="Explosive Damage",Increment=0.01f,Rank=0,StartingValue=0.f,MaxValue=0.25)
	ExplosiveResistance=(Name="Explosive Resistance",Increment=0.02f,Rank=0,StartingValue=0.1f,MaxValue=0.6f)
	ExplosiveAmmo=(Name="Explosive Ammo",Increment=1.f,Rank=0,StartingValue=0.0f,MaxValue=5.f)

	PerkSkills(EDemoDamage)=(Name="Damage",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_GrenadeSupplier",Increment=0.f,Rank=0,StartingValue=0.25f,MaxValue=0.25f)
	PerkSkills(EDemoTacticalReload)=(Name="Speed",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_Speed",Increment=0.f,Rank=0,StartingValue=0.10f,MaxValue=0.10f)
	PerkSkills(EDemoDirectHit)=(Name="DirectHit",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_ExplosiveResistance",Increment=0.f,Rank=0,StartingValue=0.25,MaxValue=0.25)
	PerkSkills(EDemoAmmo)=(Name="Ammo",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_Ammo",Increment=0.f,Rank=0,StartingValue=10.f,MaxValue=10.f) //5.f
	PerkSkills(EDemoSirenResistance)=(Name="SirenResistance",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_SirenResistance",Increment=0.f,Rank=0,StartingValue=0.5,MaxValue=0.5)
	PerkSkills(EDemoAoE)=(Name="AreaOfEffect",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_AoE",Increment=0.f,Rank=0,StartingValue=1.50,MaxValue=1.50)
	PerkSkills(EDemoCriticalHit)=(Name="CriticalHit",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_Crit",Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f)
	PerkSkills(EDemoConcussiveForce)=(Name="ConcussiveForce",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_ConcussiveForce",Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f)
	PerkSkills(EDemoNuke)=(Name="Nuke",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_Nuke",Increment=0.f,Rank=0,StartingValue=1.03,MaxValue=1.03)
	PerkSkills(EDemoProfessional)=(Name="Professional",IconPath="UI_PerkTalent_TEX.demolition.UI_Talents_Demolition_Professional",Increment=0.f,Rank=0,StartingValue=0.9f,MaxValue=0.9f)

	// explosion
	Begin Object Class=KFGameExplosion Name=ExploTemplate0
		Damage=200
		DamageRadius=500
		DamageFalloffExponent=1.f
		DamageDelay=0.f

		// Damage Effects
		KnockDownStrength=10
		KnockDownRadius=100
		FractureMeshRadius=500.0
		FracturePartVel=500.0
		MyDamageType=class'KFDT_Explosive_Sacrifice'
		ExplosionEffects=KFImpactEffectInfo'WEP_Dynamite_ARCH.Dynamite_Explosion'
		ExplosionSound=AkEvent'WW_WEP_EXP_Grenade_Frag.Play_WEP_EXP_Grenade_Frag_Explosion'

		// Camera Shake
		CamShake=CameraShake'FX_CameraShake_Arch.Grenades.Default_Grenade'
		CamShakeInnerRadius=450
		CamShakeOuterRadius=900
		CamShakeFalloff=0.5f
		bOrientCameraShakeTowardsEpicenter=true
		bUseOverlapCheck=false
	End Object
	SacrificeExplosionTemplate=ExploTemplate0

	Begin Object Class=KFGameExplosion Name=ExploTemplate1
		Damage=45 //15
		DamageRadius=450
		DamageFalloffExponent=1.f
		DamageDelay=0.f
		MyDamageType=class'KFDT_Toxic_DemoNuke'
		//bIgnoreInstigator is set to true in PrepareExplosionTemplate

		// Damage Effects
		KnockDownStrength=0
		KnockDownRadius=0
		FractureMeshRadius=200.0
		FracturePartVel=500.0
		ExplosionEffects=KFImpactEffectInfo'FX_Impacts_ARCH.Explosions.Nuke_Explosion'
		ExplosionSound=AkEvent'WW_GLO_Runtime.Play_WEP_Nuke_Explo'
		MomentumTransferScale=1.f

		// Camera Shake
		CamShake=CameraShake'FX_CameraShake_Arch.Grenades.Default_Grenade'
		CamShakeInnerRadius=200
		CamShakeOuterRadius=900
		CamShakeFalloff=1.5f
		bOrientCameraShakeTowardsEpicenter=true
	End Object
	NukeExplosionTemplate=ExploTemplate1

	Begin Object Class=KFGameExplosion Name=ExploTemplate2
		Damage=200
		DamageRadius=1000
		DamageFalloffExponent=1.f
		DamageDelay=0.f
		MyDamageType=class'KFDT_Explosive_DoorTrap'

		// Damage Effects
		KnockDownStrength=10
		KnockDownRadius=100
		FractureMeshRadius=500.0
		FracturePartVel=500.0
		ExplosionEffects=KFImpactEffectInfo'WEP_Dynamite_ARCH.Dynamite_Explosion'
		ExplosionSound=AkEvent'WW_WEP_EXP_Grenade_Frag.Play_WEP_EXP_Grenade_Frag_Explosion'

		// Camera Shake
		CamShake=CameraShake'FX_CameraShake_Arch.Grenades.Default_Grenade'
		CamShakeInnerRadius=450
		CamShakeOuterRadius=900
		CamShakeFalloff=0.5f
		bOrientCameraShakeTowardsEpicenter=true
		bUseOverlapCheck=false
	End Object
	DoorTrapExplosionTemplate=ExploTemplate2

	NukeExplosionActorClass=class'KFExplosion_Nuke'
	NukeDamageModifier=1.5   //1.25
	NukeRadiusModifier=1.35  //1.25
	LingeringNukePoisonDamage=20
	LingeringNukeDamageType=class'KFDT_DemoNuke_Toxic_Lingering'

	ConcussiveExplosionSound=AkEvent'WW_WEP_SA_RPG7.Play_WEP_SA_RPG7_Explosion'

    // Skill tracking
	HitAccuracyHandicap=2.0
	HeadshotAccuracyHandicap=0.0

	// Prestige Rewards
	PrestigeRewardItemIconPaths[0]="WEP_SkinSet_Prestige01_Item_TEX.knives.DemoKnife_PrestigePrecious_Mint_large"
	PrestigeRewardItemIconPaths[1]="WEP_SkinSet_Prestige02_Item_TEX.tier01.HX25_PrestigePrecious_Mint_large"
	PrestigeRewardItemIconPaths[2]="WEP_skinset_prestige03_itemtex.tier02.M79_PrestigePrecious_Mint_large"
	PrestigeRewardItemIconPaths[3]="wep_skinset_prestige04_itemtex.tier03.M16M203_PrestigePrecious_Mint_Large"
	PrestigeRewardItemIconPaths[4]="WEP_SkinSet_Prestige05_Item_TEX.tier04.RPG-7_PrestigePrecious_Mint_large"

   	ZedTimeModifyingStates(0)="WeaponFiring"
   	ZedTimeModifyingStates(1)="WeaponBurstFiring"
   	ZedTimeModifyingStates(2)="WeaponSingleFiring"
   	ZedTimeModifyingStates(3)="Reloading"
   	ZedTimeModifyingStates(4)="WeaponSingleFireAndReload"
   	ZedTimeModifyingStates(5)="FiringSecondaryState"
   	ZedTimeModifyingStates(6)="AltReloading"
    ZedTimeModifyingStates(7)="WeaponThrowing"
	ZedTimeModifyingStates(8)="HuskCannonCharge"
	ZedTimeModifyingStates(9)="BlunderbussDeployAndDetonate"

   	PassiveExtraAmmoIgnoredClassNames(0)="KFProj_DynamiteGrenade"

   	ExtraAmmoIgnoredClassNames(0)="KFProj_DynamiteGrenade"
   	ExtraAmmoIgnoredClassNames(1)="KFWeap_Thrown_C4"

	TacticalReloadAsReloadRateClassNames(0)="KFWeap_GrenadeLauncher_M32"

   	OnlySecondaryAmmoWeapons(0)="KFWeap_AssaultRifle_M16M203"

   	DamageIgnoredDTs(0)="KFDT_Ballistic_M16M203"
   	DamageIgnoredDTs(1)="KFDT_Bludgeon_M16M203"

   	AutoBuyLoadOutPath=(class'KFWeapDef_HX25', class'KFWeapDef_M79', class'KFWeapDef_M16M203', class'KFWeapDef_RPG7', class'KFWeapDef_M32')
}

