888 lines
38 KiB
Ucode
888 lines
38 KiB
Ucode
|
//=============================================================================
|
||
|
// KFGameConductor
|
||
|
//=============================================================================
|
||
|
// Game conductor which dynamically manages the difficulty and fun of the game
|
||
|
//=============================================================================
|
||
|
// Killing Floor 2
|
||
|
// Copyright (C) 2016 Tripwire Interactive LLC
|
||
|
// John "Ramm-Jaeger" Gibson
|
||
|
//=============================================================================
|
||
|
class KFGameConductor extends Object within KFGameInfo
|
||
|
config(Game);
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Player Status
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Represents the health status of all the human team players on the server */
|
||
|
var float PlayersHealthStatus;
|
||
|
/** Represents the ammo status of all the human team players on the server */
|
||
|
var float PlayersAmmoStatus;
|
||
|
/** Represents the player status of all the human team players on the server */
|
||
|
var float AggregatePlayersStatus;
|
||
|
/** Team low health threshold for determining gameplay changes */
|
||
|
var() float PlayersLowHealthThreshold;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Skill Tracking
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** How long are zeds living on average after they have seen an enemy */
|
||
|
var float ZedVisibleAverageLifespan;
|
||
|
/** Total time zeds are living after they have seen an enemy */
|
||
|
var float TotalZedVisibleLifespan;
|
||
|
/** How many zeds have been killed during this match */
|
||
|
var int TotalZedsKilled;
|
||
|
|
||
|
/** How long are zeds living on average after they have seen an enemy, for the current wave */
|
||
|
var float CurrentWaveZedVisibleAverageLifeSpan;
|
||
|
/** Total time zeds are living after they have seen an enemy, for the current wave */
|
||
|
var float CurrentWaveTotalZedVisibleLifeSpan;
|
||
|
/** How many zeds have been killed during this wave */
|
||
|
var int CurrentWaveTotalZedsKilled;
|
||
|
|
||
|
/** How long are zeds living on average after they have seen an enemy, for the last 10 seconds */
|
||
|
var float RecentZedVisibleAverageLifeSpan;
|
||
|
/** Total time zeds are living after they have seen an enemy, for the last 10 seconds */
|
||
|
var float RecentTotalZedVisibleLifeSpan;
|
||
|
/** How many zeds have been killed during tlast 10 seconds */
|
||
|
var int RecentTotalZedsKilled;
|
||
|
|
||
|
/** Keep track of the zed lifespan totals over the last 10 seconds */
|
||
|
var float RecentZedLifeSpanTotalTracker[10];
|
||
|
/** Keep track of the zed kill totals over the last 10 seconds */
|
||
|
var float RecentZedKillTotalTracker[10];
|
||
|
|
||
|
/** Represents the player skill level off all the human team players on the server */
|
||
|
var float AggregatePlayerSkill;
|
||
|
|
||
|
/** Represents the average shooting accuracy of all the human team players on the server */
|
||
|
var float PlayersAverageAccuracy;
|
||
|
/** Represents the average headshot accuracy of all the human team players on the server */
|
||
|
var float PlayersAverageHeadshotAccuracy;
|
||
|
/** What we expect the average player shot accuracy to be */
|
||
|
var float ParShotAccuracy;
|
||
|
/** What we expect the average player shot accuracy to be */
|
||
|
var float ParHeadshotAccuracy;
|
||
|
|
||
|
/** What percentage above BaseLinePlayerShootingSkill to consider a players shooting accuracy "highly skilled" */
|
||
|
var() float HighlySkilledAccuracyMod;
|
||
|
/** What percentage above BaseLinePlayerShootingSkill to use as the max value for calculating the skill modifer */
|
||
|
var() float HighlySkilledAccuracyModMax;
|
||
|
/** What percentage below BaseLinePlayerShootingSkill to consider a players shooting accuracy "less skilled" */
|
||
|
var() float LessSkilledAccuracyMod;
|
||
|
/** What percentage below BaseLinePlayerShootingSkill to use as the min value for calculating the skill modifer */
|
||
|
var() float LessSkilledAccuracyModMin;
|
||
|
|
||
|
/** What is the baseline player shooting skill value */
|
||
|
var float BaseLinePlayerShootingSkill;
|
||
|
/** What percentage weight should general shooting accuracy have for the BaseLinePlayerShootingSkill */
|
||
|
var() float ShootingAccuracySkillWeight;
|
||
|
/** What percentage weight should headshot accuracy have for the BaseLinePlayerShootingSkill */
|
||
|
var() float HeadShootingAccuracySkillWeight;
|
||
|
|
||
|
/** What we expect the average zed lifespan to be, by difficulty */
|
||
|
var float ParZedLifeSpan[4];
|
||
|
/** What we expect the average zed lifespan to be, by difficulty on solo*/
|
||
|
var float ParZedLifeSpanSolo[4];
|
||
|
|
||
|
/** What percentage of ParZedLifeSpan to consider how quickly zeds are being killed as "highly skilled" */
|
||
|
var() float ZedLifeSpanHighlySkilledThreshold;
|
||
|
/** What percentage of ParZedLifeSpan to use as the min value for calculating the skill modife */
|
||
|
var() float ZedLifeSpanHighlySkilledThresholdMin;
|
||
|
/** What percentage of ParZedLifeSpan to consider how quickly zeds are being killed as "less skilled" */
|
||
|
var() float ZedLifeSpanLessSkilledThreshold;
|
||
|
/** What percentage of ParZedLifeSpan to use as the max value for calculating the skill modifer */
|
||
|
var() float ZedLifeSpanLessSkilledThresholdMax;
|
||
|
|
||
|
/** The overall modifier to use based on the players' perk rank and playing skill */
|
||
|
var float OverallRankAndSkillModifier;
|
||
|
/** What percentage of OverallRankAndSkillModifier does PerkRank count toward */
|
||
|
var() float PerkRankPercentOfOverallSkill;
|
||
|
/** What percentage of OverallRankAndSkillModifier does shooting accuracy count toward */
|
||
|
var() float AccuracyPercentOfOverallSkill;
|
||
|
/** What percentage of OverallRankAndSkillModifier does zed lifespan count toward */
|
||
|
var() float ZedLifeSpanPercentOfOverallSkill;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Game Status
|
||
|
**********************************************************************************************/
|
||
|
|
||
|
enum EGameConductorStatus
|
||
|
{
|
||
|
GCS_Normal,
|
||
|
GCS_ForceLull
|
||
|
};
|
||
|
|
||
|
/** Current overall status of the game conductor */
|
||
|
var EGameConductorStatus GameConductorStatus;
|
||
|
|
||
|
/** How long to force a low intensity lull for if a player dies */
|
||
|
var() float PlayerDeathForceLullLength;
|
||
|
/** When we lasted forced a low intensity lull for a player dying */
|
||
|
var() float PlayerDeathForceLullTime;
|
||
|
|
||
|
/** How long to force a low intensity lull for if a solo player is surrounded */
|
||
|
var() float SoloPlayerSurroundedForceLullLength;
|
||
|
/** When we lasted forced a low intensity lull for a surrounded solo player */
|
||
|
var() float SoloPlayerSurroundedForceLullTime;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Player Experience Level
|
||
|
**********************************************************************************************/
|
||
|
|
||
|
/** The average of the perk rank for all the players on the server */
|
||
|
var float AveragePlayerPerkRank;
|
||
|
|
||
|
/** The target perk range for each difficulty level */
|
||
|
var() vector2d TargetPerkRankRange[4];
|
||
|
|
||
|
/** What is the median target perk rank for the current game */
|
||
|
var float CurrentTargetPerkRank;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Gameplay modification
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Struct containing settings for the lull state */
|
||
|
struct sLullInfo
|
||
|
{
|
||
|
/** Maximum allowed lull duration */
|
||
|
var float MaxDuration;
|
||
|
/** Cooldown until next lull period is allowed */
|
||
|
var float Cooldown;
|
||
|
};
|
||
|
|
||
|
/** How much the spawn rate can be modified by difficulty based on perk rank and how well the team is doing. At 0 it will be as slow is you want it to go, at 1.0 as fast as you want it to go, and at 0.5 should be no change */
|
||
|
var() InterpCurveFloat SpawnRateModificationRangeCurve[4];
|
||
|
|
||
|
/** Current spawn rate modification */
|
||
|
var() float CurrentSpawnRateModification;
|
||
|
|
||
|
/**
|
||
|
* What percentage of the difference between the zed movement speed on the current
|
||
|
* difficulty, and the zed movement speed on the next highest difficulty, do we want
|
||
|
* to add/subtract to the movement speed based on how well/poorly a team is doing.
|
||
|
* X = the % difference to use for slowing zeds down if a team is doing poorly,
|
||
|
* Y = the % difference to use for speeding up zeds if the team is doing well.
|
||
|
* Valid values for X are between 0 (no change) and 0.9. Valid values for Y are
|
||
|
* between 0 (no change) and 1.0. For example, using 0.5 for X and Y would give
|
||
|
* you an increase or decrease in movement speed halfway in between the speed of
|
||
|
* the zeds on the current difficulty, and the speed of the zeds on the next higher
|
||
|
* difficulty.
|
||
|
*/
|
||
|
var() vector2d AIMovementSpeedModificationRange[4];
|
||
|
|
||
|
/** Current AI Movement speed modifier */
|
||
|
var float CurrentAIMovementSpeedMod;
|
||
|
/** Current modification the game conductor is making to versus player zed health */
|
||
|
var float CurrentVersusZedHealthMod;
|
||
|
/** Current modification the game conductor is making to versus player zed damage */
|
||
|
var float CurrentVersusZedDamageMod;
|
||
|
|
||
|
/** Whether or not this game difficulty allows low intensity zed mode when players get low on health, forced lull, etc */
|
||
|
var() int AllowLowIntensityZedModeByDifficulty[4];
|
||
|
|
||
|
/** Per-difficulty settings for the lull state */
|
||
|
var() array<sLullInfo> LullSettings;
|
||
|
/** Last time the lull cooldown was triggered */
|
||
|
var transient float LullCooldownStartTime;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Logging and debugging
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Whether or not to log Game Conductor activity */
|
||
|
var() bool bLogGameConductor;
|
||
|
|
||
|
/** When true bypass the game conductor making any adjustments */
|
||
|
var() bool bBypassGameConductor;
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Player Status
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Initialize the game conductor */
|
||
|
function Initialize()
|
||
|
{
|
||
|
BaseLinePlayerShootingSkill = ParShotAccuracy * ShootingAccuracySkillWeight + ParHeadshotAccuracy * HeadShootingAccuracySkillWeight;
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************
|
||
|
* @name Skill Tracking
|
||
|
********************************************************************************************* */
|
||
|
|
||
|
/** Update stats when a zed is killed */
|
||
|
function HandleZedKill( float ZedVisibleTimeAlive )
|
||
|
{
|
||
|
TotalZedsKilled++;
|
||
|
TotalZedVisibleLifespan += ZedVisibleTimeAlive;
|
||
|
ZedVisibleAverageLifespan = TotalZedVisibleLifespan/float(TotalZedsKilled);
|
||
|
|
||
|
CurrentWaveTotalZedsKilled++;
|
||
|
CurrentWaveTotalZedVisibleLifeSpan += ZedVisibleTimeAlive;
|
||
|
CurrentWaveZedVisibleAverageLifeSpan = CurrentWaveTotalZedVisibleLifeSpan/float(CurrentWaveTotalZedsKilled);
|
||
|
|
||
|
RecentTotalZedsKilled++;
|
||
|
RecentTotalZedVisibleLifeSpan += ZedVisibleTimeAlive;
|
||
|
|
||
|
//`log("TotalZedsKilled = "$TotalZedsKilled$" TotalZedVisibleLifespan = "$TotalZedVisibleLifespan$" ZedVisibleAverageLifespan = "$ZedVisibleAverageLifespan$" seconds");
|
||
|
//`log("CurrentWaveTotalZedsKilled = "$CurrentWaveTotalZedsKilled$" CurrentWaveZedVisibleAverageLifeSpan = "$CurrentWaveZedVisibleAverageLifeSpan$" CurrentWaveZedVisibleAverageLifeSpan = "$CurrentWaveZedVisibleAverageLifeSpan$" seconds");
|
||
|
}
|
||
|
|
||
|
/** Let the game conductor know a human team pawn just died */
|
||
|
function NotifyHumanTeamPlayerDeath()
|
||
|
{
|
||
|
if( !MyKFGRI.IsBossWave() )
|
||
|
{
|
||
|
`log("Human team player died, forcing a lull for "$PlayerDeathForceLullLength$" seconds!", bLogGameConductor);
|
||
|
|
||
|
GameConductorStatus = GCS_ForceLull;
|
||
|
PlayerDeathForceLullTime = WorldInfo.TimeSeconds;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Let the game conductor know that the solo player has been surrounded */
|
||
|
function NotifySoloPlayerSurrounded()
|
||
|
{
|
||
|
if( !MyKFGRI.IsBossWave() && GameConductorStatus != GCS_ForceLull )
|
||
|
{
|
||
|
`log("Human solo player surrounded, forcing a lull for "$SoloPlayerSurroundedForceLullLength$" seconds!", bLogGameConductor);
|
||
|
|
||
|
GameConductorStatus = GCS_ForceLull;
|
||
|
SoloPlayerSurroundedForceLullTime = WorldInfo.TimeSeconds;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Reset stats that are collected for a single wave */
|
||
|
function ResetWaveStats()
|
||
|
{
|
||
|
CurrentWaveTotalZedsKilled=0;
|
||
|
CurrentWaveTotalZedVisibleLifeSpan=0;
|
||
|
CurrentWaveZedVisibleAverageLifeSpan=0;
|
||
|
}
|
||
|
|
||
|
/** Update the average perk rank of the players on the server */
|
||
|
function UpdateAveragePerkRank()
|
||
|
{
|
||
|
local KFPlayerController KFPC;
|
||
|
local KFPerk MyPerk;
|
||
|
local int NumHumanTeamPlayers;
|
||
|
local float TotalPerkRank;
|
||
|
|
||
|
foreach WorldInfo.AllControllers( class'KFPlayerController', KFPC )
|
||
|
{
|
||
|
if( KFPC.GetTeamNum() == 0 )
|
||
|
{
|
||
|
MyPerk = KFPC.CurrentPerk;
|
||
|
|
||
|
if( MyPerk != none )
|
||
|
{
|
||
|
//`log(KFPC$" MyPerk = "$MyPerk$" MyPerk.GetLevel() = "$MyPerk.GetLevel());
|
||
|
NumHumanTeamPlayers++;
|
||
|
TotalPerkRank += Float(MyPerk.GetLevel());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( NumHumanTeamPlayers > 0 )
|
||
|
{
|
||
|
AveragePlayerPerkRank = TotalPerkRank/NumHumanTeamPlayers;
|
||
|
//`log(self@GetFuncName()$" AveragePlayerPerkRank = "$AveragePlayerPerkRank$" TotalPerkRank = "$TotalPerkRank$" NumHumanTeamPlayers = "$NumHumanTeamPlayers);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
AveragePlayerPerkRank = 0;
|
||
|
//`log(self@GetFuncName()$" AveragePlayerPerkRank = "$AveragePlayerPerkRank$" TotalPerkRank = "$TotalPerkRank$" NumHumanTeamPlayers = "$NumHumanTeamPlayers);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/** Handle someone switching teams */
|
||
|
function HandlePlayerChangedTeam()
|
||
|
{
|
||
|
// Perk levels are now valid handle that
|
||
|
UpdateAveragePerkRank();
|
||
|
}
|
||
|
|
||
|
/** Called once per second to evaluate changes to gameplay */
|
||
|
function TimerUpdate()
|
||
|
{
|
||
|
UpdatePlayerAccuracyStats();
|
||
|
UpdateZedLifespanStats();
|
||
|
UpdatePlayersStatus();
|
||
|
UpdatePlayersAggregateSkill();
|
||
|
|
||
|
UpdateOverallStatus();
|
||
|
|
||
|
EvaluateSpawnRateModification();
|
||
|
EvaluateAIMovementSpeedModification();
|
||
|
}
|
||
|
|
||
|
/** Update the tracking of the status of the players on the server */
|
||
|
function UpdatePlayersStatus()
|
||
|
{
|
||
|
local Controller C;
|
||
|
local KFPawn_Human KFP;
|
||
|
local float TotalHealth, TotalAmmo, TotalMaxAmmo;
|
||
|
local KFInventoryManager KFIM;
|
||
|
local int i;
|
||
|
local KFPlayerReplicationInfo KFPRI;
|
||
|
local float NumHumanPlayers;
|
||
|
|
||
|
UpdateAveragePerkRank();
|
||
|
|
||
|
foreach WorldInfo.AllControllers( class'Controller', C )
|
||
|
{
|
||
|
if( C.GetTeamNum() == 0 )
|
||
|
{
|
||
|
// Add up the health of living players
|
||
|
if( C.Pawn != none )
|
||
|
{
|
||
|
KFP = KFPawn_Human(C.Pawn);
|
||
|
|
||
|
if( KFP != none && KFPawn_Customization(KFP) == none )
|
||
|
{
|
||
|
TotalHealth += Float(KFP.Health)/Float(KFP.HealthMax);
|
||
|
NumHumanPlayers += 1.0;
|
||
|
//`log("C = "$C$" KFP.Health = "$KFP.Health$" KFP.HealthMax = "$KFP.HealthMax$" C.Pawn = "$C.Pawn);
|
||
|
|
||
|
KFIM = KFInventoryManager(KFP.InvManager);
|
||
|
if (KFIM !=none)
|
||
|
{
|
||
|
TotalAmmo += KFIM.GetPrimaryAmmoPercentage();
|
||
|
TotalMaxAmmo += 1.0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Add up the health (or lack thereof) of players that don't have a pawn, so have
|
||
|
// already died. Doesn't count newly joined players waiting to spawn.
|
||
|
else if( C.PlayerReplicationInfo != none && !C.PlayerReplicationInfo.bOnlySpectator &&
|
||
|
!C.PlayerReplicationInfo.bWaitingPlayer )
|
||
|
{
|
||
|
KFPRI = KFPlayerReplicationInfo(C.PlayerReplicationInfo);
|
||
|
|
||
|
if( KFPRI != none )
|
||
|
{
|
||
|
//`log("C = "$C$" KFPRI.PlayerHealthPercent = "$ByteToFloat(KFPRI.PlayerHealthPercent)$" KFP.HealthMax = 100");
|
||
|
TotalHealth += ByteToFloat(KFPRI.PlayerHealthPercent);
|
||
|
NumHumanPlayers += 1.0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( NumHumanPlayers > 0 )
|
||
|
{
|
||
|
PlayersHealthStatus = TotalHealth/NumHumanPlayers;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PlayersHealthStatus = 0;
|
||
|
}
|
||
|
|
||
|
if( TotalMaxAmmo > 0 )
|
||
|
{
|
||
|
PlayersAmmoStatus = TotalAmmo/TotalMaxAmmo;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PlayersAmmoStatus = 0;
|
||
|
}
|
||
|
|
||
|
// Aggregates all player status variables into one value
|
||
|
AggregatePlayersStatus = (PlayersHealthStatus * 0.5) + (PlayersAmmoStatus * 0.5);
|
||
|
|
||
|
//`log("PlayersHealthStatus = "$PlayersHealthStatus$" PlayersAmmoStatus = "$PlayersAmmoStatus$" AggregatePlayerStatus = "$AggregatePlayersStatus);
|
||
|
|
||
|
for( i = 0; i < (ArrayCount(MyKFGRI.PlayersHealthStatusTracker) - 1); i++ )
|
||
|
{
|
||
|
MyKFGRI.PlayersHealthStatusTracker[i] = MyKFGRI.PlayersHealthStatusTracker[i+1];
|
||
|
MyKFGRI.PlayersAmmoStatusTracker[i] = MyKFGRI.PlayersAmmoStatusTracker[i+1];
|
||
|
MyKFGRI.AggregatePlayersStatusTracker[i] = MyKFGRI.AggregatePlayersStatusTracker[i+1];
|
||
|
}
|
||
|
|
||
|
MyKFGRI.PlayersHealthStatusTracker[ArrayCount(MyKFGRI.PlayersHealthStatusTracker) -1] = PlayersHealthStatus;
|
||
|
MyKFGRI.PlayersAmmoStatusTracker[ArrayCount(MyKFGRI.PlayersAmmoStatusTracker) -1] = PlayersAmmoStatus;
|
||
|
MyKFGRI.AggregatePlayersStatusTracker[ArrayCount(MyKFGRI.AggregatePlayersStatusTracker) -1] = AggregatePlayersStatus;
|
||
|
}
|
||
|
|
||
|
/** Update the tracking of the status of the players on the server */
|
||
|
function UpdatePlayersAggregateSkill()
|
||
|
{
|
||
|
local int i;
|
||
|
|
||
|
// Heuristic for how players are performing.
|
||
|
AggregatePlayerSkill = PlayersAverageAccuracy * 0.25 + PlayersAverageHeadshotAccuracy * 0.75;
|
||
|
|
||
|
for( i = 0; i < (ArrayCount(MyKFGRI.AggregatePlayerSkillTracker) - 1); i++ )
|
||
|
{
|
||
|
MyKFGRI.AggregatePlayerSkillTracker[i] = MyKFGRI.AggregatePlayerSkillTracker[i+1];
|
||
|
}
|
||
|
|
||
|
MyKFGRI.AggregatePlayerSkillTracker[ArrayCount(MyKFGRI.AggregatePlayerSkillTracker) -1] = AggregatePlayerSkill;
|
||
|
}
|
||
|
|
||
|
/** Update the accuracy stats of the players on the server */
|
||
|
function UpdatePlayerAccuracyStats()
|
||
|
{
|
||
|
local KFPlayerController KFPC;
|
||
|
local int NumHumanTeamPlayers;
|
||
|
local float TotalAccuracy, TotalHeadShotAccuracy;
|
||
|
local int i;
|
||
|
|
||
|
foreach WorldInfo.AllControllers( class'KFPlayerController', KFPC )
|
||
|
{
|
||
|
if( KFPC.GetTeamNum() == 0 && KFPC.GetPerk() != none )
|
||
|
{
|
||
|
NumHumanTeamPlayers++;
|
||
|
if( KFPC.ShotsFired > 0 )
|
||
|
{
|
||
|
TotalAccuracy += (Float(KFPC.ShotsHit)/Float(KFPC.ShotsFired) * 100.0) + KFPC.GetPerk().HitAccuracyHandicap;
|
||
|
TotalHeadShotAccuracy += (Float(KFPC.ShotsHitHeadshot)/Float(KFPC.ShotsFired) * 100.0) + KFPC.GetPerk().HeadshotAccuracyHandicap;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < (ArrayCount(MyKFGRI.PlayerAccuracyTracker) - 1); i++ )
|
||
|
{
|
||
|
MyKFGRI.PlayerAccuracyTracker[i] = MyKFGRI.PlayerAccuracyTracker[i+1];
|
||
|
MyKFGRI.PlayerHeadshotAccuracyTracker[i] = MyKFGRI.PlayerHeadshotAccuracyTracker[i+1];
|
||
|
}
|
||
|
|
||
|
if( NumHumanTeamPlayers > 0 )
|
||
|
{
|
||
|
PlayersAverageAccuracy = TotalAccuracy/float(NumHumanTeamPlayers);
|
||
|
PlayersAverageHeadshotAccuracy = TotalHeadShotAccuracy/float(NumHumanTeamPlayers);
|
||
|
|
||
|
MyKFGRI.PlayerAccuracyTracker[ArrayCount(MyKFGRI.PlayerAccuracyTracker) -1] = PlayersAverageAccuracy;
|
||
|
MyKFGRI.PlayerHeadshotAccuracyTracker[ArrayCount(MyKFGRI.PlayerHeadshotAccuracyTracker) -1] = PlayersAverageHeadshotAccuracy;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Update the stats on how long zeds are surviving on the server */
|
||
|
function UpdateZedLifespanStats()
|
||
|
{
|
||
|
local int i;
|
||
|
local float CurrentTotalLifeSpan;
|
||
|
local int CurrentTotalKills;
|
||
|
local float TotalRecentAverageLifespan;
|
||
|
|
||
|
for( i = 0; i < (ArrayCount(MyKFGRI.TotalZedLifeSpanAverageTracker) - 1); i++ )
|
||
|
{
|
||
|
CurrentTotalLifeSpan += RecentZedLifeSpanTotalTracker[i];
|
||
|
CurrentTotalKills += RecentZedKillTotalTracker[i];
|
||
|
if( i > 6 )
|
||
|
{
|
||
|
TotalRecentAverageLifespan += MyKFGRI.RecentZedLifeSpanAverageTracker[i+1];
|
||
|
}
|
||
|
|
||
|
MyKFGRI.TotalZedLifeSpanAverageTracker[i] = MyKFGRI.TotalZedLifeSpanAverageTracker[i+1];
|
||
|
MyKFGRI.CurrentWaveZedLifeSpanAverageTracker[i] = MyKFGRI.CurrentWaveZedLifeSpanAverageTracker[i+1];
|
||
|
MyKFGRI.RecentZedLifeSpanAverageTracker[i] = MyKFGRI.RecentZedLifeSpanAverageTracker[i+1];
|
||
|
RecentZedKillTotalTracker[i] = RecentZedKillTotalTracker[i+1];
|
||
|
RecentZedLifeSpanTotalTracker[i] = RecentZedLifeSpanTotalTracker[i+1];
|
||
|
}
|
||
|
|
||
|
MyKFGRI.TotalZedLifeSpanAverageTracker[ArrayCount(MyKFGRI.TotalZedLifeSpanAverageTracker) -1] = ZedVisibleAverageLifespan;
|
||
|
MyKFGRI.CurrentWaveZedLifeSpanAverageTracker[ArrayCount(MyKFGRI.CurrentWaveZedLifeSpanAverageTracker) -1] = CurrentWaveZedVisibleAverageLifeSpan;
|
||
|
|
||
|
// Keep track of recent kills
|
||
|
CurrentTotalLifeSpan += RecentTotalZedVisibleLifeSpan;
|
||
|
CurrentTotalKills += RecentTotalZedsKilled;
|
||
|
RecentZedLifeSpanTotalTracker[ArrayCount(RecentZedLifeSpanTotalTracker) -1] = RecentTotalZedVisibleLifeSpan;
|
||
|
RecentZedKillTotalTracker[ArrayCount(RecentZedKillTotalTracker) -1] = RecentTotalZedsKilled;
|
||
|
if( CurrentTotalKills > 0 )
|
||
|
{
|
||
|
RecentZedVisibleAverageLifeSpan = CurrentTotalLifeSpan/float(CurrentTotalKills);
|
||
|
TotalRecentAverageLifespan += RecentZedVisibleAverageLifeSpan;
|
||
|
|
||
|
// Smooth the value with an average over the last 3 values
|
||
|
MyKFGRI.RecentZedLifeSpanAverageTracker[ArrayCount(MyKFGRI.RecentZedLifeSpanAverageTracker) -1] = TotalRecentAverageLifespan/3.0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No recent kills, just populate with the last checks values
|
||
|
RecentZedVisibleAverageLifeSpan = MyKFGRI.RecentZedLifeSpanAverageTracker[ArrayCount(MyKFGRI.RecentZedLifeSpanAverageTracker) -2];
|
||
|
MyKFGRI.RecentZedLifeSpanAverageTracker[ArrayCount(MyKFGRI.RecentZedLifeSpanAverageTracker) -1] = RecentZedVisibleAverageLifeSpan;
|
||
|
}
|
||
|
|
||
|
// Clear the recent kill/lifespawn info
|
||
|
RecentTotalZedVisibleLifeSpan = 0;
|
||
|
RecentTotalZedsKilled = 0;
|
||
|
}
|
||
|
|
||
|
/** Returns what we expect the average zed lifespan to be, by difficulty */
|
||
|
function float GetParZedLifeSpan()
|
||
|
{
|
||
|
if( !bOnePlayerAtStart )
|
||
|
{
|
||
|
return ParZedLifeSpan[GameDifficulty];
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return ParZedLifeSpanSolo[GameDifficulty];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Calculate the overall status of the player's rank and performance */
|
||
|
function UpdateOverallStatus()
|
||
|
{
|
||
|
local float PerkRankModifier;
|
||
|
local float SkillModifier;
|
||
|
local float LifeSpanModifier;
|
||
|
local float HighlySkilledAccuracy, LessSkilledAccuracy;
|
||
|
local float HighlySkilledZedLifespan, LessSkilledZedLifespan;
|
||
|
local bool bPlayerHealthLow;
|
||
|
local int i;
|
||
|
|
||
|
// Take us out of a forced lull if the time is up
|
||
|
if( GameConductorStatus == GCS_ForceLull
|
||
|
&& `TimeSince(PlayerDeathForceLullTime) > PlayerDeathForceLullLength
|
||
|
&& `TimeSince(SoloPlayerSurroundedForceLullTime) > SoloPlayerSurroundedForceLullLength )
|
||
|
{
|
||
|
GameConductorStatus = GCS_Normal;
|
||
|
`log("Forced lull completed", bLogGameConductor);
|
||
|
}
|
||
|
|
||
|
MyKFGRI.CurrentGameConductorStatus = GameConductorStatus;
|
||
|
MyKFGRI.CurrentParZedLifeSpan = GetParZedLifeSpan();
|
||
|
|
||
|
for( i = 0; i < (ArrayCount(MyKFGRI.OverallRankAndSkillModifierTracker) - 1); i++ )
|
||
|
{
|
||
|
MyKFGRI.OverallRankAndSkillModifierTracker[i] = MyKFGRI.OverallRankAndSkillModifierTracker[i+1];
|
||
|
}
|
||
|
|
||
|
// Bypassing making game conductor adjustments
|
||
|
if( bBypassGameConductor || MyKFGRI.IsBossWave() )
|
||
|
{
|
||
|
OverallRankAndSkillModifier = 0.5;
|
||
|
`log("Bypassing GameConductor adjustment OverallRankAndSkillModifier = "$OverallRankAndSkillModifier, bLogGameConductor);
|
||
|
MyKFGRI.OverallRankAndSkillModifierTracker[ArrayCount(MyKFGRI.OverallRankAndSkillModifierTracker) -1] = OverallRankAndSkillModifier;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Forced lull, or most of the team dead, or single player nearly dead, so slow things down
|
||
|
bPlayerHealthLow = PlayersHealthStatus < PlayersLowHealthThreshold;
|
||
|
if( GameConductorStatus == GCS_ForceLull
|
||
|
|| (bPlayerHealthLow && (LullCooldownStartTime == 0.f || `TimeSince(LullCooldownStartTime) > LullSettings[GameDifficulty].Cooldown)) )
|
||
|
{
|
||
|
OverallRankAndSkillModifier = 0.0;
|
||
|
`log("Players low on health PlayersHealthStatus: "$PlayersHealthStatus$" chilling things out, OverallRankAndSkillModifier= "$OverallRankAndSkillModifier, bLogGameConductor);
|
||
|
MyKFGRI.OverallRankAndSkillModifierTracker[ArrayCount(MyKFGRI.OverallRankAndSkillModifierTracker) -1] = OverallRankAndSkillModifier;
|
||
|
|
||
|
// Start the lull timer. Don't allow lulls to last too long
|
||
|
if( bPlayerHealthLow && !MyKFGRI.IsTimerActive(nameOf(Timer_EndLull), self) )
|
||
|
{
|
||
|
MyKFGRI.SetTimer( LullSettings[GameDifficulty].MaxDuration, false, nameOf(Timer_EndLull), self );
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// No longer in a lull, reset duration timer
|
||
|
MyKFGRI.ClearTimer( nameOf(Timer_EndLull), self );
|
||
|
|
||
|
if( WithinRange(TargetPerkRankRange[GameDifficulty],AveragePlayerPerkRank) )
|
||
|
{
|
||
|
PerkRankModifier = GetRangePctByValue( TargetPerkRankRange[GameDifficulty], AveragePlayerPerkRank );
|
||
|
}
|
||
|
else if( AveragePlayerPerkRank < TargetPerkRankRange[GameDifficulty].X )
|
||
|
{
|
||
|
PerkRankModifier = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PerkRankModifier = 1;
|
||
|
}
|
||
|
|
||
|
// Evaluate player skill if its greater than 15 seconds into the match,
|
||
|
// so you have some data to go by
|
||
|
if( MyKFGRI != none && MyKFGRI.ElapsedTime > 15 && AggregatePlayerSkill != 0 )
|
||
|
{
|
||
|
HighlySkilledAccuracy = BaseLinePlayerShootingSkill * HighlySkilledAccuracyMod;
|
||
|
LessSkilledAccuracy = BaseLinePlayerShootingSkill * LessSkilledAccuracyMod;
|
||
|
|
||
|
if( AggregatePlayerSkill > HighlySkilledAccuracy )
|
||
|
{
|
||
|
// Highly skilled players
|
||
|
SkillModifier = Lerp(0.51,1.0, FMin(1.0,(AggregatePlayerSkill - HighlySkilledAccuracy)/((BaseLinePlayerShootingSkill * HighlySkilledAccuracyModMax) - HighlySkilledAccuracy)));
|
||
|
}
|
||
|
else if( AggregatePlayerSkill < LessSkilledAccuracy )
|
||
|
{
|
||
|
// Less skilled players
|
||
|
SkillModifier = Lerp(0.49,0.0, FMax(0,(LessSkilledAccuracy - AggregatePlayerSkill)/(LessSkilledAccuracy - (BaseLinePlayerShootingSkill * LessSkilledAccuracyModMin))));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Standard skilled players
|
||
|
SkillModifier = 0.5;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Standard skilled players
|
||
|
SkillModifier = 0.5;
|
||
|
}
|
||
|
|
||
|
if( RecentZedVisibleAverageLifeSpan > 0 )
|
||
|
{
|
||
|
HighlySkilledZedLifespan = GetParZedLifeSpan() * ZedLifeSpanHighlySkilledThreshold;
|
||
|
LessSkilledZedLifespan = GetParZedLifeSpan() * ZedLifeSpanLessSkilledThreshold;
|
||
|
|
||
|
if( RecentZedVisibleAverageLifeSpan < HighlySkilledZedLifespan )
|
||
|
{
|
||
|
// Highly skilled players
|
||
|
LifeSpanModifier = Lerp(0.51,1.0, FMin(1.0,(HighlySkilledZedLifespan - RecentZedVisibleAverageLifeSpan)/( HighlySkilledZedLifespan - (GetParZedLifeSpan() * ZedLifeSpanHighlySkilledThresholdMin))));
|
||
|
|
||
|
}
|
||
|
else if( RecentZedVisibleAverageLifeSpan > LessSkilledZedLifespan )
|
||
|
{
|
||
|
// Less skilled players
|
||
|
LifeSpanModifier = Lerp(0.49,0.0, FMin(1.0,(RecentZedVisibleAverageLifeSpan - LessSkilledZedLifespan)/((GetParZedLifeSpan() * ZedLifeSpanLessSkilledThresholdMax) - LessSkilledZedLifespan)));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Standard skilled players
|
||
|
LifeSpanModifier = 0.5;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Standard skilled players
|
||
|
LifeSpanModifier = 0.5;
|
||
|
}
|
||
|
|
||
|
OverallRankAndSkillModifier = PerkRankModifier * PerkRankPercentOfOverallSkill + SkillModifier * AccuracyPercentOfOverallSkill + LifeSpanModifier * ZedLifeSpanPercentOfOverallSkill;
|
||
|
MyKFGRI.OverallRankAndSkillModifierTracker[ArrayCount(MyKFGRI.OverallRankAndSkillModifierTracker) -1] = OverallRankAndSkillModifier;
|
||
|
|
||
|
`log("PerkRankModifier = "$PerkRankModifier$" SkillModifier = "$SkillModifier$" LifeSpanModifier = "$LifeSpanModifier$" OverallRankAndSkillModifier= "$OverallRankAndSkillModifier$" GetParZedLifeSpan() = "$GetParZedLifeSpan(), bLogGameConductor);
|
||
|
}
|
||
|
|
||
|
/** Called when a zed does an attack to give an opportunity to scale their attack cooldowns */
|
||
|
function UpdateOverallAttackCoolDowns(KFAIController KFAIC)
|
||
|
{
|
||
|
local bool bAllowLowZedIntensity;
|
||
|
|
||
|
if( GameDifficulty < ArrayCount(AllowLowIntensityZedModeByDifficulty) )
|
||
|
{
|
||
|
bAllowLowZedIntensity = (AllowLowIntensityZedModeByDifficulty[GameDifficulty] == 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bAllowLowZedIntensity = (AllowLowIntensityZedModeByDifficulty[ArrayCount(AllowLowIntensityZedModeByDifficulty) - 1] == 1);
|
||
|
}
|
||
|
|
||
|
if( !bBypassGameConductor && bAllowLowZedIntensity && !MyKFGRI.IsBossWave() )
|
||
|
{
|
||
|
if( GameConductorStatus == GCS_ForceLull || OverallRankAndSkillModifier == 0 )
|
||
|
{
|
||
|
KFAIC.SetOverallCooldownTimer(KFAIC.LowIntensityAttackCooldown);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
KFAIC.SetOverallCooldownTimer(0.0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Potentially modify the spawn rate based on various factors */
|
||
|
function EvaluateSpawnRateModification()
|
||
|
{
|
||
|
local int i;
|
||
|
|
||
|
CurrentSpawnRateModification = EvalInterpCurveFloat(SpawnRateModificationRangeCurve[GameDifficulty], OverallRankAndSkillModifier);
|
||
|
|
||
|
if( MyKFGRI.bGameConductorGraphingEnabled )
|
||
|
{
|
||
|
for( i = 0; i < (ArrayCount(MyKFGRI.ZedSpawnRateModifierTracker) - 1); i++ )
|
||
|
{
|
||
|
MyKFGRI.ZedSpawnRateModifierTracker[i] = MyKFGRI.ZedSpawnRateModifierTracker[i+1];
|
||
|
MyKFGRI.ZedSpawnRateTracker[i] = MyKFGRI.ZedSpawnRateTracker[i+1];
|
||
|
}
|
||
|
|
||
|
MyKFGRI.ZedSpawnRateModifierTracker[ArrayCount(MyKFGRI.ZedSpawnRateModifierTracker) -1] = CurrentSpawnRateModification;
|
||
|
MyKFGRI.ZedSpawnRateTracker[ArrayCount(MyKFGRI.ZedSpawnRateTracker) -1] = MyKFGRI.CurrentNextSpawnTime;
|
||
|
}
|
||
|
|
||
|
`log("CurrentSpawnRateModification = "$CurrentSpawnRateModification$" MyKFGRI.ElapsedTime "$MyKFGRI.ElapsedTime, bLogGameConductor);
|
||
|
}
|
||
|
|
||
|
/** Potentially modify the AI movement speed based on various factors */
|
||
|
function EvaluateAIMovementSpeedModification()
|
||
|
{
|
||
|
local float DifficultySpeedAdjustMod, BaseGroundSpeedMod;
|
||
|
local KFPawn_Monster KFPM;
|
||
|
local float UsedMovementSpeedPercentIncrease, UsedMovementSpeedPercentDecrease;
|
||
|
local float UsedMovementSpeedPercent;
|
||
|
local int i;
|
||
|
|
||
|
if( GameDifficulty < ArrayCount(AIMovementSpeedModificationRange) )
|
||
|
{
|
||
|
UsedMovementSpeedPercentDecrease = AIMovementSpeedModificationRange[GameDifficulty].X;
|
||
|
UsedMovementSpeedPercentIncrease = AIMovementSpeedModificationRange[GameDifficulty].Y;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UsedMovementSpeedPercentDecrease = AIMovementSpeedModificationRange[ArrayCount(AIMovementSpeedModificationRange) - 1].X;
|
||
|
UsedMovementSpeedPercentIncrease = AIMovementSpeedModificationRange[ArrayCount(AIMovementSpeedModificationRange) - 1].Y;
|
||
|
}
|
||
|
|
||
|
// Clamp the values to reasonable settings
|
||
|
UsedMovementSpeedPercentDecrease = FClamp( UsedMovementSpeedPercentDecrease, 0.0, 0.9 );
|
||
|
UsedMovementSpeedPercentIncrease = FClamp( UsedMovementSpeedPercentIncrease, 0.0, 1.0 );
|
||
|
|
||
|
BaseGroundSpeedMod = DifficultyInfo.GetAIGroundSpeedMod();
|
||
|
|
||
|
if( OverallRankAndSkillModifier < 0.5 )
|
||
|
{
|
||
|
UsedMovementSpeedPercent = UsedMovementSpeedPercentDecrease;
|
||
|
}
|
||
|
else if( OverallRankAndSkillModifier > 0.5 )
|
||
|
{
|
||
|
UsedMovementSpeedPercent = UsedMovementSpeedPercentIncrease;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UsedMovementSpeedPercent = 0;
|
||
|
}
|
||
|
|
||
|
if( UsedMovementSpeedPercent > 0 )
|
||
|
{
|
||
|
if( GameDifficulty <= 0 ) // Normal
|
||
|
{
|
||
|
DifficultySpeedAdjustMod = (DifficultyInfo.Hard.MovementSpeedMod - DifficultyInfo.Normal.MovementSpeedMod) * UsedMovementSpeedPercent;
|
||
|
}
|
||
|
else if( GameDifficulty <= 1 ) // Hard
|
||
|
{
|
||
|
DifficultySpeedAdjustMod = (DifficultyInfo.Suicidal.MovementSpeedMod - DifficultyInfo.Hard.MovementSpeedMod) * UsedMovementSpeedPercent;
|
||
|
}
|
||
|
else // Suicidal and HOE
|
||
|
{
|
||
|
DifficultySpeedAdjustMod = (DifficultyInfo.HellOnEarth.MovementSpeedMod - DifficultyInfo.Suicidal.MovementSpeedMod) * UsedMovementSpeedPercent;
|
||
|
}
|
||
|
|
||
|
if( OverallRankAndSkillModifier < 0.5 )
|
||
|
{
|
||
|
CurrentAIMovementSpeedMod = BaseGroundSpeedMod - (DifficultySpeedAdjustMod * (0.5 - OverallRankAndSkillModifier) * 2.0);
|
||
|
}
|
||
|
else if( OverallRankAndSkillModifier > 0.5 )
|
||
|
{
|
||
|
CurrentAIMovementSpeedMod = BaseGroundSpeedMod + (DifficultySpeedAdjustMod * (OverallRankAndSkillModifier - 0.5) * 2.0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CurrentAIMovementSpeedMod = BaseGroundSpeedMod;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CurrentAIMovementSpeedMod = BaseGroundSpeedMod;
|
||
|
}
|
||
|
|
||
|
if( MyKFGRI.bGameConductorGraphingEnabled )
|
||
|
{
|
||
|
for( i = 0; i < (ArrayCount(MyKFGRI.ZedMovementSpeedModifierTracker) - 1); i++ )
|
||
|
{
|
||
|
MyKFGRI.ZedMovementSpeedModifierTracker[i] = MyKFGRI.ZedMovementSpeedModifierTracker[i+1];
|
||
|
}
|
||
|
|
||
|
MyKFGRI.ZedMovementSpeedModifierTracker[ArrayCount(MyKFGRI.ZedMovementSpeedModifierTracker) -1] = CurrentAIMovementSpeedMod;
|
||
|
}
|
||
|
|
||
|
// Adjust the speed of the currently living pawns
|
||
|
foreach WorldInfo.AllPawns( class'KFPawn_Monster', KFPM )
|
||
|
{
|
||
|
if( KFPM.Health > 0 )
|
||
|
{
|
||
|
KFPM.AdjustMovementSpeed(CurrentAIMovementSpeedMod);
|
||
|
`log("Adjust KFPM.default.GroundSpeed = "$KFPM.default.GroundSpeed$" CurrentAIMovementSpeedMod = "$CurrentAIMovementSpeedMod$" percent of default = "$(KFPM.default.GroundSpeed * CurrentAIMovementSpeedMod)/KFPM.default.GroundSpeed, bLogGameConductor);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
`log("CurrentAIMovementSpeedMod = "$CurrentAIMovementSpeedMod$" DifficultyInfo.GetAIGroundSpeedMod() = "$DifficultyInfo.GetAIGroundSpeedMod(), bLogGameConductor);
|
||
|
}
|
||
|
|
||
|
function bool WithinRange( vector2d Range, float TestValue )
|
||
|
{
|
||
|
if( TestValue >= Range.X && TestValue <= Range.Y )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/** Ends the lull period and puts it on cooldown */
|
||
|
function Timer_EndLull()
|
||
|
{
|
||
|
LullCooldownStartTime = WorldInfo.TimeSeconds;
|
||
|
}
|
||
|
|
||
|
defaultproperties
|
||
|
{
|
||
|
TargetPerkRankRange(`DIFFICULTY_NORMAL) =(X=0,Y=7)
|
||
|
TargetPerkRankRange(`DIFFICULTY_HARD) =(X=0,Y=12)
|
||
|
TargetPerkRankRange(`DIFFICULTY_SUICIDAL) =(X=12,Y=25)
|
||
|
TargetPerkRankRange(`DIFFICULTY_HELLONEARTH)=(X=24.999,Y=25)
|
||
|
|
||
|
SpawnRateModificationRangeCurve(`DIFFICULTY_NORMAL) =(Points=((InVal=0.f,OutVal=1.25f),(InVal=0.5f, OutVal=1.0),(InVal=1.f, OutVal=0.75f)))
|
||
|
SpawnRateModificationRangeCurve(`DIFFICULTY_HARD) =(Points=((InVal=0.f,OutVal=1.25f),(InVal=0.5f, OutVal=1.0),(InVal=1.f, OutVal=0.75f)))
|
||
|
SpawnRateModificationRangeCurve(`DIFFICULTY_SUICIDAL) =(Points=((InVal=0.f,OutVal=1.25f),(InVal=0.5f, OutVal=1.0),(InVal=1.f, OutVal=0.5f)))
|
||
|
SpawnRateModificationRangeCurve(`DIFFICULTY_HELLONEARTH)=(Points=((InVal=0.f,OutVal=1.0f),(InVal=0.5f, OutVal=1.0),(InVal=1.f, OutVal=0.75f)))
|
||
|
|
||
|
AIMovementSpeedModificationRange(`DIFFICULTY_NORMAL) =(X=0.5,Y=0.5)
|
||
|
AIMovementSpeedModificationRange(`DIFFICULTY_HARD) =(X=0.5,Y=0.5)
|
||
|
AIMovementSpeedModificationRange(`DIFFICULTY_SUICIDAL) =(X=0.5,Y=0.65)
|
||
|
AIMovementSpeedModificationRange(`DIFFICULTY_HELLONEARTH) =(X=0.0,Y=0.0)
|
||
|
|
||
|
AllowLowIntensityZedModeByDifficulty(`DIFFICULTY_NORMAL) =1
|
||
|
AllowLowIntensityZedModeByDifficulty(`DIFFICULTY_HARD) =1
|
||
|
AllowLowIntensityZedModeByDifficulty(`DIFFICULTY_SUICIDAL) =1
|
||
|
AllowLowIntensityZedModeByDifficulty(`DIFFICULTY_HELLONEARTH) =0
|
||
|
|
||
|
CurrentSpawnRateModification=1.0
|
||
|
CurrentAIMovementSpeedMod=1.0
|
||
|
CurrentVersusZedHealthMod=1.0
|
||
|
CurrentVersusZedDamageMod=1.0
|
||
|
|
||
|
ParShotAccuracy=48.0
|
||
|
ParHeadshotAccuracy=10.0
|
||
|
ParZedLifeSpan(`DIFFICULTY_NORMAL) =35.0
|
||
|
ParZedLifeSpan(`DIFFICULTY_HARD) =32.0
|
||
|
ParZedLifeSpan(`DIFFICULTY_SUICIDAL) =28.0
|
||
|
ParZedLifeSpan(`DIFFICULTY_HELLONEARTH) =22.0
|
||
|
ParZedLifeSpanSolo(`DIFFICULTY_NORMAL) =23.0
|
||
|
ParZedLifeSpanSolo(`DIFFICULTY_HARD) =23.0
|
||
|
ParZedLifeSpanSolo(`DIFFICULTY_SUICIDAL) =22.0
|
||
|
ParZedLifeSpanSolo(`DIFFICULTY_HELLONEARTH) =17.0
|
||
|
|
||
|
HighlySkilledAccuracyMod=1.25
|
||
|
HighlySkilledAccuracyModMax=1.5
|
||
|
LessSkilledAccuracyMod=0.75
|
||
|
LessSkilledAccuracyModMin=0.5
|
||
|
ZedLifeSpanHighlySkilledThreshold=0.75
|
||
|
ZedLifeSpanLessSkilledThreshold=1.25
|
||
|
ZedLifeSpanHighlySkilledThresholdMin=0.5
|
||
|
ZedLifeSpanLessSkilledThresholdMax=1.5
|
||
|
|
||
|
ShootingAccuracySkillWeight=0.25
|
||
|
HeadShootingAccuracySkillWeight=0.75
|
||
|
|
||
|
PerkRankPercentOfOverallSkill=0.25
|
||
|
AccuracyPercentOfOverallSkill=0.25
|
||
|
ZedLifeSpanPercentOfOverallSkill=0.5
|
||
|
PlayersLowHealthThreshold=0.5
|
||
|
|
||
|
LullSettings(`DIFFICULTY_NORMAL) ={(MaxDuration=10.0, Cooldown=12.0)}
|
||
|
LullSettings(`DIFFICULTY_HARD) ={(MaxDuration=8.0, Cooldown=14.0)}
|
||
|
LullSettings(`DIFFICULTY_SUICIDAL) ={(MaxDuration=6.0, Cooldown=16.0)}
|
||
|
LullSettings(`DIFFICULTY_HELLONEARTH) ={(MaxDuration=5.0, Cooldown=18.0)}
|
||
|
|
||
|
bLogGameConductor=false
|
||
|
bBypassGameConductor=false
|
||
|
GameConductorStatus=GCS_Normal
|
||
|
PlayerDeathForceLullLength=15.0
|
||
|
SoloPlayerSurroundedForceLullLength=10.0
|
||
|
}
|