KF2-Server-Extension/ServerExt/Classes/ExtPerkManager.uc

919 lines
23 KiB
Ucode

/*
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;
// SWAT Enforcer
var array<Actor> CurrentBumpedActors; // The unique list of actors that have been bumped before the last cooldown reset
var float LastBumpTime; // The last time a zed was bumped using battering ram
var float BumpCooldown; // The amount of time between when the last actor was bumped and another actor can be bumped again
var float BumpMomentum; // Amount of momentum when bumping zeds
var int BumpDamageAmount; // Amount of damage Battering Ram bumps deal
var class<DamageType> BumpDamageType; // Damage type used for Battering Ram bump damage
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);
}
simulated function OnBump(Actor BumpedActor, KFPawn_Human BumpInstigator, vector BumpedVelocity, rotator BumpedRotation)
{
local KFPawn_Monster KFPM;
local bool CanBump;
if (ShouldKnockDownOnBump() && Normal(BumpedVelocity) dot Vector(BumpedRotation) > 0.7f)
{
KFPM = KFPawn_Monster(BumpedActor);
if (KFPM != none)
{
// cooldown so that the same zed can't be bumped multiple frames back to back
// especially relevant if they can't be knocked down or stumbled so the player is always bumping them
if (WorldInfo.TimeSeconds - LastBumpTime > BumpCooldown)
{
CurrentBumpedActors.length = 0;
CurrentBumpedActors.AddItem(BumpedActor);
CanBump = true;
}
// if still within the cooldown time, can still bump the actor as long as it hasn't been bumped yet
else if (CurrentBumpedActors.Find(BumpedActor) == INDEX_NONE)
{
CurrentBumpedActors.AddItem(BumpedActor);
CanBump = true;
}
LastBumpTime = WorldInfo.TimeSeconds;
if (CanBump)
{
if (KFPM.IsHeadless())
{
KFPM.TakeDamage(KFPM.HealthMax, BumpInstigator.Controller, BumpInstigator.Location,
Normal(vector(BumpedRotation)) * BumpMomentum, BumpDamageType);
}
else
{
KFPM.TakeDamage(BumpDamageAmount, BumpInstigator.Controller, BumpInstigator.Location,
Normal(vector(BumpedRotation)) * BumpMomentum, BumpDamageType);
KFPM.Knockdown(BumpedVelocity * 3, vect(1, 1, 1), KFPM.Location, 1000, 100);
}
}
}
}
}
// 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
// SWAT bumping
BumpCooldown = 0.1f
BumpMomentum=1.f
BumpDamageAmount=450
BumpDamageType=class'KFDT_SWATBatteringRam'
}