Manager component that spawns monster spawners over time. It tracks active spawner instances for zombies, ghosts and vampires, evaluates target counts from curves, and clones MonsterSpawner prefabs into a spawn area when allowed.
using Sandbox;
using System;
using System.Linq;
public sealed class MonsterManager : Component
{
[Property] GameObject MonsterSpawnerPrefab;
[Property] GameObject ZombiePrefab;
[Property] GameObject GhostPrefab;
[Property] GameObject VampirePrefab;
[Property] BBox SpawnArea;
[Property] Curve ZombieCurve;
[Property] Curve GhostCurve;
[Property] Curve VampireCurve;
float TotalSpawnerLimit;
List<GameObject> ActiveZombieSpawner;
List<GameObject> ActiveGhostSpawner;
List<GameObject> ActiveVampireSpawner;
[Property] float CloneFrequenz;
[Property] Curve SpawnCooldownCurve;
TimeSince SinceGameStart;
TimeUntil UntilNextClone;
Random random;
protected override void OnStart()
{
ActiveZombieSpawner = new List<GameObject>();
ActiveGhostSpawner = new List<GameObject>();
ActiveVampireSpawner = new List<GameObject>();
SinceGameStart = 0;
UntilNextClone = CloneFrequenz;
random = new Random();
}
protected override void OnFixedUpdate()
{
// DebugOverlay.Box( SpawnArea, Color.Green );
// Listen aufräumen bevor neue Spawner gecloned werden
ActiveZombieSpawner.RemoveAll( x => !x.IsValid );
ActiveGhostSpawner.RemoveAll( x => !x.IsValid );
ActiveVampireSpawner.RemoveAll( x => !x.IsValid );
int GesamtzahlAktiveSpawner = ActiveZombieSpawner.Count
+ ActiveGhostSpawner.Count
+ ActiveVampireSpawner.Count;
TotalSpawnerLimit = ZombieCurve.Evaluate(SinceGameStart / 60)
+ GhostCurve.Evaluate(SinceGameStart / 60)
+ VampireCurve.Evaluate(SinceGameStart / 60);
if (GesamtzahlAktiveSpawner >= TotalSpawnerLimit) { UntilNextClone = CloneFrequenz; }
// Wenn das Limit nicht auschgeschöpft ist und UntilNextClone, dann im Verhältnis niedrigsten vorhandenen Spawner clonen
if ((float)GesamtzahlAktiveSpawner < TotalSpawnerLimit && UntilNextClone)
{
float zombieDifferenz = ZombieCurve.Evaluate(SinceGameStart / 60) - ActiveZombieSpawner.Count;
float ghostDifferenz = GhostCurve.Evaluate(SinceGameStart / 60) - ActiveGhostSpawner.Count;
float vampireDifferenz = VampireCurve.Evaluate(SinceGameStart / 60) - ActiveVampireSpawner.Count;
zombieDifferenz = zombieDifferenz.Remap(0, ZombieCurve.Evaluate(SinceGameStart / 60));
ghostDifferenz = ghostDifferenz.Remap(0, GhostCurve.Evaluate(SinceGameStart / 60));
vampireDifferenz = vampireDifferenz.Remap(0, VampireCurve.Evaluate(SinceGameStart/ 60));
float[] differenzen = {zombieDifferenz, ghostDifferenz, vampireDifferenz};
// Index von größter Differenz finden und entsprechendes Prefab clonen
int differenzenMaxIndex = Array.IndexOf(differenzen, differenzen.Max());
switch (differenzenMaxIndex)
{
default:
Log.Error("MonsterManager: OnFixedUpdate() DEFAULT");
break;
case 0: // Zombie
CloneSpawner(ZombiePrefab, differenzenMaxIndex);
break;
case 1: // Ghost
CloneSpawner(GhostPrefab, differenzenMaxIndex);
break;
case 2: // Vampire
CloneSpawner(VampirePrefab, differenzenMaxIndex);
break;
}
UntilNextClone = CloneFrequenz;
}
}
void CloneSpawner(GameObject prefab, int indexCase)
{
// Scale? Spawnfreq? Spawnanzahl?
// Valid Spawnpos check
Vector3 possibleSpawnPosition = SpawnArea.RandomPointInside;
SceneTraceResult spawnPosTrace = Scene.Trace.Sphere(300, possibleSpawnPosition, possibleSpawnPosition + Vector3.Down * 1000)
.WithoutTags("world", "enemy")
.Run();
while ( spawnPosTrace.Hit )
{
possibleSpawnPosition = SpawnArea.RandomPointInside;
spawnPosTrace = Scene.Trace.Sphere( 300, possibleSpawnPosition, possibleSpawnPosition + Vector3.Down * 1000 )
.IgnoreGameObjectHierarchy( GameObject )
.WithoutTags( "World", "enemy" )
.Run();
DebugOverlay.Trace(spawnPosTrace);
Log.Info( "Spawnpos trace hit, trying new position");
}
spawnPosTrace = Scene.Trace.Ray( possibleSpawnPosition, possibleSpawnPosition + Vector3.Down * 1000 )
.IgnoreGameObjectHierarchy( GameObject )
.WithTag( "World" )
.Run();
Vector3 actualSpawnPosition = possibleSpawnPosition.WithZ(spawnPosTrace.EndPosition.z);
GameObject newSpawner = MonsterSpawnerPrefab.Clone(actualSpawnPosition, WorldRotation.Angles().WithYaw(random.Int(0, 359)), Vector3.One);
newSpawner.GetComponent<MonsterSpawner>().MonsterPrefab = prefab;
newSpawner.GetComponent<MonsterSpawner>().MonsterSpawnCooldown = SpawnCooldownCurve.Evaluate(SinceGameStart / 60);
Log.Info( "Cloning spawner for prefab: " + prefab.Name + " at " + newSpawner.WorldPosition );
switch (indexCase)
{
default:
Log.Error("Monster Manager: CloneSpawner() DEFAULT");
break;
case 0: // Zombie
newSpawner.GetComponent<MonsterSpawner>().LineRenderer.Color = Color.Average( new Color[] { Color.Green * 3, Color.White * .1f } ) * 8;
ActiveZombieSpawner.Add(newSpawner);
newSpawner.GetComponent<MonsterSpawner>().MaxMonsterSpawns = random.Int(7, 10);
break;
case 1: // Ghost
newSpawner.GetComponent<MonsterSpawner>().LineRenderer.Color = Color.Average( new Color[] { Color.Blue * 3, Color.White * .1f } ) * 8;
ActiveGhostSpawner.Add(newSpawner);
newSpawner.GetComponent<MonsterSpawner>().MaxMonsterSpawns = random.Int(4, 7);
break;
case 2: // Vampire
newSpawner.GetComponent<MonsterSpawner>().LineRenderer.Color = Color.Average( new Color[] { Color.Red * 3, Color.White * .1f } ) * 8;
ActiveVampireSpawner.Add(newSpawner);
newSpawner.GetComponent<MonsterSpawner>().MaxMonsterSpawns = random.Int(2, 4);
break;
}
}
}