//============================================================================= // KFPerk_Survivalist //============================================================================= // The survivalist perk class //============================================================================= // Killing Floor 2 // Copyright (C) 2016 Tripwire Interactive LLC // - Christian "schneidzekk" Schneider //============================================================================= class KFPerk_Survivalist extends KFPerk native; //`include(KFOnlineStats.uci) /** Passives */ var private const PerkSkill WeaponDamage; var private const PerkSkill DamageResistance; var private const PerkSkill HeavyBodyArmor; var private const PerkSkill ZedTimeReload; /** Selectable skills */ enum ESurvivalistPerkSkills { ESurvivalist_TacticalReload, ESurvivalist_HeavyWeaponsReload, ESurvivalist_FieldMedic, ESurvivalist_MeleeExpert, ESurvivalist_AmmoVest, ESurvivalist_BigPockets, ESurvivalist_Shrapnel, ESurvivalist_MakeThingsGoBoom, ESurvivalist_MadMan, ESurvivalist_IncapMaster }; var const int SecondaryNearKillXPModifier[4]; var private const float InjectionPotencyModifier; var private const float MeleeExpertAttackSpeedModifier; var private const GameExplosion ShrapnelExplosionTemplate; var private const float ShrapnelChance; var private const float SnarePower; var private const float MeleeExpertMovementSpeedModifier; var private const float PassiveWeaponSwitchModifier; var class HealingGrenadeWeaponDef; var class MolotovGrenadeWeaponDef; var private const array > PrimaryWeaponPaths; var private const array > GrenadeWeaponPaths; var private const array KnifeWeaponPaths; var byte StartingWeaponClassIndex; var byte StartingGrenadeClassIndex; // This is the cached value for the tentative Grenade selection (only applied when gameplay time) var byte CurrentGrenadeClassIndex; // This is the gameplay value we use for Grenade Index, it can only be changed while TraderTime / Wave Start / Wave Ended var private const array TacticalReloadAsReloadRateClassNames; /** When MakeThingsGoBoom skill is selected the survivalist gets additional explosive resistance */ var private const float MakeThingsGoBoomExplosiveResistance; var private const byte MedicGrenadeIndex; var private const byte FirebugGrenadeIndex; var private transient bool bIsGrenadeDirty; `define WEAP_IDX_NONE 255 /********************************************************************************************* * @name Perk init and spawning ******************************************************************************************** */ /** On spawn, modify owning pawn based on perk selection */ function SetPlayerDefaults( Pawn PlayerPawn ) { local float NewArmor; super.SetPlayerDefaults( PlayerPawn ); if( OwnerPawn.Role == ROLE_Authority ) { NewArmor = OwnerPawn.default.MaxArmor * GetPassiveValue( HeavyBodyArmor, CurrentLevel ); OwnerPawn.AddArmor( Round( NewArmor ) ); } } /** * @brief(Server) Modify Instigator settings based on selected perk */ function ApplyWeightLimits() { local KFInventoryManager KFIM; KFIM = KFInventoryManager(OwnerPawn.InvManager); if( KFIM != none ) { if( IsBigPocketsActive() ) { `QALog( "Big Pockets Mod" @ GetPercentage(KFIM.MaxCarryBlocks, KFIM.default.MaxCarryBlocks + PerkSkills[ESurvivalist_BigPockets].StartingValue), bLogPerk ); KFIM.MaxCarryBlocks = KFIM.default.MaxCarryBlocks + GetSkillValue( PerkSkills[ESurvivalist_BigPockets] ); CheckForOverWeight( KFIM ); } else { super.ApplyWeightLimits(); } } } function bool ShouldGetAllTheXP() { return true; } /* Returns the primary weapon's class path for this perk */ simulated function string GetPrimaryWeaponClassPath() { if (StartingWeaponClassIndex == `WEAP_IDX_NONE) { StartingWeaponClassIndex = Rand(PrimaryWeaponPaths.length); } AutoBuyLoadOutPath.InsertItem(0,PrimaryWeaponPaths[StartingWeaponClassIndex]); return PrimaryWeaponPaths[StartingWeaponClassIndex].default.WeaponClassPath; } function bool ShouldAutosellWeapon(class DefClass) { //Because survivalists get a random first weapon in their auto buy load out, if they ever swap // to another valid on-perk T1 then attempt to autobuy, they could be left in situations where // they sell the new valid T1, but don't have enough money to buy any other weapons. In this // case, we shouldn't sell the weapon if it's also part of the primary weapons that they could // start with in a valid match. if (super.ShouldAutosellWeapon(DefClass)) { return PrimaryWeaponPaths.Find(DefClass) == INDEX_NONE; } return false; } /********************************************************************************************* * @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 DamageType, optional int HitZoneIdx ) { local KFWeapon KFW; local float TempDamage; TempDamage = InDamage; TempDamage += InDamage * GetPassiveValue( WeaponDamage, CurrentLevel ); if( DamageCauser != none ) { KFW = GetWeaponFromDamageCauser( DamageCauser ); } if( KFW != none ) { if( KFW.IsMeleeWeapon() && IsMeleeExpertActive() ) { TempDamage += InDamage * GetSkillValue( PerkSkills[ESurvivalist_MeleeExpert] ); } } `QALog( "Total Damage Given" @ DamageType @ KFW @ GetPercentage( InDamage, Round(TempDamage) ), bLogPerk ); InDamage = Round(TempDamage); } /** * @brief Modifies the damage taken * * @param InDamage damage * @param DamageType the damage type used (optional) */ function ModifyDamageTaken( out int InDamage, optional class DamageType, optional Controller InstigatedBy ) { local float TempDamage; if( InDamage <= 0 ) { return; } TempDamage = InDamage; TempDamage -= InDamage * GetPassiveValue( DamageResistance, CurrentLevel ); if (IsMakeThingsGoBoomActive()) { // only for explosive damage if (ClassIsChildOf(DamageType, class'KFDT_Explosive')) { TempDamage = FMax(TempDamage - InDamage * MakeThingsGoBoomExplosiveResistance, 0.0f); } } `QALog( "Total DamageResistance" @ DamageType @ 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() ) { return; } TempDuration = InDuration; if( IsMeleeExpertActive() ) { TempDuration -= TempDuration * static.GetMeleeExpertAttackSpeedModifier(); } `QALog( "Total, Melee Attack Speed" @ GetPercentage( InDuration, TempDuration ) @ "Start/End" @ InDuration @ TempDuration, bLogPerk ); InDuration = TempDuration; } /** * @brief Weapons and perk skills can affect the jog/sprint speed * * @param Speed jog/sprint speed */ simulated function ModifySpeed( out float Speed ) { local float TempSpeed; local KFWeapon KFW; if( IsMeleeExpertActive() ) { KFW = GetOwnerWeapon(); if( KFW != none && KFW.IsMeleeWeapon() ) { TempSpeed = Speed + Speed * static.GetMeleeExpertMovementSpeedModifier(); Speed = TempSpeed; } } } simulated private static function float GetMeleeExpertMovementSpeedModifier() { return default.MeleeExpertMovementSpeedModifier; } simulated private static function float GetMeleeExpertAttackSpeedModifier() { return default.MeleeExpertAttackSpeedModifier; } simulated function bool HasHeavyArmor() { return true; } /** * @brief Modifies the reload speed for commando weapons * * @param ReloadDuration Length of the reload animation * @param GiveAmmoTime Time after the weapon actually gets some ammo */ simulated function float GetReloadRateScale( KFWeapon KFW ) { if( WorldInfo.TimeDilation < 1.f && IsZedTimeReloadAllowed() ) { return 1.f - GetPassiveValue( ZedTimeReload, GetLevel() ); } if (((IsTacticalReloadActive() && IsWeaponOnPerkLight(KFW)) || (IsHeavyReloadActive() && IsWeaponOnPerkHeavy(KFW))) && TacticalReloadAsReloadRateClassNames.Find(KFW.class.Name) != INDEX_NONE) { return 0.8f; } return 1.f; } /** * @brief For modes that disable zed time skill tiers, also disable zed time reload */ simulated function bool IsZedTimeReloadAllowed() { return MyKFGRI != none ? (MyKFGRI.MaxPerkLevel == MyKFGRI.default.MaxPerkLevel) : false; } /** * @brief Modifies the pawn's MaxArmor * * @param MaxArmor the maximum armor value */ function ModifyArmor( out byte MaxArmor ) { local float TempArmor; if( HasHeavyArmor() ) { TempArmor = MaxArmor; TempArmor += MaxArmor * GetPassiveValue( HeavyBodyArmor, CurrentLevel ); MaxArmor = Round( TempArmor ); } } /** * @brief The passive skill of the survivalist modifies the weapon switch speed * * @param ModifiedSwitchTime Duration of putting down or equipping the weapon */ simulated function ModifyWeaponSwitchTime( out float ModifiedSwitchTime ) { `QALog( "(Passive Weapon Switch) Increase:" @ GetPercentage( ModifiedSwitchTime, ModifiedSwitchTime * (1.f - static.GetPassiveWeaponSwitchModifier()) ), bLogPerk ); ModifiedSwitchTime *= 1.f - static.GetPassiveWeaponSwitchModifier(); } simulated final static function float GetPassiveWeaponSwitchModifier() { return default.PassiveWeaponSwitchModifier; } /********************************************************************************************* * @name Selectable skills functions ********************************************************************************************* */ /** * @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() && IsWeaponOnPerkLight(KFW)) || (IsHeavyReloadActive() && IsWeaponOnPerkHeavy(KFW))) && TacticalReloadAsReloadRateClassNames.Find(KFW.class.Name) == INDEX_NONE); } simulated function bool IsWeaponOnPerkLight( KFWeapon KFW ) { if( KFW != none ) { return (class'KFPerk_Commando'.static.IsWeaponOnPerk( KFW,, class'KFPerk_Commando' ) || class'KFPerk_Gunslinger'.static.IsWeaponOnPerk( KFW,, class'KFPerk_Gunslinger' ) || class'KFPerk_SWAT'.static.IsWeaponOnPerk( KFW,, class'KFPerk_SWAT' )); } return false; } simulated function bool IsWeaponOnPerkHeavy( KFWeapon KFW ) { if( KFW != none ) { return (class'KFPerk_Demolitionist'.static.IsWeaponOnPerk( KFW,, class'KFPerk_Demolitionist' ) || class'KFPerk_Support'.static.IsWeaponOnPerk( KFW,, class'KFPerk_Support' ) || class'KFPerk_Sharpshooter'.static.IsWeaponOnPerk( KFW,, class'KFPerk_Sharpshooter' ) || KFW.IsA('KFPerk_Survivalist') || (KFW.IsHeavyWeapon() && IsWeaponOnPerk(KFW)) ); } return false; } /** * @brief Modifies how long one recharge cycle takes * * @param RechargeRate charging rate per sec */ simulated function ModifyHealerRechargeTime( out float RechargeRate ) { if( IsFieldMedicActive() ) { RechargeRate -= RechargeRate * GetSkillValue( PerkSkills[ESurvivalist_FieldMedic] ); } } /** * @brief Modifies how potent one healing shot is * * @param HealAmount health repaired * @return true if Armament is active to repair armor if possible */ function bool ModifyHealAmount( out float HealAmount ) { if( IsFieldMedicActive() ) { HealAmount *= static.GetInjectionPotencyModifier(); } return IsFieldMedicActive(); } static private function float GetInjectionPotencyModifier() { return default.InjectionPotencyModifier; } /** * @brief Modifies the max spare ammo * * @param KFW The weapon * @param MaxSpareAmmo 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 bool bUsesAmmo; local float TempMaxSpareAmmoAmount; bUsesAmmo = (KFW == none) ? TraderItem.WeaponDef.static.UsesAmmo() : KFW.UsesAmmo(); if( IsAmmoVestActive() && bUsesAmmo ) { TempMaxSpareAmmoAmount = MaxSpareAmmo; TempMaxSpareAmmoAmount += MaxSpareAmmo * GetSkillValue( PerkSkills[ESurvivalist_AmmoVest] ); MaxSpareAmmo = Round( TempMaxSpareAmmoAmount ); } } /** * @brief Checks if a zed could potentially explode later * * @param KFDT damage type used to deal damage * @return if the zed could explode when dying */ function bool CouldBeZedShrapnel( class KFDT ) { return IsZedShrapnelActive(); } /** * @brief The Zed shrapnel skill can spawn an explosion, this function delivers the template * * @return A game explosion template */ function GameExplosion GetExplosionTemplate() { return default.ShrapnelExplosionTemplate; } /** * @brief Checks if a Zed should explode * * @return explode or not */ simulated function bool ShouldShrapnel() { return IsZedShrapnelActive() && fRand() <= default.ShrapnelChance; } simulated function float GetAoERadiusModifier() { return IsMakeThingsGoBoomActive() ? GetSkillValue( PerkSkills[ESurvivalist_MakeThingsGoBoom] ) : 1.f; } /** * @brief Skills can modify the zed time time delation * * @param W used weapon * @return time dilation modifier */ simulated function float GetZedTimeModifier( KFWeapon W ) { local name StateName; if( GetMadManActive() && (!W.IsMeleeWeapon() || KFWeap_MeleeBase(W).default.bHasToBeConsideredAsRangedWeaponForPerks || IsBlastBrawlers(W) )) { StateName = W.GetStateName(); `Warn(StateName); // 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( ZedTimeModifyingStates.Find( StateName ) != INDEX_NONE || (StateName == 'MeleeChainAttacking' && IsBlastBrawlers(W)) || (StateName == 'FiringSecondaryState' && IsFAMAS(W))) { return GetSkillValue( PerkSkills[ESurvivalist_MadMan] ); } } return 0.f; } /** * @brief Specific modifier for the Minigun Windup rotation * * @return time dilation modifier */ simulated function float GetZedTimeModifierForWindUp() { if( GetMadManActive() ) { return GetSkillValue( PerkSkills[ESurvivalist_MadMan] ); } return 0.f; } /** * @brief Skills can can change the knock down power * @return knock down power in % */ function float GetKnockdownPowerModifier( optional class DamageType, optional byte BodyPart, optional bool bIsSprinting=false ) { if( GetIncapMasterActive() ) { `QALog( "LimbShots knockdown, Hit" @ BodyPart @ GetSkillValue( PerkSkills[ESurvivalist_IncapMaster] ), bLogPerk ); //[FFERRANDO @ SABER3D] INCAP MASTER NOW ONLY MODIFIES STUN POWER return 0; //return GetSkillValue( PerkSkills[ESurvivalist_IncapMaster] ); } return 0.f; } /** * @brief skills and weapons can modify the stumbling power * @return stumpling power modifier */ function float GetStumblePowerModifier( optional KFPawn KFP, optional class DamageType, optional out float CooldownModifier, optional byte BodyPart ) { if( GetIncapMasterActive() ) { `QALog( "CenterMass Stumble, Hit" @ BodyPart @ GetSkillValue( PerkSkills[ESurvivalist_IncapMaster] ), bLogPerk ); //[FFERRANDO @ SABER3D] INCAP MASTER NOW ONLY MODIFIES STUN POWER return 0; //return GetSkillValue( PerkSkills[ESurvivalist_IncapMaster] ); } return 0.f; } /** * @brief skills and weapons can guarantee a stun * @return true/false */ function bool IsStunGuaranteed( optional class DamageType, optional byte HitZoneIdx ) { if( GetIncapMasterActive() ) { return true; } return false; } simulated function float GetSnarePowerModifier( optional class DamageType, optional byte HitZoneIdx ) { if( GetIncapMasterActive() ) { return default.SnarePower; } return 0.f; } /* Returns the grenade class for this perk */ simulated function class< KFProj_Grenade > GetGrenadeClass() { return class(DynamicLoadObject(GrenadeWeaponPaths[CurrentGrenadeClassIndex].default.WeaponClassPath, class'Class')); } /** * @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.SecondaryNearKillXPModifier[Difficulty]; } /** * @brief can the perk earn small radius kill xp * @return DT is on berserker perk */ function bool CanEarnSmallRadiusKillXP(class DT) { return class'KFPerk_Berserker'.static.IsDamageTypeOnPerk(class(DT)) || class'KFPerk_Berserker'.static.IsBackupDamageTypeOnPerk(DT); } /********************************************************************************************* * @name Getters etc ********************************************************************************************* */ /** * @brief Checks if tactical reload skill is active (client & server) * * @return true/false */ simulated private function bool IsTacticalReloadActive() { return PerkSkills[ESurvivalist_TacticalReload].bActive && IsPerkLevelAllowed(ESurvivalist_TacticalReload); } /** * @brief Checks if the heavy weapons reload skill is active (client & server) * * @return true/false */ simulated private function bool IsHeavyReloadActive() { return PerkSkills[ESurvivalist_HeavyWeaponsReload].bActive && IsPerkLevelAllowed(ESurvivalist_HeavyWeaponsReload); } /** * @brief Checks if Field Medic skill is active * * @return true/false */ simulated private function bool IsFieldMedicActive() { return PerkSkills[ESurvivalist_FieldMedic].bActive && IsPerkLevelAllowed(ESurvivalist_FieldMedic); } /** * @brief Checks if Melee Expert skill is active * * @return true/false */ simulated private function bool IsMeleeExpertActive() { return PerkSkills[ESurvivalist_MeleeExpert].bActive && IsPerkLevelAllowed(ESurvivalist_MeleeExpert); } /** * @brief Checks if Big Pockets skill is active * * @return true/false */ simulated private function bool IsAmmoVestActive() { return PerkSkills[ESurvivalist_AmmoVest].bActive && IsPerkLevelAllowed(ESurvivalist_AmmoVest); } /** * @brief Checks if Big Pockets skill is active * * @return true/false */ simulated private function bool IsBigPocketsActive() { return PerkSkills[ESurvivalist_BigPockets].bActive && IsPerkLevelAllowed(ESurvivalist_BigPockets); } /** * @brief Checks if Big Pockets skill is active * * @return true/false */ simulated private function bool IsZedShrapnelActive() { return PerkSkills[ESurvivalist_Shrapnel].bActive && IsPerkLevelAllowed(ESurvivalist_Shrapnel); } /** * @brief Checks if Big Pockets skill is active * * @return true/false */ simulated private function bool IsMakeThingsGoBoomActive() { return PerkSkills[ESurvivalist_MakeThingsGoBoom].bActive && IsPerkLevelAllowed(ESurvivalist_MakeThingsGoBoom); } /** * @brief Checks if the Madman skill is active and if we are in zed time * * @return true/false */ simulated function bool GetMadManActive() { return IsMadManActive() && WorldInfo.TimeDilation < 1.f; } /** * @brief Checks if Big Pockets skill is active * * @return true/false */ simulated private function bool IsMadManActive() { return PerkSkills[ESurvivalist_MadMan].bActive && IsPerkLevelAllowed(ESurvivalist_MadMan); } /** * @brief Checks if the Incap Master skill is active and if we are in zed time * * @return true/false */ simulated function bool GetIncapMasterActive() { return IsIncapMasterActive() && WorldInfo.TimeDilation < 1.f; } /** * @brief Checks if Incap Master skill is active * * @return true/false */ simulated private function bool IsIncapMasterActive() { return PerkSkills[ESurvivalist_IncapMaster].bActive && IsPerkLevelAllowed(ESurvivalist_IncapMaster); } /********************************************************************************************* * @name Hud/UI/Stats/Exp ********************************************************************************************* */ /** * @brief how much XP is earned by a clot kill depending on the game's difficulty * * @param Difficulty current game difficulty * @return XP earned */ simulated static function int GetClotKillXP( byte Difficulty ) { return default.SecondaryXPModifier[Difficulty]; } simulated static function GetPassiveStrings( out array PassiveValues, out array Increments, byte Level ) { PassiveValues[0] = Left( string(GetPassiveValue( default.WeaponDamage, Level ) * 100 ), InStr(string(GetPassiveValue( default.WeaponDamage, Level ) * 100 ), ".") + 2 ) $ "%"; PassiveValues[1] = Round( GetPassiveValue( default.DamageResistance, Level ) * 100 ) $ "%"; PassiveValues[2] = Round( GetPassiveValue( default.HeavyBodyArmor, Level ) * 100 ) $ "%"; PassiveValues[3] = Round( GetPassiveValue( default.ZedTimeReload, Level ) * 100 ) $ "%"; PassiveValues[4] = ""; Increments[0] = "[" @ Left( string( default.WeaponDamage.Increment * 100 ), InStr(string(default.WeaponDamage.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[1] = "[" @ Left( string( default.DamageResistance.Increment * 100 ), InStr(string(default.DamageResistance.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[2] = "[" @ Left( string( default.HeavyBodyArmor.Increment * 100 ), InStr(string(default.HeavyBodyArmor.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[3] = "[" @ Left( string( default.ZedTimeReload.Increment * 100 ), InStr(string(default.ZedTimeReload.Increment * 100), ".") + 2 ) $ "% /" @ default.LevelString @ "]"; Increments[4] = ""; } simulated function string GetGrenadeImagePath() { return CurrentGrenadeClassIndex == `WEAP_IDX_NONE ? default.GrenadeWeaponDef.static.GetImagePath() : default.GrenadeWeaponPaths[CurrentGrenadeClassIndex].static.GetImagePath(); } simulated function class GetGrenadeWeaponDef() { return GrenadeWeaponPaths[CurrentGrenadeClassIndex]; } static simulated function bool CanChoosePrimaryWeapon() { return true; } static simulated function bool CanChooseGrenade() { return true; } simulated function byte OnPrevWeaponSelected() { if (StartingWeaponClassIndex == `WEAP_IDX_NONE) { StartingWeaponClassIndex = PrimaryWeaponPaths.Length - 1; } else if (StartingWeaponClassIndex == 0) { StartingWeaponClassIndex = `WEAP_IDX_NONE; } else { --StartingWeaponClassIndex; } return StartingWeaponClassIndex; } simulated function byte OnNextWeaponSelected() { if (StartingWeaponClassIndex == `WEAP_IDX_NONE) { StartingWeaponClassIndex = 0; } else if (StartingWeaponClassIndex == PrimaryWeaponPaths.Length - 1) { StartingWeaponClassIndex = `WEAP_IDX_NONE; } else { ++StartingWeaponClassIndex; } return StartingWeaponClassIndex; } simulated function byte OnPrevGrenadeSelected() { if (StartingGrenadeClassIndex == `WEAP_IDX_NONE) { StartingGrenadeClassIndex = GrenadeWeaponPaths.Length - 1; } else { --StartingGrenadeClassIndex; if (StartingGrenadeClassIndex == FirebugGrenadeIndex && !IsBigPocketsActive()) { --StartingGrenadeClassIndex; } if (StartingGrenadeClassIndex == MedicGrenadeIndex && !IsAmmoVestActive()) { --StartingGrenadeClassIndex; } if (StartingGrenadeClassIndex > GrenadeWeaponPaths.Length - 1) { StartingGrenadeClassIndex = `WEAP_IDX_NONE; } } bIsGrenadeDirty=true; return StartingGrenadeClassIndex; } simulated function byte OnNextGrenadeSelected() { if (StartingGrenadeClassIndex == `WEAP_IDX_NONE) { StartingGrenadeClassIndex = 0; } else { ++StartingGrenadeClassIndex; if (StartingGrenadeClassIndex == MedicGrenadeIndex && !IsAmmoVestActive()) { ++StartingGrenadeClassIndex; } if (StartingGrenadeClassIndex == FirebugGrenadeIndex && !IsBigPocketsActive()) { ++StartingGrenadeClassIndex; } if (StartingGrenadeClassIndex > GrenadeWeaponPaths.Length - 1) { StartingGrenadeClassIndex = `WEAP_IDX_NONE; } } bIsGrenadeDirty=true; return StartingGrenadeClassIndex; } static simulated function string GetPrimaryWeaponName(byte Idx) { return Idx == `WEAP_IDX_NONE ? default.PrimaryWeaponDef.static.GetItemName() : default.PrimaryWeaponPaths[Idx].static.GetItemName(); } static simulated function string GetPrimaryWeaponImagePath(byte Idx) { return Idx == `WEAP_IDX_NONE ? default.PrimaryWeaponDef.static.GetImagePath() : default.PrimaryWeaponPaths[Idx].static.GetImagePath(); } static simulated function string GetGrenadeWeaponName(byte Idx) { return Idx == `WEAP_IDX_NONE ? default.GrenadeWeaponDef.static.GetItemName() : default.GrenadeWeaponPaths[Idx].static.GetItemName(); } static simulated function string GetGrenadeWeaponImagePath(byte Idx) { return Idx == `WEAP_IDX_NONE ? default.GrenadeWeaponDef.static.GetImagePath() : default.GrenadeWeaponPaths[Idx].static.GetImagePath(); } /* Returns the primary weapon's class path for this perk */ simulated function string GetGrenadeClassPath() { return GrenadeWeaponPaths[StartingGrenadeClassIndex].default.WeaponClassPath; } simulated function byte GetGrenadeSelectedIndex() { return CurrentGrenadeClassIndex; } simulated static function byte GetWeaponSelectedIndex(byte idx) { if (idx >= default.PrimaryWeaponPaths.Length && idx < 255) { return 0; } if (idx == 255) { return `WEAP_IDX_NONE; } return idx; } simulated function SetWeaponSelectedIndex(byte idx) { StartingWeaponClassIndex = static.GetWeaponSelectedIndex(idx); ServerUpdateCurrentWeapon(StartingWeaponClassIndex); } simulated function SetGrenadeSelectedIndex(byte idx) { local KFGameReplicationInfo KFGRI; KFGRI = KFGameReplicationInfo(WorldInfo.GRI); if (idx >= default.GrenadeWeaponPaths.Length && idx < 255) { StartingGrenadeClassIndex = 0; } else if (idx == 255) { StartingGrenadeClassIndex = `WEAP_IDX_NONE; } else { StartingGrenadeClassIndex = idx; if (StartingGrenadeClassIndex == MedicGrenadeIndex || StartingGrenadeClassIndex == FirebugGrenadeIndex) { if (IsAmmoVestActive()) { StartingGrenadeClassIndex = MedicGrenadeIndex; } else if (IsBigPocketsActive()) { StartingGrenadeClassIndex = FirebugGrenadeIndex; } else { StartingGrenadeClassIndex = 0; } } } // If we are in no gameplay time insta change if (!KFGRI.bWaveIsActive) { UpdateCurrentGrenade(); } } simulated static function byte GetGrenadeSelectedIndexUsingSkills(byte idx, byte InSelectedSkills[`MAX_PERK_SKILLS], bool IsChoosingPrev, bool IsChoosingNext) { local bool AmmoVestActive, BigPocketsActive; local byte SelectedGrenadeIndex; AmmoVestActive = false; BigPocketsActive = false; if (idx >= default.GrenadeWeaponPaths.Length && idx < 255) { SelectedGrenadeIndex = 0; } else if (idx == 255) { SelectedGrenadeIndex = `WEAP_IDX_NONE; } else { SelectedGrenadeIndex = idx; if (SelectedGrenadeIndex == default.MedicGrenadeIndex || SelectedGrenadeIndex == default.FirebugGrenadeIndex) { AmmoVestActive = InSelectedSkills[2] == 1; BigPocketsActive = InSelectedSkills[2] == 2; if (IsChoosingPrev) { if (SelectedGrenadeIndex == default.FirebugGrenadeIndex) { if (BigPocketsActive == false) { --SelectedGrenadeIndex; } } if (SelectedGrenadeIndex == default.MedicGrenadeIndex) { if (AmmoVestActive == false) { --SelectedGrenadeIndex; } } } else if (IsChoosingNext) { if (SelectedGrenadeIndex == default.MedicGrenadeIndex) { if (AmmoVestActive == false) { ++SelectedGrenadeIndex; } } if (SelectedGrenadeIndex == default.FirebugGrenadeIndex) { if (BigPocketsActive == false) { ++SelectedGrenadeIndex; } } } else { if (AmmoVestActive) { SelectedGrenadeIndex = default.MedicGrenadeIndex; } else if (BigPocketsActive) { SelectedGrenadeIndex = default.FirebugGrenadeIndex; } else { SelectedGrenadeIndex = 0; } } if (SelectedGrenadeIndex > default.GrenadeWeaponPaths.Length - 1) { SelectedGrenadeIndex = `WEAP_IDX_NONE; } } } return SelectedGrenadeIndex; } simulated function InitializeGrenades() { local byte MaxValue; if (StartingGrenadeClassIndex == `WEAP_IDX_NONE && ( bIsGrenadeDirty || CurrentGrenadeClassIndex == `WEAP_IDX_NONE)) { MaxValue = (!IsAmmoVestActive() && !IsBigPocketsActive()) ? GrenadeWeaponPaths.length - 1 : GrenadeWeaponPaths.length; CurrentGrenadeClassIndex = Rand(MaxValue); if ( (!IsAmmoVestActive() && CurrentGrenadeClassIndex == MedicGrenadeIndex) || (!IsBigPocketsActive() && CurrentGrenadeClassIndex == FirebugGrenadeIndex) || (CurrentLevel < 15 && (CurrentGrenadeClassIndex == MedicGrenadeIndex || CurrentGrenadeClassIndex == FirebugGrenadeIndex))) { CurrentGrenadeClassIndex = GrenadeWeaponPaths.length - 1; } bIsGrenadeDirty = false; if (Controller(Owner).IsLocalController()) { ServerUpdateCurrentGrenade(CurrentGrenadeClassIndex); } } } simulated function OnClientWaveEnded() { super.OnWaveEnded(); UpdateCurrentGrenade(); } simulated function UpdateCurrentGrenade() { if (StartingGrenadeClassIndex == `WEAP_IDX_NONE) { InitializeGrenades(); } else if (CurrentGrenadeClassIndex != StartingGrenadeClassIndex || bIsGrenadeDirty) { CurrentGrenadeClassIndex = StartingGrenadeClassIndex; if (Controller(Owner).IsLocalController()) { ServerUpdateCurrentGrenade(CurrentGrenadeClassIndex); } } } reliable server function ServerUpdateCurrentWeapon(byte Value) { StartingWeaponClassIndex = Value; } reliable server function ServerUpdateCurrentGrenade(byte CurrentIndex) { CurrentGrenadeClassIndex = CurrentIndex; } DefaultProperties { StartingWeaponClassIndex=0 StartingGrenadeClassIndex=0 CurrentGrenadeClassIndex=0 bIsGrenadeDirty=true PerkIcon=Texture2D'UI_PerkIcons_TEX.UI_PerkIcon_Survivalist' PrimaryWeaponDef=class'KFWeapDef_Random' KnifeWeaponDef=class'KFweapDef_Knife_Survivalist' GrenadeWeaponDef=class'KFWeapDef_RandomGrenade' HealingGrenadeWeaponDef=class'KFWeapDef_Grenade_Medic' MolotovGrenadeWeaponDef=class'KFWeapDef_Grenade_Firebug' ProgressStatID=STATID_Surv_Progress PerkBuildStatID=STATID_Surv_Build WeaponDamage=(Name="Weapon Damage",Increment=0.006f,Rank=0,StartingValue=0.f,MaxValue=0.15f) DamageResistance=(Name="Damage Resistance",Increment=0.01,Rank=0,StartingValue=0.f,MaxValue=0.25) HeavyBodyArmor=(Name="Heavy Body Armor",Increment=0.01,Rank=0,StartingValue=0.f,MaxValue=0.25) ZedTimeReload=(Name="Zed Time Reload",Increment=0.03f,Rank=0,StartingValue=0.f,MaxValue=0.75f) PassiveWeaponSwitchModifier=0.35f PerkSkills(ESurvivalist_TacticalReload)=(Name="TacticalReload",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_TacticalReload", Increment=0.f,Rank=0,StartingValue=0.25,MaxValue=0.25) PerkSkills(ESurvivalist_HeavyWeaponsReload)=(Name="HeavyWeaponsReload",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_HeavyWeapons", Increment=0.f,Rank=0,StartingValue=2.5f,MaxValue=2.5f) PerkSkills(ESurvivalist_FieldMedic)=(Name="FieldMedic",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_FieldMedic", Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f) PerkSkills(ESurvivalist_MeleeExpert)=(Name="MeleeExpert",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_MeleeExpert", Increment=0.f,Rank=0,StartingValue=0.75f,MaxValue=0.75f) //0.5f PerkSkills(ESurvivalist_AmmoVest)=(Name="AmmoVest",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_AmmoVest", Increment=0.f,Rank=0,StartingValue=0.15f,MaxValue=0.15f) PerkSkills(ESurvivalist_BigPockets)=(Name="BigPockets",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_BigPockets", Increment=0.f,Rank=0,StartingValue=5.f,MaxValue=5.f) PerkSkills(ESurvivalist_Shrapnel)=(Name="ZedShrapnel",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_Shrapnel", Increment=0.f,Rank=0,StartingValue=2.f,MaxValue=2.f) PerkSkills(ESurvivalist_MakeThingsGoBoom)=(Name="MakeThingsGoBoom",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_Boom", Increment=0.f,Rank=0,StartingValue=1.5f,MaxValue=1.5f) //1.4f PerkSkills(ESurvivalist_MadMan)=(Name="MadMan",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_Madman", Increment=0.f,Rank=0,StartingValue=0.5f,MaxValue=0.5f) PerkSkills(ESurvivalist_IncapMaster)=(Name="IncapMaster",IconPath="UI_PerkTalent_TEX.Survivalist.UI_Talents_Survivalist_IncapMaster", Increment=0.f,Rank=0,StartingValue=5000.0f,MaxValue=5000.0f) SecondaryXPModifier(0)=2 SecondaryXPModifier(1)=3 SecondaryXPModifier(2)=4 SecondaryXPModifier(3)=7 SecondaryNearKillXPModifier(0)=6 SecondaryNearKillXPModifier(1)=8 SecondaryNearKillXPModifier(2)=10 SecondaryNearKillXPModifier(3)=14 InjectionPotencyModifier=1.3f MeleeExpertAttackSpeedModifier=0.2f ShrapnelChance=0.3f SnarePower=20 MeleeExpertMovementSpeedModifier=0.25 Begin Object Class=KFGameExplosion Name=ExploTemplate0 Damage=50 //231 //120 DamageRadius=250 //840 //600 DamageFalloffExponent=1 DamageDelay=0.f MyDamageType=class'KFDT_Explosive_Shrapnel' // Damage Effects //KnockDownStrength=0 FractureMeshRadius=200.0 FracturePartVel=500.0 ExplosionEffects=KFImpactEffectInfo'FX_Explosions_ARCH.FX_Combustion_Explosion' ExplosionSound=AkEvent'WW_WEP_EXP_Grenade_Frag.Play_WEP_EXP_Grenade_Frag_Explosion' // Camera Shake CamShake=CameraShake'FX_CameraShake_Arch.Misc_Explosions.Light_Explosion_Rumble' CamShakeInnerRadius=450 CamShakeOuterRadius=900 CamShakeFalloff=1.f bOrientCameraShakeTowardsEpicenter=true End Object ShrapnelExplosionTemplate=ExploTemplate0 ZedTimeModifyingStates(0)="WeaponFiring" ZedTimeModifyingStates(1)="WeaponBurstFiring" ZedTimeModifyingStates(2)="WeaponSingleFiring" ZedTimeModifyingStates(3)="WeaponSingleFireAndReload" ZedTimeModifyingStates(4)="SprayingFire" ZedTimeModifyingStates(5)="WeaponAltFiring" ZedTimeModifyingStates(6)="HuskCannonCharge" ZedTimeModifyingStates(7)="CompoundBowCharge" ZedTimeModifyingStates(8)="BlunderbussDeployAndDetonate" ZedTimeModifyingStates(9)="WeaponWindingUp" ZedTimeModifyingStates(10)="MineReconstructorCharge" ZedTimeModifyingStates(11)="WeaponSonicGunSingleFiring" ZedTimeModifyingStates(12)="WeaponSonicGunCharging" PrimaryWeaponPaths(0)=class'KFWeapDef_Crovel' PrimaryWeaponPaths(1)=class'KFWeapDef_AR15' PrimaryWeaponPaths(2)=class'KFWeapDef_MB500' PrimaryWeaponPaths(3)=class'KFWeapDef_MedicPistol' PrimaryWeaponPaths(4)=class'KFWeapDef_HX25' PrimaryWeaponPaths(5)=class'KFWeapDef_CaulkBurn' PrimaryWeaponPaths(6)=class'KFWeapDef_Remington1858Dual' PrimaryWeaponPaths(7)=class'KFWeapDef_Winchester1894' PrimaryWeaponPaths(8)=class'KFWeapDef_MP7' AutoBuyLoadOutPath=(class'KFWeapDef_DragonsBreath', class'KFWeapDef_FreezeThrower', class'KFWeapDef_MedicRifle', class'KFWeapDef_LazerCutter') GrenadeWeaponPaths(0)=class'KFWeapDef_Grenade_Commando' GrenadeWeaponPaths(1)=class'KFWeapDef_Grenade_Support' GrenadeWeaponPaths(2)=class'KFWeapDef_Grenade_Medic' GrenadeWeaponPaths(3)=class'KFWeapDef_Grenade_Firebug' GrenadeWeaponPaths(4)=class'KFWeapDef_Grenade_Gunslinger' GrenadeWeaponPaths(5)=class'KFWeapDef_Grenade_SWAT' MedicGrenadeIndex = 2; FirebugGrenadeIndex = 3; // Prestige Rewards PrestigeRewardItemIconPaths[0]="WEP_SkinSet_Prestige01_Item_TEX.knives.SurvivalistKnife_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[1]="WEP_SkinSet_Prestige02_Item_TEX.tier01.FreezeThrower_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[2]="WEP_skinset_prestige03_itemtex.tier02.TommyGun_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[3]="wep_skinset_prestige04_itemtex.tier03.VLAD-1000Nailgun_PrestigePrecious_Mint_large" PrestigeRewardItemIconPaths[4]="WEP_SkinSet_Prestige05_Item_TEX.tier04.Killerwatt_PrestigePrecious_Mint_large" TacticalReloadAsReloadRateClassNames(0)="KFWeap_GrenadeLauncher_M32" MakeThingsGoBoomExplosiveResistance=0.5f //0.4f }