refactoring, improvements, fixes
- fixed spawn when all spawn volumes are busy; - refactoring (removed obsolete code, improved readability); - improved logging.
This commit is contained in:
parent
b5355d9fb5
commit
fd3c9116e4
@ -13,7 +13,7 @@ struct S_SpawnEntryCfg
|
|||||||
var bool bSpawnAtPlayerStart;
|
var bool bSpawnAtPlayerStart;
|
||||||
};
|
};
|
||||||
|
|
||||||
var public config bool bStopRegularSpawn;
|
var public config bool bStopRegularSpawn;
|
||||||
var private config Array<S_SpawnEntryCfg> Spawn;
|
var private config Array<S_SpawnEntryCfg> Spawn;
|
||||||
|
|
||||||
public static function InitConfig(int Version, int LatestVersion, KFGI_Access KFGIA)
|
public static function InitConfig(int Version, int LatestVersion, KFGI_Access KFGIA)
|
||||||
@ -34,9 +34,11 @@ public static function InitConfig(int Version, int LatestVersion, KFGI_Access KF
|
|||||||
|
|
||||||
private static function ApplyDefault(KFGI_Access KFGIA)
|
private static function ApplyDefault(KFGI_Access KFGIA)
|
||||||
{
|
{
|
||||||
local S_SpawnEntryCfg SpawnEntry;
|
local S_SpawnEntryCfg SpawnEntry;
|
||||||
local Array<class<KFPawn_Monster> > KFPM_Bosses;
|
local Array<class<KFPawn_Monster> > KFPM_Bosses;
|
||||||
local class<KFPawn_Monster> KFPMC;
|
local class<KFPawn_Monster> KFPMC;
|
||||||
|
|
||||||
|
default.Spawn.Length = 0;
|
||||||
|
|
||||||
default.bStopRegularSpawn = true;
|
default.bStopRegularSpawn = true;
|
||||||
default.Spawn.Length = 0;
|
default.Spawn.Length = 0;
|
||||||
@ -61,8 +63,9 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
|||||||
local S_SpawnEntry SpawnEntry;
|
local S_SpawnEntry SpawnEntry;
|
||||||
local int Line;
|
local int Line;
|
||||||
local bool Errors;
|
local bool Errors;
|
||||||
|
local int Loaded;
|
||||||
|
|
||||||
`ZS_Info("Load boss waves spawn list:");
|
`ZS_Info("Load boss waves spawn list...");
|
||||||
foreach default.Spawn(SpawnEntryCfg, Line)
|
foreach default.Spawn(SpawnEntryCfg, Line)
|
||||||
{
|
{
|
||||||
Errors = false;
|
Errors = false;
|
||||||
@ -115,11 +118,21 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
|||||||
|
|
||||||
if (!Errors)
|
if (!Errors)
|
||||||
{
|
{
|
||||||
|
Loaded++;
|
||||||
SpawnList.AddItem(SpawnEntry);
|
SpawnList.AddItem(SpawnEntry);
|
||||||
`ZS_Info("[" $ Line + 1 $ "]" @ "Loaded successfully:" @ SpawnEntryCfg.BossClass @ SpawnEntryCfg.ZedClass);
|
`ZS_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (" $ SpawnEntryCfg.BossClass $ ")" @ SpawnEntryCfg.ZedClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Loaded == default.Spawn.Length)
|
||||||
|
{
|
||||||
|
`ZS_Info("Boss spawn list loaded successfully");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
`ZS_Info("Boss spawn list: loaded" @ Loaded @ "of" @ default.Spawn.Length @ "entries");
|
||||||
|
}
|
||||||
|
|
||||||
return SpawnList;
|
return SpawnList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ public static function InitConfig(int Version, int LatestVersion, KFGI_Access KF
|
|||||||
|
|
||||||
private static function ApplyDefault(KFGI_Access KFGIA)
|
private static function ApplyDefault(KFGI_Access KFGIA)
|
||||||
{
|
{
|
||||||
local S_SpawnEntryCfg SpawnEntry;
|
local S_SpawnEntryCfg SpawnEntry;
|
||||||
local Array<class<KFPawn_Monster> > KFPM_Zeds;
|
local Array<class<KFPawn_Monster> > KFPM_Zeds;
|
||||||
local class<KFPawn_Monster> KFPMC;
|
local class<KFPawn_Monster> KFPMC;
|
||||||
|
|
||||||
default.Spawn.Length = 0;
|
default.Spawn.Length = 0;
|
||||||
|
|
||||||
@ -64,6 +64,7 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
|||||||
local S_SpawnEntry SpawnEntry;
|
local S_SpawnEntry SpawnEntry;
|
||||||
local int Line;
|
local int Line;
|
||||||
local bool Errors;
|
local bool Errors;
|
||||||
|
local int Loaded;
|
||||||
|
|
||||||
`ZS_Info("Load spawn list:");
|
`ZS_Info("Load spawn list:");
|
||||||
foreach default.Spawn(SpawnEntryCfg, Line)
|
foreach default.Spawn(SpawnEntryCfg, Line)
|
||||||
@ -123,11 +124,21 @@ public static function Array<S_SpawnEntry> Load(E_LogLevel LogLevel)
|
|||||||
|
|
||||||
if (!Errors)
|
if (!Errors)
|
||||||
{
|
{
|
||||||
|
Loaded++;
|
||||||
SpawnList.AddItem(SpawnEntry);
|
SpawnList.AddItem(SpawnEntry);
|
||||||
`ZS_Info("[" $ Line + 1 $ "]" @ "Loaded successfully:" @ SpawnEntryCfg.Wave @ SpawnEntryCfg.ZedClass);
|
`ZS_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (w" $ SpawnEntryCfg.Wave $ ")" @ SpawnEntryCfg.ZedClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Loaded == default.Spawn.Length)
|
||||||
|
{
|
||||||
|
`ZS_Info("Regular spawn list loaded successfully");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
`ZS_Info("Regular spawn list: loaded" @ Loaded @ "of" @ default.Spawn.Length @ "entries");
|
||||||
|
}
|
||||||
|
|
||||||
return SpawnList;
|
return SpawnList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ public static function InitConfig(int Version, int LatestVersion)
|
|||||||
private static function ApplyDefault()
|
private static function ApplyDefault()
|
||||||
{
|
{
|
||||||
local S_SpawnEntryCfg SpawnEntry;
|
local S_SpawnEntryCfg SpawnEntry;
|
||||||
local EAIType AIType;
|
local EAIType AIType;
|
||||||
|
|
||||||
default.bStopRegularSpawn = true;
|
default.bStopRegularSpawn = true;
|
||||||
default.Spawn.Length = 0;
|
default.Spawn.Length = 0;
|
||||||
@ -61,6 +61,7 @@ public static function Array<S_SpawnEntry> Load(KFGameInfo_Endless KFGIE, E_LogL
|
|||||||
local S_SpawnEntry SpawnEntry;
|
local S_SpawnEntry SpawnEntry;
|
||||||
local int Line;
|
local int Line;
|
||||||
local bool Errors;
|
local bool Errors;
|
||||||
|
local int Loaded;
|
||||||
|
|
||||||
if (KFGIE == None)
|
if (KFGIE == None)
|
||||||
{
|
{
|
||||||
@ -126,11 +127,21 @@ public static function Array<S_SpawnEntry> Load(KFGameInfo_Endless KFGIE, E_LogL
|
|||||||
|
|
||||||
if (!Errors)
|
if (!Errors)
|
||||||
{
|
{
|
||||||
|
Loaded++;
|
||||||
SpawnList.AddItem(SpawnEntry);
|
SpawnList.AddItem(SpawnEntry);
|
||||||
`ZS_Info("[" $ Line + 1 $ "]" @ "Loaded successfully:" @ SpawnEntryCfg.Wave @ SpawnEntryCfg.ZedClass);
|
`ZS_Debug("[" $ Line + 1 $ "]" @ "Loaded successfully: (" $ SpawnEntryCfg.Wave $ ")" @ SpawnEntryCfg.ZedClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Loaded == default.Spawn.Length)
|
||||||
|
{
|
||||||
|
`ZS_Info("Special spawn list loaded successfully");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
`ZS_Info("Special spawn list: loaded" @ Loaded @ "of" @ default.Spawn.Length @ "entries");
|
||||||
|
}
|
||||||
|
|
||||||
return SpawnList;
|
return SpawnList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,18 +26,20 @@ struct S_SpawnEntry
|
|||||||
{
|
{
|
||||||
var class<KFPawn_Monster> BossClass;
|
var class<KFPawn_Monster> BossClass;
|
||||||
var class<KFPawn_Monster> ZedClass;
|
var class<KFPawn_Monster> ZedClass;
|
||||||
var byte Wave;
|
var byte Wave;
|
||||||
var int SpawnCountBase;
|
var int SpawnCountBase;
|
||||||
var int SingleSpawnLimitDefault;
|
var int SingleSpawnLimitDefault;
|
||||||
var int SingleSpawnLimit;
|
var int SingleSpawnLimit;
|
||||||
var float Probability;
|
var float Probability;
|
||||||
var float RelativeStartDefault;
|
var float RelativeStartDefault;
|
||||||
var float RelativeStart;
|
var float RelativeStart;
|
||||||
var int DelayDefault;
|
var int DelayDefault;
|
||||||
var int Delay;
|
var int Delay;
|
||||||
var int SpawnsLeft;
|
var int SpawnsLeft;
|
||||||
var int SpawnsTotal;
|
var int SpawnsTotal;
|
||||||
var bool SpawnAtPlayerStart;
|
var bool SpawnAtPlayerStart;
|
||||||
|
var bool ForceSpawn;
|
||||||
|
var String ZedNameFiller;
|
||||||
};
|
};
|
||||||
|
|
||||||
var private config int Version;
|
var private config int Version;
|
||||||
@ -65,14 +67,11 @@ var private int CycleWaveSize;
|
|||||||
var private int WaveTotalAI;
|
var private int WaveTotalAI;
|
||||||
|
|
||||||
var private class<KFPawn_Monster> CurrentBossClass;
|
var private class<KFPawn_Monster> CurrentBossClass;
|
||||||
var private Array<class<KFPawn_Monster> > BossClassCache;
|
|
||||||
var private Array<class<KFPawn_Monster> > CustomZeds;
|
var private Array<class<KFPawn_Monster> > CustomZeds;
|
||||||
|
|
||||||
var private String SpawnTimerLastMessage;
|
var private bool SpawnActive;
|
||||||
var private String SpawnListsComment;
|
var private String SpawnListsComment;
|
||||||
|
|
||||||
delegate bool WaveCondition(S_SpawnEntry SE);
|
|
||||||
|
|
||||||
public event PreBeginPlay()
|
public event PreBeginPlay()
|
||||||
{
|
{
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
@ -192,21 +191,11 @@ private function Init()
|
|||||||
CycleWaveSize = CycleWaveSize - CycleWaveShift + 1;
|
CycleWaveSize = CycleWaveSize - CycleWaveShift + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateBossCache();
|
|
||||||
PreloadContent();
|
PreloadContent();
|
||||||
|
|
||||||
SetTimer(float(dt), true, nameof(SpawnTimer));
|
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()
|
private function PreloadContent()
|
||||||
{
|
{
|
||||||
local class<KFPawn_Monster> PawnClass;
|
local class<KFPawn_Monster> PawnClass;
|
||||||
@ -236,29 +225,11 @@ private function ExtractCustomZedsFromSpawnList(Array<S_SpawnEntry> SpawnList, o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
private function SpawnTimer()
|
||||||
{
|
{
|
||||||
local S_SpawnEntry SE;
|
local S_SpawnEntry SE;
|
||||||
local int Index;
|
local int Index;
|
||||||
|
local float Threshold;
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
|
|
||||||
@ -273,6 +244,12 @@ private function SpawnTimer()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SpawnListCurrent.Length == 0)
|
||||||
|
{
|
||||||
|
SpawnTimerLogger(true, "No spawn list for this wave");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (CfgSpawn.default.AliveSpawnLimit > 0 && KFGIS.AIAliveCount >= CfgSpawn.default.AliveSpawnLimit)
|
if (CfgSpawn.default.AliveSpawnLimit > 0 && KFGIS.AIAliveCount >= CfgSpawn.default.AliveSpawnLimit)
|
||||||
{
|
{
|
||||||
SpawnTimerLogger(true, "alive spawn limit reached");
|
SpawnTimerLogger(true, "alive spawn limit reached");
|
||||||
@ -291,9 +268,15 @@ private function SpawnTimer()
|
|||||||
|
|
||||||
SpawnTimerLogger(false, SpawnListsComment);
|
SpawnTimerLogger(false, SpawnListsComment);
|
||||||
|
|
||||||
|
Threshold = 1.0f - (float(KFGIS.MyKFGRI.AIRemaining) / float(WaveTotalAI));
|
||||||
foreach SpawnListCurrent(SE, Index)
|
foreach SpawnListCurrent(SE, Index)
|
||||||
{
|
{
|
||||||
if (!ReadyToStart(SE))
|
if (NoFreeSpawnSlots)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SE.RelativeStart != 0.0f && SE.RelativeStart > Threshold)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -316,6 +299,8 @@ private function SetupWave()
|
|||||||
local Array<String> SpawnListNames;
|
local Array<String> SpawnListNames;
|
||||||
local int WaveTotalAIDef;
|
local int WaveTotalAIDef;
|
||||||
local String WaveTypeInfo;
|
local String WaveTypeInfo;
|
||||||
|
local S_SpawnEntry SE;
|
||||||
|
local EAIType SWType;
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
|
|
||||||
@ -329,10 +314,16 @@ private function SetupWave()
|
|||||||
|
|
||||||
if (KFGIE != None)
|
if (KFGIE != None)
|
||||||
{
|
{
|
||||||
|
if (KFGameReplicationInfo_Endless(KFGIE.GameReplicationInfo).CurrentWeeklyMode != INDEX_NONE)
|
||||||
|
{
|
||||||
|
WaveTypeInfo = "Weekly:" @ KFGameReplicationInfo_Endless(KFGIE.GameReplicationInfo).CurrentWeeklyMode;
|
||||||
|
}
|
||||||
|
|
||||||
SpecialWave = KFGameReplicationInfo_Endless(KFGIE.GameReplicationInfo).CurrentSpecialMode;
|
SpecialWave = KFGameReplicationInfo_Endless(KFGIE.GameReplicationInfo).CurrentSpecialMode;
|
||||||
if (SpecialWave != INDEX_NONE)
|
if (SpecialWave != INDEX_NONE)
|
||||||
{
|
{
|
||||||
WaveTypeInfo = "Special:" @ EAIType(SpecialWave);
|
SWType = EAIType(SpecialWave);
|
||||||
|
WaveTypeInfo = "Special:" @ SWType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,7 +332,7 @@ private function SetupWave()
|
|||||||
CurrentBossClass = KFGIA.BossAITypePawn(EBossAIType(KFGIS.MyKFGRI.BossIndex));
|
CurrentBossClass = KFGIA.BossAITypePawn(EBossAIType(KFGIS.MyKFGRI.BossIndex));
|
||||||
if (CurrentBossClass == None)
|
if (CurrentBossClass == None)
|
||||||
{
|
{
|
||||||
`ZS_Error("Can't determine boss class:" @ CurrentBossClass);
|
`ZS_Error("Can't determine boss class. Boss index:" @ KFGIS.MyKFGRI.BossIndex);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -375,23 +366,29 @@ private function SetupWave()
|
|||||||
if (UseRegularSpawnList)
|
if (UseRegularSpawnList)
|
||||||
{
|
{
|
||||||
SpawnListNames.AddItem("regular");
|
SpawnListNames.AddItem("regular");
|
||||||
FillCurrentSpawnList(SpawnListRW, WaveConditionRegular);
|
foreach SpawnListRW(SE)
|
||||||
|
if (SE.Wave == KFGIS.WaveNum - CycleWaveSize * (CurrentCycle - 1))
|
||||||
|
SpawnListCurrent.AddItem(SE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UseSpecialSpawnList)
|
if (UseSpecialSpawnList)
|
||||||
{
|
{
|
||||||
SpawnListNames.AddItem("special");
|
SpawnListNames.AddItem("special");
|
||||||
FillCurrentSpawnList(SpawnListSW, WaveConditionSpecial);
|
foreach SpawnListSW(SE)
|
||||||
|
if (SE.Wave == SpecialWave)
|
||||||
|
SpawnListCurrent.AddItem(SE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UseBossSpawnList)
|
if (UseBossSpawnList)
|
||||||
{
|
{
|
||||||
SpawnListNames.AddItem("boss");
|
SpawnListNames.AddItem("boss");
|
||||||
FillCurrentSpawnList(SpawnListBW, WaveConditionBoss);
|
foreach SpawnListBW(SE)
|
||||||
|
if (SE.BossClass == CurrentBossClass)
|
||||||
|
SpawnListCurrent.AddItem(SE);
|
||||||
}
|
}
|
||||||
|
|
||||||
JoinArray(SpawnListNames, SpawnListsComment, ", ");
|
JoinArray(SpawnListNames, SpawnListsComment, ", ");
|
||||||
ResetSpawnList(SpawnListCurrent);
|
AdjustSpawnList(SpawnListCurrent);
|
||||||
|
|
||||||
if (WaveTypeInfo != "")
|
if (WaveTypeInfo != "")
|
||||||
{
|
{
|
||||||
@ -401,24 +398,14 @@ private function SetupWave()
|
|||||||
`ZS_Info("Wave" @ CurrentWave @ WaveTypeInfo);
|
`ZS_Info("Wave" @ CurrentWave @ WaveTypeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function FillCurrentSpawnList(Array<S_SpawnEntry> SpawnList, delegate<WaveCondition> Condition)
|
private function AdjustSpawnList(out Array<S_SpawnEntry> List)
|
||||||
{
|
|
||||||
local S_SpawnEntry SE;
|
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
|
||||||
|
|
||||||
foreach SpawnList(SE)
|
|
||||||
if (Condition(SE))
|
|
||||||
SpawnListCurrent.AddItem(SE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function ResetSpawnList(out Array<S_SpawnEntry> List)
|
|
||||||
{
|
{
|
||||||
local S_SpawnEntry SE;
|
local S_SpawnEntry SE;
|
||||||
local int Index;
|
local int Index;
|
||||||
local float Cycle, Players;
|
local float Cycle, Players;
|
||||||
local float MSB, MSC, MSP;
|
local float MSB, MSC, MSP;
|
||||||
local float MLB, MLC, MLP;
|
local float MLB, MLC, MLP;
|
||||||
|
local int ZedNameMaxLength;
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
|
|
||||||
@ -433,8 +420,10 @@ private function ResetSpawnList(out Array<S_SpawnEntry> List)
|
|||||||
MLC = CfgSpawn.default.SingleSpawnLimitCycleMultiplier;
|
MLC = CfgSpawn.default.SingleSpawnLimitCycleMultiplier;
|
||||||
MLP = CfgSpawn.default.SingleSpawnLimitPlayerMultiplier;
|
MLP = CfgSpawn.default.SingleSpawnLimitPlayerMultiplier;
|
||||||
|
|
||||||
|
ZedNameMaxLength = 0;
|
||||||
foreach List(SE, Index)
|
foreach List(SE, Index)
|
||||||
{
|
{
|
||||||
|
ZedNameMaxLength = Max(ZedNameMaxLength, Len(String(SE.ZedClass)));
|
||||||
if (KFGIS.MyKFGRI.IsBossWave())
|
if (KFGIS.MyKFGRI.IsBossWave())
|
||||||
{
|
{
|
||||||
List[Index].RelativeStart = 0.f;
|
List[Index].RelativeStart = 0.f;
|
||||||
@ -449,10 +438,17 @@ private function ResetSpawnList(out Array<S_SpawnEntry> List)
|
|||||||
List[Index].Delay = 0;
|
List[Index].Delay = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
List[Index].SpawnsTotal = Round(SE.SpawnCountBase * (MSB + MSC * (Cycle - 1.0f) + MSP * (Players - 1.0f)));
|
List[Index].ForceSpawn = false;
|
||||||
List[Index].SpawnsLeft = List[Index].SpawnsTotal;
|
List[Index].SpawnsTotal = Round(SE.SpawnCountBase * (MSB + MSC * (Cycle - 1.0f) + MSP * (Players - 1.0f)));
|
||||||
|
|
||||||
List[Index].SingleSpawnLimit = Round(SE.SingleSpawnLimitDefault * (MLB + MLC * (Cycle - 1.0f) + MLP * (Players - 1.0f)));
|
List[Index].SingleSpawnLimit = Round(SE.SingleSpawnLimitDefault * (MLB + MLC * (Cycle - 1.0f) + MLP * (Players - 1.0f)));
|
||||||
|
List[Index].SpawnsLeft = List[Index].SpawnsTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach List(SE, Index)
|
||||||
|
{
|
||||||
|
List[Index].ZedNameFiller = "";
|
||||||
|
while (Len(String(SE.ZedClass)) + Len(List[Index].ZedNameFiller) < ZedNameMaxLength)
|
||||||
|
List[Index].ZedNameFiller @= "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,24 +466,10 @@ private function SpawnTimerLogger(bool Stop, optional String Comment)
|
|||||||
if (Comment != "")
|
if (Comment != "")
|
||||||
Message @= "(" $ Comment $ ")";
|
Message @= "(" $ Comment $ ")";
|
||||||
|
|
||||||
if (Message != SpawnTimerLastMessage)
|
if (SpawnActive == Stop)
|
||||||
{
|
{
|
||||||
`ZS_Info(Message);
|
`ZS_Info(Message);
|
||||||
SpawnTimerLastMessage = Message;
|
SpawnActive = !Stop;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function bool ReadyToStart(S_SpawnEntry SE)
|
|
||||||
{
|
|
||||||
`ZS_Trace(`Location);
|
|
||||||
|
|
||||||
if (SE.RelativeStart == 0.f)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (SE.RelativeStart <= 1.0f - (float(KFGIS.MyKFGRI.AIRemaining) / float(WaveTotalAI)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,19 +477,23 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
|||||||
{
|
{
|
||||||
local S_SpawnEntry SE;
|
local S_SpawnEntry SE;
|
||||||
local int FreeSpawnSlots, SpawnCount, Spawned;
|
local int FreeSpawnSlots, SpawnCount, Spawned;
|
||||||
local String Message;
|
local String Action, Comment, NextSpawn;
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
|
|
||||||
SE = SpawnList[Index];
|
SE = SpawnList[Index];
|
||||||
|
|
||||||
SpawnList[Index].Delay = SE.DelayDefault;
|
SpawnList[Index].Delay = SE.DelayDefault;
|
||||||
if (FRand() <= SE.Probability)
|
if (FRand() <= SE.Probability || SE.ForceSpawn)
|
||||||
{
|
{
|
||||||
if (SE.SingleSpawnLimit == 0 || SE.SpawnsLeft < SE.SingleSpawnLimit)
|
if (SE.SingleSpawnLimit == 0 || SE.SpawnsLeft < SE.SingleSpawnLimit)
|
||||||
|
{
|
||||||
SpawnCount = SE.SpawnsLeft;
|
SpawnCount = SE.SpawnsLeft;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
SpawnCount = SE.SingleSpawnLimit;
|
SpawnCount = SE.SingleSpawnLimit;
|
||||||
|
}
|
||||||
|
|
||||||
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
||||||
{
|
{
|
||||||
@ -520,26 +506,52 @@ private function SpawnEntry(out Array<S_SpawnEntry> SpawnList, int Index)
|
|||||||
}
|
}
|
||||||
else if (SpawnCount > FreeSpawnSlots)
|
else if (SpawnCount > FreeSpawnSlots)
|
||||||
{
|
{
|
||||||
`ZS_Info("Not enough free slots to spawn, will spawn" @ FreeSpawnSlots @ "instead of" @ SpawnCount);
|
|
||||||
SpawnCount = FreeSpawnSlots;
|
SpawnCount = FreeSpawnSlots;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spawned = SpawnZed(SE.ZedClass, SpawnCount, SE.SpawnAtPlayerStart);
|
Spawned = SpawnZed(SE.ZedClass, SpawnCount, SE.SpawnAtPlayerStart);
|
||||||
Message = "Spawned:" @ SE.ZedClass @ "x" $ Spawned;
|
if (Spawned == INDEX_NONE)
|
||||||
|
{
|
||||||
|
SpawnList[Index].Delay = 5;
|
||||||
|
SpawnList[Index].ForceSpawn = true;
|
||||||
|
Action = "Skip spawn";
|
||||||
|
Comment = "no free spawn volume, try to spawn it again in" @ SpawnList[Index].Delay @ "seconds...";
|
||||||
|
SpawnLog(SE, Action, Comment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Spawned == 0)
|
||||||
|
{
|
||||||
|
Action = "Spawn failed";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpawnList[Index].ForceSpawn = false;
|
||||||
|
Action = "Spawned";
|
||||||
|
Comment = "x" $ Spawned;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Message = "Skip spawn" @ SE.ZedClass @ "due to probability:" @ SE.Probability * 100 $ "%";
|
Action = "Skip spawn";
|
||||||
|
Comment = "due to" @ Round(SE.Probability * 100) $ "%" @ "probability";
|
||||||
Spawned = SE.SingleSpawnLimit;
|
Spawned = SE.SingleSpawnLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpawnList[Index].SpawnsLeft -= Spawned;
|
SpawnList[Index].SpawnsLeft -= Spawned;
|
||||||
if (SpawnList[Index].SpawnsLeft > 0)
|
if (SpawnList[Index].SpawnsLeft > 0)
|
||||||
{
|
{
|
||||||
Message @= "(Next spawn after" @ SE.DelayDefault $ "sec," @ "spawns left:" @ SpawnList[Index].SpawnsLeft $ ")";
|
NextSpawn = "next after" @ SE.DelayDefault $ "sec," @ "pawns left:" @ SpawnList[Index].SpawnsLeft;
|
||||||
}
|
}
|
||||||
`ZS_Info(Message);
|
SpawnLog(SE, Action, Comment, NextSpawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function SpawnLog(S_SpawnEntry SE, String Action, optional String Comment, optional String NextSpawn)
|
||||||
|
{
|
||||||
|
if (Comment != "") Comment = ":" @ Comment;
|
||||||
|
if (NextSpawn != "") NextSpawn = "(" $ NextSpawn $ ")";
|
||||||
|
|
||||||
|
`ZS_Info(String(SE.ZedClass) $ SE.ZedNameFiller @ ">" @ Action $ Comment @ NextSpawn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function int PlayerCount()
|
private function int PlayerCount()
|
||||||
@ -584,57 +596,71 @@ private function Vector PlayerStartLocation()
|
|||||||
private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bool SpawnAtPlayerStart)
|
private function int SpawnZed(class<KFPawn_Monster> ZedClass, int SpawnCount, bool SpawnAtPlayerStart)
|
||||||
{
|
{
|
||||||
local Array<class<KFPawn_Monster> > CustomSquad;
|
local Array<class<KFPawn_Monster> > CustomSquad;
|
||||||
local Vector SpawnLocation;
|
local Vector SpawnLocation, PlayerStart;
|
||||||
|
local KFSpawnVolume SpawnVolume;
|
||||||
local KFPawn_Monster KFPM;
|
local KFPawn_Monster KFPM;
|
||||||
local Controller C;
|
local Controller C;
|
||||||
local int SpawnFailed, Spawned;
|
local int Failed, Spawned;
|
||||||
local int Index;
|
local int Index;
|
||||||
|
|
||||||
`ZS_Trace(`Location);
|
`ZS_Trace(`Location);
|
||||||
|
|
||||||
for (Index = 0; Index < SpawnCount; Index++)
|
PlayerStart = PlayerStartLocation();
|
||||||
CustomSquad.AddItem(ZedClass);
|
|
||||||
|
|
||||||
if (SpawnAtPlayerStart)
|
if (SpawnAtPlayerStart)
|
||||||
{
|
{
|
||||||
SpawnLocation = PlayerStartLocation();
|
SpawnLocation = PlayerStart;
|
||||||
SpawnLocation.Y += 64;
|
SpawnLocation.Y += 64;
|
||||||
SpawnLocation.Z += 64;
|
SpawnLocation.Z += 64;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SpawnLocation = KFGIS.SpawnManager.GetBestSpawnVolume(CustomSquad).Location;
|
for (Index = 0; Index < SpawnCount; Index++)
|
||||||
|
{
|
||||||
|
CustomSquad.AddItem(ZedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnVolume = KFGIS.SpawnManager.GetBestSpawnVolume(CustomSquad);
|
||||||
|
if (SpawnVolume == None)
|
||||||
|
{
|
||||||
|
return INDEX_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnLocation = SpawnVolume.Location;
|
||||||
|
if (SpawnLocation == PlayerStart)
|
||||||
|
{
|
||||||
|
return INDEX_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
SpawnLocation.Z += 10;
|
SpawnLocation.Z += 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpawnFailed = 0;
|
Spawned = 0; Failed = 0;
|
||||||
for (Index = 0; Index < SpawnCount; Index++)
|
while (Failed + Spawned < SpawnCount)
|
||||||
{
|
{
|
||||||
KFPM = Spawn(ZedClass,,, SpawnLocation, rot(0,0,1),, true);
|
KFPM = Spawn(ZedClass,,, SpawnLocation, rot(0,0,1),, true);
|
||||||
if (KFPM == None)
|
if (KFPM == None)
|
||||||
{
|
{
|
||||||
`ZS_Error("Can't spawn" @ ZedClass);
|
`ZS_Error("Can't spawn" @ ZedClass);
|
||||||
SpawnFailed++;
|
Failed++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
C = KFPM.Spawn(KFPM.ControllerClass);
|
C = KFPM.Spawn(KFPM.ControllerClass);
|
||||||
if (C == None)
|
if (C == None)
|
||||||
{
|
{
|
||||||
`ZS_Error("Can't spawn controller for" @ ZedClass $ ". Destroy this KFPawn...");
|
`ZS_Error("Can't spawn controller for" @ ZedClass $ ". Destroy this" @ KFPM $ "...");
|
||||||
KFPM.Destroy();
|
KFPM.Destroy();
|
||||||
SpawnFailed++;
|
Failed++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
C.Possess(KFPM, false);
|
C.Possess(KFPM, false);
|
||||||
|
Spawned++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Spawned = (SpawnCount - SpawnFailed);
|
|
||||||
|
|
||||||
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
if (CfgSpawn.default.bShadowSpawn && !KFGIS.MyKFGRI.IsBossWave())
|
||||||
{
|
{
|
||||||
KFGIS.NumAIFinishedSpawning += Spawned;
|
KFGIS.NumAIFinishedSpawning += Spawned;
|
||||||
KFGIS.NumAISpawnsQueued += Spawned;
|
KFGIS.NumAISpawnsQueued += Spawned;
|
||||||
}
|
}
|
||||||
|
|
||||||
KFGIS.UpdateAIRemaining();
|
KFGIS.UpdateAIRemaining();
|
||||||
|
Loading…
Reference in New Issue
Block a user