rott/kf_sources/ServerPerks/Classes/ClientPerkRepLink.uc
2026-07-14 20:27:09 +07:00

557 lines
15 KiB
Ucode

Class ClientPerkRepLink extends LinkedReplicationInfo
DependsOn(SRHUDKillingFloor);
var int RDamageHealedStat, RWeldingPointsStat, RShotgunDamageStat, RHeadshotKillsStat, RChainsawKills,
RStalkerKillsStat, RBullpupDamageStat, RMeleeDamageStat, RFlameThrowerDamageStat,RTotalZedTimeStat,
RSelfHealsStat, RSoleSurvivorWavesStat, RCashDonatedStat, RFeedingKillsStat,RHuntingShotgunKills,
RBurningCrossbowKillsStat, RGibbedFleshpoundsStat, RStalkersKilledWithExplosivesStat,
RGibbedEnemiesStat, RBloatKillsStat, RSirenKillsStat, RKillsStat, RMedicKnifeKills, RExplosivesDamageStat,
TotalPlayTime, WinsCount, LostsCount;
var byte MinimumLevel,MaximumLevel;
var float NextRepTime,RequirementScaling;
var int ClientAccknowledged[2],SendIndex,ClientAckSkinNum;
struct FPerksListType
{
var class<SRVeterancyTypes> PerkClass;
var byte CurrentLevel;
};
var array<FPerksListType> CachePerks;
var SRStatsBase StatObject;
struct FShopItemIndex
{
var class<Pickup> PC;
var byte CatNum,bDLCLocked;
};
struct FShopCategoryIndex
{
var string Name;
var byte PerkIndex;
};
var array<FShopItemIndex> ShopInventory;
var array<Material> ShopPerkIcons;
var array<FShopCategoryIndex> ShopCategories;
var array<string> CustomChars;
var array<GUIBuyable> AllocatedObjects;
var array<SRHUDKillingFloor.SmileyMessageType> SmileyTags;
var int CurrentPerkProgress;
var KFPlayerReplicationInfo OwnerPRI;
var SRCustomProgress CustomLink;
var string ServerWebSite,UserID;
var bool bMinimalRequirements,bBWZEDTime,bNoStandardChars,bReceivedURL,bRepCompleted;
replication
{
reliable if( Role==ROLE_Authority && bNetOwner )
RDamageHealedStat, RWeldingPointsStat, RShotgunDamageStat, RHeadshotKillsStat, RChainsawKills,
RStalkerKillsStat, RBullpupDamageStat, RMeleeDamageStat, RFlameThrowerDamageStat,
RSelfHealsStat, RSoleSurvivorWavesStat, RCashDonatedStat, RFeedingKillsStat, RHuntingShotgunKills,
RBurningCrossbowKillsStat, RGibbedFleshpoundsStat, RStalkersKilledWithExplosivesStat, RExplosivesDamageStat,
RGibbedEnemiesStat, RBloatKillsStat, RTotalZedTimeStat, RSirenKillsStat, RKillsStat, RMedicKnifeKills,
TotalPlayTime, WinsCount, LostsCount, bBWZEDTime, bNoStandardChars,
MinimumLevel, RequirementScaling, MaximumLevel, bMinimalRequirements,CustomLink;
// Functions server can call.
reliable if( Role == ROLE_Authority )
ClientReceivePerk,ClientPerkLevel,ClientReceiveWeapon,ClientSendAcknowledge,ClientReceiveCategory,
ClientReceiveChar,ClientReceiveTag,ClientAllReceived,ClientReceiveURL;
reliable if( Role < ROLE_Authority )
ServerSelectPerk,ServerRequestPerks,ServerAcnowledge,ServerSetCharacter,ServerAckSkin;
}
function Destroyed()
{
local SRCustomProgress S,NS;
for( S=CustomLink; S!=None; S=NS )
{
NS = S.NextLink;
S.Destroy();
}
Super.Destroyed();
}
simulated final function string GetCustomValue( class<SRCustomProgress> C )
{
local SRCustomProgress S;
for( S=CustomLink; S!=None; S=S.NextLink )
if( S.Class.Name==C.Name )
return S.GetProgress();
return "";
}
simulated final function int GetCustomValueInt( class<SRCustomProgress> C )
{
local SRCustomProgress S;
for( S=CustomLink; S!=None; S=S.NextLink )
if( S.Class.Name==C.Name )
return S.GetProgressInt();
return 0;
}
final function SRCustomProgress AddCustomValue( class<SRCustomProgress> C )
{
local SRCustomProgress S,Last;
for( S=CustomLink; S!=None; S=S.NextLink )
{
Last = S;
if( S.Class.Name==C.Name )
return S;
}
S = Spawn(C,Owner);
S.RepLink = Self;
// Add new one in the end of the chain.
if( Last!=None )
Last.NextLink = S;
else CustomLink = S;
return S;
}
final function ProgressCustomValue( class<SRCustomProgress> C, int Count )
{
local SRCustomProgress S;
for( S=CustomLink; S!=None; S=S.NextLink )
{
if( S.Class.Name==C.Name )
{
S.IncrementProgress(Count);
break;
}
}
}
final function SpawnCustomLinks()
{
local int i;
for( i=0; i<CachePerks.Length; ++i )
CachePerks[i].PerkClass.Static.AddCustomStats(Self);
}
simulated static final function ClientPerkRepLink FindStats( PlayerController Other )
{
local LinkedReplicationInfo L;
local ClientPerkRepLink C;
if( Other.PlayerReplicationInfo==None )
{
foreach Other.DynamicActors(Class'ClientPerkRepLink',C)
if( C.Owner==Other )
{
C.RepLinkBroken();
return C;
}
return None; // Not yet init.
}
for( L=Other.PlayerReplicationInfo.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo )
if( ClientPerkRepLink(L)!=None )
return ClientPerkRepLink(L);
if( Other.Level.NetMode!=NM_Client )
return None; // Not yet init.
foreach Other.DynamicActors(Class'ClientPerkRepLink',C)
if( C.Owner==Other )
{
C.RepLinkBroken();
return C;
}
return None;
}
simulated function Tick( float DeltaTime )
{
local PlayerController PC;
local LinkedReplicationInfo L;
if( Level.NetMode==NM_DedicatedServer )
{
Disable('Tick');
return;
}
PC = Level.GetLocalPlayerController();
if( Level.NetMode!=NM_Client && PC!=Owner )
{
Disable('Tick');
return;
}
if( PC.PlayerReplicationInfo==None )
return;
Disable('Tick');
Class'SRLevelCleanup'.Static.AddSafeCleanup(PC);
if( PC.PlayerReplicationInfo.CustomReplicationInfo!=None )
{
for( L=PC.PlayerReplicationInfo.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo )
if( L==Self )
return; // Make sure not already added.
NextReplicationInfo = None;
for( L=PC.PlayerReplicationInfo.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo )
if( L.NextReplicationInfo==None )
{
L.NextReplicationInfo = Self; // Add to the end of the chain.
return;
}
}
PC.PlayerReplicationInfo.CustomReplicationInfo = Self;
}
simulated final function RepLinkBroken() // Called by GUI when this is noticed.
{
Enable('Tick');
Tick(0);
}
final function Class<SRVeterancyTypes> PickRandomPerk()
{
local array< class<SRVeterancyTypes> > CA;
local int i;
for( i=0; i<CachePerks.Length; i++ )
{
if( CachePerks[i].PerkClass!=None && CachePerks[i].CurrentLevel>0 )
CA[CA.Length] = CachePerks[i].PerkClass;
}
if( CA.Length==0 )
return None;
return CA[Rand(CA.Length)];
}
final function ServerSelectPerk( Class<SRVeterancyTypes> VetType )
{
StatObject.ServerSelectPerk(VetType);
}
final function ServerRequestPerks()
{
if( NextRepTime<Level.TimeSeconds )
SendClientPerks();
}
final function SendClientPerks()
{
local byte i;
if( !StatObject.bStatsReadyNow )
return;
NextRepTime = Level.TimeSeconds+2.f;
for( i=0; i<CachePerks.Length; i++ )
ClientReceivePerk(i,CachePerks[i].PerkClass,CachePerks[i].CurrentLevel);
}
simulated function ClientReceivePerk( int Index, class<SRVeterancyTypes> V, byte Level )
{
// Setup correct icon for trader.
if( V.Default.PerkIndex<255 && V.Default.OnHUDIcon!=None )
{
if( ShopPerkIcons.Length<=V.Default.PerkIndex )
ShopPerkIcons.Length = V.Default.PerkIndex+1;
ShopPerkIcons[V.Default.PerkIndex] = V.Default.OnHUDIcon;
}
if( CachePerks.Length<=Index )
CachePerks.Length = (Index+1);
CachePerks[Index].PerkClass = V;
CachePerks[Index].CurrentLevel = Level;
}
simulated function ClientPerkLevel( int Index, byte CurLevel )
{
Level.GetLocalPlayerController().ReceiveLocalizedMessage(Class'KFVetEarnedMessageSR',(CurLevel-1),,,CachePerks[Index].PerkClass);
CachePerks[Index].CurrentLevel = CurLevel;
}
simulated function ClientReceiveWeapon( int Index, class<Pickup> P, byte Categ )
{
ShopInventory.Length = Max(ShopInventory.Length,Index+1);
if( ShopInventory[Index].PC==None )
{
ShopInventory[Index].PC = P;
ShopInventory[Index].CatNum = Categ;
if( class<KFWeapon>(P.Default.InventoryType)!=none
&& (class<KFWeapon>(P.Default.InventoryType).Default.AppID>0
|| class<KFWeapon>(P.Default.InventoryType).Default.UnlockedByAchievement!=-1) )
ShopInventory[Index].bDLCLocked = 1;
++ClientAccknowledged[0];
}
}
simulated function ClientReceiveCategory( byte Index, FShopCategoryIndex S )
{
ShopCategories.Length = Max(ShopCategories.Length,Index+1);
if( ShopCategories[Index].Name=="" )
{
ShopCategories[Index] = S;
++ClientAccknowledged[1];
}
}
simulated function ClientReceiveURL( string S, string ID )
{
ServerWebSite = S;
UserID = ID;
bReceivedURL = true;
}
simulated function ClientSendAcknowledge()
{
ServerAcnowledge(ClientAccknowledged[0],ClientAccknowledged[1]);
}
function ServerAcnowledge( int A, int B )
{
ClientAccknowledged[0] = A;
ClientAccknowledged[1] = B;
}
simulated function ClientReceiveChar( string CharName, int Num )
{
CustomChars.Length = Num+1;
CustomChars[Num] = CharName;
ServerAckSkin(Num+1);
}
simulated function ClientReceiveTag( Texture T, string Tag, bool bInCaps )
{
local int i;
i = SmileyTags.Length;
SmileyTags.Length = i+1;
SmileyTags[i].SmileyTex = T;
SmileyTags[i].SmileyTag = Tag;
SmileyTags[i].bInCAPS = bInCaps;
}
simulated function ClientAllReceived()
{
local PlayerController PC;
local int i;
bRepCompleted = true;
PC = Level.GetLocalPlayerController();
if( (PC!=None && PC==Owner) || Level.NetMode==NM_Client )
{
// Check if a DLC check is required.
for( i=(ShopInventory.Length-1); i>=0; --i )
if( ShopInventory[i].bDLCLocked!=0 )
{
Spawn(Class'SRSteamStatsGet',Owner).Link = Self;
break;
}
}
if( SmileyTags.Length==0 )
return;
if( PC!=None && SRHUDKillingFloor(PC.MyHUD)!=None )
SRHUDKillingFloor(PC.MyHUD).SmileyMsgs = SmileyTags;
}
function ServerAckSkin( int Index )
{
ClientAckSkinNum = Index;
}
simulated final function string PickRandomCustomChar()
{
local string S;
local int i;
if( CustomChars.Length==0 )
return "";
S = CustomChars[Rand(CustomChars.Length)];
i = InStr(S,":");
if( i>=0 )
S = Mid(S,i+1);
return S;
}
simulated final function bool IsCustomCharacter( string CN )
{
local int i;
for( i=0; i<CustomChars.Length; ++i )
if( CustomChars[i]~=CN || Right(CustomChars[i],Len(CN)+1)~=(":"$CN) )
return true;
return false;
}
simulated final function SelectedCharacter( string CN )
{
if( !IsCustomCharacter(CN) ) // Was not a custom character, update URL too.
{
if( bNoStandardChars && CustomChars.Length>0 ) // Denied.
return;
Level.GetLocalPlayerController().UpdateURL("Character", CN, True);
}
ServerSetCharacter(CN);
}
function ServerSetCharacter( string CN )
{
if( xPlayer(Owner)!=None )
StatObject.ChangeCharacter(CN);
}
final function bool CanBuyPickup( class<KFWeaponPickup> WC )
{
local int i;
local KFPlayerReplicationInfo K;
for( i=(ShopInventory.Length-1); i>=0; --i )
if( ShopInventory[i].PC==WC )
{
K = KFPlayerReplicationInfo(StatObject.PlayerOwner.PlayerReplicationInfo);
for( i=(CachePerks.Length-1); i>=0; --i )
if( !CachePerks[i].PerkClass.Static.AllowWeaponInTrader(WC,K,CachePerks[i].CurrentLevel) )
return false;
return true;
}
return false;
}
Auto state RepSetup
{
final function InitDLCCheck()
{
local int i;
for( i=(ShopInventory.Length-1); i>=0; --i )
{
if( class<KFWeapon>(ShopInventory[i].PC.Default.InventoryType)!=none
&& (class<KFWeapon>(ShopInventory[i].PC.Default.InventoryType).Default.AppID>0
|| class<KFWeapon>(ShopInventory[i].PC.Default.InventoryType).Default.UnlockedByAchievement!=-1) )
ShopInventory[i].bDLCLocked = 1;
}
}
Begin:
if( Level.NetMode==NM_Client )
Stop;
Sleep(1.f);
NetUpdateFrequency = 0.5f;
if( NetConnection(StatObject.PlayerOwner.Player)!=None ) // Network client.
{
ClientReceiveURL(ServerWebSite,StatObject.PlayerOwner.GetPlayerIDHash());
// Now MAKE SURE client receives the full inventory list.
while( ClientAccknowledged[0]<ShopInventory.Length || ClientAccknowledged[1]<ShopCategories.Length )
{
for( SendIndex=0; SendIndex<ShopInventory.Length; ++SendIndex )
{
ClientReceiveWeapon(SendIndex,ShopInventory[SendIndex].PC,ShopInventory[SendIndex].CatNum);
Sleep(0.1f);
}
for( SendIndex=0; SendIndex<ShopCategories.Length; ++SendIndex )
{
ClientReceiveCategory(SendIndex,ShopCategories[SendIndex]);
Sleep(0.1f);
}
ClientSendAcknowledge();
Sleep(1.f);
}
// Send client all the custom characters.
while( ClientAckSkinNum<CustomChars.Length )
{
ClientReceiveChar(CustomChars[ClientAckSkinNum],ClientAckSkinNum);
Sleep(0.15f);
}
// Send all chat icons.
for( SendIndex=0; SendIndex<SmileyTags.Length; ++SendIndex )
{
ClientReceiveTag(SmileyTags[SendIndex].SmileyTex,SmileyTags[SendIndex].SmileyTag,SmileyTags[SendIndex].bInCAPS);
Sleep(0.1f);
}
SmileyTags.Length = 0;
}
else
{
bReceivedURL = true;
InitDLCCheck();
}
ClientAllReceived();
GoToState('UpdatePerkProgress');
}
state UpdatePerkProgress
{
final function UpdateProgression()
{
local class<SRVeterancyTypes> SV;
local byte Lv;
local float V;
SV = Class<SRVeterancyTypes>(OwnerPRI.ClientVeteranSkill);
Lv = OwnerPRI.ClientVeteranSkillLevel+1;
if( Lv<MaximumLevel && SV!=None )
{
V = SV.Static.GetTotalProgress(Self,Lv) * 10000.f;
CurrentPerkProgress = V;
}
else CurrentPerkProgress = -1;
ForceValue();
}
final function ForceValue()
{
if( CurrentPerkProgress!=OwnerPRI.ThreeSecondScore )
{
OwnerPRI.ThreeSecondScore = CurrentPerkProgress;
OwnerPRI.NetUpdateTime = Level.TimeSeconds-1;
}
}
Begin:
Sleep(FRand());
OwnerPRI = KFPlayerReplicationInfo(StatObject.PlayerOwner.PlayerReplicationInfo);
if( OwnerPRI==None )
Stop;
while( true )
{
Sleep(0.5);
UpdateProgression();
Sleep(1);
ForceValue();
Sleep(1);
ForceValue();
Sleep(1);
ForceValue();
}
}
simulated final function ResetItem( GUIBuyable Item )
{
Item.ItemName = "";
Item.ItemDescription = "";
Item.ItemCategorie = "";
Item.ItemImage = None;
Item.ItemWeaponClass = None;
Item.ItemAmmoClass = None;
Item.ItemPickupClass = None;
Item.ItemCost = 0;
Item.ItemAmmoCost = 0;
Item.ItemFillAmmoCost = 0;
Item.ItemWeight = 0;
Item.ItemPower = 0;
Item.ItemRange = 0;
Item.ItemSpeed = 0;
Item.ItemAmmoCurrent = 0;
Item.ItemAmmoMax = 0;
Item.bSaleList = false;
Item.bSellable = false;
Item.bMelee = false;
Item.bIsVest = false;
Item.bIsFirstAidKit = false;
Item.ItemPerkIndex = 0;
Item.ItemSellValue = 0;
}
defaultproperties
{
MaximumLevel=7
RequirementScaling=1.000000
ShopPerkIcons(0)=Texture'KillingFloorHUD.Perks.Perk_Medic'
ShopPerkIcons(1)=Texture'KillingFloorHUD.Perks.Perk_Support'
ShopPerkIcons(2)=Texture'KillingFloorHUD.Perks.Perk_SharpShooter'
ShopPerkIcons(3)=Texture'KillingFloorHUD.Perks.Perk_Commando'
ShopPerkIcons(4)=Texture'KillingFloorHUD.Perks.Perk_Berserker'
ShopPerkIcons(5)=Texture'KillingFloorHUD.Perks.Perk_Firebug'
ShopPerkIcons(6)=Texture'KillingFloor2HUD.Perk_Icons.Perk_Demolition'
ShopPerkIcons(7)=Texture'KillingFloor2HUD.Perk_Icons.No_Perk_Icon'
UserID="Local"
bOnlyRelevantToOwner=True
bAlwaysRelevant=False
}