//============================================================================= // KFPerk_Support //============================================================================= // Support Perk //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC // - Christian "schneidzekk" Schneider //============================================================================= class KFPerk_Support extends KFPerk hidecategories(Mobile, Object, Debug, Advanced, Physics, Actor, Attachment, Display) native; //`include(KFOnlineStats.uci) struct native sSuppliedPawnInfo { var KFPawn_Human SuppliedPawn; var bool bSuppliedAmmo; var bool bSuppliedArmor; }; /** Passives */ var private const PerkSkill Ammo; // Increased ammo var private const PerkSkill WeldingProficiency; // Welding speed modifier var private const PerkSkill ShotgunDamage; // Shotgun dmg modifier var private const PerkSkill ShotgunPenetration; // Shotgun extra penetration Use INTs only var private const PerkSkill Strength; var private Array SuppliedPawnList; var private const float BarrageFiringRate; var private const float ResupplyMaxSpareAmmoModifier; var private const AkEvent ReceivedAmmoSound; var private const AkEvent ReceivedArmorSound; var private const AkEvent ReceivedAmmoAndArmorSound; var private const array HighCapMagExemptList; var private const array AdditionalOnPerkDTNames; var private const int DoorRepairXP[4]; // Door repair XP per-difficulty enum ESupportPerkSkills { ESupportHighCapMags, ESupportTacticalReload, ESupportFortitude, ESupportSalvo, ESupportAPShot, ESupportTightChoke, ESupportResupply, ESupportConcussionRounds, ESupportPerforate, ESupportBarrage, }; /********************************************************************************************* * @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 (KFGRI == none && KFGRI.bTraderIsOpen) { return; } //ResetSupplier(); } /** * We need to separate this from ApplySkillsToPawn() to avoid resetting weight limits (and losing weapons) * every time a skill or level is changed */ function ApplyWeightLimits() { local KFInventoryManager KFIM; KFIM = KFInventoryManager(OwnerPawn.InvManager); if( KFIM != none ) { `QALog( "Strength Mod" @ GetPercentage(KFIM.MaxCarryBlocks, KFIM.default.MaxCarryBlocks + GetExtraStrength( CurrentLevel )), bLogPerk ); KFIM.MaxCarryBlocks = KFIM.default.MaxCarryBlocks + GetExtraStrength( CurrentLevel ); CheckForOverWeight( KFIM ); } } /** * @brief Calculates the additional carry blocks per perk level * * @param Level Current perk level * @return additional blocks */ simulated private final static function float GetExtraStrength( int Level ) { return default.Strength.Increment * FFloor( float( Level ) / 5.f ); } /** * @brief Sets up the supplier skill */ simulated final private function ResetSupplier() { if( MyPRI != none && IsSupplierActive() ) { if( SuppliedPawnList.Length > 0 ) { SuppliedPawnList.Remove( 0, SuppliedPawnList.Length ); } MyPRI.PerkSupplyLevel = IsResupplyActive() ? 2 : 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_ReceiveAmmo ); OwnerPC.SetPendingInteractionMessage(); } } else if( InteractionTrigger != none ) { InteractionTrigger.Destroy(); } } /********************************************************************************************* * @name Passives ********************************************************************************************* */ /** * @brief Modifes 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 DamageType, optional int HitZoneIdx ) { local KFWeapon KFW; local float TempDamage; TempDamage = InDamage; `QALog( GetFuncName() @ "BaseDamage =" @ InDamage, bLogPerk ); TempDamage = InDamage; if( DamageCauser != none ) { KFW = GetWeaponFromDamageCauser( DamageCauser ); } if( ((KFW != none && IsWeaponOnPerk( KFW,, self.class )) || (DamageType != none && IsDamageTypeOnPerk( DamageType ))) && !ClassIsChildOf( DamageType, class'KFDT_Explosive' ) ) { TempDamage += InDamage * GetPassiveValue( ShotgunDamage, CurrentLevel ); `QALog( GetFuncName() @ "+ Extra Shotgun Damage =" @ TempDamage, bLogPerk ); if( IsSalvoActive() ) { TempDamage += InDamage * GetSkillValue( PerkSkills[ESupportSalvo] ); `QALog( GetFuncName() @ "+ Salvo Damage =" @ TempDamage, bLogPerk ); } } `QALog( "Total Damage Given" @ Damagetype @ KFW @ GetPercentage( InDamage, Round(TempDamage) ), bLogPerk ); InDamage = Round( TempDamage ); } /** Welding Proficiency - faster welding/unwelding */ /** * @brief Modifies the welding speed * * @param FastenRate how much faster do we weld * @param UnfastenRate how much faster do we unweld * */ simulated function ModifyWeldingRate( out float FastenRate, out float UnfastenRate ) { local float WeldingModifier; WeldingModifier = GetPassiveValue( WeldingProficiency, CurrentLevel ); FastenRate *= WeldingModifier; UnFastenRate *= WeldingModifier; `QALog( "Welding Modifier" @ WeldingModifier, bLogPerk ); } /** * @brief the higher the level the more can we penetrate * * @param Level current perk level * @param DamageType the use weapon's damage type * @param bForce * @return the additional penetrations */ simulated function float GetPenetrationModifier( byte Level, class DamageType, optional bool bForce ) { local float PenetrationPower; // Only buff damage types that are associated with support if( !bForce && (DamageType == none || !IsDamageTypeOnPerk( Damagetype )) ) { return 0; } PenetrationPower = IsAPShotActive() ? GetSkillValue( PerkSkills[ESupportAPShot] ) : 0.f; PenetrationPower = IsPerforateActive() ? GetSkillValue( PerkSkills[ESupportPerforate] ) : PenetrationPower; `QALog( "PenetrationPower" @ PenetrationPower + GetPassiveValue( ShotgunPenetration, Level ), bLogPerk ); return PenetrationPower + GetPassiveValue( ShotgunPenetration, Level ); } simulated function bool IgnoresPenetrationDmgReduction() { return IsPerforateActive(); } /********************************************************************************************* * @name Selectable skills ********************************************************************************************* */ /** * @brief Modifies mag capacity and count * * @param KFW the weapon * @param MagazineCapacity modified mag capacity * @param WeaponPerkClass the weapon's associated perk class (optional) */ simulated function ModifyMagSizeAndNumber( KFWeapon KFW, out int MagazineCapacity, optional array< Class > WeaponPerkClass, optional bool bSecondary=false, optional name WeaponClassName) { local float TempCapacity; TempCapacity = MagazineCapacity; // FAMAS needs its secondary ammo affected if( (!bSecondary || IsFAMAS(KFW)) && IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && (KFW == none || !KFW.bNoMagazine) && HighCapMagExemptList.Find(WeaponClassName) == INDEX_NONE ) { if( IsHighCapMagsMagActive() ) { TempCapacity += MagazineCapacity * GetSkillValue( PerkSkills[ESupportHighCapMags] ); } } MagazineCapacity = Round(TempCapacity); } /** * @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 ) { local float TempSpareAmmoAmount; local array< class > WeaponPerkClass; if(KFW == none) { WeaponPerkClass = TraderItem.AssociatedPerkClasses; } else { WeaponPerkClass = KFW.GetAssociatedPerkClasses(); } if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) ) { TempSpareAmmoAmount = PrimarySpareAmmo; TempSpareAmmoAmount += PrimarySpareAmmo * GetPassiveValue( Ammo, CurrentLevel ); PrimarySpareAmmo = Round( TempSpareAmmoAmount ); } } /** * @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 float TempMaxSpareAmmoAmount; local array< class > WeaponPerkClass; if(KFW == none) { WeaponPerkClass = TraderItem.AssociatedPerkClasses; } else { WeaponPerkClass = KFW.GetAssociatedPerkClasses(); } if( IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && MaxSpareAmmo > 0 ) { TempMaxSpareAmmoAmount = MaxSpareAmmo; TempMaxSpareAmmoAmount += MaxSpareAmmo * GetPassiveValue( Ammo, CurrentLevel ); `QALog( "Ammo Passive, MaxSpareAmmo = +" @ (MaxSpareAmmo + MaxSpareAmmo * GetPassiveValue( Ammo, CurrentLevel )) / MaxSpareAmmo $ "%", bLogPerk ); if( IsResupplyActive() ) { TempMaxSpareAmmoAmount += MaxSpareAmmo * GetResupplyMaxSpareAmmoModifier(); `QALog( "Resupply skill, MaxSpareAmmo = +" @ (MaxSpareAmmo + MaxSpareAmmo * GetResupplyMaxSpareAmmoModifier()) / MaxSpareAmmo $ "%", bLogPerk ); } MaxSpareAmmo = Round( TempMaxSpareAmmoAmount ); } } simulated private static function float GetResupplyMaxSpareAmmoModifier() { return default.ResupplyMaxSpareAmmoModifier; } /** * @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 )); } /** * @brief modifies the players health * * @param InHealth health */ function ModifyHealth( out int InHealth ) { local float TempHealth; if( IsFortitudeActive() ) { TempHealth = InHealth; TempHealth += InHealth * GetSkillValue( PerkSkills[ESupportFortitude] ); InHealth = FCeil( TempHealth ); } } simulated function float GetTightChokeModifier() { local KFWeapon MyKFWeapon; MyKFWeapon = GetOwnerWeapon(); if( IsTightChokeActive() && IsWeaponOnPerk(MyKFWeapon,, self.class) ) { return GetSkillValue( PerkSkills[ESupportTightChoke] ); } return super.GetTightChokeModifier(); } /** * @brief skills and weapons can modify the stumbling power * @return stumbling power modifier */ function float GetStumblePowerModifier( optional KFPawn KFP, optional class DamageType, optional out float CooldownModifier, optional byte BodyPart ) { if( IsWeaponOnPerk( GetOwnerWeapon(),, self.class ) && IsConcussionRoundsActive() ) { return GetSkillValue( PerkSkills[ESupportConcussionRounds] ); } return 0.f; } /** * @brief General interaction with another pawn, here: give ammo * * @param KFPH Pawn to interact with */ simulated function Interact( KFPawn_Human KFPH ) { local KFWeapon KFW; local int Idx, MagCount; local KFPlayerController KFPC; local KFPlayerReplicationInfo UserPRI, OwnerPRI; local bool bCanSupplyAmmo, bCanSupplyArmor; local bool bReceivedAmmo, bReceivedArmor; local sSuppliedPawnInfo SuppliedPawnInfo; // Do nothing if supplier isn't active if( !IsSupplierActive() ) { return; } bCanSupplyAmmo = true; bCanSupplyArmor = true; Idx = SuppliedPawnList.Find( 'SuppliedPawn', KFPH ); if( Idx != INDEX_NONE ) { bCanSupplyAmmo = !SuppliedPawnList[Idx].bSuppliedAmmo; bCanSupplyArmor = !SuppliedPawnList[Idx].bSuppliedArmor; if( !bCanSupplyAmmo && !bCanSupplyArmor ) { return; } } if( bCanSupplyAmmo ) { foreach KFPH.InvManager.InventoryActors( class'KFWeapon', KFW ) { if( KFW.DenyPerkResupply() ) { continue; } // resupply 1 mag for every 5 initial mags MagCount = Max( KFW.InitialSpareMags[0] / 1.5, 1 ); // 3, 1 `QALog( "Supply Ammo Primary Weapon " @ KFW @ MagCount * KFW.MagazineCapacity[0], bLogPerk ); bReceivedAmmo = (KFW.AddAmmo( MagCount * KFW.MagazineCapacity[0] * (IsResupplyActive() ? 1.3f : 1.0f) ) > 0 ) ? true : bReceivedAmmo; if( KFW.CanRefillSecondaryAmmo() ) { // resupply 1 mag for every 5 initial mags `QALog( "Supply Ammo Secondary Weapon" @ KFW @ Max( KFW.InitialSpareMags[1] / 3, 1 ), bLogPerk ); // If our secondary ammo isn't mag-based (like the Eviscerator), restore a portion of max ammo instead bReceivedAmmo = (KFW.AddSecondaryAmmo( Max(KFW.AmmoPickupScale[1] * (IsResupplyActive() ? 1.3f : 1.0f) * KFW.MagazineCapacity[1], 1) ) > 0) ? true : bReceivedAmmo; } } } if( bCanSupplyArmor && IsResupplyActive() && KFPH.Armor != KFPH.GetMaxArmor() ) { KFPH.AddArmor( KFPH.MaxArmor * GetSkillValue( PerkSkills[ESupportResupply] ) ); bReceivedArmor = true; } // Add to array (if necessary) and flag as supplied as needed if( bReceivedArmor || bReceivedAmmo ) { if( Idx == INDEX_NONE ) { SuppliedPawnInfo.SuppliedPawn = KFPH; SuppliedPawnInfo.bSuppliedAmmo = bReceivedAmmo; SuppliedPawnInfo.bSuppliedArmor = bReceivedArmor; Idx = SuppliedPawnList.Length; SuppliedPawnList.AddItem( SuppliedPawnInfo ); } else { SuppliedPawnList[Idx].bSuppliedAmmo = SuppliedPawnList[Idx].bSuppliedAmmo || bReceivedAmmo; SuppliedPawnList[Idx].bSuppliedArmor = SuppliedPawnList[Idx].bSuppliedArmor || bReceivedArmor; } if( Role == ROLE_Authority ) { KFPC = KFPlayerController( KFPH.Controller ); if( bReceivedAmmo ) { OwnerPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', bReceivedArmor ? GMT_GaveAmmoAndArmorTo : GMT_GaveAmmoTo, KFPC.PlayerReplicationInfo ); KFPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', bReceivedArmor ? GMT_ReceivedAmmoAndArmorFrom : GMT_ReceivedAmmoFrom, OwnerPC.PlayerReplicationInfo ); } else if( bReceivedArmor ) { OwnerPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_GaveArmorTo, KFPC.PlayerReplicationInfo ); KFPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_ReceivedArmorFrom, OwnerPC.PlayerReplicationInfo ); } UserPRI = KFPlayerReplicationInfo( KFPC.PlayerReplicationInfo ); OwnerPRI = KFPlayerReplicationInfo( OwnerPC.PlayerReplicationInfo ); if( UserPRI != none && OwnerPRI != none ) { UserPRI.MarkSupplierOwnerUsed( OwnerPRI, SuppliedPawnList[Idx].bSuppliedAmmo, SuppliedPawnList[Idx].bSuppliedArmor ); } } } else if( Role == ROLE_Authority ) { KFPC = KFPlayerController( KFPH.Controller ); if( IsResupplyActive() ) { KFPC.ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_AmmoAndArmorAreFull, OwnerPC.PlayerReplicationInfo ); } else { 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 ) { local int Idx; if( IsSupplierActive() ) { Idx = SuppliedPawnList.Find( 'SuppliedPawn', MyKFPH ); // Pawn hasn't gotten anything from us yet if( Idx == INDEX_NONE ) { return true; } // Resupply is active and pawn hasn't gotten armor yet if( IsResupplyActive() && !SuppliedPawnList[Idx].bSuppliedArmor ) { return true; } // Pawn hasn't gotten ammo return !SuppliedPawnList[Idx].bSuppliedAmmo; } } simulated static function AKEvent GetReceivedAmmoSound() { return default.ReceivedAmmoSound; } simulated static function AKEvent GetReceivedArmorSound() { return default.ReceivedArmorSound; } simulated static function AKEvent GetReceivedAmmoAndArmorSound() { return default.ReceivedAmmoAndArmorSound; } /** * @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(); // Blast Brawlers use a different state for shooting (combining melee + firing). Needs a special case for this // FAMAS uses alt fire as common firing. Another special case added if( IsWeaponOnPerk( W,, self.class ) && CouldBarrageActive() && (ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)) || (IsFAMAS(W) && StateName == 'FiringSecondaryState'))) { return BarrageFiringRate; } return 0.f; } /** * @brief Resets certain perk values on wave start/end */ function OnWaveStart() { Super.OnWaveStart(); ResetSupplier(); } simulated static function GetPassiveStrings( out array PassiveValues, out array Increments, byte Level ) { PassiveValues[0] = Round( (GetPassiveValue( default.WeldingProficiency, Level ) - 1) * 100) $ "%"; PassiveValues[1] = Round( GetPassiveValue( default.ShotgunDamage, Level ) * 100) $ "%"; PassiveValues[2] = Round( GetPassiveValue( default.ShotgunPenetration, Level ) * 100) $ "%"; PassiveValues[3] = Round( GetPassiveValue( default.Ammo, Level ) * 100) $ "%"; PassiveValues[4] = ""; PassiveValues[5] = ""; Increments[0] = "[" @ Left( string( default.WeldingProficiency.Increment * 100 ), InStr(string(default.WeldingProficiency.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[1] = "[" @ Left( string( default.ShotgunDamage.Increment * 100 ), InStr(string(default.ShotgunDamage.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[2] = "[" @ Left( string( default.ShotgunPenetration.Increment * 100 ), InStr(string(default.ShotgunPenetration.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[3] = "[" @ Left( string( default.Ammo.Increment * 100 ), InStr(string(default.Ammo.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[4] = ""; Increments[5] = ""; } /** * @brief DamageType on perk? * * @param KFDT The damage type * @return true/false */ static function bool IsDamageTypeOnPerk( class KFDT ) { if( KFDT != none && default.AdditionalOnPerkDTNames.Find( KFDT.name ) != INDEX_NONE ) { return true; } return super.IsDamageTypeOnPerk( KFDT ); } /********************************************************************************************* * @name Getters ********************************************************************************************* */ /** * @brief Checks if the supplier skill is active * * @return true if we have the skill enabled */ simulated function bool IsSupplierActive() { return true; } /** * @brief Checks if the barrage skill is active and if we are in zed time * * @return true if we have the skill enabled */ private function bool IsBarrageActive() { return PerkSkills[ESupportBarrage].bActive && WorldInfo.TimeDilation < 1.f && IsPerkLevelAllowed(ESupportBarrage); } simulated private function bool CouldBarrageActive() { return PerkSkills[ESupportBarrage].bActive && IsPerkLevelAllowed(ESupportBarrage); } /** * @brief Checks if the ammo skill is active * * @return true if we have the skill enabled */ simulated private function bool IsHighCapMagsMagActive() { return PerkSkills[ESupportHighCapMags].bActive && IsPerkLevelAllowed(ESupportHighCapMags); } /** * @brief Checks if the fortitude skill is active * * @return true if we have the skill enabled */ private function bool IsFortitudeActive() { return PerkSkills[ESupportFortitude].bActive && IsPerkLevelAllowed(ESupportFortitude); } /** * @brief Checks if the Salvo skill is active * * @return true if we have the skill enabled */ private function bool IsSalvoActive() { return PerkSkills[ESupportSalvo].bActive && IsPerkLevelAllowed(ESupportSalvo); } /** * @brief Checks if the Armor Piercing Shot skill is active * * @return true if we have the skill enabled */ simulated private function bool IsAPShotActive() { return PerkSkills[ESupportAPShot].bActive && IsPerkLevelAllowed(ESupportAPShot); } /** * @brief Checks if the Tight Choke skill is active * * @return true if we have the skill enabled */ simulated private function bool IsTightChokeActive() { return PerkSkills[ESupportTightChoke].bActive && IsPerkLevelAllowed(ESupportTightChoke); } /** * @brief Checks if the tactical reload skill is active (client and server) * * @return true/false */ simulated private function bool IsTacticalReloadActive() { return PerkSkills[ESupportTacticalReload].bActive && IsPerkLevelAllowed(ESupportTacticalReload); } /** * @brief Checks if the Concussion Rounds skill is active * * @return true/false */ private function bool IsConcussionRoundsActive() { return PerkSkills[ESupportConcussionRounds].bActive && IsPerkLevelAllowed(ESupportConcussionRounds); } /** * @brief Checks if the Resupply skill is active * * @return true/false */ simulated private function bool IsResupplyActive() { return PerkSkills[ESupportResupply].bActive && IsPerkLevelAllowed(ESupportResupply); } /** * @brief Checks if the Perforate skill is active * * @return true/false */ simulated private function bool IsPerforateActive() { return PerkSkills[ESupportPerforate].bActive && WorldInfo.TimeDilation < 1.f && IsPerkLevelAllowed(ESupportPerforate); } /** * @brief Checks if we can repair doors * * @return true/false */ native function bool CanRepairDoors(); /** */ static function GetDoorRepairXP(out int XP, byte Difficulty) { XP = default.DoorRepairXP[Difficulty]; } /********************************************************************************************* * @name Logging / debug ********************************************************************************************* */ /** Log What type of reload the weapon would use given ammo count */ private simulated function name LogTacticalReload() { local KFWeapon KFW; KFW = GetOwnerWeapon(); if( KFW != none ) { return KFW.GetReloadAnimName( GetUsingTactialReload(KFW) ); } return ''; } /** QA Logging - Report Perk Info */ simulated function LogPerkSkills() { super.LogPerkSkills(); if( bLogPerk ) { /** `log( "PASSIVE PERKS" ); `log( "-Welding Modifier:" @ (GetPassiveValue(WeldingProficiency, CurrentLevel) - 1) *100 $"%" ); `log( "-Shotgun Damage Modifier:" @ (GetPassiveValue(ShotgunDamage, CurrentLevel) - 1) *100 $"%" ); `log( "-Shotgun Penetration Modifier:" @ GetPassiveValue(default.ShotgunPenetration, CurrentLevel) ); `log( "-Grenade Damage Modifier:" @ GetPassiveValue(GrenadeDamage, CurrentLevel, 100.f)*100 $"%" ); `log( "Skill Tree" ); `log( "-Ammo:" @ PerkSkills[ESupportTacticalReload].bActive ); `log( "-Supplier:" @ IsSupplierActive()) ; `log( "-Fortitude:" @ PerkSkills[ESupportFortitude].bActive ); `log( "-Regeneration:" @ PerkSkills[ESupportRegeneration].bActive ); `log( "-Bombard:" @ PerkSkills[ESupportBombard].bActive ); `log( "-Tactical Reload:" @ PerkSkills[ESupportTacticalReload].bActive @ LogTacticalReload() ); `log( "-Strength:" @ PerkSkills[ESupportStrength].bActive ); `log( "-Tenacity:" @ PerkSkills[ESupportTenacity].bActive ); `log( "-Safeguard:" @ PerkSkills[ESupportSafeguard].bActive ); `log( "-Barrage:" @ PerkSkills[ESupportBarrage].bActive );*/ } } simulated function PlayerDied() { super.PlayerDied(); if(InteractionTrigger != none) { InteractionTrigger.DestroyTrigger(); } } DefaultProperties { ProgressStatID=STATID_Sup_Progress PerkBuildStatID=STATID_Sup_Build PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Support' InteractIcon=Texture2D'UI_World_TEX.Support_Supplier_HUD' PrimaryWeaponDef=class'KFWeapDef_MB500' KnifeWeaponDef=class'KFWeapDef_Knife_Support' GrenadeWeaponDef=class'KFWeapDef_Grenade_Support' BarrageFiringRate=0.9f ResupplyMaxSpareAmmoModifier=0.20 //0.15 ReceivedAmmoSound=AkEvent'WW_UI_PlayerCharacter.Play_UI_Pickup_Ammo' ReceivedAmmoAndArmorSound=AkEvent'WW_UI_PlayerCharacter.Play_UI_Pickup_Armor' ReceivedArmorSound=AkEvent'WW_UI_PlayerCharacter.Play_UI_Pickup_Armor' Ammo=(Name="Ammo",Increment=0.01f,Rank=0,StartingValue=0.0,MaxValue=0.25f) //0.4 WeldingProficiency=(Name="Welding Proficiency",Increment=0.03f,Rank=0,StartingValue=1.f,MaxValue=1.75f) //1.5 ShotgunDamage=(Name="Shotgun Damage",Increment=0.01f,Rank=0,StartingValue=0.f,MaxValue=0.25f) ShotgunPenetration=(Name="Shotgun Penetration",Increment=0.20,Rank=0,StartingValue=0.0f,MaxValue=5.0f) //6.25 Strength=(Name="Strength",Increment=1.f,Rank=0,StartingValue=0.f,MaxValue=5.f) PerkSkills(ESupportHighCapMags)=(Name="HighCapMags",IconPath="UI_PerkTalent_TEX.support.UI_Talents_Support_HighCapacityMags",Increment=0.f,Rank=0,StartingValue=0.75,MaxValue=0.75) //0.5 PerkSkills(ESupportTacticalReload)=(Name="TacticalReload",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_TacticalReload",Increment=0.f,Rank=0,StartingValue=0.8f,MaxValue=0.f) PerkSkills(ESupportFortitude)=(Name="Fortitude",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_Fortitude",Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f) PerkSkills(ESupportSalvo)=(Name="Salvo",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_Salvo",Increment=0.f,Rank=0,StartingValue=0.3f,MaxValue=0.3f) PerkSkills(ESupportAPShot)=(Name="APShot",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_ArmorPiercing",Increment=0.f,Rank=0,StartingValue=4.f,MaxValue=4.f) //3 PerkSkills(ESupportTightChoke)=(Name="TightChoke",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_TightChoke",Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f) //0.7 PerkSkills(ESupportResupply)=(Name="Resupply",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_ResupplyPack",Increment=0.f ,Rank=0,StartingValue=0.2f,MaxValue=0.2f) PerkSkills(ESupportConcussionRounds)=(Name="ConcussionRounds",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_ConcussionRounds",Increment=0.f,Rank=0,StartingValue=1.5f,MaxValue=1.5f) PerkSkills(ESupportPerforate)=(Name="Perforate",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_Penetrator",Increment=0.f,Rank=0,StartingValue=30.f,MaxValue=30.f) PerkSkills(ESupportBarrage)=(Name="Barrage",IconPath="UI_PerkTalent_TEX.Support.UI_Talents_Support_Barrage",Increment=0.f,Rank=0,StartingValue=5.f,MaxValue=5.f) SecondaryXPModifier[0]=8 SecondaryXPModifier[1]=8 SecondaryXPModifier[2]=8 SecondaryXPModifier[3]=8 DoorRepairXP[0]=18 DoorRepairXP[1]=24 DoorRepairXP[2]=30 DoorRepairXP[3]=42 // Skill tracking HitAccuracyHandicap=-6.0 HeadshotAccuracyHandicap=-3.0 AutoBuyLoadOutPath=(class'KFWeapDef_MB500', class'KFWeapDef_DoubleBarrel', class'KFWeapDef_M4', class'KFWeapDef_AA12') // Prestige Rewards PrestigeRewardItemIconPaths[0]="WEP_SkinSet_Prestige01_Item_TEX.knives.SupportKnife_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[1]="WEP_SkinSet_Prestige02_Item_TEX.tier01.MB500_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[2]="WEP_skinset_prestige03_itemtex.tier02.Boomstick_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[3]="wep_skinset_prestige04_itemtex.tier03.M4_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[4]="WEP_SkinSet_Prestige05_Item_TEX.tier04.AA12_PrestigePrecious_Mint_large" ZedTimeModifyingStates(0)="WeaponFiring" ZedTimeModifyingStates(1)="WeaponBurstFiring" ZedTimeModifyingStates(2)="WeaponSingleFiring" ZedTimeModifyingStates(3)="WeaponAltFiring" ZedTimeModifyingStates(4)="BlunderbussDeployAndDetonate" HighCapMagExemptList(0)="KFWeap_Shotgun_DoubleBarrel" HighCapMagExemptList(1)="KFWeap_HRG_Revolver_Buckshot" HighCapMagExemptList(2)="KFWeap_HRG_Revolver_DualBuckshot" HighCapMagExemptList(3)="KFWeap_Shotgun_ElephantGun" AdditionalOnPerkDTNames(0)="KFDT_Ballistic_Shotgun_Medic" AdditionalOnPerkDTNames(1)="KFDT_Ballistic_DragonsBreath" AdditionalOnPerkDTNames(2)="KFDT_Ballistic_NailShotgun" }