using Content.Server.VentHorde.Components; using Content.Shared.Destructible; using Content.Shared.Jittering; using Content.Shared.Throwing; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; namespace Content.Server.VentHorde.Systems; public sealed class VentHordeSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedJitteringSystem _jitter = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnSpawnerInit); SubscribeLocalEvent(OnSpawnerShutdown); SubscribeLocalEvent(OnSpawnerBreakage); SubscribeLocalEvent(OnSpawnerAnchored); } private void OnSpawnerInit(Entity entity, ref MapInitEvent args) { _jitter.AddJitter(entity); } private void OnSpawnerShutdown(Entity entity, ref ComponentShutdown args) { _audio.Stop(entity.Comp.AudioStream); RemCompDeferred(entity); } private void OnSpawnerBreakage(Entity entity, ref BreakageEventArgs args) { // There is no escape. EndHordeSpawn(entity); } private void OnSpawnerAnchored(Entity entity, ref AnchorStateChangedEvent args) { // Anchor state changes when the entity is broken, to avoid double spawning we check if the entity is gonna be deleted. if (TerminatingOrDeleted(entity)) return; // There is no escape. EndHordeSpawn(entity); } /// /// Starts a horde spawn at an entity. /// /// The entity to spawn the horde at. /// List of entities to spawn. /// Time after which to spawn the entities. /// If an already active spawner is selected, will add entities to its list. Otherwise, will fail. public void StartHordeSpawn(EntityUid uid, List spawns, TimeSpan spawnDelay, bool append = true) { if (TryComp(uid, out var hordeSpawner)) { if (append) { hordeSpawner.Entities.AddRange(spawns); } return; } hordeSpawner = EnsureComp(uid); hordeSpawner.AudioStream = _audio.PlayPvs(hordeSpawner.PassiveSound, uid, hordeSpawner.PassiveSound.Params.WithLoop(true))?.Entity; hordeSpawner.Entities = spawns; hordeSpawner.SpawnTime = _timing.CurTime + spawnDelay; } /// /// Ends a horde spawn, causing all entities to spawn at once. /// /// The horde spawner entity. public void EndHordeSpawn(Entity entity) { entity.Comp.AudioStream = _audio.Stop(entity.Comp.AudioStream); _audio.PlayPvs(entity.Comp.EndSound, entity); foreach (var spawn in entity.Comp.Entities) { var spawned = Spawn(spawn, Transform(entity).Coordinates); var direction = _random.NextVector2() * _random.NextFloat(entity.Comp.MinThrowDistance, entity.Comp.MaxThrowDistance); var throwSpeed = _random.NextFloat(entity.Comp.MinThrowSpeed, entity.Comp.MaxThrowSpeed); _throwing.TryThrow(spawned, direction, throwSpeed); } RemCompDeferred(entity); } public override void Update(float frameTime) { base.Update(frameTime); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp)) { if (comp.SpawnTime != null && _timing.CurTime > comp.SpawnTime) { EndHordeSpawn((uid, comp)); } } } }