3545 lines
141 KiB
Ucode
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
|
|
}
|