1549 lines
45 KiB
Ucode
1549 lines
45 KiB
Ucode
// Base perk.
|
|
Class Ext_PerkBase extends Info
|
|
NotPlaceable
|
|
Abstract
|
|
Config(ServerExt)
|
|
DependsOn(ExtWebAdmin_UI);
|
|
|
|
var array<FWebAdminConfigInfo> WebConfigs;
|
|
|
|
var ExtPerkManager PerkManager;
|
|
var Controller PlayerOwner;
|
|
|
|
var() localized string PerkName;
|
|
var() Texture2D PerkIcon;
|
|
var() class<KFPerk> BasePerk; // KF perk that this perk is based on.
|
|
var() class<KFWeapon> PrimaryMelee,PrimaryWeapon;
|
|
var() class<KFWeaponDefinition> PrimaryWeaponDef,SecondaryWeaponDef,KnifeWeaponDef,GrenadeWeaponDef;
|
|
var() class<KFProj_Grenade> GrenadeClass,PerkGrenade,SuperGrenade;
|
|
var() int HealExpUpNum,WeldExpUpNum; // Efficiency of healing and welding XP up.
|
|
|
|
// For trader.
|
|
var() array<class<KFWeaponDefinition> > AutoBuyLoadOutPath;
|
|
|
|
// Config init stuff.
|
|
var config int ConfigVersion;
|
|
var const int CurrentConfigVer;
|
|
|
|
// Variables.
|
|
var config int FirstLevelExp, // How much EXP needed for first level.
|
|
LevelUpExpCost, // How much EXP needed for every level up.
|
|
LevelUpIncCost, // How much EXP increase needed for each level up.
|
|
MinimumLevel,
|
|
MaximumLevel,
|
|
StarPointsPerLevel,
|
|
MinLevelForPrestige, // Minimum level required for perk prestige.
|
|
PrestigeSPIncrease, // Starpoint increase per prestige levelup.
|
|
MaxPrestige,
|
|
MinimalDataLevel; // Maximum prestige level.
|
|
var config float PrestigeXPReduce; // Amount of XP cost is reduced for each prestige.
|
|
var config array<string> TraitClasses;
|
|
|
|
var array<float> Modifiers;
|
|
|
|
var int CurrentLevel, // Current level player is on.
|
|
CurrentEXP, // Current amount of EXP user has.
|
|
NextLevelEXP, // Experience needed for next level.
|
|
CurrentSP, // Current amount of star points.
|
|
LastLevelEXP, // Number of XP was needed for last level.
|
|
CurrentPrestige; // Current prestige level.
|
|
|
|
struct FPerkStat
|
|
{
|
|
var config int MaxValue,CostPerValue;
|
|
var config float Progress;
|
|
var config name StatType;
|
|
var transient int CurrentValue,OldValue;
|
|
var transient float DisplayValue;
|
|
var transient string UIName;
|
|
};
|
|
var config array<FPerkStat> PerkStats;
|
|
|
|
struct FDefPerkStat
|
|
{
|
|
var int MaxValue,CostPerValue;
|
|
var float Progress;
|
|
var name StatType;
|
|
var bool bHiddenConfig; // Hide this config by default.
|
|
};
|
|
var() array<FDefPerkStat> DefPerkStats;
|
|
var() array< class<Ext_TraitBase> > DefTraitList;
|
|
|
|
struct FPlayerTrait
|
|
{
|
|
var class<Ext_TraitBase> TraitType;
|
|
var byte CurrentLevel;
|
|
var Ext_TraitDataStore Data;
|
|
};
|
|
var array<FPlayerTrait> PerkTraits;
|
|
|
|
// Server -> Client replication variables
|
|
var byte RepState;
|
|
var int RepIndex;
|
|
var transient float NextAuthTime;
|
|
|
|
var int ToxicDartDamage;
|
|
var byte EnemyHealthRange;
|
|
var() array<float> EnemyDistDraw;
|
|
|
|
var bool bOwnerNetClient,bClientAuthorized,bPerkNetReady,bHasNightVision,bCanBeGrabbed,bExplosiveWeld,bExplodeOnContact,bNapalmFire,bFireExplode,bToxicDart,bTacticalReload,bHeavyArmor,bHasSWATEnforcer;
|
|
|
|
var localized string StatSpeed;
|
|
var localized string StatDamage;
|
|
var localized string StatRecoil;
|
|
var localized string StatSpread;
|
|
var localized string StatRate;
|
|
var localized string StatReload;
|
|
var localized string StatHealth;
|
|
var localized string StatKnockDown;
|
|
var localized string StatWelder;
|
|
var localized string StatHeal;
|
|
var localized string StatMag;
|
|
var localized string StatSpare;
|
|
var localized string StatOffDamage;
|
|
var localized string StatSelfDamage;
|
|
var localized string StatArmor;
|
|
var localized string StatPoisonDmg;
|
|
var localized string StatSonicDmg;
|
|
var localized string StatFireDmg;
|
|
var localized string StatAllDmg;
|
|
var localized string StatHeadDamage;
|
|
var localized string StatHealRecharge;
|
|
|
|
reliable client simulated function string UIName(FDefPerkStat DefPerkStat)
|
|
{
|
|
switch( DefPerkStat.StatType )
|
|
{
|
|
case name("Speed"): return StatSpeed;
|
|
case name("Damage"): return StatDamage;
|
|
case name("Recoil"): return StatRecoil;
|
|
case name("Spread"): return StatSpread;
|
|
case name("Rate"): return StatRate;
|
|
case name("Reload"): return StatReload;
|
|
case name("Health"): return StatHealth;
|
|
case name("KnockDown"): return StatKnockDown;
|
|
case name("Welder"): return StatWelder;
|
|
case name("Heal"): return StatHeal;
|
|
case name("Mag"): return StatMag;
|
|
case name("Spare"): return StatSpare;
|
|
case name("OffDamage"): return StatOffDamage;
|
|
case name("SelfDamage"): return StatSelfDamage;
|
|
case name("Armor"): return StatArmor;
|
|
case name("PoisonDmg"): return StatPoisonDmg;
|
|
case name("SonicDmg"): return StatSonicDmg;
|
|
case name("FireDmg"): return StatFireDmg;
|
|
case name("AllDmg"): return StatAllDmg;
|
|
case name("HeadDamage"): return StatHeadDamage;
|
|
case name("HealRecharge"): return StatHealRecharge;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
replication
|
|
{
|
|
// Things the server should send to the client.
|
|
if ( true )
|
|
CurrentLevel,CurrentPrestige,CurrentEXP,NextLevelEXP,CurrentSP,LastLevelEXP,bHasNightVision,MinLevelForPrestige,PrestigeSPIncrease,MaxPrestige,bTacticalReload,EnemyHealthRange;
|
|
}
|
|
|
|
simulated final function bool IsWeaponOnPerk( KFWeapon W )
|
|
{
|
|
if( class<KFPerk_Survivalist>(BasePerk) != None )
|
|
return true;
|
|
|
|
//if( W.AllowedForAllPerks() )
|
|
// return true;
|
|
|
|
return W!=None && W.GetWeaponPerkClass(BasePerk)==BasePerk;
|
|
}
|
|
|
|
simulated static function string GetPerkIconPath( int Level )
|
|
{
|
|
return "img://"$PathName(default.PerkIcon);
|
|
}
|
|
|
|
simulated function PostBeginPlay()
|
|
{
|
|
local int i,j;
|
|
local class<Ext_TraitBase> T;
|
|
|
|
if( WorldInfo.NetMode==NM_Client )
|
|
{
|
|
PerkStats.Length = 0; // Prevent client desync with client settings.
|
|
PlayerOwner = GetALocalPlayerController();
|
|
SetTimer(0.01,false,'InitPerk');
|
|
}
|
|
else
|
|
{
|
|
RemoteRole = ROLE_None; // Make sure these actors get replicated in order to client.
|
|
PlayerOwner = Controller(Owner);
|
|
if( PlayerOwner==None )
|
|
{
|
|
`Log(Self@"spawned without owner.");
|
|
Destroy();
|
|
return;
|
|
}
|
|
bOwnerNetClient = (PlayerController(Owner)!=None && LocalPlayer(PlayerController(Owner).Player)==None);
|
|
|
|
// Load trait classes.
|
|
j = 0;
|
|
for( i=0; i<TraitClasses.Length; ++i )
|
|
{
|
|
T = class<Ext_TraitBase>(DynamicLoadObject(TraitClasses[i],Class'Class'));
|
|
if( T==None || !T.Static.IsEnabled(Self) )
|
|
continue;
|
|
PerkTraits.Length = j+1;
|
|
PerkTraits[j].TraitType = T;
|
|
++j;
|
|
}
|
|
|
|
// Setup serverside stat info (for XML log files).
|
|
for( j=0; j<PerkStats.Length; ++j )
|
|
{
|
|
i = DefPerkStats.Find('StatType',PerkStats[j].StatType);
|
|
if( i>=0 )
|
|
PerkStats[j].UIName = UIName(DefPerkStats[i]);
|
|
else
|
|
{
|
|
// Fallback to parent perk for trying to find name.
|
|
i = Class'Ext_PerkBase'.Default.DefPerkStats.Find('StatType',PerkStats[j].StatType);
|
|
if( i>=0 )
|
|
PerkStats[j].UIName = UIName(Class'Ext_PerkBase'.Default.DefPerkStats[i]);
|
|
else PerkStats[j].UIName = string(PerkStats[j].StatType); // Fallback to stat name then...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
simulated function InitPerk()
|
|
{
|
|
if( PlayerOwner==None )
|
|
PlayerOwner = GetALocalPlayerController();
|
|
if( PerkManager==None )
|
|
{
|
|
foreach DynamicActors(class'ExtPerkManager',PerkManager)
|
|
{
|
|
PerkManager.RegisterPerk(Self);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
simulated function Destroyed()
|
|
{
|
|
local int i;
|
|
|
|
if( PerkManager!=None )
|
|
PerkManager.UnregisterPerk(Self);
|
|
if( WorldInfo.NetMode!=NM_Client )
|
|
{
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
PerkTraits[i].TraitType.Static.CleanupTrait(ExtPlayerController(Owner),Self,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
|
|
// For HUD UI
|
|
simulated final function string GetLevelString()
|
|
{
|
|
return (CurrentPrestige>0 ? (string(CurrentPrestige)$"-"$string(CurrentLevel)) : string(CurrentLevel));
|
|
}
|
|
|
|
// For progress bar on HUD
|
|
simulated final function float GetProgressPercent()
|
|
{
|
|
return FClamp(float(CurrentEXP-LastLevelEXP) / FMax(float(NextLevelEXP-LastLevelEXP),1.f),0.f,1.f);
|
|
}
|
|
|
|
// Whetever if user can use prestige now.
|
|
simulated final function bool CanPrestige()
|
|
{
|
|
return (MinLevelForPrestige>=0 && CurrentPrestige<MaxPrestige) ? CurrentLevel>=MinLevelForPrestige : false;
|
|
}
|
|
|
|
// Whetever to save this perk status or not
|
|
final function bool HasAnyProgress()
|
|
{
|
|
return (CurrentEXP>0 || CurrentPrestige>0);
|
|
}
|
|
|
|
reliable client simulated function ClientReceiveStat( int Index, int MaxValue, int CostPerValue, name Type, int CurValue, float Progress )
|
|
{
|
|
local int i;
|
|
|
|
if( WorldInfo.NetMode==NM_Client )
|
|
{
|
|
if( PerkStats.Length<=Index )
|
|
PerkStats.Length = Index+1;
|
|
PerkStats[Index].MaxValue = MaxValue;
|
|
PerkStats[Index].CostPerValue = CostPerValue;
|
|
PerkStats[Index].StatType = Type;
|
|
PerkStats[Index].CurrentValue = CurValue;
|
|
PerkStats[Index].DisplayValue = 0.f;
|
|
PerkStats[Index].Progress = Progress;
|
|
}
|
|
i = DefPerkStats.Find('StatType',Type);
|
|
if( i>=0 )
|
|
PerkStats[Index].UIName = UIName(DefPerkStats[i]);
|
|
else
|
|
{
|
|
// Fallback to parent perk for trying to find name.
|
|
i = Class'Ext_PerkBase'.Default.DefPerkStats.Find('StatType',Type);
|
|
if( i>=0 )
|
|
PerkStats[Index].UIName = UIName(Class'Ext_PerkBase'.Default.DefPerkStats[i]);
|
|
else PerkStats[Index].UIName = string(Type); // Fallback to stat name then...
|
|
}
|
|
}
|
|
reliable client simulated function ClientSetStatValue( int Index, int NewValue )
|
|
{
|
|
if( PerkStats.Length<=Index )
|
|
PerkStats.Length = Index+1;
|
|
PerkStats[Index].CurrentValue = NewValue;
|
|
if( bPerkNetReady )
|
|
ApplyEffects();
|
|
}
|
|
reliable client simulated function ClientReceiveTrait( int Index, class<Ext_TraitBase> TC, byte Lvl )
|
|
{
|
|
if( PerkTraits.Length<=Index )
|
|
PerkTraits.Length = Index+1;
|
|
PerkTraits[Index].TraitType = TC;
|
|
PerkTraits[Index].CurrentLevel = Lvl;
|
|
}
|
|
reliable client simulated function ClientReceiveTraitData( int Index, string Data )
|
|
{
|
|
if( WorldInfo.NetMode==NM_Client )
|
|
PerkTraits[Index].TraitType.Static.ClientSetRepData(Data);
|
|
}
|
|
reliable client simulated function ClientReceiveTraitLvl( int Index, byte NewLevel )
|
|
{
|
|
PerkTraits[Index].CurrentLevel = NewLevel;
|
|
}
|
|
|
|
final function SetPerkStat( name Type, int Value )
|
|
{
|
|
local int i;
|
|
|
|
i = PerkStats.Find('StatType',Type);
|
|
if( i>=0 )
|
|
PerkStats[i].CurrentValue = Value;
|
|
}
|
|
final function int GetPerkStat( name Type )
|
|
{
|
|
local int i;
|
|
|
|
i = PerkStats.Find('StatType',Type);
|
|
if( i==-1 )
|
|
return 0;
|
|
return PerkStats[i].CurrentValue;
|
|
}
|
|
|
|
function bool EarnedEXP( int EXP )
|
|
{
|
|
local int n;
|
|
|
|
bForceNetUpdate = true;
|
|
CurrentEXP+=EXP;
|
|
while( CurrentEXP>=NextLevelEXP && CurrentLevel<MaximumLevel && n<20 )
|
|
{
|
|
++n;
|
|
LastLevelEXP = NextLevelEXP;
|
|
NextLevelEXP = GetNeededExp(++CurrentLevel);
|
|
}
|
|
if( n>0 )
|
|
{
|
|
CurrentSP+=(n*(StarPointsPerLevel+CurrentPrestige*PrestigeSPIncrease));
|
|
if( PerkManager.PRIOwner!=None && PerkManager.CurrentPerk==Self )
|
|
UpdatePRILevel();
|
|
// TODO - broadcast level up messages.
|
|
if( ExtPlayerController(PlayerOwner)!=None )
|
|
ExtPlayerController(PlayerOwner).ReceiveLevelUp(Self,CurrentLevel);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
final function UpdatePRILevel()
|
|
{
|
|
PerkManager.PRIOwner.SetLevelProgress(CurrentLevel,CurrentPrestige,MinimumLevel,MaximumLevel);
|
|
}
|
|
|
|
// XML output
|
|
function OutputXML( ExtStatWriter Data )
|
|
{
|
|
local int i;
|
|
|
|
Data.StartIntendent("perk","class",string(Class.Name));
|
|
Data.WriteValue("perkname",PerkName);
|
|
Data.WriteValue("level",string(CurrentLevel));
|
|
Data.WriteValue("prestige",string(CurrentPrestige));
|
|
Data.WriteValue("exp",string(CurrentEXP));
|
|
Data.WriteValue("points",string(CurrentSP));
|
|
Data.WriteValue("exptilnext",string(NextLevelEXP));
|
|
Data.WriteValue("exponprev",string(LastLevelEXP));
|
|
|
|
for( i=0; i<PerkStats.Length; ++i )
|
|
{
|
|
if( PerkStats[i].CurrentValue>0 )
|
|
{
|
|
Data.StartIntendent("stat","type",string(PerkStats[i].StatType));
|
|
Data.WriteValue("name",GetStatUIStr(i));
|
|
Data.WriteValue("value",string(PerkStats[i].CurrentValue));
|
|
Data.WriteValue("progress",string(PerkStats[i].DisplayValue));
|
|
Data.EndIntendent();
|
|
}
|
|
}
|
|
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
{
|
|
Data.StartIntendent("trait","class",string(PerkTraits[i].TraitType.Name));
|
|
Data.WriteValue("name",PerkTraits[i].TraitType.Default.TraitName);
|
|
Data.WriteValue("level",string(PerkTraits[i].CurrentLevel));
|
|
Data.EndIntendent();
|
|
}
|
|
}
|
|
|
|
Data.EndIntendent();
|
|
}
|
|
|
|
// Data saving.
|
|
function SaveData( ExtSaveDataBase Data )
|
|
{
|
|
local int i,j;
|
|
|
|
// Write current EXP.
|
|
Data.SaveInt(CurrentEXP,3);
|
|
|
|
// Write current prestige
|
|
Data.SaveInt(CurrentPrestige,3);
|
|
|
|
// Count number of given stats
|
|
j = 0;
|
|
for( i=0; i<PerkStats.Length; ++i )
|
|
if( PerkStats[i].CurrentValue>0 )
|
|
++j;
|
|
|
|
// Then perk stats.
|
|
Data.SaveInt(j);
|
|
for( i=0; i<PerkStats.Length; ++i )
|
|
{
|
|
if( PerkStats[i].CurrentValue>0 )
|
|
{
|
|
Data.SaveStr(string(PerkStats[i].StatType));
|
|
Data.SaveInt(PerkStats[i].CurrentValue,1);
|
|
}
|
|
}
|
|
|
|
// Count bought traits.
|
|
j = 0;
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
++j;
|
|
|
|
// Then traits.
|
|
Data.SaveInt(j);
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
{
|
|
Data.SaveStr(string(PerkTraits[i].TraitType));
|
|
Data.SaveInt(PerkTraits[i].CurrentLevel);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Data loading.
|
|
function LoadData( ExtSaveDataBase Data )
|
|
{
|
|
local int i,j,l,n;
|
|
local string S;
|
|
|
|
CurrentEXP = Data.ReadInt(3);
|
|
// if(MinimalDataLevel > 0)
|
|
// {
|
|
// i = GetNeededExp(MinimalDataLevel-1)
|
|
// if(i > CurrentEXP)
|
|
// CurrentEXP = i
|
|
// }
|
|
|
|
if( Data.GetArVer()>=1 )
|
|
CurrentPrestige = Data.ReadInt(3);
|
|
|
|
l = Data.ReadInt(); // Perk stats length.
|
|
for( i=0; i<l; ++i )
|
|
{
|
|
S = Data.ReadStr();
|
|
n = Data.ReadInt(1);
|
|
for( j=0; j<PerkStats.Length; ++j )
|
|
if( S~=string(PerkStats[j].StatType) )
|
|
{
|
|
PerkStats[j].CurrentValue = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
l = Data.ReadInt(); // Traits stats length.
|
|
for( i=0; i<l; ++i )
|
|
{
|
|
S = Data.ReadStr();
|
|
n = Data.ReadInt();
|
|
for( j=0; j<PerkTraits.Length; ++j )
|
|
if( S~=string(PerkTraits[j].TraitType) )
|
|
{
|
|
PerkTraits[j].CurrentLevel = n;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
final function int CalcLevelForExp( int InExp )
|
|
{
|
|
local int i,a,b;
|
|
|
|
// Fast method to calc level for a player.
|
|
b = MaximumLevel+1;
|
|
a = Min(MinimumLevel,b);
|
|
while( true )
|
|
{
|
|
if( a==b || (a+1)==b )
|
|
{
|
|
if( a<MaximumLevel && InExp>=GetNeededExp(a) )
|
|
++a;
|
|
break;
|
|
}
|
|
i = a+((b-a)>>1);
|
|
if( InExp<GetNeededExp(i) ) // Lower!
|
|
b = i;
|
|
else a = i; // Higher!
|
|
}
|
|
return Clamp(a,MinimumLevel,MaximumLevel);
|
|
}
|
|
|
|
// Initialize perk after stats have been loaded.
|
|
function SetInitialLevel()
|
|
{
|
|
local int i,a,b;
|
|
local byte MT,j;
|
|
|
|
if(MinimalDataLevel > 0)
|
|
{
|
|
i = GetNeededExp(MinimalDataLevel-1);
|
|
if(i > CurrentEXP)
|
|
CurrentEXP = i;
|
|
}
|
|
|
|
// Set to initial level player is on after configures has loaded.
|
|
CurrentLevel = CalcLevelForExp(CurrentEXP);
|
|
CurrentSP = CurrentLevel*(StarPointsPerLevel+CurrentPrestige*PrestigeSPIncrease);
|
|
NextLevelEXP = GetNeededExp(CurrentLevel);
|
|
LastLevelEXP = (CurrentLevel>MinimumLevel ? GetNeededExp(CurrentLevel-1) : 0);
|
|
|
|
// Now verify the points player used on individual stats.
|
|
for( i=0; i<PerkStats.Length; ++i )
|
|
{
|
|
if( PerkStats[i].CurrentValue>0 )
|
|
{
|
|
PerkStats[i].CurrentValue = Min(PerkStats[i].CurrentValue,PerkStats[i].MaxValue);
|
|
a = PerkStats[i].CurrentValue*PerkStats[i].CostPerValue;
|
|
if( CurrentSP>a )
|
|
CurrentSP-=a;
|
|
else if( CurrentSP<=0 ) // No points at all for this.
|
|
PerkStats[i].CurrentValue = 0;
|
|
else // Nope, reduce the stat!
|
|
{
|
|
a = CurrentSP/PerkStats[i].CostPerValue;
|
|
PerkStats[i].CurrentValue = a;
|
|
CurrentSP-=(a*PerkStats[i].CostPerValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then verify trait levels and costs.
|
|
MT = 0;
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
{
|
|
PerkTraits[i].CurrentLevel = Min(PerkTraits[i].CurrentLevel,PerkTraits[i].TraitType.Default.NumLevels);
|
|
|
|
if( PerkTraits[i].TraitType.Default.LoadPriority>0 )
|
|
MT = Max(MT,PerkTraits[i].TraitType.Default.LoadPriority);
|
|
else
|
|
{
|
|
if( !PerkTraits[i].TraitType.Static.MeetsRequirements(PerkTraits[i].CurrentLevel-1,Self) )
|
|
a = 0;
|
|
else
|
|
{
|
|
for( a=0; a<PerkTraits[i].CurrentLevel; ++a )
|
|
{
|
|
b = PerkTraits[i].TraitType.Static.GetTraitCost(a);
|
|
if( b>CurrentSP )
|
|
break;
|
|
CurrentSP-=b;
|
|
}
|
|
}
|
|
PerkTraits[i].CurrentLevel = a;
|
|
if( PerkTraits[i].CurrentLevel>0 && PerkTraits[i].Data==None )
|
|
PerkTraits[i].Data = PerkTraits[i].TraitType.Static.InitializeFor(Self,ExtPlayerController(Owner));
|
|
}
|
|
}
|
|
if( PerkTraits[i].CurrentLevel==0 && PerkTraits[i].Data!=None )
|
|
PerkTraits[i].TraitType.Static.CleanupTrait(ExtPlayerController(Owner),Self,PerkTraits[i].Data);
|
|
}
|
|
|
|
// Delayed loads.
|
|
for( j=1; j<=MT; ++j )
|
|
{
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 && PerkTraits[i].TraitType.Default.LoadPriority==j )
|
|
{
|
|
if( !PerkTraits[i].TraitType.Static.MeetsRequirements(PerkTraits[i].CurrentLevel-1,Self) )
|
|
a = 0;
|
|
else
|
|
{
|
|
for( a=0; a<PerkTraits[i].CurrentLevel; ++a )
|
|
{
|
|
b = PerkTraits[i].TraitType.Static.GetTraitCost(a);
|
|
if( b>CurrentSP )
|
|
break;
|
|
CurrentSP-=b;
|
|
}
|
|
}
|
|
PerkTraits[i].CurrentLevel = a;
|
|
if( PerkTraits[i].CurrentLevel>0 && PerkTraits[i].Data==None )
|
|
PerkTraits[i].Data = PerkTraits[i].TraitType.Static.InitializeFor(Self,ExtPlayerController(Owner));
|
|
}
|
|
if( PerkTraits[i].CurrentLevel==0 && PerkTraits[i].Data!=None )
|
|
PerkTraits[i].TraitType.Static.CleanupTrait(ExtPlayerController(Owner),Self,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
|
|
ApplyEffects();
|
|
if( PerkManager.CurrentPerk==Self )
|
|
ActivateTraits();
|
|
}
|
|
|
|
// Check the needed amount of EXP for a perk.
|
|
function int GetNeededExp( int LevelNum )
|
|
{
|
|
if( LevelNum<MinimumLevel || LevelNum>=MaximumLevel )
|
|
return 0;
|
|
LevelNum-=MinimumLevel;
|
|
LevelNum = (FirstLevelExp+(LevelNum*LevelUpExpCost)+(LevelNum*LevelNum*LevelUpIncCost));
|
|
if( CurrentPrestige>0 && PrestigeXPReduce>0 )
|
|
LevelNum *= (1.f / (1.f + PrestigeXPReduce*CurrentPrestige));
|
|
return LevelNum;
|
|
}
|
|
|
|
// Configure initialization.
|
|
static function CheckConfig()
|
|
{
|
|
local int i;
|
|
local class<Ext_TraitBase> T;
|
|
|
|
if( Default.ConfigVersion!=Default.CurrentConfigVer )
|
|
{
|
|
UpdateConfigs(Default.ConfigVersion);
|
|
Default.ConfigVersion = Default.CurrentConfigVer;
|
|
StaticSaveConfig();
|
|
}
|
|
for( i=0; i<Default.TraitClasses.Length; ++i )
|
|
{
|
|
T = class<Ext_TraitBase>(DynamicLoadObject(Default.TraitClasses[i],Class'Class'));
|
|
if( T!=None )
|
|
T.Static.CheckConfig();
|
|
}
|
|
}
|
|
static function UpdateConfigs( int OldVer )
|
|
{
|
|
local int i,j;
|
|
|
|
if( OldVer==0 )
|
|
{
|
|
Default.FirstLevelExp = 400;
|
|
Default.LevelUpExpCost = 500;
|
|
Default.LevelUpIncCost = 65;
|
|
Default.MinimumLevel = 0;
|
|
Default.MaximumLevel = 150;
|
|
Default.StarPointsPerLevel = 15;
|
|
|
|
// Prestige.
|
|
Default.MinLevelForPrestige = 140;
|
|
Default.PrestigeSPIncrease = 1;
|
|
Default.MaxPrestige = 20;
|
|
Default.MinimalDataLevel = 0;
|
|
Default.PrestigeXPReduce = 0.05;
|
|
|
|
Default.PerkStats.Length = 0;
|
|
AddStatsCfg(0);
|
|
Default.TraitClasses.Length = Default.DefTraitList.Length;
|
|
for( i=0; i<Default.DefTraitList.Length; ++i )
|
|
Default.TraitClasses[i] = PathName(Default.DefTraitList[i]);
|
|
}
|
|
else
|
|
{
|
|
// Add progress.
|
|
if( OldVer==1 )
|
|
{
|
|
for( i=0; i<Default.PerkStats.Length; ++i )
|
|
{
|
|
j = Default.DefPerkStats.Find('StatType',Default.PerkStats[i].StatType);
|
|
if( j>=0 )
|
|
Default.PerkStats[i].Progress = Default.DefPerkStats[j].Progress;
|
|
}
|
|
// Add off-perk damage stat.
|
|
AddStatsCfg(12);
|
|
}
|
|
else if( OldVer<=3 )
|
|
AddStatsCfg(13); // Add self damage.
|
|
else if( OldVer<=4 )
|
|
AddStatsCfg(15); // Add poison damage.
|
|
else if( OldVer<=7 )
|
|
AddStatsCfg(16); // Add sonic/fire damage.
|
|
else if( OldVer<=12 )
|
|
AddStatsCfg(18); // Add all damage.
|
|
else if( OldVer<=13 )
|
|
AddStatsCfg(19); // Add HeadDamage and HealRecharge
|
|
if( OldVer<=5 )
|
|
{
|
|
// Add prestige
|
|
Default.MinLevelForPrestige = 140;
|
|
Default.PrestigeSPIncrease = 1;
|
|
Default.MaxPrestige = 20;
|
|
Default.PrestigeXPReduce = 0.05;
|
|
}
|
|
|
|
Default.TraitClasses.Length = Default.DefTraitList.Length;
|
|
for( i=0; i<Default.DefTraitList.Length; ++i )
|
|
Default.TraitClasses[i] = PathName(Default.DefTraitList[i]);
|
|
}
|
|
}
|
|
static final function AddStatsCfg( int StartRange )
|
|
{
|
|
local int i,j;
|
|
|
|
j = Default.PerkStats.Length;
|
|
for( i=StartRange; i<Default.DefPerkStats.Length; ++i )
|
|
{
|
|
if( Default.DefPerkStats[i].bHiddenConfig || Default.PerkStats.Find('StatType',Default.DefPerkStats[i].StatType)>=0 ) // Don't add if already found for some reason.
|
|
continue;
|
|
Default.PerkStats.Length = j+1;
|
|
Default.PerkStats[j].MaxValue = Default.DefPerkStats[i].MaxValue;
|
|
Default.PerkStats[j].CostPerValue = Default.DefPerkStats[i].CostPerValue;
|
|
Default.PerkStats[j].StatType = Default.DefPerkStats[i].StatType;
|
|
Default.PerkStats[j].Progress = Default.DefPerkStats[i].Progress;
|
|
++j;
|
|
}
|
|
}
|
|
|
|
// WebAdmin UI stuff.
|
|
static function InitWebAdmin( ExtWebAdmin_UI UI )
|
|
{
|
|
local class<Ext_TraitBase> T;
|
|
local int i;
|
|
|
|
UI.AddSettingsPage("Perk "$Default.PerkName,Default.Class,Default.WebConfigs,GetValue,ApplyValue);
|
|
|
|
for( i=0; i<Default.TraitClasses.Length; ++i )
|
|
{
|
|
T = class<Ext_TraitBase>(DynamicLoadObject(Default.TraitClasses[i],Class'Class'));
|
|
if( T==None || UI.HasConfigFor(T) )
|
|
continue;
|
|
T.Static.InitWebAdmin(UI);
|
|
}
|
|
}
|
|
static function string GetValue( name PropName, int ElementIndex )
|
|
{
|
|
switch( PropName )
|
|
{
|
|
case 'FirstLevelExp':
|
|
return string(Default.FirstLevelExp);
|
|
case 'LevelUpExpCost':
|
|
return string(Default.LevelUpExpCost);
|
|
case 'LevelUpIncCost':
|
|
return string(Default.LevelUpIncCost);
|
|
case 'MinimumLevel':
|
|
return string(Default.MinimumLevel);
|
|
case 'MaximumLevel':
|
|
return string(Default.MaximumLevel);
|
|
case 'StarPointsPerLevel':
|
|
return string(Default.StarPointsPerLevel);
|
|
case 'TraitClasses':
|
|
return ElementIndex==-1 ? string(Default.TraitClasses.Length) : Default.TraitClasses[ElementIndex];
|
|
case 'PerkStats':
|
|
return ElementIndex==-1 ? string(Default.PerkStats.Length) : Default.PerkStats[ElementIndex].StatType$","$Default.PerkStats[ElementIndex].MaxValue$","$Default.PerkStats[ElementIndex].CostPerValue$","$Default.PerkStats[ElementIndex].Progress;
|
|
case 'MinLevelForPrestige':
|
|
return string(Default.MinLevelForPrestige);
|
|
case 'PrestigeSPIncrease':
|
|
return string(Default.PrestigeSPIncrease);
|
|
case 'MinimalDataLevel':
|
|
return string(Default.MinimalDataLevel);
|
|
case 'MaxPrestige':
|
|
return string(Default.MaxPrestige);
|
|
case 'PrestigeXPReduce':
|
|
return string(Default.PrestigeXPReduce);
|
|
}
|
|
}
|
|
static function ApplyValue( name PropName, int ElementIndex, string Value )
|
|
{
|
|
switch( PropName )
|
|
{
|
|
case 'FirstLevelExp':
|
|
Default.FirstLevelExp = int(Value); break;
|
|
case 'LevelUpExpCost':
|
|
Default.LevelUpExpCost = int(Value); break;
|
|
case 'MinimalDataLevel':
|
|
Default.MinimalDataLevel = int(Value); break;
|
|
case 'LevelUpIncCost':
|
|
Default.LevelUpIncCost = int(Value); break;
|
|
case 'MinimumLevel':
|
|
Default.MinimumLevel = int(Value); break;
|
|
case 'MaximumLevel':
|
|
Default.MaximumLevel = int(Value); break;
|
|
case 'StarPointsPerLevel':
|
|
Default.StarPointsPerLevel = int(Value); break;
|
|
case 'TraitClasses':
|
|
if( Value=="#DELETE" )
|
|
Default.TraitClasses.Remove(ElementIndex,1);
|
|
else
|
|
{
|
|
if( ElementIndex>=Default.TraitClasses.Length )
|
|
Default.TraitClasses.Length = ElementIndex+1;
|
|
Default.TraitClasses[ElementIndex] = Value;
|
|
}
|
|
break;
|
|
case 'PerkStats':
|
|
if( Value=="#DELETE" )
|
|
Default.PerkStats.Remove(ElementIndex,1);
|
|
else
|
|
{
|
|
if( ElementIndex>=Default.PerkStats.Length )
|
|
Default.PerkStats.Length = ElementIndex+1;
|
|
Default.PerkStats[ElementIndex] = ParsePerkStatStr(Value);
|
|
}
|
|
break;
|
|
case 'MinLevelForPrestige':
|
|
Default.MinLevelForPrestige = int(Value); break;
|
|
case 'PrestigeSPIncrease':
|
|
Default.PrestigeSPIncrease = int(Value); break;
|
|
case 'MaxPrestige':
|
|
Default.MaxPrestige = int(Value); break;
|
|
case 'PrestigeXPReduce':
|
|
Default.PrestigeXPReduce = float(Value); break;
|
|
default:
|
|
return;
|
|
}
|
|
StaticSaveConfig();
|
|
}
|
|
static final function FPerkStat ParsePerkStatStr( string S )
|
|
{
|
|
local FPerkStat Res;
|
|
local int i;
|
|
|
|
i = InStr(S,",");
|
|
if( i==-1 )
|
|
return Res;
|
|
Res.StatType = name(Left(S,i));
|
|
S = Mid(S,i+1);
|
|
i = InStr(S,",");
|
|
if( i==-1 )
|
|
return Res;
|
|
Res.MaxValue = int(Left(S,i));
|
|
S = Mid(S,i+1);
|
|
i = InStr(S,",");
|
|
if( i==-1 )
|
|
return Res;
|
|
Res.CostPerValue = int(Left(S,i));
|
|
Res.Progress = float(Mid(S,i+1));
|
|
return Res;
|
|
}
|
|
|
|
// Amount and iStat values are verified already by ServerExtMut.
|
|
function bool IncrementStat( int iStat, int Amount )
|
|
{
|
|
PerkStats[iStat].CurrentValue+=Amount;
|
|
if( bOwnerNetClient )
|
|
ClientSetStatValue(iStat,PerkStats[iStat].CurrentValue);
|
|
PerkManager.bStatsDirty = true;
|
|
ApplyEffects();
|
|
bForceNetUpdate = true;
|
|
return true;
|
|
}
|
|
|
|
simulated function ApplyEffects()
|
|
{
|
|
local int i;
|
|
|
|
for( i=0; i<PerkStats.Length; ++i )
|
|
{
|
|
if( PerkStats[i].CurrentValue!=PerkStats[i].OldValue )
|
|
{
|
|
PerkStats[i].OldValue = PerkStats[i].CurrentValue;
|
|
PerkStats[i].DisplayValue = ApplyEffect(PerkStats[i].StatType,PerkStats[i].CurrentValue,PerkStats[i].Progress/100.f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Notify that player just spawned.
|
|
function ApplyEffectsTo( KFPawn_Human P )
|
|
{
|
|
local int i;
|
|
local bool bSec;
|
|
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
{
|
|
if( PerkTraits[i].TraitType.Default.bPostApplyEffect )
|
|
bSec = true;
|
|
else PerkTraits[i].TraitType.Static.ApplyEffectOn(P,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
if( bSec )
|
|
{
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 && PerkTraits[i].TraitType.Default.bPostApplyEffect )
|
|
PerkTraits[i].TraitType.Static.ApplyEffectOn(P,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Player joined/perk changed.
|
|
function ActivateTraits()
|
|
{
|
|
local int i;
|
|
local KFPawn_Human KFP;
|
|
local bool bSec;
|
|
|
|
KFP = KFPawn_Human(PlayerOwner.Pawn);
|
|
if( KFP!=None && !KFP.IsAliveAndWell() )
|
|
KFP = None;
|
|
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
{
|
|
PerkTraits[i].TraitType.Static.TraitActivate(Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
if( KFP!=None )
|
|
{
|
|
if( PerkTraits[i].TraitType.Default.bPostApplyEffect )
|
|
bSec = true;
|
|
else PerkTraits[i].TraitType.Static.ApplyEffectOn(KFP,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
}
|
|
if( bSec )
|
|
{
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 && PerkTraits[i].TraitType.Default.bPostApplyEffect )
|
|
PerkTraits[i].TraitType.Static.ApplyEffectOn(KFP,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Player disconnected/perk changed.
|
|
function DeactivateTraits()
|
|
{
|
|
local int i;
|
|
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
PerkTraits[i].TraitType.Static.TraitDeActivate(Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
|
|
simulated unreliable client function ClientAuth()
|
|
{
|
|
if( Owner==None )
|
|
SetOwner(PlayerOwner);
|
|
ServerAck();
|
|
}
|
|
unreliable server function ServerAck()
|
|
{
|
|
if( !bClientAuthorized )
|
|
{
|
|
bClientAuthorized = true;
|
|
RepState = 0;
|
|
RepIndex = 0;
|
|
SetTimer(0.01+FRand()*0.025,true,'ReplicateTimer');
|
|
}
|
|
}
|
|
function ReplicateTimer()
|
|
{
|
|
switch( RepState )
|
|
{
|
|
case 0: // Send all perk stats
|
|
if( RepIndex>=PerkStats.Length )
|
|
{
|
|
++RepState;
|
|
RepIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
ClientReceiveStat(RepIndex,PerkStats[RepIndex].MaxValue,PerkStats[RepIndex].CostPerValue,PerkStats[RepIndex].StatType,PerkStats[RepIndex].CurrentValue,PerkStats[RepIndex].Progress);
|
|
++RepIndex;
|
|
}
|
|
break;
|
|
case 1: // Send all traits
|
|
if( RepIndex>=PerkTraits.Length )
|
|
++RepState;
|
|
else
|
|
{
|
|
ClientReceiveTrait(RepIndex,PerkTraits[RepIndex].TraitType,PerkTraits[RepIndex].CurrentLevel);
|
|
ClientReceiveTraitData(RepIndex,PerkTraits[RepIndex].TraitType.Static.GetRepData());
|
|
++RepIndex;
|
|
}
|
|
break;
|
|
default:
|
|
ClearTimer('ReplicateTimer');
|
|
bPerkNetReady = true;
|
|
ClientIsReady(); // Notify client were ready.
|
|
}
|
|
}
|
|
simulated reliable client function ClientIsReady()
|
|
{
|
|
bPerkNetReady = true;
|
|
ApplyEffects();
|
|
}
|
|
simulated function string GetStatUIStr( int iStat )
|
|
{
|
|
local string S;
|
|
local bool bLoop;
|
|
|
|
S = string(Abs(PerkStats[iStat].DisplayValue*100.f));
|
|
bLoop = true;
|
|
|
|
// Chop off float digits that aren't needed.
|
|
while( bLoop )
|
|
{
|
|
switch( Right(S,1) )
|
|
{
|
|
case "0":
|
|
S = Left(S,Len(S)-1);
|
|
break;
|
|
case ".":
|
|
S = Left(S,Len(S)-1);
|
|
bLoop = false;
|
|
break;
|
|
default:
|
|
bLoop = false;
|
|
}
|
|
}
|
|
return Repl(PerkStats[iStat].UIName,"&",S);
|
|
}
|
|
|
|
final function UnloadStats( optional byte Mode )
|
|
{
|
|
local int i,j;
|
|
local KFPawn_Human KFP;
|
|
|
|
PerkManager.bStatsDirty = true;
|
|
if( Mode<=1 )
|
|
{
|
|
// Reset stats.
|
|
for( i=0; i<PerkStats.Length; ++i )
|
|
{
|
|
if( PerkStats[i].CurrentValue>0 )
|
|
{
|
|
CurrentSP+=(PerkStats[i].CurrentValue*PerkStats[i].CostPerValue);
|
|
PerkStats[i].CurrentValue = 0;
|
|
if( bOwnerNetClient )
|
|
ClientSetStatValue(i,0);
|
|
}
|
|
}
|
|
ApplyEffects();
|
|
}
|
|
if( Mode==0 || Mode==2 )
|
|
{
|
|
KFP = KFPawn_Human(PlayerOwner.Pawn);
|
|
if( KFP!=None && !KFP.IsAliveAndWell() )
|
|
KFP = None;
|
|
|
|
// Reset traits.
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
{
|
|
for( j=0; j<PerkTraits[i].CurrentLevel; ++j )
|
|
CurrentSP+=PerkTraits[i].TraitType.Static.GetTraitCost(j);
|
|
if( PerkManager.CurrentPerk==Self )
|
|
{
|
|
PerkTraits[i].TraitType.Static.TraitDeActivate(Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
if( KFP!=None )
|
|
PerkTraits[i].TraitType.Static.CancelEffectOn(KFP,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
PerkTraits[i].TraitType.Static.CleanupTrait(ExtPlayerController(Owner),Self,PerkTraits[i].Data);
|
|
PerkTraits[i].CurrentLevel = 0;
|
|
if( bOwnerNetClient )
|
|
ClientReceiveTraitLvl(i,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function FullReset( optional bool bNotPrestige )
|
|
{
|
|
UnloadStats();
|
|
|
|
// Set minimum values.
|
|
CurrentEXP = 0;
|
|
if( !bNotPrestige )
|
|
CurrentPrestige = 0;
|
|
CurrentLevel = MinimumLevel;
|
|
CurrentSP = CurrentLevel*(StarPointsPerLevel+CurrentPrestige*PrestigeSPIncrease);
|
|
NextLevelEXP = GetNeededExp(CurrentLevel);
|
|
LastLevelEXP = 0;
|
|
|
|
if( PerkManager.CurrentPerk==Self && PerkManager.PRIOwner!=None )
|
|
{
|
|
PerkManager.PRIOwner.SetLevelProgress(CurrentLevel,CurrentPrestige,MinimumLevel,MaximumLevel);
|
|
PerkManager.PRIOwner.bForceNetUpdate = true;
|
|
}
|
|
|
|
bForceNetUpdate = true;
|
|
}
|
|
|
|
function bool PreventDeath( KFPawn_Human Player, Controller Killer, Class<DamageType> DamType )
|
|
{
|
|
local int i;
|
|
|
|
// Doing 2 passes of this so that things don't go out of order (spawn retaliation effect when you get redeemed etc)
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 && PerkTraits[i].TraitType.Default.bHighPriorityDeath && PerkTraits[i].TraitType.Static.PreventDeath(Player,Killer,DamType,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data) )
|
|
return true;
|
|
}
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 && !PerkTraits[i].TraitType.Default.bHighPriorityDeath && PerkTraits[i].TraitType.Static.PreventDeath(Player,Killer,DamType,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
simulated function PlayerDied()
|
|
{
|
|
local int i;
|
|
|
|
if( WorldInfo.NetMode!=NM_Client )
|
|
{
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
PerkTraits[i].TraitType.Static.PlayerDied(Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stat modifier functions.
|
|
simulated function float ApplyEffect( name Type, float Value, float Progress )
|
|
{
|
|
local bool bActivePerk;
|
|
|
|
bActivePerk = (PerkManager!=None && PerkManager.CurrentPerk==Self);
|
|
switch( Type )
|
|
{
|
|
case 'Speed':
|
|
Modifiers[0] = 1.f + (Value*Progress);
|
|
break;
|
|
case 'Damage':
|
|
Modifiers[1] = 1.f + (Value*Progress);
|
|
break;
|
|
case 'Recoil':
|
|
Modifiers[2] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'Spread':
|
|
Modifiers[3] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'Rate':
|
|
Modifiers[4] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'Reload':
|
|
Modifiers[5] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'Health':
|
|
Modifiers[6] = 1.f + (Value*Progress);
|
|
if( bActivePerk && PlayerOwner.Pawn!=None )
|
|
{
|
|
PlayerOwner.Pawn.HealthMax = PlayerOwner.Pawn.Default.Health;
|
|
ModifyHealth(PlayerOwner.Pawn.HealthMax);
|
|
}
|
|
break;
|
|
case 'KnockDown':
|
|
Modifiers[7] = FMin(1.f + (Value*Progress),2.f);
|
|
return (Modifiers[7]-1.f);
|
|
case 'Welder':
|
|
Modifiers[8] = 1.f + (Value*Progress);
|
|
break;
|
|
case 'Heal':
|
|
Modifiers[9] = 1.f + (Value*Progress);
|
|
break;
|
|
case 'Mag':
|
|
Modifiers[10] = 1.f + (Value*Progress);
|
|
if( bActivePerk && WorldInfo.NetMode!=NM_Client && PlayerOwner.Pawn!=None && PlayerOwner.Pawn.InvManager!=None )
|
|
UpdateAmmoStatus(PlayerOwner.Pawn.InvManager);
|
|
break;
|
|
case 'Spare':
|
|
Modifiers[11] = 1.f + (Value*Progress);
|
|
if( bActivePerk && WorldInfo.NetMode!=NM_Client && PlayerOwner.Pawn!=None && PlayerOwner.Pawn.InvManager!=None )
|
|
UpdateAmmoStatus(PlayerOwner.Pawn.InvManager);
|
|
break;
|
|
case 'OffDamage':
|
|
Modifiers[12] = 1.f + (Value*Progress);
|
|
break;
|
|
case 'SelfDamage':
|
|
Modifiers[13] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'Armor':
|
|
Modifiers[14] = (Value*Progress*100.f);
|
|
if( bActivePerk && KFPawn_Human(PlayerOwner.Pawn)!=None )
|
|
{
|
|
KFPawn_Human(PlayerOwner.Pawn).MaxArmor = KFPawn_Human(PlayerOwner.Pawn).Default.MaxArmor;
|
|
ModifyArmor(KFPawn_Human(PlayerOwner.Pawn).MaxArmor);
|
|
}
|
|
return FMin(Value*Progress,1.55);
|
|
case 'PoisonDmg':
|
|
Modifiers[15] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'SonicDmg':
|
|
Modifiers[16] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'FireDmg':
|
|
Modifiers[17] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'AllDmg':
|
|
Modifiers[18] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
case 'HeadDamage':
|
|
Modifiers[19] = Value*Progress;
|
|
break;
|
|
case 'HealRecharge':
|
|
Modifiers[20] = 1.f / (1.f+Value*Progress);
|
|
break;
|
|
}
|
|
return (Value*Progress);
|
|
}
|
|
|
|
simulated function ModifyDamageGiven( out int InDamage, optional Actor DamageCauser, optional KFPawn_Monster MyKFPM, optional KFPlayerController DamageInstigator, optional class<KFDamageType> DamageType, optional int HitZoneIdx )
|
|
{
|
|
if( BasePerk==None || (DamageType!=None && DamageType.Default.ModifierPerkList.Find(BasePerk)>=0) || (KFWeapon(DamageCauser)!=None && IsWeaponOnPerk(KFWeapon(DamageCauser))) )
|
|
{
|
|
if(HitZoneIdx == 0)
|
|
InDamage *= (Modifiers[1] + Modifiers[19]);
|
|
else
|
|
InDamage *= Modifiers[1];
|
|
}
|
|
else if( DamageType==None || DamageType.Name!='KFDT_SuicideExplosive' )
|
|
InDamage *= Modifiers[12];
|
|
}
|
|
simulated function ModifyDamageTaken( out int InDamage, optional class<DamageType> DamageType, optional Controller InstigatedBy )
|
|
{
|
|
if( InDamage>0 )
|
|
{
|
|
if( (InstigatedBy==None || InstigatedBy==PlayerOwner) && class<KFDamageType>(DamageType)!=None )
|
|
InDamage *= Modifiers[13];
|
|
else if( Modifiers[15]<1 && class<KFDT_Toxic>(DamageType)!=None )
|
|
InDamage = Max(InDamage*Modifiers[15],1); // Do at least 1 damage.
|
|
else if( Modifiers[16]<1 && class<KFDT_Sonic>(DamageType)!=None )
|
|
InDamage = Max(InDamage*Modifiers[16],1);
|
|
else if( Modifiers[17]<1 && class<KFDT_Fire>(DamageType)!=None )
|
|
InDamage = Max(InDamage*Modifiers[17],1);
|
|
if( Modifiers[18]<1 && InstigatedBy!=None && InstigatedBy!=PlayerOwner )
|
|
InDamage = Max(InDamage*Modifiers[18],1);
|
|
}
|
|
}
|
|
simulated function ModifyRecoil( out float CurrentRecoilModifier, KFWeapon KFW )
|
|
{
|
|
if( IsWeaponOnPerk(KFW) )
|
|
CurrentRecoilModifier *= Modifiers[2];
|
|
}
|
|
simulated function ModifySpread( out float InSpread )
|
|
{
|
|
InSpread *= Modifiers[3];
|
|
}
|
|
simulated function ModifyRateOfFire( out float InRate, KFWeapon KFW )
|
|
{
|
|
if( IsWeaponOnPerk(KFW) )
|
|
InRate *= Modifiers[4];
|
|
}
|
|
simulated function float GetReloadRateScale(KFWeapon KFW)
|
|
{
|
|
return (IsWeaponOnPerk(KFW) ? Modifiers[5] : 1.f);
|
|
}
|
|
function ModifyHealth( out int InHealth )
|
|
{
|
|
InHealth *= Modifiers[6];
|
|
}
|
|
function ModifyArmor( out byte MaxArmor )
|
|
{
|
|
MaxArmor = Min(MaxArmor+Modifiers[14],255);
|
|
}
|
|
function float GetKnockdownPowerModifier()
|
|
{
|
|
return Modifiers[7];
|
|
}
|
|
function float GetStunPowerModifier( optional class<DamageType> DamageType, optional byte HitZoneIdx )
|
|
{
|
|
return Modifiers[7];
|
|
}
|
|
|
|
simulated function ModifyMeleeAttackSpeed( out float InDuration );
|
|
|
|
function AddDefaultInventory( KFPawn P )
|
|
{
|
|
local int i;
|
|
|
|
if( PrimaryWeapon!=None )
|
|
P.DefaultInventory.AddItem(PrimaryWeapon);
|
|
P.DefaultInventory.AddItem(PrimaryMelee);
|
|
if( KFInventoryManager(P.InvManager)!=None )
|
|
KFInventoryManager(P.InvManager).MaxCarryBlocks = KFInventoryManager(P.InvManager).Default.MaxCarryBlocks+Modifiers[10];
|
|
|
|
for( i=0; i<PerkTraits.Length; ++i )
|
|
{
|
|
if( PerkTraits[i].CurrentLevel>0 )
|
|
PerkTraits[i].TraitType.Static.AddDefaultInventory(P,Self,PerkTraits[i].CurrentLevel,PerkTraits[i].Data);
|
|
}
|
|
}
|
|
|
|
simulated function ModifyWeldingRate( out float FastenRate, out float UnfastenRate )
|
|
{
|
|
FastenRate *= Modifiers[8];
|
|
UnfastenRate *= Modifiers[8];
|
|
}
|
|
|
|
function bool RepairArmor( Pawn HealTarget )
|
|
{
|
|
return false;
|
|
}
|
|
function bool ModifyHealAmount( out float HealAmount )
|
|
{
|
|
HealAmount*=Modifiers[9];
|
|
return false;
|
|
}
|
|
simulated function ModifyMagSizeAndNumber( KFWeapon KFW, out int MagazineCapacity, optional array< Class<KFPerk> > WeaponPerkClass, optional bool bSecondary=false, optional name WeaponClassname )
|
|
{
|
|
if( MagazineCapacity>2 && (KFW==None ? WeaponPerkClass.Find(BasePerk)>=0 : IsWeaponOnPerk(KFW)) ) // Skip boomstick for this.
|
|
MagazineCapacity = Min(MagazineCapacity*Modifiers[10],255);
|
|
}
|
|
simulated function ModifySpareAmmoAmount( KFWeapon KFW, out int PrimarySpareAmmo, optional const out STraderItem TraderItem, optional bool bSecondary )
|
|
{
|
|
if( KFW==None ? TraderItem.AssociatedPerkClasses.Find(BasePerk)>=0 : IsWeaponOnPerk(KFW) )
|
|
PrimarySpareAmmo*=Modifiers[11];
|
|
}
|
|
simulated function bool ShouldMagSizeModifySpareAmmo( KFWeapon KFW, optional Class<KFPerk> WeaponPerkClass )
|
|
{
|
|
return (KFW==None ? WeaponPerkClass==BasePerk : IsWeaponOnPerk(KFW));
|
|
}
|
|
|
|
final function UpdateAmmoStatus( InventoryManager Inv )
|
|
{
|
|
local KFWeapon W;
|
|
|
|
foreach Inv.InventoryActors(class'KFWeapon',W)
|
|
{
|
|
if( IsWeaponOnPerk(W) )
|
|
W.ReInitializeAmmoCounts(PerkManager);
|
|
}
|
|
}
|
|
|
|
simulated function ModifyHealerRechargeTime( out float RechargeRate )
|
|
{
|
|
RechargeRate *= Modifiers[20];
|
|
}
|
|
|
|
simulated function DrawSpecialPerkHUD(Canvas C)
|
|
{
|
|
if( EnemyHealthRange>0 && PlayerOwner!=None && KFPawn_Human(PlayerOwner.Pawn)!=None )
|
|
DrawEnemyHealth(C);
|
|
}
|
|
|
|
simulated final function DrawEnemyHealth( Canvas C )
|
|
{
|
|
local KFPawn_Monster KFPM;
|
|
local vector X,CameraLocation;
|
|
|
|
X = vector(PlayerOwner.Pawn.GetViewRotation());
|
|
CameraLocation = PlayerOwner.Pawn.GetPawnViewLocation();
|
|
|
|
foreach WorldInfo.AllPawns(class'KFPawn_Monster',KFPM,CameraLocation,EnemyDistDraw[EnemyHealthRange-1])
|
|
{
|
|
if( KFPM.IsAliveAndWell() && `TimeSince(KFPM.Mesh.LastRenderTime)<0.1f && KFPM.CanShowHealth() && KFPM.GetTeamNum()!=0 && (X Dot (KFPM.Location - CameraLocation))>0 )
|
|
DrawZedHealthbar(C,KFPM,CameraLocation);
|
|
}
|
|
}
|
|
|
|
simulated final function DrawZedHealthbar(Canvas C, KFPawn_Monster KFPM, vector CameraLocation )
|
|
{
|
|
local vector ScreenPos, TargetLocation;
|
|
local float HealthBarLength, HealthbarHeight, HealthScale;
|
|
|
|
HealthbarLength = FMin(50.f * (float(C.SizeX) / 1024.f), 50.f) * class'KFGFxHudWrapper'.Default.FriendlyHudScale;
|
|
HealthbarHeight = FMin(6.f * (float(C.SizeX) / 1024.f), 6.f) * class'KFGFxHudWrapper'.Default.FriendlyHudScale;
|
|
HealthScale = FClamp(float(KFPM.Health) / float(KFPM.HealthMax),0.f,1.f);
|
|
|
|
if( KFPM.bCrawler && KFPM.Floor.Z <= -0.7f && KFPM.Physics == PHYS_Spider )
|
|
{
|
|
TargetLocation = KFPM.Location + vect(0,0,-1) * KFPM.GetCollisionHeight() * 1.2;
|
|
}
|
|
else
|
|
{
|
|
TargetLocation = KFPM.Location + vect(0,0,1) * KFPM.GetCollisionHeight() * 1.2;
|
|
}
|
|
|
|
ScreenPos = C.Project(TargetLocation);
|
|
if( ScreenPos.X < 0 || ScreenPos.X > C.SizeX || ScreenPos.Y < 0 || ScreenPos.Y > C.SizeY || !FastTrace(TargetLocation, CameraLocation) )
|
|
return;
|
|
|
|
C.EnableStencilTest(true);
|
|
C.SetDrawColor(0, 0, 0, 255);
|
|
C.SetPos(ScreenPos.X - HealthBarLength * 0.5, ScreenPos.Y);
|
|
C.DrawTileStretched(class'KFPerk_Commando'.Default.WhiteMaterial, HealthbarLength, HealthbarHeight, 0, 0, 32, 32);
|
|
|
|
C.SetDrawColor(237, 8, 0, 255);
|
|
C.SetPos(ScreenPos.X - HealthBarLength * 0.5 + 1.0, ScreenPos.Y + 1.0);
|
|
C.DrawTileStretched(class'KFPerk_Commando'.Default.WhiteMaterial, (HealthBarLength - 2.0) * HealthScale, HealthbarHeight - 2.0, 0, 0, 32, 32);
|
|
C.EnableStencilTest(false);
|
|
}
|
|
|
|
function PlayerKilled( KFPawn_Monster Victim, class<DamageType> DamageType );
|
|
|
|
function ModifyBloatBileDoT( out float DoTScaler )
|
|
{
|
|
DoTScaler = Modifiers[15];
|
|
}
|
|
|
|
simulated function bool GetIsUberAmmoActive( KFWeapon KFW )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
function UpdatePerkHeadShots( ImpactInfo Impact, class<DamageType> DamageType, int NumHit );
|
|
|
|
function CheckForAirborneAgent( KFPawn HealTarget, class<DamageType> DamType, int HealAmount );
|
|
|
|
simulated function float GetZedTimeModifier( KFWeapon W )
|
|
{
|
|
return 0.f;
|
|
}
|
|
|
|
simulated function bool GetUsingTactialReload( KFWeapon KFW )
|
|
{
|
|
return (bTacticalReload && IsWeaponOnPerk(KFW));
|
|
}
|
|
|
|
simulated function float GetIronSightSpeedModifier( KFWeapon KFW )
|
|
{
|
|
return 1.f;
|
|
}
|
|
|
|
function OnWaveEnded();
|
|
function NotifyZedTimeStarted();
|
|
|
|
simulated function float GetZedTimeExtensions( byte Level )
|
|
{
|
|
return 1.f;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
CurrentConfigVer=14
|
|
bOnlyRelevantToOwner=true
|
|
bCanBeGrabbed=true
|
|
NetUpdateFrequency=1
|
|
GrenadeClass=class'KFProj_FragGrenade'
|
|
PerkGrenade=class'KFProj_FragGrenade'
|
|
SuperGrenade=class'ExtProj_SUPERGrenade'
|
|
HealExpUpNum=12
|
|
WeldExpUpNum=180
|
|
ToxicDartDamage=15
|
|
NetPriority=4
|
|
|
|
SecondaryWeaponDef=class'KFWeapDef_9mm'
|
|
KnifeWeaponDef=class'KFWeapDef_Knife_Commando'
|
|
GrenadeWeaponDef=class'KFWeapDef_Grenade_Support'
|
|
|
|
DefTraitList.Add(class'Ext_TraitGrenadeUpg')
|
|
DefTraitList.Add(class'Ext_TraitNightvision')
|
|
DefTraitList.Add(class'Ext_TraitAmmoReg')
|
|
DefTraitList.Add(class'Ext_TraitHealthReg')
|
|
DefTraitList.Add(class'Ext_TraitArmorReg')
|
|
DefTraitList.Add(class'Ext_TraitCarryCap')
|
|
DefTraitList.Add(class'Ext_TraitGrenadeCap')
|
|
DefTraitList.Add(class'Ext_TraitMedicPistol')
|
|
DefTraitList.Add(class'Ext_TraitZED_Summon')
|
|
DefTraitList.Add(class'Ext_TraitZED_Health')
|
|
DefTraitList.Add(class'Ext_TraitZED_Damage')
|
|
DefTraitList.Add(class'Ext_TraitZED_SummonExt')
|
|
DefTraitList.Add(class'Ext_TraitGhost')
|
|
DefTraitList.Add(class'Ext_TraitRetali')
|
|
DefTraitList.Add(class'Ext_TraitDuracell')
|
|
DefTraitList.Add(class'Ext_TraitRagdoll')
|
|
DefTraitList.Add(class'Ext_TraitAutoFire')
|
|
DefTraitList.Add(class'Ext_TraitBunnyHop')
|
|
DefTraitList.Add(class'Ext_TraitKnockback')
|
|
|
|
WebConfigs.Add((PropType=0,PropName="FirstLevelExp",UIName="First Level XP",UIDesc="EXP required for the FIRST level"))
|
|
WebConfigs.Add((PropType=0,PropName="LevelUpExpCost",UIName="Level Up XP",UIDesc="EXP cost for every next level (Level * ThisValue"))
|
|
WebConfigs.Add((PropType=0,PropName="LevelUpIncCost",UIName="Level Up Inc XP",UIDesc="Increased EXP cost for every level ((Level^2) * ThisValue)"))
|
|
WebConfigs.Add((PropType=0,PropName="MinimumLevel",UIName="Minimum Level",UIDesc="The minimum level of players"))
|
|
WebConfigs.Add((PropType=0,PropName="MaximumLevel",UIName="Maximum Level",UIDesc="The maximum level of players"))
|
|
WebConfigs.Add((PropType=0,PropName="StarPointsPerLevel",UIName="Star Points Per Lvl",UIDesc="Number of star points players earn per level"))
|
|
WebConfigs.Add((PropType=2,PropName="TraitClasses",UIName="Trait Classes",UIDesc="The class names of traits players can buy",NumElements=-1))
|
|
WebConfigs.Add((PropType=2,PropName="PerkStats",UIName="Perk Stats",UIDesc="List of perk stats (format in: StatName,Max Stat,Cost Per Stat,Progress Per Level)",NumElements=-1))
|
|
WebConfigs.Add((PropType=0,PropName="MinLevelForPrestige",UIName="Min Level For Prestige",UIDesc="Minimum level required to prestige the perk (-1 = disabled)"))
|
|
WebConfigs.Add((PropType=0,PropName="PrestigeSPIncrease",UIName="Prestige SP Increase",UIDesc="Star points increase per level for every prestige"))
|
|
WebConfigs.Add((PropType=0,PropName="MaxPrestige",UIName="Max Prestige",UIDesc="Maximum prestige level"))
|
|
WebConfigs.Add((PropType=0,PropName="PrestigeXPReduce",UIName="Prestige XP Reduce",UIDesc="Percent amount of XP cost is reduced for each prestige (1.0 = 1/2, or 50 % of XP)"))
|
|
// WebConfigs.Add((PropType=0,PropName="MinimalDataLevel",UIName="Minimal Real Level",UIDesc="Minimal level for new players or who loads from saves"))
|
|
|
|
// TODO: localize
|
|
DefPerkStats(0)=(MaxValue=50,CostPerValue=1,StatType="Speed",Progress=0.4)
|
|
DefPerkStats(1)=(MaxValue=1000,CostPerValue=1,StatType="Damage",Progress=0.5)
|
|
DefPerkStats(2)=(MaxValue=90,CostPerValue=1,StatType="Recoil",Progress=1)
|
|
DefPerkStats(3)=(MaxValue=80,CostPerValue=1,StatType="Spread",Progress=0.75)
|
|
DefPerkStats(4)=(MaxValue=1000,CostPerValue=1,StatType="Rate",Progress=0.5)
|
|
DefPerkStats(5)=(MaxValue=1000,CostPerValue=1,StatType="Reload",Progress=0.5)
|
|
DefPerkStats(6)=(MaxValue=150,CostPerValue=1,StatType="Health",Progress=1)
|
|
DefPerkStats(7)=(MaxValue=100,CostPerValue=1,StatType="KnockDown",Progress=1)
|
|
DefPerkStats(8)=(MaxValue=200,CostPerValue=1,StatType="Welder",bHiddenConfig=true,Progress=0.5)
|
|
DefPerkStats(9)=(MaxValue=400,CostPerValue=1,StatType="Heal",bHiddenConfig=true,Progress=0.5)
|
|
DefPerkStats(10)=(MaxValue=400,CostPerValue=1,StatType="Mag",Progress=1)
|
|
DefPerkStats(11)=(MaxValue=500,CostPerValue=1,StatType="Spare",Progress=1)
|
|
DefPerkStats(12)=(MaxValue=1000,CostPerValue=1,StatType="OffDamage",Progress=0.25)
|
|
DefPerkStats(13)=(MaxValue=1000,CostPerValue=1,StatType="SelfDamage",Progress=1,bHiddenConfig=true)
|
|
DefPerkStats(14)=(MaxValue=150,CostPerValue=1,StatType="Armor",Progress=1)
|
|
DefPerkStats(15)=(MaxValue=1000,CostPerValue=1,StatType="PoisonDmg",Progress=1.5,bHiddenConfig=true)
|
|
DefPerkStats(16)=(MaxValue=1000,CostPerValue=1,StatType="SonicDmg",Progress=1.5,bHiddenConfig=true)
|
|
DefPerkStats(17)=(MaxValue=1000,CostPerValue=1,StatType="FireDmg",Progress=1.5,bHiddenConfig=true)
|
|
DefPerkStats(18)=(MaxValue=500,CostPerValue=1,StatType="AllDmg",Progress=0.25)
|
|
DefPerkStats(19)=(MaxValue=500,CostPerValue=1,StatType="HeadDamage",Progress=1,bHiddenConfig=true)
|
|
DefPerkStats(20)=(MaxValue=200,CostPerValue=1,StatType="HealRecharge",Progress=0.5,bHiddenConfig=true)
|
|
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(0.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(1.f)
|
|
Modifiers.Add(0.f)
|
|
Modifiers.Add(1.f)
|
|
|
|
EnemyDistDraw.Add(500)
|
|
EnemyDistDraw.Add(700)
|
|
EnemyDistDraw.Add(1000)
|
|
EnemyDistDraw.Add(1600)
|
|
} |