Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
0de9c0088c | |||
3fbf4e9c20 | |||
40c9546858 | |||
83286ea046 | |||
88fcd8b235 |
53
README.md
53
README.md
@ -1 +1,52 @@
|
|||||||
# KF2-ZedSpawner
|
# ZedSpawner
|
||||||
|
|
||||||
|
[](https://steamcommunity.com/sharedfiles/filedetails/?id=2811290931)
|
||||||
|
[](https://steamcommunity.com/sharedfiles/filedetails/?id=2811290931)
|
||||||
|
[](https://steamcommunity.com/sharedfiles/filedetails/?id=2811290931)
|
||||||
|
[](https://steamcommunity.com/sharedfiles/filedetails/?id=2811290931)
|
||||||
|
[](https://github.com/GenZmeY/KF2-ZedSpawner/tags)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
|
# Description
|
||||||
|
Spawner for zeds. Started as a modification of the [PissJar](https://steamcommunity.com/sharedfiles/filedetails/?id=1082749153) and [Windows 10](https://steamcommunity.com/sharedfiles/filedetails/?id=2488241348) version, but grew into something completely different...
|
||||||
|
|
||||||
|
# Features
|
||||||
|
- spawn without increasing zed counter;
|
||||||
|
- spawn depends on the number of players;
|
||||||
|
- cyclic spawn (useful for endless mode);
|
||||||
|
- separate spawn for special waves and boss waves;
|
||||||
|
- spawn after a certain percentage of killed zeds.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
**UNDER CONSTRUCTION**
|
||||||
|
|
||||||
|
[Spawn Calculator](https://docs.google.com/spreadsheets/d/1q67WJ36jhj6Y0lPNO5tS2bU79Wphu4Xmi62me6DAwtM/edit?usp=sharing)
|
||||||
|
|
||||||
|
# Build
|
||||||
|
**Note:** If you want to build/test/brew/publish a mutator without git-bash and/or scripts, follow [these instructions](https://tripwireinteractive.atlassian.net/wiki/spaces/KF2SW/pages/26247172/KF2+Code+Modding+How-to) instead of what is described here.
|
||||||
|
1. Install [Killing Floor 2](https://store.steampowered.com/app/232090/Killing_Floor_2/), Killing Floor 2 - SDK and [git for windows](https://git-scm.com/download/win);
|
||||||
|
2. open git-bash and go to any folder where you want to store sources:
|
||||||
|
`cd <ANY_FOLDER_YOU_WANT>`
|
||||||
|
3. Clone this repository and go to the source folder:
|
||||||
|
`git clone https://github.com/GenZmeY/KF2-ZedSpawner && cd KF2-ZedSpawner`
|
||||||
|
4. Download dependencies:
|
||||||
|
`git submodule init && git submodule update`
|
||||||
|
5. Compile:
|
||||||
|
`./tools/builder -c`
|
||||||
|
5. The compiled files will be here:
|
||||||
|
`C:\Users\<USERNAME>\Documents\My Games\KillingFloor2\KFGame\Unpublished\BrewedPC\Script\`
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
Open git-bash in the source folder and run command:
|
||||||
|
`./tools/builder -t`
|
||||||
|
(or `./tools/builder -ct` if you haven't compiled the mutator yet)
|
||||||
|
|
||||||
|
A local single-user test will be launched with parameters from `builder.cfg` (edit this file if you want to test mutator with different parameters).
|
||||||
|
|
||||||
|
# Bug reports
|
||||||
|
If you find a bug, go to the [issue page](https://github.com/GenZmeY/KF2-ZedSpawner/issues) and check if there is a description of your bug. If not, create a new issue.
|
||||||
|
Describe what the bug looks like and how reproduce it.
|
||||||
|
Attaching your KFZedSpawner.ini and Launch.log can also be helpful.
|
||||||
|
|
||||||
|
# License
|
||||||
|
[GNU GPLv3](LICENSE)
|
||||||
|
@ -63,6 +63,12 @@ public static function bool Load(E_LogLevel LogLevel)
|
|||||||
Errors = true;
|
Errors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (default.SingleSpawnLimitMultiplier <= 0.f)
|
||||||
|
{
|
||||||
|
`ZS_Error("SingleSpawnLimitMultiplier" @ "(" $ default.SingleSpawnLimitMultiplier $ ")" @ "must be greater than 0.0");
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (default.SingleSpawnLimitPlayerMultiplier < 0.f)
|
if (default.SingleSpawnLimitPlayerMultiplier < 0.f)
|
||||||
{
|
{
|
||||||
`ZS_Error("SingleSpawnLimitPlayerMultiplier" @ "(" $ default.SingleSpawnLimitPlayerMultiplier $ ")" @ "must be greater than or equal 0.0");
|
`ZS_Error("SingleSpawnLimitPlayerMultiplier" @ "(" $ default.SingleSpawnLimitPlayerMultiplier $ ")" @ "must be greater than or equal 0.0");
|
||||||
|
@ -35,8 +35,8 @@ struct S_SpawnEntry
|
|||||||
var float RelativeStart;
|
var float RelativeStart;
|
||||||
var int DelayDefault;
|
var int DelayDefault;
|
||||||
var int Delay;
|
var int Delay;
|
||||||
var int SpawnsLeft;
|
var int PawnsLeft;
|
||||||
var int SpawnsTotal;
|
var int PawnsTotal;
|
||||||
var bool SpawnAtPlayerStart;
|
var bool SpawnAtPlayerStart;
|
||||||
var bool ForceSpawn;
|
var bool ForceSpawn;
|
||||||
var String ZedNameFiller;
|
var String ZedNameFiller;
|
||||||
@ -287,7 +287,7 @@ private function SpawnTimer()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SE.SpawnsLeft > 0)
|
if (SE.PawnsLeft > 0)
|
||||||
{
|
{
|
||||||
SpawnEntry(SpawnListCurrent, Index);
|
SpawnEntry(SpawnListCurrent, Index);
|
||||||
}
|
}
|
||||||
@ -403,8 +403,9 @@ private function AdjustSpawnList(out Array<S_SpawnEntry> List)
|
|||||||
local S_SpawnEntry SE;
|
local S_SpawnEntry SE;
|
||||||
local int Index;
|
local int Index;
|
||||||
local float Cycle, Players;
|
local float Cycle, Players;
|
||||||
local float MSB, MSC, MSP;
|
local float B, TM, TCM, TPM;
|
||||||
local float MLB, MLC, MLP;
|
local float L, LM, LCM, LPM;
|
||||||
|
local float PawnTotalF, PawnLimitF;
|
||||||
local int ZedNameMaxLength;
|
local int ZedNameMaxLength;
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
@ -412,13 +413,16 @@ private function AdjustSpawnList(out Array<S_SpawnEntry> List)
|
|||||||
Cycle = float(CurrentCycle);
|
Cycle = float(CurrentCycle);
|
||||||
Players = float(PlayerCount());
|
Players = float(PlayerCount());
|
||||||
|
|
||||||
MSB = CfgSpawn.default.ZedTotalMultiplier;
|
B = float(SE.SpawnCountBase);
|
||||||
MSC = CfgSpawn.default.SpawnTotalCycleMultiplier;
|
L = float(SE.SingleSpawnLimitDefault);
|
||||||
MSP = CfgSpawn.default.SpawnTotalPlayerMultiplier;
|
|
||||||
|
TM = CfgSpawn.default.ZedTotalMultiplier;
|
||||||
|
TCM = CfgSpawn.default.SpawnTotalCycleMultiplier;
|
||||||
|
TPM = CfgSpawn.default.SpawnTotalPlayerMultiplier;
|
||||||
|
|
||||||
MLB = CfgSpawn.default.SingleSpawnLimitMultiplier;
|
LM = CfgSpawn.default.SingleSpawnLimitMultiplier;
|
||||||
MLC = CfgSpawn.default.SingleSpawnLimitCycleMultiplier;
|
LCM = CfgSpawn.default.SingleSpawnLimitCycleMultiplier;
|
||||||
MLP = CfgSpawn.default.SingleSpawnLimitPlayerMultiplier;
|
LPM = CfgSpawn.default.SingleSpawnLimitPlayerMultiplier;
|
||||||
|
|
||||||
ZedNameMaxLength = 0;
|
ZedNameMaxLength = 0;
|
||||||
foreach List(SE, Index)
|
foreach List(SE, Index)
|
||||||
@ -437,11 +441,14 @@ private function AdjustSpawnList(out Array<S_SpawnEntry> List)
|
|||||||
else
|
else
|
||||||
List[Index].Delay = 0;
|
List[Index].Delay = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
List[Index].ForceSpawn = false;
|
PawnTotalF = B * (TM + TCM * (Cycle - 1.0f) + TPM * (Players - 1.0f));
|
||||||
List[Index].SpawnsTotal = Round(SE.SpawnCountBase * (MSB + MSC * (Cycle - 1.0f) + MSP * (Players - 1.0f)));
|
PawnLimitF = L * (LM + LCM * (Cycle - 1.0f) + LPM * (Players - 1.0f));
|
||||||
List[Index].SingleSpawnLimit = Round(SE.SingleSpawnLimitDefault * (MLB + MLC * (Cycle - 1.0f) + MLP * (Players - 1.0f)));
|
|
||||||
List[Index].SpawnsLeft = List[Index].SpawnsTotal;
|
List[Index].ForceSpawn = false;
|
||||||
|
List[Index].PawnsTotal = Max(Round(PawnTotalF), 1);
|
||||||
|
List[Index].SingleSpawnLimit = Max(Round(PawnLimitF), 1);
|
||||||
|
List[Index].PawnsLeft = List[Index].PawnsTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach List(SE, Index)
|
foreach List(SE, Index)
|
||||||
@ -476,7 +483,7 @@ private function SpawnTimerLogger(bool Stop, optional String Comment)
|
|||||||
private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
||||||
{
|
{
|
||||||
local S_SpawnEntry SE;
|
local S_SpawnEntry SE;
|
||||||
local int FreeSpawnSlots, SpawnCount, Spawned;
|
local int FreeSpawnSlots, PawnCount, Spawned;
|
||||||
local String Action, Comment, NextSpawn;
|
local String Action, Comment, NextSpawn;
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
@ -486,13 +493,13 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
|||||||
SpawnList[Index].Delay = SE.DelayDefault;
|
SpawnList[Index].Delay = SE.DelayDefault;
|
||||||
if (FRand() <= SE.Probability || SE.ForceSpawn)
|
if (FRand() <= SE.Probability || SE.ForceSpawn)
|
||||||
{
|
{
|
||||||
if (SE.SingleSpawnLimit == 0 || SE.SpawnsLeft < SE.SingleSpawnLimit)
|
if (SE.SingleSpawnLimit == 0 || SE.PawnsLeft < SE.SingleSpawnLimit)
|
||||||
{
|
{
|
||||||
SpawnCount = SE.SpawnsLeft;
|
PawnCount = SE.PawnsLeft;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SpawnCount = SE.SingleSpawnLimit;
|
PawnCount = SE.SingleSpawnLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
||||||
@ -501,16 +508,16 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
|||||||
if (FreeSpawnSlots == 0)
|
if (FreeSpawnSlots == 0)
|
||||||
{
|
{
|
||||||
NoFreeSpawnSlots = true;
|
NoFreeSpawnSlots = true;
|
||||||
SpawnList[Index].SpawnsLeft = 0;
|
SpawnList[Index].PawnsLeft = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (SpawnCount > FreeSpawnSlots)
|
else if (PawnCount > FreeSpawnSlots)
|
||||||
{
|
{
|
||||||
SpawnCount = FreeSpawnSlots;
|
PawnCount = FreeSpawnSlots;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spawned = SpawnZed(SE.ZedClass, SpawnCount, SE.SpawnAtPlayerStart);
|
Spawned = SpawnZed(SE.ZedClass, PawnCount, SE.SpawnAtPlayerStart);
|
||||||
if (Spawned == INDEX_NONE)
|
if (Spawned == INDEX_NONE)
|
||||||
{
|
{
|
||||||
SpawnList[Index].Delay = 5;
|
SpawnList[Index].Delay = 5;
|
||||||
@ -538,10 +545,10 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
|||||||
Spawned = SE.SingleSpawnLimit;
|
Spawned = SE.SingleSpawnLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpawnList[Index].SpawnsLeft -= Spawned;
|
SpawnList[Index].PawnsLeft -= Spawned;
|
||||||
if (SpawnList[Index].SpawnsLeft > 0)
|
if (SpawnList[Index].PawnsLeft > 0)
|
||||||
{
|
{
|
||||||
NextSpawn = "next after" @ SE.DelayDefault $ "sec," @ "pawns left:" @ SpawnList[Index].SpawnsLeft;
|
NextSpawn = "next after" @ SE.DelayDefault $ "sec," @ "pawns left:" @ SpawnList[Index].PawnsLeft;
|
||||||
}
|
}
|
||||||
SpawnLog(SE, Action, Comment, NextSpawn);
|
SpawnLog(SE, Action, Comment, NextSpawn);
|
||||||
}
|
}
|
||||||
@ -593,7 +600,7 @@ private function Vector PlayerStartLocation()
|
|||||||
return KFGIS.FindPlayerStart(None, 0).Location;
|
return KFGIS.FindPlayerStart(None, 0).Location;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bool SpawnAtPlayerStart)
|
private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, bool SpawnAtPlayerStart)
|
||||||
{
|
{
|
||||||
local Array<class<KFPawn_Monster> > CustomSquad;
|
local Array<class<KFPawn_Monster> > CustomSquad;
|
||||||
local Vector SpawnLocation, PlayerStart;
|
local Vector SpawnLocation, PlayerStart;
|
||||||
@ -614,7 +621,7 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bo
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (Index = 0; Index < SpawnCount; Index++)
|
for (Index = 0; Index < PawnCount; Index++)
|
||||||
{
|
{
|
||||||
CustomSquad.AddItem(ZedClass);
|
CustomSquad.AddItem(ZedClass);
|
||||||
}
|
}
|
||||||
@ -635,7 +642,7 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
Spawned = 0; Failed = 0;
|
Spawned = 0; Failed = 0;
|
||||||
while (Failed + Spawned < SpawnCount)
|
while (Failed + Spawned < PawnCount)
|
||||||
{
|
{
|
||||||
KFPM = Spawn(ZedClass,,, SpawnLocation, rot(0,0,1),, true);
|
KFPM = Spawn(ZedClass,,, SpawnLocation, rot(0,0,1),, true);
|
||||||
if (KFPM == None)
|
if (KFPM == None)
|
||||||
|
Reference in New Issue
Block a user