diff --git a/Content.Server/_EE/FootPrint/FootPrintsSystem.cs b/Content.Server/_EE/FootPrint/FootPrintsSystem.cs
index b30a721b93b..f751698ac08 100644
--- a/Content.Server/_EE/FootPrint/FootPrintsSystem.cs
+++ b/Content.Server/_EE/FootPrint/FootPrintsSystem.cs
@@ -1,4 +1,6 @@
-using Content.Server.Atmos.Components;
+using System.Linq;
+using System.Numerics; // DeltaV
+using Content.Server.Atmos.Components;
using Content.Shared._EE.FootPrint;
using Content.Shared.Inventory;
using Content.Shared.Mobs;
@@ -7,7 +9,9 @@
// using Content.Shared.Standing;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.GameTicking; // DeltaV
using Robust.Shared.Map;
+using Robust.Shared.Map.Components; // DeltaV
using Robust.Shared.Random;
namespace Content.Server._EE.FootPrint;
@@ -21,6 +25,16 @@ public sealed class FootPrintsSystem : EntitySystem
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
+ [Dependency] private readonly SharedMapSystem _mapSystem = default!; // DeltaV
+
+ // DeltaV - Max amount of footprints per tile
+ // Not stored on the component because its convenient here
+ private const int MaxFootprintsPerTile = 2;
+
+ ///
+ /// DeltaV: Dictionary tracking footprints per tile using tile coordinates as key
+ ///
+ private readonly Dictionary> _footprintsPerTile = new();
private EntityQuery _transformQuery;
private EntityQuery _mobThresholdQuery;
@@ -38,6 +52,10 @@ public override void Initialize()
SubscribeLocalEvent(OnStartupComponent);
SubscribeLocalEvent(OnMove);
+ SubscribeLocalEvent(OnFootPrintInit); // DeltaV
+ SubscribeLocalEvent(OnFootPrintRemove); // DeltaV
+ SubscribeLocalEvent(OnGridTerminating); // DeltaV
+ SubscribeLocalEvent(OnRoundEnd); // DeltaV
}
private void OnStartupComponent(EntityUid uid, FootPrintsComponent component, ComponentStartup args)
@@ -62,7 +80,12 @@ private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent
component.RightStep = !component.RightStep;
- var entity = Spawn(component.StepProtoId, CalcCoords(gridUid, component, transform, dragging));
+ // DeltaV - Check if we've hit the footprint limit for this tile before spawning
+ var coords = CalcCoords(gridUid, component, transform, dragging);
+ if (!ShouldCreateNewFootprint(coords))
+ return;
+
+ var entity = Spawn(component.StepProtoId, coords);
var footPrintComponent = EnsureComp(entity);
footPrintComponent.PrintOwner = uid;
@@ -92,6 +115,119 @@ private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent
_solution.TryAddReagent(footPrintComponent.Solution.Value, component.ReagentToTransfer, 1, out _);
}
+ ///
+ /// DeltaV: Checks if a new footprint can be created at the specified coordinates based on the per-tile limit.
+ ///
+ /// True if a new footprint can be created, false if the tile is full or invalid
+ private bool ShouldCreateNewFootprint(EntityCoordinates coords)
+ {
+ if (!coords.IsValid(EntityManager))
+ return false;
+
+ var mapCoords = _transform.ToMapCoordinates(coords);
+
+ if (!_map.TryFindGridAt(mapCoords, out var gridUid, out var grid))
+ return false;
+
+ var tilePos = _mapSystem.CoordinatesToTile(gridUid, grid, coords);
+ return !_footprintsPerTile.TryGetValue(tilePos, out var footprints) || footprints.Count < MaxFootprintsPerTile;
+ }
+
+ ///
+ /// DeltaV: Handles the initialization of a footprint component.
+ ///
+ private void OnFootPrintInit(Entity ent, ref ComponentInit args)
+ {
+ if (!TryGetTilePos(ent.Owner, out var tilePos))
+ return;
+
+ if (!_footprintsPerTile.TryGetValue(tilePos, out var footprints))
+ {
+ footprints = new Queue();
+ _footprintsPerTile[tilePos] = footprints;
+ }
+
+ footprints.Enqueue(ent);
+
+ // If we've exceeded the limit, remove the oldest footprint
+ if (footprints.Count > MaxFootprintsPerTile)
+ {
+ var oldestFootprint = footprints.Dequeue();
+ if (Exists(oldestFootprint))
+ QueueDel(oldestFootprint);
+ }
+ }
+
+ ///
+ /// DeltaV: Handles cleanup when a footprint component is removed.
+ ///
+ private void OnFootPrintRemove(Entity ent, ref ComponentRemove args)
+ {
+ if (!TryGetTilePos(ent.Owner, out var tilePos))
+ return;
+
+ if (_footprintsPerTile.TryGetValue(tilePos, out var footprints))
+ {
+ // Create a new queue without the removed footprint
+ var newQueue = new Queue(footprints.Where(x => x != ent.Owner));
+ if (newQueue.Count > 0)
+ _footprintsPerTile[tilePos] = newQueue;
+ else
+ _footprintsPerTile.Remove(tilePos);
+ }
+ }
+
+ ///
+ /// DeltaV: Handles cleanup when a grid is being terminated, removing all footprint tracking data from that grid.
+ ///
+ private void OnGridTerminating(Entity ent, ref EntityTerminatingEvent args)
+ {
+ // Find and remove all footprints that belong to this grid's tiles
+ var toRemove = new List();
+
+ foreach (var (pos, footprints) in _footprintsPerTile)
+ {
+ // Convert position to map coordinates to check if it belongs to this grid
+ var mapCoords = _transform.ToMapCoordinates(new EntityCoordinates(ent,
+ new Vector2(pos.X * ent.Comp.TileSize, pos.Y * ent.Comp.TileSize)));
+
+ if (_map.TryFindGridAt(mapCoords, out var gridUid, out _) && gridUid == ent.Owner)
+ {
+ toRemove.Add(pos);
+ }
+ }
+
+ foreach (var pos in toRemove)
+ {
+ _footprintsPerTile.Remove(pos);
+ }
+ }
+
+ ///
+ /// DeltaV: Attempts to get the tile position for a given entity.
+ ///
+ private bool TryGetTilePos(EntityUid uid, out Vector2i tilePos)
+ {
+ tilePos = default;
+
+ var coords = new EntityCoordinates(Transform(uid).ParentUid, Transform(uid).LocalPosition);
+ var mapCoords = _transform.ToMapCoordinates(coords);
+
+ if (!_map.TryFindGridAt(mapCoords, out var gridUid, out var grid))
+ return false;
+
+ tilePos = _mapSystem.CoordinatesToTile(gridUid, grid, coords);
+ return true;
+ }
+
+ ///
+ /// DeltaV: Clean up the dict on round end
+ ///
+ private void OnRoundEnd(RoundRestartCleanupEvent ev)
+ {
+ _footprintsPerTile.Clear();
+ }
+
private EntityCoordinates CalcCoords(EntityUid uid, FootPrintsComponent component, TransformComponent transform, bool state)
{
if (state)