Compare commits
7 Commits
SpawnManag
...
v1.0.2
Author | SHA1 | Date | |
---|---|---|---|
b5355d9fb5 | |||
62c3f79c5e | |||
d466d1fc79 | |||
f0b10fb158 | |||
b0727dd677 | |||
25b3e85009 | |||
41f186ee15 |
7
PublicationContent/description.txt
Normal file
7
PublicationContent/description.txt
Normal file
@ -0,0 +1,7 @@
|
||||
[h1]ZedSpawner[/h1]
|
||||
|
||||
[h1]Description[/h1]
|
||||
Work In Progress...
|
||||
|
||||
[h1]Sources[/h1]
|
||||
[url=https://github.com/GenZmeY/KF2-ZedSpawner]https://github.com/GenZmeY/KF2-ZedSpawner[/url]
|
BIN
PublicationContent/preview.png
Normal file
BIN
PublicationContent/preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 418 KiB |
BIN
PublicationContent/preview.psd
Normal file
BIN
PublicationContent/preview.psd
Normal file
Binary file not shown.
1
PublicationContent/tags.txt
Normal file
1
PublicationContent/tags.txt
Normal file
@ -0,0 +1 @@
|
||||
Mutators
|
1
PublicationContent/title.txt
Normal file
1
PublicationContent/title.txt
Normal file
@ -0,0 +1 @@
|
||||
ZedSpawner
|
@ -1,9 +1,40 @@
|
||||
class KFGI_Access extends Object
|
||||
within KFGameInfo_Survival;
|
||||
within KFGameInfo;
|
||||
|
||||
// Bypass protected modifier for these lists
|
||||
public function Array<class<KFPawn_Monster> > GetAIClassList()
|
||||
{
|
||||
local Array<class<KFPawn_Monster> > RV;
|
||||
local class<KFPawn_Monster> KFPMC;
|
||||
|
||||
foreach AIClassList(KFPMC)
|
||||
RV.AddItem(KFPMC);
|
||||
|
||||
return RV;
|
||||
}
|
||||
|
||||
function bool IsCustomZed(class<KFPawn_Monster> KFPM)
|
||||
public function Array<class<KFPawn_Monster> > GetNonSpawnAIClassList()
|
||||
{
|
||||
local Array<class<KFPawn_Monster> > RV;
|
||||
local class<KFPawn_Monster> KFPMC;
|
||||
|
||||
foreach NonSpawnAIClassList(KFPMC)
|
||||
RV.AddItem(KFPMC);
|
||||
|
||||
return RV;
|
||||
}
|
||||
|
||||
public function Array<class<KFPawn_Monster> > GetAIBossClassList()
|
||||
{
|
||||
local Array<class<KFPawn_Monster> > RV;
|
||||
local class<KFPawn_Monster> KFPMC;
|
||||
|
||||
foreach AIBossClassList(KFPMC)
|
||||
RV.AddItem(KFPMC);
|
||||
|
||||
return RV;
|
||||
}
|
||||
|
||||
public function bool IsCustomZed(class<KFPawn_Monster> KFPM)
|
||||
{
|
||||
if (AIClassList.Find(KFPM) != INDEX_NONE) return false;
|
||||
if (NonSpawnAIClassList.Find(KFPM) != INDEX_NONE) return false;
|
||||
@ -11,6 +42,50 @@ function bool IsCustomZed(class<KFPawn_Monster> KFPM)
|
||||
return true;
|
||||
}
|
||||
|
||||
public function bool IsOriginalAI(class<KFPawn_Monster> KFPM, optional out EAIType AIType)
|
||||
{
|
||||
local int Type;
|
||||
|
||||
Type = AIClassList.Find(KFPM);
|
||||
if (Type != INDEX_NONE)
|
||||
{
|
||||
AIType = EAIType(Type);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function bool IsOriginalAIBoss(class<KFPawn_Monster> KFPM, optional out EBossAIType AIType)
|
||||
{
|
||||
local int Type;
|
||||
|
||||
Type = AIBossClassList.Find(KFPM);
|
||||
if (Type != INDEX_NONE)
|
||||
{
|
||||
AIType = EBossAIType(Type);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function class<KFPawn_Monster> AITypePawn(EAIType AIType)
|
||||
{
|
||||
if (AIType < AIClassList.Length)
|
||||
return AIClassList[AIType];
|
||||
else
|
||||
return None;
|
||||
}
|
||||
|
||||
public function class<KFPawn_Monster> BossAITypePawn(EBossAIType AIType)
|
||||
{
|
||||
if (AIType < AIBossClassList.Length)
|
||||
return AIBossClassList[AIType];
|
||||
else
|
||||
return None;
|
||||
}
|
||||
|
||||
defaultproperties
|
||||
{
|
||||
|
||||
|
@ -2,31 +2,43 @@ class Spawn extends Object
|
||||
dependson(ZedSpawner)
|
||||
config(ZedSpawner);
|
||||
|
||||
var config bool bCyclicalSpawn;
|
||||
var config bool bShadowSpawn;
|
||||
var public config bool bCyclicalSpawn;
|
||||
var public config bool bShadowSpawn;
|
||||
var public config float ZedTotalMultiplier;
|
||||
var public config float SpawnTotalPlayerMultiplier;
|
||||
var public config float SpawnTotalCycleMultiplier;
|
||||
var public config float SingleSpawnLimitMultiplier;
|
||||
var public config float SingleSpawnLimitPlayerMultiplier;
|
||||
var public config float SingleSpawnLimitCycleMultiplier;
|
||||
var public config int AliveSpawnLimit;
|
||||
|
||||
var config float ZedTotalMultiplier;
|
||||
var config float SpawnTotalPlayerMultiplier;
|
||||
var config float SpawnTotalCycleMultiplier;
|
||||
|
||||
var config float SingleSpawnLimitMultiplier;
|
||||
var config float SingleSpawnLimitPlayerMultiplier;
|
||||
var config float SingleSpawnLimitCycleMultiplier;
|
||||
|
||||
var config int AliveSpawnLimit;
|
||||
|
||||
public static function InitConfig()
|
||||
public static function InitConfig(int Version, int LatestVersion)
|
||||
{
|
||||
default.bCyclicalSpawn = true;
|
||||
default.bShadowSpawn = true;
|
||||
default.ZedTotalMultiplier = 1.0;
|
||||
default.SpawnTotalPlayerMultiplier = 0.75;
|
||||
default.SpawnTotalCycleMultiplier = 0.75;
|
||||
default.SingleSpawnLimitPlayerMultiplier = 0.75;
|
||||
default.SingleSpawnLimitCycleMultiplier = 0.75;
|
||||
default.AliveSpawnLimit = 0;
|
||||
switch (Version)
|
||||
{
|
||||
case `NO_CONFIG:
|
||||
ApplyDefault();
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (LatestVersion != Version)
|
||||
{
|
||||
StaticSaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
StaticSaveConfig();
|
||||
private static function ApplyDefault()
|
||||
{
|
||||
default.bCyclicalSpawn = true;
|
||||
default.bShadowSpawn = true;
|
||||
default.ZedTotalMultiplier = 1.0;
|
||||
default.SpawnTotalPlayerMultiplier = 0.75;
|
||||
default.SpawnTotalCycleMultiplier = 0.75;
|
||||
default.SingleSpawnLimitMultiplier = 1.0;
|
||||
default.SingleSpawnLimitPlayerMultiplier = 0.75;
|
||||
default.SingleSpawnLimitCycleMultiplier = 0.75;
|
||||
default.AliveSpawnLimit = 0;
|
||||
}
|
||||
|
||||
public static function bool Load(E_LogLevel LogLevel)
|
||||
|
@ -13,27 +13,45 @@ struct S_SpawnEntryCfg
|
||||
var bool bSpawnAtPlayerStart;
|
||||
};
|
||||
|
||||
var config bool bStopRegularSpawn;
|
||||
var config Array<S_SpawnEntryCfg> Spawn;
|
||||
var public config bool bStopRegularSpawn;
|
||||
var private config Array<S_SpawnEntryCfg> Spawn;
|
||||
|
||||
public static function InitConfig()
|
||||
public static function InitConfig(int Version, int LatestVersion, KFGI_Access KFGIA)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case `NO_CONFIG:
|
||||
ApplyDefault(KFGIA);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (LatestVersion != Version)
|
||||
{
|
||||
StaticSaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private static function ApplyDefault(KFGI_Access KFGIA)
|
||||
{
|
||||
local S_SpawnEntryCfg SpawnEntry;
|
||||
local Array<class<KFPawn_Monster> > KFPM_Bosses;
|
||||
local class<KFPawn_Monster> KFPMC;
|
||||
|
||||
default.bStopRegularSpawn = true;
|
||||
|
||||
default.Spawn.Length = 0;
|
||||
|
||||
SpawnEntry.BossClass = "KFGameContent.KFPawn_ZedFleshpoundKing";
|
||||
SpawnEntry.ZedClass = "SomePackage.SomeFleshpoundClass";
|
||||
default.bStopRegularSpawn = true;
|
||||
default.Spawn.Length = 0;
|
||||
SpawnEntry.ZedClass = "SomePackage.SomeClass";
|
||||
SpawnEntry.SpawnCountBase = 2;
|
||||
SpawnEntry.SingleSpawnLimit = 1;
|
||||
SpawnEntry.Delay = 60;
|
||||
SpawnEntry.Delay = 30;
|
||||
SpawnEntry.Probability = 100;
|
||||
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||
default.Spawn.AddItem(SpawnEntry);
|
||||
|
||||
StaticSaveConfig();
|
||||
KFPM_Bosses = KFGIA.GetAIBossClassList();
|
||||
foreach KFPM_Bosses(KFPMC)
|
||||
{
|
||||
SpawnEntry.BossClass = "KFGameContent." $ String(KFPMC);
|
||||
default.Spawn.AddItem(SpawnEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
||||
|
@ -14,35 +14,47 @@ struct S_SpawnEntryCfg
|
||||
var bool bSpawnAtPlayerStart;
|
||||
};
|
||||
|
||||
var config Array<S_SpawnEntryCfg> Spawn;
|
||||
var public config Array<S_SpawnEntryCfg> Spawn;
|
||||
|
||||
public static function InitConfig()
|
||||
public static function InitConfig(int Version, int LatestVersion, KFGI_Access KFGIA)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case `NO_CONFIG:
|
||||
ApplyDefault(KFGIA);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (LatestVersion != Version)
|
||||
{
|
||||
StaticSaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private static function ApplyDefault(KFGI_Access KFGIA)
|
||||
{
|
||||
local S_SpawnEntryCfg SpawnEntry;
|
||||
local Array<class<KFPawn_Monster> > KFPM_Zeds;
|
||||
local class<KFPawn_Monster> KFPMC;
|
||||
|
||||
default.Spawn.Length = 0;
|
||||
|
||||
SpawnEntry.Wave = 1;
|
||||
SpawnEntry.ZedClass = "SomePackage.SomeZedClass1";
|
||||
SpawnEntry.SpawnCountBase = 2;
|
||||
SpawnEntry.SingleSpawnLimit = 1;
|
||||
SpawnEntry.RelativeStart = 0;
|
||||
SpawnEntry.Delay = 60;
|
||||
SpawnEntry.Probability = 100;
|
||||
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||
default.Spawn.AddItem(SpawnEntry);
|
||||
|
||||
SpawnEntry.Wave = 2;
|
||||
SpawnEntry.ZedClass = "SomePackage.SomeZedClass2";
|
||||
SpawnEntry.Wave = 0;
|
||||
SpawnEntry.SpawnCountBase = 2;
|
||||
SpawnEntry.SingleSpawnLimit = 1;
|
||||
SpawnEntry.RelativeStart = 25;
|
||||
SpawnEntry.Delay = 30;
|
||||
SpawnEntry.Probability = 50;
|
||||
SpawnEntry.Delay = 60;
|
||||
SpawnEntry.Probability = 100;
|
||||
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||
default.Spawn.AddItem(SpawnEntry);
|
||||
|
||||
StaticSaveConfig();
|
||||
KFPM_Zeds = KFGIA.GetAIClassList();
|
||||
foreach KFPM_Zeds(KFPMC)
|
||||
{
|
||||
++SpawnEntry.Wave;
|
||||
SpawnEntry.ZedClass = "KFGameContent." $ String(KFPMC);
|
||||
default.Spawn.AddItem(SpawnEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
||||
|
@ -14,18 +14,32 @@ struct S_SpawnEntryCfg
|
||||
var bool bSpawnAtPlayerStart;
|
||||
};
|
||||
|
||||
var config bool bStopRegularSpawn;
|
||||
var config Array<S_SpawnEntryCfg> Spawn;
|
||||
var public config bool bStopRegularSpawn;
|
||||
var private config Array<S_SpawnEntryCfg> Spawn;
|
||||
|
||||
public static function InitConfig()
|
||||
public static function InitConfig(int Version, int LatestVersion)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case `NO_CONFIG:
|
||||
ApplyDefault();
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (LatestVersion != Version)
|
||||
{
|
||||
StaticSaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private static function ApplyDefault()
|
||||
{
|
||||
local S_SpawnEntryCfg SpawnEntry;
|
||||
local EAIType AIType;
|
||||
|
||||
default.bStopRegularSpawn = true;
|
||||
|
||||
default.Spawn.Length = 0;
|
||||
|
||||
SpawnEntry.Wave = AT_Husk;
|
||||
SpawnEntry.ZedClass = "SomePackage.SomeClass";
|
||||
SpawnEntry.SpawnCountBase = 2;
|
||||
SpawnEntry.SingleSpawnLimit = 1;
|
||||
@ -33,9 +47,11 @@ public static function InitConfig()
|
||||
SpawnEntry.Delay = 60;
|
||||
SpawnEntry.Probability = 100;
|
||||
SpawnEntry.bSpawnAtPlayerStart = false;
|
||||
default.Spawn.AddItem(SpawnEntry);
|
||||
|
||||
StaticSaveConfig();
|
||||
foreach class'KFGameInfo_Endless'.default.SpecialWaveTypes(AIType)
|
||||
{
|
||||
SpawnEntry.Wave = AIType;
|
||||
default.Spawn.AddItem(SpawnEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public static function Array<S_SpawnEntry> Load(KFGameInfo_Endless KFGIE, E_LogLevel LogLevel)
|
||||
|
@ -1,10 +1,12 @@
|
||||
class ZedSpawner extends Info
|
||||
config(ZedSpawner);
|
||||
|
||||
const LatestVersion = 1;
|
||||
|
||||
const dt = 1;
|
||||
|
||||
const CfgSpawn = class'Spawn';
|
||||
const CfgSpawnListR = class'SpawnListRegular';
|
||||
const CfgSpawnListRW = class'SpawnListRegular';
|
||||
const CfgSpawnListBW = class'SpawnListBossWaves';
|
||||
const CfgSpawnListSW = class'SpawnListSpecialWaves';
|
||||
|
||||
@ -24,7 +26,7 @@ struct S_SpawnEntry
|
||||
{
|
||||
var class<KFPawn_Monster> BossClass;
|
||||
var class<KFPawn_Monster> ZedClass;
|
||||
var int Wave;
|
||||
var byte Wave;
|
||||
var int SpawnCountBase;
|
||||
var int SingleSpawnLimitDefault;
|
||||
var int SingleSpawnLimit;
|
||||
@ -38,126 +40,110 @@ struct S_SpawnEntry
|
||||
var bool SpawnAtPlayerStart;
|
||||
};
|
||||
|
||||
var config bool bConfigInitialized;
|
||||
var config E_LogLevel LogLevel;
|
||||
var private config int Version;
|
||||
var private config E_LogLevel LogLevel;
|
||||
|
||||
var private Array<S_SpawnEntry> SpawnListR;
|
||||
var private Array<S_SpawnEntry> SpawnListRW;
|
||||
var private Array<S_SpawnEntry> SpawnListBW;
|
||||
var private Array<S_SpawnEntry> SpawnListSW;
|
||||
var private Array<S_SpawnEntry> SpawnListCurrent;
|
||||
|
||||
var private bool NoFreeSpawnSlots;
|
||||
var private bool UseRegularSpawnList;
|
||||
var private bool UseBossSpawnList;
|
||||
var private bool UseSpecialSpawnList;
|
||||
|
||||
var private KFGameInfo_Survival KFGIS;
|
||||
var private KFGameInfo_Endless KFGIE;
|
||||
|
||||
var private KFGI_Access KFGIA;
|
||||
var private KFGI_Access KFGIA;
|
||||
|
||||
var private int CurrentWave;
|
||||
var private int SpecialWave;
|
||||
var private int CurrentCycle;
|
||||
var private int CycleWaveShift;
|
||||
var private int CycleWaveSize;
|
||||
|
||||
var private int WaveTotalAI;
|
||||
var private class<KFPawn_Monster> CurrentBossClass;
|
||||
var private int SpecialWave;
|
||||
|
||||
var private String SpawnTimerLastMessage;
|
||||
|
||||
var private class<KFPawn_Monster> CurrentBossClass;
|
||||
var private Array<class<KFPawn_Monster> > BossClassCache;
|
||||
var private Array<class<KFPawn_Monster> > CustomZeds;
|
||||
|
||||
var private String SpawnTimerLastMessage;
|
||||
var private String SpawnListsComment;
|
||||
|
||||
delegate bool WaveCondition(S_SpawnEntry SE);
|
||||
|
||||
public function bool WaveConditionRegular(S_SpawnEntry SE)
|
||||
public event PreBeginPlay()
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
return (SE.Wave == KFGIS.WaveNum - CycleWaveSize * (CurrentCycle - 1));
|
||||
}
|
||||
|
||||
public function bool WaveConditionBoss(S_SpawnEntry SE)
|
||||
{
|
||||
local KFPawn_Monster KFPM;
|
||||
local int Index;
|
||||
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
if (CurrentBossClass == None)
|
||||
{
|
||||
foreach WorldInfo.AllPawns(class'KFPawn_Monster', KFPM)
|
||||
{
|
||||
Index = BossClassCache.Find(KFPM.class);
|
||||
if (Index != INDEX_NONE)
|
||||
{
|
||||
CurrentBossClass = BossClassCache[Index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentBossClass == None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (SE.BossClass == CurrentBossClass);
|
||||
}
|
||||
|
||||
public function bool WaveConditionSpecial(S_SpawnEntry SE)
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
return (SE.Wave == SpecialWave);
|
||||
}
|
||||
|
||||
event PostBeginPlay()
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
Super.PostBeginPlay();
|
||||
|
||||
if (WorldInfo.NetMode == NM_Client)
|
||||
{
|
||||
`ZS_Fatal("NetMode == NM_Client, Destroy...");
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
Super.PreBeginPlay();
|
||||
}
|
||||
|
||||
public event PostBeginPlay()
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
if (bPendingDelete) return;
|
||||
|
||||
Super.PostBeginPlay();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
private function InitConfig()
|
||||
{
|
||||
if (Version == `NO_CONFIG)
|
||||
{
|
||||
LogLevel = LL_Info;
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
CfgSpawn.static.InitConfig(Version, LatestVersion);
|
||||
CfgSpawnListRW.static.InitConfig(Version, LatestVersion, KFGIA);
|
||||
CfgSpawnListBW.static.InitConfig(Version, LatestVersion, KFGIA);
|
||||
CfgSpawnListSW.static.InitConfig(Version, LatestVersion);
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case `NO_CONFIG:
|
||||
`ZS_Info("Config created");
|
||||
|
||||
case MaxInt:
|
||||
`ZS_Info("Config updated to version"@LatestVersion);
|
||||
break;
|
||||
|
||||
case LatestVersion:
|
||||
`ZS_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);
|
||||
break;
|
||||
}
|
||||
|
||||
if (LatestVersion != Version)
|
||||
{
|
||||
Version = LatestVersion;
|
||||
SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private function Init()
|
||||
{
|
||||
local S_SpawnEntry SE;
|
||||
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
if (!bConfigInitialized)
|
||||
{
|
||||
bConfigInitialized = true;
|
||||
LogLevel = LL_Info;
|
||||
SaveConfig();
|
||||
CfgSpawn.static.InitConfig();
|
||||
CfgSpawnListR.static.InitConfig();
|
||||
CfgSpawnListBW.static.InitConfig();
|
||||
CfgSpawnListSW.static.InitConfig();
|
||||
`ZS_Info("Config initialized.");
|
||||
}
|
||||
|
||||
if (LogLevel == LL_WrongLevel)
|
||||
{
|
||||
LogLevel = LL_Info;
|
||||
`ZS_Warn("Wrong 'LogLevel', return to default value");
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
`ZS_Log("LogLevel:" @ LogLevel);
|
||||
|
||||
if (!CfgSpawn.static.Load(LogLevel))
|
||||
{
|
||||
`ZS_Fatal("Wrong settings, Destroy...");
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentWave = INDEX_NONE;
|
||||
KFGIS = KFGameInfo_Survival(WorldInfo.Game);
|
||||
if (KFGIS == None)
|
||||
{
|
||||
@ -167,62 +153,113 @@ private function Init()
|
||||
}
|
||||
|
||||
KFGIA = new(KFGIS) class'KFGI_Access';
|
||||
|
||||
KFGIE = KFGameInfo_Endless(KFGIS);
|
||||
|
||||
SpawnListR = CfgSpawnListR.static.Load(LogLevel);
|
||||
InitConfig();
|
||||
|
||||
if (LogLevel == LL_WrongLevel)
|
||||
{
|
||||
LogLevel = LL_Info;
|
||||
`ZS_Warn("Wrong 'LogLevel', return to default value");
|
||||
SaveConfig();
|
||||
}
|
||||
`ZS_Log("LogLevel:" @ LogLevel);
|
||||
|
||||
if (!CfgSpawn.static.Load(LogLevel))
|
||||
{
|
||||
`ZS_Fatal("Wrong settings, Destroy...");
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnListRW = CfgSpawnListRW.static.Load(LogLevel);
|
||||
SpawnListBW = CfgSpawnListBW.static.Load(LogLevel);
|
||||
SpawnListSW = CfgSpawnListSW.static.Load(KFGIE, LogLevel);
|
||||
|
||||
CurrentWave = INDEX_NONE;
|
||||
SpecialWave = INDEX_NONE;
|
||||
CurrentCycle = 1;
|
||||
CycleWaveSize = 0;
|
||||
CycleWaveShift = MaxInt;
|
||||
foreach SpawnListR(SE)
|
||||
{
|
||||
if (CustomZeds.Find(SE.ZedClass) == INDEX_NONE
|
||||
&& KFGIA.IsCustomZed(SE.ZedClass))
|
||||
{
|
||||
`ZS_Debug("Add custom zed:" @ SE.ZedClass);
|
||||
CustomZeds.AddItem(SE.ZedClass);
|
||||
SE.ZedClass.static.PreloadContent();
|
||||
}
|
||||
|
||||
CycleWaveShift = Min(CycleWaveShift, SE.Wave);
|
||||
CycleWaveSize = Max(CycleWaveSize, SE.Wave);
|
||||
}
|
||||
CycleWaveSize = CycleWaveSize - CycleWaveShift + 1;
|
||||
|
||||
foreach SpawnListBW(SE)
|
||||
if (CfgSpawn.default.bCyclicalSpawn)
|
||||
{
|
||||
if (CustomZeds.Find(SE.ZedClass) == INDEX_NONE
|
||||
&& KFGIA.IsCustomZed(SE.ZedClass))
|
||||
CycleWaveSize = 0;
|
||||
CycleWaveShift = MaxInt;
|
||||
foreach SpawnListRW(SE)
|
||||
{
|
||||
`ZS_Debug("Add custom zed:" @ SE.ZedClass);
|
||||
CustomZeds.AddItem(SE.ZedClass);
|
||||
SE.ZedClass.static.PreloadContent();
|
||||
CycleWaveShift = Min(CycleWaveShift, SE.Wave);
|
||||
CycleWaveSize = Max(CycleWaveSize, SE.Wave);
|
||||
}
|
||||
|
||||
if (BossClassCache.Find(SE.BossClass) == INDEX_NONE)
|
||||
BossClassCache.AddItem(SE.BossClass);
|
||||
CycleWaveSize = CycleWaveSize - CycleWaveShift + 1;
|
||||
}
|
||||
|
||||
foreach SpawnListSW(SE)
|
||||
{
|
||||
if (CustomZeds.Find(SE.ZedClass) == INDEX_NONE
|
||||
&& KFGIA.IsCustomZed(SE.ZedClass))
|
||||
{
|
||||
`ZS_Debug("Add custom zed:" @ SE.ZedClass);
|
||||
CustomZeds.AddItem(SE.ZedClass);
|
||||
SE.ZedClass.static.PreloadContent();
|
||||
}
|
||||
}
|
||||
CreateBossCache();
|
||||
PreloadContent();
|
||||
|
||||
SetTimer(float(dt), true, nameof(SpawnTimer));
|
||||
}
|
||||
|
||||
private function CreateBossCache()
|
||||
{
|
||||
local S_SpawnEntry SE;
|
||||
|
||||
foreach SpawnListBW(SE)
|
||||
if (BossClassCache.Find(SE.BossClass) == INDEX_NONE)
|
||||
BossClassCache.AddItem(SE.BossClass);
|
||||
}
|
||||
|
||||
private function PreloadContent()
|
||||
{
|
||||
local class<KFPawn_Monster> PawnClass;
|
||||
|
||||
ExtractCustomZedsFromSpawnList(SpawnListRW, CustomZeds);
|
||||
ExtractCustomZedsFromSpawnList(SpawnListBW, CustomZeds);
|
||||
ExtractCustomZedsFromSpawnList(SpawnListSW, CustomZeds);
|
||||
|
||||
foreach CustomZeds(PawnClass)
|
||||
{
|
||||
`ZS_Info("Preload content:" @ PawnClass);
|
||||
PawnClass.static.PreloadContent();
|
||||
}
|
||||
}
|
||||
|
||||
private function ExtractCustomZedsFromSpawnList(Array<S_SpawnEntry> SpawnList, out Array<class<KFPawn_Monster> > Out)
|
||||
{
|
||||
local S_SpawnEntry SE;
|
||||
|
||||
foreach SpawnList(SE)
|
||||
{
|
||||
if (Out.Find(SE.ZedClass) == INDEX_NONE
|
||||
&& KFGIA.IsCustomZed(SE.ZedClass))
|
||||
{
|
||||
Out.AddItem(SE.ZedClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function bool WaveConditionRegular(S_SpawnEntry SE)
|
||||
{
|
||||
return (SE.Wave == KFGIS.WaveNum - CycleWaveSize * (CurrentCycle - 1));
|
||||
}
|
||||
|
||||
public function bool WaveConditionBoss(S_SpawnEntry SE)
|
||||
{
|
||||
if (CurrentBossClass == None)
|
||||
return false;
|
||||
else
|
||||
return (SE.BossClass == CurrentBossClass);
|
||||
}
|
||||
|
||||
public function bool WaveConditionSpecial(S_SpawnEntry SE)
|
||||
{
|
||||
return (SE.Wave == SpecialWave);
|
||||
}
|
||||
|
||||
|
||||
private function SpawnTimer()
|
||||
{
|
||||
local S_SpawnEntry SE;
|
||||
local int Index;
|
||||
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
if (KFGIS.WaveNum != 0 && CurrentWave < KFGIS.WaveNum)
|
||||
@ -241,42 +278,77 @@ private function SpawnTimer()
|
||||
SpawnTimerLogger(true, "alive spawn limit reached");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!KFGIS.MyKFGRI.IsBossWave() && CfgSpawn.default.bShadowSpawn)
|
||||
{
|
||||
if (NoFreeSpawnSlots || KFGIS.MyKFGRI.AIRemaining <= KFGIS.AIAliveCount)
|
||||
{
|
||||
NoFreeSpawnSlots = true;
|
||||
SpawnTimerLogger(true, "no free spawn slots");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SpawnTimerLogger(false, SpawnListsComment);
|
||||
|
||||
foreach SpawnListCurrent(SE, Index)
|
||||
{
|
||||
if (!ReadyToStart(SE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if ((SpecialWave == INDEX_NONE && !KFGIS.MyKFGRI.IsBossWave())
|
||||
|| (SpecialWave != INDEX_NONE && !CfgSpawnListSW.default.bStopRegularSpawn)
|
||||
|| (KFGIS.MyKFGRI.IsBossWave() && !CfgSpawnListBW.default.bStopRegularSpawn))
|
||||
{
|
||||
SpawnZeds(SpawnListR, WaveConditionRegular);
|
||||
}
|
||||
|
||||
if (SpecialWave != INDEX_NONE)
|
||||
{
|
||||
SpawnZeds(SpawnListSW, WaveConditionSpecial);
|
||||
}
|
||||
|
||||
if (KFGIS.MyKFGRI.IsBossWave())
|
||||
{
|
||||
SpawnZeds(SpawnListBW, WaveConditionBoss);
|
||||
if (SE.Delay > 0)
|
||||
{
|
||||
SpawnListCurrent[Index].Delay -= dt;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SE.SpawnsLeft > 0)
|
||||
{
|
||||
SpawnEntry(SpawnListCurrent, Index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function SetupWave()
|
||||
{
|
||||
local int WaveTotalAIDef;
|
||||
local Array<String> SpawnListNames;
|
||||
local int WaveTotalAIDef;
|
||||
local String WaveTypeInfo;
|
||||
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
if (CfgSpawn.default.bCyclicalSpawn && KFGIS.WaveNum > 1 && KFGIS.WaveNum == CycleWaveShift + CycleWaveSize * CurrentCycle)
|
||||
{
|
||||
CurrentCycle++;
|
||||
`ZS_Info("Spawn cycle started:" @ CurrentCycle);
|
||||
}
|
||||
|
||||
CurrentWave = KFGIS.WaveNum;
|
||||
|
||||
if (!KFGIS.MyKFGRI.IsBossWave())
|
||||
if (KFGIE != None)
|
||||
{
|
||||
SpecialWave = KFGameReplicationInfo_Endless(KFGIE.GameReplicationInfo).CurrentSpecialMode;
|
||||
if (SpecialWave != INDEX_NONE)
|
||||
{
|
||||
WaveTypeInfo = "Special:" @ EAIType(SpecialWave);
|
||||
}
|
||||
}
|
||||
|
||||
if (KFGIS.MyKFGRI.IsBossWave())
|
||||
{
|
||||
CurrentBossClass = KFGIA.BossAITypePawn(EBossAIType(KFGIS.MyKFGRI.BossIndex));
|
||||
if (CurrentBossClass == None)
|
||||
{
|
||||
`ZS_Error("Can't determine boss class:" @ CurrentBossClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
WaveTypeInfo = "Boss:" @ CurrentBossClass;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WaveTotalAIDef = KFGIS.SpawnManager.WaveTotalAI;
|
||||
KFGIS.SpawnManager.WaveTotalAI *= CfgSpawn.default.ZedTotalMultiplier;
|
||||
@ -288,24 +360,56 @@ private function SetupWave()
|
||||
{
|
||||
`ZS_Info("increase WaveTotalAI from" @ WaveTotalAIDef @ "to" @ WaveTotalAI @ "due to ZedTotalMultiplier" @ "(" $ CfgSpawn.default.ZedTotalMultiplier $ ")");
|
||||
}
|
||||
|
||||
CurrentBossClass = None;
|
||||
}
|
||||
|
||||
if (CfgSpawn.default.bCyclicalSpawn && KFGIS.WaveNum > 1 && KFGIS.WaveNum == CycleWaveShift + CycleWaveSize * CurrentCycle)
|
||||
NoFreeSpawnSlots = false;
|
||||
UseBossSpawnList = KFGIS.MyKFGRI.IsBossWave();
|
||||
UseSpecialSpawnList = (SpecialWave != INDEX_NONE);
|
||||
UseRegularSpawnList = ((!UseSpecialSpawnList && !UseBossSpawnList)
|
||||
|| (UseSpecialSpawnList && !CfgSpawnListSW.default.bStopRegularSpawn)
|
||||
|| (UseBossSpawnList && !CfgSpawnListBW.default.bStopRegularSpawn));
|
||||
|
||||
SpawnListCurrent.Length = 0;
|
||||
if (UseRegularSpawnList)
|
||||
{
|
||||
CurrentCycle++;
|
||||
`ZS_Info("Next spawn cycle started:" @ CurrentCycle);
|
||||
SpawnListNames.AddItem("regular");
|
||||
FillCurrentSpawnList(SpawnListRW, WaveConditionRegular);
|
||||
}
|
||||
|
||||
ResetSpawnList(SpawnListR);
|
||||
ResetSpawnList(SpawnListSW);
|
||||
ResetSpawnList(SpawnListBW);
|
||||
|
||||
CurrentBossClass = None;
|
||||
|
||||
if (KFGIE != None)
|
||||
if (UseSpecialSpawnList)
|
||||
{
|
||||
SpecialWave = KFGameReplicationInfo_Endless(KFGIE.GameReplicationInfo).CurrentSpecialMode;
|
||||
SpawnListNames.AddItem("special");
|
||||
FillCurrentSpawnList(SpawnListSW, WaveConditionSpecial);
|
||||
}
|
||||
|
||||
if (UseBossSpawnList)
|
||||
{
|
||||
SpawnListNames.AddItem("boss");
|
||||
FillCurrentSpawnList(SpawnListBW, WaveConditionBoss);
|
||||
}
|
||||
|
||||
JoinArray(SpawnListNames, SpawnListsComment, ", ");
|
||||
ResetSpawnList(SpawnListCurrent);
|
||||
|
||||
if (WaveTypeInfo != "")
|
||||
{
|
||||
WaveTypeInfo = "(" $ WaveTypeInfo $ ")";
|
||||
}
|
||||
|
||||
`ZS_Info("Wave" @ CurrentWave @ WaveTypeInfo);
|
||||
}
|
||||
|
||||
private function FillCurrentSpawnList(Array<S_SpawnEntry> SpawnList, delegate<WaveCondition> Condition)
|
||||
{
|
||||
local S_SpawnEntry SE;
|
||||
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
foreach SpawnList(SE)
|
||||
if (Condition(SE))
|
||||
SpawnListCurrent.AddItem(SE);
|
||||
}
|
||||
|
||||
private function ResetSpawnList(out Array<S_SpawnEntry> List)
|
||||
@ -339,7 +443,7 @@ private function ResetSpawnList(out Array<S_SpawnEntry> List)
|
||||
else
|
||||
{
|
||||
List[Index].RelativeStart = SE.RelativeStartDefault;
|
||||
if (SE.RelativeStart == 0.f)
|
||||
if (List[Index].RelativeStart == 0.f)
|
||||
List[Index].Delay = SE.DelayDefault;
|
||||
else
|
||||
List[Index].Delay = 0;
|
||||
@ -349,12 +453,10 @@ private function ResetSpawnList(out Array<S_SpawnEntry> List)
|
||||
List[Index].SpawnsLeft = List[Index].SpawnsTotal;
|
||||
|
||||
List[Index].SingleSpawnLimit = Round(SE.SingleSpawnLimitDefault * (MLB + MLC * (Cycle - 1.0f) + MLP * (Players - 1.0f)));
|
||||
|
||||
`ZS_Debug(SE.ZedClass @ "SpawnsTotal:" @ List[Index].SpawnsTotal @ "SingleSpawnLimit:" @ List[Index].SingleSpawnLimit);
|
||||
}
|
||||
}
|
||||
|
||||
private function SpawnTimerLogger(bool Stop, optional String Reason)
|
||||
private function SpawnTimerLogger(bool Stop, optional String Comment)
|
||||
{
|
||||
local String Message;
|
||||
|
||||
@ -365,8 +467,8 @@ private function SpawnTimerLogger(bool Stop, optional String Reason)
|
||||
else
|
||||
Message = "Start spawn";
|
||||
|
||||
if (Reason != "")
|
||||
Message @= "(" $ Reason $ ")";
|
||||
if (Comment != "")
|
||||
Message @= "(" $ Comment $ ")";
|
||||
|
||||
if (Message != SpawnTimerLastMessage)
|
||||
{
|
||||
@ -375,27 +477,6 @@ private function SpawnTimerLogger(bool Stop, optional String Reason)
|
||||
}
|
||||
}
|
||||
|
||||
private function SpawnZeds(out Array<S_SpawnEntry> SpawnList, delegate<WaveCondition> Condition)
|
||||
{
|
||||
local S_SpawnEntry SE;
|
||||
local int Index;
|
||||
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
foreach SpawnList(SE, Index)
|
||||
{
|
||||
if (Condition(SE))
|
||||
{
|
||||
if (!ReadyToStart(SE)) continue;
|
||||
|
||||
if (ReadyToSpawn(SE))
|
||||
SpawnEntry(SpawnListR, Index);
|
||||
else
|
||||
SpawnListR[Index].Delay -= dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function bool ReadyToStart(S_SpawnEntry SE)
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
@ -410,13 +491,6 @@ private function bool ReadyToStart(S_SpawnEntry SE)
|
||||
}
|
||||
}
|
||||
|
||||
private function bool ReadyToSpawn(S_SpawnEntry SE)
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
return SE.Delay <= 0 && SE.SpawnsLeft > 0;
|
||||
}
|
||||
|
||||
private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
||||
{
|
||||
local S_SpawnEntry SE;
|
||||
@ -438,7 +512,13 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
||||
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
||||
{
|
||||
FreeSpawnSlots = KFGIS.MyKFGRI.AIRemaining - KFGIS.AIAliveCount;
|
||||
if (SpawnCount > FreeSpawnSlots)
|
||||
if (FreeSpawnSlots == 0)
|
||||
{
|
||||
NoFreeSpawnSlots = true;
|
||||
SpawnList[Index].SpawnsLeft = 0;
|
||||
return;
|
||||
}
|
||||
else if (SpawnCount > FreeSpawnSlots)
|
||||
{
|
||||
`ZS_Info("Not enough free slots to spawn, will spawn" @ FreeSpawnSlots @ "instead of" @ SpawnCount);
|
||||
SpawnCount = FreeSpawnSlots;
|
||||
@ -507,7 +587,7 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bo
|
||||
local Vector SpawnLocation;
|
||||
local KFPawn_Monster KFPM;
|
||||
local Controller C;
|
||||
local int SpawnFailed;
|
||||
local int SpawnFailed, Spawned;
|
||||
local int Index;
|
||||
|
||||
`ZS_Trace(`Location);
|
||||
@ -526,7 +606,7 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bo
|
||||
SpawnLocation = KFGIS.SpawnManager.GetBestSpawnVolume(CustomSquad).Location;
|
||||
SpawnLocation.Z += 10;
|
||||
}
|
||||
|
||||
|
||||
SpawnFailed = 0;
|
||||
for (Index = 0; Index < SpawnCount; Index++)
|
||||
{
|
||||
@ -548,15 +628,18 @@ private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bo
|
||||
}
|
||||
C.Possess(KFPM, false);
|
||||
}
|
||||
|
||||
Spawned = (SpawnCount - SpawnFailed);
|
||||
|
||||
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
||||
{
|
||||
KFGIS.MyKFGRI.AIRemaining -= (SpawnCount - SpawnFailed);
|
||||
KFGIS.NumAIFinishedSpawning += Spawned;
|
||||
KFGIS.NumAISpawnsQueued += Spawned;
|
||||
}
|
||||
|
||||
KFGIS.RefreshMonsterAliveCount();
|
||||
|
||||
return SpawnCount - SpawnFailed;
|
||||
KFGIS.UpdateAIRemaining();
|
||||
|
||||
return Spawned;
|
||||
}
|
||||
|
||||
public function NotifyLogin(Controller C)
|
||||
|
@ -10,6 +10,8 @@ replication
|
||||
LogLevel;
|
||||
}
|
||||
|
||||
public simulated function bool SafeDestroy() { if (!bPendingDelete) return Destroy(); else return true; }
|
||||
|
||||
public reliable client function ClientSync(class<KFPawn_Monster> CustomZed)
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
@ -31,18 +33,18 @@ public reliable client function SyncFinished()
|
||||
CustomZed.static.PreloadContent();
|
||||
}
|
||||
|
||||
Destroy();
|
||||
SafeDestroy();
|
||||
}
|
||||
|
||||
public reliable server function ServerSync()
|
||||
{
|
||||
`ZS_Trace(`Location);
|
||||
|
||||
if (CustomZeds.Length == Recieved)
|
||||
if (CustomZeds.Length == Recieved || WorldInfo.NetMode == NM_StandAlone)
|
||||
{
|
||||
`ZS_Debug("Sync finished");
|
||||
SyncFinished();
|
||||
Destroy();
|
||||
SafeDestroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -8,3 +8,5 @@
|
||||
`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
|
Reference in New Issue
Block a user