/* 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 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 P ) { local int i; for( i=0; i HP.HealthMax ) HP.Health = HP.HealthMax; if( HP.Armor > HP.MaxArmor ) HP.Armor = HP.MaxArmor; } } } } simulated final function Ext_PerkBase FindPerk( class P ) { local int i; for( i=0; i=0; --i ) { UserPerks[i].PerkManager = None; UserPerks[i].Destroy(); } } function EarnedEXP( int EXP, optional byte Mode ) { 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=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 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 DamageType, optional int HitZoneIdx ) { if( CurrentPerk!=None ) CurrentPerk.ModifyDamageGiven(InDamage,DamageCauser,MyKFPM,DamageInstigator,DamageType,HitZoneIdx); } simulated function ModifyDamageTaken( out int InDamage, optional class 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, optional byte BodyPart, optional bool bIsSprinting=false ) { return (CurrentPerk!=None ? CurrentPerk.GetKnockdownPowerModifier() : 1.f); } function float GetStumblePowerModifier( optional KFPawn KFP, optional class DamageType, optional out float CooldownModifier, optional byte BodyPart ) { return (CurrentPerk!=None ? CurrentPerk.GetKnockdownPowerModifier() : 1.f); } function float GetStunPowerModifier( optional class 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 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 byte MagazineCapacity, optional array< Class > 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 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 ) { 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, int NumHit ) { if( CurrentPerk!=None ) CurrentPerk.UpdatePerkHeadShots(Impact,DamageType,NumHit); } function CheckForAirborneAgent( KFPawn HealTarget, class 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 KFDT ) { return (CurrentPerk!=None ? (CurrentPerk.bFireExplode && class(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 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, 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 DamageType, optional bool bForce ) { return (Ext_PerkSupport(CurrentPerk)!=None ? Ext_PerkSupport(CurrentPerk).GetPenetrationModifier(Level, DamageType, bForce) : 0.f); } defaultproperties { bTickIsDisabled=false NetPriority=3.5 }