GenZmeY version
This commit is contained in:
parent
7d0c523562
commit
6285a921a5
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "tools"]
|
||||||
|
path = tools
|
||||||
|
url = https://github.com/GenZmeY/KF2-BuildTools
|
58
ZedSpawner/Classes/Spawn.uc
Normal file
58
ZedSpawner/Classes/Spawn.uc
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
class Spawn extends Object
|
||||||
|
dependson(ZedSpawner)
|
||||||
|
config(ZedSpawner);
|
||||||
|
|
||||||
|
var config bool bCyclicalSpawn;
|
||||||
|
var config bool bShadowSpawn;
|
||||||
|
var config float ZedMultiplier;
|
||||||
|
var config float PlayerMultiplier;
|
||||||
|
var config float CycleMultiplier;
|
||||||
|
var config int AliveSpawnLimit;
|
||||||
|
|
||||||
|
public static function InitConfig()
|
||||||
|
{
|
||||||
|
default.bCyclicalSpawn = true;
|
||||||
|
default.bShadowSpawn = true;
|
||||||
|
default.ZedMultiplier = 1.0;
|
||||||
|
default.PlayerMultiplier = 0.25;
|
||||||
|
default.CycleMultiplier = 0.25;
|
||||||
|
default.AliveSpawnLimit = 0;
|
||||||
|
|
||||||
|
StaticSaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function bool Load(E_LogLevel LogLevel)
|
||||||
|
{
|
||||||
|
local bool Errors;
|
||||||
|
|
||||||
|
if (default.ZedMultiplier <= 0.f)
|
||||||
|
{
|
||||||
|
`ZS_Error("ZedMultiplier" @ "(" $ default.ZedMultiplier $ ")" @ "must be greater than 0.0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default.PlayerMultiplier < 0.f)
|
||||||
|
{
|
||||||
|
`ZS_Error("PlayerMultiplier" @ "(" $ default.PlayerMultiplier $ ")" @ "must be greater than or equal 0.0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default.CycleMultiplier < 0.f)
|
||||||
|
{
|
||||||
|
`ZS_Error("CycleMultiplier" @ "(" $ default.CycleMultiplier $ ")" @ "must be greater than or equal 0.0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (default.AliveSpawnLimit < 0)
|
||||||
|
{
|
||||||
|
`ZS_Error("AliveSpawnLimit" @ "(" $ default.AliveSpawnLimit $ ")" @ "must be greater than or equal 0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !Errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultproperties
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
125
ZedSpawner/Classes/SpawnList.uc
Normal file
125
ZedSpawner/Classes/SpawnList.uc
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
class SpawnList extends Object
|
||||||
|
dependson(ZedSpawner)
|
||||||
|
config(ZedSpawner);
|
||||||
|
|
||||||
|
struct S_SpawnEntryCfg
|
||||||
|
{
|
||||||
|
var int Wave;
|
||||||
|
var String ZedClass;
|
||||||
|
var int RelativeDelay;
|
||||||
|
var int Delay;
|
||||||
|
var int Probability;
|
||||||
|
var int SpawnAtOnce;
|
||||||
|
var int MaxSpawns;
|
||||||
|
var bool bSpawnAtPlayerStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
var config Array<S_SpawnEntryCfg> Spawn;
|
||||||
|
|
||||||
|
public static function InitConfig()
|
||||||
|
{
|
||||||
|
local S_SpawnEntryCfg SpawnEntry;
|
||||||
|
|
||||||
|
default.Spawn.Length = 0;
|
||||||
|
|
||||||
|
SpawnEntry.Wave = 1;
|
||||||
|
SpawnEntry.ZedClass = "SomePackage.SomeZedClass1";
|
||||||
|
SpawnEntry.SpawnAtOnce = 1;
|
||||||
|
SpawnEntry.MaxSpawns = 1;
|
||||||
|
SpawnEntry.RelativeDelay = 0;
|
||||||
|
SpawnEntry.Delay = 60;
|
||||||
|
SpawnEntry.Probability = 100;
|
||||||
|
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||||
|
default.Spawn.AddItem(SpawnEntry);
|
||||||
|
|
||||||
|
SpawnEntry.Wave = 2;
|
||||||
|
SpawnEntry.ZedClass = "SomePackage.SomeZedClass2";
|
||||||
|
SpawnEntry.SpawnAtOnce = 2;
|
||||||
|
SpawnEntry.MaxSpawns = 4;
|
||||||
|
SpawnEntry.RelativeDelay = 0;
|
||||||
|
SpawnEntry.Delay = 30;
|
||||||
|
SpawnEntry.Probability = 50;
|
||||||
|
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||||
|
default.Spawn.AddItem(SpawnEntry);
|
||||||
|
|
||||||
|
StaticSaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
||||||
|
{
|
||||||
|
local Array<S_SpawnEntry> SpawnList;
|
||||||
|
local S_SpawnEntryCfg SpawnEntryCfg;
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
local int Line;
|
||||||
|
local bool Errors;
|
||||||
|
|
||||||
|
`ZS_Info("Load spawn list:", LogLevel);
|
||||||
|
foreach default.Spawn(SpawnEntryCfg, Line)
|
||||||
|
{
|
||||||
|
Errors = false;
|
||||||
|
|
||||||
|
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, LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.SpawnAtOnce = SpawnEntryCfg.SpawnAtOnce;
|
||||||
|
if (SpawnEntry.SpawnAtOnce <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "SpawnAtOnce" @ "(" $ SpawnEntryCfg.SpawnAtOnce $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
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", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.RelativeDelayDefault = SpawnEntryCfg.RelativeDelay / 100.f;
|
||||||
|
if (SpawnEntryCfg.RelativeDelay < 0 || SpawnEntryCfg.RelativeDelay > 100)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "RelativeDelay" @ "(" $ SpawnEntryCfg.RelativeDelay $ ")" @ "must be greater than or equal 0 and less than or equal 100", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.DelayDefault = SpawnEntryCfg.Delay;
|
||||||
|
if (SpawnEntryCfg.Delay <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.MaxSpawns = SpawnEntryCfg.MaxSpawns;
|
||||||
|
if (SpawnEntryCfg.Delay <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "MaxSpawns" @ "(" $ SpawnEntryCfg.MaxSpawns $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.SpawnAtPlayerStart = SpawnEntryCfg.bSpawnAtPlayerStart;
|
||||||
|
|
||||||
|
if (!Errors)
|
||||||
|
{
|
||||||
|
SpawnList.AddItem(SpawnEntry);
|
||||||
|
`ZS_Info("[" $ Line + 1 $ "]" @ "Loaded successfully:" @ SpawnEntryCfg.Wave @ SpawnEntryCfg.ZedClass, LogLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SpawnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultproperties
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
111
ZedSpawner/Classes/SpawnListBossWaves.uc
Normal file
111
ZedSpawner/Classes/SpawnListBossWaves.uc
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
class SpawnListBossWaves extends Object
|
||||||
|
dependson(ZedSpawner)
|
||||||
|
config(ZedSpawner);
|
||||||
|
|
||||||
|
struct S_SpawnEntryCfg
|
||||||
|
{
|
||||||
|
var String BossClass;
|
||||||
|
var String ZedClass;
|
||||||
|
var int Delay;
|
||||||
|
var int Probability;
|
||||||
|
var int SpawnAtOnce;
|
||||||
|
var int MaxSpawns;
|
||||||
|
var bool bSpawnAtPlayerStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
var config bool bStopRegularSpawn;
|
||||||
|
var config Array<S_SpawnEntryCfg> Spawn;
|
||||||
|
|
||||||
|
public static function InitConfig()
|
||||||
|
{
|
||||||
|
local S_SpawnEntryCfg SpawnEntry;
|
||||||
|
|
||||||
|
default.bStopRegularSpawn = true;
|
||||||
|
|
||||||
|
default.Spawn.Length = 0;
|
||||||
|
|
||||||
|
SpawnEntry.BossClass = "KFGameContent.KFPawn_ZedFleshpoundKing";
|
||||||
|
SpawnEntry.ZedClass = "SomePackage.SomeFleshpoundClass";
|
||||||
|
SpawnEntry.SpawnAtOnce = 1;
|
||||||
|
SpawnEntry.MaxSpawns = 1;
|
||||||
|
SpawnEntry.Delay = 60;
|
||||||
|
SpawnEntry.Probability = 1;
|
||||||
|
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||||
|
default.Spawn.AddItem(SpawnEntry);
|
||||||
|
|
||||||
|
StaticSaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
||||||
|
{
|
||||||
|
local Array<S_SpawnEntry> SpawnList;
|
||||||
|
local S_SpawnEntryCfg SpawnEntryCfg;
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
local int Line;
|
||||||
|
local bool Errors;
|
||||||
|
|
||||||
|
`ZS_Info("Load boss waves spawn list:", LogLevel);
|
||||||
|
foreach default.Spawn(SpawnEntryCfg, Line)
|
||||||
|
{
|
||||||
|
Errors = false;
|
||||||
|
|
||||||
|
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, LogLevel);
|
||||||
|
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, LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.SpawnAtOnce = SpawnEntryCfg.SpawnAtOnce;
|
||||||
|
if (SpawnEntry.SpawnAtOnce <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "SpawnAtOnce" @ "(" $ SpawnEntryCfg.SpawnAtOnce $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
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", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.RelativeDelayDefault = 0.f;
|
||||||
|
|
||||||
|
SpawnEntry.DelayDefault = SpawnEntryCfg.Delay;
|
||||||
|
if (SpawnEntryCfg.Delay <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.MaxSpawns = SpawnEntryCfg.MaxSpawns;
|
||||||
|
if (SpawnEntryCfg.Delay <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "MaxSpawns" @ "(" $ SpawnEntryCfg.MaxSpawns $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.SpawnAtPlayerStart = SpawnEntryCfg.bSpawnAtPlayerStart;
|
||||||
|
|
||||||
|
if (!Errors)
|
||||||
|
{
|
||||||
|
SpawnList.AddItem(SpawnEntry);
|
||||||
|
`ZS_Info("[" $ Line + 1 $ "]" @ "Loaded successfully:" @ SpawnEntryCfg.BossClass @ SpawnEntryCfg.ZedClass, LogLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SpawnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultproperties
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
124
ZedSpawner/Classes/SpawnListSpecialWaves.uc
Normal file
124
ZedSpawner/Classes/SpawnListSpecialWaves.uc
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
class SpawnListSpecialWaves extends Object
|
||||||
|
dependson(ZedSpawner)
|
||||||
|
config(ZedSpawner);
|
||||||
|
|
||||||
|
struct S_SpawnEntryCfg
|
||||||
|
{
|
||||||
|
var EAIType Wave;
|
||||||
|
var String ZedClass;
|
||||||
|
var int RelativeDelay;
|
||||||
|
var int Delay;
|
||||||
|
var int Probability;
|
||||||
|
var int SpawnAtOnce;
|
||||||
|
var int MaxSpawns;
|
||||||
|
var bool bSpawnAtPlayerStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
var config bool bStopRegularSpawn;
|
||||||
|
var config Array<S_SpawnEntryCfg> Spawn;
|
||||||
|
|
||||||
|
public static function InitConfig()
|
||||||
|
{
|
||||||
|
local S_SpawnEntryCfg SpawnEntry;
|
||||||
|
|
||||||
|
default.bStopRegularSpawn = true;
|
||||||
|
|
||||||
|
default.Spawn.Length = 0;
|
||||||
|
|
||||||
|
SpawnEntry.Wave = AT_Husk;
|
||||||
|
SpawnEntry.ZedClass = "SomePackage.SomeHuskClass";
|
||||||
|
SpawnEntry.SpawnAtOnce = 1;
|
||||||
|
SpawnEntry.MaxSpawns = 1;
|
||||||
|
SpawnEntry.RelativeDelay = 0;
|
||||||
|
SpawnEntry.Delay = 60;
|
||||||
|
SpawnEntry.Probability = 1;
|
||||||
|
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||||
|
default.Spawn.AddItem(SpawnEntry);
|
||||||
|
|
||||||
|
StaticSaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Array<S_SpawnEntry> Load(KFGameInfo_Endless KFGIE, E_LogLevel LogLevel)
|
||||||
|
{
|
||||||
|
local Array<S_SpawnEntry> SpawnList;
|
||||||
|
local S_SpawnEntryCfg SpawnEntryCfg;
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
local int Line;
|
||||||
|
local bool Errors;
|
||||||
|
|
||||||
|
if (KFGIE == None)
|
||||||
|
{
|
||||||
|
`ZS_Info("Not Endless mode, skip loading special waves", LogLevel);
|
||||||
|
return SpawnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
`ZS_Info("Load special waves spawn list:", LogLevel);
|
||||||
|
foreach default.Spawn(SpawnEntryCfg, Line)
|
||||||
|
{
|
||||||
|
Errors = false;
|
||||||
|
|
||||||
|
SpawnEntry.Wave = SpawnEntryCfg.Wave;
|
||||||
|
if (KFGIE.SpecialWaveTypes.Find(EAIType(SpawnEntryCfg.Wave)) == INDEX_NONE)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "Unknown special wave:" @ SpawnEntryCfg.Wave, LogLevel);
|
||||||
|
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, LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.SpawnAtOnce = SpawnEntryCfg.SpawnAtOnce;
|
||||||
|
if (SpawnEntry.SpawnAtOnce <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "SpawnAtOnce" @ "(" $ SpawnEntryCfg.SpawnAtOnce $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
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", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.RelativeDelayDefault = SpawnEntryCfg.RelativeDelay / 100.f;
|
||||||
|
if (SpawnEntryCfg.RelativeDelay < 0 || SpawnEntryCfg.RelativeDelay > 100)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "RelativeDelay" @ "(" $ SpawnEntryCfg.RelativeDelay $ ")" @ "must be greater than or equal 0 and less than or equal 100", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.DelayDefault = SpawnEntryCfg.Delay;
|
||||||
|
if (SpawnEntryCfg.Delay <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "Delay" @ "(" $ SpawnEntryCfg.Delay $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.MaxSpawns = SpawnEntryCfg.MaxSpawns;
|
||||||
|
if (SpawnEntryCfg.Delay <= 0)
|
||||||
|
{
|
||||||
|
`ZS_Warn("[" $ Line + 1 $ "]" @ "MaxSpawns" @ "(" $ SpawnEntryCfg.MaxSpawns $ ")" @ "must be greater than 0", LogLevel);
|
||||||
|
Errors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEntry.SpawnAtPlayerStart = SpawnEntryCfg.bSpawnAtPlayerStart;
|
||||||
|
|
||||||
|
if (!Errors)
|
||||||
|
{
|
||||||
|
SpawnList.AddItem(SpawnEntry);
|
||||||
|
`ZS_Info("[" $ Line + 1 $ "]" @ "Loaded successfully:" @ SpawnEntryCfg.Wave @ SpawnEntryCfg.ZedClass, LogLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SpawnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultproperties
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
552
ZedSpawner/Classes/ZedSpawner.uc
Normal file
552
ZedSpawner/Classes/ZedSpawner.uc
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
class ZedSpawner extends Info
|
||||||
|
config(ZedSpawner);
|
||||||
|
|
||||||
|
var const int dt;
|
||||||
|
|
||||||
|
var const class<Spawn> CfgSpawn;
|
||||||
|
var const class<SpawnList> CfgSpawnList;
|
||||||
|
var const class<SpawnListBossWaves> CfgSpawnListBW;
|
||||||
|
var const class<SpawnListSpecialWaves> CfgSpawnListSW;
|
||||||
|
|
||||||
|
enum E_LogLevel
|
||||||
|
{
|
||||||
|
LL_WrongLevel,
|
||||||
|
LL_Fatal,
|
||||||
|
LL_Error,
|
||||||
|
LL_Warning,
|
||||||
|
LL_Info,
|
||||||
|
LL_Debug,
|
||||||
|
LL_Trace,
|
||||||
|
LL_All
|
||||||
|
};
|
||||||
|
|
||||||
|
struct S_SpawnEntry
|
||||||
|
{
|
||||||
|
var class<KFPawn_Monster> BossClass;
|
||||||
|
var class<KFPawn_Monster> ZedClass;
|
||||||
|
var int Wave;
|
||||||
|
var int SpawnAtOnce;
|
||||||
|
var float Probability;
|
||||||
|
var float RelativeDelayDefault;
|
||||||
|
var float RelativeDelay;
|
||||||
|
var int DelayDefault;
|
||||||
|
var int Delay;
|
||||||
|
var int SpawnsDone;
|
||||||
|
var int MaxSpawns;
|
||||||
|
var bool SpawnAtPlayerStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
var config bool bConfigInitialized;
|
||||||
|
var config E_LogLevel LogLevel;
|
||||||
|
|
||||||
|
var private Array<S_SpawnEntry> SpawnList;
|
||||||
|
var private Array<S_SpawnEntry> SpawnListBW;
|
||||||
|
var private Array<S_SpawnEntry> SpawnListSW;
|
||||||
|
|
||||||
|
var private KFGameInfo_Survival KFGIS;
|
||||||
|
var private KFGameInfo_Endless KFGIE;
|
||||||
|
|
||||||
|
var private int CurrentWave;
|
||||||
|
var private int CurrentCycle;
|
||||||
|
var private int CycleWaveShift;
|
||||||
|
var private int CycleWaveSize;
|
||||||
|
|
||||||
|
var private int WaveTotalAI;
|
||||||
|
var private class<KFPawn_Monster> CurrentBossClass;
|
||||||
|
|
||||||
|
var private String SpawnTimerLastMessage;
|
||||||
|
|
||||||
|
var private Array<class<KFPawn_Monster> > BossClassCache;
|
||||||
|
|
||||||
|
event PostBeginPlay()
|
||||||
|
{
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
Super.PostBeginPlay();
|
||||||
|
|
||||||
|
if (WorldInfo.NetMode == NM_Client)
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function Init()
|
||||||
|
{
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
if (!bConfigInitialized)
|
||||||
|
{
|
||||||
|
bConfigInitialized = true;
|
||||||
|
LogLevel = LL_Info;
|
||||||
|
SaveConfig();
|
||||||
|
CfgSpawn.static.InitConfig();
|
||||||
|
CfgSpawnList.static.InitConfig();
|
||||||
|
CfgSpawnListBW.static.InitConfig();
|
||||||
|
CfgSpawnListSW.static.InitConfig();
|
||||||
|
`ZS_Info("Config initialized.", LogLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LogLevel == LL_WrongLevel)
|
||||||
|
{
|
||||||
|
LogLevel = LL_Info;
|
||||||
|
`ZS_Warn("Wrong 'LogLevel', return to default value", LogLevel);
|
||||||
|
SaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
`ZS_Log("LogLevel:" @ LogLevel);
|
||||||
|
|
||||||
|
if (!CfgSpawn.static.Load(LogLevel))
|
||||||
|
{
|
||||||
|
`ZS_Fatal("Wrong settings, Destroy...", LogLevel);
|
||||||
|
Destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentWave = INDEX_NONE;
|
||||||
|
KFGIS = KFGameInfo_Survival(WorldInfo.Game);
|
||||||
|
if (KFGIS == None)
|
||||||
|
{
|
||||||
|
`ZS_Fatal("Incompatible gamemode:" @ WorldInfo.Game $ ". Destroy...", LogLevel);
|
||||||
|
Destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KFGIE = KFGameInfo_Endless(KFGIS);
|
||||||
|
|
||||||
|
SpawnList = CfgSpawnList.static.Load(LogLevel);
|
||||||
|
SpawnListBW = CfgSpawnListBW.static.Load(LogLevel);
|
||||||
|
SpawnListSW = CfgSpawnListSW.static.Load(KFGIE, LogLevel);
|
||||||
|
|
||||||
|
foreach SpawnListBW(SpawnEntry)
|
||||||
|
BossClassCache.AddItem(SpawnEntry.BossClass);
|
||||||
|
|
||||||
|
CurrentCycle = 1;
|
||||||
|
CycleWaveSize = 0;
|
||||||
|
CycleWaveShift = MaxInt;
|
||||||
|
foreach SpawnList(SpawnEntry)
|
||||||
|
{
|
||||||
|
CycleWaveShift = Min(CycleWaveShift, SpawnEntry.Wave);
|
||||||
|
CycleWaveSize = Max(CycleWaveSize, SpawnEntry.Wave);
|
||||||
|
}
|
||||||
|
CycleWaveSize = CycleWaveSize - CycleWaveShift + 1;
|
||||||
|
|
||||||
|
SetTimer(float(dt), true, nameof(SpawnTimer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function SpawnTimer()
|
||||||
|
{
|
||||||
|
local int WaveTotalAIDef;
|
||||||
|
local int SpecialWave;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
if (CurrentWave < KFGIS.WaveNum)
|
||||||
|
{
|
||||||
|
CurrentWave = KFGIS.WaveNum;
|
||||||
|
|
||||||
|
if (!KFGIS.MyKFGRI.IsBossWave())
|
||||||
|
{
|
||||||
|
WaveTotalAIDef = KFGIS.SpawnManager.WaveTotalAI;
|
||||||
|
KFGIS.SpawnManager.WaveTotalAI *= CfgSpawn.default.ZedMultiplier;
|
||||||
|
WaveTotalAI = KFGIS.SpawnManager.WaveTotalAI;
|
||||||
|
KFGIS.MyKFGRI.WaveTotalAICount = KFGIS.SpawnManager.WaveTotalAI;
|
||||||
|
KFGIS.MyKFGRI.AIRemaining = KFGIS.SpawnManager.WaveTotalAI;
|
||||||
|
|
||||||
|
if (WaveTotalAIDef != KFGIS.SpawnManager.WaveTotalAI)
|
||||||
|
{
|
||||||
|
`ZS_Info("increase WaveTotalAI from" @ WaveTotalAIDef @ "to" @ WaveTotalAI @ "due to ZedMultiplier" @ "(" $ CfgSpawn.default.ZedMultiplier $ ")", LogLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CfgSpawn.default.bCyclicalSpawn && KFGIS.WaveNum > 1 && KFGIS.WaveNum == CycleWaveShift + CycleWaveSize * CurrentCycle)
|
||||||
|
{
|
||||||
|
CurrentCycle++;
|
||||||
|
`ZS_Info("Next spawn cycle started:" @ CurrentCycle, LogLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetSpawnList(SpawnList);
|
||||||
|
ResetSpawnList(SpawnListSW);
|
||||||
|
ResetSpawnList(SpawnListBW);
|
||||||
|
|
||||||
|
CurrentBossClass = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!KFGIS.IsWaveActive())
|
||||||
|
{
|
||||||
|
SpawnTimerLogger(true, "wave is not active");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CfgSpawn.default.AliveSpawnLimit > 0 && KFGIS.AIAliveCount >= CfgSpawn.default.AliveSpawnLimit)
|
||||||
|
{
|
||||||
|
SpawnTimerLogger(true, "alive spawn limit reached");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!KFGIS.MyKFGRI.IsBossWave() && CfgSpawn.default.bShadowSpawn && KFGIS.MyKFGRI.AIRemaining <= KFGIS.AIAliveCount)
|
||||||
|
{
|
||||||
|
SpawnTimerLogger(true, "shadow spawn is active and no free spawn slots");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnTimerLogger(false);
|
||||||
|
|
||||||
|
SpecialWave = INDEX_NONE;
|
||||||
|
if (KFGIE != None)
|
||||||
|
{
|
||||||
|
SpecialWave = KFGameReplicationInfo_Endless(KFGIE.GameReplicationInfo).CurrentSpecialMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((SpecialWave == INDEX_NONE && !KFGIS.MyKFGRI.IsBossWave())
|
||||||
|
|| (SpecialWave != INDEX_NONE && !CfgSpawnListSW.default.bStopRegularSpawn)
|
||||||
|
|| (KFGIS.MyKFGRI.IsBossWave() && !CfgSpawnListBW.default.bStopRegularSpawn))
|
||||||
|
SpawnRegularWaveZeds();
|
||||||
|
|
||||||
|
if (SpecialWave != INDEX_NONE)
|
||||||
|
SpawnSpecialWaveZeds(SpecialWave);
|
||||||
|
|
||||||
|
if (KFGIS.MyKFGRI.IsBossWave())
|
||||||
|
SpawnBossWaveZeds();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ResetSpawnList(out Array<S_SpawnEntry> List)
|
||||||
|
{
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
local int i;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
foreach List(SpawnEntry, i)
|
||||||
|
{
|
||||||
|
List[i].SpawnsDone = 0;
|
||||||
|
|
||||||
|
if (KFGIS.MyKFGRI.IsBossWave())
|
||||||
|
{
|
||||||
|
List[i].RelativeDelay = 0.f;
|
||||||
|
List[i].Delay = SpawnEntry.DelayDefault;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List[i].RelativeDelay = SpawnEntry.RelativeDelayDefault;
|
||||||
|
if (SpawnEntry.RelativeDelay == 0.f)
|
||||||
|
List[i].Delay = SpawnEntry.DelayDefault;
|
||||||
|
else
|
||||||
|
List[i].Delay = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function SpawnTimerLogger(bool Stop, optional String Reason)
|
||||||
|
{
|
||||||
|
local String Message;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
if (Stop)
|
||||||
|
Message = "Stop spawn";
|
||||||
|
else
|
||||||
|
Message = "Start spawn";
|
||||||
|
|
||||||
|
if (Reason != "")
|
||||||
|
Message @= "(" $ Reason $ ")";
|
||||||
|
|
||||||
|
if (Message != SpawnTimerLastMessage)
|
||||||
|
{
|
||||||
|
`ZS_Info(Message, LogLevel);
|
||||||
|
SpawnTimerLastMessage = Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function SpawnRegularWaveZeds()
|
||||||
|
{
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
local int Spawned, SpawnsLeft, i;
|
||||||
|
local String Message;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
foreach SpawnList(SpawnEntry, i)
|
||||||
|
{
|
||||||
|
if (SpawnEntry.Wave == KFGIS.WaveNum - CycleWaveSize * (CurrentCycle - 1))
|
||||||
|
{
|
||||||
|
SpawnList[i].Delay -= dt;
|
||||||
|
if (TimeToSpawn(SpawnEntry))
|
||||||
|
{
|
||||||
|
SpawnList[i].Delay = SpawnEntry.DelayDefault;
|
||||||
|
if (FRand() <= SpawnEntry.Probability)
|
||||||
|
{
|
||||||
|
Spawned = SpawnZed(SpawnEntry.ZedClass, SpawnEntry.SpawnAtOnce, SpawnEntry.SpawnAtPlayerStart);
|
||||||
|
Message = "Spawned:" @ SpawnEntry.ZedClass @ "x" $ Spawned;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message = "Skip spawn" @ SpawnEntry.ZedClass @ "due to probability:" @ SpawnEntry.Probability * 100 $ "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnList[i].SpawnsDone++;
|
||||||
|
SpawnsLeft = SpawnEntry.MaxSpawns - SpawnList[i].SpawnsDone;
|
||||||
|
if (SpawnsLeft > 0)
|
||||||
|
{
|
||||||
|
Message @= "(Next spawn after" @ SpawnEntry.DelayDefault $ "sec," @ "spawns left:" @ SpawnsLeft $ ")";
|
||||||
|
}
|
||||||
|
`ZS_Info(Message, LogLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function SpawnSpecialWaveZeds(int SpecialWave)
|
||||||
|
{
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
local int Spawned, SpawnsLeft, i;
|
||||||
|
local String Message;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
foreach SpawnListSW(SpawnEntry, i)
|
||||||
|
{
|
||||||
|
if (SpawnEntry.Wave == SpecialWave)
|
||||||
|
{
|
||||||
|
SpawnListSW[i].Delay -= dt;
|
||||||
|
|
||||||
|
if (TimeToSpawn(SpawnEntry))
|
||||||
|
{
|
||||||
|
SpawnListSW[i].Delay = SpawnEntry.DelayDefault;
|
||||||
|
if (FRand() <= SpawnEntry.Probability)
|
||||||
|
{
|
||||||
|
Spawned = SpawnZed(SpawnEntry.ZedClass, SpawnEntry.SpawnAtOnce, SpawnEntry.SpawnAtPlayerStart);
|
||||||
|
Message = "Spawned:" @ SpawnEntry.ZedClass @ "x" $ Spawned;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message = "Skip spawn" @ SpawnEntry.ZedClass @ "due to probability:" @ SpawnEntry.Probability * 100 $ "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnListSW[i].SpawnsDone++;
|
||||||
|
SpawnsLeft = SpawnEntry.MaxSpawns - SpawnListSW[i].SpawnsDone;
|
||||||
|
if (SpawnsLeft > 0)
|
||||||
|
{
|
||||||
|
Message @= "(Next spawn after" @ SpawnEntry.DelayDefault $ "sec," @ "spawns left:" @ SpawnsLeft $ ")";
|
||||||
|
}
|
||||||
|
`ZS_Info(Message, LogLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function SpawnBossWaveZeds()
|
||||||
|
{
|
||||||
|
local S_SpawnEntry SpawnEntry;
|
||||||
|
local KFPawn_Monster KFPM;
|
||||||
|
local int Spawned, SpawnsLeft, i;
|
||||||
|
local String Message;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
if (CurrentBossClass == None)
|
||||||
|
{
|
||||||
|
foreach WorldInfo.AllPawns(class'KFPawn_Monster', KFPM)
|
||||||
|
{
|
||||||
|
i = BossClassCache.Find(KFPM.class);
|
||||||
|
if (i != INDEX_NONE)
|
||||||
|
{
|
||||||
|
CurrentBossClass = BossClassCache[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrentBossClass == None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach SpawnListBW(SpawnEntry, i)
|
||||||
|
{
|
||||||
|
if (SpawnEntry.BossClass == CurrentBossClass)
|
||||||
|
{
|
||||||
|
SpawnListBW[i].Delay -= dt;
|
||||||
|
if (TimeToSpawn(SpawnEntry))
|
||||||
|
{
|
||||||
|
SpawnListBW[i].Delay = SpawnEntry.DelayDefault;
|
||||||
|
if (FRand() <= SpawnEntry.Probability)
|
||||||
|
{
|
||||||
|
Spawned = SpawnZed(SpawnEntry.ZedClass, SpawnEntry.SpawnAtOnce, SpawnEntry.SpawnAtPlayerStart);
|
||||||
|
Message = "Spawned:" @ SpawnEntry.ZedClass @ "x" $ Spawned;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Message = "Skip spawn" @ SpawnEntry.ZedClass @ "due to probability:" @ SpawnEntry.Probability * 100 $ "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnListBW[i].SpawnsDone++;
|
||||||
|
SpawnsLeft = SpawnEntry.MaxSpawns - SpawnListBW[i].SpawnsDone;
|
||||||
|
if (SpawnsLeft > 0)
|
||||||
|
{
|
||||||
|
Message @= "(Next spawn after" @ SpawnEntry.DelayDefault $ "sec," @ "spawns left:" @ SpawnsLeft $ ")";
|
||||||
|
}
|
||||||
|
`ZS_Info(Message, LogLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function bool TimeToSpawn(S_SpawnEntry SpawnEntry)
|
||||||
|
{
|
||||||
|
local bool DelayReady, DelayRelativeReady;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
DelayReady = SpawnEntry.Delay <= 0 && SpawnEntry.MaxSpawns >= 0 && SpawnEntry.SpawnsDone < SpawnEntry.MaxSpawns;
|
||||||
|
|
||||||
|
if (SpawnEntry.RelativeDelay == 0.f)
|
||||||
|
{
|
||||||
|
DelayRelativeReady = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DelayRelativeReady = (SpawnEntry.RelativeDelay <= 1.0f - (float(KFGIS.MyKFGRI.AIRemaining) / float(WaveTotalAI)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return DelayReady && DelayRelativeReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function int PlayerCount()
|
||||||
|
{
|
||||||
|
local PlayerController PC;
|
||||||
|
local int HumanPlayers;
|
||||||
|
local KFOnlineGameSettings KFGameSettings;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
if (KFGIS.PlayfabInter != None && KFGIS.PlayfabInter.GetGameSettings() != None)
|
||||||
|
{
|
||||||
|
KFGameSettings = KFOnlineGameSettings(KFGIS.PlayfabInter.GetGameSettings());
|
||||||
|
HumanPlayers = KFGameSettings.NumPublicConnections - KFGameSettings.NumOpenPublicConnections;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HumanPlayers = 0;
|
||||||
|
foreach WorldInfo.AllControllers(class'PlayerController', PC)
|
||||||
|
if (PC.bIsPlayer
|
||||||
|
&& PC.PlayerReplicationInfo != None
|
||||||
|
&& !PC.PlayerReplicationInfo.bOnlySpectator
|
||||||
|
&& !PC.PlayerReplicationInfo.bBot)
|
||||||
|
HumanPlayers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HumanPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function Vector PlayerStartLocation()
|
||||||
|
{
|
||||||
|
local PlayerController PC;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
foreach WorldInfo.AllControllers(class'PlayerController', PC)
|
||||||
|
return KFGIS.FindPlayerStart(PC, 0).Location;
|
||||||
|
|
||||||
|
return KFGIS.FindPlayerStart(None, 0).Location;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnAtOnce, bool SpawnAtPlayerStart)
|
||||||
|
{
|
||||||
|
local Array<class<KFPawn_Monster> > CustomSquad;
|
||||||
|
local Vector SpawnLocation;
|
||||||
|
local KFPawn_Monster KFPM;
|
||||||
|
local Controller C;
|
||||||
|
local int ModdedSpawnCount;
|
||||||
|
local int FreeSpawnSlots;
|
||||||
|
local int SpawnFailed;
|
||||||
|
local int i;
|
||||||
|
|
||||||
|
`ZS_Trace(`Location, LogLevel);
|
||||||
|
|
||||||
|
ModdedSpawnCount = Round(float(SpawnAtOnce) * (1.0f + float(CurrentCycle - 1) * CfgSpawn.default.CycleMultiplier + float(PlayerCount() - 1) * CfgSpawn.default.PlayerMultiplier));
|
||||||
|
|
||||||
|
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
||||||
|
{
|
||||||
|
FreeSpawnSlots = KFGIS.MyKFGRI.AIRemaining - KFGIS.AIAliveCount;
|
||||||
|
if (ModdedSpawnCount > FreeSpawnSlots)
|
||||||
|
{
|
||||||
|
`ZS_Info("Not enough free slots to spawn, will spawn" @ FreeSpawnSlots @ "instead of" @ ModdedSpawnCount, LogLevel);
|
||||||
|
ModdedSpawnCount = FreeSpawnSlots;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ModdedSpawnCount; i++)
|
||||||
|
CustomSquad.AddItem(ZedClass);
|
||||||
|
|
||||||
|
if (SpawnAtPlayerStart)
|
||||||
|
{
|
||||||
|
SpawnLocation = PlayerStartLocation();
|
||||||
|
SpawnLocation.Y += 64;
|
||||||
|
SpawnLocation.Z += 64;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpawnLocation = KFGIS.SpawnManager.GetBestSpawnVolume(CustomSquad).Location;
|
||||||
|
SpawnLocation.Z += 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnFailed = 0;
|
||||||
|
for (i = 0; i < ModdedSpawnCount; i++)
|
||||||
|
{
|
||||||
|
KFPM = Spawn(ZedClass,,, SpawnLocation, rot(0,0,1),, true);
|
||||||
|
if (KFPM == None)
|
||||||
|
{
|
||||||
|
`ZS_Error("Can't spawn" @ ZedClass, LogLevel);
|
||||||
|
SpawnFailed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
C = KFPM.Spawn(KFPM.ControllerClass);
|
||||||
|
if (C == None)
|
||||||
|
{
|
||||||
|
`ZS_Error("Can't spawn controller for" @ ZedClass $ ". Destroy this KFPawn...", LogLevel);
|
||||||
|
KFPM.Destroy();
|
||||||
|
SpawnFailed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
C.Possess(KFPM, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
||||||
|
{
|
||||||
|
KFGIS.MyKFGRI.AIRemaining -= (ModdedSpawnCount - SpawnFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
KFGIS.RefreshMonsterAliveCount();
|
||||||
|
|
||||||
|
return ModdedSpawnCount - SpawnFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function PrintSpawnEntry(S_SpawnEntry SE)
|
||||||
|
{
|
||||||
|
`ZS_Debug("BossClass:" @ SE.BossClass, LogLevel);
|
||||||
|
`ZS_Debug("ZedClass:" @ SE.ZedClass, LogLevel);
|
||||||
|
`ZS_Debug("Wave:" @ SE.Wave, LogLevel);
|
||||||
|
`ZS_Debug("SpawnAtOnce:" @ SE.SpawnAtOnce, LogLevel);
|
||||||
|
`ZS_Debug("Probability:" @ SE.Probability, LogLevel);
|
||||||
|
`ZS_Debug("RelativeDelay:" @ SE.RelativeDelay @ "(" $ SE.RelativeDelayDefault $ ")", LogLevel);
|
||||||
|
`ZS_Debug("Delay:" @ SE.Delay @ "(" $ SE.DelayDefault $ ")", LogLevel);
|
||||||
|
`ZS_Debug("SpawnsDone:" @ SE.SpawnsDone @ "of" @ SE.MaxSpawns, LogLevel);
|
||||||
|
`ZS_Debug("SpawnAtPlayerStart:" @ SE.SpawnAtPlayerStart, LogLevel);
|
||||||
|
`ZS_Debug("---------------------", LogLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultProperties
|
||||||
|
{
|
||||||
|
dt = 1
|
||||||
|
|
||||||
|
CfgSpawn = class'Spawn'
|
||||||
|
CfgSpawnList = class'SpawnList'
|
||||||
|
CfgSpawnListBW = class'SpawnListBossWaves'
|
||||||
|
CfgSpawnListSW = class'SpawnListSpecialWaves'
|
||||||
|
}
|
44
ZedSpawner/Classes/ZedSpawnerMut.uc
Normal file
44
ZedSpawner/Classes/ZedSpawnerMut.uc
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
class ZedSpawnerMut extends KFMutator
|
||||||
|
dependson(ZedSpawner);
|
||||||
|
|
||||||
|
var private ZedSpawner ZS;
|
||||||
|
|
||||||
|
event PreBeginPlay()
|
||||||
|
{
|
||||||
|
Super.PreBeginPlay();
|
||||||
|
|
||||||
|
if (WorldInfo.NetMode == NM_Client) return;
|
||||||
|
|
||||||
|
foreach WorldInfo.DynamicActors(class'ZedSpawner', ZS)
|
||||||
|
{
|
||||||
|
`ZS_Log("Found 'ZedSpawner'");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ZS == None)
|
||||||
|
{
|
||||||
|
`ZS_Log("Spawn 'ZedSpawner'");
|
||||||
|
ZS = WorldInfo.Spawn(class'ZedSpawner');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ZS == None)
|
||||||
|
{
|
||||||
|
`ZS_Log("Can't Spawn 'ZedSpawner', Destroy...");
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AddMutator(Mutator Mut)
|
||||||
|
{
|
||||||
|
if (Mut == Self) return;
|
||||||
|
|
||||||
|
if (Mut.Class == Class)
|
||||||
|
Mut.Destroy();
|
||||||
|
else
|
||||||
|
Super.AddMutator(Mut);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultProperties
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
10
ZedSpawner/Globals.uci
Normal file
10
ZedSpawner/Globals.uci
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
`define ZS_Tag 'ZedSpawner'
|
||||||
|
|
||||||
|
`define ZS_Log(msg, cond) `log(`msg `if(`cond), `cond`{endif}, `ZS_Tag)
|
||||||
|
|
||||||
|
`define ZS_Fatal(msg, level) `log("FATAL:" @ `msg, (`level >= LL_Fatal), `ZS_Tag)
|
||||||
|
`define ZS_Error(msg, level) `log("ERROR:" @ `msg, (`level >= LL_Error), `ZS_Tag)
|
||||||
|
`define ZS_Warn(msg, level) `log("WARN:" @ `msg, (`level >= LL_Warning), `ZS_Tag)
|
||||||
|
`define ZS_Info(msg, level) `log("INFO:" @ `msg, (`level >= LL_Info), `ZS_Tag)
|
||||||
|
`define ZS_Debug(msg, level) `log("DEBUG:" @ `msg, (`level >= LL_Debug), `ZS_Tag)
|
||||||
|
`define ZS_Trace(msg, level) `log("TRACE:" @ `msg, (`level >= LL_Trace), `ZS_Tag)
|
@ -1,310 +0,0 @@
|
|||||||
class ZedVarient extends KFMutator
|
|
||||||
config(ZedVarient);
|
|
||||||
|
|
||||||
//Timer rate
|
|
||||||
var const float dt;
|
|
||||||
|
|
||||||
//User out cfg
|
|
||||||
struct TZedCfg
|
|
||||||
{
|
|
||||||
var int Wave;
|
|
||||||
var int SpawnAtOnce;
|
|
||||||
var int SpawnsDone;
|
|
||||||
var int MaxSpawns;
|
|
||||||
var int Spawnsleft;
|
|
||||||
var string Zed;
|
|
||||||
var float Probability;
|
|
||||||
var float Delay;
|
|
||||||
};
|
|
||||||
|
|
||||||
//Mut inner cfg
|
|
||||||
struct TZedCfgTmp
|
|
||||||
{
|
|
||||||
var int Wave;
|
|
||||||
var int SpawnAtOnce;
|
|
||||||
var class<KFPawn_Monster> Zed;
|
|
||||||
var float Probability;
|
|
||||||
var float DefDelay;
|
|
||||||
var float Delay;
|
|
||||||
var int SpawnsDone;
|
|
||||||
var int MaxSpawns;
|
|
||||||
var int SpawnsLeft;
|
|
||||||
};
|
|
||||||
|
|
||||||
var config float ZedMultiplier;
|
|
||||||
var config bool bConfigsInit;
|
|
||||||
var config array<TZedCfg> CustomZeds;
|
|
||||||
var config array<string> Bosses;
|
|
||||||
|
|
||||||
var array< class<KFPawn_Monster> > LoadedBosses;
|
|
||||||
var array< TZedCfgTmp > LoadedCustomZeds;
|
|
||||||
var KFGameInfo_Survival KFGT;
|
|
||||||
var int cwave;
|
|
||||||
var array<KFPawn_Monster> UZBosses;
|
|
||||||
var bool bFB;
|
|
||||||
var KFPawn_Monster OriginalBoss;
|
|
||||||
var int BossesLeft;
|
|
||||||
//Dunno how to properly calculate using original stuff
|
|
||||||
var int NeedMoreZeds;
|
|
||||||
var int MaxSpawns;
|
|
||||||
var int SpawnsLeft;
|
|
||||||
var int SpawnsDone;
|
|
||||||
|
|
||||||
function PostBeginPlay()
|
|
||||||
{
|
|
||||||
local int i;
|
|
||||||
local class<KFPawn_Monster> C;
|
|
||||||
local TZedCfgTmp zct;
|
|
||||||
|
|
||||||
for(i=0;i<Bosses.Length;i++)
|
|
||||||
{
|
|
||||||
C = class<KFPawn_Monster>(DynamicLoadObject(Bosses[i],Class'Class'));
|
|
||||||
|
|
||||||
if(C!=None)
|
|
||||||
LoadedBosses.AddItem(C);
|
|
||||||
else
|
|
||||||
LogInternal("Error while loading"@Bosses[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!bConfigsInit)
|
|
||||||
{
|
|
||||||
bConfigsInit = true;
|
|
||||||
ZedMultiplier = 1.0;
|
|
||||||
SaveConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
cwave=-1;
|
|
||||||
KFGT = KFGameInfo_Survival(WorldInfo.Game);
|
|
||||||
|
|
||||||
//Init inner cfg
|
|
||||||
for(i=0;i<CustomZeds.Length;i++)
|
|
||||||
{
|
|
||||||
C = class<KFPawn_Monster>(DynamicLoadObject(CustomZeds[i].Zed,Class'Class'));
|
|
||||||
|
|
||||||
if(C!=None && CustomZeds[i].Wave>0 && CustomZeds[i].SpawnAtOnce>0 && CustomZeds[i].Probability>0 && CustomZeds[i].Delay>0 && CustomZeds[i].MaxSpawns>0)
|
|
||||||
{
|
|
||||||
LogInternal("LOADED"@CustomZeds[i].Zed);
|
|
||||||
zct.Wave = CustomZeds[i].Wave;
|
|
||||||
zct.SpawnAtOnce = CustomZeds[i].SpawnAtOnce;
|
|
||||||
zct.Probability = CustomZeds[i].Probability;
|
|
||||||
zct.DefDelay = CustomZeds[i].Delay;
|
|
||||||
zct.Delay = CustomZeds[i].Delay;
|
|
||||||
zct.MaxSpawns = CustomZeds[i].MaxSpawns;
|
|
||||||
zct.SpawnsLeft = CustomZeds[i].MaxSpawns - CustomZeds[i].SpawnsDone;
|
|
||||||
zct.Zed = C;
|
|
||||||
LoadedCustomZeds.AddItem(zct);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogInternal("Error while loading"@CustomZeds[i].Zed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetTimer(dt,true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Timer()
|
|
||||||
{
|
|
||||||
//setup total amount multiplier
|
|
||||||
if(cwave<KFGT.WaveNum && KFGT.WaveNum!=KFGT.WaveMax)
|
|
||||||
{
|
|
||||||
cwave=KFGT.WaveNum;
|
|
||||||
KFGT.SpawnManager.WaveTotalAI*=ZedMultiplier;
|
|
||||||
KFGT.MyKFGRI.WaveTotalAICount = KFGT.SpawnManager.WaveTotalAI;
|
|
||||||
KFGT.MyKFGRI.AIRemaining = KFGT.SpawnManager.WaveTotalAI;
|
|
||||||
NeedMoreZeds=KFGT.SpawnManager.WaveTotalAI;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpawnCustomZeds();
|
|
||||||
KFGT.RefreshMonsterAliveCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
function SpawnCustomZeds()
|
|
||||||
{
|
|
||||||
local int i, j;
|
|
||||||
local array< class<KFPawn_Monster> > CSquad;
|
|
||||||
local KFSpawnVolume KFSV;
|
|
||||||
|
|
||||||
KFSV = KFGT.SpawnManager.GetBestSpawnVolume(CSquad);
|
|
||||||
|
|
||||||
if( !KFGT.IsWaveActive() || NeedMoreZeds<=0 || KFGT.AIAliveCount>128 || KFSV.Location==PlayerController(Owner).StartSpot.Location )
|
|
||||||
return; // Maxmonsters, ????, ?? ???? ?? //VSize(KFSV.Location-PlayerController(Owner).Pawn.Location)<650.f //KFSV.bNoCollisionFailForSpawn==true
|
|
||||||
|
|
||||||
for(i=0;i<LoadedCustomZeds.Length;i++)
|
|
||||||
{
|
|
||||||
if(LoadedCustomZeds[i].Wave==KFGT.WaveNum)
|
|
||||||
{
|
|
||||||
LoadedCustomZeds[i].Delay-=dt;
|
|
||||||
|
|
||||||
if(LoadedCustomZeds[i].Delay<=0 && LoadedCustomZeds[i].SpawnsLeft>0 && LoadedCustomZeds[i].MaxSpawns>=0 && (LoadedCustomZeds[i].SpawnsDone<LoadedCustomZeds[i].MaxSpawns) )
|
|
||||||
{
|
|
||||||
LoadedCustomZeds[i].Delay=LoadedCustomZeds[i].DefDelay;
|
|
||||||
|
|
||||||
if(FRand()<=LoadedCustomZeds[i].Probability)
|
|
||||||
{
|
|
||||||
CSquad.Length=0;
|
|
||||||
CSquad.AddItem(LoadedCustomZeds[i].Zed);
|
|
||||||
|
|
||||||
for(j=0;j<LoadedCustomZeds[i].SpawnAtOnce;j++)
|
|
||||||
{
|
|
||||||
TryToSpawnZed(KFSV.Location,LoadedCustomZeds[i].Zed);
|
|
||||||
LoadedCustomZeds[i].SpawnsDone++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function TryToSpawnZed( vector L, class<KFPawn_Monster> ZedClass )
|
|
||||||
{
|
|
||||||
local KFPawn_Monster M;
|
|
||||||
local Controller C;
|
|
||||||
|
|
||||||
if( ZedClass==class'HL2Monsters.Combine_Strider' || ZedClass==class'HL2Monsters.Combine_Gunship' || ZedClass==class'HL2Monsters.Hunter_Chopper' )
|
|
||||||
{
|
|
||||||
L = KFGameInfo(WorldInfo.Game).FindPlayerStart(PlayerController(Owner),0).Location;
|
|
||||||
L.Y += 64;
|
|
||||||
L.Z += 64;
|
|
||||||
}
|
|
||||||
else L.Z += 10;
|
|
||||||
|
|
||||||
M = Spawn(ZedClass,,,L,rot(0,0,1),,true);
|
|
||||||
|
|
||||||
if( M==None )
|
|
||||||
return;
|
|
||||||
|
|
||||||
C = M.Spawn(M.ControllerClass);
|
|
||||||
C.Possess(M,false);
|
|
||||||
KFGT.MyKFGRI.AIRemaining+=1; //added
|
|
||||||
KFGT.NumAISpawnsQueued++;
|
|
||||||
KFGT.AIAliveCount++;
|
|
||||||
KFGT.RefreshMonsterAliveCount();
|
|
||||||
NeedMoreZeds--;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Kill original boss
|
|
||||||
//function KillOriginalBoss()
|
|
||||||
//{
|
|
||||||
// if(OriginalBoss!=None)
|
|
||||||
// {
|
|
||||||
// OriginalBoss.Suicide();
|
|
||||||
// SetTimer(1, false, 'ResetCamera');
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//Rollback camera mode
|
|
||||||
function ResetCamera()
|
|
||||||
{
|
|
||||||
local KFPlayerController PC;
|
|
||||||
|
|
||||||
foreach WorldInfo.AllControllers( class'KFPlayerController', PC )
|
|
||||||
{
|
|
||||||
PC.ServerCamera( 'ThirdPerson' );
|
|
||||||
PC.ServerCamera( 'FirstPerson' );
|
|
||||||
PC.HideBossNameplate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Rollback wave number
|
|
||||||
function ReturnWaveNum()
|
|
||||||
{
|
|
||||||
KFGT.MyKFGRI.WaveNum++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Prevent end game on any but last boss kill
|
|
||||||
function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> damageType, vector HitLocation)
|
|
||||||
{
|
|
||||||
if(KFGT.WaveNum==KFGT.WaveMax)
|
|
||||||
{
|
|
||||||
if(UZBosses.Find(KFPawn_Monster(Killed))>=0)
|
|
||||||
{
|
|
||||||
BossesLeft--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(KFPawn_MonsterBoss(Killed)!=None && BossesLeft>0)
|
|
||||||
{
|
|
||||||
KFGT.MyKFGRI.WaveNum--;
|
|
||||||
SetTimer(0.2, false, 'ReturnWaveNum');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (NextMutator != None && NextMutator.PreventDeath(Killed, Killer, damageType, HitLocation));
|
|
||||||
}
|
|
||||||
|
|
||||||
function AddMutator(Mutator M)
|
|
||||||
{
|
|
||||||
if( M!=Self )
|
|
||||||
{
|
|
||||||
if( M.Class==Class )
|
|
||||||
M.Destroy();
|
|
||||||
else Super.AddMutator(M);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Spawn bosses by original boss
|
|
||||||
//function bool CheckReplacement(Actor Other)
|
|
||||||
//{
|
|
||||||
// local int i, j;
|
|
||||||
//
|
|
||||||
// if(KFPawn_Monster(Other)!=None)
|
|
||||||
// NeedMoreZeds--;
|
|
||||||
//
|
|
||||||
// if(KFPawn_MonsterBoss(Other)!=None && !bFB && KFGT.WaveNum==KFGT.WaveMax)
|
|
||||||
// {
|
|
||||||
// bFB=true;
|
|
||||||
// OriginalBoss = KFPawn_Monster(Other);
|
|
||||||
// SetTimer(1,false,'KillOriginalBoss');
|
|
||||||
//
|
|
||||||
// for( i=0; i<LoadedBosses.Length;i++)
|
|
||||||
// {
|
|
||||||
// // 10 tries for each zed
|
|
||||||
// for( j=0; j<10; j++ )
|
|
||||||
// if( TryToSpawnBoss(Pawn(Other), LoadedBosses[i]) )
|
|
||||||
// {
|
|
||||||
// KFGameInfo_Survival(WorldInfo.Game).MyKFGRI.AIRemaining+=1;
|
|
||||||
// `log("Spawn succeded for"@Bosses[i]);
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//Spawn routine
|
|
||||||
function bool TryToSpawnBoss( Pawn A, class<KFPawn_Monster> MC )
|
|
||||||
{
|
|
||||||
local vector V;
|
|
||||||
local vector E,HL,HN;
|
|
||||||
local KFPawn_Monster M;
|
|
||||||
local Controller C;
|
|
||||||
|
|
||||||
E.X = A.GetCollisionRadius()*0.8;
|
|
||||||
E.Y = E.X;
|
|
||||||
E.Z = A.GetCollisionHeight()*0.8;
|
|
||||||
V=A.Location;
|
|
||||||
V.Z+=32; //32
|
|
||||||
|
|
||||||
if(FRand()>0.5)
|
|
||||||
V.X+= FRand()>0.5 ? 100 : -100;
|
|
||||||
else
|
|
||||||
V.Y+= FRand()>0.5 ? 100 : -100;
|
|
||||||
|
|
||||||
if( A.Trace(HL,HN,V,A.Location,false,E)!=None )
|
|
||||||
V = HL;
|
|
||||||
|
|
||||||
M = A.Spawn(MC,,,V,A.Rotation,,true);
|
|
||||||
if( M==None )
|
|
||||||
return false;
|
|
||||||
C = M.Spawn(M.ControllerClass);
|
|
||||||
C.Possess(M,false);
|
|
||||||
|
|
||||||
BossesLeft++;
|
|
||||||
UZBosses.AddItem(M);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultProperties
|
|
||||||
{
|
|
||||||
dt=1.0f
|
|
||||||
}
|
|
52
builder.cfg
Normal file
52
builder.cfg
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
### Build parameters ###
|
||||||
|
|
||||||
|
# If True - compresses the mutator when compiling
|
||||||
|
# Scripts will be stored in binary form
|
||||||
|
# (reduces the size of the output file)
|
||||||
|
StripSource="True"
|
||||||
|
|
||||||
|
# Mutators to be compiled
|
||||||
|
# Specify them with a space as a separator,
|
||||||
|
# Mutators will be compiled in the specified order
|
||||||
|
PackageBuildOrder="ZedSpawner"
|
||||||
|
|
||||||
|
|
||||||
|
### Steam Workshop upload parameters ###
|
||||||
|
|
||||||
|
# Mutators that will be uploaded to the workshop
|
||||||
|
# Specify them with a space as a separator,
|
||||||
|
# The order doesn't matter
|
||||||
|
PackageUpload="ZedSpawner"
|
||||||
|
|
||||||
|
|
||||||
|
### Test parameters ###
|
||||||
|
|
||||||
|
# Map:
|
||||||
|
Map="KF-Nuked"
|
||||||
|
|
||||||
|
# Game:
|
||||||
|
# Survival: KFGameContent.KFGameInfo_Survival
|
||||||
|
# WeeklyOutbreak: KFGameContent.KFGameInfo_WeeklySurvival
|
||||||
|
# Endless: KFGameContent.KFGameInfo_Endless
|
||||||
|
# Objective: KFGameContent.KFGameInfo_Objective
|
||||||
|
# Versus: KFGameContent.KFGameInfo_VersusSurvival
|
||||||
|
Game="KFGameContent.KFGameInfo_Survival"
|
||||||
|
|
||||||
|
# Difficulty:
|
||||||
|
# Normal: 0
|
||||||
|
# Hard: 1
|
||||||
|
# Suicide: 2
|
||||||
|
# Hell: 3
|
||||||
|
Difficulty="0"
|
||||||
|
|
||||||
|
# GameLength:
|
||||||
|
# 4 waves: 0
|
||||||
|
# 7 waves: 1
|
||||||
|
# 10 waves: 2
|
||||||
|
GameLength="0"
|
||||||
|
|
||||||
|
# Mutators
|
||||||
|
Mutators="ZedSpawner.ZedSpawnerMut"
|
||||||
|
|
||||||
|
# Additional parameters
|
||||||
|
Args=""
|
1
tools
Submodule
1
tools
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 49fcaf67a29c5b478dc10f1f0ae08ed43017cd36
|
Loading…
x
Reference in New Issue
Block a user