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>
This commit is contained in:
B_Kirill 2026-07-14 12:43:46 +10:00 • committed by GitHub
parent 928ecf541b
commit 308c92b160
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 34 additions and 8 deletions

View file

@ -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";
/// <summary>
/// Entities spawned on reaching this threshold, from a min to a max.
/// </summary>
[DataField]
public Dictionary<EntProtoId, MinMax> Spawn = new();
public Dictionary<EntProtoId, MinMax> 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;
/// <summary>
/// Time in seconds to wait before spawning entities.
/// </summary>
/// <remarks>
/// If positive, <see cref="DoTransferForensics"/> and <see cref="SpawnInContainer"/> are ignored.
/// </remarks>
[DataField]
public float SpawnAfter;
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
{
var tSys = system.EntityManager.System<TransformSystem>();
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<StackComponent>(owner, out var stack))
@ -53,11 +66,23 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
if (count == 0)
continue;
if (EntityPrototypeHelpers.HasComponent<StackComponent>(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<TimedDespawnComponent>(spawner);
timedDespawn.Lifetime = SpawnAfter;
var spawnOnDespawn = system.EntityManager.GetComponent<SpawnOnDespawnComponent>(spawner);
system.EntityManager.System<SpawnOnDespawnSystem>().SetPrototype((spawner, spawnOnDespawn), entityId);
}
}
else if (EntityPrototypeHelpers.HasComponent<StackComponent>(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);
}

View file

@ -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