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

3545 lines
141 KiB
Ucode

/*****************************************************************************
* ScrN Total Game Balance
* @author [ScrN]PooSH, contact via steam: http://steamcommunity.com/id/scrn-poosh/
* Copyright (c) 2012-2018 PU Developing IK, All Rights Reserved.
*****************************************************************************/
class ScrnBalance extends Mutator
Config(ScrnBalanceSrv);
#exec OBJ LOAD FILE=ScrnAnims.ukx
#exec OBJ LOAD FILE=ScrnTex.utx
#exec OBJ LOAD FILE=ScrnAch_T.utx
const VERSION = 96106;
var ScrnBalance Mut; // pointer to self to use in static functions, i.e class'ScrnBalance'.default.Mut
var const string BonusCapGroup;
var localized string strBonusLevel;
var localized string strVersion;
var localized string strStatus, strStatus2;
var localized string strSrvWarning, strSrvWarning2;
var localized string strBetaOnly;
// SRVFLAGS
var transient int SrvFlags; // used for network replication of the values below
var globalconfig bool bSpawn0, bNoStartCashToss, bMedicRewardFromTeam;
var globalconfig bool bAltBurnMech, bDoubleDoT;
var globalconfig bool bReplaceNades, bShieldWeight, bHardcore, bBeta;
var globalconfig bool bShowDamages, bManualReload, bForceManualReload, bAllowWeaponLock;
var globalconfig bool bNoPerkChanges, bPerkChangeBoss, bPerkChangeDead, b10Stars;
VAR globalconfig bool bTraderSpeedBoost;
// END OF SRVFLAGS
var transient byte HardcoreLevel; // set from ScrnGameRules. Used for replication purposes only.
var deprecated byte ReqBalanceMode;
var transient byte SrvReqBalanceMode;
var globalconfig int ForcedMaxPlayers;
var globalconfig int
BonusLevelNormalMax
, BonusLevelHardMin, BonusLevelHardMax
, BonusLevelSuiMin, BonusLevelSuiMax
, BonusLevelHoeMin, BonusLevelHoeMax;
var globalconfig float Post6RequirementScaling, WeldingRequirementScaling, StalkerRequirementScaling;
var transient int MinLevel, MaxLevel;
// Changing default value of variable disables its replication, cuz engine thinks it wasn't changed
var transient int SrvMinLevel, SrvMaxLevel;
var transient bool bInitialized;
var KFGameType KF;
var ScrnGameType ScrnGT;
var bool bStoryMode; // Objective Game mode (KFStoryGameInfo)
var bool bTSCGame; // Team Survival Competition (TSCGame)
struct SPickupReplacement {
var class<Pickup> oldClass;
var class<Pickup> newClass;
};
var array<SPickupReplacement> pickupReplaceArray;
var const int FragReplacementIndex;
var globalconfig bool bReplacePickups, bReplacePickupsStory;
var class<ScrnFunctions> Functions;
var protected byte GameStartCountDown;
//v3
/**
* PerkedWeapons is a list of weapons, which gets bonuses of specified perk
* PerkedAmmo: list of WEAPONS, which gets bonuses for primary ammo (count, mag. size etc.)
* PerkedSecondaryAmmo: list of WEAPONS, which gets bonuses for secondary ammo (count)
*
* All 3 lists have the same format:
* PerkedWeapons|PerkedAmmo|PerkedSecondaryAmmo=<perk index>:<Weapon class name>:<Bonuses>
* , where
* Perk index is an index in Perks array, 0-7.
* weapon class name: class name of custom weapon without package name, e.g. G36CAssaultRifle
* Bonuses: string of bonus letters:
* A - primary ammo bonuses (count, magazine size, discounts)
* B - secondary ammo bonuses
* P - primary fire bonuses (damage, headshot multiplier, fire rate)
* S - secondary fire bonuses
* $ - discount
* D - change primary fire's damage type to perk's default one
* d - change secondary fire's damage type to perk's default one
* * - add to special weapons list
* If bonuses aren't specified, all of them will be applied, except Dd*
*/
var globalconfig array<string> PerkedWeapons, CustomPerks;
//var StringReplicationInfo ClientPerkedWeaponsSRI, ClientPerksSRI;
var array< class<ScrnVeterancyTypes> > Perks;
/*
// removed because array replication doesn't wort in unreal engine 2
struct CustomPerkedClass {
var int PerkIndex;
var class BonusClass;
};
var array<CustomPerkedClass> CustomPerkedClassArray;
*/
var ScrnBurnMech BurnMech; // Alternate Burning Mechanism
// v9.05: replaced ammo spawn behavior.
// Now ammo boxes are spawned only once per wave (at the beginning).
// When picked up, another box will spawn somewhere else on the map (after some cooldown time).
// When wave is near the end (TotalMaxMonsters == 0 and NumMonsters < 10), ammo box count is lowered
// 5 times.
var transient byte PickupSetupMonsters;
var transient bool bPickupSetupReduced;
var ScrnMapInfo MapInfo;
var ScrnGameRules GameRules;
var ScrnSrvReplInfo SrvInfo;
var localized string strAchEarn;
var globalconfig bool bBroadcastAchievementEarn; //tell other players that somebody earned an achievement (excluding map achs)
var globalconfig int AchievementFlags;
var transient int SrvAchievementFlags; // used for network replication
var bool bUseAchievements;
const ACH_ALLFLAGS = 0xFFFFFFFF;
const ACH_ENABLE = 0x0001;
const ACH_HARD = 0x0002;
const ACH_SUI = 0x0004;
const ACH_HOE = 0x0008;
const ACH_SCRNZEDS = 0x0010;
const ACH_WPCZEDS = 0x0020;
const ACH_HARDPAT = 0x0040;
const ACH_DOOM3 = 0x0080;
const ACH_TOURNEY = 0x1000;
var globalconfig bool bSaveStatsOnAchievementEarned; //save stats to serverpeprks database every time an achievement is earned
var transient bool bNeedToSaveStats;
var transient float NextStatSaveTime;
var protected bool bTradingDoorsOpen; // used to check wave start / end
var protected transient byte CurWave; // used to check wave start / end
var ScrnCustomWeaponLink CustomWeaponLink;
var transient float FirstTickTime;
var transient bool bTickExecuted;
var transient bool bInitReplicationReceived;
var Mutator ServerPerksMut;
var transient bool bAllowAlwaysPerkChanges; // value replicated from ServerPerksMut
var globalconfig bool bAllowVoting;
var ScrnBalanceVoting MyVotingOptions;
var globalconfig bool bPauseTraderOnly; //game can be vote-paused in trader time only
var globalconfig float SkippedTradeTimeMult; //how much of the skipped trader time (mvote ENDTRADE) to add to the next one (0 - don't add, 1 - full, 0.5 - half of the skipped time)
var transient int TradeTimeAddSeconds; //amount of seconds to add to the next trader time
var globalconfig bool bAllowBlameVote, bAllowKickVote;
var globalconfig int BlameVoteCoolDown;
var globalconfig bool bAllowPauseVote, bAllowLockPerkVote, bAllowBoringVote;
var globalconfig byte MaxVoteKillMonsters;
var globalconfig int MaxVoteKillHP;
var globalconfig bool bVoteKillCheckVisibility;
var globalconfig float VoteKillPenaltyMult;
var globalconfig byte MinVoteFF, MaxVoteFF;
var globalconfig byte MinVoteDifficulty;
var ScrnBalancePersistence Persistence;
var globalconfig bool bDynamicLevelCap;
var int OriginalMaxLevel;
var globalconfig string ServerPerksPkgName; // if user didn't added SP mut before ScrnBalance - do it for him!
var globalconfig array<string> AutoLoadMutators;
var globalconfig bool bReplaceHUD, bReplaceScoreBoard;
var globalconfig float Post6ZedSpawnInc, Post6AmmoSpawnInc;
var globalconfig float Post6ZedsPerPlayer;
var globalconfig bool bAlterWaveSize;
var globalconfig int MaxWaveSize;
var globalconfig int MaxZombiesOnce;
var globalconfig float EndGameStatBonus;
var globalconfig float FirstStatBonusMult;
var globalconfig bool bStatBonusUsesHL;
var globalconfig int StatBonusMinHL;
var globalconfig int SharpProgMinDmg; // if headshot damage dealth with Sharpshooter's weapon exceeds this, then SS gets +1 to perk progression
var globalconfig bool bBroadcastPickups; // broadcast weapon pickups
var globalconfig String BroadcastPickupText; // broadcast weapon pickups
var protected globalconfig byte EventNum;
var transient byte CurrentEventNum;
var globalconfig bool bForceEvent;
var globalconfig bool bAutoKickOffPerkPlayers;
var localized String strAutoKickOffPerk;
struct SSquadConfig {
var String SquadName;
var string MonsterClass;
var byte NumMonsters;
};
var globalconfig array<SSquadConfig> VoteSquad;
var globalconfig bool bResetSquadsAtStart; // calls ScrnGameRules.ResetSquads() at map start
struct SSquad {
var String SquadName;
var array < class<KFMonster> > Monsters;
};
var array<SSquad> Squads;
// KF.WaveMonsters value after all zeds added by SpawnSquad() are spawned
// if ( SquadSpawnedMonsters > 0 && KF.WaveMonsters < SquadSpawnedMonsters )
// then those zeds are still waiting in spawn queue
var int SquadSpawnedMonsters;
var globalconfig bool bNoRequiredEquipment;
var globalconfig bool bUseExpLevelForSpawnInventory;
var globalconfig array<string> SpawnInventory;
var globalconfig int StartCashNormal, StartCashHard, StartCashSui, StartCashHoE;
var globalconfig int MinRespawnCashNormal, MinRespawnCashHard, MinRespawnCashSui, MinRespawnCashHoE;
var globalconfig int TraderTimeNormal, TraderTimeHard, TraderTimeSui, TraderTimeHoE;
var globalconfig bool bLeaveCashOnDisconnect, bPlayerZEDTime;
struct SColorTag {
var string T;
var byte R, G, B;
};
var array<SColorTag> ColorTags;
var globalconfig string ColoredServerName;
var float OriginalWaveSpawnPeriod;
var globalconfig float MinZedSpawnPeriod;
var globalconfig bool bSpawnRateFix;
var globalconfig bool bServerInfoVeterancy;
var transient array<KFUseTrigger> DoorKeys;
var transient array<KFUseTrigger> DoubleDoorKeys; // keys with at least 2 doors
var StaticMesh AmmoBoxMesh;
var float AmmoBoxDrawScale;
var vector AmmoBoxDrawScale3D;
struct SSpecialPlayers {
var int SteamID32;
var string AvatarRef, ClanIconRef, PreNameIconRef, PostNameIconRef;
var Material Avatar, ClanIcon, PreNameIcon, PostNameIcon;
var Color PrefixIconColor, PostfixIconColor;
var int Playoffs, TourneyWon;
};
var const private array<SSpecialPlayers> HighlyDecorated;
var globalconfig string MySteamID64;
enum ECustomLockType
{
LOCK_Level,
LOCK_Ach,
LOCK_AchGroup
};
var name NameOfString; // used in StringToName()
struct SDLCLock {
var String Item;
var byte Group;
var ECustomLockType Type;
var name ID;
var byte Value;
// non-config stuff
var transient class<Pickup> PickupClass;
};
var globalconfig array<SDLCLock> DLCLocks; // replicated via ScrnClientPerkRepLink
var globalconfig bool bUseDLCLocks,bUseDLCLevelLocks;
var protected transient int LockCount;
var globalconfig bool bBuyPerkedWeaponsOnly, bPickPerkedWeaponsOnly;
struct SNameValuePair {
var name ID;
var int Value;
};
var globalconfig bool bFixMusic;
var globalconfig bool bRespawnDoors;
var transient bool bTeamsLocked; // Set by ScrnGameType. Used for replication purposes.
var globalconfig float LockTeamMinWave, LockTeamAutoWave;
var globalconfig bool bForceSteamNames;
var globalconfig bool bLogObjectsAtMapStart, bLogObjectsAtMapEnd;
// MutateCommands must be in UPPERCASE and sorted
var const array<string> MutateCommands;
enum EMutateCommand
{
MUTATE_ACCURACY,
MUTATE_CHECK,
MUTATE_CMDLINE,
MUTATE_DEBUGGAME,
MUTATE_DEBUGPICKUPS,
MUTATE_DEBUGSPI,
MUTATE_ENEMIES,
MUTATE_GIMMECOOKIES,
MUTATE_HELP,
MUTATE_HL,
MUTATE_LEVEL,
MUTATE_MAPDIFF,
MUTATE_MAPZEDS,
MUTATE_MUTLIST,
MUTATE_PERKSTATS,
MUTATE_PLAYERLIST,
MUTATE_STATUS,
MUTATE_VERSION,
MUTATE_ZED,
MUTATE_ZEDLIST
};
var globalconfig bool bScrnWaves;
// TSC stuff
var globalconfig bool bNoTeamSkins;
// SrvTourneyMode should be used for informative purposes only.
// All real checks must be done server-side only to prevent cheating.
var transient byte SrvTourneyMode;
// END OF TSC STUFF
replication
{
reliable if ( (bNetInitial || bNetDirty) && Role == ROLE_Authority )
SrvMinLevel, SrvMaxLevel, HardcoreLevel, bTeamsLocked;
// flags to replicate config variables
reliable if ( bNetInitial && Role == ROLE_Authority )
SrvFlags, SrvAchievementFlags, SrvReqBalanceMode;
// non-config vars and configs vars which seem to replicate fine
reliable if ( bNetInitial && Role == ROLE_Authority )
CustomWeaponLink, SrvTourneyMode, bTSCGame,
Post6RequirementScaling, WeldingRequirementScaling, StalkerRequirementScaling;
}
// ======================================= FUNCTIONS =======================================
simulated function PostNetReceive()
{
super.PostNetReceive();
if ( bInitReplicationReceived && Role < ROLE_Authority ) {
TimeLog("Additional Settings received from a server.");
ClientInitSettings();
}
}
simulated function PostNetBeginPlay()
{
super.PostNetBeginPlay();
if ( Role < ROLE_Authority ) {
TimeLog("Initial Settings received from a server.");
ClientInitSettings();
bInitReplicationReceived = true;
}
}
// client-side only
simulated function ClientInitSettings()
{
local ScrnPlayerController LocalPlayer;
// setting default settings on server screws up repliction
if ( Role == ROLE_Authority )
return;
Mut = self;
default.Mut = self;
class'ScrnBalance'.default.Mut = self;
bStoryMode = KF_StoryGRI(Level.GRI) != none;
LoadReplicationData();
LocalPlayer = ScrnPlayerController(Level.GetLocalPlayerController());
if ( LocalPlayer != none && LocalPlayer.Mut == none ) {
LocalPlayer.Mut = self;
LocalPlayer.LoadMutSettings();
}
InitSettings();
}
// v8: no more using default settings
// use class'ScrnBalance'.default.Mut.<variable> instead of class'ScrnBalance'.default.<variable>
/*
simulated function ClientInitStaticSettings(class<ScrnBalance> MyBalanceClass)
{
}
*/
// client & server side
simulated function InitSettings()
{
ApplySpawnBalance();
ApplyWeaponFix();
class'ScrnVetSupportSpec'.default.progressArray1[0]=1000.0 * WeldingRequirementScaling;
class'ScrnVetSupportSpec'.default.progressArray1[1]=2000.0 * WeldingRequirementScaling;
class'ScrnVetSupportSpec'.default.progressArray1[2]=7000.0 * WeldingRequirementScaling;
class'ScrnVetSupportSpec'.default.progressArray1[3]=33500.0 * WeldingRequirementScaling;
class'ScrnVetSupportSpec'.default.progressArray1[4]=120000.0 * WeldingRequirementScaling;
class'ScrnVetSupportSpec'.default.progressArray1[5]=250000.0 * WeldingRequirementScaling;
class'ScrnVetSupportSpec'.default.progressArray1[6]=370000.0 * WeldingRequirementScaling;
class'ScrnVetCommando'.default.progressArray0[0]=10.0 * StalkerRequirementScaling;
class'ScrnVetCommando'.default.progressArray0[1]=30.0 * StalkerRequirementScaling;
class'ScrnVetCommando'.default.progressArray0[2]=100.0 * StalkerRequirementScaling;
class'ScrnVetCommando'.default.progressArray0[3]=325.0 * StalkerRequirementScaling;
class'ScrnVetCommando'.default.progressArray0[4]=1200.0 * StalkerRequirementScaling;
class'ScrnVetCommando'.default.progressArray0[5]=2400.0 * StalkerRequirementScaling;
class'ScrnVetCommando'.default.progressArray0[6]=3600.0 * StalkerRequirementScaling;
if (bShieldWeight) {
bReplaceNades = true;
default.bReplaceNades = true;
class'ScrnBalance'.default.bReplaceNades = true;
class'ScrnFrag'.default.Weight = 0;
class'ScrnBalanceSrv.ScrnHumanPawn'.default.StandardVestClass.default.Weight = 1;
}
else {
class'ScrnFrag'.default.Weight = 1;
class'ScrnBalanceSrv.ScrnHumanPawn'.default.StandardVestClass.default.Weight = 0;
}
class'ScrnFragPickup'.default.Weight = class'ScrnFrag'.default.Weight;
RecalcAllPawnWeight();
class'KFMod.CrossbowArrow'.default.DamageRadius = 0; // isn't used anywhere. Set to 0 to fix description
// Achievements
bUseAchievements = bool(AchievementFlags & ACH_ENABLE);
default.bUseAchievements = bUseAchievements;
default.bStoryMode = bStoryMode;
default.bTSCGame = bTSCGame;
// fixes critical bug:
// Assertion failed: inst->KPhysRootIndex != INDEX_NONE && inst->KPhysLastIndex != INDEX_NONE [File:.\KSkeletal.cpp] [Line: 595]
class'FellLava'.default.bSkeletize = false;
// Fix missing textures
class'Welder'.default.TraderInfoTexture = texture(class'Welder'.default.SelectedHudImage);
class'Syringe'.default.TraderInfoTexture = texture(class'Syringe'.default.SelectedHudImage);
EventZedNames();
bInitialized = true;
}
simulated function EventZedNames()
{
class'KFChar.ZombieClot_CIRCUS'.default.MenuName = "Strange Little Clot";
class'KFChar.ZombieBloat_CIRCUS'.default.MenuName = "Pukey the Clown";
class'KFChar.ZombieStalker_CIRCUS'.default.MenuName = "Assistant";
class'KFChar.ZombieCrawler_CIRCUS'.default.MenuName = "Two-Head Girl";
class'KFChar.ZombieGorefast_CIRCUS'.default.MenuName = "Sword Polisher";
class'KFChar.ZombieHusk_CIRCUS'.default.MenuName = "Mecha-Man";
class'KFChar.ZombieSiren_CIRCUS'.default.MenuName = "Bearded Beauty";
class'KFChar.ZombieScrake_CIRCUS'.default.MenuName = "Man Monkey";
class'KFChar.ZombieFleshpound_CIRCUS'.default.MenuName = "Flesh Clown";
class'KFChar.ZombieBoss_CIRCUS'.default.MenuName = "Ring Leader";
class'KFChar.ZombieClot_HALLOWEEN'.default.MenuName = "Honey Biscuit";
class'KFChar.ZombieBloat_HALLOWEEN'.default.MenuName = "Mama Bessie";
class'KFChar.ZombieStalker_HALLOWEEN'.default.MenuName = "Maggie May";
class'KFChar.ZombieCrawler_HALLOWEEN'.default.MenuName = "Half of Uncle Pervis";
class'KFChar.ZombieGorefast_HALLOWEEN'.default.MenuName = "Banjo Chewey";
class'KFChar.ZombieHusk_HALLOWEEN'.default.MenuName = "Brother Sparky";
class'KFChar.ZombieSiren_HALLOWEEN'.default.MenuName = "Granny Crystal May";
class'KFChar.ZombieScrake_HALLOWEEN'.default.MenuName = "Cousin Otis";
class'KFChar.ZombieFleshpound_HALLOWEEN'.default.MenuName = "Bubba";
class'KFChar.ZombieBoss_HALLOWEEN'.default.MenuName = "Sheriff Wade";
class'KFChar.ZombieClot_XMAS'.default.MenuName = "Elf";
class'KFChar.ZombieBloat_XMAS'.default.MenuName = "Fake Santa";
class'KFChar.ZombieStalker_XMAS'.default.MenuName = "Mrs. Claws";
class'KFChar.ZombieCrawler_XMAS'.default.MenuName = "Reindeer";
class'KFChar.ZombieGorefast_XMAS'.default.MenuName = "Gingerfast";
class'KFChar.ZombieHusk_XMAS'.default.MenuName = "Snow Husk";
class'KFChar.ZombieSiren_XMAS'.default.MenuName = "Screaming Tree";
class'KFChar.ZombieScrake_XMAS'.default.MenuName = "Jack Frost";
class'KFChar.ZombieFleshpound_XMAS'.default.MenuName = "Nutcracker";
class'KFChar.ZombieBoss_XMAS'.default.MenuName = "Santriarch";
}
simulated function TimeLog(coerce string s)
{
log("["$Level.TimeSeconds$"s]" @ s, 'ScrnBalance');
}
static function MessageBonusLevel(PlayerController KPC)
{
local String msg;
if ( KPC == none )
return;
msg = default.strBonusLevel;
ReplaceText(msg, "%s", String(class'ScrnBalanceSrv.ScrnVeterancyTypes'.static.GetClientVeteranSkillLevel(KFPlayerReplicationInfo(KPC.PlayerReplicationInfo))));
KPC.ClientMessage(msg);
}
static final function string GetVersionStr()
{
local String msg;
local int v;
local byte major, minor, patch;
msg = default.strVersion;
v = VERSION;
// for some reason, UnrealScript has operator % declared only for float not for int.
// So we can't use % here do to precision
major = v / 10000; v -= major * 10000;
minor = v / 100; v -= minor * 100;
patch = v;
ReplaceText(msg, "%m", string(major));
ReplaceText(msg, "%n", class'ScrnFunctions'.static.LPad(minor, 2, "0"));
ReplaceText(msg, "%p", string(patch));
return msg;
}
static function MessageVersion(PlayerController PC)
{
if ( PC != none )
PC.ClientMessage(default.FriendlyName @ GetVersionStr());
}
function MessageStatus(PlayerController PC)
{
local String msg;
local KFPlayerReplicationInfo KFPRI;
local ClientPerkRepLink R;
local int i, j;
local array<name> WeaponPackages;
local name pkg;
if ( PC == none )
return;
KFPRI = KFPlayerReplicationInfo(PC.PlayerReplicationInfo);
if ( ScrnGT != none && ScrnGT.IsTourney() )
PC.ClientMessage("*** TOURNEY MODE ***", 'Log');
msg = strStatus;
ReplaceText(msg, "%v", String(KFPRI.ClientVeteranSkillLevel));
ReplaceText(msg, "%b", String(class'ScrnBalanceSrv.ScrnVeterancyTypes'.static.GetClientVeteranSkillLevel(KFPRI)));
ReplaceText(msg, "%n", String(MinLevel));
ReplaceText(msg, "%x", String(MaxLevel));
PC.ClientMessage(msg, 'Log');
msg = strStatus2;
ReplaceText(msg, "%a", String(bAltBurnMech));
ReplaceText(msg, "%m", String(KF.MaxZombiesOnce));
PC.ClientMessage(msg, 'Log');
R = SRStatsBase(PC.SteamStatsAndAchievements).Rep;
if ( R != none ) {
msg = "Weapon Packages:";
for ( i=0; i<R.ShopInventory.length; ++i ) {
pkg = R.ShopInventory[i].PC.outer.name;
for ( j=0; j<WeaponPackages.length; ++j ) {
if ( WeaponPackages[j] == pkg ) {
pkg = '';
break;
}
}
if ( pkg != '' ) {
WeaponPackages[WeaponPackages.length] = pkg;
msg @= string(pkg);
}
}
PC.ClientMessage(msg, 'Log');
}
}
//message to clients their effective bonus levels, it they don't match perk level
function BroadcastBonusLevels()
{
local Controller P;
local KFPlayerController Player;
local KFPlayerReplicationInfo KFPRI;
for ( P = Level.ControllerList; P != none; P = P.nextController ) {
if ( !P.bIsPlayer )
continue;
Player = KFPlayerController(P);
if ( Player != none ) {
KFPRI = KFPlayerReplicationInfo(Player.PlayerReplicationInfo);
if (KFPRI.ClientVeteranSkillLevel != class'ScrnBalanceSrv.ScrnVeterancyTypes'.static.GetClientVeteranSkillLevel(KFPRI))
MessageBonusLevel(Player);
}
}
}
function BroadcastMessage(string Msg, optional bool bSaveToLog)
{
local Controller P;
local PlayerController Player;
local name MsgType;
if ( bSaveToLog) {
log(Msg, 'ScrnBalance');
MsgType = 'Log';
}
for ( P = Level.ControllerList; P != none; P = P.nextController ) {
Player = PlayerController(P);
if ( Player != none ) {
Player.ClientMessage(Msg, MsgType);
}
}
}
function SendFriendlyFireWarning(PlayerController Player)
{
if ( !bTSCGame )
Player.ClientMessage(ColorString("FRIENDLY FIRE " $int(KF.FriendlyFireScale*100)$"% !!!", 255, 127, 1));
}
// copy-pasted from ScrnPlayerController for easy access
static final function string ColorString(string s, byte R, byte G, byte B)
{
return chr(27)$chr(max(R,1))$chr(max(G,1))$chr(max(B,1))$s;
}
static final function string ColorStringC(string s, color c)
{
return chr(27)$chr(max(c.R,1))$chr(max(c.G,1))$chr(max(c.B,1))$s;
}
static final function string StripColor(string s)
{
local int p;
p = InStr(s,chr(27));
while ( p>=0 )
{
s = left(s,p)$mid(S,p+4);
p = InStr(s,Chr(27));
}
return s;
}
// returns first i amount of characters excluding escape color codes
static final function string LeftCol(string ColoredString, int i)
{
local string s;
local int p, c;
if ( Len(ColoredString) <= i )
return ColoredString;
c = i;
s = ColoredString;
p = InStr(s,chr(27));
while ( p >=0 && p < i ) {
c+=4; // add 4 more characters due to color code
s = left(s, p) $ mid(s, p+4);
p = InStr(s,Chr(27));
}
return Left(ColoredString, c);
}
simulated function string ParseColorTags(string ColoredText, optional PlayerReplicationInfo PRI)
{
local int i;
local string s;
s = ColoredText;
if ( PRI != none && PRI.Team != none )
ReplaceText(s, "^t", ColorStringC("", class'ScrnHUD'.default.TextColors[PRI.Team.TeamIndex]));
else
ReplaceText(s, "^t", "");
if ( KFPlayerReplicationInfo(PRI) != none )
ReplaceText(s, "^p", ColorStringC("", class'ScrnHUD'.static.PerkColor(KFPlayerReplicationInfo(PRI).ClientVeteranSkillLevel)));
else
ReplaceText(s, "^p", "");
for ( i=0; i<ColorTags.Length; ++i ) {
ReplaceText(s, ColorTags[i].T, ColorString("",
ColorTags[i].R, ColorTags[i].G, ColorTags[i].B));
}
return s;
}
simulated function string StripColorTags(string ColoredText)
{
local int i;
local string s;
s = ColoredText;
ReplaceText(s, "^p", "");
ReplaceText(s, "^t", "");
for ( i=0; i<ColorTags.Length; ++i ) {
ReplaceText(s, ColorTags[i].T, "");
}
return s;
}
simulated function string ColoredPlayerName(PlayerReplicationInfo PRI)
{
if ( PRI == none )
return "";
return ParseColorTags(PRI.PlayerName, PRI);
}
function StolenWeapon(Pawn NewOwner, KFWeaponPickup WP)
{
local string str;
str = BroadcastPickupText;
ReplaceText(str, "%p", ColorString(ParseColorTags(NewOwner.GetHumanReadableName(), NewOwner.PlayerReplicationInfo), 192, 1, 1) $ ColorString("", 192, 192, 192));
ReplaceText(str, "%o", ColorString(ColoredPlayerName(WP.DroppedBy.PlayerReplicationInfo), 1, 192, 1) $ ColorString("", 192, 192, 192));
ReplaceText(str, "%w", ColorString(WP.ItemName, 1, 96, 192) $ ColorString("", 192, 192, 192));
ReplaceText(str, "%$", ColorString(String(WP.SellValue), 192, 192, 1) $ ColorString("", 192, 192, 192));
BroadcastMessage(str);
}
//recalculate
function RecalcAllPawnWeight()
{
local ScrnHumanPawn P;
foreach DynamicActors(class'ScrnBalanceSrv.ScrnHumanPawn', P)
P.RecalcWeight();
}
function WelcomeMessage(PlayerController PC)
{
if ( PC != none ) {
MessageVersion(PC);
MessageStatus(PC);
if ( (Level.NetMode == NM_Standalone || Level.NetMode == NM_ListenServer) && class.outer.name == 'ScrnBalanceSrv' ) {
PC.ClientMessage(strSrvWarning);
PC.ClientMessage(strSrvWarning2);
}
}
}
// Setup the random ammo pickups
function SetupPickups(optional bool bReduceAmount, optional bool bBoostAmount)
{
local float W, A; //chance of spawning weapon / ammo box
local bool bSpawned;
local int i;
// randomize remaining monster count, when pickups are reset to avoid players exploiting this
// knowledge
PickupSetupMonsters = 10 + rand(10);
bPickupSetupReduced = bReduceAmount;
// Except the beginner, where all pickups are still spawned
if ( KF.GameDifficulty < 2 ) {
for ( i = 0; i < KF.WeaponPickups.Length ; i++ )
KF.WeaponPickups[i].EnableMe();
for ( i = 0; i < KF.AmmoPickups.Length ; i++ )
KF.AmmoPickups[i].GotoState('Pickup');
return;
}
// Randomize Available Ammo Pickups
if ( bReduceAmount ) {
W = 0.10;
A = 0.10;
}
else if ( bBoostAmount ) {
W = 0.50;
A = 0.70;
}
else if ( KF.GameDifficulty >= 5.0 ) {
// Suicidal and Hell on Earth
W = 0.15;
A = 0.35;
}
else if ( KF.GameDifficulty >= 4.0 ) {
// Hard
W = 0.25;
A = 0.50;
}
else {
// Normal
W = 0.35;
A = 0.50;
}
if ( KF.NumPlayers > 6 ) {
A *= 1.0 + float(KF.NumPlayers - 6)*Post6AmmoSpawnInc;
}
if ( KF.WeaponPickups.Length > 0 ) {
for ( i = 0; i < KF.WeaponPickups.Length ; i++ )
{
if ( frand() < W ) {
if ( !KF.WeaponPickups[i].bIsEnabledNow )
KF.WeaponPickups[i].EnableMe();
bSpawned = true;
}
else if ( KF.WeaponPickups[i].bIsEnabledNow )
KF.WeaponPickups[i].DisableMe();
}
if ( !bSpawned )
KF.WeaponPickups[rand(KF.WeaponPickups.Length)].EnableMe();
}
AdjustAmmoBoxCount(ceil(A * KF.AmmoPickups.Length));
}
function AdjustAmmoBoxCount(int DesiredAmmoBoxCount)
{
local int i;
local array<KFAmmoPickup> AvailableAmmoBoxes;
local int CurrentAmmoBoxCount;
for ( i = 0; i < KF.AmmoPickups.Length ; i++ ) {
if ( !KF.AmmoPickups[i].bSleeping )
++CurrentAmmoBoxCount;
}
if ( CurrentAmmoBoxCount < DesiredAmmoBoxCount ) {
// not enough ammo on the map - spawn more
for ( i = 0; i < KF.AmmoPickups.Length ; i++ ) {
if ( KF.AmmoPickups[i].bSleeping )
AvailableAmmoBoxes[AvailableAmmoBoxes.Length] = KF.AmmoPickups[i];
}
while ( CurrentAmmoBoxCount < DesiredAmmoBoxCount && AvailableAmmoBoxes.Length > 0 ) {
i = rand(AvailableAmmoBoxes.Length);
AvailableAmmoBoxes[i].GotoState('Pickup');
AvailableAmmoBoxes.remove(i, 1);
++CurrentAmmoBoxCount;
}
}
else if ( CurrentAmmoBoxCount > DesiredAmmoBoxCount ) {
// too many ammo boxes - remove those which are not seen by players
for ( i = 0; i < KF.AmmoPickups.Length ; i++ ) {
if ( !KF.AmmoPickups[i].bSleeping && !KF.AmmoPickups[i].PlayerCanSeeMe() )
AvailableAmmoBoxes[AvailableAmmoBoxes.Length] = KF.AmmoPickups[i];
}
while ( CurrentAmmoBoxCount > DesiredAmmoBoxCount && AvailableAmmoBoxes.Length > 0 ) {
i = rand(AvailableAmmoBoxes.Length);
AvailableAmmoBoxes[i].GotoState('Sleeping', 'Begin');
AvailableAmmoBoxes.remove(i, 1);
--CurrentAmmoBoxCount;
}
}
if ( ScrnGT != none ) {
ScrnGT.DesiredAmmoBoxCount = DesiredAmmoBoxCount;
}
}
function MessagePickups(PlayerController Sender)
{
local int i, a, w;
local String msg;
a = KF.AmmoPickups.length;
for ( i = 0; i < KF.AmmoPickups.length; ++i ) {
if ( KF.AmmoPickups[i].bSleeping )
--a;
}
w = KF.WeaponPickups.length;
for ( i = 0; i < KF.WeaponPickups.length; ++i ) {
if ( !KF.WeaponPickups[i].bIsEnabledNow )
--w;
}
msg = "Ammo boxes Spawned/Total: " $ a $ "/" $ KF.AmmoPickups.length;
if ( ScrnGT != none ) {
msg $= "; Current/Desired: " $ ScrnGT.CurrentAmmoBoxCount $ "/" $ ScrnGT.DesiredAmmoBoxCount;
msg $= "; In queue: " $ ScrnGT.SleepingAmmo.length;
}
msg $= ". Weapons Spawned/Total: " $ w $ "/" $ KF.WeaponPickups.length;
Sender.ClientMessage(msg);
}
function ForceMaxPlayers()
{
if ( ForcedMaxPlayers > 0 && ForcedMaxPlayers != Level.Game.MaxPlayers ) {
Log("Forcing server max players from " $ Level.Game.MaxPlayers $ " to " $ ForcedMaxPlayers,'ScrnBalance');
Level.Game.MaxPlayers = ForcedMaxPlayers;
Level.Game.Default.MaxPlayers = ForcedMaxPlayers;
}
}
function AchievementEarned(ScrnAchievements AchHandler, int AchIndex)
{
if ( bSaveStatsOnAchievementEarned && AchHandler.AchDefs[AchIndex].bUnlockedJustNow )
bNeedToSaveStats = true;
NextStatSaveTime = Level.TimeSeconds + 5; // wait a bit - maybe other achievements will be earned soon
if ( bBroadcastAchievementEarn )
BroadcastAchEarn(AchHandler, AchIndex);
}
function BroadcastAchEarn(ScrnAchievements AchHandler, int AchIndex)
{
local Controller C;
local ScrnPlayerController ScrnPlayer;
local string s;
if ( PlayerController(AchHandler.Owner) == none || PlayerController(AchHandler.Owner).PlayerReplicationInfo == none )
return;
s = strAchEarn;
ReplaceText(s, "%p", PlayerController(AchHandler.Owner).PlayerReplicationInfo.PlayerName);
ReplaceText(s, "%a", AchHandler.AchDefs[AchIndex].DisplayName);
for (C = Level.ControllerList; C != None; C = C.NextController) {
if ( !C.bIsPlayer )
continue;
ScrnPlayer = ScrnPlayerController(C);
if ( ScrnPlayer != None && ScrnPlayer != AchHandler.Owner ) {
ScrnPlayer.ClientMessage(ColorString(s, 1, 150, 255));
}
}
}
function BroadcastFakedAchievement(int AchIndex)
{
local Controller C;
for (C = Level.ControllerList; C != None; C = C.NextController) {
if ( !C.bIsPlayer )
continue;
if ( PlayerController(C) != none)
PlayerController(C).ReceiveLocalizedMessage(class'ScrnBalanceSrv.ScrnFakedAchMsg', AchIndex);
}
}
function Mutator FindServerPerksMut()
{
local Mutator M;
if ( ServerPerksMut != none )
return ServerPerksMut;
for ( M = KF.BaseMutator; M != None; M = M.NextMutator ) {
if ( M.IsA('ServerPerksMut') ) {
ServerPerksMut = M;
break;
}
}
if ( ServerPerksMut == none ) {
log("ServerPerksMut not found!", 'ScrnBalance');
}
return ServerPerksMut;
}
// do everything possible to find ScrnBalance mutator
static final function ScrnBalance Myself(LevelInfo Level)
{
local Mutator M;
local ScrnBalance SBM;
if ( default.Mut != none )
return default.Mut;
// shouldn't reach here
// server-side
if ( Level.Game != none ) {
for ( M = Level.Game.BaseMutator; M != None; M = M.NextMutator ) {
SBM = ScrnBalance(M);
if ( SBM != none ) {
default.Mut = SBM;
return SBM;
}
}
}
// clien-side
foreach Level.DynamicActors(class'ScrnBalance', SBM) {
default.Mut = SBM;
return SBM;
}
log("Unable to find myself :(", 'ScrnBalance');
return none;
}
final function GetRidOfMut(name MutatorName)
{
local Mutator M;
for ( M = KF.BaseMutator; M != None; M = M.NextMutator ) {
if ( M.IsA(MutatorName) ) {
M.Destroy();
return;
}
}
}
//can't statically link to ServerPerksMut, cuz it is server-side only
function SaveStats()
{
bNeedToSaveStats = false;
if ( ServerPerksMut == none )
FindServerPerksMut();
if ( ServerPerksMut == none )
return;
if ( Level.Game.bGameEnded ) {
// v8.18 - do nothing, Serverperks will save game itself
// GotoState('StatsSaving');
// goto EndGameTracker state again to force save perks
// doing so will increment win/loose counter extra time - need to contact Marco about this
}
else {
// adjust variables of ServerPerksMut to force saving
ServerPerksMut.SetPropertyText("WaveCounter", "100");
ServerPerksMut.SetPropertyText("LastSavedWave", "-1");
}
}
/*
state StatsSaving
{
Begin:
log("Saving stats...", 'ScrnBalance');
ServerPerksMut.GotoState('');
sleep(1.0);
if ( ServerPerksMut.IsInState('EndGameTracker') ) {
log("Can't save stats!", 'ScrnBalance');
}
else {
ServerPerksMut.GotoState('EndGameTracker');
}
GotoState('');
}
*/
function DynamicLevelCap()
{
local int num, m;
if ( !bDynamicLevelCap )
return;
m = OriginalMaxLevel;
if ( bTSCGame )
num = max(KF.Teams[0].Size, KF.Teams[1].Size);
else
num = KF.NumPlayers;
if (num > 6)
m += num-6; //extra level per player
if ( MaxLevel != m) {
MaxLevel = m;
default.MaxLevel = m;
SrvMaxLevel = m; //send value to clients
}
}
// kick players, who have no perk
function KickOffPerkPlayers()
{
local Controller C;
local KFPlayerController KFPC;
local KFPlayerReplicationInfo KFPRI;
for ( C = Level.ControllerList; C != none; C = C.nextController ) {
if ( !C.bIsPlayer )
continue;
KFPC = KFPlayerController(C);
KFPRI = KFPlayerReplicationInfo(C.PlayerReplicationInfo);
if ( KFPC != none && KFPC.Pawn != none && NetConnection(KFPC.Player)!=None
&& KFPRI != none && KFPRI.ClientVeteranSkill == none
&& !KFPRI.bOnlySpectator && !KFPRI.bIsSpectator )
{
KFPC.ClientNetworkMessage("AC_Kicked", strAutoKickOffPerk);
if ( KFPC.Pawn != none && Vehicle(KFPC.Pawn) == none )
KFPC.Pawn.Destroy();
KFPC.Destroy();
}
}
}
// executes each second while match is in progress
function GameTimer()
{
if ( bStoryMode ) {
KF.bWaveInProgress = !KFStoryGameInfo(KF).IsTraderTime();
KFGameReplicationInfo(Level.GRI).bWaveInProgress = KF.bWaveInProgress;
}
// check for wave start/end
if ( bTradingDoorsOpen != KF.bTradingDoorsOpen )
{
bTradingDoorsOpen = KF.bTradingDoorsOpen;
if ( bTradingDoorsOpen ) {
// Trader Time
if ( bStoryMode && KF.WaveNum == CurWave ) {
// seems like story game doesn't increment wave counter
KF.WaveNum++;
}
GameRules.WaveEnded();
KF.WaveCountDown += TradeTimeAddSeconds;
TradeTimeAddSeconds = 0;
}
else {
// Wave in Progress
CurWave = KF.WaveNum;
if ( bAutoKickOffPerkPlayers )
KickOffPerkPlayers();
SquadSpawnedMonsters = 0;
GameRules.WaveStarted();
if ( MyVotingOptions != none && MyVotingOptions.VotingHandler.IsMyVotingRunning(MyVotingOptions, MyVotingOptions.VOTE_ENDTRADE) )
MyVotingOptions.VotingHandler.VoteFailed();
DestroyExtraPipebombs();
// call SetupPickups only when playing non-ScrnGameType mode.
// ScrnGameType automatically calls SetupPickups() during wave begin.
if ( ScrnGT == none && !bStoryMode )
SetupPickups(false);
}
if ( bDynamicLevelCap )
DynamicLevelCap();
}
if ( !bStoryMode ) {
CheckDoors();
if ( !bPickupSetupReduced && KF.TotalMaxMonsters <= 0 && KF.NumMonsters < PickupSetupMonsters )
SetupPickups(true);
}
}
function Timer()
{
// todo - talk to Marco about forcing game saving
if ( bNeedToSaveStats && Level.TimeSeconds > NextStatSaveTime ) {
SaveStats();
}
if ( KF.IsInState('MatchInProgress') ) {
if ( GameStartCountDown > 0)
GameStartCountDown--; // wait for 10 seconds for game to be started and set up first wave
else
GameTimer();
}
}
function FixMusic()
{
if ( KF.MapSongHandler == none )
KF.MapSongHandler = spawn(class'ScrnMusicTrigger');
// Allow L.D. to set boss battle song.
// Don't use Dirge music as boss battle, because default KF_Abandon better.
if ( left(KF.MonsterCollection.default.EndGameBossClass,
InStr(KF.MonsterCollection.default.EndGameBossClass,".")) ~= "ScrnDoom3KF" )
KF.BossBattleSong = "EGT-SignOfEvil"; // try this. If client doesn't have that song, then KF_Abandon will be played
else if ( KF.MapSongHandler.WaveBasedSongs.Length > 10 && KF.MapSongHandler.WaveBasedSongs[10].CombatSong != ""
&& !(left(KF.MapSongHandler.WaveBasedSongs[10].CombatSong, 5) ~= "Dirge") )
KF.BossBattleSong = KF.MapSongHandler.WaveBasedSongs[10].CombatSong;
}
simulated function Tick( float DeltaTime )
{
if ( !bTickExecuted ) {
bTickExecuted = true;
FirstTickTime = Level.TimeSeconds;
}
if ( Role == ROLE_Authority ) {
OriginalWaveSpawnPeriod = KF.KFLRules.WaveSpawnPeriod;
if ( OriginalWaveSpawnPeriod < MinZedSpawnPeriod ) {
OriginalWaveSpawnPeriod = MinZedSpawnPeriod;
KF.KFLRules.WaveSpawnPeriod = MinZedSpawnPeriod;
}
ForceMaxPlayers();
if ( !bStoryMode ) {
if ( ScrnGT != none ) {
ScrnGT.CheckZedSpawnList();
}
InitDoors();
SetStartCash();
if ( bFixMusic )
FixMusic();
}
if ( bTSCGame ) {
Level.GRI.bNoTeamSkins = bNoTeamSkins && !ScrnGT.IsTourney();
}
if ( ColoredServerName != "" ) {
Level.GRI.ServerName = ParseColorTags(ColoredServerName);
}
SetTimer(1, true);
Disable('Tick');
}
else if ( bInitialized ) {
Disable('Tick');
}
else if ( Level.TimeSeconds - FirstTickTime > 5 ) {
// this shouldn't happen
Timelog("Settings receiving timeout failed - initializing with default settings");
InitSettings();
Disable('Tick');
}
}
function SetStartCash()
{
local Controller C;
if ( KF.GameDifficulty >= 7 ) // HoE
{
KF.TimeBetweenWaves = TraderTimeHoE;
KF.StartingCash = StartCashHoE;
KF.MinRespawnCash = MinRespawnCashHoE;
}
else if (KF.GameDifficulty >= 5 ) // Suicidal
{
KF.TimeBetweenWaves = TraderTimeSui;
KF.StartingCash = StartCashSui;
KF.MinRespawnCash = MinRespawnCashSui;
}
else if ( KF.GameDifficulty >= 4 ) // Hard
{
KF.TimeBetweenWaves = TraderTimeHard;
KF.StartingCash = StartCashHard;
KF.MinRespawnCash = MinRespawnCashHard;
}
else
{
KF.TimeBetweenWaves = TraderTimeNormal;
KF.StartingCash = StartCashNormal;
KF.MinRespawnCash = MinRespawnCashNormal;
}
for ( C = Level.ControllerList; C != none; C = C.nextController ) {
if ( C.PlayerReplicationInfo != none ) {
if ( ScrnGT != none && PlayerController(C) != none )
ScrnGT.GiveStartingCash(PlayerController(C));
else
C.PlayerReplicationInfo.Score = KF.StartingCash;
}
}
}
function DebugArmor(PlayerController Sender)
{
local Controller P;
local ScrnHumanPawn ScrnPawn;
for ( P = Level.ControllerList; P != none; P = P.nextController ) {
ScrnPawn = ScrnHumanPawn(P.Pawn);
if ( ScrnPawn != none ) {
Sender.ClientMessage(ScrnPawn.PlayerReplicationInfo.PlayerName @ GetItemName(String(ScrnPawn.GetCurrentVestClass())) @ int(ScrnPawn.ShieldStrength)$"/"$int(ScrnPawn.ShieldStrengthMax));
}
}
}
function MessagePerkStats(PlayerController Sender)
{
local ScrnPlayerInfo SPI;
SPI = GameRules.GetPlayerInfo(Sender);
if ( SPI == none )
Sender.ClientMessage("No player info record found");
else {
Sender.ClientMessage(ColorString("Initial Perk Stats: " $ SPI.PerkStatStr(SPI.GameStartStats), 255, 1, 200));
Sender.ClientMessage(ColorString("Perk Progression: " $ SPI.PerkProgressStr(SPI.GameStartStats), 255, 1, 200));
}
}
// returns number of zeds, which have Other as enemy
function MsgEnemies(PlayerController Sender)
{
local Controller C;
local PlayerController PC;
local array<PlayerController> Players;
local array<int> Enemies;
local int i;
for ( C = Level.ControllerList; C != none; C = C.nextController ) {
if ( !C.bIsPlayer && C.Enemy != none ) {
PC = PlayerController(C.Enemy.Controller);
if ( PC != none ) {
for ( i=0; i<Players.length; ++i ) {
if ( Players[i] == PC )
break;
}
if ( i == Players.length ) {
Players[i] = PC;
Enemies[i] = 1;
}
else
Enemies[i] = Enemies[i] + 1;
}
}
}
for ( i=0; i<Players.length; ++i ) {
Sender.ClientMessage(Enemies[i] $ " enemy(-ies) vs " $ Players[i].PlayerReplicationInfo.PlayerName );
}
}
/** Splits long message on short ones before sending it to client.
* @param Sender Player, who will receive message(-s).
* @param S String to send.
* @param MaxLen Max length of one string. Default: 80. If S is longer than this value,
* then it will be splitted on serveral messages.
* @param Divider Character to be used as divider. Default: Space. String is splitted
* at last divder's position before MaxLen is reached.
*/
static function LongMessage(PlayerController Sender, string S, optional int MaxLen, optional string Divider)
{
local int pos;
local string part;
if ( Sender == none )
return;
if ( MaxLen == 0 )
MaxLen = 80;
if ( Divider == "" )
Divider = " ";
while ( len(part) + len(S) > MaxLen ) {
pos = InStr(S, Divider);
if ( pos == -1 )
break; // no more dividers
if ( part != "" && len(part) + pos + 1 > MaxLen) {
Sender.ClientMessage(part);
part = "";
}
part $= Left(S, pos + 1);
S = Mid(S, pos+1);
}
part $= S;
if ( part != "" )
Sender.ClientMessage(part);
}
function bool CheckAdmin(PlayerController Sender)
{
if ( (Sender.PlayerReplicationInfo != none && Sender.PlayerReplicationInfo.bAdmin)
|| Level.NetMode == NM_Standalone || Level.NetMode == NM_ListenServer )
return true;
Sender.ClientMessage("Requires ADMIN priviledges");
return false;
}
function bool CheckScrnGT(PlayerController Sender)
{
if ( ScrnGT != none )
return true;
Sender.ClientMessage("Avaliable in ScrnGameType only!");
return false;
}
function bool CheckNotTourney(PlayerController Sender)
{
if ( ScrnGT == none || !ScrnGT.IsTourney() )
return true;
Sender.ClientMessage("Not Available in TOURNEY mode");
return false;
}
function Mutate(string MutateString, PlayerController Sender)
{
local string Value;
local int cmd;
if ( MutateString == "" )
return;
super.Mutate(MutateString, Sender);
Divide(MutateString, " ", MutateString, Value);
MutateString = caps(MutateString);
cmd = BinarySearchStr(MutateCommands, MutateString);
if ( cmd == -1 )
return; //unknown command
switch ( EMutateCommand(cmd) ) {
case MUTATE_ACCURACY:
SendAccuracy(Sender);
break;
case MUTATE_CHECK:
Sender.ClientMessage(FriendlyName);
break;
case MUTATE_CMDLINE:
if ( CheckAdmin(Sender) && CheckScrnGT(Sender) )
LongMessage(Sender, ScrnGT.GetCmdLine(), 80, "?");
break;
case MUTATE_DEBUGGAME:
if ( CheckAdmin(Sender) )
Sender.ClientMessage("Game=" $ KF.class.name
@ "EventNum=" $ CurrentEventNum
@ "MonsterCollection=" $ KF.MonsterCollection
@ "Boss=" $ KF.MonsterCollection.default.EndGameBossClass);
break;
case MUTATE_DEBUGPICKUPS:
MessagePickups(Sender);
break;
case MUTATE_DEBUGSPI:
GameRules.DebugSPI(Sender);
break;
case MUTATE_ENEMIES:
if ( CheckAdmin(Sender) )
MsgEnemies(Sender);
break;
case MUTATE_GIMMECOOKIES:
XPBoost(Sender, 'TSCT', 6);
break;
case MUTATE_HELP:
MessageMutateCommands(Sender);
break;
case MUTATE_HL:
Sender.ClientMessage("Hardcore Level = " $ GameRules.HardcoreLevel);
break;
case MUTATE_LEVEL:
MessageBonusLevel(Sender);
break;
case MUTATE_MAPDIFF:
if ( CheckAdmin(Sender) )
MapInfo.SetMapDifficulty(Value, Sender);
break;
case MUTATE_MAPZEDS:
if ( CheckAdmin(Sender) && CheckNotTourney(Sender) ) {
if ( MapInfo.SetMapZeds(int(Value), Sender) ) {
SetMaxZombiesOnce();
BroadcastMessage("Max zeds at once set to " $ KF.MaxZombiesOnce);
}
}
break;
case MUTATE_MUTLIST:
Sender.ClientMessage(MutatorList());
break;
case MUTATE_PERKSTATS:
MessagePerkStats(Sender);
break;
case MUTATE_PLAYERLIST:
SendPlayerList(Sender);
break;
case MUTATE_STATUS:
MessageStatus(Sender);
break;
case MUTATE_VERSION:
MessageVersion(Sender);
break;
case MUTATE_ZED:
if ( ScrnGT == none || ScrnGT.ScrnGameLength == none )
Sender.ClientMessage("Avaliable only in ScrnGameType + bScrnWaves");
else if ( CheckAdmin(Sender) ) {
ScrnGT.ScrnGameLength.ZedCmd(Sender, Value);
}
break;
case MUTATE_ZEDLIST:
if ( ScrnGT == none || ScrnGT.ScrnGameLength == none )
SendZedList(Sender);
else
ScrnGT.ScrnGameLength.ZedCmd(Sender, "LIST");
break;
}
}
// for debug purposes only
/*
private final function DebugDoors(PlayerController Sender)
{
local KFDoorMover door;
local Inventory I;
local int c;
foreach DynamicActors(class'KFDoorMover', door)
door.RespawnDoor();
if ( Sender.Pawn != none ) {
for ( I = Sender.Pawn.Inventory; I != none; I = I.Inventory ) {
if ( Single(I) != none ) {
KFFire(Single(I).GetFireMode(0)).DamageType = class'DamTypeFrag';
KFFire(Single(I).GetFireMode(0)).DamageMax = 1000;
return;
}
if ( ++c > 1000 )
return; //circular link prevention
}
}
}
*/
function MessageMutateCommands(PlayerController Sender)
{
local int i;
Sender.ClientMessage("ScrN Mutate Commands");
Sender.ClientMessage("====================");
for ( i = 0; i < MutateCommands.length; ++i ) {
Sender.ClientMessage(MutateCommands[i]);
}
}
function XPBoost(PlayerController Sender, name Achievement, byte Level)
{
local ScrnCustomPRI ScrnPRI;
local SRStatsBase SteamStats;
local ScrnPlayerInfo SPI;
local SRCustomProgress S;
local int v;
ScrnPRI = class'ScrnCustomPRI'.static.FindMe(Sender.PlayerReplicationInfo);
if ( ScrnPRI == none || !ScrnPRI.IsTourneyMember() )
return;
SteamStats = SRStatsBase(Sender.SteamStatsAndAchievements);
SPI = GameRules.GetPlayerInfo(Sender);
if ( SPI == none || SteamStats == none ) {
Sender.ClientMessage("Player achievements are not loaded yet. Try again later.");
return;
}
// ProgressAchievement() returns true if progress has been made, i.e.
// achievement wasn't achieved before
if ( Achievement == '' || SPI.ProgressAchievement(Achievement, 1) ) {
SteamStats.AddDamageHealed(class'ScrnVetFieldMedic'.default.progressArray0[Level]);
SteamStats.AddShotgunDamage(class'ScrnVetSupportSpec'.default.progressArray0[Level]);
SteamStats.AddWeldingPoints(class'ScrnVetSupportSpec'.default.progressArray1[Level]);
v = class'ScrnVetSharpshooter'.default.progressArray0[Level];
while ( v-- > 0 )
SteamStats.AddHeadshotKill(false);
v = class'ScrnVetCommando'.default.progressArray0[Level];
while ( v-- > 0 )
SteamStats.AddStalkerKill();
SteamStats.AddBullpupDamage(class'ScrnVetCommando'.default.progressArray1[Level]);
SteamStats.AddMeleeDamage(class'ScrnVetBerserker'.default.progressArray0[Level]);
SteamStats.AddFlameThrowerDamage(class'ScrnVetFirebug'.default.progressArray0[Level]);
SteamStats.AddExplosivesDamage(class'ScrnVetDemolitions'.default.progressArray0[Level]);
for ( S = SteamStats.Rep.CustomLink; S!=none; S=S.NextLink ) {
if ( ScrnPistolKillProgress(S) != none )
S.IncrementProgress(class'ScrnVetGunslinger'.default.progressArray0[Level]);
else if ( ScrnPistolDamageProgress(S) != none )
S.IncrementProgress(class'ScrnVetGunslinger'.default.progressArray1[Level]);
}
SaveStats(); // for ServerPerks to write stats
// ensure that xp boost won't be multiplied by end-game bonus
SPI.GameStartStats.bSet = false;
SPI.BackupStats(SPI.GameStartStats);
}
}
function string MutatorList()
{
local Mutator M;
local string result;
result = string(KF.BaseMutator.class);
for ( M = KF.BaseMutator.NextMutator; M != None; M = M.NextMutator ) {
result $= ","$string(M.class);
}
return result;
}
function SendZedList(PlayerController Sender)
{
local int i, j, k, NumZeds;
local string str, ZedClass;
Sender.ClientMessage("Collection: " $ KF.MonsterCollection);
Sender.ClientMessage("Boss: " $ KF.MonsterCollection.default.EndGameBossClass);
for ( k=0; k< KF.MonsterCollection.default.MonsterClasses.Length; ++k ) {
Sender.ClientMessage(
ColorString(KF.MonsterCollection.default.MonsterClasses[k].MID, 1, 100, 200)
$ ColorString(": "$KF.MonsterCollection.default.MonsterClasses[k].MClassName, 200, 200, 200));
}
Sender.ClientMessage("SpecialSquads:");
for ( i=0; i< KF.MonsterCollection.default.SpecialSquads.Length; ++i ) {
if ( KF.MonsterCollection.default.SpecialSquads[i].ZedClass.Length > 0 ) {
str = "";
for( j=0; j<KF.MonsterCollection.default.SpecialSquads[i].ZedClass.Length; j++ ) {
ZedClass = KF.MonsterCollection.default.SpecialSquads[i].ZedClass[j];
NumZeds = KF.MonsterCollection.default.SpecialSquads[i].NumZeds[j];
if( ZedClass != "" ) {
// replace zed class with MonsterID
for ( k=0; k< KF.MonsterCollection.default.MonsterClasses.Length; ++k ) {
if ( ZedClass ~= KF.MonsterCollection.default.MonsterClasses[k].MClassName ) {
ZedClass = ColorString(KF.MonsterCollection.default.MonsterClasses[k].MID, 1, 100, 200);
break;
}
}
if ( k == KF.MonsterCollection.default.MonsterClasses.Length )
ZedClass = GetItemName(ZedClass); // zed class not found in monster collection
str @= ColorString(NumZeds$"x", 200, 200, 200) $ GetItemName(ZedClass);
}
}
if ( str != "" )
Sender.ClientMessage("Wave "$string(i+1)$":" $ str);
}
}
}
function SendPlayerList(PlayerController Sender)
{
local array<PlayerReplicationInfo> AllPRI;
local PlayerController PC;
local int i;
Level.Game.GameReplicationInfo.GetPRIArray(AllPRI);
for (i = 0; i<AllPRI.Length; i++) {
PC = PlayerController(AllPRI[i].Owner);
if( PC != none && AllPRI[i].PlayerName != "WebAdmin")
Sender.ClientMessage(Right(" "$AllPRI[i].PlayerID, 3)$")"
@ PC.GetPlayerIDHash()
@ AllPRI[i].PlayerName
, 'Log');
}
}
// @param Pct 0..1
simulated function string GetColoredPercent(float Pct, optional bool bRightAlign)
{
local string s;
s = string(int(100*Pct)) $ "%";
if ( bRightAlign && Pct < 1.0 )
s = " "$s;
if ( Pct >= 0.90 )
return ColorString(s,1,100,200); // light blue
else if ( Pct >= 0.75 )
return ColorString(s,1,200,1); // green
else if ( Pct >= 0.50 )
return ColorString(s,200,200,1); // yellow
return ColorString(s,200,1,1); // red
}
function SendAccuracy(PlayerController Sender)
{
local ScrnPlayerInfo SPI;
local string msg;
for ( SPI=GameRules.PlayerInfo; SPI!=none; SPI=SPI.NextPlayerInfo ) {
if ( SPI.PlayerOwner == none || SPI.PlayerOwner.PlayerReplicationInfo == none || SPI.HeadshotsPerGame == 0 )
continue;
msg =
ColoredPlayerName(SPI.PlayerOwner.PlayerReplicationInfo) $ ": "
@ "Wave: "$GetColoredPercent(SPI.GetAccuracyWave(), false)
@ ColorString("("$SPI.HeadshotsPerWave,1,220,1)$ColorString("/"$SPI.BodyShotsPerWave,200,200,200)$")"
@ " Game: "$GetColoredPercent(SPI.GetAccuracyGame(), false)
@ ColorString("("$SPI.HeadshotsPerGame,1,220,1)$ColorString("/"$SPI.BodyshotsPerGame,200,200,200)$")";
if ( ScrnPlayerController(SPI.PlayerOwner) != none && ScrnPlayerController(SPI.PlayerOwner).ab_warning > 0 )
msg @= ColorString("AB="$ScrnPlayerController(SPI.PlayerOwner).ab_warning$"%", 255, 200, 1);
Sender.ClientMessage(msg);
}
}
static final function String FormatTime( int Seconds )
{
local int Minutes, Hours;
local String Time;
if( Seconds > 3600 )
{
Hours = Seconds / 3600;
Seconds -= Hours * 3600;
Time = Hours$":";
}
Minutes = Seconds / 60;
Seconds -= Minutes * 60;
if( Minutes >= 10 || Hours == 0 )
Time = Time $ Minutes $ ":";
else
Time = Time $ "0" $ Minutes $ ":";
if( Seconds >= 10 )
Time = Time $ Seconds;
else
Time = Time $ "0" $ Seconds;
return Time;
}
static function FillPlayInfo(PlayInfo PlayInfo)
{
Super.FillPlayInfo(PlayInfo);
PlayInfo.AddSetting(default.BonusCapGroup,"BonusLevelNormalMax","2.Normal Max Bonus Level ",1,0, "Text", "4;0:70",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"BonusLevelHardMin","4.Hard Min Bonus Level",1,0, "Text", "4;0:70",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"BonusLevelHardMax","4.Hard Max Bonus Level",1,0, "Text", "4;0:70",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"BonusLevelSuiMin","5.Suicidal Min Bonus Level",1,0, "Text", "4;0:70",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"BonusLevelSuiMax","5.Suicidal Max Bonus Level",1,0, "Text", "4;0:70",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"BonusLevelHoeMin","7.HoE Min Bonus Level",1,0, "Text", "4;0:70",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"BonusLevelHoeMax","7.HoE Max Bonus Level",1,0, "Text", "4;0:70",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"Post6RequirementScaling","Level 7+ Scaling",1,0, "Text", "6;0.01:4.00",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"MaxZombiesOnce","Max Specimens At Once",1,0, "Text", "4;8:254",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"bSpawn0","Zero Cost of Initial Inventory",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bHardcore","Hardcore Mode",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bReplacePickups","Replace Pickups",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bReplacePickupsStory","Replace Pickups (Story)",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bReplaceNades","Replace Grenades",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bShieldWeight","Armor Has Weight",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bAltBurnMech","Alternate Burning Mechanism",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bShowDamages","Show Damages",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bReplaceHUD","Replace HUD",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bNoPerkChanges","No Perk Changes",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"EventNum","Event", 0, 1, "Select", "0;Autodetect;255;Normal;1;Summer;2;Halloween;3;Xmas;254;Random",,,True);
PlayInfo.AddSetting(default.BonusCapGroup,"bForceEvent","Force Event",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bUseDLCLocks","Trader Requirements",1,0, "Check");
PlayInfo.AddSetting(default.BonusCapGroup,"bUseDLCLevelLocks","Perk Level Requirements",1,0, "Check");
//PlayInfo.AddSetting(default.BonusCapGroup,"PerkedWeapons","Custom Perked Weapons",1,1,"Text","42",,,True);
//PlayInfo.AddSetting(default.BonusCapGroup,"CustomPerks","Custom Perks",1,1,"Text","42",,,True);
}
static function string GetDescriptionText(string PropName)
{
switch (PropName)
{
case "BonusLevelNormalMax": return "Maximum perk level, which bonuses can be applied on difficulty. Perk levels above this won't have any extra bonuses.";
case "BonusLevelHardMin": return "Minimum perk level, which bonuses can be applied on Hard difficulty. Perk levels below will be granted with minimal bonus anyway.";
case "BonusLevelHardMax": return "Maximum perk level, which bonuses can be applied on Hard difficulty. Perk levels above this won't have any extra bonuses.";
case "BonusLevelSuiMin": return "Minimum perk level, which bonuses can be applied on Suicidal difficulty. Perk levels below will be granted with minimal bonus anyway.";
case "BonusLevelSuiMax": return "Maximum perk level, which bonuses can be applied on Suicidal difficulty. Perk levels above this won't have any extra bonuses.";
case "BonusLevelHoeMin": return "Minimum perk level, which bonuses can be applied on HoE difficulty. Perk levels below will be granted with minimal bonus anyway.";
case "BonusLevelHoeMax": return "Maximum perk level, which bonuses can be applied on HoE difficulty. Perk levels above this won't have any extra bonuses.";
case "BonusLevelHoeMax": return "Maximum perk level, which bonuses can be applied on HoE difficulty. Perk levels above this won't have any extra bonuses.";
case "Post6RequirementScaling": return "Additional requirement scaling after reaching level 6";
case "MaxZombiesOnce": return "Maximum specimens at once on playtime, note that high values will LAG when theres a lot of them.";
case "bHardcore": return "For those who still think game is too easy...";
case "bSpawn0": return "All initial weapons costs nothing";
case "bReplacePickups": return "Replaces weapon pickups on a map with their Scrn Editon (SE) versions.";
case "bReplacePickupsStory": return "Replaces weapon pickups in Objective Mode with their Scrn Editon (SE) versions.";
case "bReplaceNades": return "Replaces hand grenades with 'coockable' ScrN version. Players can disable grenade cooking in ScrN Settings menu anyway. Disabling it here removes this ability from the server.";
case "bShieldWeight": return "Kevlar Vest weights 1 block instead of hand grenades. Players without vest can carry more. Automatically enables hand grenade replacing with ScrN version";
case "bAltBurnMech": return "Use Alternate Burning Mechanism. Shorter burning period, but higher damage at the begining. Also fixes many bugs, including Crawler Infinite Burning.";
case "bShowDamages": return "Allows showing damage values on the HUD. Clients will still be able to turn it off in their User.ini";
case "bReplaceHUD": return "Replace heads-up display with ScrN version (recommended). Disable only if you have compatibility issues with other mods!";
case "bNoPerkChanges": return "Disables perk changes during the game.";
case "EventNum": return "Setup KF event zeds";
case "bForceEvent": return "Check it to force selected event";
case "bUseDLCLocks": return "If checked, then trader items may have perk level or/and achievement requirements.";
case "bUseDLCLevelLocks": return "Uncheck this to leave only achievement-specific trader requirements.";
//case "PerkedWeapons": return "List of perked custom weapons for perk bonuses in format '<PerkIndex>:<WeaponClassName>:<Bonuses>'";
//case "CustomPerks": return "List of custom perks. Perks should be defined the same way as in Serverperks.ini";
}
return Super.GetDescriptionText(PropName);
}
function GetServerDetails( out GameInfo.ServerResponseLine ServerState )
{
// append the mutator name.
local int i;
local string wave_status;
super.GetServerDetails(ServerState);
if ( !bServerInfoVeterancy ) {
for ( i=0; i<ServerState.ServerInfo.Length; ++i ) {
if ( ServerState.ServerInfo[i].Key == "Veterancy" || Left(ServerState.ServerInfo[i].Key, 9) == "SP: Perk " )
ServerState.ServerInfo.remove(i--, 1);
}
}
i = ServerState.ServerInfo.Length;
ServerState.ServerInfo.insert(i, 4);
ServerState.ServerInfo[i].Key = "ScrN Balance";
ServerState.ServerInfo[i++].Value = GetVersionStr();
ServerState.ServerInfo[i].Key = "Perk bonus level min";
ServerState.ServerInfo[i++].Value = String(MinLevel);
ServerState.ServerInfo[i].Key = "Perk bonus level max";
ServerState.ServerInfo[i++].Value = String(MaxLevel);
if ( KF.IsInState('PendingMatch') )
wave_status = "LOBBY";
else if ( KF.IsInState('MatchInProgress') )
wave_status = String(KF.WaveNum + 1) $ " / " $ KF.FinalWave;
else if ( KF.IsInState('MatchOver') )
wave_status = "Game Over";
else
wave_status = "Unknown";
ServerState.ServerInfo[i].Key = "Current Wave";
ServerState.ServerInfo[i++].Value = wave_status;
}
function SetLevels()
{
if ( Level.Game.GameDifficulty >= 7 ) // HoE
{
MinLevel = BonusLevelHoeMin;
MaxLevel = BonusLevelHoeMax;
}
else if (Level.Game.GameDifficulty >= 5 ) // Suicidal
{
MinLevel = BonusLevelSuiMin;
MaxLevel = BonusLevelSuiMax;
}
else if ( Level.Game.GameDifficulty >= 4 ) // Hard
{
MinLevel = BonusLevelHardMin;
MaxLevel = BonusLevelHardMax;
}
else
{
MinLevel = -1;
MaxLevel = BonusLevelNormalMax;
}
if ( ScrnGT != none && ScrnGT.IsTourney() ) {
MaxLevel = clamp(MaxLevel, 0, 6);
MinLevel = MaxLevel;
}
if ( MinLevel > MaxLevel )
MinLevel = MaxLevel;
OriginalMaxLevel = MaxLevel;
//for the replication
SrvMinLevel = MinLevel;
SrvMaxLevel = MaxLevel;
}
function SetReplicationData()
{
SrvMinLevel = MinLevel;
SrvMaxLevel = MaxLevel;
SrvAchievementFlags = AchievementFlags;
SrvFlags = 0;
// if ( bSpawnBalance ) SrvFlags = SrvFlags | 0x00000001;
if ( bSpawn0 ) SrvFlags = SrvFlags | 0x00000002;
if ( bNoStartCashToss ) SrvFlags = SrvFlags | 0x00000004;
if ( bMedicRewardFromTeam ) SrvFlags = SrvFlags | 0x00000008;
// if ( bWeaponFix ) SrvFlags = SrvFlags | 0x00000010;
if ( bAltBurnMech ) SrvFlags = SrvFlags | 0x00000020;
// if ( bGunslinger ) SrvFlags = SrvFlags | 0x00000040;
if ( bTraderSpeedBoost ) SrvFlags = SrvFlags | 0x00000080;
if ( bReplaceNades ) SrvFlags = SrvFlags | 0x00000100;
if ( bShieldWeight ) SrvFlags = SrvFlags | 0x00000200;
if ( bHardcore ) SrvFlags = SrvFlags | 0x00000400;
if ( bBeta ) SrvFlags = SrvFlags | 0x00000800;
if ( bShowDamages ) SrvFlags = SrvFlags | 0x00001000;
if ( bManualReload ) SrvFlags = SrvFlags | 0x00002000;
if ( bForceManualReload ) SrvFlags = SrvFlags | 0x00004000;
if ( bAllowWeaponLock ) SrvFlags = SrvFlags | 0x00008000;
if ( bNoPerkChanges ) SrvFlags = SrvFlags | 0x00010000;
if ( bPerkChangeBoss ) SrvFlags = SrvFlags | 0x00020000;
if ( b10Stars ) SrvFlags = SrvFlags | 0x00040000;
if ( bBuyPerkedWeaponsOnly ) SrvFlags = SrvFlags | 0x00080000;
}
simulated function LoadReplicationData()
{
if ( Role == ROLE_Authority )
return;
MinLevel = SrvMinLevel;
MaxLevel = SrvMaxLevel;
AchievementFlags = SrvAchievementFlags;
// bSpawnBalance = (SrvFlags & 0x00000001) > 0;
bSpawn0 = (SrvFlags & 0x00000002) > 0;
bNoStartCashToss = (SrvFlags & 0x00000004) > 0;
bMedicRewardFromTeam = (SrvFlags & 0x00000008) > 0;
// bWeaponFix = (SrvFlags & 0x00000010) > 0;
bAltBurnMech = (SrvFlags & 0x00000020) > 0;
// bGunslinger = (SrvFlags & 0x00000040) > 0;
bTraderSpeedBoost = (SrvFlags & 0x00000080) > 0;
bReplaceNades = (SrvFlags & 0x00000100) > 0;
bShieldWeight = (SrvFlags & 0x00000200) > 0;
bHardcore = (SrvFlags & 0x00000400) > 0;
bBeta = (SrvFlags & 0x00000800) > 0;
bShowDamages = (SrvFlags & 0x00001000) > 0;
bManualReload = (SrvFlags & 0x00002000) > 0;
bForceManualReload = (SrvFlags & 0x00004000) > 0;
bAllowWeaponLock = (SrvFlags & 0x00008000) > 0;
bNoPerkChanges = (SrvFlags & 0x00010000) > 0;
bPerkChangeBoss = (SrvFlags & 0x00020000) > 0;
b10Stars = (SrvFlags & 0x00040000) > 0;
bBuyPerkedWeaponsOnly = (SrvFlags & 0x00080000) > 0;
bPerkChangeDead = (SrvFlags & 0x00100000) > 0;
// = (SrvFlags & 0x00200000) > 0;
// = (SrvFlags & 0x00400000) > 0;
// = (SrvFlags & 0x00800000) > 0;
}
simulated function ApplySpawnBalance()
{
// nothing left here
}
simulated function ApplyWeaponFix()
{
class'KFMod.ZEDGun'.default.UnlockedByAchievement = -1;
class'KFMod.ZEDGun'.default.AppID = 0;
class'KFMod.DwarfAxe'.default.UnlockedByAchievement = -1;
class'KFMod.DwarfAxe'.default.AppID = 0;
// fix missing references
class'KFMod.Shotgun'.default.HudImageRef="KillingFloorHUD.WeaponSelect.combat_shotgun_unselected";
class'KFMod.Shotgun'.default.SelectedHudImageRef="KillingFloorHUD.WeaponSelect.combat_shotgun";
class'KFMod.Shotgun'.default.SelectSoundRef="KF_PumpSGSnd.SG_Select";
class'KFMod.Shotgun'.default.MeshRef="KF_Weapons_Trip.Shotgun_Trip";
class'KFMod.Shotgun'.default.SkinRefs[0]="KF_Weapons_Trip_T.Shotguns.shotgun_cmb";
class'KFMod.ShotgunAttachment'.default.MeshRef="KF_Weapons3rd_Trip.Shotgun_3rd";
class'KFMod.DualDeagle'.default.HudImageRef="KillingFloorHUD.WeaponSelect.dual_handcannon_unselected";
class'KFMod.DualDeagle'.default.SelectedHudImageRef="KillingFloorHUD.WeaponSelect.dual_handcannon";
class'KFMod.DualDeagle'.default.SelectSoundRef="KF_HandcannonSnd.50AE_Select";
class'KFMod.DualDeagle'.default.MeshRef="KF_Weapons_Trip.Dual50_Trip";
class'KFMod.DualDeagle'.default.SkinRefs[0]="KF_Weapons_Trip_T.Pistols.deagle_cmb";
class'KFMod.GoldenDualDeagle'.default.HudImageRef="KillingFloor2HUD.WeaponSelect.Gold_Dual_Deagle_unselected";
class'KFMod.GoldenDualDeagle'.default.SelectedHudImageRef="KillingFloor2HUD.WeaponSelect.Gold_Dual_Deagle";
class'KFMod.GoldenDualDeagle'.default.SelectSoundRef="KF_HandcannonSnd.50AE_Select";
class'KFMod.GoldenDualDeagle'.default.MeshRef="KF_Weapons_Trip.Dual50_Trip";
class'KFMod.GoldenDualDeagle'.default.SkinRefs[0]="KF_Weapons_Gold_T.Weapons.Gold_deagle_cmb";
// prevent weapons from dropping on death
class'Knife'.default.bCanThrow = bStoryMode;
class'Syringe'.default.bCanThrow = bStoryMode;
class'Welder'.default.bCanThrow = bStoryMode;
}
/*
Weapon bonuses' letters:
W - weapon bonuses (reload, recoil)
$ - discount (weapon, ammo)
A - primary ammo bonuses (count, magazine size)
B - secondary ammo bonuses (count)
D - Override damage type with perk's default one (to enable leveling).
d - Override secondary fire's damage type
P - primary fire bonuses (damage, headshot multiplier, fire rate)
S - secondary fire bonuses (damage, headshot multiplier, fire rate)
* - Special weapon
*/
function LoadCustomWeapons()
{
local string S, WeaponClassStr, WeaponBonusStr, PriceStr;
local int i,j;
local int PerkIndex, MaxPerkIndex;
local bool AllBonuses;
local class<KFWeapon> W;
local class<ScrnVeterancyTypes> ScrnPerk;
local ScrnCustomWeaponLink ClientLink;
local int ForcePrice;
// Load custom perks first
MaxPerkIndex = 9;
for( i = 0; i < CustomPerks.Length; i++ ) {
S = CustomPerks[i];
j = InStr(S,":");
if ( j > 0 ) {
PerkIndex = int(Left(S, j));
if ( PerkIndex < 10 || PerkIndex > 255 ) {
log("Custom Perk index must be between 10 and 255! Perk '"$S$"' ignored", 'ScrnBalance');
continue;
}
S = Mid(S, j+1);
}
else {
PerkIndex = MaxPerkIndex + 1;
}
ScrnPerk = class<ScrnVeterancyTypes>(DynamicLoadObject(S, Class'Class'));
if( ScrnPerk != None ) {
Perks[PerkIndex] = ScrnPerk;
AddToPackageMap(String(ScrnPerk.Outer.Name));
MaxPerkIndex = max(MaxPerkIndex, PerkIndex);
log("Perk: '" $ S $"' loaded with index = " $ String(PerkIndex), 'ScrnBalance');
}
else
log("Unable to load custom perk: '" $ S $"'.", 'ScrnBalance');
}
// Load weapon bonuses
for( i=0; i < PerkedWeapons.Length; i++ ) {
PriceStr = "";
WeaponBonusStr = "";
S = PerkedWeapons[i];
j = InStr(S,":");
if( j <= 0 ) {
log("Illegal Custom Weapon definition: '" $ S $"'! Wrong perk index.", 'ScrnBalance');
continue;
}
PerkIndex = int(Left(S, j));
if ( PerkIndex < 0 || PerkIndex >= Perks.length || Perks[PerkIndex] == none ) {
log("Illegal Custom Weapon definition: '" $ S $"'! Wrong perk index.", 'ScrnBalance');
continue;
}
ScrnPerk = Perks[PerkIndex];
WeaponClassStr = Mid(S, j+1);
// bonuses
j = InStr(WeaponClassStr,":");
if ( j >= 0 ) {
WeaponBonusStr = Mid(WeaponClassStr, j+1);
WeaponClassStr = Left(WeaponClassStr, j);
// price
j = InStr(WeaponBonusStr,":");
if ( j >= 0 ) {
PriceStr = Mid(WeaponBonusStr, j+1);
WeaponBonusStr = Left(WeaponBonusStr, j);
}
}
AllBonuses = WeaponBonusStr == "";
ForcePrice = int(PriceStr);
//log("WeaponBonusStr="$WeaponBonusStr @ "ForcePrice="$ForcePrice , 'ScrnBalance');
W = class<KFWeapon>(DynamicLoadObject(WeaponClassStr, Class'Class'));
if( W == none ) {
log("Can't load Custom Weapon: '" $ WeaponClassStr $"'!", 'ScrnBalance');
continue;
}
ClientLink = spawn(class'ScrnBalanceSrv.ScrnCustomWeaponLink');
if ( ClientLink == none ) {
log("Can't load Client Replication Link for a Custom Weapon: '" $ W $"'!", 'ScrnBalance');
continue;
}
//add to the begining
ClientLink.NextReplicationInfo = CustomWeaponLink;
CustomWeaponLink = ClientLink;
ClientLink.Perk = ScrnPerk;
ClientLink.WeaponClass = W;
if ( AllBonuses ) {
ClientLink.bWeapon = true;
ClientLink.bDiscount = true;
ClientLink.bFire = true;
ClientLink.bFireAlt = true;
ClientLink.bAmmo = true;
ClientLink.bAmmoAlt = true;
}
else {
ClientLink.bWeapon = InStr(WeaponBonusStr,"W") != -1;
ClientLink.bDiscount = InStr(WeaponBonusStr,"$") != -1;
ClientLink.bFire = InStr(WeaponBonusStr,"P") != -1;
ClientLink.bFireAlt = InStr(WeaponBonusStr,"S") != -1;
ClientLink.bAmmo = InStr(WeaponBonusStr,"A") != -1;
ClientLink.bAmmoAlt = InStr(WeaponBonusStr,"B") != -1;
}
ClientLink.bOverrideDamType = InStr(WeaponBonusStr,"D") != -1;
ClientLink.bOverrideDamTypeAlt = InStr(WeaponBonusStr,"d") != -1;
ClientLink.bSpecial = InStr(WeaponBonusStr,"*") != -1;
ClientLink.ForcePrice = ForcePrice;
ClientLink.LoadWeaponBonuses();
}
}
function LoadSpawnInventory()
{
local int i, j, index, k;
local byte X;
local string S, PickupStr, LevelStr, AmmoStr, SellStr, AchStr;
local int PerkIndex;
local class<ScrnVeterancyTypes> ScrnPerk;
local class<Pickup> Pickup;
local bool bAllPerks;
local bool bTourney;
bTourney = ScrnGT != none && ScrnGT.IsTourney();
// clear old inventory left from previous map
for ( j=0; j<Perks.length; ++j )
if ( Perks[j] != none )
Perks[j].default.DefaultInventory.length = 0;
for ( i=0; i<SpawnInventory.length; ++i ) {
bAllPerks = false;
LevelStr = "";
AmmoStr = "";
SellStr = "";
AchStr = "";
X = 0;
S = SpawnInventory[i];
j = InStr(S,":");
if( j <= 0 ) {
log("Illegal Spawn Inventory definition: '" $ S $"'! Wrong perk index.", 'ScrnBalance');
continue;
}
PickupStr = Mid(S, j+1);
S = Left(S, j);
j = InStr(S,"-"); // "PerkIndex-X"
if ( j > 0 ) {
X = int(Mid(S, j+1));
S = Left(S, j);
}
if ( S == "*" ) {
bAllPerks = true;
PerkIndex = 0;
}
else
PerkIndex = int(S);
if ( PerkIndex < 0 || PerkIndex >= Perks.length || Perks[PerkIndex] == none ) {
log("Illegal Spawn Inventory definition: '" $ S $"'! Wrong perk index.", 'ScrnBalance');
continue;
}
ScrnPerk = Perks[PerkIndex];
//pickup
j = InStr(PickupStr,":");
if ( j >= 0 ) {
LevelStr = Mid(PickupStr, j+1);
PickupStr = Left(PickupStr, j);
// ammo
j = InStr(LevelStr,":");
if ( j >= 0 ) {
AmmoStr = Mid(LevelStr, j+1);
LevelStr = Left(LevelStr, j);
// sell value
j = InStr(AmmoStr,":");
if ( j >= 0 ) {
SellStr = Mid(AmmoStr, j+1);
AmmoStr = Left(AmmoStr, j);
}
// Achievement
j = InStr(SellStr,":");
if ( j >= 0 ) {
AchStr = Mid(SellStr, j+1);
SellStr = Left(SellStr, j);
}
}
}
if ( bTourney && AchStr != "" )
continue; // do not allow achievement-specific inventory in tournaments
Pickup = class<Pickup>(DynamicLoadObject(PickupStr, Class'Class'));
if( Pickup == none ) {
log("Can't load Spawn Inventory: '" $ PickupStr $"'!", 'ScrnBalance');
continue;
}
index = ScrnPerk.default.DefaultInventory.length;
ScrnPerk.default.DefaultInventory.insert(index, 1);
ScrnPerk.default.DefaultInventory[index].PickupClass = Pickup;
// LevelStr in format <MinLevel>[-<MaxLevel>]
j = InStr(LevelStr,"-");
if ( j > 0 ) {
ScrnPerk.default.DefaultInventory[index].MinPerkLevel = byte(Left(LevelStr, j));
ScrnPerk.default.DefaultInventory[index].MaxPerkLevel = byte(Mid(LevelStr, j+1));
}
else {
ScrnPerk.default.DefaultInventory[index].MinPerkLevel = byte(LevelStr);
ScrnPerk.default.DefaultInventory[index].MaxPerkLevel = ScrnPerk.default.DefaultInventory[index].MinPerkLevel;
}
// AmmoStr in format <Ammo>[+<AmmoPerLevel>]
if ( AmmoStr != "" ) {
ScrnPerk.default.DefaultInventory[index].bSetAmmo = true;
j = InStr(AmmoStr,"+");
if ( j > 0 ) {
ScrnPerk.default.DefaultInventory[index].AmmoAmount = int(Left(AmmoStr, j));
ScrnPerk.default.DefaultInventory[index].AmmoPerLevel = int(Mid(AmmoStr, j+1));
}
else {
ScrnPerk.default.DefaultInventory[index].AmmoAmount = int(AmmoStr);
}
}
// SellValue
if ( SellStr != "" )
ScrnPerk.default.DefaultInventory[index].SellValue = int(SellStr);
if ( AchStr != "" )
ScrnPerk.default.DefaultInventory[index].Achievement = StringToName(AchStr);
ScrnPerk.default.DefaultInventory[index].X = X;
if ( bAllPerks ) {
for ( j=1; j<Perks.length; ++j ) {
if ( Perks[j] != none && Perks[j] != ScrnPerk && Perks[j] != Class'ScrnVeterancyTypes' ) {
k = Perks[j].default.DefaultInventory.length;
Perks[j].default.DefaultInventory.insert(k, 1);
Perks[j].default.DefaultInventory[k].PickupClass = ScrnPerk.default.DefaultInventory[index].PickupClass;
Perks[j].default.DefaultInventory[k].MinPerkLevel = ScrnPerk.default.DefaultInventory[index].MinPerkLevel;
Perks[j].default.DefaultInventory[k].MaxPerkLevel = ScrnPerk.default.DefaultInventory[index].MaxPerkLevel;
Perks[j].default.DefaultInventory[k].bSetAmmo = ScrnPerk.default.DefaultInventory[index].bSetAmmo;
Perks[j].default.DefaultInventory[k].AmmoAmount = ScrnPerk.default.DefaultInventory[index].AmmoAmount;
Perks[j].default.DefaultInventory[k].AmmoPerLevel = ScrnPerk.default.DefaultInventory[index].AmmoPerLevel;
Perks[j].default.DefaultInventory[k].SellValue = ScrnPerk.default.DefaultInventory[index].SellValue;
Perks[j].default.DefaultInventory[k].Achievement = ScrnPerk.default.DefaultInventory[index].Achievement;
Perks[j].default.DefaultInventory[k].X = ScrnPerk.default.DefaultInventory[index].X;
}
}
}
}
}
// UE2 doesn't support direct string to name typecasting
// That's why need to use the following hack
final function name StringToName(string str)
{
SetPropertyText("NameOfString", str);
return NameOfString;
}
function SetupStoryRules(KFLevelRules_Story StoryRules)
{
local int i,j;
local Class<Inventory> OldInv;
for( i=0; i<StoryRules.RequiredPlayerEquipment.Length; ++i ) {
OldInv = StoryRules.RequiredPlayerEquipment[i];
for ( j=0; j<pickupReplaceArray.length; ++j ) {
if ( pickupReplaceArray[j].oldClass.default.InventoryType == OldInv ) {
StoryRules.RequiredPlayerEquipment[i] = pickupReplaceArray[j].NewClass.default.InventoryType;
break;
}
}
}
}
function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
local int i;
//Log("CheckReplacement: " $ String(Other), 'ScrnBalance');
// first check classes that need to be replaced
if ( Other.class == class'KFRandomItemSpawn' ) {
if ( !bReplacePickups )
return true;
ReplaceWith(Other, "ScrnBalanceSrv.ScrnRandomItemSpawn");
return false;
}
else if ( Other.class == class'KFAmmoPickup' ) {
AmmoBoxMesh = Other.StaticMesh;
AmmoBoxDrawScale = Other.DrawScale;
AmmoBoxDrawScale3D = Other.DrawScale3D;
ReplaceWith(Other, "ScrnBalanceSrv.ScrnAmmoPickup");
return false;
}
else if ( bReplacePickups && Pickup(Other) != none && KF.IsInState('MatchInProgress') ) {
// don't replace pickups placed on the map by the author - replace only dropped ones
// or spawned by KFRandomItemSpawn
i = FindPickupReplacementIndex(Pickup(Other));
if ( i != -1 ) {
ReplaceWith(Other, string(pickupReplaceArray[i].NewClass));
return false;
}
return true; // no need to replace
}
// classes below do not need replacement
if ( PlayerController(Other)!=None ) {
if ( ScrnPlayerController(Other) != none )
ScrnPlayerController(Other).Mut = self;
}
else if ( KFMonster(Other) != none ) {
// harder zapping
if ( ZombieFleshPound(Other) != none )
KFMonster(Other).ZapThreshold = 3.75;
else if ( KFMonster(Other).default.Health >= 1000 )
KFMonster(Other).ZapThreshold = 1.75;
GameRules.RegisterMonster(KFMonster(Other));
}
else if ( SRStatsBase(Other) != none ) {
SetupRepLink(SRStatsBase(Other).Rep);
}
else if ( Other.class == class'ScrnAmmoPickup' ) {
Other.SetStaticMesh(AmmoBoxMesh);
Other.SetDrawScale(AmmoBoxDrawScale);
Other.SetDrawScale3D(AmmoBoxDrawScale3D);
}
else if ( bStoryMode ) {
if ( KFLevelRules_Story(Other) != none ) {
if ( bReplacePickupsStory )
SetupStoryRules(KFLevelRules_Story(Other));
}
else if ( KF_StoryNPC(Other) != none ) {
if ( Other.class == class'KF_BreakerBoxNPC' ) // don't alter subclasses
KF_StoryNPC(Other).BaseAIThreatRating = 20;
else if ( Other.class == class'KF_RingMasterNPC' )
KF_StoryNPC(Other).BaseAIThreatRating = 40;
}
}
return true;
}
function SetupRepLink(ClientPerkRepLink R)
{
local ScrnClientPerkRepLink ScrnRep;
local int i, j;
if ( R == none )
return;
// replace with ScrnClientPerkRepLink
if ( ScrnClientPerkRepLink(R) != none )
return; // already set up
ScrnRep = Spawn(Class'ScrnClientPerkRepLink',R.Owner);
ScrnRep.StatObject = R.StatObject;
ScrnRep.StatObject.Rep = ScrnRep;
ScrnRep.ServerWebSite = R.ServerWebSite;
ScrnRep.MinimumLevel = R.MinimumLevel;
ScrnRep.MaximumLevel = R.MaximumLevel;
ScrnRep.RequirementScaling = R.RequirementScaling;
ScrnRep.CachePerks = R.CachePerks;
// remove non-scrn perks
for( i=0; i<ScrnRep.CachePerks.Length; ++i) {
if ( class<ScrnVeterancyTypes>(ScrnRep.CachePerks[i].PerkClass) == none )
ScrnRep.CachePerks.remove(i--, 1);
}
ScrnRep.OwnerPC = ScrnPlayerController(R.Owner);
ScrnRep.OwnerPRI = KFPlayerReplicationInfo(ScrnRep.OwnerPC.PlayerReplicationInfo);
log("Creating ScrnClientPerkRepLink for player " $ ScrnRep.OwnerPRI.PlayerName, 'ScrnBalance');
R.GotoState('');
R.Destroy();
R = ScrnRep;
class'ScrnAchievements'.static.InitAchievements(ScrnRep);
if ( bUseDLCLocks && !bTSCGame && (ScrnGT == none || !ScrnGT.IsTourney()) ) {
ScrnRep.Locks.Length = LockCount;
for( i=0; i<DLCLocks.Length; ++i) {
if ( DLCLocks[i].PickupClass != none
&& (bUseDLCLevelLocks || DLCLocks[i].Type != LOCK_Level) )
{
ScrnRep.Locks[j].PickupClass = DLCLocks[i].PickupClass;
ScrnRep.Locks[j].Group = DLCLocks[i].Group;
ScrnRep.Locks[j].Type = DLCLocks[i].Type;
ScrnRep.Locks[j].ID = DLCLocks[i].ID;
ScrnRep.Locks[j].MaxProgress = DLCLocks[i].Value;
++j;
}
}
}
if ( ScrnGT != none )
ScrnGT.SetupRepLink(ScrnRep);
// used for client replication
if ( ScrnRep.ShopCategories.Length > 250 )
ScrnRep.ShopCategories.Length = 250; //wtf?
ScrnRep.TotalCategories = ScrnRep.ShopCategories.Length;
ScrnRep.TotalWeapons = ScrnRep.ShopInventory.Length;
ScrnRep.TotalLocks = ScrnRep.Locks.Length;
ScrnRep.TotalChars = ScrnRep.CustomChars.Length;
}
function ForceEvent()
{
local int i;
local class<KFMonstersCollection> MC;
i = MapInfo.FindMapInfo(false);
if ( i != -1 && MapInfo.MapInfo[i].ForceEventNum > 0 )
CurrentEventNum = MapInfo.MapInfo[i].ForceEventNum;
else if ( EventNum == 0 )
CurrentEventNum = int(KF.GetSpecialEventType()); // autodetect event
else
CurrentEventNum = EventNum;
if ( CurrentEventNum == 254 ) {
// 254 - random event
CurrentEventNum = 1 + rand(4);
}
if (bScrnWaves) {
return; // all we need for ScrnWaves is to load CurrentEventNum. ScrnGameLength will handle everything else.
}
if ( MC == none ) {
switch (CurrentEventNum) {
case 0: case 4: case 255: // force regular zeds
log("Normal zeds forced for this map", 'ScrnBalance');
KF.MonsterCollection = class'KFMod.KFMonstersCollection';
CurrentEventNum = 0;
break;
case 1:
log("Summer zeds forced for this map", 'ScrnBalance');
KF.MonsterCollection = class'KFMod.KFMonstersSummer';
break;
case 2:
log("Halloween zeds forced for this map", 'ScrnBalance');
KF.MonsterCollection = class'KFMod.KFMonstersHalloween';
break;
case 3:
log("Xmas zeds forced for this map", 'ScrnBalance');
KF.MonsterCollection = class'KFMod.KFMonstersXmas';
break;
default:
log("Unknown Event Number: "$CurrentEventNum, 'ScrnBalance');
CurrentEventNum = EventNum;
return;
}
}
KF.SpecialEventMonsterCollections[KF.GetSpecialEventType()] = KF.MonsterCollection;
class'ScrnGameRules'.static.ResetGameSquads(KF, CurrentEventNum);
KF.PrepareSpecialSquads();
KF.LoadUpMonsterList();
}
// returns index from Squads array or -1, if squad is not found and bCreateNew=false
// quad is not found and bCreateNew=true, new squad will be added with a given name
function int FindSquad(String SquadName, optional bool bCreateNew)
{
local int i;
for ( i=0; i<Squads.length; ++i )
if ( Squads[i].SquadName ~= SquadName )
return i;
if ( bCreateNew ) {
Squads.insert(i, 1);
Squads[i].SquadName = SquadName;
return i;
}
return -1;
}
function SetupVoteSquads()
{
local int i, j, q;
local Class<KFMonster> MC;
local name pkg;
for ( i=0; i<VoteSquad.length; ++i ) {
if ( VoteSquad[i].SquadName == "" || VoteSquad[i].MonsterClass == "" || VoteSquad[i].NumMonsters == 0 )
continue;
MC = Class<KFMonster>(DynamicLoadObject(VoteSquad[i].MonsterClass,Class'Class'));
if ( MC == none ) {
log("SetupVoteSquad: Unable to load monster '"$VoteSquad[i].MonsterClass, 'ScrnBalance');
continue;
}
MC.static.PreCacheAssets(Level);
pkg = MC.outer.name;
if ( pkg != 'KFChar' ) {
AddToPackageMap(String(pkg));
log(pkg $ " added to ServerPackages", 'ScrnBalance');
}
q = FindSquad(VoteSquad[i].SquadName, true);
j = VoteSquad[i].NumMonsters;
while ( j-- > 0 )
Squads[q].Monsters[Squads[q].Monsters.length] = MC;
}
}
function bool IsSquadWaitingToSpawn()
{
return SquadSpawnedMonsters > 0 && KF.WaveMonsters < SquadSpawnedMonsters;
}
function SpawnSquad(String SquadName)
{
local int q, count, i;
if ( KF.bTradingDoorsOpen || KF.TotalMaxMonsters <= 0 || IsSquadWaitingToSpawn() )
return;
q = FindSquad(SquadName);
if ( q == -1 )
return;
count = Squads[q].Monsters.length;
if ( count == 0 || count > KF.TotalMaxMonsters - KF.NextSpawnSquad.Length )
return;
SquadSpawnedMonsters = KF.WaveMonsters + count;
KF.NextSpawnSquad.insert(0, count);
for ( i=0; i<count; ++i )
KF.NextSpawnSquad[i] = Squads[q].Monsters[i];
//if( KF.NextSpawnSquad.length > 6 )
// KF.NextSpawnSquad.Remove(6, KF.NextSpawnSquad.length - 6);
KF.LastZVol = KF.FindSpawningVolume();
KF.LastSpawningVolume = KF.LastZVol;
}
function SetMaxZombiesOnce()
{
local int i, value;
i = MapInfo.FindMapInfo(false);
if ( i != -1 && MapInfo.MapInfo[i].MaxZombiesOnce >= 16 )
value = MapInfo.MapInfo[i].MaxZombiesOnce;
else
value = MaxZombiesOnce;
value = clamp(value, 16, 254);
KF.StandardMaxZombiesOnce = value;
KF.MaxZombiesOnce = value;
KF.MaxMonsters = Clamp(KF.TotalMaxMonsters,5,value);
}
function LogObjects()
{
log("LIST OF LOADED OBJECTS:");
ConsoleCommand("OBJ LIST", true);
}
function PostBeginPlay()
{
local ScrnVotingHandlerMut VH;
local ScrnAchHandler AchHandler;
local int i;
local KFGameReplicationInfo KFGRI;
KF = KFGameType(Level.Game);
if (KF == none) {
Log("ERROR: Wrong GameType (requires KFGameType)", 'ScrnBalance');
Destroy();
return;
}
KFGRI = KFGameReplicationInfo(Level.Game.GameReplicationInfo);
if ( bLogObjectsAtMapStart )
LogObjects();
if ( Mut != none && Mut != self )
Mut.Destroy();
Mut = self;
default.Mut = self;
class'ScrnBalance'.default.Mut = self; // in case of classes extended from ScrnBalance
KF.MonsterCollection = KF.SpecialEventMonsterCollections[KF.GetSpecialEventType()]; // v1061 fix
KF.bUseZEDThreatAssessment = true; // always use ScrnHumanPawn.AssessThreatTo()
bStoryMode = KFStoryGameInfo(KF) != none;
bTSCGame = TSCGame(KF) != none && !TSCGame(KF).bSingleTeamGame;
ScrnGT = ScrnGameType(KF);
if ( ScrnGT != none ) {
ScrnGT.ScrnBalanceMut = self;
}
else if ( ScrnStoryGameInfo(KF) != none ) {
ScrnStoryGameInfo(KF).ScrnBalanceMut = self;
}
MapInfo = Spawn(Class'ScrnBalanceSrv.ScrnMapInfo');
if ( bForceEvent )
ForceEvent();
else
CurrentEventNum = int(KF.GetSpecialEventType()); // autodetect event
AddToPackageMap("ScrnAnims.ukx");
AddToPackageMap("ScrnSnd.uax"); // Promoted!!!!!!!!!! :)
if ( !bStoryMode ) {
SetMaxZombiesOnce();
}
// CHECK & LOAD SERVERPERKS
GetRidOfMut('AliensKFServerPerksMut');
FindServerPerksMut();
if ( ServerPerksMut == none ) {
log("ServerPerksMut must be loaded before ScrN Balance! Loading it now...", 'ScrnBalance');
Level.Game.AddMutator(ServerPerksPkgName, false);
//check again
FindServerPerksMut();
if ( ServerPerksMut == none )
log("Unable to spawn " $ ServerPerksPkgName, 'ScrnBalance');
}
bAllowAlwaysPerkChanges = ServerPerksMut.GetPropertyText("bAllowAlwaysPerkChanges") ~= "True";
bNoPerkChanges = bNoPerkChanges && !bAllowAlwaysPerkChanges;
if ( !ClassIsChildOf(KF.PlayerControllerClass, class'ScrnBalanceSrv.ScrnPlayerController') ) {
KF.PlayerControllerClass = class'ScrnBalanceSrv.ScrnPlayerController';
KF.PlayerControllerClassName = string(Class'ScrnBalanceSrv.ScrnPlayerController');
}
if ( bReplaceHUD )
KF.HUDType = string(Class'ScrnBalanceSrv.ScrnHUD');
if ( bReplaceScoreBoard )
Level.Game.ScoreBoardType = string(Class'ScrnBalanceSrv.ScrnScoreBoard');
KF.LoginMenuClass = string(Class'ScrnBalanceSrv.ScrnInvasionLoginMenu');
Persistence = new class'ScrnBalancePersistence';
if ( Persistence.Difficulty > 0 ) {
if ( Persistence.Difficulty == 6 || Persistence.Difficulty == 8 ) {
bHardcore = true;
KF.GameDifficulty = Persistence.Difficulty - 1;
log("Game difficulty: " $ string(KF.GameDifficulty) $ " + Hardcore", 'ScrnBalance');
}
else {
bHardcore = false;
KF.GameDifficulty = Persistence.Difficulty;
log("Game difficulty: " $ string(KF.GameDifficulty), 'ScrnBalance');
}
KFGRI.GameDiff = KF.GameDifficulty;
KFGRI.BaseDifficulty = KF.GameDifficulty;
}
SetLevels();
SetReplicationData();
//exec this on server side only
ApplySpawnBalance();
ApplyWeaponFix();
bUseAchievements = bool(AchievementFlags & ACH_ENABLE);
GameRules = Spawn(Class'ScrnBalanceSrv.ScrnGameRules');
if ( GameRules != none ) {
GameRules.Mut = self;
GameRules.bShowDamages = bShowDamages;
GameRules.bUseAchievements = bUseAchievements && KF.GameDifficulty >= 2;
if ( GameRules.bUseAchievements ) {
// spawn achievement handlers
AchHandler = GameRules.Spawn(Class'ScrnBalanceSrv.ScrnAchHandler');
}
if ( !bScrnWaves && (bResetSquadsAtStart || EventNum == 254) ) {
GameRules.ResetGameSquads(KF, CurrentEventNum);
}
}
else {
log("Unable to spawn Game Rules!", 'ScrnBalance');
}
if (bAltBurnMech) {
BurnMech = spawn(class'ScrnBalanceSrv.ScrnBurnMech');
default.BurnMech = BurnMech;
if ( bDoubleDoT ) {
BurnMech.BurnPeriod = 0.5;
BurnMech.BurnDuration = 16;
BurnMech.BurnInCount = 4;
if ( AchHandler != none )
AchHandler.iDoT_Damage = 150;
}
}
if ( bAllowVoting ) {
VH = class'ScrnVotingHandlerMut'.static.GetVotingHandler(Level.Game);
if ( VH == none ) {
Level.Game.AddMutator(string(class'ScrnVotingHandlerMut'), false);
VH = class'ScrnVotingHandlerMut'.static.GetVotingHandler(Level.Game);
}
if ( VH != none ) {
MyVotingOptions = ScrnBalanceVoting(VH.AddVotingOptions(class'ScrnBalanceSrv.ScrnBalanceVoting'));
if ( MyVotingOptions != none ) {
MyVotingOptions.Mut = self;
}
}
else
log("Unable to spawn voting handler mutator", 'ScrnBalance');
}
LoadCustomWeapons();
// proceed DLCLocks
LockCount = 0;
for ( i=0; i<DLCLocks.length; ++i ) {
if ( DLCLocks[i].Item != "" )
DLCLocks[i].PickupClass = class<Pickup>(DynamicLoadObject(DLCLocks[i].Item, Class'Class'));
if ( DLCLocks[i].PickupClass != none && (bUseDLCLevelLocks || DLCLocks[i].Type != LOCK_Level) )
++LockCount;
}
InitSettings();
LoadSpawnInventory();
SetupVoteSquads();
SetupSrvInfo();
if ( bStoryMode ) {
class'ScrnAchievements'.static.RegisterAchievements(class'AchObjMaps');
}
Log(FriendlyName @ GetVersionStr()$" loaded", 'ScrnBalance');
for ( i=0; i<AutoLoadMutators.length; ++i ) {
if ( AutoLoadMutators[i] != "" ) {
Log("Loading additional mutator: " $ AutoLoadMutators[i], 'ScrnBalance');
KF.AddMutator(AutoLoadMutators[i], true);
}
}
}
function SetupSrvInfo()
{
if ( SrvInfo == none )
SrvInfo = Spawn(Class'ScrnBalanceSrv.ScrnSrvReplInfo');
SrvInfo.bForceSteamNames = bForceSteamNames;
}
function InitDoors()
{
local KFUseTrigger t;
foreach DynamicActors(class'KFUseTrigger', t) {
DoorKeys[DoorKeys.Length] = t;
if ( t.DoorOwners.Length >= 2 )
DoubleDoorKeys[DoubleDoorKeys.Length] = t;
}
}
function CheckDoors()
{
local int i, j;
local bool bBlowDoors;
local KFUseTrigger key;
for ( i=0; i<DoubleDoorKeys.length; ++i ) {
key = DoubleDoorKeys[i];
bBlowDoors = false;
for ( j=1; j<key.DoorOwners.Length; ++j ) {
// blow all doors if one is blown
if ( key.DoorOwners[j].bDoorIsDead != key.DoorOwners[0].bDoorIsDead ) {
bBlowDoors = true;
break;
}
}
if ( bBlowDoors ) {
for ( j=0; j<key.DoorOwners.Length; ++j ) {
if ( !key.DoorOwners[j].bDoorIsDead )
key.DoorOwners[j].GoBang(none, vect(0,0,0), vect(0,0,0), none);
}
}
}
}
function RespawnDoors()
{
local int i, j;
local KFUseTrigger key;
for ( i = 0; i < DoorKeys.length; ++i ) {
key = DoorKeys[i];
for ( j = 0; j < key.DoorOwners.Length; ++j ) {
key.DoorOwners[j].RespawnDoor();
}
}
}
function BlowDoors()
{
local int i, j;
local KFUseTrigger key;
for ( i = 0; i < DoorKeys.length; ++i ) {
key = DoorKeys[i];
for ( j = 0; j < key.DoorOwners.Length; ++j ) {
if (!key.DoorOwners[j].bDoorIsDead) {
key.DoorOwners[j].GoBang(none, vect(0,0,0), vect(0,0,0), none);
}
}
}
}
function WeldDoors(float WeldPct)
{
local int i, j;
local KFUseTrigger key;
for ( i = 0; i < DoorKeys.length; ++i ) {
key = DoorKeys[i];
if ( WeldPct ~= -1.0 )
key.WeldStrength = fclamp((0.01 + frand()) * key.MaxWeldStrength, key.WeldStrength, key.MaxWeldStrength);
else
key.WeldStrength = WeldPct * key.MaxWeldStrength;
for ( j = 0; j < key.DoorOwners.Length; ++j ) {
key.DoorOwners[j].bShouldBeOpen = false;
key.DoorOwners[j].RespawnDoor();
key.DoorOwners[j].DoClose();
if ( !key.DoorOwners[j].bNoSeal ) {
key.DoorOwners[j].SetWeldStrength(key.WeldStrength);
}
}
}
}
function UnweldDoors()
{
local int i, j;
local KFUseTrigger key;
for ( i = 0; i < DoorKeys.length; ++i ) {
key = DoorKeys[i];
key.WeldStrength = 0;
for ( j = 0; j < key.DoorOwners.Length; ++j ) {
if ( !key.DoorOwners[j].bDoorIsDead ) {
key.DoorOwners[j].SetWeldStrength(0);
}
}
}
}
// 50% chance for dooor to be welded
// 25% - unweld
// 25% - blow
function RandomizeDoors()
{
local int i, j;
local KFUseTrigger key;
local float f;
for ( i = 0; i < DoorKeys.length; ++i ) {
key = DoorKeys[i];
f = frand();
if ( f > 0.505 ) { // weld
key.WeldStrength = (f - 0.5) * 2.0;
for ( j = 0; j < key.DoorOwners.Length; ++j ) {
key.DoorOwners[j].bShouldBeOpen = false;
key.DoorOwners[j].RespawnDoor();
key.DoorOwners[j].DoClose();
if ( !key.DoorOwners[j].bNoSeal ) {
key.DoorOwners[j].SetWeldStrength(key.WeldStrength);
}
}
}
else if ( f > 0.25 ) { // unweld or respawn
key.WeldStrength = 0;
for ( j = 0; j < key.DoorOwners.Length; ++j ) {
key.DoorOwners[j].RespawnDoor();
key.DoorOwners[j].SetWeldStrength(0);
}
}
else { // blow
for ( j = 0; j < key.DoorOwners.Length; ++j ) {
if ( !key.DoorOwners[j].bDoorIsDead ) {
key.DoorOwners[j].GoBang(none, vect(0,0,0), vect(0,0,0), none);
}
}
}
}
}
static function class<ScrnVeterancyTypes> PickRandomPerk(ScrnClientPerkRepLink L)
{
local array< class<ScrnVeterancyTypes> > CA;
local int i;
local class<ScrnVeterancyTypes> Perk;
for ( i=0; i < L.CachePerks.length; ++i ) {
if ( L.CachePerks[i].CurrentLevel > 0 ) {
Perk = class<ScrnVeterancyTypes>(L.CachePerks[i].PerkClass);
if ( Perk != none && !Perk.default.bLocked )
CA[CA.Length] = Perk;
}
}
if ( CA.Length > 0 )
return CA[rand(CA.Length)];
return none;
}
function LockPerk(class<ScrnVeterancyTypes> Perk, bool bLock)
{
local Controller C;
local PlayerController Player;
local ClientPerkRepLink L;
local int i;
local class<ScrnVeterancyTypes> RandomPerk;
Perk.default.bLocked = bLock;
for ( C = Level.ControllerList; C != none; C = C.nextController ) {
Player = PlayerController(C);
if ( Player != none && SRStatsBase(Player.SteamStatsAndAchievements) != none ) {
L = SRStatsBase(Player.SteamStatsAndAchievements).Rep;
if ( L != none ) {
for ( i=0; i < L.CachePerks.length; ++i ) {
if ( L.CachePerks[i].PerkClass == Perk ) {
if ( bLock ) {
L.ClientPerkLevel(i, 255); // mark perk as locked
if ( KFPlayerReplicationInfo(Player.PlayerReplicationInfo).ClientVeteranSkill == Perk ) {
RandomPerk = PickRandomPerk(ScrnClientPerkRepLink(L));
if ( RandomPerk != none )
ScrnClientPerkRepLink(L).ServerSelectPerkSE(RandomPerk);
}
}
else {
// set highest bit to 1 (128 = 10000000b) to notify client
// that perk has been unlocked
L.ClientPerkLevel(i, 0x80 | L.CachePerks[i].PerkClass.static.PerkIsAvailable(L));
}
break;
}
}
}
}
}
}
function bool ShouldReplacePickups()
{
if ( bStoryMode )
return bReplacePickupsStory;
return bReplacePickups;
}
// replaces weapon pickup's inventory type with ScrN version of that weapon
// returns true of replacement was made
function bool ReplacePickup( Pickup item )
{
local int i;
if ( FragPickup(item) != none ) {
if ( !bReplaceNades || ScrnFragPickup(item) != none )
return false;
i = FragReplacementIndex; // index of frag replacement
}
else {
if ( !ShouldReplacePickups() )
return false;
i = FindPickupReplacementIndex(item);
if ( i == -1 )
return false;
}
item.InventoryType = pickupReplaceArray[i].NewClass.default.InventoryType;
item.default.InventoryType = pickupReplaceArray[i].NewClass.default.InventoryType; // pistols reset they inventory type to default
if ( KFWeaponPickup(item) != none ) {
KFWeaponPickup(item).Weight = class<KFWeaponPickup>(pickupReplaceArray[i].NewClass).default.Weight;
KFWeaponPickup(item).default.Weight = class<KFWeaponPickup>(pickupReplaceArray[i].NewClass).default.Weight;
}
return true;
}
// returns -1, if not found
function int FindPickupReplacementIndex( Pickup item )
{
local int i;
for ( i=0; i<pickupReplaceArray.length; ++i ) {
if ( pickupReplaceArray[i].oldClass == item.class )
return i;
}
return -1;
}
// returns true if there are doom3 monsters in the game.
//If there are no doom3 monsters spawned yet, then function returns false, even if server is running doom3 mutator
function bool Doom3Check()
{
return GameRules.GameDoom3Kills > 0;
}
static function DestroyLinkedInfo( LinkedReplicationInfo EntryLink )
{
local LinkedReplicationInfo CurrentLink, NextLink;
for ( CurrentLink = EntryLink; CurrentLink != none; CurrentLink = NextLink ) {
NextLink = CurrentLink.NextReplicationInfo;
CurrentLink.Destroy();
}
EntryLink = none;
}
function ServerTraveling(string URL, bool bItems)
{
local int j;
log("ServerTraveling", 'ScrnBalance');
if ( bLogObjectsAtMapEnd )
LogObjects();
if (NextMutator != None)
NextMutator.ServerTraveling(URL,bItems);
if ( ScrnGT == none || ScrnGT.ScrnGameLength == none )
class'ScrnGameRules'.static.ResetGameSquads(KF, CurrentEventNum);
class'ScrnAchievements'.static.ResetAchList();
if ( Level.NetMode == NM_DedicatedServer ) {
// break links to self
Mut = none;
default.Mut = none;
class'ScrnBalance'.default.Mut = none;
}
for ( j=0; j<Perks.length; ++j )
if ( Perks[j] != none )
Perks[j].default.DefaultInventory.length = 0;
DestroyLinkedInfo(CustomWeaponLink);
CustomWeaponLink = none;
// destroy local objects
if ( MapInfo != none ) {
MapInfo.Destroy();
MapInfo = none;
}
if ( BurnMech != none ) {
BurnMech.Destroy();
BurnMech = none;
}
if ( SrvInfo != none ) {
SrvInfo.Destroy();
SrvInfo = none;
}
}
simulated function Destroyed()
{
super.Destroyed();
log("ScrnBalance destroyed", 'ScrnBalance');
}
// Limits placed pipebomb count to perk's capacity
function DestroyExtraPipebombs()
{
local PipeBombProjectile P;
local KFPlayerReplicationInfo KFPRI;
local array<KFPlayerReplicationInfo> KFPRIArray;
local array<byte> PipeBombCapacity;
local int i;
foreach DynamicActors(Class'PipeBombProjectile',P)
{
if( !P.bHidden && P.Instigator != none )
{
KFPRI = KFPlayerReplicationInfo(P.Instigator.PlayerReplicationInfo);
if ( KFPRI == none || KFPRI.ClientVeteranSkill == none )
continue;
for ( i=0; i<KFPRIArray.length; ++i ) {
if ( KFPRIArray[i] == KFPRI )
break;
}
if ( i == KFPRIArray.length ) {
// KFPRI not found. Add a new record.
KFPRIArray[i] = KFPRI;
PipeBombCapacity[i] = 2 * KFPRI.ClientVeteranSkill.static.AddExtraAmmoFor(KFPRI, class'PipeBombAmmo');
}
if ( PipeBombCapacity[i] > 0 )
PipeBombCapacity[i]--;
else
P.bEnemyDetected = true; // blow up
}
}
}
function BlamePlayer(ScrnPlayerController PC, string Reason, optional int BlameInc)
{
local ScrnCustomPRI ScrnPRI;
if ( PC == none )
return;
ScrnPRI = class'ScrnCustomPRI'.static.FindMe(PC.PlayerReplicationInfo);
if ( ScrnPRI == none )
return;
if ( BlameInc == 0 )
BlameInc = 1;
ScrnPRI.BlameCounter += BlameInc;
PC.ReceiveLocalizedMessage(class'ScrnBlamedMsg', ScrnPRI.BlameCounter);
ReplaceText(Reason, "%p", PC.GetHumanReadableName());
BroadcastMessage(ColorString(Reason, 200, 200, 1), false);
}
final static function bool GetHighlyDecorated(int SteamID32,
out material Avatar, out material ClanIcon,
out material PreNameIcon, out Color PrefixIconColor, out material PostNameIcon, out Color PostfixIconColor,
out int Playoffs, out int TourneyWon)
{
local int start, end, i;
start = 0;
end = default.HighlyDecorated.length;
while ( start < end ) {
i = start + ((end - start)>>1);
if ( SteamID32 == default.HighlyDecorated[i].SteamID32 ) {
if ( default.HighlyDecorated[i].Avatar == none && default.HighlyDecorated[i].AvatarRef != "" )
default.HighlyDecorated[i].Avatar = Material(DynamicLoadObject(default.HighlyDecorated[i].AvatarRef, class'Material'));
if ( default.HighlyDecorated[i].ClanIcon == none && default.HighlyDecorated[i].ClanIconRef != "" )
default.HighlyDecorated[i].ClanIcon = Material(DynamicLoadObject(default.HighlyDecorated[i].ClanIconRef, class'Material'));
if ( default.HighlyDecorated[i].PreNameIcon == none && default.HighlyDecorated[i].PreNameIconRef != "" )
default.HighlyDecorated[i].PreNameIcon = Material(DynamicLoadObject(default.HighlyDecorated[i].PreNameIconRef, class'Material'));
if ( default.HighlyDecorated[i].PostNameIcon == none && default.HighlyDecorated[i].PostNameIconRef != "" )
default.HighlyDecorated[i].PostNameIcon = Material(DynamicLoadObject(default.HighlyDecorated[i].PostNameIconRef, class'Material'));
Avatar = default.HighlyDecorated[i].Avatar;
ClanIcon = default.HighlyDecorated[i].ClanIcon;
PreNameIcon = default.HighlyDecorated[i].PreNameIcon;
PrefixIconColor = default.HighlyDecorated[i].PrefixIconColor;
PostNameIcon = default.HighlyDecorated[i].PostNameIcon;
PostfixIconColor = default.HighlyDecorated[i].PostfixIconColor;
Playoffs = default.HighlyDecorated[i].Playoffs;
TourneyWon = default.HighlyDecorated[i].TourneyWon;
return true;
}
else if ( SteamID32 < default.HighlyDecorated[i].SteamID32 )
end = i;
else
start = i + 1;
}
Avatar = none;
ClanIcon = none;
PreNameIcon = none;
PostNameIcon = none;
Playoffs = 0;
TourneyWon = 0;
return false;
}
/** Performs binary search on sorted array.
* @param arr : array of sorted items (in ascending order). Array will not be modified.
* out modifier is used just for performance purpose (pass by reference).
* @param val : value to search
* @return array index or -1, if value not found.
*/
final static function int BinarySearch(out array<int> arr, int val)
{
local int start, end, i;
start = 0;
end = arr.length;
while ( start < end ) {
i = start + ((end - start)>>1);
if ( arr[i] == val )
return i;
else if ( val < arr[i] )
end = i;
else
start = i + 1;
}
return -1;
}
final static function int BinarySearchStr(out array<string> arr, string val)
{
local int start, end, i;
start = 0;
end = arr.length;
while ( start < end ) {
i = start + ((end - start)>>1);
if ( arr[i] == val )
return i;
else if ( val < arr[i] )
end = i;
else
start = i + 1;
}
return -1;
}
defaultproperties
{
BonusCapGroup="ScrnBalance"
strBonusLevel="Your effective perk bonus level is [%s]"
strVersion="v%m.%n.%p"
strStatus="Your perk level: Visual=%v, Effective=[%b]. Server perk range is [%n..%x]."
strStatus2="Alt.Burn=%a. MaxZombiesOnce=%m."
strSrvWarning="You are using dedicated server version of ScrnBalance that shouldn't be installed on local machines! Please Obtain client version from Steam Workshop."
strSrvWarning2="If you are getting version mismatch erros, delete KillingFloorSystemScrnBalanceSrv.u file."
strBetaOnly="Only avaliable during Beta testing (bBeta=True)"
bAltBurnMech=True
bReplaceNades=True
bShowDamages=True
bAllowWeaponLock=True
bTraderSpeedBoost=True
BonusLevelNormalMax=4
BonusLevelHardMax=5
BonusLevelSuiMin=4
BonusLevelSuiMax=6
BonusLevelHoeMin=6
BonusLevelHoeMax=6
Post6RequirementScaling=1.000000
WeldingRequirementScaling=1.000000
StalkerRequirementScaling=1.000000
pickupReplaceArray(0)=(oldClass=Class'KFMod.MP7MPickup',NewClass=Class'ScrnBalanceSrv.ScrnMP7MPickup')
pickupReplaceArray(1)=(oldClass=Class'KFMod.MP5MPickup',NewClass=Class'ScrnBalanceSrv.ScrnMP5MPickup')
pickupReplaceArray(2)=(oldClass=Class'KFMod.KrissMPickup',NewClass=Class'ScrnBalanceSrv.ScrnKrissMPickup')
pickupReplaceArray(3)=(oldClass=Class'KFMod.M7A3MPickup',NewClass=Class'ScrnBalanceSrv.ScrnM7A3MPickup')
pickupReplaceArray(4)=(oldClass=Class'KFMod.ShotgunPickup',NewClass=Class'ScrnBalanceSrv.ScrnShotgunPickup')
pickupReplaceArray(5)=(oldClass=Class'KFMod.BoomStickPickup',NewClass=Class'ScrnBalanceSrv.ScrnBoomStickPickup')
pickupReplaceArray(6)=(oldClass=Class'KFMod.NailGunPickup',NewClass=Class'ScrnBalanceSrv.ScrnNailGunPickup')
pickupReplaceArray(7)=(oldClass=Class'KFMod.KSGPickup',NewClass=Class'ScrnBalanceSrv.ScrnKSGPickup')
pickupReplaceArray(8)=(oldClass=Class'KFMod.BenelliPickup',NewClass=Class'ScrnBalanceSrv.ScrnBenelliPickup')
pickupReplaceArray(9)=(oldClass=Class'KFMod.AA12Pickup',NewClass=Class'ScrnBalanceSrv.ScrnAA12Pickup')
pickupReplaceArray(10)=(oldClass=Class'KFMod.SinglePickup',NewClass=Class'ScrnBalanceSrv.ScrnSinglePickup')
pickupReplaceArray(11)=(oldClass=Class'KFMod.Magnum44Pickup',NewClass=Class'ScrnBalanceSrv.ScrnMagnum44Pickup')
pickupReplaceArray(12)=(oldClass=Class'KFMod.MK23Pickup',NewClass=Class'ScrnBalanceSrv.ScrnMK23Pickup')
pickupReplaceArray(13)=(oldClass=Class'KFMod.DeaglePickup',NewClass=Class'ScrnBalanceSrv.ScrnDeaglePickup')
pickupReplaceArray(14)=(oldClass=Class'KFMod.WinchesterPickup',NewClass=Class'ScrnBalanceSrv.ScrnWinchesterPickup')
pickupReplaceArray(15)=(oldClass=Class'KFMod.SPSniperPickup',NewClass=Class'ScrnBalanceSrv.ScrnSPSniperPickup')
pickupReplaceArray(16)=(oldClass=Class'KFMod.M14EBRPickup',NewClass=Class'ScrnBalanceSrv.ScrnM14EBRPickup')
pickupReplaceArray(17)=(oldClass=Class'KFMod.M99Pickup',NewClass=Class'ScrnBalanceSrv.ScrnM99Pickup')
pickupReplaceArray(18)=(oldClass=Class'KFMod.BullpupPickup',NewClass=Class'ScrnBalanceSrv.ScrnBullpupPickup')
pickupReplaceArray(19)=(oldClass=Class'KFMod.AK47Pickup',NewClass=Class'ScrnBalanceSrv.ScrnAK47Pickup')
pickupReplaceArray(20)=(oldClass=Class'KFMod.M4Pickup',NewClass=Class'ScrnBalanceSrv.ScrnM4Pickup')
pickupReplaceArray(21)=(oldClass=Class'KFMod.SPThompsonPickup',NewClass=Class'ScrnBalanceSrv.ScrnSPThompsonPickup')
pickupReplaceArray(22)=(oldClass=Class'KFMod.ThompsonDrumPickup',NewClass=Class'ScrnBalanceSrv.ScrnThompsonDrumPickup')
pickupReplaceArray(23)=(oldClass=Class'KFMod.SCARMK17Pickup',NewClass=Class'ScrnBalanceSrv.ScrnSCARMK17Pickup')
pickupReplaceArray(24)=(oldClass=Class'KFMod.FNFAL_ACOG_Pickup',NewClass=Class'ScrnBalanceSrv.ScrnFNFAL_ACOG_Pickup')
pickupReplaceArray(25)=(oldClass=Class'KFMod.MachetePickup',NewClass=Class'ScrnBalanceSrv.ScrnMachetePickup')
pickupReplaceArray(26)=(oldClass=Class'KFMod.AxePickup',NewClass=Class'ScrnBalanceSrv.ScrnAxePickup')
pickupReplaceArray(27)=(oldClass=Class'KFMod.ChainsawPickup',NewClass=Class'ScrnBalanceSrv.ScrnChainsawPickup')
pickupReplaceArray(28)=(oldClass=Class'KFMod.KatanaPickup',NewClass=Class'ScrnBalanceSrv.ScrnKatanaPickup')
pickupReplaceArray(29)=(oldClass=Class'KFMod.ScythePickup',NewClass=Class'ScrnBalanceSrv.ScrnScythePickup')
pickupReplaceArray(30)=(oldClass=Class'KFMod.ClaymoreSwordPickup',NewClass=Class'ScrnBalanceSrv.ScrnClaymoreSwordPickup')
pickupReplaceArray(31)=(oldClass=Class'KFMod.CrossbuzzsawPickup',NewClass=Class'ScrnBalanceSrv.ScrnCrossbuzzsawPickup')
pickupReplaceArray(32)=(oldClass=Class'KFMod.MAC10Pickup',NewClass=Class'ScrnBalanceSrv.ScrnMAC10Pickup')
pickupReplaceArray(33)=(oldClass=Class'KFMod.FlareRevolverPickup',NewClass=Class'ScrnBalanceSrv.ScrnFlareRevolverPickup')
pickupReplaceArray(34)=(oldClass=Class'KFMod.DualFlareRevolverPickup',NewClass=Class'ScrnBalanceSrv.ScrnDualFlareRevolverPickup')
pickupReplaceArray(35)=(oldClass=Class'KFMod.FlameThrowerPickup',NewClass=Class'ScrnBalanceSrv.ScrnFlameThrowerPickup')
pickupReplaceArray(36)=(oldClass=Class'KFMod.HuskGunPickup',NewClass=Class'ScrnBalanceSrv.ScrnHuskGunPickup')
pickupReplaceArray(37)=(oldClass=Class'KFMod.PipeBombPickup',NewClass=Class'ScrnBalanceSrv.ScrnPipeBombPickup')
pickupReplaceArray(38)=(oldClass=Class'KFMod.M4203Pickup',NewClass=Class'ScrnBalanceSrv.ScrnM4203Pickup')
pickupReplaceArray(39)=(oldClass=Class'KFMod.M32Pickup',NewClass=Class'ScrnBalanceSrv.ScrnM32Pickup')
pickupReplaceArray(40)=(oldClass=Class'KFMod.LAWPickup',NewClass=Class'ScrnBalanceSrv.ScrnLAWPickup')
pickupReplaceArray(41)=(oldClass=Class'KFMod.Dual44MagnumPickup',NewClass=Class'ScrnBalanceSrv.ScrnDual44MagnumPickup')
pickupReplaceArray(42)=(oldClass=Class'KFMod.DualMK23Pickup',NewClass=Class'ScrnBalanceSrv.ScrnDualMK23Pickup')
pickupReplaceArray(43)=(oldClass=Class'KFMod.DualDeaglePickup',NewClass=Class'ScrnBalanceSrv.ScrnDualDeaglePickup')
pickupReplaceArray(44)=(oldClass=Class'KFMod.SyringePickup',NewClass=Class'ScrnBalanceSrv.ScrnSyringePickup')
pickupReplaceArray(45)=(oldClass=Class'KFMod.FragPickup',NewClass=Class'ScrnBalanceSrv.ScrnFragPickup')
pickupReplaceArray(46)=(oldClass=Class'KFMod.M79Pickup',NewClass=Class'ScrnBalanceSrv.ScrnM79Pickup')
pickupReplaceArray(47)=(oldClass=Class'KFMod.CrossbowPickup',NewClass=Class'ScrnBalanceSrv.ScrnCrossbowPickup')
pickupReplaceArray(48)=(oldClass=Class'KFMod.KnifePickup',NewClass=Class'ScrnBalanceSrv.ScrnKnifePickup')
FragReplacementIndex=45
bReplacePickups=True
bReplacePickupsStory=True
Functions=Class'ScrnBalanceSrv.ScrnFunctions'
GameStartCountDown=12
Perks(0)=Class'ScrnBalanceSrv.ScrnVetFieldMedic'
Perks(1)=Class'ScrnBalanceSrv.ScrnVetSupportSpec'
Perks(2)=Class'ScrnBalanceSrv.ScrnVetSharpshooter'
Perks(3)=Class'ScrnBalanceSrv.ScrnVetCommando'
Perks(4)=Class'ScrnBalanceSrv.ScrnVetBerserker'
Perks(5)=Class'ScrnBalanceSrv.ScrnVetFirebug'
Perks(6)=Class'ScrnBalanceSrv.ScrnVetDemolitions'
Perks(7)=Class'ScrnBalanceSrv.ScrnVeterancyTypes'
Perks(8)=Class'ScrnBalanceSrv.ScrnVetGunslinger'
Perks(9)=Class'ScrnBalanceSrv.ScrnVetCombatMedic'
strAchEarn="%p earned an achievement: %a"
bBroadcastAchievementEarn=True
AchievementFlags=255
bSaveStatsOnAchievementEarned=True
bTradingDoorsOpen=True
bAllowVoting=True
bPauseTraderOnly=True
SkippedTradeTimeMult=1.000000
bAllowBlameVote=True
bAllowKickVote=True
BlameVoteCoolDown=60
bAllowPauseVote=True
bAllowLockPerkVote=True
bAllowBoringVote=True
MaxVoteKillMonsters=5
MaxVoteKillHP=2000
bVoteKillCheckVisibility=True
VoteKillPenaltyMult=5.000000
MinVoteDifficulty=2
ServerPerksPkgName="ScrnSP.ServerPerksMutSE"
bReplaceHUD=True
bReplaceScoreBoard=True
Post6ZedSpawnInc=0.250000
Post6AmmoSpawnInc=0.200000
Post6ZedsPerPlayer=0.400000
bAlterWaveSize=True
MaxWaveSize=800
MaxZombiesOnce=48
EndGameStatBonus=0.500000
FirstStatBonusMult=2.000000
bStatBonusUsesHL=True
SharpProgMinDmg=1000
bBroadcastPickups=True
BroadcastPickupText="%p picked up %o's %w ($%$)."
bAutoKickOffPerkPlayers=True
strAutoKickOffPerk="You have been auto kicked from the server for playing without a perk. Type RECONNECT in the console to join the server again and choose a perk."
SpawnInventory(0)="0:ScrnBalanceSrv.ScrnCombatVestPickup:5-255:100"
SpawnInventory(1)="0:ScrnBalanceSrv.ScrnMP7MPickup:6-255:200+20:157"
SpawnInventory(2)="1:ScrnBalanceSrv.ScrnShotgunPickup:5:24:150"
SpawnInventory(3)="1:ScrnBalanceSrv.ScrnBoomStickPickup:6-255:24+6:225"
SpawnInventory(4)="2:ScrnBalanceSrv.ScrnWinchesterPickup:5:40:150"
SpawnInventory(5)="2:ScrnBalanceSrv.ScrnCrossbowPickup:6-255:12+3:225"
SpawnInventory(6)="3:ScrnBalanceSrv.ScrnBullpupPickup:5:200:150"
SpawnInventory(7)="3:ScrnBalanceSrv.ScrnAK47Pickup:6-255:150+30:225"
SpawnInventory(8)="4:ScrnBalanceSrv.ScrnAxePickup:5::150"
SpawnInventory(9)="4:ScrnBalanceSrv.ScrnChainsawPickup:6-255:500+50:225"
SpawnInventory(10)="5:ScrnBalanceSrv.ScrnMAC10Pickup:5:200:150"
SpawnInventory(11)="5:ScrnBalanceSrv.ScrnFlameThrowerPickup:6-255:320+80:225"
SpawnInventory(12)="6:ScrnBalanceSrv.ScrnFragPickup:5:10"
SpawnInventory(13)="6:ScrnBalanceSrv.ScrnM79Pickup:6-255:12+2:225"
SpawnInventory(14)="8:ScrnBalanceSrv.ScrnDualiesPickup:5:150:150"
SpawnInventory(15)="8:ScrnBalanceSrv.ScrnDual44MagnumPickup:6-255:66+12:225"
StartCashNormal=250
StartCashHard=250
StartCashSui=200
StartCashHoE=200
MinRespawnCashNormal=200
MinRespawnCashHard=200
MinRespawnCashSui=150
MinRespawnCashHoE=100
TraderTimeNormal=60
TraderTimeHard=60
TraderTimeSui=60
TraderTimeHoE=60
bLeaveCashOnDisconnect=True
bPlayerZEDTime=True
ColorTags(0)=(t="^0",R=1,G=1,B=1)
ColorTags(1)=(t="^1",R=200,G=1,B=1)
ColorTags(2)=(t="^2",R=1,G=200,B=1)
ColorTags(3)=(t="^3",R=200,G=200,B=1)
ColorTags(4)=(t="^4",R=1,G=1,B=255)
ColorTags(5)=(t="^5",R=1,G=255,B=255)
ColorTags(6)=(t="^6",R=200,G=1,B=200)
ColorTags(7)=(t="^7",R=200,G=200,B=200)
ColorTags(8)=(t="^8",R=255,G=127)
ColorTags(9)=(t="^9",R=128,G=128,B=128)
ColorTags(10)=(t="^w$",R=255,G=255,B=255)
ColorTags(11)=(t="^r$",R=255,G=1,B=1)
ColorTags(12)=(t="^g$",R=1,G=255,B=1)
ColorTags(13)=(t="^b$",R=1,G=1,B=255)
ColorTags(14)=(t="^y$",R=255,G=255,B=1)
ColorTags(15)=(t="^c$",R=1,G=255,B=255)
ColorTags(16)=(t="^o$",R=255,G=140,B=1)
ColorTags(17)=(t="^u$",R=255,G=20,B=147)
ColorTags(18)=(t="^s$",R=1,G=192,B=255)
ColorTags(19)=(t="^n$",R=139,G=69,B=19)
ColorTags(20)=(t="^W$",R=112,G=138,B=144)
ColorTags(21)=(t="^R$",R=132,G=1,B=1)
ColorTags(22)=(t="^G$",R=1,G=132,B=1)
ColorTags(23)=(t="^B$",R=1,G=1,B=132)
ColorTags(24)=(t="^Y$",R=255,G=192,B=1)
ColorTags(25)=(t="^C$",R=1,G=160,B=192)
ColorTags(26)=(t="^O$",R=255,G=69,B=1)
ColorTags(27)=(t="^U$",R=160,G=32,B=240)
ColorTags(28)=(t="^S$",R=65,G=105,B=225)
ColorTags(29)=(t="^N$",R=80,G=40,B=20)
MinZedSpawnPeriod=2.000000
bSpawnRateFix=True
bServerInfoVeterancy=True
AmmoBoxMesh=StaticMesh'kf_generic_sm.pickups.Metal_Ammo_Box'
AmmoBoxDrawScale=1.000000
AmmoBoxDrawScale3D=(X=1.000000,Y=1.000000,Z=1.000000)
HighlyDecorated(0)=(SteamID32=3907835,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(1)=(SteamID32=4787302,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(2)=(SteamID32=15243342,ClanIconRef="ScrnTex.Players.Code",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Medic_Grey")
HighlyDecorated(3)=(SteamID32=18524053,AvatarRef="ScrnTex.Players.FabZen",ClanIconRef="ScrnTex.Players.SALTY",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Commander_Grey",PostNameIconRef="KillingFloor2HUD.PerkReset.PReset_Commander_Grey")
HighlyDecorated(4)=(SteamID32=18871148,ClanIconRef="ScrnTex.Players.SALTY",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Berserker_Grey")
HighlyDecorated(5)=(SteamID32=20530727,AvatarRef="ScrnTex.Players.LazyBunta")
HighlyDecorated(6)=(SteamID32=21825964,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(7)=(SteamID32=25188900,AvatarRef="ScrnTex.Players.Scuddles",PreNameIconRef="ScrnTex.Perks.Hud_Perk_Star_Gray",PostNameIconRef="ScrnTex.Perks.Hud_Perk_Star_Gray")
HighlyDecorated(8)=(SteamID32=26505257,AvatarRef="ScrnTex.Players.Duckbuster",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Support_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(9)=(SteamID32=27263782,AvatarRef="ScrnTex.Players.Termcat",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(10)=(SteamID32=27784497,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(11)=(SteamID32=32271863,AvatarRef="ScrnTex.Players.PooSH",PreNameIconRef="ScrnTex.Perks.Hud_Perk_Star_Gray",PostNameIconRef="ScrnTex.Perks.Hud_Perk_Star_Gray")
HighlyDecorated(12)=(SteamID32=32279441,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(13)=(SteamID32=32708029,ClanIconRef="ScrnTex.Players.Dosh",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Firebug_Grey")
HighlyDecorated(14)=(SteamID32=32779545,ClanIconRef="ScrnTex.Players.SALTY",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Support_Grey")
HighlyDecorated(15)=(SteamID32=32976519,ClanIconRef="ScrnTex.Players.Dosh",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey",Playoffs=1)
HighlyDecorated(16)=(SteamID32=34308728,AvatarRef="ScrnTex.Players.Janitor",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Firebug_Grey",PostNameIconRef="KillingFloor2HUD.PerkReset.PReset_Firebug_Grey")
HighlyDecorated(17)=(SteamID32=37444251,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(18)=(SteamID32=41734606,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(19)=(SteamID32=43087787,AvatarRef="ScrnTex.Players.Chaos",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(20)=(SteamID32=43944237,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(21)=(SteamID32=44141219,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(22)=(SteamID32=44328745,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(23)=(SteamID32=44388687,AvatarRef="ScrnTex.Players.Aze",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Commander_Grey")
HighlyDecorated(24)=(SteamID32=45006648,ClanIconRef="ScrnTex.Players.SALTY",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey")
HighlyDecorated(25)=(SteamID32=45088649,AvatarRef="ScrnTex.Players.Joe",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Berserker_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',PrefixIconColor=(B=255,G=255,R=255),Playoffs=1)
HighlyDecorated(26)=(SteamID32=45352894,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(27)=(SteamID32=45594574,AvatarRef="ScrnTex.Players.CodeReaper",PreNameIconRef="ScrnTex.Players.BowieKnifeLeft",PostNameIconRef="ScrnTex.Players.BowieKnifeRight")
HighlyDecorated(28)=(SteamID32=46023864,AvatarRef="ScrnTex.Players.Baron",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Medic_Grey",PostNameIconRef="ScrnAch_T.Achievements.Baron",PostfixIconColor=(B=255,G=255,R=255,A=255))
HighlyDecorated(29)=(SteamID32=47199674,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(30)=(SteamID32=47820768,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(31)=(SteamID32=49361376,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(32)=(SteamID32=51667940,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(33)=(SteamID32=52109233,PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Support_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(34)=(SteamID32=53781980,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(35)=(SteamID32=54179805,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(36)=(SteamID32=54471316,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(37)=(SteamID32=57193815,ClanIconRef="ScrnTex.Players.Dosh",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey")
HighlyDecorated(38)=(SteamID32=58813412,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(39)=(SteamID32=59018230,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(40)=(SteamID32=59344954,PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Support_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(41)=(SteamID32=59865355,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(42)=(SteamID32=61480134,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(43)=(SteamID32=61647562,ClanIconRef="ScrnTex.Players.Dosh",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Demolition_Grey")
HighlyDecorated(44)=(SteamID32=63934831,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(45)=(SteamID32=64861994,AvatarRef="ScrnTex.Players.dkanus",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(46)=(SteamID32=66725591,ClanIconRef="ScrnTex.Players.SALTY",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Medic_Grey")
HighlyDecorated(47)=(SteamID32=66767874,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(48)=(SteamID32=67141366,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(49)=(SteamID32=68703215,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(50)=(SteamID32=68932148,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(51)=(SteamID32=70606615,AvatarRef="ScrnTex.Players.aaa")
HighlyDecorated(52)=(SteamID32=71427768,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(53)=(SteamID32=71462150,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey")
HighlyDecorated(54)=(SteamID32=72523409,AvatarRef="ScrnTex.Players.Candybee",PreNameIconRef="ScrnTex.Players.Mudflapgirl_left",PostNameIconRef="ScrnTex.Players.Mudflapgirl_right")
HighlyDecorated(55)=(SteamID32=75600845,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(56)=(SteamID32=76661591,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(57)=(SteamID32=77967745,ClanIconRef="ScrnTex.Players.SALTY",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Demolition_Grey")
HighlyDecorated(58)=(SteamID32=81279347,ClanIconRef="ScrnTex.Players.Dosh")
HighlyDecorated(59)=(SteamID32=81947447,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(60)=(SteamID32=82239578,AvatarRef="ScrnTex.Players.Toaste",PreNameIconRef="ScrnTex.Players.Yinyang",PostNameIconRef="ScrnTex.Players.Yinyang")
HighlyDecorated(61)=(SteamID32=83417929,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(62)=(SteamID32=84050600,AvatarRef="ScrnTex.Players.NikC",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Demolition_Grey")
HighlyDecorated(63)=(SteamID32=84652399,AvatarRef="ScrnTex.Players.Droop",PreNameIconRef="ScrnTex.Players.Batman",PostNameIconRef="ScrnTex.Players.Batman")
HighlyDecorated(64)=(SteamID32=85142081,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(65)=(SteamID32=87647886,ClanIconRef="ScrnTex.Players.Dosh")
HighlyDecorated(66)=(SteamID32=89323130,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(67)=(SteamID32=93919492,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(68)=(SteamID32=95752287,AvatarRef="ScrnTex.Players.FosterKF2",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey",PostNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey",PrefixIconColor=(A=1))
HighlyDecorated(69)=(SteamID32=99293732,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(70)=(SteamID32=102203653,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_chicken")
HighlyDecorated(71)=(SteamID32=102496714,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(72)=(SteamID32=106835439,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(73)=(SteamID32=107039826,AvatarRef="ScrnTex.Players.Seely",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(74)=(SteamID32=109654784,AvatarRef="ScrnTex.Players.BertieDastard",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Support_Grey")
HighlyDecorated(75)=(SteamID32=110496233,AvatarRef="ScrnTex.Players.VP",ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_dragon")
HighlyDecorated(76)=(SteamID32=112564543,ClanIconRef="ScrnTex.Players.Dosh",PreNameIconRef="ScrnTex.Perks.Hud_Perk_Star_Gray",PostNameIconRef="ScrnTex.Perks.Hud_Perk_Star_Gray")
HighlyDecorated(77)=(SteamID32=113961551,AvatarRef="ScrnTex.Players.Baffi",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Demolition_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(78)=(SteamID32=114826433,AvatarRef="ScrnTex.Players.nmm",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey",ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(79)=(SteamID32=121550025,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(80)=(SteamID32=124874371,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(81)=(SteamID32=127624729,AvatarRef="ScrnTex.Players.Scrublord")
HighlyDecorated(82)=(SteamID32=128107383,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(83)=(SteamID32=128199891,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(84)=(SteamID32=134825301,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(85)=(SteamID32=136078273,ClanIconRef="ScrnTex.Players.SALTY",PreNameIconRef="KillingFloor2HUD.PerkReset.PReset_Firebug_Grey")
HighlyDecorated(86)=(SteamID32=139723798,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(87)=(SteamID32=143999622,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(88)=(SteamID32=150832205,AvatarRef="ScrnTex.Players.Taloril")
HighlyDecorated(89)=(SteamID32=152138369,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(90)=(SteamID32=152983683,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(91)=(SteamID32=153788974,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(92)=(SteamID32=160715546,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(93)=(SteamID32=162712343,ClanIcon=Texture'ScrnTex.Tourney.TourneyMember',Playoffs=1)
HighlyDecorated(94)=(SteamID32=173722095,AvatarRef="ScrnTex.Players.catcat",PreNameIconRef="ScrnTex.Players.RadioActive",PostNameIconRef="ScrnTex.Players.RadioActive")
HighlyDecorated(95)=(SteamID32=182247922,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_kom")
HighlyDecorated(96)=(SteamID32=190669364,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_doll")
HighlyDecorated(97)=(SteamID32=192070782,ClanIconRef="ScrnTex.Tourney.TBN",Playoffs=1,TourneyWon=1)
HighlyDecorated(98)=(SteamID32=319278244,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_doll")
HighlyDecorated(99)=(SteamID32=359866000,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_kom")
HighlyDecorated(100)=(SteamID32=374934166,AvatarRef="ScrnTex.Players.Bligiet",PreNameIconRef="ScrnTex.Players.BowieKnifeLeft",PostNameIconRef="ScrnTex.Players.BowieKnifeRight")
HighlyDecorated(101)=(SteamID32=378534555,AvatarRef="ScrnTex.Players.BossRoss",PreNameIconRef="ScrnTex.Players.BossRossNameIcon",PostNameIconRef="ScrnTex.Players.BossRossNameIcon")
HighlyDecorated(102)=(SteamID32=389474897,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_homer")
HighlyDecorated(103)=(SteamID32=391528064,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_doll")
HighlyDecorated(104)=(SteamID32=397272710,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_doll")
HighlyDecorated(105)=(SteamID32=403136595,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="ScrnTex.Players.Medal_f")
HighlyDecorated(106)=(SteamID32=405312393,ClanIconRef="ScrnTex.Players.VP",PreNameIconRef="ScrnTex.Players.Medal_VP",PostNameIconRef="KillingFloor2HUD.PerkReset.PReset_Sharpshooter_Grey")
bUseDLCLevelLocks=True
bFixMusic=True
LockTeamMinWave=7.000000
LockTeamAutoWave=8.500000
bForceSteamNames=True
MutateCommands(0)="ACCURACY"
MutateCommands(1)="CHECK"
MutateCommands(2)="CMDLINE"
MutateCommands(3)="DEBUGGAME"
MutateCommands(4)="DEBUGPICKUPS"
MutateCommands(5)="DEBUGSPI"
MutateCommands(6)="ENEMIES"
MutateCommands(7)="GIMMECOOKIES"
MutateCommands(8)="HELP"
MutateCommands(9)="HL"
MutateCommands(10)="LEVEL"
MutateCommands(11)="MAPDIFF"
MutateCommands(12)="MAPZEDS"
MutateCommands(13)="MUTLIST"
MutateCommands(14)="PERKSTATS"
MutateCommands(15)="PLAYERLIST"
MutateCommands(16)="STATUS"
MutateCommands(17)="VERSION"
MutateCommands(18)="ZED"
MutateCommands(19)="ZEDLIST"
bScrnWaves=True
bAddToServerPackages=True
GroupName="KF-Scrn"
FriendlyName="The ScrN Balance Server"
Description="This Mutator must be used only on servers. Clients should use workshop version"
bAlwaysRelevant=True
bOnlyDirtyReplication=False
RemoteRole=ROLE_SimulatedProxy
bNetNotify=True
}