// Server extension mutator, by Marco. Class ServerExtMut extends KFMutator config(ServerExtMut); // Webadmin var array<FWebAdminConfigInfo> WebConfigs; struct FInventory { var class<Inventory> ItemClass; var int Values[4]; }; struct CFGCustomZedXP { var string zed; // zed name var float XP1; // normal var float XP2; // hard var float XP3; // suicidal var float XP4; // hoe }; struct FSavedInvEntry { var Controller OwnerPlayer; var byte Gren; var array<FInventory> Inv; }; var array<FSavedInvEntry> PlayerInv; var config array<string> PerkClasses,CustomChars,AdminCommands,CustomItems,BonusGameSongs,BonusGameFX; var config array<CFGCustomZedXP> CustomZedXP; var array< class<Ext_PerkBase> > LoadedPerks; var array<FCustomCharEntry> CustomCharList; var ExtPlayerStat ServerStatLoader; var KFPawn LastHitZed; var int LastHitHP; var ExtPlayerController LastDamageDealer; var vector LastDamagePosition; var private const array<string> DevList; var transient private array<UniqueNetId> DevNetID; var ExtXMLOutput FileOutput; var transient class<DamageType> LastKillDamageType; var SoundCue BonusGameCue; var Object BonusGameFXObj; var array<FCustomTraderItem> CustomItemList; var KFGFxObject_TraderItems CustomTrader; const SettingsTagVer=13; var KFGameReplicationInfo KF; var config int SettingsInit; var config int ForcedMaxPlayers,PlayerRespawnTime,LargeMonsterHP,StatAutoSaveWaves,MinUnloadPerkLevel,PostGameRespawnCost,MaxTopPlayers; var config float UnloadPerkExpCost; var globalconfig string ServerMOTD,StatFileDir; var array<Controller> PendingSpawners; var int LastWaveNum,NumWaveSwitches; var ExtSpawnPointHelper SpawnPointer; var bool bRespawnCheck,bSpecialSpawn,bGameHasEnded,bIsPostGame; var config bool bKillMessages,bDamageMessages,bEnableMapVote,bNoAdminCommands,bNoWebAdmin,bNoBoomstickJumping,bDumpXMLStats,bRagdollFromFall,bRagdollFromMomentum,bRagdollFromBackhit,bAddCountryTags; var config bool bServerPerksMode; var config bool bDLCWeaponsForFree; var config bool bDontUseOriginalWeaponry; var config bool bAllowStandartPistolUpgrade; var config bool bDisableCustomTrader; //Custom XP lightly array struct CustomZedXPStruct { var class<KFPawn_Monster> zedclass; var float XPValues[4]; }; var array<CustomZedXPStruct> CustomZedXPArray; function PostBeginPlay() { local xVotingHandler MV; local int i,j; local class<Ext_PerkBase> PK; local UniqueNetId Id; local KFCharacterInfo_Human CH; local ObjectReferencer OR; local Object O; local string S; local bool bLock; Super.PostBeginPlay(); if (WorldInfo.Game.BaseMutator==None) WorldInfo.Game.BaseMutator = Self; else WorldInfo.Game.BaseMutator.AddMutator(Self); if (bDeleteMe) // This was a duplicate instance of the mutator. return; SpawnPointer = class'ExtSpawnPointHelper'.Static.FindHelper(WorldInfo); // Start init world pathlist. //OnlineSubsystemSteamworks(class'GameEngine'.Static.GetOnlineSubsystem()).Int64ToUniqueNetId("",Id); //`Log("TEST"@class'OnlineSubsystem'.Static.UniqueNetIdToString(Id)); DevNetID.Length = DevList.Length; for (i=0; i<DevList.Length; ++i) { class'OnlineSubsystem'.Static.StringToUniqueNetId(DevList[i],Id); DevNetID[i] = Id; } ServerStatLoader = new (None) class'ExtPlayerStat'; WorldInfo.Game.HUDType = class'KFExtendedHUD'; WorldInfo.Game.PlayerControllerClass = class'ExtPlayerController'; WorldInfo.Game.PlayerReplicationInfoClass = class'ExtPlayerReplicationInfo'; WorldInfo.Game.DefaultPawnClass = class'ExtHumanPawn'; KFGameInfo(WorldInfo.Game).CustomizationPawnClass = class'ExtPawn_Customization'; KFGameInfo(WorldInfo.Game).KFGFxManagerClass = class'ExtMoviePlayer_Manager'; // trader things if (!bDisableCustomTrader && CustomTrader==None) { CustomTrader = class'ExtPlayerReplicationInfo'.Static.CreateNewList(); SetTimer(0.001, false, 'EditTraiderItems'); } if (ServerMOTD=="") ServerMOTD = "Message of the Day"; if (StatFileDir=="") { StatFileDir = "../../KFGame/Script/%s.usa"; Default.StatFileDir = "../../KFGame/Script/%s.usa"; } if (SettingsInit!=SettingsTagVer) { if (SettingsInit==0) ForcedMaxPlayers = 6; if (SettingsInit<2) { bKillMessages = true; bDamageMessages = true; LargeMonsterHP = 800; } if (SettingsInit<3) bEnableMapVote = true; if (SettingsInit<5) { StatAutoSaveWaves = 1; PerkClasses.Length = 10; PerkClasses[0] = PathName(class'Ext_PerkBerserker'); PerkClasses[1] = PathName(class'Ext_PerkCommando'); PerkClasses[2] = PathName(class'Ext_PerkFieldMedic'); PerkClasses[3] = PathName(class'Ext_PerkSupport'); PerkClasses[4] = PathName(class'Ext_PerkDemolition'); PerkClasses[5] = PathName(class'Ext_PerkFirebug'); PerkClasses[6] = PathName(class'Ext_PerkGunslinger'); PerkClasses[7] = PathName(class'Ext_PerkSharpshooter'); PerkClasses[8] = PathName(class'Ext_PerkSWAT'); PerkClasses[9] = PathName(class'Ext_PerkSurvivalist'); } else if (SettingsInit<11) { PerkClasses.AddItem(PathName(class'Ext_PerkSharpshooter')); PerkClasses.AddItem(PathName(class'Ext_PerkSWAT')); PerkClasses.AddItem(PathName(class'Ext_PerkSurvivalist')); } else if (SettingsInit==11) PerkClasses.AddItem(PathName(class'Ext_PerkSurvivalist')); if (SettingsInit<6) { MinUnloadPerkLevel = 25; UnloadPerkExpCost = 0.1; } if (SettingsInit<8) { AdminCommands.Length = 2; AdminCommands[0] = "Kick:Kick Player"; AdminCommands[1] = "KickBan:Kick-Ban Player"; } if (SettingsInit<9) MaxTopPlayers = 50; if (SettingsInit < 13) { bDLCWeaponsForFree = True; bAllowStandartPistolUpgrade = True; bDisableCustomTrader = False; } SettingsInit = SettingsTagVer; SaveConfig(); } for (i=0; i<PerkClasses.Length; ++i) { PK = class<Ext_PerkBase>(DynamicLoadObject(PerkClasses[i],class'Class')); if (PK!=None) { LoadedPerks.AddItem(PK); PK.Static.CheckConfig(); } } j = 0; for (i=0; i<CustomChars.Length; ++i) { bLock = Left(CustomChars[i],1)=="*"; S = (bLock ? Mid(CustomChars[i],1) : CustomChars[i]); CH = KFCharacterInfo_Human(DynamicLoadObject(S,class'KFCharacterInfo_Human',true)); if (CH!=None) { CustomCharList.Length = j+1; CustomCharList[j].bLock = bLock; CustomCharList[j].Char = CH; ++j; continue; } OR = ObjectReferencer(DynamicLoadObject(S,class'ObjectReferencer')); if (OR!=None) { foreach OR.ReferencedObjects(O) { if (KFCharacterInfo_Human(O)!=None) { CustomCharList.Length = j+1; CustomCharList[j].bLock = bLock; CustomCharList[j].Char = KFCharacterInfo_Human(O); CustomCharList[j].Ref = OR; ++j; } } } } // Bonus (pong) game contents. if (BonusGameSongs.Length>0) { BonusGameCue = SoundCue(DynamicLoadObject(BonusGameSongs[Rand(BonusGameSongs.Length)],class'SoundCue')); } if (BonusGameFX.Length>0) { BonusGameFXObj = DynamicLoadObject(BonusGameFX[Rand(BonusGameFX.Length)],class'Object'); if (SoundCue(BonusGameFXObj)==None && ObjectReferencer(BonusGameFXObj)==None) // Check valid type. BonusGameFXObj = None; } if (ForcedMaxPlayers>0) { SetMaxPlayers(); SetTimer(0.001,false,'SetMaxPlayers'); } bRespawnCheck = (PlayerRespawnTime>0); if (bRespawnCheck) SetTimer(1,true); if (bEnableMapVote) { foreach DynamicActors(class'xVotingHandler',MV) break; if (MV==None) MV = Spawn(class'xVotingHandler'); MV.BaseMutator = Class; } SetTimer(1,true,'CheckWave'); if (!bNoWebAdmin && WorldInfo.NetMode!=NM_StandAlone) SetTimer(0.1,false,'SetupWebAdmin'); if (bDumpXMLStats) FileOutput = Spawn(class'ExtXMLOutput'); UpdateCustomZedXPArray(); // Causes bugs // SetTimer(0.1,'CheckPickupFactories') } function UpdateCustomZedXPArray() { local int i; local CustomZedXPStruct zedxp; CustomZedXPArray.Length = 0; // Custom XP for custom zeds for (i=0;i<CustomZedXP.Length;i++) { zedxp.zedclass = class<KFPawn_Monster>(DynamicLoadObject(CustomZedXP[i].zed,Class'Class')); if (zedxp.zedclass == none) { `log("Error loading"@CustomZedXP[i].zed); continue; } zedxp.XPValues[0] = CustomZedXP[i].XP1; zedxp.XPValues[1] = CustomZedXP[i].XP2; zedxp.XPValues[2] = CustomZedXP[i].XP3; zedxp.XPValues[3] = CustomZedXP[i].XP4; CustomZedXPArray.AddItem(zedxp); `log("CustomXP: Loaded"@PathName(zedxp.zedclass)); } } // function CheckPickupFactories() // { // local KFPickupFactory_Item ItemFactory; // // Disable 9mm and medpistol in all PickupFactories // foreach AllActors(class'KFPickupFactory_Item', ItemFactory) // { // for (i=0;i<ItemFactory.ItemPickups.Length;i++) // { // if (ItemFactory.ItemPickups[i].ItemClass == class'KFGameContent.KFWeap_Pistol_9mm' // || ItemFactory.ItemPickups[i].ItemClass == class'KFGameContent.KFWeap_Pistol_Medic') // { // ItemFactory.ItemPickups.Remove(i, 1); // break; // } // } // } // } function EditTraiderItems() { local int i; local KFGFxObject_TraderItems Trad; // local FCustomTraderItem CI; // local STraderItem SI; // var config bool bDLCWeaponsForFree; // var config bool bDontUseOriginalWeaponry; if (!bDontUseOriginalWeaponry) { Trad = KFGameReplicationInfo(WorldInfo.GRI).TraderItems; // Remove dual 9mm, 9mm, medpistol and DLC weapons for (i=0;i<Trad.SaleItems.Length;i++) { if (string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_Dual9mm" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_Medic" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_9mm") { // Remove pistols continue; } if (bDLCWeaponsForFree) { // DLC Weapons if (string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_Blunderbuss" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Blunt_ChainBat" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_ChiappaRhino" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_ChiappaRhinoDual" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Bow_CompoundBow" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Ice_FreezeThrower" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Edged_IonThruster" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Rifle_MosinNagant" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_AssaultRifle_LazerCutter" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_SMG_G18" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Pistol_DualG18" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Mine_Reconstructor" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Minigun" || string(Trad.SaleItems[i].ClassName) ~= "KFWeap_Rifle_FrostShotgunAxe") { continue; } } // Adding original weapon AddCIToTraderEx(Trad.SaleItems[i].WeaponDef); } } // Reinfo and resorting items // MyKFGI.MyKFGRI.TraderItems.SetItemsInfo(MyKFGI.MyKFGRI.TraderItems.SaleItems); // MyKFGI.MyKFGRI.TraderItems.SortItemsInfo(MyKFGI.MyKFGRI.TraderItems.SaleItems); if (bAllowStandartPistolUpgrade) { // Add custom 9mm for upgrades AddCIToTrader("ServerExt.ExtWeapDef_9mm"); // Add custom medpistol for upgrades AddCIToTrader("ServerExt.ExtWeapDef_MedicPistol"); } //Add DLCs weapons for free if (bDLCWeaponsForFree) { AddCIToTrader("ServerExt.DLCWeapDef_Blunderbuss"); AddCIToTrader("ServerExt.DLCWeapDef_ChainBat"); AddCIToTrader("ServerExt.DLCWeapDef_ChiappaRhino"); AddCIToTrader("ServerExt.DLCWeapDef_ChiappaRhinoDual"); AddCIToTrader("ServerExt.DLCWeapDef_CompoundBow"); AddCIToTrader("ServerExt.DLCWeapDef_FreezeThrower"); AddCIToTrader("ServerExt.DLCWeapDef_IonThruster"); AddCIToTrader("ServerExt.DLCWeapDef_G18"); AddCIToTrader("ServerExt.DLCWeapDef_MosinNagant"); AddCIToTrader("ServerExt.DLCWeapDef_LazerCutter"); AddCIToTrader("ServerExt.DLCWeapDef_Pistol_DualG18"); AddCIToTrader("ServerExt.DLCWeapDef_Pistol_G18C"); AddCIToTrader("ServerExt.DLCWeapDef_Mine_Reconstructor"); AddCIToTrader("ServerExt.DLCWeapDef_Minigun"); AddCIToTrader("ServerExt.DLCWeapDef_Rifle_FrostShotgunAxe"); } // Add custom items from WebAdmin for (i=0; i<CustomItems.Length; ++i) { AddCIToTrader(CustomItems[i]); } InitGRIList(); } function AddCIToTrader(string weapdef) { local FCustomTraderItem CI; CI.WeaponDef = class<KFWeaponDefinition>(DynamicLoadObject(weapdef,class'Class')); if (CI.WeaponDef == None) return; CI.WeaponClass = class<KFWeapon>(DynamicLoadObject(CI.WeaponDef.Default.WeaponClassPath,class'Class')); if (CI.WeaponClass == None) return; CustomItemList.AddItem(CI); class'ExtPlayerReplicationInfo'.Static.SetWeaponInfo(WorldInfo.NetMode==NM_DedicatedServer,CustomTrader.SaleItems.Length,CI,CustomTrader); } function AddCIToTraderEx(class<KFWeaponDefinition> weapdef) { local FCustomTraderItem CI; CI.WeaponDef = weapdef; if (CI.WeaponDef == None) return; CI.WeaponClass = class<KFWeapon>(DynamicLoadObject(CI.WeaponDef.Default.WeaponClassPath,class'Class')); if (CI.WeaponClass == None) return; CustomItemList.AddItem(CI); class'ExtPlayerReplicationInfo'.Static.SetWeaponInfo(WorldInfo.NetMode==NM_DedicatedServer,CustomTrader.SaleItems.Length,CI,CustomTrader); } static final function string GetStatFile(const out UniqueNetId UID) { return Repl(Default.StatFileDir,"%s","U_"$class'OnlineSubsystem'.Static.UniqueNetIdToString(UID)); } final function bool IsDev(const out UniqueNetId UID) { local int i; for (i=(DevNetID.Length-1); i>=0; --i) if (DevNetID[i]==UID) return true; return false; } function InitGRIList() { local ExtPlayerController PC; KFGameReplicationInfo(WorldInfo.GRI).TraderItems = CustomTrader; // Must sync up local client. if (WorldInfo.NetMode==NM_StandAlone) { foreach LocalPlayerControllers(class'ExtPlayerController',PC) if (PC.PurchaseHelper!=None) PC.PurchaseHelper.TraderItems = CustomTrader; } } function CheckWave() { if (KF==None) { KF = KFGameReplicationInfo(WorldInfo.GRI); if (KF==None) return; } if (LastWaveNum!=KF.WaveNum) { LastWaveNum = KF.WaveNum; NotifyWaveChange(); } if (!bGameHasEnded && KF.bMatchIsOver) // HACK, since KFGameInfo_Survival doesn't properly notify mutators of this! { SaveAllPerks(true); bGameHasEnded = true; } } function NotifyWaveChange() { local ExtPlayerController ExtPC; local KFProj_RicochetStickBullet KFBolt; if (bRespawnCheck) { bIsPostGame = (KF.WaveMax<KF.WaveNum); bRespawnCheck = (!bIsPostGame || PostGameRespawnCost>=0); if (bRespawnCheck) SavePlayerInventory(); } if (StatAutoSaveWaves>0 && ++NumWaveSwitches>=StatAutoSaveWaves) { NumWaveSwitches = 0; SaveAllPerks(); } if (!KF.bTraderIsOpen) { foreach WorldInfo.AllControllers(class'ExtPlayerController',ExtPC) ExtPC.bSetPerk = false; } foreach WorldInfo.AllActors(class'KFProj_RicochetStickBullet', KFBolt) { if (KFProj_Bolt_CompoundBowSharp(KFBolt) != none || KFProj_Bolt_Crossbow(KFBolt) != none) KFBolt.Destroy(); } } function SetupWebAdmin() { local WebServer W; local WebAdmin A; local ExtWebApp xW; local byte i; foreach AllActors(class'WebServer',W) break; if (W!=None) { for (i=0; (i<10 && A==None); ++i) A = WebAdmin(W.ApplicationObjects[i]); if (A!=None) { xW = new (None) class'ExtWebApp'; xW.MyMutator = Self; A.addQueryHandler(xW); } else `Log("ExtWebAdmin ERROR: No valid WebAdmin application found!"); } else `Log("ExtWebAdmin ERROR: No WebServer object found!"); } function SetMaxPlayers() { local OnlineGameSettings GameSettings; WorldInfo.Game.MaxPlayers = ForcedMaxPlayers; WorldInfo.Game.MaxPlayersAllowed = ForcedMaxPlayers; if (WorldInfo.Game.GameInterface!=None) { GameSettings = WorldInfo.Game.GameInterface.GetGameSettings(WorldInfo.Game.PlayerReplicationInfoClass.default.SessionName); if (GameSettings!=None) GameSettings.NumPublicConnections = ForcedMaxPlayers; } } function AddMutator(Mutator M) { if (M!=Self) // Make sure we don't get added twice. { if (M.Class==Class) M.Destroy(); else Super.AddMutator(M); } } function bool IsFromMod(Object O) { local string PackageName; if (O == None) return false; PackageName = string(O.GetPackageName()); if (Len(PackageName)>1 && InStr(Caps(PackageName), "KF") == 0) { PackageName = string(O); if (Len(PackageName)>1 && InStr(Caps(PackageName), "KF") == 0) return false; } return true; } function CustomXP(Controller Killer, Controller Killed) { local KFPlayerController KFPC; local KFPawn_Monster KFM; local int i, j; local KFPlayerReplicationInfo DamagerKFPRI; local float XP; local KFPerk InstigatorPerk; local bool cont; KFM = KFPawn_Monster(Killed.Pawn); for (i = 0; i < KFM.DamageHistory.Length; i++) { DamagerKFPRI = KFPlayerReplicationInfo(KFM.DamageHistory[i].DamagerPRI); if (DamagerKFPRI != None) { // Check that no mods are used in this kill cont = true; for (j=0; j < KFM.DamageHistory[i].DamageCausers.Length; j++) { if (IsFromMod(KFM.DamageHistory[i].DamageCausers[j]) || IsFromMod(KFM.DamageHistory[i].DamageTypes[j])) { cont = false; break; } } if (cont && !IsFromMod(KFM)) { // No mods - exit the loop, the game will add experience by itself continue; } // Distribute experience points KFPC = KFPlayerController(DamagerKFPRI.Owner); if (KFPC != none) { XP = 0; for (j = 0; j < CustomZedXPArray.Length; j++) { if (KFM.Class == CustomZedXPArray[j].zedclass) { XP = CustomZedXPArray[j].XPValues[MyKFGI.GameDifficulty]; break; } } if (XP == 0) { XP = KFM.static.GetXPValue(MyKFGI.GameDifficulty); } InstigatorPerk = KFPC.GetPerk(); // Special for survivalist - he gets experience for everything // And for TF2Sentry - he has no perk in DamageHistory if (InstigatorPerk.ShouldGetAllTheXP() || KFM.DamageHistory[i].DamagePerks.Length == 0) { KFPC.OnPlayerXPAdded(XP, InstigatorPerk.Class); continue; } XP /= KFM.DamageHistory[i].DamagePerks.Length; for (j = 0; j < KFM.DamageHistory[i].DamagePerks.Length; j++) { KFPC.OnPlayerXPAdded(FCeil(XP), KFM.DamageHistory[i].DamagePerks[j]); } } } } } function ScoreKill(Controller Killer, Controller Killed) { local KFPlayerController KFPC; local ExtPerkManager KillersPerk; if (bRespawnCheck && Killed.bIsPlayer) CheckRespawn(Killed); if (KFPawn_Monster(Killed.Pawn) != None && Killed.GetTeamNum() != 0 && Killer.bIsPlayer && Killer.GetTeamNum() == 0) { if (ExtPlayerController(Killer)!=None && ExtPlayerController(Killer).ActivePerkManager!=None) ExtPlayerController(Killer).ActivePerkManager.PlayerKilled(KFPawn_Monster(Killed.Pawn),LastKillDamageType); if (bKillMessages && Killer.PlayerReplicationInfo!=None) BroadcastKillMessage(Killed.Pawn,Killer); CustomXP(Killer, Killed); } if (MyKFGI != None && MyKFGI.IsZedTimeActive() && KFPawn_Monster(Killed.Pawn) != None) { KFPC = KFPlayerController(Killer); if (KFPC != none) { KillersPerk = ExtPerkManager(KFPC.GetPerk()); if (MyKFGI.ZedTimeRemaining > 0.f && KillersPerk != none && KillersPerk.GetZedTimeExtensions( KFPC.GetLevel() ) > MyKFGI.ZedTimeExtensionsUsed) { MyKFGI.DramaticEvent(1.0); MyKFGI.ZedTimeExtensionsUsed++; } } } if (ExtPlayerController(Killed) != None) CheckPerkChange(ExtPlayerController(Killed)); if (NextMutator != None) NextMutator.ScoreKill(Killer, Killed); } function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> damageType, vector HitLocation) { if ((KFPawn_Human(Killed)!=None && CheckPreventDeath(KFPawn_Human(Killed),Killer,damageType)) || Super.PreventDeath(Killed,Killer,damageType,HitLocation)) return true; LastKillDamageType = damageType; if (Killed.Controller!=None && KFPawn_Monster(Killed)!=None) { // Hack for when pet kills a zed. if (Killed.GetTeamNum()!=0) { if (Killer!=None && Killer!=Killed.Controller && Killer.GetTeamNum()==0 && Ext_T_MonsterPRI(Killer.PlayerReplicationInfo)!=None) GT_PlayerKilled(Ext_T_MonsterPRI(Killer.PlayerReplicationInfo).OwnerController,Killed.Controller,damageType); } // Broadcast pet's deathmessage. else if (Killed.PlayerReplicationInfo!=None && PlayerController(Killed.Controller)==None && damageType!=class'KFDT_Healing') BroadcastFFDeath(Killer,Killed,damageType); } return false; } // Replica of KFGameInfo.Killed base. final function GT_PlayerKilled(Controller Killer, Controller Killed, class<DamageType> damageType) { local ExtPlayerController KFPC; local KFPawn_Monster MonsterPawn; local KFGameInfo KFG; KFG = KFGameInfo(WorldInfo.Game); ScoreKill(Killer,Killed); // Broadcast kill message. KFPC = ExtPlayerController(Killer); MonsterPawn = KFPawn_Monster(Killed.Pawn); if (KFG!=None && KFPC != none && MonsterPawn!=none) { //Chris: We have to do it earlier here because we need a damage type KFPC.AddZedKill(MonsterPawn.class, KFG.GameDifficulty, damageType, false); // Not support in v1096: KFGameInfo.CheckForBerserkerSmallRadiusKill //if (KFPC.ActivePerkManager!=none && KFPC.ActivePerkManager.CanEarnSmallRadiusKillXP(damageType)) // KFG.CheckForBerserkerSmallRadiusKill(MonsterPawn, KFPC); } } final function bool CheckPreventDeath(KFPawn_Human Victim, Controller Killer, class<DamageType> damageType) { local ExtPlayerController E; if (Victim.IsA('KFPawn_Customization')) return false; E = ExtPlayerController(Victim.Controller); return (E!=None && E.ActivePerkManager!=None && E.ActivePerkManager.CurrentPerk!=None && E.ActivePerkManager.CurrentPerk.PreventDeath(Victim,Killer,damageType)); } final function BroadcastKillMessage(Pawn Killed, Controller Killer) { local ExtPlayerController E; if (Killer==None || Killer.PlayerReplicationInfo==None) return; if (Killed.Default.Health>=LargeMonsterHP) { foreach WorldInfo.AllControllers(class'ExtPlayerController',E) if (!E.bClientHideKillMsg) E.ReceiveKillMessage(Killed.Class,true,Killer.PlayerReplicationInfo); } else if (ExtPlayerController(Killer)!=None && !ExtPlayerController(Killer).bClientHideKillMsg) ExtPlayerController(Killer).ReceiveKillMessage(Killed.Class); } final function BroadcastFFDeath(Controller Killer, Pawn Killed, class<DamageType> damageType) { local ExtPlayerController E; local PlayerReplicationInfo KillerPRI; local string P; local bool bFF; P = Killed.PlayerReplicationInfo.PlayerName; if (Killer==None || Killer==Killed.Controller) { foreach WorldInfo.AllControllers(class'ExtPlayerController',E) E.ClientZedKillMessage(damageType,P); return; } bFF = (Killer.GetTeamNum()==0); KillerPRI = Killer.PlayerReplicationInfo; if (PlayerController(Killer)==None) KillerPRI = None; foreach WorldInfo.AllControllers(class'ExtPlayerController',E) E.ClientZedKillMessage(damageType,P,KillerPRI,Killer.Pawn.Class,bFF); } function NetDamage(int OriginalDamage, out int Damage, Pawn Injured, Controller InstigatedBy, vector HitLocation, out vector Momentum, class<DamageType> DamageType, Actor DamageCauser) { if (NextMutator != None) NextMutator.NetDamage(OriginalDamage, Damage, Injured, InstigatedBy, HitLocation, Momentum, DamageType, DamageCauser); if (LastDamageDealer!=None) // Make sure no other damagers interfear with the old thing going on. { ClearTimer('CheckDamageDone'); CheckDamageDone(); } if (KFPawn_Monster(Injured) != None && InstigatedBy != none && InstigatedBy.GetTeamNum() == Injured.GetTeamNum()) { Momentum = vect(0,0,0); Damage = 0; return; } if (Damage>0 && InstigatedBy!=None) { if (KFPawn_Monster(Injured)!=None) { if (Injured.GetTeamNum()!=0) { LastDamageDealer = ExtPlayerController(InstigatedBy); if (bDamageMessages && LastDamageDealer!=None && !LastDamageDealer.bNoDamageTracking) { // Must delay this until next to get accurate damage dealt result. LastHitZed = KFPawn(Injured); LastHitHP = LastHitZed.Health; LastDamagePosition = HitLocation; SetTimer(0.001,false,'CheckDamageDone'); } else { LastDamageDealer = None; // Give credits to pet's owner. if (Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo)!=None) HackSetHistory(KFPawn(Injured),Injured,Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo).OwnerController,Damage,HitLocation); } } else if (KFPawn(InstigatedBy.Pawn).GetTeamNum() != KFPawn(Injured).GetTeamNum()) { Momentum = vect(0,0,0); Damage = 0; } } else if (bDamageMessages && KFPawn_Human(Injured)!=None && Injured.GetTeamNum()==0 && InstigatedBy.GetTeamNum()!=0 && ExtPlayerController(InstigatedBy)!=None) { LastDamageDealer = ExtPlayerController(InstigatedBy); if (bDamageMessages && !LastDamageDealer.bClientHideNumbers) { // Must delay this until next to get accurate damage dealt result. LastHitZed = KFPawn(Injured); LastHitHP = LastHitZed.Health; LastDamagePosition = HitLocation; SetTimer(0.001,false,'CheckDamageDone'); } } } } final function CheckDamageDone() { local int Damage; if (LastDamageDealer!=None && LastHitZed!=None && LastHitHP!=LastHitZed.Health) { Damage = LastHitHP-Max(LastHitZed.Health,0); if (Damage>0) { if (!LastDamageDealer.bClientHideDamageMsg && KFPawn_Monster(LastHitZed)!=None) LastDamageDealer.ReceiveDamageMessage(LastHitZed.Class,Damage); if (!LastDamageDealer.bClientHideNumbers) LastDamageDealer.ClientNumberMsg(Damage,LastDamagePosition,DMG_PawnDamage); } } LastDamageDealer = None; } final function HackSetHistory(KFPawn C, Pawn Injured, Controller Player, int Damage, vector HitLocation) { local int i; local ExtPlayerController PC; if (Player==None) return; PC = ExtPlayerController(Player); if (bDamageMessages && PC!=None) { if (!PC.bClientHideDamageMsg) PC.ReceiveDamageMessage(Injured.Class,Damage); if (!PC.bClientHideNumbers) PC.ClientNumberMsg(Damage,HitLocation,DMG_PawnDamage); } i = C.DamageHistory.Find('DamagerController',Player); if (i==-1) { i = C.DamageHistory.Length; C.DamageHistory.Length = i+1; C.DamageHistory[i].DamagerController = Player; C.DamageHistory[i].DamagerPRI = Player.PlayerReplicationInfo; C.DamageHistory[i].DamagePerks.AddItem(class'ExtPerkManager'); C.DamageHistory[i].Damage = Damage; } else if ((WorldInfo.TimeSeconds-C.DamageHistory[i].LastTimeDamaged)<10) C.DamageHistory[i].Damage += Damage; else C.DamageHistory[i].Damage = Damage; C.DamageHistory[i].LastTimeDamaged = WorldInfo.TimeSeconds; C.DamageHistory[i].TotalDamage += Damage; } function bool HandleRestartGame() { if (!bGameHasEnded) { SaveAllPerks(true); bGameHasEnded = true; } return Super.HandleRestartGame(); } function NotifyLogout(Controller Exiting) { if (KFPlayerController(Exiting)!=None) RemoveRespawn(Exiting); if (!bGameHasEnded && ExtPlayerController(Exiting)!=None) { CheckPerkChange(ExtPlayerController(Exiting)); SavePlayerPerk(ExtPlayerController(Exiting)); } if (NextMutator != None) NextMutator.NotifyLogout(Exiting); } function NotifyLogin(Controller NewPlayer) { if (ExtPlayerController(NewPlayer)!=None) { if (ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo)!=None) InitCustomChars(ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo)); if (bAddCountryTags && NetConnection(PlayerController(NewPlayer).Player)!=None) ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).SetPlayerNameTag(class'CtryDatabase'.Static.GetClientCountryStr(PlayerController(NewPlayer).GetPlayerNetworkAddress())); ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).bIsDev = IsDev(NewPlayer.PlayerReplicationInfo.UniqueId); if (WorldInfo.NetMode!=NM_StandAlone) ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).OnRepNextItem = GetNextItem; if (BonusGameCue!=None || BonusGameFXObj!=None) ExtPlayerController(NewPlayer).ClientSetBonus(BonusGameCue,BonusGameFXObj); if (bRespawnCheck) CheckRespawn(NewPlayer); if (!bGameHasEnded) InitializePerks(ExtPlayerController(NewPlayer)); SendMOTD(ExtPlayerController(NewPlayer)); } if (NextMutator != None) NextMutator.NotifyLogin(NewPlayer); } final function InitializePerks(ExtPlayerController Other) { local ExtPerkManager PM; local Ext_PerkBase P; local int i; Other.OnChangePerk = PlayerChangePerk; Other.OnBoughtStats = PlayerBuyStats; Other.OnBoughtTrait = PlayerBoughtTrait; Other.OnPerkReset = ResetPlayerPerk; Other.OnAdminHandle = AdminCommand; Other.OnSetMOTD = AdminSetMOTD; Other.OnRequestUnload = PlayerUnloadInfo; Other.OnSpectateChange = PlayerChangeSpec; Other.OnClientGetStat = class'ExtStatList'.Static.GetStat; PM = Other.ActivePerkManager; PM.InitPerks(); for (i=0; i<LoadedPerks.Length; ++i) { P = Spawn(LoadedPerks[i],Other); PM.RegisterPerk(P); } ServerStatLoader.FlushData(); if (ServerStatLoader.LoadStatFile(Other)) { ServerStatLoader.ToStart(); PM.LoadData(ServerStatLoader); if (Default.MaxTopPlayers>0) class'ExtStatList'.Static.SetTopPlayers(Other); } PM.ServerInitPerks(); PM.InitiateClientRep(); } final function SendMOTD(ExtPlayerController PC) { local string S; local int i; S = ServerMOTD; while (Len(S)>510) { PC.ReceiveServerMOTD(Left(S,500),false); S = Mid(S,500); } PC.ReceiveServerMOTD(S,true); for (i=0; i<AdminCommands.Length; ++i) PC.AddAdminCmd(AdminCommands[i]); } final function SavePlayerPerk(ExtPlayerController PC) { if (PC.ActivePerkManager!=None && PC.ActivePerkManager.bStatsDirty) { // Verify broken stats. if (PC.ActivePerkManager.bUserStatsBroken) { PC.ClientMessage("Warning: Your stats are broken, not saving.",'Priority'); return; } ServerStatLoader.FlushData(); if (ServerStatLoader.LoadStatFile(PC) && ServerStatLoader.GetSaveVersion()!=PC.ActivePerkManager.UserDataVersion) { PC.ActivePerkManager.bUserStatsBroken = true; PC.ClientMessage("Warning: Your stats save data version differs from what is loaded, stat saving disabled to prevent stats loss.",'Priority'); return; } // Actually save. ServerStatLoader.FlushData(); PC.ActivePerkManager.SaveData(ServerStatLoader); ServerStatLoader.SaveStatFile(PC); PC.ActivePerkManager.bStatsDirty = false; // Write XML output. if (FileOutput!=None) FileOutput.DumpXML(PC.ActivePerkManager); } } function SaveAllPerks(optional bool bOnEndGame) { local ExtPlayerController PC; if (bGameHasEnded) return; foreach WorldInfo.AllControllers(class'ExtPlayerController',PC) if (PC.ActivePerkManager!=None && PC.ActivePerkManager.bStatsDirty) { if (bOnEndGame) CheckPerkChange(PC); SavePlayerPerk(PC); } } function CheckRespawn(Controller PC) { if (!PC.bIsPlayer || ExtPlayerReplicationInfo(PC.PlayerReplicationInfo)==None || PC.PlayerReplicationInfo.bOnlySpectator || WorldInfo.Game.bWaitingToStartMatch || WorldInfo.Game.bGameEnded) return; // VS redead. if (ExtHumanPawn(PC.Pawn)!=None && ExtHumanPawn(PC.Pawn).bPendingRedead) return; if (bIsPostGame && PC.PlayerReplicationInfo.Score<PostGameRespawnCost) { if (PlayerController(PC)!=None) PlayerController(PC).ClientMessage("You can't afford to respawn anymore (need "$PostGameRespawnCost@Chr(163)$")!",'LowCriticalEvent'); return; } ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = PlayerRespawnTime; PC.PlayerReplicationInfo.bForceNetUpdate = true; if (PendingSpawners.Find(PC)<0) PendingSpawners.AddItem(PC); } function RemoveRespawn(Controller PC) { ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PendingSpawners.RemoveItem(PC); } final function InitPlayer(ExtHumanPawn Other) { local ExtPlayerReplicationInfo PRI; PRI = ExtPlayerReplicationInfo(Other.PlayerReplicationInfo); if (PRI!=None && PRI.PerkManager!=None && PRI.PerkManager.CurrentPerk!=None) PRI.PerkManager.CurrentPerk.ApplyEffectsTo(Other); Other.bRagdollFromFalling = bRagdollFromFall; Other.bRagdollFromMomentum = bRagdollFromMomentum; Other.bRagdollFromBackhit = bRagdollFromBackhit; } function ModifyPlayer(Pawn Other) { if (ExtHumanPawn(Other)!=None) InitPlayer(ExtHumanPawn(Other)); if (NextMutator != None) NextMutator.ModifyPlayer(Other); } function Timer() { local int i; local Controller PC; local bool bSpawned,bAllDead; bAllDead = (KFGameInfo(WorldInfo.Game).GetLivingPlayerCount()<=0 || WorldInfo.Game.bGameEnded || !bRespawnCheck); for (i=0; i<PendingSpawners.Length; ++i) { PC = PendingSpawners[i]; if (bAllDead || PC==None || PC.PlayerReplicationInfo.bOnlySpectator || (PC.Pawn!=None && PC.Pawn.IsAliveAndWell())) { if (PC!=None) { ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PC.PlayerReplicationInfo.bForceNetUpdate = true; } PendingSpawners.Remove(i--,1); } else if (bIsPostGame && PC.PlayerReplicationInfo.Score<PostGameRespawnCost) { ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PC.PlayerReplicationInfo.bForceNetUpdate = true; if (PlayerController(PC)!=None) PlayerController(PC).ClientMessage("You can't afford to respawn anymore (need "$PostGameRespawnCost@Chr(163)$")!",'LowCriticalEvent'); PendingSpawners.Remove(i--,1); } else if (--ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter<=0) { PC.PlayerReplicationInfo.bForceNetUpdate = true; ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = 0; if (!bSpawned) // Spawn only one player at time (so game doesn't crash if many players spawn in same time). { bSpawned = true; if (RespawnPlayer(PC)) { if (bIsPostGame) { if (PlayerController(PC)!=None) PlayerController(PC).ClientMessage("This respawn cost you "$PostGameRespawnCost@Chr(163)$"!",'LowCriticalEvent'); PC.PlayerReplicationInfo.Score-=PostGameRespawnCost; } ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PC.PlayerReplicationInfo.bForceNetUpdate = true; } } } else PC.PlayerReplicationInfo.bForceNetUpdate = true; } } final function SavePlayerInventory() { local KFPawn_Human P; local int i,j; local Inventory Inv; local KFWeapon K; PlayerInv.Length = 0; i = 0; foreach WorldInfo.AllPawns(class'KFPawn_Human',P) if (P.IsAliveAndWell() && P.InvManager!=None && P.Controller!=None && P.Controller.PlayerReplicationInfo!=None) { PlayerInv.Length = i+1; PlayerInv[i].OwnerPlayer = P.Controller; PlayerInv[i].Gren = KFInventoryManager(P.InvManager).GrenadeCount; j = 0; foreach P.InvManager.InventoryActors(class'Inventory',Inv) { if (KFInventory_Money(Inv)!=None) continue; K = KFWeapon(Inv); if (K!=None && !K.bCanThrow) // Skip non-throwable items. continue; PlayerInv[i].Inv.Length = j+1; PlayerInv[i].Inv[j].ItemClass = Inv.Class; if (K!=None) { PlayerInv[i].Inv[j].Values[0] = K.SpareAmmoCount[0]; PlayerInv[i].Inv[j].Values[1] = K.SpareAmmoCount[1]; PlayerInv[i].Inv[j].Values[2] = K.AmmoCount[0]; PlayerInv[i].Inv[j].Values[3] = K.AmmoCount[1]; } ++j; } ++i; } } final function bool AddPlayerSpecificInv(Pawn Other) { local int i,j; local Inventory Inv; local KFWeapon K; for (i=(PlayerInv.Length-1); i>=0; --i) if (PlayerInv[i].OwnerPlayer==Other.Controller) { KFInventoryManager(Other.InvManager).bInfiniteWeight = true; KFInventoryManager(Other.InvManager).GrenadeCount = PlayerInv[i].Gren; for (j=(PlayerInv[i].Inv.Length-1); j>=0; --j) { Inv = Other.InvManager.FindInventoryType(PlayerInv[i].Inv[j].ItemClass,false); if (Inv==None) { Inv = Other.InvManager.CreateInventory(PlayerInv[i].Inv[j].ItemClass); } K = KFWeapon(Inv); if (K!=None) { K.SpareAmmoCount[0] = PlayerInv[i].Inv[j].Values[0]; K.SpareAmmoCount[1] = PlayerInv[i].Inv[j].Values[1]; K.AmmoCount[0] = PlayerInv[i].Inv[j].Values[2]; K.AmmoCount[1] = PlayerInv[i].Inv[j].Values[3]; K.ClientForceAmmoUpdate(K.AmmoCount[0],K.SpareAmmoCount[0]); K.ClientForceSecondaryAmmoUpdate(K.AmmoCount[1]); } } if (Other.InvManager.FindInventoryType(class'KFInventory_Money',true)==None) Other.InvManager.CreateInventory(class'KFInventory_Money'); KFInventoryManager(Other.InvManager).bInfiniteWeight = false; return true; } return false; } final function Pawn SpawnDefaultPawnfor (Controller NewPlayer, Actor StartSpot) // Clone of GameInfo one, but with Actor StartSpot. { local class<Pawn> PlayerClass; local Rotator R; local Pawn ResultPawn; PlayerClass = WorldInfo.Game.GetDefaultPlayerClass(NewPlayer); R.Yaw = StartSpot.Rotation.Yaw; ResultPawn = Spawn(PlayerClass,,,StartSpot.Location,R,,true); return ResultPawn; } final function bool RespawnPlayer(Controller NewPlayer) { local KFPlayerReplicationInfo KFPRI; local KFPlayerController KFPC; local Actor startSpot; local int Idx; local array<SequenceObject> Events; local SeqEvent_PlayerSpawned SpawnedEvent; local LocalPlayer LP; if (NewPlayer.Pawn!=None) NewPlayer.Pawn.Destroy(); // figure out the team number and find the start spot StartSpot = SpawnPointer.PickBestSpawn(); // if a start spot wasn't found, if (startSpot == None) { // check for a previously assigned spot if (NewPlayer.StartSpot != None) { StartSpot = NewPlayer.StartSpot; `warn("Player start not found, using last start spot"); } else { // otherwise abort `warn("Player start not found, failed to restart player"); return false; } } // try to create a pawn to use of the default class for this player NewPlayer.Pawn = SpawnDefaultPawnfor (NewPlayer, StartSpot); if (NewPlayer.Pawn == None) { NewPlayer.GotoState('Dead'); if (PlayerController(NewPlayer) != None) PlayerController(NewPlayer).ClientGotoState('Dead','Begin'); return false; } else { // initialize and start it up if (NavigationPoint(startSpot)!=None) NewPlayer.Pawn.SetAnchor(NavigationPoint(startSpot)); if (PlayerController(NewPlayer) != None) { PlayerController(NewPlayer).TimeMargin = -0.1; if (NavigationPoint(startSpot)!=None) NavigationPoint(startSpot).AnchoredPawn = None; // SetAnchor() will set this since IsHumanControlled() won't return true for the Pawn yet } NewPlayer.Pawn.LastStartSpot = PlayerStart(startSpot); NewPlayer.Pawn.LastStartTime = WorldInfo.TimeSeconds; NewPlayer.Possess(NewPlayer.Pawn, false); NewPlayer.Pawn.PlayTeleportEffect(true, true); NewPlayer.ClientSetRotation(NewPlayer.Pawn.Rotation, TRUE); if (!WorldInfo.bNoDefaultInventoryForPlayer) { AddPlayerSpecificInv(NewPlayer.Pawn); WorldInfo.Game.AddDefaultInventory(NewPlayer.Pawn); } WorldInfo.Game.SetPlayerDefaults(NewPlayer.Pawn); // activate spawned events if (WorldInfo.GetGameSequence() != None) { WorldInfo.GetGameSequence().FindSeqObjectsByClass(class'SeqEvent_PlayerSpawned',TRUE,Events); for (Idx = 0; Idx < Events.Length; Idx++) { SpawnedEvent = SeqEvent_PlayerSpawned(Events[Idx]); if (SpawnedEvent != None && SpawnedEvent.CheckActivate(NewPlayer,NewPlayer)) { SpawnedEvent.SpawnPoint = startSpot; SpawnedEvent.PopulateLinkedVariableValues(); } } } } KFPC = KFPlayerController(NewPlayer); KFPRI = KFPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo); // To fix custom post processing chain when not running in editor or PIE. if (KFPC != none) { LP = LocalPlayer(KFPC.Player); if (LP != None) { LP.RemoveAllPostProcessingChains(); LP.InsertPostProcessingChain(LP.Outer.GetWorldPostProcessChain(),INDEX_NONE,true); if (KFPC.myHUD != None) { KFPC.myHUD.NotifyBindPostProcessEffects(); } } } KFGameInfo(WorldInfo.Game).SetTeam(NewPlayer, KFGameInfo(WorldInfo.Game).Teams[0]); if (KFPC != none) { // Initialize game play post process effects such as damage, low health, etc. KFPC.InitGameplayPostProcessFX(); } if (KFPRI!=None) { if (KFPRI.Deaths == 0) KFPRI.Score = KFGameInfo(WorldInfo.Game).DifficultyInfo.GetAdjustedStartingCash(); KFPRI.PlayerHealth = NewPlayer.Pawn.Health; KFPRI.PlayerHealthPercent = FloatToByte(float(NewPlayer.Pawn.Health) / float(NewPlayer.Pawn.HealthMax)); } return true; } function PlayerBuyStats(ExtPlayerController PC, class<Ext_PerkBase> Perk, int iStat, int Amount) { local Ext_PerkBase P; local int i; if (bGameHasEnded) return; P = PC.ActivePerkManager.FindPerk(Perk); if (P==None || !P.bPerkNetReady || iStat>=P.PerkStats.Length) return; Amount = Min(Amount,P.PerkStats[iStat].MaxValue-P.PerkStats[iStat].CurrentValue); if (Amount<=0) return; i = Amount*P.PerkStats[iStat].CostPerValue; if (i>P.CurrentSP) { Amount = P.CurrentSP/P.PerkStats[iStat].CostPerValue; if (Amount<=0) return; i = Amount*P.PerkStats[iStat].CostPerValue; } P.CurrentSP-=i; if (!P.IncrementStat(iStat,Amount)) PC.ClientMessage("Failed to buy stat."); } function PlayerChangePerk(ExtPlayerController PC, class<Ext_PerkBase> NewPerk) { if (bGameHasEnded) return; if (NewPerk==PC.ActivePerkManager.CurrentPerk.Class) { if (PC.PendingPerkClass!=None) { PC.ClientMessage("You will remain the same perk now."); PC.PendingPerkClass = None; } } else if (PC.ActivePerkManager.CurrentPerk==None || KFPawn_Customization(PC.Pawn)!=None || (!PC.bSetPerk && KFGameReplicationInfo(WorldInfo.GRI).bTraderIsOpen)) { if (PC.ActivePerkManager.ApplyPerkClass(NewPerk)) { PC.ClientMessage("You have changed your perk to "$NewPerk.Default.PerkName); PC.bSetPerk = true; } else PC.ClientMessage("Invalid perk "$NewPerk.Default.PerkName); } else if (PC.bSetPerk) PC.ClientMessage("Can only change perks once per wave"); else { PC.ClientMessage("You will change to perk '"$NewPerk.Default.PerkName$"' during trader time."); PC.PendingPerkClass = NewPerk; } } function CheckPerkChange(ExtPlayerController PC) { if (PC.PendingPerkClass!=None) { if (PC.ActivePerkManager.ApplyPerkClass(PC.PendingPerkClass)) { PC.ClientMessage("You have changed your perk to "$PC.PendingPerkClass.Default.PerkName); PC.bSetPerk = true; } else PC.ClientMessage("Invalid perk "$PC.PendingPerkClass.Default.PerkName); PC.PendingPerkClass = None; } } function Tick(float DeltaTime) { local bool bCheckedWave; local ExtPlayerController ExtPC; if (KFGameReplicationInfo(WorldInfo.GRI).bTraderIsOpen && !bCheckedWave) { foreach WorldInfo.AllControllers(class'ExtPlayerController',ExtPC) CheckPerkChange(ExtPC); bCheckedWave = true; } else if (bCheckedWave) bCheckedWave = false; } function PlayerBoughtTrait(ExtPlayerController PC, class<Ext_PerkBase> PerkClass, class<Ext_TraitBase> Trait) { local Ext_PerkBase P; local int i,cost; if (bGameHasEnded) return; P = PC.ActivePerkManager.FindPerk(PerkClass); if (P==None || !P.bPerkNetReady) return; for (i=0; i<P.PerkTraits.Length; ++i) { if (P.PerkTraits[i].TraitType==Trait) { if (P.PerkTraits[i].CurrentLevel>=Trait.Default.NumLevels) return; cost = Trait.Static.GetTraitCost(P.PerkTraits[i].CurrentLevel); if (cost>P.CurrentSP || !Trait.Static.MeetsRequirements(P.PerkTraits[i].CurrentLevel,P)) return; PC.ActivePerkManager.bStatsDirty = true; P.CurrentSP-=cost; P.bForceNetUpdate = true; ++P.PerkTraits[i].CurrentLevel; P.ClientReceiveTraitLvl(i,P.PerkTraits[i].CurrentLevel); if (P.PerkTraits[i].CurrentLevel==1) P.PerkTraits[i].Data = Trait.Static.Initializefor (P,PC); if (PC.ActivePerkManager.CurrentPerk==P) { Trait.Static.TraitDeActivate(P,P.PerkTraits[i].CurrentLevel-1,P.PerkTraits[i].Data); Trait.Static.TraitActivate(P,P.PerkTraits[i].CurrentLevel,P.PerkTraits[i].Data); if (KFPawn_Human(PC.Pawn)!=None) { Trait.Static.CancelEffectOn(KFPawn_Human(PC.Pawn),P,P.PerkTraits[i].CurrentLevel-1,P.PerkTraits[i].Data); Trait.Static.ApplyEffectOn(KFPawn_Human(PC.Pawn),P,P.PerkTraits[i].CurrentLevel,P.PerkTraits[i].Data); } } break; } } } function PlayerUnloadInfo(ExtPlayerController PC, byte CallID, class<Ext_PerkBase> PerkClass, bool bUnload) { local Ext_PerkBase P; local int LostExp,NewLvl; // Verify if client tries to cause errors. if (PC==None || PerkClass==None || PC.ActivePerkManager==None) return; // Perk unloading disabled on this server. if (MinUnloadPerkLevel==-1) { if (!bUnload) PC.ClientGotUnloadInfo(CallID,0); return; } P = PC.ActivePerkManager.FindPerk(PerkClass); if (P==None) // More client hack attempts. return; if (P.CurrentLevel<MinUnloadPerkLevel) // Verify minimum level. { if (!bUnload) PC.ClientGotUnloadInfo(CallID,1,MinUnloadPerkLevel); return; } // Calc how much EXP is lost on this progress. LostExp = Round(float(P.CurrentEXP) * UnloadPerkExpCost); if (!bUnload) { if (LostExp==0) // Generous server admin! PC.ClientGotUnloadInfo(CallID,2,0,0); else { // Calc how many levels are dropped. NewLvl = P.CalcLevelForExp(P.CurrentEXP-LostExp); PC.ClientGotUnloadInfo(CallID,2,LostExp,P.CurrentLevel-NewLvl); } return; } P.UnloadStats(); P.CurrentEXP -= LostExp; P.SetInitialLevel(); PC.ActivePerkManager.PRIOwner.SetLevelProgress(P.CurrentLevel,P.CurrentPrestige,P.MinimumLevel,P.MaximumLevel); if (PC.Pawn!=None) PC.Pawn.Suicide(); } function ResetPlayerPerk(ExtPlayerController PC, class<Ext_PerkBase> PerkClass, bool bPrestige) { local Ext_PerkBase P; if (bGameHasEnded) return; P = PC.ActivePerkManager.FindPerk(PerkClass); if (P==None || !P.bPerkNetReady) return; if (bPrestige) { if (!P.CanPrestige()) { PC.ClientMessage("Prestige for this perk is not allowed."); return; } ++P.CurrentPrestige; } P.FullReset(bPrestige); } function bool CheckReplacement(Actor Other) { if (bNoBoomstickJumping && KFWeap_Shotgun_DoubleBarrel(Other)!=None) KFWeap_Shotgun_DoubleBarrel(Other).DoubleBarrelKickMomentum = 5.f; return true; } final function InitCustomChars(ExtPlayerReplicationInfo PRI) { PRI.CustomCharList = CustomCharList; } final function bool HasPrivs(ExtPlayerReplicationInfo P) { return WorldInfo.NetMode==NM_StandAlone || (P!=None && P.ShowAdminName() && (P.AdminType<=1 || P.AdminType==255)); } function AdminCommand(ExtPlayerController PC, int PlayerID, int Action) { local ExtPlayerController E; local int i; if (bNoAdminCommands) { PC.ClientMessage("Admin level commands are disabled.",'Priority'); return; } if (!HasPrivs(ExtPlayerReplicationInfo(PC.PlayerReplicationInfo))) { PC.ClientMessage("You do not have enough admin priveleges.",'Priority'); return; } foreach WorldInfo.AllControllers(class'ExtPlayerController',E) if (E.PlayerReplicationInfo.PlayerID==PlayerID) break; if (E==None) { PC.ClientMessage("Action failed, missing playerID: "$PlayerID,'Priority'); return; } if (Action>=100) // Set perk level. { if (E.ActivePerkManager.CurrentPerk==None) { PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); return; } if (Action>=100000) // Set prestige level. { if (E.ActivePerkManager.CurrentPerk.MinLevelForPrestige<0) { PC.ClientMessage("Perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" has prestige disabled!",'Priority'); return; } Action = Min(Action-100000,E.ActivePerkManager.CurrentPerk.MaxPrestige); E.ActivePerkManager.CurrentPerk.CurrentPrestige = Action; PC.ClientMessage("Set "$E.PlayerReplicationInfo.PlayerName$"' perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" prestige level to "$Action,'Priority'); E.ActivePerkManager.CurrentPerk.FullReset(true); } else { Action = Clamp(Action-100,E.ActivePerkManager.CurrentPerk.MinimumLevel,E.ActivePerkManager.CurrentPerk.MaximumLevel); E.ActivePerkManager.CurrentPerk.CurrentEXP = E.ActivePerkManager.CurrentPerk.GetNeededExp(Action-1); PC.ClientMessage("Set "$E.PlayerReplicationInfo.PlayerName$"' perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" level to "$Action,'Priority'); E.ActivePerkManager.CurrentPerk.SetInitialLevel(); E.ActivePerkManager.CurrentPerk.UpdatePRILevel(); } return; } switch (Action) { case 0: // Reset ALL Stats for (i=0; i<E.ActivePerkManager.UserPerks.Length; ++i) E.ActivePerkManager.UserPerks[i].FullReset(); PC.ClientMessage("Reset EVERY perk for "$E.PlayerReplicationInfo.PlayerName,'Priority'); break; case 1: // Reset Current Perk Stats if (E.ActivePerkManager.CurrentPerk!=None) { E.ActivePerkManager.CurrentPerk.FullReset(); PC.ClientMessage("Reset perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 2: // Add 1,000 XP case 3: // Add 10,000 XP case 4: // Advance Perk Level if (E.ActivePerkManager.CurrentPerk!=None) { if (Action==2) i = 1000; else if (Action==3) i = 10000; else i = Max(E.ActivePerkManager.CurrentPerk.NextLevelEXP - E.ActivePerkManager.CurrentPerk.CurrentEXP,0); E.ActivePerkManager.EarnedEXP(i); PC.ClientMessage("Gave "$i$" XP for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 5: // Unload all stats if (E.ActivePerkManager.CurrentPerk!=None) { E.ActivePerkManager.CurrentPerk.UnloadStats(1); PC.ClientMessage("Unloaded all stats for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 6: // Unload all traits if (E.ActivePerkManager.CurrentPerk!=None) { E.ActivePerkManager.CurrentPerk.UnloadStats(2); PC.ClientMessage("Unloaded all traits for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 7: // Remove 1,000 XP case 8: // Remove 10,000 XP if (E.ActivePerkManager.CurrentPerk!=None) { if (Action==6) i = 1000; else i = 10000; E.ActivePerkManager.CurrentPerk.CurrentEXP = Max(E.ActivePerkManager.CurrentPerk.CurrentEXP-i,0); PC.ClientMessage("Removed "$i$" XP from "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 9: // Show Debug Info PC.ClientMessage("DEBUG info for "$E.PlayerReplicationInfo.PlayerName,'Priority'); PC.ClientMessage("PerkManager "$E.ActivePerkManager$" Current Perk: "$E.ActivePerkManager.CurrentPerk,'Priority'); PC.ClientMessage("Perks Count: "$E.ActivePerkManager.UserPerks.Length,'Priority'); for (i=0; i<E.ActivePerkManager.UserPerks.Length; ++i) PC.ClientMessage("Perk "$i$": "$E.ActivePerkManager.UserPerks[i]$" XP:"$E.ActivePerkManager.UserPerks[i].CurrentEXP$" Lv:"$E.ActivePerkManager.UserPerks[i].CurrentLevel$" Rep:"$E.ActivePerkManager.UserPerks[i].bPerkNetReady,'Priority'); break; default: PC.ClientMessage("Unknown admin action.",'Priority'); } } function AdminSetMOTD(ExtPlayerController PC, string S) { if (!HasPrivs(ExtPlayerReplicationInfo(PC.PlayerReplicationInfo))) return; ServerMOTD = S; SaveConfig(); PC.ClientMessage("Message of the Day updated.",'Priority'); } function PlayerChangeSpec(ExtPlayerController PC, bool bSpectator) { if (bSpectator==PC.PlayerReplicationInfo.bOnlySpectator || PC.NextSpectateChange>WorldInfo.TimeSeconds) return; PC.NextSpectateChange = WorldInfo.TimeSeconds+0.5; if (WorldInfo.Game.bGameEnded) PC.ClientMessage("Can't change spectate mode after end-game."); else if (WorldInfo.Game.bWaitingToStartMatch) PC.ClientMessage("Can't change spectate mode before game has started."); else if (WorldInfo.Game.AtCapacity(bSpectator,PC.PlayerReplicationInfo.UniqueId)) PC.ClientMessage("Can't change spectate mode because game is at its maximum capacity."); else if (bSpectator) { PC.NextSpectateChange = WorldInfo.TimeSeconds+2.5; if (PC.PlayerReplicationInfo.Team!=None) PC.PlayerReplicationInfo.Team.RemoveFromTeam(PC); PC.PlayerReplicationInfo.bOnlySpectator = true; if (PC.Pawn!=None) PC.Pawn.KilledBy(None); PC.Reset(); --WorldInfo.Game.NumPlayers; ++WorldInfo.Game.NumSpectators; WorldInfo.Game.Broadcast(PC,PC.PlayerReplicationInfo.GetHumanReadableName()@"became a spectator"); RemoveRespawn(PC); } else { PC.PlayerReplicationInfo.bOnlySpectator = false; if (!WorldInfo.Game.ChangeTeam(PC,WorldInfo.Game.PickTeam(0,PC,PC.PlayerReplicationInfo.UniqueId),false)) { PC.PlayerReplicationInfo.bOnlySpectator = true; PC.ClientMessage("Can't become an active player, failed to set a team."); return; } PC.NextSpectateChange = WorldInfo.TimeSeconds+2.5; ++WorldInfo.Game.NumPlayers; --WorldInfo.Game.NumSpectators; PC.Reset(); WorldInfo.Game.Broadcast(PC,PC.PlayerReplicationInfo.GetHumanReadableName()@"became an active player"); if (bRespawnCheck) CheckRespawn(PC); } } function bool GetNextItem(ExtPlayerReplicationInfo PRI, int RepIndex) { if (RepIndex>=CustomItemList.Length) return false; PRI.ClientAddTraderItem(RepIndex,CustomItemList[RepIndex]); return true; } function InitWebAdmin(ExtWebAdmin_UI UI) { local int i; UI.AddSettingsPage("Main Server Ext",Class,WebConfigs,WebAdminGetValue,WebAdminSetValue); for (i=0; i<LoadedPerks.Length; ++i) LoadedPerks[i].Static.InitWebAdmin(UI); } function string WebAdminGetValue(name PropName, int ElementIndex) { switch (PropName) { case 'StatFileDir': return StatFileDir; case 'ForcedMaxPlayers': return string(ForcedMaxPlayers); case 'PlayerRespawnTime': return string(PlayerRespawnTime); case 'StatAutoSaveWaves': return string(StatAutoSaveWaves); case 'PostGameRespawnCost': return string(PostGameRespawnCost); case 'bKillMessages': return string(bKillMessages); case 'LargeMonsterHP': return string(LargeMonsterHP); case 'bDamageMessages': return string(bDamageMessages); case 'bEnableMapVote': return string(bEnableMapVote); case 'bNoBoomstickJumping': return string(bNoBoomstickJumping); case 'bNoAdminCommands': return string(bNoAdminCommands); case 'bDumpXMLStats': return string(bDumpXMLStats); case 'bRagdollFromFall': return string(bRagdollFromFall); case 'bRagdollFromMomentum': return string(bRagdollFromMomentum); case 'bRagdollFromBackhit': return string(bRagdollFromBackhit); case 'bAddCountryTags': return string(bAddCountryTags); case 'MaxTopPlayers': return string(MaxTopPlayers); case 'MinUnloadPerkLevel': return string(MinUnloadPerkLevel); case 'bDontUseOriginalWeaponry': return string(bDontUseOriginalWeaponry); case 'bDisableCustomTrader': return string(bDisableCustomTrader); case 'bAllowStandartPistolUpgrade': return string(bAllowStandartPistolUpgrade); case 'bDLCWeaponsForFree': return string(bDLCWeaponsForFree); case 'UnloadPerkExpCost': return string(UnloadPerkExpCost); case 'PerkClasses': return (ElementIndex==-1 ? string(PerkClasses.Length) : PerkClasses[ElementIndex]); case 'CustomChars': return (ElementIndex==-1 ? string(CustomChars.Length) : CustomChars[ElementIndex]); case 'AdminCommands': return (ElementIndex==-1 ? string(AdminCommands.Length) : AdminCommands[ElementIndex]); case 'CustomItems': return (ElementIndex==-1 ? string(CustomItems.Length) : CustomItems[ElementIndex]); case 'ServerMOTD': return Repl(ServerMOTD,"|",Chr(10)); case 'BonusGameSongs': return (ElementIndex==-1 ? string(BonusGameSongs.Length) : BonusGameSongs[ElementIndex]); case 'BonusGameFX': return (ElementIndex==-1 ? string(BonusGameFX.Length) : BonusGameFX[ElementIndex]); } } final function UpdateArray(out array<string> Ar, int Index, const out string Value) { if (Value=="#DELETE") Ar.Remove(Index,1); else { if (Index>=Ar.Length) Ar.Length = Index+1; Ar[Index] = Value; } } function WebAdminSetValue(name PropName, int ElementIndex, string Value) { switch (PropName) { case 'StatFileDir': StatFileDir = Value; break; case 'ForcedMaxPlayers': ForcedMaxPlayers = int(Value); break; case 'PlayerRespawnTime': PlayerRespawnTime = int(Value); break; case 'StatAutoSaveWaves': StatAutoSaveWaves = int(Value); break; case 'PostGameRespawnCost': PostGameRespawnCost = int(Value); break; case 'bKillMessages': bKillMessages = bool(Value); break; case 'LargeMonsterHP': LargeMonsterHP = int(Value); break; case 'MinUnloadPerkLevel': MinUnloadPerkLevel = int(Value); break; case 'UnloadPerkExpCost': UnloadPerkExpCost = float(Value); break; case 'bDamageMessages': bDamageMessages = bool(Value); break; case 'bEnableMapVote': bEnableMapVote = bool(Value); break; case 'bNoAdminCommands': bNoAdminCommands = bool(Value); break; case 'bDumpXMLStats': bDumpXMLStats = bool(Value); break; case 'bNoBoomstickJumping': bNoBoomstickJumping = bool(Value); break; case 'bRagdollFromFall': bRagdollFromFall = bool(Value); break; case 'bRagdollFromMomentum': bRagdollFromMomentum = bool(Value); break; case 'bRagdollFromBackhit': bRagdollFromBackhit = bool(Value); break; case 'bDontUseOriginalWeaponry': bDontUseOriginalWeaponry = bool(Value); break; case 'bDisableCustomTrader': bDisableCustomTrader = bool(Value); break; case 'bAllowStandartPistolUpgrade': bAllowStandartPistolUpgrade = bool(Value); break; case 'bDLCWeaponsForFree': bDLCWeaponsForFree = bool(Value); break; case 'bAddCountryTags': bAddCountryTags = bool(Value); break; case 'MaxTopPlayers': MaxTopPlayers = int(Value); break; case 'ServerMOTD': ServerMOTD = Repl(Value,Chr(13)$Chr(10),"|"); break; case 'PerkClasses': UpdateArray(PerkClasses,ElementIndex,Value); break; case 'CustomChars': UpdateArray(CustomChars,ElementIndex,Value); break; case 'AdminCommands': UpdateArray(AdminCommands,ElementIndex,Value); break; case 'CustomItems': UpdateArray(CustomItems,ElementIndex,Value); break; case 'BonusGameSongs': UpdateArray(BonusGameSongs,ElementIndex,Value); break; case 'BonusGameFX': UpdateArray(BonusGameFX,ElementIndex,Value); break; default: return; } SaveConfig(); } defaultproperties { // Main devs DevList.Add("0x0110000100E8984E") // Marco DevList.Add("0x01100001023DF8A8") // ForrestMarkX // Some fixes and changes DevList.Add("0x011000010AF1C7CA") // inklesspen DevList.Add("0x011000010276FBCB") // GenZmeY WebConfigs.Add((PropType=0,PropName="StatFileDir",UIName="Stat File Dir",UIDesc="Location of the stat files on the HDD (%s = unique player ID)")) WebConfigs.Add((PropType=0,PropName="ForcedMaxPlayers",UIName="Server Max Players",UIDesc="A forced max players value of the server (0 = use standard KF2 setting)")) WebConfigs.Add((PropType=0,PropName="PlayerRespawnTime",UIName="Respawn Time",UIDesc="Players respawn time in seconds after they die (0 = no respawning)")) WebConfigs.Add((PropType=0,PropName="PostGameRespawnCost",UIName="Post-Game Respawn Cost",UIDesc="Amount of dosh it'll cost to be respawned after end-game (only for custom gametypes that support this).")) WebConfigs.Add((PropType=0,PropName="StatAutoSaveWaves",UIName="Stat Auto-Save Waves",UIDesc="How often should stats be auto-saved (1 = every wave, 2 = every second wave etc)")) WebConfigs.Add((PropType=0,PropName="MinUnloadPerkLevel",UIName="Min Unload Perk Level",UIDesc="Minimum level a player should be on before they can use the perk stat unload (-1 = never).")) WebConfigs.Add((PropType=0,PropName="UnloadPerkExpCost",UIName="Perk Unload XP Cost",UIDesc="The percent of XP it costs for a player to use a perk unload (1 = all XP, 0 = none).")) WebConfigs.Add((PropType=1,PropName="bKillMessages",UIName="Show Kill Messages",UIDesc="Display on players HUD a kill counter every time they kill something")) WebConfigs.Add((PropType=0,PropName="LargeMonsterHP",UIName="Large Monster HP",UIDesc="If the enemy kill a monster with more HP then this, broadcast kill message to everyone")) WebConfigs.Add((PropType=1,PropName="bDamageMessages",UIName="Show Damage Messages",UIDesc="Display on players HUD a damage counter every time they damage an enemy")) WebConfigs.Add((PropType=1,PropName="bEnableMapVote",UIName="Enable MapVote",UIDesc="Enable MapVote X on this server")) WebConfigs.Add((PropType=1,PropName="bNoBoomstickJumping",UIName="No Boomstick Jumps",UIDesc="Disable boomstick knockback, so people can't glitch with it on maps")) WebConfigs.Add((PropType=1,PropName="bNoAdminCommands",UIName="Disable Admin menu",UIDesc="Disable admin menu commands so admins can't modify XP or levels of players")) WebConfigs.Add((PropType=1,PropName="bDumpXMLStats",UIName="Dump XML stats",UIDesc="Dump XML stat files for some external stat loggers")) WebConfigs.Add((PropType=1,PropName="bRagdollFromFall",UIName="Ragdoll From Fall",UIDesc="Make players ragdoll if they fall from a high place")) WebConfigs.Add((PropType=1,PropName="bRagdollFromMomentum",UIName="Ragdoll From Momentum",UIDesc="Make players ragdoll if they take a damage with high momentum transfer")) WebConfigs.Add((PropType=1,PropName="bRagdollFromBackhit",UIName="Ragdoll From Backhit",UIDesc="Make players ragdoll if they take a big hit to their back")) WebConfigs.Add((PropType=1,PropName="bAddCountryTags",UIName="Add Country Tags",UIDesc="Add player country tags to their names")) WebConfigs.Add((PropType=0,PropName="MaxTopPlayers",UIName="Max top players",UIDesc="Maximum top players to broadcast of and to keep track of.")) WebConfigs.Add((PropType=2,PropName="PerkClasses",UIName="Perk Classes",UIDesc="List of RPG perks players can play as (careful with removing them, because any perks removed will permanently delete the gained XP for every player for that perk)!",NumElements=-1)) WebConfigs.Add((PropType=2,PropName="CustomChars",UIName="Custom Chars",UIDesc="List of custom characters for this server (prefix with * to mark as admin character).",NumElements=-1)) WebConfigs.Add((PropType=2,PropName="AdminCommands",UIName="Admin Commands",UIDesc="List of Admin commands to show on scoreboard UI for admins (use : to split actual command with display name for the command)",NumElements=-1)) WebConfigs.Add((PropType=3,PropName="ServerMOTD",UIName="MOTD",UIDesc="Message of the Day")) WebConfigs.Add((PropType=2,PropName="BonusGameSongs",UIName="Bonus Game Songs",UIDesc="List of custom musics to play during level change pong game.",NumElements=-1)) WebConfigs.Add((PropType=2,PropName="BonusGameFX",UIName="Bonus Game FX",UIDesc="List of custom FX to play on pong game.",NumElements=-1)) WebConfigs.Add((PropType=1,PropName="bDisableCustomTrader",UIName="Disable custom trader",UIDesc="Warning! That option will disable all settings below")) WebConfigs.Add((PropType=2,PropName="CustomItems",UIName="Custom Inventory",UIDesc="List of custom inventory to add to trader (must be KFWeaponDefinition class).",NumElements=-1)) WebConfigs.Add((PropType=1,PropName="bDontUseOriginalWeaponry",UIName="Disable original weapons",UIDesc="Allows to buy default weapons")) WebConfigs.Add((PropType=1,PropName="bDLCWeaponsForFree",UIName="Free DLC weapons",UIDesc="Allows to buy DLC weapons")) WebConfigs.Add((PropType=1,PropName="bAllowStandartPistolUpgrade",UIName="Standard pistol upgrades",UIDesc="Allows to upgrade standard pistol")) }