Killing Floor: Zed Count per Wave, MaxZombiesOnce and Squad Spawn Delay
A server owner on killingfloor.ru asked in 2016 how to make zeds spawn more and faster without touching the sandbox settings: a higher MaxZombiesOnce and a shorter delay between squads. Flame answered with a walk-through of KFGameType.SetupWave() and the spawn timer. This page summarises it.
The Killing Floor specimen roster, from Clot to Patriarch, in two texture versions. Every one of them counts towards the limits below.
Two different limits
| Variable | Meaning |
|---|---|
TotalMaxMonsters | How many zeds the wave contains in total (not at once) |
MaxMonsters | How many zeds can be alive at the same time. In practice it equals MaxZombiesOnce |
During a wave the game keeps spawning until the number of live zeds reaches MaxMonsters. You kill a zed, the count drops, a new one spawns. This continues until the wave's total is used up.
How SetupWave counts zeds
- It takes
WaveMaxMonstersfor the current wave from theWavesarray:
Waves[0]=(WaveMask=2013265920,WaveMaxMonsters=20,WaveDuration=180,WaveDifficulty=3.000000)
- It multiplies that by a difficulty modifier:
| Difficulty | GameDifficulty | Modifier |
|---|---|---|
| Beginner | 1 | 0.7 |
| Normal | 2+ | 1.0 |
| Hard, Suicidal | 4, 5 | 1.3 |
| Hell on Earth | 7 | 1.7 |
The code comment still labels the 7+ branch "Suicidal": it predates Hell on Earth.
- And by a player-count modifier (players plus bots):
| Players | 1 | 2 | 3 | 4 | 5 | 6 | more |
|---|---|---|---|---|---|---|---|
| Modifier | 1 | 2 | 2.75 | 3.5 | 4 | 4.5 | players × 0.8 |
- The result is clamped:
NewMaxMonsters = NewMaxMonsters * DifficultyMod * NumPlayersMod;
TotalMaxMonsters = Clamp(NewMaxMonsters, 5, 800);
MaxMonsters = Clamp(TotalMaxMonsters, 5, MaxZombiesOnce);
Clamp(x, min, max) keeps x inside the range. Two conclusions:
- Stock KF never puts more than 800 zeds in one wave, whatever
WaveMaxMonsterssays. To lift that you have to change the constant in your own game type'sSetupWave(). The SRGameType from the thread usedClamp(NewMaxMonsters,5,1100). TotalMaxMonstersis almost always larger thanMaxZombiesOnce, soMaxMonstersends up equal toMaxZombiesOnce.
Example: 6 players on Hell on Earth with WaveMaxMonsters=20 give 20 × 1.7 × 4.5 = 153 zeds in the wave, at most MaxZombiesOnce of them alive at once.
Where each setting must go
Wavesis aconfigarray declared inInvasion. Every game type derived from it reads its own section, so ifKillingFloor.inihas a[ServerPerks.SRGameType]section withWaveslines, those win.MaxZombiesOnceisglobalconfiginKFGameType. It is read only from[KFMod.KFGameType]. Setting it in[ServerPerks.SRGameType]has no effect.
[KFMod.KFGameType]
MaxZombiesOnce=64
The sandbox UI does not let you go above 64, but the ini value can be higher. Think twice before you do: players with weak PCs start to lag, and by wave 6 or so the load builds up into all kinds of desync (missing weapons, invisible zeds). 64 is a sensible ceiling, and faster squad spawns are usually the better lever.
The delay between squads
WaveStartSpawnPeriod comes up in every such discussion, but according to Flame's reading of the code it is not used anywhere. The real delay sits in Timer() of the MatchInProgress state, in the bWaveInProgress branch:
if ( nextSpawnSquad.length > 0 )
NextMonsterTime = Level.TimeSeconds + 0.2;
else
NextMonsterTime = Level.TimeSeconds + CalcNextSquadSpawnTime();
While a squad is still being placed, zeds come every 0.2 s. Between squads the pause comes from CalcNextSquadSpawnTime(), about 1.5 s on average on Hell on Earth. To make spawning almost continuous, override the state in your game type and use a constant:
state MatchInProgress
{
function Timer()
{
// copy the whole stock KFGameType.MatchInProgress.Timer() body here,
// then replace the NextMonsterTime branch with:
NextMonsterTime = Level.TimeSeconds + 0.2;
}
}
Copy the stock Timer() body in full and change only that line. You do not need to copy the other functions of the state.
What CalcNextSquadSpawnTime() does
Flame posted the stock function KFMod.KFGameType.MatchInProgress.CalcNextSquadSpawnTime() in the killingfloor.ru forum thread "Популяция монстров" (how to make zeds come out of the spawners faster when MaxZombiesOnce is raised to 64). The base value is KFLRules.WaveSpawnPeriod, which is then multiplied by player-count factors:
| Players | Early waves | Later waves |
|---|---|---|
| 3 or fewer | 1.0 (unchanged) | 1.1 |
| 4 | 0.85 | 1.0 |
| 5 | 0.65 | 0.75 |
| 6 or more | 0.3 | 0.60 |
"Early waves" are WaveNum < 2 for a short game, WaveNum < 4 for a normal one and WaveNum < 7 for a long one. For a custom-length game (GL_Custom) the early-wave factors (0.85 / 0.65 / 0.3) apply to all waves. On Hard and above (GameDifficulty >= 4.0) the time is additionally multiplied by 0.85. Finally a sine wave adds "peaks and valleys" to the spawn intensity:
SineMod = 1.0 - Abs(sin(WaveTimeElapsed * SineWaveFreq));
...
NextSpawnTime += SineMod * (NextSpawnTime * 2);
So the variable to change is var() float SineWaveFreq;, which controls the frequency of this sine wave and with it the speed and intensity of zed spawning.
WaveCountDown is something else entirely: it is the trader-time countdown between waves, not the spawn delay.
Summary
| Goal | Where |
|---|---|
| Zeds alive at once | MaxZombiesOnce in [KFMod.KFGameType] |
| Zeds per wave | WaveMaxMonsters in Waves, in your game type's section |
| More than 800 per wave | Change the clamp in your own SetupWave() |
| Faster squads | Override MatchInProgress.Timer() in your game type |
| Time between waves | Trader time, not the spawn code |
FAQ
- Why does MaxZombiesOnce in [ServerPerks.SRGameType] do nothing?
- MaxZombiesOnce is declared globalconfig in KFGameType, so it is only read from the [KFMod.KFGameType] section of KillingFloor.ini, whatever game type the server runs.
- Does WaveStartSpawnPeriod change the delay between zed spawns?
- According to the code review on killingfloor.ru, no: the variable is not used anywhere in the game code. The delay between squads comes from CalcNextSquadSpawnTime().