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