2 Commits

View File

@ -6,7 +6,7 @@ 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';
@ -43,9 +43,15 @@ struct S_SpawnEntry
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;
@ -63,6 +69,7 @@ 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);
@ -100,7 +107,7 @@ private function InitConfig()
}
CfgSpawn.static.InitConfig(Version, LatestVersion);
CfgSpawnListR.static.InitConfig(Version, LatestVersion, KFGIA);
CfgSpawnListRW.static.InitConfig(Version, LatestVersion, KFGIA);
CfgSpawnListBW.static.InitConfig(Version, LatestVersion, KFGIA);
CfgSpawnListSW.static.InitConfig(Version, LatestVersion);
@ -165,7 +172,7 @@ private function Init()
return;
}
SpawnListR = CfgSpawnListR.static.Load(LogLevel);
SpawnListRW = CfgSpawnListRW.static.Load(LogLevel);
SpawnListBW = CfgSpawnListBW.static.Load(LogLevel);
SpawnListSW = CfgSpawnListSW.static.Load(KFGIE, LogLevel);
@ -177,7 +184,7 @@ private function Init()
{
CycleWaveSize = 0;
CycleWaveShift = MaxInt;
foreach SpawnListR(SE)
foreach SpawnListRW(SE)
{
CycleWaveShift = Min(CycleWaveShift, SE.Wave);
CycleWaveSize = Max(CycleWaveSize, SE.Wave);
@ -204,7 +211,7 @@ private function PreloadContent()
{
local class<KFPawn_Monster> PawnClass;
ExtractCustomZedsFromSpawnList(SpawnListR, CustomZeds);
ExtractCustomZedsFromSpawnList(SpawnListRW, CustomZeds);
ExtractCustomZedsFromSpawnList(SpawnListBW, CustomZeds);
ExtractCustomZedsFromSpawnList(SpawnListSW, CustomZeds);
@ -231,48 +238,28 @@ private function ExtractCustomZedsFromSpawnList(Array<S_SpawnEntry> SpawnList, o
public function bool WaveConditionRegular(S_SpawnEntry SE)
{
`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);
else
return (SE.BossClass == CurrentBossClass);
}
public function bool WaveConditionSpecial(S_SpawnEntry SE)
{
`ZS_Trace(`Location);
return (SE.Wave == SpecialWave);
}
private function SpawnTimer()
{
local S_SpawnEntry SE;
local int Index;
`ZS_Trace(`Location);
if (KFGIS.WaveNum != 0 && CurrentWave < KFGIS.WaveNum)
@ -291,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;
@ -338,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)
@ -389,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;
@ -399,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;
@ -415,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)
{
@ -425,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);
@ -460,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;
@ -488,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;
@ -557,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);
@ -576,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++)
{
@ -598,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)