using System.Numerics; using Content.Server.Spawners.Components; using Content.Server.Spawners.EntitySystems; using Content.Shared.Random; using Content.Shared.Random.Helpers; using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Spawners; namespace Content.Server.Destructible.Thresholds.Behaviors; /// /// Behavior that can be assigned to a trigger that that takes a /// and spawns a number of the same entity between a given min and max /// at a random offset from the final position of the entity. /// [Serializable] [DataDefinition] public sealed partial class WeightedSpawnEntityBehavior : IThresholdBehavior { /// /// A table of entities with assigned weights to randomly pick from /// [DataField(required: true)] public ProtoId WeightedEntityTable; /// /// How far away to spawn the entity from the parent position /// [DataField] public float SpawnOffset = 1; /// /// The mininum number of entities to spawn randomly /// [DataField] public int MinSpawn = 1; /// /// The max number of entities to spawn randomly /// [DataField] public int MaxSpawn = 1; /// /// Time in seconds to wait before spawning entities /// [DataField] public float SpawnAfter; public void Execute(EntityUid uid, DestructibleSystem system, EntityUid? cause = null) { // Get the position at which to start initially spawning entities var transform = system.EntityManager.System(); var position = transform.GetMapCoordinates(uid); // Helper function used to randomly get an offset to apply to the original position Vector2 GetRandomVector() => new (system.Random.NextFloat(-SpawnOffset, SpawnOffset), system.Random.NextFloat(-SpawnOffset, SpawnOffset)); // Randomly pick the entity to spawn and randomly pick how many to spawn var entity = system.PrototypeManager.Index(WeightedEntityTable).Pick(system.Random); var amountToSpawn = system.Random.NextFloat(MinSpawn, MaxSpawn); // Different behaviors for delayed spawning and immediate spawning if (SpawnAfter != 0) { // if it fails to get the spawner, this won't ever work so just return if (!system.PrototypeManager.TryIndex("TemporaryEntityForTimedDespawnSpawners", out var tempSpawnerProto)) return; // spawn the spawner, assign it a lifetime, and assign the entity that it will spawn when despawned for (var i = 0; i < amountToSpawn; i++) { var spawner = system.EntityManager.SpawnEntity(tempSpawnerProto.ID, position.Offset(GetRandomVector())); system.EntityManager.EnsureComponent(spawner, out var timedDespawnComponent); timedDespawnComponent.Lifetime = SpawnAfter; system.EntityManager.EnsureComponent(spawner, out var spawnOnDespawnComponent); system.EntityManager.System().SetPrototype((spawner, spawnOnDespawnComponent), entity); } } else { // directly spawn the desired entities for (var i = 0; i < amountToSpawn; i++) { system.EntityManager.SpawnEntity(entity, position.Offset(GetRandomVector())); } } } }