/* Extended Perk Manager Written by Marco */ Class ExtPerkManager extends KFPerk; enum EReplicateState { REP_CustomCharacters, REP_CustomInventory, REP_PerkClasses, REP_Done }; const CUR_SaveVersion=2; var int UserDataVersion; var transient float LastNapalmTime; // Server -> Client rep status var byte RepState; var int RepIndex; var array<Ext_PerkBase> UserPerks; var Ext_PerkBase CurrentPerk; var int ExpUpStatus[2]; var string StrPerkName; var ExtPlayerReplicationInfo PRIOwner; var Controller PlayerOwner; // Stats var int TotalEXP,TotalKills,TotalPlayTime; var bool bStatsDirty,bServerReady,bUserStatsBroken,bCurrentlyHealing; replication { // Things the server should send to the client. if (bNetDirty) CurrentPerk; } final function SetGrenadeCap(byte AddedCap) { MaxGrenadeCount = Default.MaxGrenadeCount + AddedCap; if (RepState==REP_Done) ClientSetGrenadeCap(MaxGrenadeCount); } simulated reliable client function ClientSetGrenadeCap(byte NewCap) { MaxGrenadeCount = NewCap; } function bool ApplyPerkClass(class<Ext_PerkBase> P) { local int i; for (i=0; i<UserPerks.Length; ++i) if (UserPerks[i].Class==P) { ApplyPerk(UserPerks[i]); return true; } return false; } function bool ApplyPerkName(string S) { local int i; for (i=0; i<UserPerks.Length; ++i) if (string(UserPerks[i].Class.Name)~=S) { ApplyPerk(UserPerks[i]); return true; } return false; } function ApplyPerk(Ext_PerkBase P) { local KFPawn_Human HP; local KFInventoryManager InvMan; local Ext_T_ZEDHelper H; local int i; if (P==None) return; if (PlayerOwner.Pawn != None) { InvMan = KFInventoryManager(PlayerOwner.Pawn.InvManager); if (InvMan != None) InvMan.MaxCarryBlocks = InvMan.Default.MaxCarryBlocks; foreach PlayerOwner.Pawn.ChildActors(class'Ext_T_ZEDHelper',H) { H.Destroy(); } HP = KFPawn_Human(PlayerOwner.Pawn); if (HP != None) HP.DefaultInventory = HP.Default.DefaultInventory; } if (CurrentPerk != None) { CurrentPerk.DeactivateTraits(); for (i=0; i<CurrentPerk.PerkTraits.Length; ++i) { CurrentPerk.PerkTraits[i].TraitType.Static.CancelEffectOn(KFPawn_Human(PlayerOwner.Pawn),CurrentPerk,CurrentPerk.PerkTraits[i].CurrentLevel,CurrentPerk.PerkTraits[i].Data); } } bStatsDirty = true; CurrentPerk = P; if (PRIOwner!=None) { PRIOwner.ECurrentPerk = P.Class; PRIOwner.FCurrentPerk = P; P.UpdatePRILevel(); } if (CurrentPerk!=None) { CurrentPerk.ActivateTraits(); if (PlayerOwner.Pawn != None) { HP = KFPawn_Human(PlayerOwner.Pawn); if (HP != None) { HP.HealthMax = HP.default.Health; HP.MaxArmor = HP.default.MaxArmor; ModifyHealth(HP.HealthMax); ModifyArmor(HP.MaxArmor); if (HP.Health > HP.HealthMax) HP.Health = HP.HealthMax; if (HP.Armor > HP.MaxArmor) HP.Armor = HP.MaxArmor; } } } } simulated final function Ext_PerkBase FindPerk(class<Ext_PerkBase> P) { local int i; for (i=0; i<UserPerks.Length; ++i) if (UserPerks[i].Class==P) return UserPerks[i]; return None; } simulated function PostBeginPlay() { SetTimer(0.01,false,'InitPerks'); if (WorldInfo.NetMode!=NM_Client) SetTimer(1,true,'CheckPlayTime'); } simulated function InitPerks() { local Ext_PerkBase P; if (WorldInfo.NetMode==NM_Client) { foreach DynamicActors(class'Ext_PerkBase',P) if (P.PerkManager!=Self) RegisterPerk(P); } else if (PRIOwner!=PlayerOwner.PlayerReplicationInfo) // See if was assigned an inactive PRI. { PRIOwner = ExtPlayerReplicationInfo(PlayerOwner.PlayerReplicationInfo); if (PRIOwner!=None) { if (CurrentPerk!=None) { PRIOwner.ECurrentPerk = CurrentPerk.Class; CurrentPerk.UpdatePRILevel(); } PRIOwner.RepKills = TotalKills; PRIOwner.RepEXP = TotalEXP; PRIOwner.SetInitPlayTime(TotalPlayTime); PRIOwner.PerkManager = Self; } } } function CheckPlayTime() { ++TotalPlayTime; // Stats. } function ServerInitPerks() { local int i; for (i=0; i<UserPerks.Length; ++i) UserPerks[i].SetInitialLevel(); bServerReady = true; CurrentPerk = None; if (StrPerkName!="") ApplyPerkName(StrPerkName); if (CurrentPerk==None) ApplyPerk(UserPerks[Rand(UserPerks.Length)]); } simulated function RegisterPerk(Ext_PerkBase P) { UserPerks[UserPerks.Length] = P; P.PerkManager = Self; } simulated function UnregisterPerk(Ext_PerkBase P) { UserPerks.RemoveItem(P); P.PerkManager = None; } function Destroyed() { local int i; for (i=(UserPerks.Length-1); i>=0; --i) { UserPerks[i].PerkManager = None; UserPerks[i].Destroy(); } } function EarnedEXP(int EXP, optional byte Mode) { // `log("EarnedEXP" @ GetScriptTrace()); if (CurrentPerk!=None) { // Limit how much EXP we got for healing and welding. switch (Mode) { case 1: ExpUpStatus[0]+=EXP; EXP = ExpUpStatus[0]/CurrentPerk.WeldExpUpNum; if (EXP>0) ExpUpStatus[0]-=(EXP*CurrentPerk.WeldExpUpNum); break; case 2: ExpUpStatus[1]+=EXP; EXP = ExpUpStatus[1]/CurrentPerk.HealExpUpNum; if (EXP>0) ExpUpStatus[1]-=(EXP*CurrentPerk.HealExpUpNum); break; } if (EXP>0 && CurrentPerk.EarnedEXP(EXP)) { TotalEXP+=EXP; PRIOwner.RepEXP+=EXP; bStatsDirty = true; } } } // XML stat writing function OutputXML(ExtStatWriter Data) { local string S; local int i; Data.StartIntendent("user","ver",string(CUR_SaveVersion)); Data.WriteValue("id64",OnlineSubsystemSteamworks(class'GameEngine'.Static.GetOnlineSubsystem()).UniqueNetIdToInt64(PRIOwner.UniqueId)); Data.WriteValue("name",PRIOwner.PlayerName); Data.WriteValue("exp",string(TotalEXP)); Data.WriteValue("kills",string(TotalKills)); Data.WriteValue("time",string(TotalPlayTime)); if (ExtPlayerController(Owner)!=None && ExtPlayerController(Owner).PendingPerkClass!=None) S = string(ExtPlayerController(Owner).PendingPerkClass.Name); else S = (CurrentPerk!=None ? string(CurrentPerk.Class.Name) : "None"); Data.WriteValue("activeperk",S); for (i=0; i<UserPerks.Length; ++i) if (UserPerks[i].HasAnyProgress()) UserPerks[i].OutputXML(Data); Data.EndIntendent(); } // Data saving. function SaveData(ExtSaveDataBase Data) { local int i,o; Data.FlushData(); Data.SetSaveVersion(++UserDataVersion); Data.SetArVer(CUR_SaveVersion); // Write global stats. Data.SaveInt(TotalEXP,3); Data.SaveInt(TotalKills,3); Data.SaveInt(TotalPlayTime,3); // Write character. if (PRIOwner!=None) PRIOwner.SaveCustomCharacter(Data); else class'ExtPlayerReplicationInfo'.Static.DummySaveChar(Data); // Write selected perk. if (ExtPlayerController(Owner)!=None && ExtPlayerController(Owner).PendingPerkClass!=None) Data.SaveStr(string(ExtPlayerController(Owner).PendingPerkClass.Name)); else Data.SaveStr(CurrentPerk!=None ? string(CurrentPerk.Class.Name) : ""); // Count how many progressed perks we have. o = 0; for (i=0; i<UserPerks.Length; ++i) if (UserPerks[i].HasAnyProgress()) ++o; // Then write count we have. Data.SaveInt(o); // Then perk stats. for (i=0; i<UserPerks.Length; ++i) { if (!UserPerks[i].HasAnyProgress()) // Skip this perk. continue; Data.SaveStr(string(UserPerks[i].Class.Name)); o = Data.TellOffset(); // Mark checkpoint. Data.SaveInt(0,1); // Reserve space for later. UserPerks[i].SaveData(Data); // Now save the skip offset for perk data incase perk gets removed from server. Data.SeekOffset(o); Data.SaveInt(Data.TotalSize(),1); Data.ToEnd(); } } // Data loading. function LoadData(ExtSaveDataBase Data) { local int i,j,l,o; local string S; Data.ToStart(); UserDataVersion = Data.GetSaveVersion(); // Read global stats. TotalEXP = Data.ReadInt(3); TotalKills = Data.ReadInt(3); TotalPlayTime = Data.ReadInt(3); // Read character. if (PRIOwner!=None) { PRIOwner.RepKills = TotalKills; PRIOwner.RepEXP = TotalEXP; PRIOwner.SetInitPlayTime(TotalPlayTime); PRIOwner.LoadCustomCharacter(Data); } else class'ExtPlayerReplicationInfo'.Static.DummyLoadChar(Data); // Find selected perk. CurrentPerk = None; StrPerkName = Data.ReadStr(); l = Data.ReadInt(); // Perk stats length. for (i=0; i<l; ++i) { S = Data.ReadStr(); o = Data.ReadInt(1); // Read skip offset. Data.PushEOFLimit(o); for (j=0; j<UserPerks.Length; ++j) if (S~=string(UserPerks[j].Class.Name)) { UserPerks[j].LoadData(Data); break; } Data.PopEOFLimit(); Data.SeekOffset(o); // Jump to end of this section. } bStatsDirty = false; } function AddDefaultInventory(KFPawn P) { local KFInventoryManager KFIM; if (P != none && P.InvManager != none) { KFIM = KFInventoryManager(P.InvManager); if (KFIM != none) { //Grenades added on spawn KFIM.GiveInitialGrenadeCount(); } if (CurrentPerk!=None) CurrentPerk.AddDefaultInventory(P); } } simulated function PlayerDied() { if (CurrentPerk!=None) CurrentPerk.PlayerDied(); } function PreNotifyPlayerLeave() { if (CurrentPerk!=None) CurrentPerk.DeactivateTraits(); } // Start client replication of perks data. // Call this once the stats has been properly loaded serverside! function InitiateClientRep() { RepState = 0; RepIndex = 0; SetTimer(0.01,true,'ReplicateTimer'); } function ReplicateTimer() { switch (RepState) { case REP_CustomCharacters: // Replicate custom characters. if (RepIndex>=PRIOwner.CustomCharList.Length) { PRIOwner.AllCharReceived(); RepIndex = 0; ++RepState; } else { PRIOwner.ReceivedCharacter(RepIndex,PRIOwner.CustomCharList[RepIndex]); ++RepIndex; } break; case REP_CustomInventory: // Replicate custom trader inventory if (!PRIOwner.OnRepNextItem(PRIOwner,RepIndex)) { RepIndex = 0; ++RepState; } else ++RepIndex; break; case REP_PerkClasses: // Open up all actor channel connections. if (RepIndex>=UserPerks.Length) { RepIndex = 0; ++RepState; } else if (UserPerks[RepIndex].bClientAuthorized) { if (UserPerks[RepIndex].bPerkNetReady) ++RepIndex; } else { UserPerks[RepIndex].RemoteRole = ROLE_SimulatedProxy; if (UserPerks[RepIndex].NextAuthTime<WorldInfo.RealTimeSeconds) { UserPerks[RepIndex].NextAuthTime = WorldInfo.RealTimeSeconds+0.5; UserPerks[RepIndex].ClientAuth(); } } break; default: if (MaxGrenadeCount!=Default.MaxGrenadeCount) ClientSetGrenadeCap(MaxGrenadeCount); ClearTimer('ReplicateTimer'); } } function bool CanEarnSmallRadiusKillXP(class<DamageType> DT) { return true; } simulated function ModifySpeed(out float Speed) { if (CurrentPerk!=None) Speed *= CurrentPerk.Modifiers[0]; } function ModifyDamageGiven(out int InDamage, optional Actor DamageCauser, optional KFPawn_Monster MyKFPM, optional KFPlayerController DamageInstigator, optional class<KFDamageType> DamageType, optional int HitZoneIdx) { if (CurrentPerk!=None) CurrentPerk.ModifyDamageGiven(InDamage,DamageCauser,MyKFPM,DamageInstigator,DamageType,HitZoneIdx); } simulated function ModifyDamageTaken(out int InDamage, optional class<DamageType> DamageType, optional Controller InstigatedBy) { if (CurrentPerk!=None) CurrentPerk.ModifyDamageTaken(InDamage,DamageType,InstigatedBy); } simulated function ModifyRecoil(out float CurrentRecoilModifier, KFWeapon KFW) { if (CurrentPerk!=None) CurrentPerk.ModifyRecoil(CurrentRecoilModifier,KFW); } simulated function ModifySpread(out float InSpread) { if (CurrentPerk!=None) CurrentPerk.ModifySpread(InSpread); } simulated function ModifyRateOfFire(out float InRate, KFWeapon KFW) { if (CurrentPerk!=None) CurrentPerk.ModifyRateOfFire(InRate,KFW); } simulated function float GetReloadRateScale(KFWeapon KFW) { return (CurrentPerk!=None ? CurrentPerk.GetReloadRateScale(KFW) : 1.f); } simulated function bool GetUsingTactialReload(KFWeapon KFW) { return (CurrentPerk!=None ? CurrentPerk.GetUsingTactialReload(KFW) : false); } function ModifyHealth(out int InHealth) { if (CurrentPerk!=None) CurrentPerk.ModifyHealth(InHealth); } function ModifyArmor(out byte MaxArmor) { if (CurrentPerk!=None) CurrentPerk.ModifyArmor(MaxArmor); } function float GetKnockdownPowerModifier(optional class<DamageType> DamageType, optional byte BodyPart, optional bool bIsSprinting=false) { return (CurrentPerk!=None ? CurrentPerk.GetKnockdownPowerModifier() : 1.f); } function float GetStumblePowerModifier(optional KFPawn KFP, optional class<KFDamageType> DamageType, optional out float CooldownModifier, optional byte BodyPart) { return (CurrentPerk!=None ? CurrentPerk.GetKnockdownPowerModifier() : 1.f); } function float GetStunPowerModifier(optional class<DamageType> DamageType, optional byte HitZoneIdx) { return (CurrentPerk!=None ? CurrentPerk.GetStunPowerModifier(DamageType,HitZoneIdx) : 1.f); } simulated function ModifyMeleeAttackSpeed(out float InDuration, KFWeapon KFW) { if (CurrentPerk!=None) CurrentPerk.ModifyMeleeAttackSpeed(InDuration); } simulated function class<KFProj_Grenade> GetGrenadeClass() { return (CurrentPerk!=None ? CurrentPerk.GrenadeClass : GrenadeClass); } simulated function ModifyWeldingRate(out float FastenRate, out float UnfastenRate) { if (CurrentPerk!=None) CurrentPerk.ModifyWeldingRate(FastenRate,UnfastenRate); } simulated function bool HasNightVision() { return (CurrentPerk!=None ? CurrentPerk.bHasNightVision : false); } function bool RepairArmor(Pawn HealTarget) { return (CurrentPerk!=None ? CurrentPerk.RepairArmor(HealTarget) : false); } function bool ModifyHealAmount(out float HealAmount) { return (CurrentPerk!=None ? CurrentPerk.ModifyHealAmount(HealAmount) : false); } function bool CanNotBeGrabbed() { return (CurrentPerk!=None ? !CurrentPerk.bCanBeGrabbed : false); } simulated function ModifyMagSizeAndNumber(KFWeapon KFW, out int MagazineCapacity, optional array< Class<KFPerk> > WeaponPerkClass, optional bool bSecondary=false, optional name WeaponClassname) { if (CurrentPerk!=None) CurrentPerk.ModifyMagSizeAndNumber(KFW,MagazineCapacity,WeaponPerkClass,bSecondary,WeaponClassname); } simulated function ModifySpareAmmoAmount(KFWeapon KFW, out int PrimarySpareAmmo, optional const out STraderItem TraderItem, optional bool bSecondary=false) { if (CurrentPerk!=None) CurrentPerk.ModifySpareAmmoAmount(KFW,PrimarySpareAmmo,TraderItem,bSecondary); } simulated function ModifyMaxSpareAmmoAmount(KFWeapon KFW, out int SpareAmmoCapacity, optional const out STraderItem TraderItem, optional bool bSecondary=false) { if (CurrentPerk!=None) CurrentPerk.ModifySpareAmmoAmount(KFW,SpareAmmoCapacity,TraderItem,bSecondary); } simulated function bool ShouldMagSizeModifySpareAmmo(KFWeapon KFW, optional Class<KFPerk> WeaponPerkClass) { return (CurrentPerk!=None ? CurrentPerk.ShouldMagSizeModifySpareAmmo(KFW,WeaponPerkClass) : false); } simulated function ModifyHealerRechargeTime(out float RechargeRate) { if (CurrentPerk!=None) CurrentPerk.ModifyHealerRechargeTime(RechargeRate); } simulated function bool CanExplosiveWeld() { return (CurrentPerk!=None ? CurrentPerk.bExplosiveWeld : false); } simulated function bool IsOnContactActive() { return (CurrentPerk!=None ? CurrentPerk.bExplodeOnContact : false); } function bool CanSpreadNapalm() { if (CurrentPerk!=None && CurrentPerk.bNapalmFire && LastNapalmTime!=WorldInfo.TimeSeconds) { LastNapalmTime = WorldInfo.TimeSeconds; // Avoid infinite script recursion in KFPawn_Monster. return true; } return false; } simulated function bool IsRangeActive() { return MyPRI!=None ? MyPRI.bExtraFireRange : false; } simulated function DrawSpecialPerkHUD(Canvas C) { if (CurrentPerk!=None) CurrentPerk.DrawSpecialPerkHUD(C); } function PlayerKilled(KFPawn_Monster Victim, class<DamageType> DamageType) { if (CurrentPerk!=None) CurrentPerk.PlayerKilled(Victim,DamageType); } function ModifyBloatBileDoT(out float DoTScaler) { if (CurrentPerk!=None) CurrentPerk.ModifyBloatBileDoT(DoTScaler); } simulated function bool GetIsUberAmmoActive(KFWeapon KFW) { return (CurrentPerk!=None ? CurrentPerk.GetIsUberAmmoActive(KFW) : false); } function UpdatePerkHeadShots(ImpactInfo Impact, class<DamageType> DamageType, int NumHit) { if (CurrentPerk!=None) CurrentPerk.UpdatePerkHeadShots(Impact,DamageType,NumHit); } function CheckForAirborneAgent(KFPawn HealTarget, class<DamageType> DamType, int HealAmount) { if (!bCurrentlyHealing && CurrentPerk!=None) { // Using boolean to avoid infinite recursion. bCurrentlyHealing = true; CurrentPerk.CheckForAirborneAgent(HealTarget,DamType,HealAmount); bCurrentlyHealing = false; } } simulated function float GetZedTimeModifier(KFWeapon W) { return (CurrentPerk!=None ? CurrentPerk.GetZedTimeModifier(W) : 0.f); } // Poison darts function bool IsAcidicCompoundActive() { return (CurrentPerk!=None ? CurrentPerk.bToxicDart : false); } function ModifyACDamage(out int InDamage) { if (CurrentPerk!=None && CurrentPerk.bToxicDart) InDamage += CurrentPerk.ToxicDartDamage; } // Zombie explosion! function bool CouldBeZedShrapnel(class<KFDamageType> KFDT) { return (CurrentPerk!=None ? (CurrentPerk.bFireExplode && class<KFDT_Fire>(KFDT)!=None) : false); } simulated function bool ShouldShrapnel() { return (CurrentPerk!=None ? (CurrentPerk.bFireExplode && Rand(3)==0) : false); } function GameExplosion GetExplosionTemplate() { return class'KFPerk_Firebug'.Default.ExplosionTemplate; } // Additional functions function OnWaveEnded() { CurrentPerk.OnWaveEnded(); } function NotifyZedTimeStarted() { CurrentPerk.NotifyZedTimeStarted(); } simulated function float GetZedTimeExtensions(byte Level) { return CurrentPerk.GetZedTimeExtensions(Level); } // SWAT: simulated function bool HasHeavyArmor() { return (CurrentPerk!=None && CurrentPerk.bHeavyArmor); } simulated function float GetIronSightSpeedModifier(KFWeapon KFW) { return (CurrentPerk!=None ? CurrentPerk.GetIronSightSpeedModifier(KFW) : 1.f); } simulated function float GetCrouchSpeedModifier(KFWeapon KFW) { return (CurrentPerk!=None ? CurrentPerk.GetIronSightSpeedModifier(KFW) : 1.f); } simulated function bool ShouldKnockDownOnBump() { return (CurrentPerk!=None && CurrentPerk.bHasSWATEnforcer); } // DEMO: simulated function bool ShouldRandSirenResist() { return (Ext_PerkDemolition(CurrentPerk)!=None ? Ext_PerkDemolition(CurrentPerk).bSirenResistance : false); } simulated function bool IsAoEActive() { return (Ext_PerkDemolition(CurrentPerk)!=None ? Ext_PerkDemolition(CurrentPerk).AOEMult > 1.0f : false); } simulated function bool ShouldSacrifice() { return (Ext_PerkDemolition(CurrentPerk)!=None ? (Ext_PerkDemolition(CurrentPerk).bCanUseSacrifice && !Ext_PerkDemolition(CurrentPerk).bUsedSacrifice) : false); } simulated function bool ShouldNeverDud() { return (Ext_PerkDemolition(CurrentPerk)!=None ? Ext_PerkDemolition(CurrentPerk).bProfessionalActive : false); } function NotifyPerkSacrificeExploded() { if (Ext_PerkDemolition(CurrentPerk) != none) Ext_PerkDemolition(CurrentPerk).bUsedSacrifice = true; } simulated function float GetAoERadiusModifier() { return (Ext_PerkDemolition(CurrentPerk)!=None ? Ext_PerkDemolition(CurrentPerk).GetAoERadiusModifier() : 1.0); } // MEDIC: simulated function bool GetHealingSpeedBoostActive() { return (Ext_PerkFieldMedic(CurrentPerk)!=None ? Ext_PerkFieldMedic(CurrentPerk).GetHealingSpeedBoostActive() : false); } simulated function bool GetHealingDamageBoostActive() { return (Ext_PerkFieldMedic(CurrentPerk)!=None ? Ext_PerkFieldMedic(CurrentPerk).GetHealingDamageBoostActive() : false); } simulated function bool GetHealingShieldActive() { return (Ext_PerkFieldMedic(CurrentPerk)!=None ? Ext_PerkFieldMedic(CurrentPerk).GetHealingShieldActive() : false); } simulated function float GetSelfHealingSurgePct() { return (Ext_PerkFieldMedic(CurrentPerk)!=None ? Ext_PerkFieldMedic(CurrentPerk).GetSelfHealingSurgePct() : 0.f); } function bool IsToxicDmgActive() { return (Ext_PerkFieldMedic(CurrentPerk)!=None ? Ext_PerkFieldMedic(CurrentPerk).bUseToxicDamage : false); } static function class<KFDamageType> GetToxicDmgTypeClass() { return class'Ext_PerkFieldMedic'.static.GetToxicDmgTypeClass(); } static function ModifyToxicDmg(out int ToxicDamage) { ToxicDamage = class'Ext_PerkFieldMedic'.static.ModifyToxicDmg(ToxicDamage); } simulated function float GetSnarePower(optional class<DamageType> DamageType, optional byte HitZoneIdx) { return (Ext_PerkFieldMedic(CurrentPerk)!=None ? Ext_PerkFieldMedic(CurrentPerk).GetSnarePower(DamageType, HitZoneIdx) : 0.f); } // SUPPORT: simulated function bool CanRepairDoors() { return (Ext_PerkSupport(CurrentPerk)!=None ? Ext_PerkSupport(CurrentPerk).CanRepairDoors() : false); } simulated function float GetPenetrationModifier(byte Level, class<KFDamageType> DamageType, optional bool bForce ) { return (Ext_PerkSupport(CurrentPerk)!=None ? Ext_PerkSupport(CurrentPerk).GetPenetrationModifier(Level, DamageType, bForce) : 0.f); } // Other function ApplySkillsToPawn() { if (CheckOwnerPawn()) { OwnerPawn.UpdateGroundSpeed(); OwnerPawn.bMovesFastInZedTime = false; if (MyPRI == none) MyPRI = KFPlayerReplicationInfo(OwnerPawn.PlayerReplicationInfo); ApplyWeightLimits(); } } defaultproperties { bTickIsDisabled=false NetPriority=3.5 }