MonsterSpawner.cs

A game component that spawns monsters and reacts to its own destruction. It tracks spawned monsters, spawns new ones on a cooldown, plays sounds and creates ‘blitz’ effects on death, and damages spawned monsters when the spawner dies.

File Access
using Sandbox;
using System;
using static HealthSystem;

public sealed class MonsterSpawner : Component, HealthSystem.IHealthEvent
{
	[Property] GameObject DeadMonsterSpawner;
	[Property] public LineRenderer LineRenderer;
	[Property] GameObject BlitzPrefab;
	[Property] public GameObject MonsterPrefab; // Wird von MonsterManager gepassed
	public float MonsterSpawnStartDelay = 1f;
	public float MonsterSpawnCooldown = 1f;
	public float MonsterScaleFactor = 1f;
	[Property] public int MaxMonsterSpawns = 1;

	List<GameObject> SpawnedMonsters;

	TimeUntil UntilNextSpawn;
	Random random;

	public interface IMonsterSpawnerEvent : ISceneEvent<IMonsterSpawnerEvent>
	{
		void OnMonsterSpawn();
	}

	void IHealthEvent.OnDeath()
	{
		Sound.Play( "sounds/spawner-dies.sound", WorldPosition);

		SpawnedMonsters.RemoveAll( x => !x.IsValid );

		foreach (var monster in SpawnedMonsters)
		{
			if ( monster == null ) { return; }

			GameObject newBlitz = BlitzPrefab.Clone(monster.GetComponentInChildren<LineRenderer>().Points[0].WorldPosition, monster.WorldRotation.Angles().WithYaw(random.Float(0, 360)), new Vector3(10, 10, 10));
			newBlitz.SetParent( monster.GetComponentInChildren<LineRenderer>().GameObject );
			newBlitz.GetComponent<Blitz>().MonsterLineColor = LineRenderer.Color.Evaluate( 0.5f );
			newBlitz.GetComponent<Blitz>().LightningStrike(0.2f);


			// HealthSystem holen, wenn nicht da aus Children holen und damagen
			HealthSystem healthSystem = monster.GetComponent<HealthSystem>();
			if ( healthSystem != null ) healthSystem.Damage( healthSystem.CurrentHealth * 0.8f );
			else { monster.GetComponentInChildren<HealthSystem>().Damage( monster.GetComponentInChildren<HealthSystem>().CurrentHealth * 0.8f ); }
		}
		GameObject newDeadMonsterSpawner = DeadMonsterSpawner.Clone(WorldPosition + Vector3.Up, WorldRotation, WorldScale);
		foreach ( GameObject child in newDeadMonsterSpawner.Children ) 
		{
			child.GetComponent<Rigidbody>().ApplyImpulse( (child.WorldPosition - GameObject.WorldPosition).Normal * 3 * child.GetComponent<Rigidbody>().Mass );
			child.GetComponent<Rigidbody>().AngularVelocity = random.VectorInSphere( random.Float( 3, 5 ) );
		}
	}



	protected override void OnStart()
	{
		SpawnedMonsters = new List<GameObject>();

		random = new Random();

		UntilNextSpawn = MonsterSpawnStartDelay;
	}

	protected override void OnFixedUpdate()
	{
		// Solange Max Monsters gespawned sind ist bleibt der NextSpawn auf Cooldown
		if ( SpawnedMonsters.Count >= MaxMonsterSpawns ) UntilNextSpawn = MonsterSpawnCooldown;

		if (true)
		{
		    SpawnedMonsters.RemoveAll( x => !x.IsValid );	
		}

        // Wenn NextSpawn abgelaufen ist
		if ( UntilNextSpawn )
		{
			SpawnMonster();
			UntilNextSpawn = MonsterSpawnCooldown;
		}
	}

	void SpawnMonster()
	{
		// Particle Effekt, Sound?
		GameObject currentMonster = MonsterPrefab.Clone( WorldPosition + (Vector3)random.VectorInCircle() * random.Int(200, 500), WorldRotation, Vector3.One * MonsterScaleFactor * random.Float(0.9f, 1.1f) );
		SpawnedMonsters.Add(currentMonster);
		IMonsterSpawnerEvent.PostToGameObject( this.GameObject, x => x.OnMonsterSpawn() );
	}

	private void BlitzMonster( GameObject monster )
	{
		GameObject newBlitz = BlitzPrefab.Clone( WorldPosition, WorldRotation, Vector3.One * 0.1f );
		LineRenderer blitzLine = newBlitz.GetComponentInChildren<LineRenderer>();
		newBlitz.GetComponent<Blitz>().MonsterSpawner = GetComponent<MonsterSpawner>();
		blitzLine.Points[0].WorldPosition = WorldPosition + Vector3.Up * 50;
		blitzLine.Points[1].WorldPosition = WorldPosition + random.VectorInSphere().Normal * 16 + (monster.WorldPosition - WorldPosition) * 0.33f + Vector3.Up * 150;
		blitzLine.Points[2].WorldPosition = WorldPosition + random.VectorInSphere().Normal * 16 + (monster.WorldPosition - WorldPosition) * 0.66f + Vector3.Up * 150;
		blitzLine.Points[3].WorldPosition = monster.WorldPosition + Vector3.Up * 150;
	}
}