8 Commits

Author SHA1 Message Date
7395ff04bf add bSmoothSpawn feature 2022-07-13 12:09:18 +03:00
681870536b replace logger 2022-07-13 10:12:35 +03:00
c7e9087f6e refactoring 2022-07-13 09:56:35 +03:00
91e199a2e1 update description 2022-07-11 10:24:24 +03:00
9f3153b43f update build tools 2022-07-11 10:24:03 +03:00
e12c70bdbe Merge branch 'master' of https://github.com/GenZmeY/KF2-ZedSpawner 2022-06-13 17:09:13 +03:00
90a69ef739 ...
- now the type of unit affects the choice of spawn location
- bSpawnAtPlayerStart removed from spawn list
- SpawnAtPlayerStart can be set separately for specified maps or zed classes
- optimized spawn list loading
- added handling of the situation when the player leaves the game before preloadcontent synchronization ends
- fixed calculation of the number of zeds in some cases
2022-06-13 17:07:55 +03:00
42cf263785 Update README.md 2022-06-06 11:03:13 +03:00
16 changed files with 388 additions and 184 deletions

View File

@ -1,3 +1,5 @@
[img]https://img.shields.io/static/v1?logo=GitHub&labelColor=gray&color=blue&logoColor=white&label=&message=Open Source[/img] [img]https://img.shields.io/github/license/GenZmeY/KF2-ZedSpawner[/img] [img]https://img.shields.io/steam/subscriptions/2811290931[/img] [img]https://img.shields.io/steam/favorites/2811290931[/img] [img]https://img.shields.io/steam/update-date/2811290931[/img] [url=https://steamcommunity.com/sharedfiles/filedetails/changelog/2811290931][img]https://img.shields.io/github/v/tag/GenZmeY/KF2-ZedSpawner[/img][/url]
[h1]Description[/h1]
Spawner for zeds. Started as a modification of [url=https://steamcommunity.com/sharedfiles/filedetails/?id=2488241348]this version[/url], but now there is almost nothing left of the previous mutator, lol xD
@ -9,24 +11,30 @@ Spawner for zeds. Started as a modification of [url=https://steamcommunity.com/s
- spawn after a certain percentage of killed zeds.
[h1]Whitelisted?[/h1]
No. This mod is not whitelisted and will unrank your server. Any XP gained will not be saved.
No. This mod is not whitelisted and will de-rank your server. Any XP gained will not be saved.
[h1]Usage (single player)[/h1]
1. Subscribe to this mutator;
2. Start KF2;
3. Open console (`) and input:
[olist]
[*]Subscribe to this mutator;
[*]Start KF2;
[*]Open console (`) and input:
[b]open KF-BioticsLab?Mutator=ZedSpawner.ZedSpawnerMut[/b]
(replace the map and add the parameters you need)
4. <Enter>.
[*]<Enter>.
[/olist]
[h1]Usage (server)[/h1]
[b]Note:[/b] [i]If you don't understand what is written here, read the article [url=https://wiki.killingfloor2.com/index.php?title=Dedicated_Server_(Killing_Floor_2)][u]Dedicated Server (KF2 wiki)[/u][/url] before following these instructions.[/i]
1. Open your [b]PCServer-KFEngine.ini[/b] / [b]LinuxServer-KFEngine.ini[/b];
2. Add the following string to the [b][OnlineSubsystemSteamworks.KFWorkshopSteamworks][/b] section (create one if it doesn't exist):
[olist]
[*]Open your [b]PCServer-KFEngine.ini[/b] / [b]LinuxServer-KFEngine.ini[/b];
[*]Find the [b][IpDrv.TcpNetDriver][/b] section and make sure that there is a line (add if not):
[b]DownloadManagers=OnlineSubsystemSteamworks.SteamWorkshopDownload[/b]
❗️ If there are several [b]DownloadManagers=[/b] then the line above should be the first ❗️
[*]Add the following string to the [b][OnlineSubsystemSteamworks.KFWorkshopSteamworks][/b] section (create one if it doesn't exist):
[b]ServerSubscribedWorkshopItems=2811290931[/b]
3. Start the server and wait while the mutator is downloading;
4. Add mutator to server start parameters: [b]?Mutator=ZedSpawner.ZedSpawnerMut[/b] and restart the server.
[*]Start the server and wait while the mutator is downloading;
[*]Add mutator to server start parameters: [b]?Mutator=ZedSpawner.ZedSpawnerMut[/b] and restart the server.
[/olist]
[h1]Setup[/h1]
At the first start, the [b]KFZedSpawner.ini[/b] config will be created. There are already default settings and spawn lists, but you still need to change them because this is just an example.
@ -56,16 +64,13 @@ Use the [b][ZedSpawner.SpawnListBossWaves][/b] and [b][ZedSpawner.SpawnListSpeci
[*][b]Probability[/b] - the chance (%) of each spawn (1-100).
[*][b]SpawnCountBase[/b] - The base number of zeds to spawn, aka the number of zeds that will be spawned on the first cycle with one player. Can be adjusted by modifiers, number of players and cycle number.
[*][b]SingleSpawnLimit[/b] - maximum number of zeds for one spawn. Can be adjusted by modifiers, number of players and cycle number.
[*][b]bSpawnAtPlayerStart[/b] - exactly what is written.
[/list]
[h1]Spawn logic[/h1]
I really tried to describe in text how it works, but every time I got some kind of crap. Therefore, I decided to explain it a little differently and made a small calculator for this. It is interactive, you can change the parameters and see what happens. It has all the necessary explanations, so I think you will quickly figure out how the spawner works.
[h1]📌[url=https://redirect.genzmey.su/kf2-zedspawner-calc]Spawn calculator[/url][/h1]
[url=https://redirect.genzmey.su/kf2-zedspawner-calc][img]https://img.shields.io/static/v1?message=Spawn%20Calculator&logo=Google%20Sheets&labelColor=34A853&color=gray&logoColor=white&label=Google%20Sheets%20[/img][/url]
[i]Just please try not to interfere with each other if you see that someone is already using a calculator.[/i]
By the way, ZedSpawner logs everything it does, so reading the logs can also help you figure out how it works.
[h1]Sources[/h1]
[url=https://github.com/GenZmeY/KF2-ZedSpawner]https://github.com/GenZmeY/KF2-ZedSpawner[/url] (GNU GPLv3)
[url=https://github.com/GenZmeY/KF2-ZedSpawner]https://github.com/GenZmeY/KF2-ZedSpawner[/url] (GNU GPLv3)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@ -50,4 +50,4 @@ 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)
[GNU GPLv3](LICENSE)

View File

@ -11,6 +11,7 @@ var public config float SingleSpawnLimitMultiplier;
var public config float SingleSpawnLimitPlayerMultiplier;
var public config float SingleSpawnLimitCycleMultiplier;
var public config int AliveSpawnLimit;
var public config bool bSmoothSpawn;
public static function InitConfig(int Version, int LatestVersion)
{
@ -19,6 +20,9 @@ public static function InitConfig(int Version, int LatestVersion)
case `NO_CONFIG:
ApplyDefault();
case 3:
default.bSmoothSpawn = false;
default: break;
}
@ -32,6 +36,7 @@ private static function ApplyDefault()
{
default.bCyclicalSpawn = true;
default.bShadowSpawn = true;
default.bSmoothSpawn = true;
default.ZedTotalMultiplier = 1.0;
default.SpawnTotalPlayerMultiplier = 0.75;
default.SpawnTotalCycleMultiplier = 0.75;
@ -47,43 +52,43 @@ public static function bool Load(E_LogLevel LogLevel)
if (default.ZedTotalMultiplier <= 0.f)
{
`ZS_Error("ZedTotalMultiplier" @ "(" $ default.ZedTotalMultiplier $ ")" @ "must be greater than 0.0");
`Log_Error("ZedTotalMultiplier" @ "(" $ default.ZedTotalMultiplier $ ")" @ "must be greater than 0.0");
Errors = true;
}
if (default.SpawnTotalPlayerMultiplier < 0.f)
{
`ZS_Error("SpawnTotalPlayerMultiplier" @ "(" $ default.SpawnTotalPlayerMultiplier $ ")" @ "must be greater than or equal 0.0");
`Log_Error("SpawnTotalPlayerMultiplier" @ "(" $ default.SpawnTotalPlayerMultiplier $ ")" @ "must be greater than or equal 0.0");
Errors = true;
}
if (default.SpawnTotalCycleMultiplier < 0.f)
{
`ZS_Error("SpawnTotalCycleMultiplier" @ "(" $ default.SpawnTotalCycleMultiplier $ ")" @ "must be greater than or equal 0.0");
`Log_Error("SpawnTotalCycleMultiplier" @ "(" $ default.SpawnTotalCycleMultiplier $ ")" @ "must be greater than or equal 0.0");
Errors = true;
}
if (default.SingleSpawnLimitMultiplier <= 0.f)
{
`ZS_Error("SingleSpawnLimitMultiplier" @ "(" $ default.SingleSpawnLimitMultiplier $ ")" @ "must be greater than 0.0");
`Log_Error("SingleSpawnLimitMultiplier" @ "(" $ default.SingleSpawnLimitMultiplier $ ")" @ "must be greater than 0.0");
Errors = true;
}
if (default.SingleSpawnLimitPlayerMultiplier < 0.f)
{
`ZS_Error("SingleSpawnLimitPlayerMultiplier" @ "(" $ default.SingleSpawnLimitPlayerMultiplier $ ")" @ "must be greater than or equal 0.0");
`Log_Error("SingleSpawnLimitPlayerMultiplier" @ "(" $ default.SingleSpawnLimitPlayerMultiplier $ ")" @ "must be greater than or equal 0.0");
Errors = true;
}
if (default.SingleSpawnLimitCycleMultiplier < 0.f)
{
`ZS_Error("SingleSpawnLimitCycleMultiplier" @ "(" $ default.SingleSpawnLimitCycleMultiplier $ ")" @ "must be greater than or equal 0.0");
`Log_Error("SingleSpawnLimitCycleMultiplier" @ "(" $ default.SingleSpawnLimitCycleMultiplier $ ")" @ "must be greater than or equal 0.0");
Errors = true;
}
if (default.AliveSpawnLimit < 0)
{
`ZS_Error("AliveSpawnLimit" @ "(" $ default.AliveSpawnLimit $ ")" @ "must be greater than or equal 0");
`Log_Error("AliveSpawnLimit" @ "(" $ default.AliveSpawnLimit $ ")" @ "must be greater than or equal 0");
Errors = true;
}

View File

@ -0,0 +1,74 @@
class SpawnAtPlayerStart extends Object
dependson(ZedSpawner)
config(ZedSpawner);
var private config Array<String> ZedClass;
var public config Array<String> Map;
public static function InitConfig(int Version, int LatestVersion)
{
switch (Version)
{
case `NO_CONFIG:
case 2:
ApplyDefault();
default: break;
}
if (LatestVersion != Version)
{
StaticSaveConfig();
}
}
private static function ApplyDefault()
{
default.ZedClass.Length = 0;
default.ZedClass.AddItem("HL2Monsters.Combine_Strider");
default.ZedClass.AddItem("HL2Monsters.Combine_Gunship");
default.ZedClass.AddItem("HL2Monsters.Hunter_Chopper");
default.ZedClass.AddItem("SomePackage.SomeZedClassYouWantToSpawnAtPlayerStart");
default.Map.Length = 0;
default.Map.AddItem("KF-SomeMapNameWhereYouWantSpawnZedsAtPlayerStart");
}
public static function Array<class<KFPawn_Monster> > Load(E_LogLevel LogLevel)
{
local Array<class<KFPawn_Monster> > ZedList;
local class<KFPawn_Monster> KFPMC;
local String ZedClassTmp;
local int Line;
`Log_Info("Load zeds to spawn at player start:");
foreach default.ZedClass(ZedClassTmp, Line)
{
KFPMC = class<KFPawn_Monster>(DynamicLoadObject(ZedClassTmp, class'Class'));
if (KFPMC == None)
{
`Log_Warn("[" $ Line + 1 $ "]" @ "Can't load zed class:" @ ZedClassTmp);
}
else
{
ZedList.AddItem(KFPMC);
`Log_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully:" @ ZedClassTmp);
}
}
if (ZedList.Length == default.ZedClass.Length)
{
`Log_Info("Spawn at player start list (Zeds) loaded successfully (" $ default.ZedClass.Length @ "entries)");
}
else
{
`Log_Info("Spawn at player start list (Zeds): loaded" @ ZedList.Length @ "of" @ default.ZedClass.Length @ "entries");
}
return ZedList;
}
defaultproperties
{
}

View File

@ -10,7 +10,6 @@ struct S_SpawnEntryCfg
var int Probability;
var int SpawnCountBase;
var int SingleSpawnLimit;
var bool bSpawnAtPlayerStart;
};
var public config bool bStopRegularSpawn;
@ -47,7 +46,6 @@ private static function ApplyDefault(KFGI_Access KFGIA)
SpawnEntry.SingleSpawnLimit = 1;
SpawnEntry.Delay = 30;
SpawnEntry.Probability = 100;
SpawnEntry.bSpawnAtPlayerStart = false;
KFPM_Bosses = KFGIA.GetAIBossClassList();
foreach KFPM_Bosses(KFPMC)
{
@ -63,9 +61,8 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
local S_SpawnEntry SpawnEntry;
local int Line;
local bool Errors;
local int Loaded;
`ZS_Info("Load boss waves spawn list...");
`Log_Info("Load boss waves spawn list:");
foreach default.Spawn(SpawnEntryCfg, Line)
{
Errors = false;
@ -73,14 +70,14 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
SpawnEntry.BossClass = class<KFPawn_Monster>(DynamicLoadObject(SpawnEntryCfg.BossClass, class'Class'));
if (SpawnEntry.BossClass == None)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Can't load boss class:" @ SpawnEntryCfg.BossClass);
`Log_Warn("[" $ Line + 1 $ "]" @ "Can't load boss class:" @ SpawnEntryCfg.BossClass);
Errors = true;
}
SpawnEntry.ZedClass = class<KFPawn_Monster>(DynamicLoadObject(SpawnEntryCfg.ZedClass, class'Class'));
if (SpawnEntry.ZedClass == None)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Can't load zed class:" @ SpawnEntryCfg.ZedClass);
`Log_Warn("[" $ Line + 1 $ "]" @ "Can't load zed class:" @ SpawnEntryCfg.ZedClass);
Errors = true;
}
@ -89,48 +86,45 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
SpawnEntry.DelayDefault = SpawnEntryCfg.Delay;
if (SpawnEntryCfg.Delay <= 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0");
Errors = true;
}
SpawnEntry.Probability = SpawnEntryCfg.Probability / 100.f;
if (SpawnEntryCfg.Probability <= 0 || SpawnEntryCfg.Probability > 100)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Probability" @ "(" $ SpawnEntryCfg.Probability $ ")" @ "must be greater than 0 and less than or equal 100");
`Log_Warn("[" $ Line + 1 $ "]" @ "Probability" @ "(" $ SpawnEntryCfg.Probability $ ")" @ "must be greater than 0 and less than or equal 100");
Errors = true;
}
SpawnEntry.SpawnCountBase = SpawnEntryCfg.SpawnCountBase;
if (SpawnEntry.SpawnCountBase <= 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "SpawnCountBase" @ "(" $ SpawnEntryCfg.SpawnCountBase $ ")" @ "must be greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "SpawnCountBase" @ "(" $ SpawnEntryCfg.SpawnCountBase $ ")" @ "must be greater than 0");
Errors = true;
}
SpawnEntry.SingleSpawnLimitDefault = SpawnEntryCfg.SingleSpawnLimit;
if (SpawnEntry.SingleSpawnLimit < 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "SingleSpawnLimit" @ "(" $ SpawnEntryCfg.SingleSpawnLimit $ ")" @ "must be equal or greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "SingleSpawnLimit" @ "(" $ SpawnEntryCfg.SingleSpawnLimit $ ")" @ "must be equal or greater than 0");
Errors = true;
}
SpawnEntry.SpawnAtPlayerStart = SpawnEntryCfg.bSpawnAtPlayerStart;
if (!Errors)
{
Loaded++;
SpawnList.AddItem(SpawnEntry);
`ZS_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (" $ SpawnEntryCfg.BossClass $ ")" @ SpawnEntryCfg.ZedClass);
`Log_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (" $ SpawnEntryCfg.BossClass $ ")" @ SpawnEntryCfg.ZedClass);
}
}
if (Loaded == default.Spawn.Length)
if (SpawnList.Length == default.Spawn.Length)
{
`ZS_Info("Boss spawn list loaded successfully");
`Log_Info("Boss spawn list loaded successfully (" $ default.Spawn.Length @ "entries)");
}
else
{
`ZS_Info("Boss spawn list: loaded" @ Loaded @ "of" @ default.Spawn.Length @ "entries");
`Log_Info("Boss spawn list: loaded" @ SpawnList.Length @ "of" @ default.Spawn.Length @ "entries");
}
return SpawnList;

View File

@ -11,11 +11,15 @@ struct S_SpawnEntryCfg
var int Probability;
var int SpawnCountBase;
var int SingleSpawnLimit;
var bool bSpawnAtPlayerStart;
};
var public config Array<S_SpawnEntryCfg> Spawn;
delegate int SpawnListSort(S_SpawnEntryCfg A, S_SpawnEntryCfg B)
{
return A.Wave > B.Wave ? -1 : 0;
}
public static function InitConfig(int Version, int LatestVersion, KFGI_Access KFGIA)
{
switch (Version)
@ -46,8 +50,7 @@ private static function ApplyDefault(KFGI_Access KFGIA)
SpawnEntry.RelativeStart = 25;
SpawnEntry.Delay = 60;
SpawnEntry.Probability = 100;
SpawnEntry.bSpawnAtPlayerStart = false;
KFPM_Zeds = KFGIA.GetAIClassList();
foreach KFPM_Zeds(KFPMC)
{
@ -64,9 +67,8 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
local S_SpawnEntry SpawnEntry;
local int Line;
local bool Errors;
local int Loaded;
`ZS_Info("Load spawn list:");
`Log_Info("Load spawn list:");
foreach default.Spawn(SpawnEntryCfg, Line)
{
Errors = false;
@ -74,69 +76,68 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
SpawnEntry.Wave = SpawnEntryCfg.Wave;
if (SpawnEntryCfg.Wave <= 0 || SpawnEntryCfg.Wave > 255)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Wave" @ "(" $ SpawnEntryCfg.ZedClass $ ")" @ "must be greater than 0 and less than 256");
`Log_Warn("[" $ Line + 1 $ "]" @ "Wave" @ "(" $ SpawnEntryCfg.ZedClass $ ")" @ "must be greater than 0 and less than 256");
Errors = true;
}
SpawnEntry.ZedClass = class<KFPawn_Monster>(DynamicLoadObject(SpawnEntryCfg.ZedClass, class'Class'));
if (SpawnEntry.ZedClass == None)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Can't load zed class:" @ SpawnEntryCfg.ZedClass);
`Log_Warn("[" $ Line + 1 $ "]" @ "Can't load zed class:" @ SpawnEntryCfg.ZedClass);
Errors = true;
}
SpawnEntry.RelativeStartDefault = SpawnEntryCfg.RelativeStart / 100.f;
if (SpawnEntryCfg.RelativeStart < 0 || SpawnEntryCfg.RelativeStart > 100)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "RelativeStart" @ "(" $ SpawnEntryCfg.RelativeStart $ ")" @ "must be greater than or equal 0 and less than or equal 100");
`Log_Warn("[" $ Line + 1 $ "]" @ "RelativeStart" @ "(" $ SpawnEntryCfg.RelativeStart $ ")" @ "must be greater than or equal 0 and less than or equal 100");
Errors = true;
}
SpawnEntry.DelayDefault = SpawnEntryCfg.Delay;
if (SpawnEntryCfg.Delay <= 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0");
Errors = true;
}
SpawnEntry.Probability = SpawnEntryCfg.Probability / 100.f;
if (SpawnEntryCfg.Probability <= 0 || SpawnEntryCfg.Probability > 100)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Probability" @ "(" $ SpawnEntryCfg.Probability $ ")" @ "must be greater than 0 and less than or equal 100");
`Log_Warn("[" $ Line + 1 $ "]" @ "Probability" @ "(" $ SpawnEntryCfg.Probability $ ")" @ "must be greater than 0 and less than or equal 100");
Errors = true;
}
SpawnEntry.SpawnCountBase = SpawnEntryCfg.SpawnCountBase;
if (SpawnEntry.SpawnCountBase <= 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "SpawnCountBase" @ "(" $ SpawnEntryCfg.SpawnCountBase $ ")" @ "must be greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "SpawnCountBase" @ "(" $ SpawnEntryCfg.SpawnCountBase $ ")" @ "must be greater than 0");
Errors = true;
}
SpawnEntry.SingleSpawnLimitDefault = SpawnEntryCfg.SingleSpawnLimit;
if (SpawnEntry.SingleSpawnLimit < 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "SingleSpawnLimit" @ "(" $ SpawnEntryCfg.SingleSpawnLimit $ ")" @ "must be equal or greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "SingleSpawnLimit" @ "(" $ SpawnEntryCfg.SingleSpawnLimit $ ")" @ "must be equal or greater than 0");
Errors = true;
}
SpawnEntry.SpawnAtPlayerStart = SpawnEntryCfg.bSpawnAtPlayerStart;
if (!Errors)
{
Loaded++;
SpawnList.AddItem(SpawnEntry);
`ZS_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (w" $ SpawnEntryCfg.Wave $ ")" @ SpawnEntryCfg.ZedClass);
`Log_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (w" $ SpawnEntryCfg.Wave $ ")" @ SpawnEntryCfg.ZedClass);
}
}
if (Loaded == default.Spawn.Length)
default.Spawn.Sort(SpawnListSort);
if (SpawnList.Length == default.Spawn.Length)
{
`ZS_Info("Regular spawn list loaded successfully");
`Log_Info("Regular spawn list loaded successfully (" $ default.Spawn.Length @ "entries)");
}
else
{
`ZS_Info("Regular spawn list: loaded" @ Loaded @ "of" @ default.Spawn.Length @ "entries");
`Log_Info("Regular spawn list: loaded" @ SpawnList.Length @ "of" @ default.Spawn.Length @ "entries");
}
return SpawnList;

View File

@ -11,7 +11,6 @@ struct S_SpawnEntryCfg
var int Probability;
var int SpawnCountBase;
var int SingleSpawnLimit;
var bool bSpawnAtPlayerStart;
};
var public config bool bStopRegularSpawn;
@ -46,7 +45,6 @@ private static function ApplyDefault()
SpawnEntry.RelativeStart = 0;
SpawnEntry.Delay = 60;
SpawnEntry.Probability = 100;
SpawnEntry.bSpawnAtPlayerStart = false;
foreach class'KFGameInfo_Endless'.default.SpecialWaveTypes(AIType)
{
SpawnEntry.Wave = AIType;
@ -61,15 +59,14 @@ public static function Array<S_SpawnEntry> Load(KFGameInfo_Endless KFGIE, E_LogL
local S_SpawnEntry SpawnEntry;
local int Line;
local bool Errors;
local int Loaded;
if (KFGIE == None)
{
`ZS_Info("Not Endless mode, skip loading special waves");
`Log_Info("Not Endless mode, skip loading special waves");
return SpawnList;
}
`ZS_Info("Load special waves spawn list:");
`Log_Info("Load special waves spawn list:");
foreach default.Spawn(SpawnEntryCfg, Line)
{
Errors = false;
@ -77,69 +74,66 @@ public static function Array<S_SpawnEntry> Load(KFGameInfo_Endless KFGIE, E_LogL
SpawnEntry.Wave = SpawnEntryCfg.Wave;
if (KFGIE.SpecialWaveTypes.Find(EAIType(SpawnEntryCfg.Wave)) == INDEX_NONE)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Unknown special wave:" @ SpawnEntryCfg.Wave);
`Log_Warn("[" $ Line + 1 $ "]" @ "Unknown special wave:" @ SpawnEntryCfg.Wave);
Errors = true;
}
SpawnEntry.ZedClass = class<KFPawn_Monster>(DynamicLoadObject(SpawnEntryCfg.ZedClass, class'Class'));
if (SpawnEntry.ZedClass == None)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Can't load zed class:" @ SpawnEntryCfg.ZedClass);
`Log_Warn("[" $ Line + 1 $ "]" @ "Can't load zed class:" @ SpawnEntryCfg.ZedClass);
Errors = true;
}
SpawnEntry.RelativeStartDefault = SpawnEntryCfg.RelativeStart / 100.f;
if (SpawnEntryCfg.RelativeStart < 0 || SpawnEntryCfg.RelativeStart > 100)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "RelativeStart" @ "(" $ SpawnEntryCfg.RelativeStart $ ")" @ "must be greater than or equal 0 and less than or equal 100");
`Log_Warn("[" $ Line + 1 $ "]" @ "RelativeStart" @ "(" $ SpawnEntryCfg.RelativeStart $ ")" @ "must be greater than or equal 0 and less than or equal 100");
Errors = true;
}
SpawnEntry.DelayDefault = SpawnEntryCfg.Delay;
if (SpawnEntryCfg.Delay <= 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0");
Errors = true;
}
SpawnEntry.Probability = SpawnEntryCfg.Probability / 100.f;
if (SpawnEntryCfg.Probability <= 0 || SpawnEntryCfg.Probability > 100)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "Probability" @ "(" $ SpawnEntryCfg.Probability $ ")" @ "must be greater than 0 and less than or equal 100");
`Log_Warn("[" $ Line + 1 $ "]" @ "Probability" @ "(" $ SpawnEntryCfg.Probability $ ")" @ "must be greater than 0 and less than or equal 100");
Errors = true;
}
SpawnEntry.SpawnCountBase = SpawnEntryCfg.SpawnCountBase;
if (SpawnEntry.SpawnCountBase <= 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "SpawnCountBase" @ "(" $ SpawnEntryCfg.SpawnCountBase $ ")" @ "must be greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "SpawnCountBase" @ "(" $ SpawnEntryCfg.SpawnCountBase $ ")" @ "must be greater than 0");
Errors = true;
}
SpawnEntry.SingleSpawnLimitDefault = SpawnEntryCfg.SingleSpawnLimit;
if (SpawnEntry.SingleSpawnLimit < 0)
{
`ZS_Warn("[" $ Line + 1 $ "]" @ "SingleSpawnLimit" @ "(" $ SpawnEntryCfg.SingleSpawnLimit $ ")" @ "must be equal or greater than 0");
`Log_Warn("[" $ Line + 1 $ "]" @ "SingleSpawnLimit" @ "(" $ SpawnEntryCfg.SingleSpawnLimit $ ")" @ "must be equal or greater than 0");
Errors = true;
}
SpawnEntry.SpawnAtPlayerStart = SpawnEntryCfg.bSpawnAtPlayerStart;
if (!Errors)
{
Loaded++;
SpawnList.AddItem(SpawnEntry);
`ZS_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (" $ SpawnEntryCfg.Wave $ ")" @ SpawnEntryCfg.ZedClass);
`Log_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (" $ SpawnEntryCfg.Wave $ ")" @ SpawnEntryCfg.ZedClass);
}
}
if (Loaded == default.Spawn.Length)
if (SpawnList.Length == default.Spawn.Length)
{
`ZS_Info("Special spawn list loaded successfully");
`Log_Info("Special spawn list loaded successfully (" $ default.Spawn.Length @ "entries)");
}
else
{
`ZS_Info("Special spawn list: loaded" @ Loaded @ "of" @ default.Spawn.Length @ "entries");
`Log_Info("Special spawn list: loaded" @ SpawnList.Length @ "of" @ default.Spawn.Length @ "entries");
}
return SpawnList;

View File

@ -1,24 +1,13 @@
class ZedSpawner extends Info
config(ZedSpawner);
const LatestVersion = 2;
const LatestVersion = 4;
const CfgSpawn = class'Spawn';
const CfgSpawnListRW = class'SpawnListRegular';
const CfgSpawnListBW = class'SpawnListBossWaves';
const CfgSpawnListSW = class'SpawnListSpecialWaves';
enum E_LogLevel
{
LL_WrongLevel,
LL_Fatal,
LL_Error,
LL_Warning,
LL_Info,
LL_Debug,
LL_Trace,
LL_All
};
const CfgSpawn = class'Spawn';
const CfgSpawnAtPlayerStart = class'SpawnAtPlayerStart';
const CfgSpawnListRW = class'SpawnListRegular';
const CfgSpawnListBW = class'SpawnListBossWaves';
const CfgSpawnListSW = class'SpawnListSpecialWaves';
struct S_SpawnEntry
{
@ -35,9 +24,9 @@ struct S_SpawnEntry
var float Delay;
var int PawnsLeft;
var int PawnsTotal;
var bool SpawnAtPlayerStart;
var bool ForceSpawn;
var String ZedNameFiller;
var int SmoothPawnPool;
};
var private config int Version;
@ -55,6 +44,7 @@ var private bool NoFreeSpawnSlots;
var private bool UseRegularSpawnList;
var private bool UseBossSpawnList;
var private bool UseSpecialSpawnList;
var private bool GlobalSpawnAtPlayerStart;
var private KFGameInfo_Survival KFGIS;
var private KFGameInfo_Endless KFGIE;
@ -69,18 +59,26 @@ var private int WaveTotalAI;
var private class<KFPawn_Monster> CurrentBossClass;
var private Array<class<KFPawn_Monster> > CustomZeds;
var private Array<class<KFPawn_Monster> > SpawnAtPlayerStartZeds;
var private bool SpawnActive;
var private String SpawnListsComment;
var private Array<ZedSpawnerRepLink> RepLinks;
public simulated function bool SafeDestroy()
{
return (bPendingDelete || bDeleteMe || Destroy());
}
public event PreBeginPlay()
{
`ZS_Trace(`Location);
`Log_Trace(`Location);
if (WorldInfo.NetMode == NM_Client)
{
`ZS_Fatal("NetMode == NM_Client, Destroy...");
Destroy();
`Log_Fatal("NetMode == NM_Client, Destroy...");
SafeDestroy();
return;
}
@ -89,9 +87,9 @@ public event PreBeginPlay()
public event PostBeginPlay()
{
`ZS_Trace(`Location);
`Log_Trace(`Location);
if (bPendingDelete) return;
if (bPendingDelete || bDeleteMe) return;
Super.PostBeginPlay();
@ -102,12 +100,12 @@ private function InitConfig()
{
if (Version == `NO_CONFIG)
{
Tickrate = 1.0f;
LogLevel = LL_Info;
SaveConfig();
}
CfgSpawn.static.InitConfig(Version, LatestVersion);
CfgSpawnAtPlayerStart.static.InitConfig(Version, LatestVersion);
CfgSpawnListRW.static.InitConfig(Version, LatestVersion, KFGIA);
CfgSpawnListBW.static.InitConfig(Version, LatestVersion, KFGIA);
CfgSpawnListSW.static.InitConfig(Version, LatestVersion);
@ -115,23 +113,26 @@ private function InitConfig()
switch (Version)
{
case `NO_CONFIG:
`ZS_Info("Config created");
`Log_Info("Config created");
case 1:
Tickrate = 1.0f;
case 2:
case 3:
case MaxInt:
`ZS_Info("Config updated to version"@LatestVersion);
`Log_Info("Config updated to version"@LatestVersion);
break;
case LatestVersion:
`ZS_Info("Config is up-to-date");
`Log_Info("Config is up-to-date");
break;
default:
`ZS_Warn("The config version is higher than the current version (are you using an old mutator?)");
`ZS_Warn("Config version is" @ Version @ "but current version is" @ LatestVersion);
`ZS_Warn("The config version will be changed to" @ LatestVersion);
`Log_Warn("The config version is higher than the current version (are you using an old mutator?)");
`Log_Warn("Config version is" @ Version @ "but current version is" @ LatestVersion);
`Log_Warn("The config version will be changed to" @ LatestVersion);
break;
}
@ -145,14 +146,15 @@ private function InitConfig()
private function Init()
{
local S_SpawnEntry SE;
local String CurrentMap;
`ZS_Trace(`Location);
`Log_Trace(`Location);
KFGIS = KFGameInfo_Survival(WorldInfo.Game);
if (KFGIS == None)
{
`ZS_Fatal("Incompatible gamemode:" @ WorldInfo.Game $ ". Destroy...");
Destroy();
`Log_Fatal("Incompatible gamemode:" @ WorldInfo.Game $ ". Destroy...");
SafeDestroy();
return;
}
@ -164,29 +166,34 @@ private function Init()
if (LogLevel == LL_WrongLevel)
{
LogLevel = LL_Info;
`ZS_Warn("Wrong 'LogLevel', return to default value");
`Log_Warn("Wrong 'LogLevel', return to default value");
SaveConfig();
}
`ZS_Log("LogLevel:" @ LogLevel);
`Log_Base("LogLevel:" @ LogLevel);
if (Tickrate <= 0)
{
`ZS_Error("Spawner tickrate must be positive (current value:" @ Tickrate $ ")");
`Log_Error("Spawner tickrate must be positive (current value:" @ Tickrate $ ")");
}
if (!CfgSpawn.static.Load(LogLevel) || Tickrate <= 0)
{
`ZS_Fatal("Wrong settings, Destroy...");
Destroy();
`Log_Fatal("Wrong settings, Destroy...");
SafeDestroy();
return;
}
dt = 1 / Tickrate;
`ZS_Info("Spawner tickrate:" @ Tickrate @ "(update every" @ dt $ "s)");
`Log_Info("Spawner tickrate:" @ Tickrate @ "(update every" @ dt $ "s)");
SpawnListRW = CfgSpawnListRW.static.Load(LogLevel);
SpawnListBW = CfgSpawnListBW.static.Load(LogLevel);
SpawnListSW = CfgSpawnListSW.static.Load(KFGIE, LogLevel);
SpawnAtPlayerStartZeds = CfgSpawnAtPlayerStart.static.Load(LogLevel);
CurrentMap = String(WorldInfo.GetPackageName());
GlobalSpawnAtPlayerStart = (CfgSpawnAtPlayerStart.default.Map.Find(CurrentMap) != INDEX_NONE);
`Log_Info("GlobalSpawnAtPlayerStart:" @ GlobalSpawnAtPlayerStart $ GlobalSpawnAtPlayerStart ? "(" $ CurrentMap $ ")" : "");
CurrentWave = INDEX_NONE;
SpecialWave = INDEX_NONE;
@ -219,7 +226,7 @@ private function PreloadContent()
foreach CustomZeds(PawnClass)
{
`ZS_Info("Preload content:" @ PawnClass);
`Log_Info("Preload content:" @ PawnClass);
PawnClass.static.PreloadContent();
}
}
@ -244,7 +251,7 @@ private function SpawnTimer()
local int Index;
local float Threshold;
`ZS_Trace(`Location);
`Log_Trace(`Location);
if (KFGIS.WaveNum != 0 && CurrentWave < KFGIS.WaveNum)
{
@ -311,16 +318,17 @@ private function SetupWave()
{
local Array<String> SpawnListNames;
local int WaveTotalAIDef;
local byte BaseWave;
local String WaveTypeInfo;
local S_SpawnEntry SE;
local EAIType SWType;
`ZS_Trace(`Location);
`Log_Trace(`Location);
if (CfgSpawn.default.bCyclicalSpawn && KFGIS.WaveNum > 1 && KFGIS.WaveNum == CycleWaveShift + CycleWaveSize * CurrentCycle)
{
CurrentCycle++;
`ZS_Info("Spawn cycle started:" @ CurrentCycle);
`Log_Info("Spawn cycle started:" @ CurrentCycle);
}
CurrentWave = KFGIS.WaveNum;
@ -345,7 +353,7 @@ private function SetupWave()
CurrentBossClass = KFGIA.BossAITypePawn(EBossAIType(KFGIS.MyKFGRI.BossIndex));
if (CurrentBossClass == None)
{
`ZS_Error("Can't determine boss class. Boss index:" @ KFGIS.MyKFGRI.BossIndex);
`Log_Error("Can't determine boss class. Boss index:" @ KFGIS.MyKFGRI.BossIndex);
}
else
{
@ -362,7 +370,7 @@ private function SetupWave()
if (WaveTotalAIDef != KFGIS.SpawnManager.WaveTotalAI)
{
`ZS_Info("increase WaveTotalAI from" @ WaveTotalAIDef @ "to" @ WaveTotalAI @ "due to ZedTotalMultiplier" @ "(" $ CfgSpawn.default.ZedTotalMultiplier $ ")");
`Log_Info("increase WaveTotalAI from" @ WaveTotalAIDef @ "to" @ WaveTotalAI @ "due to ZedTotalMultiplier" @ "(" $ CfgSpawn.default.ZedTotalMultiplier $ ")");
}
CurrentBossClass = None;
@ -379,9 +387,12 @@ private function SetupWave()
if (UseRegularSpawnList)
{
SpawnListNames.AddItem("regular");
BaseWave = KFGIS.WaveNum - CycleWaveSize * (CurrentCycle - 1);
foreach SpawnListRW(SE)
if (SE.Wave == KFGIS.WaveNum - CycleWaveSize * (CurrentCycle - 1))
if (SE.Wave == BaseWave)
SpawnListCurrent.AddItem(SE);
else if (SE.Wave > BaseWave)
break;
}
if (UseSpecialSpawnList)
@ -408,7 +419,7 @@ private function SetupWave()
WaveTypeInfo = "(" $ WaveTypeInfo $ ")";
}
`ZS_Info("Wave" @ CurrentWave @ WaveTypeInfo);
`Log_Info("Wave" @ CurrentWave @ WaveTypeInfo);
}
private function AdjustSpawnList(out Array<S_SpawnEntry> List)
@ -421,14 +432,11 @@ private function AdjustSpawnList(out Array<S_SpawnEntry> List)
local float PawnTotalF, PawnLimitF;
local int ZedNameMaxLength;
`ZS_Trace(`Location);
`Log_Trace(`Location);
Cycle = float(CurrentCycle);
Players = float(PlayerCount());
B = float(SE.SpawnCountBase);
L = float(SE.SingleSpawnLimitDefault);
TM = CfgSpawn.default.ZedTotalMultiplier;
TCM = CfgSpawn.default.SpawnTotalCycleMultiplier;
TPM = CfgSpawn.default.SpawnTotalPlayerMultiplier;
@ -436,7 +444,7 @@ private function AdjustSpawnList(out Array<S_SpawnEntry> List)
LM = CfgSpawn.default.SingleSpawnLimitMultiplier;
LCM = CfgSpawn.default.SingleSpawnLimitCycleMultiplier;
LPM = CfgSpawn.default.SingleSpawnLimitPlayerMultiplier;
ZedNameMaxLength = 0;
foreach List(SE, Index)
{
@ -455,6 +463,9 @@ private function AdjustSpawnList(out Array<S_SpawnEntry> List)
List[Index].Delay = 0.0f;
}
B = float(SE.SpawnCountBase);
L = float(SE.SingleSpawnLimitDefault);
PawnTotalF = B * (TM + TCM * (Cycle - 1.0f) + TPM * (Players - 1.0f));
PawnLimitF = L * (LM + LCM * (Cycle - 1.0f) + LPM * (Players - 1.0f));
@ -476,7 +487,7 @@ private function SpawnTimerLogger(bool Stop, optional String Comment)
{
local String Message;
`ZS_Trace(`Location);
`Log_Trace(`Location);
if (Stop)
Message = "Stop spawn";
@ -488,7 +499,7 @@ private function SpawnTimerLogger(bool Stop, optional String Comment)
if (SpawnActive == Stop)
{
`ZS_Info(Message);
`Log_Info(Message);
SpawnActive = !Stop;
}
}
@ -498,12 +509,14 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
local S_SpawnEntry SE;
local int FreeSpawnSlots, PawnCount, Spawned;
local String Action, Comment, NextSpawn;
local bool SpawnAtPlayerStart;
`ZS_Trace(`Location);
`Log_Trace(`Location);
SE = SpawnList[Index];
SpawnList[Index].Delay = float(SE.DelayDefault);
if (FRand() <= SE.Probability || SE.ForceSpawn)
{
if (SE.SingleSpawnLimit == 0 || SE.PawnsLeft < SE.SingleSpawnLimit)
@ -515,6 +528,15 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
PawnCount = SE.SingleSpawnLimit;
}
if (CfgSpawn.default.bSmoothSpawn)
{
if (SE.SmoothPawnPool <= 0)
{
SpawnList[Index].SmoothPawnPool = PawnCount;
}
PawnCount = 1;
}
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
{
FreeSpawnSlots = KFGIS.MyKFGRI.AIRemaining - KFGIS.AIAliveCount;
@ -530,7 +552,9 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
}
}
Spawned = SpawnZed(SE.ZedClass, PawnCount, SE.SpawnAtPlayerStart);
SpawnAtPlayerStart = (GlobalSpawnAtPlayerStart || (SpawnAtPlayerStartZeds.Find(SE.ZedClass) != INDEX_NONE));
Spawned = SpawnZed(SE.ZedClass, PawnCount, SpawnAtPlayerStart);
if (Spawned == INDEX_NONE)
{
SpawnList[Index].Delay = 5.0f;
@ -546,9 +570,26 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
}
else
{
SpawnList[Index].ForceSpawn = false;
Action = "Spawned";
Comment = "x" $ Spawned;
if (CfgSpawn.default.bSmoothSpawn)
{
SpawnList[Index].SmoothPawnPool -= Spawned;
if (SpawnList[Index].SmoothPawnPool > 0)
{
SpawnList[Index].Delay = 1.0f;
SpawnList[Index].ForceSpawn = true;
}
else
{
SpawnList[Index].Delay = float(SE.DelayDefault);
SpawnList[Index].ForceSpawn = false;
}
}
else
{
SpawnList[Index].ForceSpawn = false;
}
}
}
else
@ -561,7 +602,14 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
SpawnList[Index].PawnsLeft -= Spawned;
if (SpawnList[Index].PawnsLeft > 0)
{
NextSpawn = "next after" @ SE.DelayDefault $ "sec," @ "pawns left:" @ SpawnList[Index].PawnsLeft;
if (CfgSpawn.default.bSmoothSpawn && SpawnList[Index].SmoothPawnPool > 0)
{
NextSpawn = "next after" @ Round(SpawnList[Index].Delay) $ "sec," @ "pawns left:" @ SpawnList[Index].SmoothPawnPool @ "(" $ SpawnList[Index].PawnsLeft $ ")";
}
else
{
NextSpawn = "next after" @ SE.DelayDefault $ "sec," @ "pawns left:" @ SpawnList[Index].PawnsLeft;
}
}
SpawnLog(SE, Action, Comment, NextSpawn);
}
@ -571,7 +619,7 @@ private function SpawnLog(S_SpawnEntry SE, String Action, optional String Commen
if (Comment != "") Comment = ":" @ Comment;
if (NextSpawn != "") NextSpawn = "(" $ NextSpawn $ ")";
`ZS_Info(String(SE.ZedClass) $ SE.ZedNameFiller @ ">" @ Action $ Comment @ NextSpawn);
`Log_Info(String(SE.ZedClass) $ SE.ZedNameFiller @ ">" @ Action $ Comment @ NextSpawn);
}
private function int PlayerCount()
@ -580,7 +628,7 @@ private function int PlayerCount()
local int HumanPlayers;
local KFOnlineGameSettings KFGameSettings;
`ZS_Trace(`Location);
`Log_Trace(`Location);
if (KFGIS.PlayfabInter != None && KFGIS.PlayfabInter.GetGameSettings() != None)
{
@ -605,7 +653,7 @@ private function Vector PlayerStartLocation()
{
local PlayerController PC;
`ZS_Trace(`Location);
`Log_Trace(`Location);
foreach WorldInfo.AllControllers(class'PlayerController', PC)
return KFGIS.FindPlayerStart(PC, 0).Location;
@ -613,9 +661,10 @@ private function Vector PlayerStartLocation()
return KFGIS.FindPlayerStart(None, 0).Location;
}
private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, bool SpawnAtPlayerStart)
private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, optional bool SpawnAtPlayerStart = false)
{
local Array<class<KFPawn_Monster> > CustomSquad;
local ESquadType PrevDesiredSquadType;
local Vector SpawnLocation, PlayerStart;
local KFSpawnVolume SpawnVolume;
local KFPawn_Monster KFPM;
@ -623,7 +672,7 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, boo
local int Failed, Spawned;
local int Index;
`ZS_Trace(`Location);
`Log_Trace(`Location);
PlayerStart = PlayerStartLocation();
if (SpawnAtPlayerStart)
@ -639,12 +688,18 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, boo
CustomSquad.AddItem(ZedClass);
}
PrevDesiredSquadType = KFGIS.SpawnManager.DesiredSquadType;
KFGIS.SpawnManager.SetDesiredSquadTypeForZedList(CustomSquad);
SpawnVolume = KFGIS.SpawnManager.GetBestSpawnVolume(CustomSquad);
KFGIS.SpawnManager.DesiredSquadType = PrevDesiredSquadType;
if (SpawnVolume == None)
{
return INDEX_NONE;
}
SpawnVolume.VolumeChosenCount++;
SpawnLocation = SpawnVolume.Location;
if (SpawnLocation == PlayerStart)
{
@ -660,7 +715,7 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, boo
KFPM = Spawn(ZedClass,,, SpawnLocation, rot(0,0,1),, true);
if (KFPM == None)
{
`ZS_Error("Can't spawn" @ ZedClass);
`Log_Error("Can't spawn" @ ZedClass);
Failed++;
continue;
}
@ -668,7 +723,7 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, boo
C = KFPM.Spawn(KFPM.ControllerClass);
if (C == None)
{
`ZS_Error("Can't spawn controller for" @ ZedClass $ ". Destroy this" @ KFPM $ "...");
`Log_Error("Can't spawn controller for" @ ZedClass $ ". Destroy this" @ KFPM $ "...");
KFPM.Destroy();
Failed++;
continue;
@ -677,6 +732,11 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, boo
Spawned++;
}
if (Spawned > 0)
{
KFGIS.SpawnManager.LastAISpawnVolume = SpawnVolume;
}
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
{
KFGIS.NumAIFinishedSpawning += Spawned;
@ -690,21 +750,60 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int PawnCount, boo
public function NotifyLogin(Controller C)
{
local ZedSpawnerRepLink RepLink;
`ZS_Trace(`Location);
RepLink = Spawn(class'ZedSpawnerRepLink', C);
RepLink.LogLevel = LogLevel;
RepLink.CustomZeds = CustomZeds;
RepLink.ServerSync();
`Log_Trace(`Location);
CreateRepLink(C);
}
public function NotifyLogout(Controller C)
{
`ZS_Trace(`Location);
`Log_Trace(`Location);
DestroyRepLink(C);
}
public function bool CreateRepLink(Controller C)
{
local ZedSpawnerRepLink RepLink;
return;
`Log_Trace(`Location);
if (C == None) return false;
RepLink = Spawn(class'ZedSpawnerRepLink', C);
if (RepLink == None) return false;
RepLink.LogLevel = LogLevel;
RepLink.CustomZeds = CustomZeds;
RepLink.ZS = Self;
RepLinks.AddItem(RepLink);
RepLink.ServerSync();
return true;
}
public function bool DestroyRepLink(Controller C)
{
local ZedSpawnerRepLink RepLink;
`Log_Trace(`Location);
if (C == None) return false;
foreach RepLinks(RepLink)
{
if (RepLink.Owner == C)
{
RepLink.SafeDestroy();
RepLinks.RemoveItem(RepLink);
return true;
}
}
return false;
}
DefaultProperties

View File

@ -3,7 +3,7 @@ class ZedSpawnerMut extends KFMutator
var private ZedSpawner ZS;
event PreBeginPlay()
public event PreBeginPlay()
{
Super.PreBeginPlay();
@ -11,19 +11,19 @@ event PreBeginPlay()
foreach WorldInfo.DynamicActors(class'ZedSpawner', ZS)
{
`ZS_Log("Found 'ZedSpawner'");
`Log_Base("Found 'ZedSpawner'");
break;
}
if (ZS == None)
{
`ZS_Log("Spawn 'ZedSpawner'");
`Log_Base("Spawn 'ZedSpawner'");
ZS = WorldInfo.Spawn(class'ZedSpawner');
}
if (ZS == None)
{
`ZS_Log("Can't Spawn 'ZedSpawner', Destroy...");
`Log_Base("Can't Spawn 'ZedSpawner', Destroy...");
Destroy();
}
}
@ -38,14 +38,14 @@ public function AddMutator(Mutator Mut)
Super.AddMutator(Mut);
}
function NotifyLogin(Controller C)
public function NotifyLogin(Controller C)
{
Super.NotifyLogin(C);
ZS.NotifyLogin(C);
}
function NotifyLogout(Controller C)
public function NotifyLogout(Controller C)
{
Super.NotifyLogout(C);

View File

@ -1,8 +1,9 @@
class ZedSpawnerRepLink extends ReplicationInfo;
var public E_LogLevel LogLevel;
var public Array<class<KFPawn_Monster> > CustomZeds;
var private int Recieved;
var public ZedSpawner ZS;
var public E_LogLevel LogLevel;
var public Array<class<KFPawn_Monster> > CustomZeds;
var private int Recieved;
replication
{
@ -10,13 +11,16 @@ replication
LogLevel;
}
public simulated function bool SafeDestroy() { if (!bPendingDelete) return Destroy(); else return true; }
public simulated function bool SafeDestroy()
{
return (bPendingDelete || bDeleteMe || Destroy());
}
public reliable client function ClientSync(class<KFPawn_Monster> CustomZed)
{
`ZS_Trace(`Location);
`Log_Trace(`Location);
`ZS_Debug("Received:" @ CustomZed);
`Log_Debug("Received:" @ CustomZed);
CustomZeds.AddItem(CustomZed);
ServerSync();
}
@ -25,11 +29,11 @@ public reliable client function SyncFinished()
{
local class<KFPawn_Monster> CustomZed;
`ZS_Trace(`Location);
`Log_Trace(`Location);
foreach CustomZeds(CustomZed)
{
`ZS_Debug("Preload Content for" @ CustomZed);
`Log_Debug("Preload Content for" @ CustomZed);
CustomZed.static.PreloadContent();
}
@ -38,17 +42,22 @@ public reliable client function SyncFinished()
public reliable server function ServerSync()
{
`ZS_Trace(`Location);
`Log_Trace(`Location);
if (bPendingDelete || bDeleteMe) return;
if (CustomZeds.Length == Recieved || WorldInfo.NetMode == NM_StandAlone)
{
`ZS_Debug("Sync finished");
`Log_Debug("Sync finished");
SyncFinished();
SafeDestroy();
if (!ZS.DestroyRepLink(Controller(Owner)))
{
SafeDestroy();
}
}
else
{
`ZS_Debug("Sync:" @ CustomZeds[Recieved]);
`Log_Debug("Sync:" @ CustomZeds[Recieved]);
ClientSync(CustomZeds[Recieved++]);
}
}

View File

@ -0,0 +1,19 @@
class _Logger extends Object
abstract;
enum E_LogLevel
{
LL_WrongLevel,
LL_Fatal,
LL_Error,
LL_Warning,
LL_Info,
LL_Debug,
LL_Trace,
LL_All
};
defaultproperties
{
}

2
ZedSpawner/Constants.uci Normal file
View File

@ -0,0 +1,2 @@
// Constants
`define NO_CONFIG 0

View File

@ -1,12 +1,3 @@
`define ZS_Tag 'ZedSpawner'
`define ZS_Log(msg, cond) `log(`msg `if(`cond), `cond`{endif}, `ZS_Tag)
`define ZS_Fatal(msg) `log("FATAL:" @ `msg, (LogLevel >= LL_Fatal), `ZS_Tag)
`define ZS_Error(msg) `log("ERROR:" @ `msg, (LogLevel >= LL_Error), `ZS_Tag)
`define ZS_Warn(msg) `log("WARN:" @ `msg, (LogLevel >= LL_Warning), `ZS_Tag)
`define ZS_Info(msg) `log("INFO:" @ `msg, (LogLevel >= LL_Info), `ZS_Tag)
`define ZS_Debug(msg) `log("DEBUG:" @ `msg, (LogLevel >= LL_Debug), `ZS_Tag)
`define ZS_Trace(msg) `log("TRACE:" @ `msg, (LogLevel >= LL_Trace), `ZS_Tag)
`define NO_CONFIG 0
// Imports
`include(Logger.uci)
`include(Constants.uci)

11
ZedSpawner/Logger.uci Normal file
View File

@ -0,0 +1,11 @@
// Logger
`define Log_Tag 'ZedSpawner'
`define Log_Base(msg, cond) `log(`msg `if(`cond), `cond`{endif}, `Log_Tag)
`define Log_Fatal(msg) `log("FATAL:" @ `msg, (LogLevel >= LL_Fatal), `Log_Tag)
`define Log_Error(msg) `log("ERROR:" @ `msg, (LogLevel >= LL_Error), `Log_Tag)
`define Log_Warn(msg) `log("WARN:" @ `msg, (LogLevel >= LL_Warning), `Log_Tag)
`define Log_Info(msg) `log("INFO:" @ `msg, (LogLevel >= LL_Info), `Log_Tag)
`define Log_Debug(msg) `log("DEBUG:" @ `msg, (LogLevel >= LL_Debug), `Log_Tag)
`define Log_Trace(msg) `log("TRACE:" @ `msg, (LogLevel >= LL_Trace), `Log_Tag)

2
tools

Submodule tools updated: 02222cf453...2f173aad7a