Class ExtPlayerReplicationInfo extends KFPlayerReplicationInfo; struct FCustomCharEntry { var bool bLock; var KFCharacterInfo_Human Char; var ObjectReferencer Ref; }; struct FMyCustomChar // Now without constant. { var int CharacterIndex,HeadMeshIndex,HeadSkinIndex,BodyMeshIndex,BodySkinIndex,AttachmentMeshIndices[`MAX_COSMETIC_ATTACHMENTS],AttachmentSkinIndices[`MAX_COSMETIC_ATTACHMENTS]; structdefaultproperties { AttachmentMeshIndices[0]=`CLEARED_ATTACHMENT_INDEX AttachmentMeshIndices[1]=`CLEARED_ATTACHMENT_INDEX AttachmentMeshIndices[2]=`CLEARED_ATTACHMENT_INDEX } }; var bool bIsMuted,bInitialPT,bIsDev,bHiddenUser,bClientUseCustom,bClientFirstChar,bClientCharListDone,bClientInitChars; enum E_AdminType { AT_Global, AT_Admin, AT_Mod, AT_TMem, AT_VIP, AT_Booster, AT_Player }; var E_AdminType AdminType; var int RespawnCounter; var class ECurrentPerk; var Ext_PerkBase FCurrentPerk; var int ECurrentPerkLevel,ECurrentPerkPrestige; var ExtPerkManager PerkManager; var string TaggedPlayerName; var repnotify string NameTag; var repnotify byte RepLevelProgress; var transient color HUDPerkColor; var byte FixedData; var int RepPlayTime,RepKills,RepEXP; // Custom character stuff. var array CustomCharList; var repnotify FMyCustomChar CustomCharacter; var transient array SaveDataObjects; var transient ExtPlayerReplicationInfo LocalOwnerPRI; // Local playercontroller owner PRI // Supplier data: var transient struct FSupplierData { var transient Pawn SuppliedPawn; var transient float NextSupplyTimer; } SupplierLimit; var repnotify class HasSupplier; replication { // Things the server should send to the client. if (true) RespawnCounter,AdminType,ECurrentPerk,ECurrentPerkLevel,ECurrentPerkPrestige,RepKills,RepEXP,RepLevelProgress,bIsDev,NameTag,FixedData,bHiddenUser,CustomCharacter,HasSupplier; if (bNetInitial || bInitialPT) RepPlayTime; } simulated function PostBeginPlay() { local PlayerController PC; Super.PostBeginPlay(); SetTimer(1,true,'TickPT'); if (WorldInfo.NetMode!=NM_DedicatedServer) { HUDPerkColor = PickPerkColor(); PC = GetALocalPlayerController(); if (PC!=None) LocalOwnerPRI = ExtPlayerReplicationInfo(PC.PlayerReplicationInfo); } else LocalOwnerPRI = Self; // Dedicated server can use self PRI. } // Resupply traits: simulated final function bool CanUseSupply(Pawn P) { return (SupplierLimit.SuppliedPawn!=P || SupplierLimit.NextSupplyTimer0 ? (string(ECurrentPerkPrestige)$"-"$string(ECurrentPerkLevel)) : string(ECurrentPerkLevel)); } simulated final function color PickPerkColor() { local float P; local byte i; if (RepLevelProgress==0) return MakeColor(255,255,255,255); P = float(RepLevelProgress) / 255.f; if (P<0.25f) // White - Blue { i = 255 - (P*1020.f); return MakeColor(i,i,255,255); } if (P<0.5f) // Blue - Green { i = ((P-0.25f)*1020.f); return MakeColor(0,i,255-i,255); } if (P<0.75f) // Green - Red { i = ((P-0.5f)*1020.f); return MakeColor(i,255-i,0,255); } // Red - Yellow i = ((P-0.75f)*1020.f); return MakeColor(255,i,0,255); } function SetInitPlayTime(int T) { bInitialPT = true; bForceNetUpdate = true; RepPlayTime = T; SetTimer(5,false,'UnsetPT'); } function UnsetPT() { bInitialPT = false; } simulated final function bool ShowAdminName() { return (bAdmin || AdminType < AT_Player); } simulated function string GetAdminName() { switch (AdminType) { case AT_Global: return "Super Admin"; case AT_Admin: case AT_Player: // TODO: Admin is the same as player? WTF? #1 return "Admin"; case AT_Mod: return "Mod"; case AT_TMem: return "Trusted Member"; case AT_VIP: return "VIP"; case AT_Booster: return "Booster"; } } simulated function string GetAdminNameAbr() { switch (AdminType) { case AT_Global: return "S"; case AT_Admin: case AT_Player: // TODO: Admin is the same as player? WTF? #2 return "A"; case AT_Mod: return "M"; case AT_TMem: return "T"; case AT_VIP: return "V"; case AT_Booster: return "B"; } } simulated function string GetAdminColor() { switch (AdminType) { case AT_Global: return "FF6600"; case AT_Admin: case AT_Player: // TODO: Admin is the same as player? WTF? #3 return "40FFFF"; case AT_Mod: return "FF33FF"; case AT_TMem: return "FF0000"; case AT_VIP: return "FFD700"; case AT_Booster: return "32A852"; } } simulated function color GetAdminColorC() { switch (AdminType) { case AT_Global: return MakeColor(255,102,0,255); case AT_Admin: case AT_Player: // TODO: Admin is the same as player? WTF? #4 return MakeColor(64,255,255,255); case AT_Mod: return MakeColor(255,51,255,255); case AT_TMem: return MakeColor(255,0,0,255); case AT_VIP: return MakeColor(255,215,0,255); case AT_Booster: return MakeColor(50,168,82,255); } } simulated function string GetHumanReadableName() { return TaggedPlayerName; } function SetFixedData(byte M) { OnModeSet(Self,M); FixedData = FixedData | M; SetTimer(5,false,'ClearFixed'); } function ClearFixed() { FixedData = 0; } simulated final function string GetDesc() { local string S; if ((FixedData & 1)!=0) S = "A."; if ((FixedData & 2)!=0) S $= "WF."; if ((FixedData & 4)!=0) S $= "G."; if ((FixedData & 8)!=0) S $= "NW."; if ((FixedData & 16)!=0) S $= "WA."; return S; } delegate OnModeSet(ExtPlayerReplicationInfo PRI, byte Num); simulated final function bool LoadPlayerCharacter(byte CharIndex, out FMyCustomChar CharInfo) { local KFCharacterInfo_Human C; if (CharIndex>=(CharacterArchetypes.Length+CustomCharList.Length)) return false; if (SaveDataObjects.Length<=CharIndex) SaveDataObjects.Length = CharIndex+1; if (SaveDataObjects[CharIndex]==None) { C = (CharIndex=(CharacterArchetypes.Length+CustomCharList.Length)) return false; if (SaveDataObjects.Length<=CustomCharacter.CharacterIndex) SaveDataObjects.Length = CustomCharacter.CharacterIndex+1; if (SaveDataObjects[CustomCharacter.CharacterIndex]==None) { C = (CustomCharacter.CharacterIndex=(CharacterArchetypes.Length+CustomCharList.Length) || IsClientCharLocked(CharIndex)) CharIndex = 0; if (bFirstSet && RepCustomizationInfo.CharacterIndex==CharIndex) { // Copy properties from default character info. NewChar.HeadMeshIndex = RepCustomizationInfo.HeadMeshIndex; NewChar.HeadSkinIndex = RepCustomizationInfo.HeadSkinIndex; NewChar.BodyMeshIndex = RepCustomizationInfo.BodyMeshIndex; NewChar.BodySkinIndex = RepCustomizationInfo.BodySkinIndex; for (i=0; i<`MAX_COSMETIC_ATTACHMENTS; ++i) { NewChar.AttachmentMeshIndices[i] = RepCustomizationInfo.AttachmentMeshIndices[i]; NewChar.AttachmentSkinIndices[i] = RepCustomizationInfo.AttachmentSkinIndices[i]; } } if (LoadPlayerCharacter(CharIndex,NewChar)) { NewChar.CharacterIndex = CharIndex; CustomCharacter = NewChar; ServerSetCharacterX(NewChar); if (WorldInfo.NetMode==NM_Client) CharacterCustomizationChanged(); } } simulated function UpdateCustomization(int Type, int MeshIndex, int SkinIndex, optional int SlotIndex) { switch (Type) { case CO_Head: CustomCharacter.HeadMeshIndex = MeshIndex; CustomCharacter.HeadSkinIndex = SkinIndex; break; case CO_Body: CustomCharacter.BodyMeshIndex = MeshIndex; CustomCharacter.BodySkinIndex = SkinIndex; break; case CO_Attachment: CustomCharacter.AttachmentMeshIndices[SlotIndex] = MeshIndex; CustomCharacter.AttachmentSkinIndices[SlotIndex] = SkinIndex; break; } SavePlayerCharacter(); ServerSetCharacterX(CustomCharacter); if (WorldInfo.NetMode==NM_Client) CharacterCustomizationChanged(); } simulated final function RemoveAttachments() { local byte i; for (i=0; i<`MAX_COSMETIC_ATTACHMENTS; ++i) { CustomCharacter.AttachmentMeshIndices[i] = `CLEARED_ATTACHMENT_INDEX; CustomCharacter.AttachmentSkinIndices[i] = 0; } SavePlayerCharacter(); ServerSetCharacterX(CustomCharacter); if (WorldInfo.NetMode==NM_Client) CharacterCustomizationChanged(); } simulated function ClearCharacterAttachment(int AttachmentIndex) { if (UsesCustomChar()) { CustomCharacter.AttachmentMeshIndices[AttachmentIndex] = `CLEARED_ATTACHMENT_INDEX; CustomCharacter.AttachmentSkinIndices[AttachmentIndex] = 0; } else Super.ClearCharacterAttachment(AttachmentIndex); } reliable server final function ServerSetCharacterX(FMyCustomChar NewMeshInfo) { if (NewMeshInfo.CharacterIndex>=(CharacterArchetypes.Length+CustomCharList.Length) || IsClientCharLocked(NewMeshInfo.CharacterIndex)) return; CustomCharacter = NewMeshInfo; if (Role == Role_Authority) { CharacterCustomizationChanged(); } } simulated final function bool IsClientCharLocked(byte Index) { if (Index=CharacterArchetypes.Length); } simulated final function KFCharacterInfo_Human GetSelectedArch() { if (UsesCustomChar()) return (CustomCharacter.CharacterIndex=2) S = Data.ReadStr(); if (S=="") // Stock skin. return; for (i=0; i=2) S = Data.ReadStr(); if (S=="") // Stock skin. return; Data.SkipBytes(4); n = Data.ReadInt(); for (i=0; i