From 308c92b1608ea1160292da6b3d5d70db14d3ca2d Mon Sep 17 00:00:00 2001 From: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:43:46 +1000 Subject: [PATCH] Add SpawnAfter for SpawnEntitiesBehavior (#41004) * Add SpawnAfter for SpawnEntitiesBehavior * Retry * Please tell me it is a random fail * Revert partially * Better code * forgot about it * Apply suggestion from @whatston3 Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> * Review * Review 2 part 1 * Review 2 part 2 * Fix random * Review --------- Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> --- .../Behaviors/SpawnEntitiesBehavior.cs | 39 +++++++++++++++---- .../Entities/Markers/Spawners/temp.yml | 3 +- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs index 13027a31fc..c051adec2c 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs @@ -1,12 +1,14 @@ using System.Numerics; using Content.Server.Forensics; -using Content.Server.Stack; +using Content.Server.Spawners.Components; +using Content.Server.Spawners.EntitySystems; using Content.Shared.Destructible.Thresholds; using Content.Shared.Prototypes; using Content.Shared.Stacks; using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Spawners; namespace Content.Server.Destructible.Thresholds.Behaviors { @@ -14,13 +16,15 @@ namespace Content.Server.Destructible.Thresholds.Behaviors [DataDefinition] public sealed partial class SpawnEntitiesBehavior : IThresholdBehavior { + private static readonly EntProtoId TempEntityProtoId = "TemporaryEntityForTimedDespawnSpawners"; + /// /// Entities spawned on reaching this threshold, from a min to a max. /// [DataField] - public Dictionary Spawn = new(); + public Dictionary Spawn = []; - [DataField("offset")] + [DataField] public float Offset { get; set; } = 0.5f; [DataField("transferForensics")] @@ -29,12 +33,21 @@ namespace Content.Server.Destructible.Thresholds.Behaviors [DataField] public bool SpawnInContainer; + /// + /// Time in seconds to wait before spawning entities. + /// + /// + /// If positive, and are ignored. + /// + [DataField] + public float SpawnAfter; + public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null) { var tSys = system.EntityManager.System(); var position = tSys.GetMapCoordinates(owner); - var getRandomVector = () => new Vector2(system.Random.NextFloat(-Offset, Offset), system.Random.NextFloat(-Offset, Offset)); + Vector2 GetRandomVector() => system.Random.NextVector2(Offset); var executions = 1; if (system.EntityManager.TryGetComponent(owner, out var stack)) @@ -53,11 +66,23 @@ namespace Content.Server.Destructible.Thresholds.Behaviors if (count == 0) continue; - if (EntityPrototypeHelpers.HasComponent(entityId, system.PrototypeManager, system.EntityManager.ComponentFactory)) + if (SpawnAfter > 0) + { + // TODO: TransferForensics is not supported here because the actual entity UID is not known until the spawner spawns it. + for (var i = 0; i < count; i++) + { + var spawner = system.EntityManager.SpawnEntity(TempEntityProtoId, position.Offset(GetRandomVector())); + var timedDespawn = system.EntityManager.GetComponent(spawner); + timedDespawn.Lifetime = SpawnAfter; + var spawnOnDespawn = system.EntityManager.GetComponent(spawner); + system.EntityManager.System().SetPrototype((spawner, spawnOnDespawn), entityId); + } + } + else if (EntityPrototypeHelpers.HasComponent(entityId, system.PrototypeManager, system.EntityManager.ComponentFactory)) { var spawned = SpawnInContainer ? system.EntityManager.SpawnNextToOrDrop(entityId, owner) - : system.EntityManager.SpawnEntity(entityId, position.Offset(getRandomVector())); + : system.EntityManager.SpawnEntity(entityId, position.Offset(GetRandomVector())); system.StackSystem.SetCount((spawned, null), count); TransferForensics(spawned, system, owner); @@ -68,7 +93,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors { var spawned = SpawnInContainer ? system.EntityManager.SpawnNextToOrDrop(entityId, owner) - : system.EntityManager.SpawnEntity(entityId, position.Offset(getRandomVector())); + : system.EntityManager.SpawnEntity(entityId, position.Offset(GetRandomVector())); TransferForensics(spawned, system, owner); } diff --git a/Resources/Prototypes/Entities/Markers/Spawners/temp.yml b/Resources/Prototypes/Entities/Markers/Spawners/temp.yml index aa76bb5ea7..cf52b00a05 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/temp.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/temp.yml @@ -8,4 +8,5 @@ bodyType: Static canCollide: false - type: TimedDespawn - # we can't declare the SpawnOnDespawnComponent because the entity is required on yml + - type: SpawnOnDespawn + prototype: Ash # placeholder (required by component), overridden at runtime