Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

limit footprints to 2 per tile #2270

Closed
wants to merge 4 commits into from
Closed
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 156 additions & 2 deletions Content.Server/_EE/FootPrint/FootPrintsSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Content.Server.Atmos.Components;
using System.Linq; // DeltaV
using System.Numerics; // DeltaV
using Content.Server.Atmos.Components;
using Content.Shared._EE.FootPrint;
using Content.Shared.Inventory;
using Content.Shared.Mobs;
Expand All @@ -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;
Expand All @@ -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;

/// <summary>
/// DeltaV: Dictionary tracking footprints per tile using tile coordinates as key
/// </summary>
private readonly Dictionary<Vector2i, Queue<EntityUid>> _footprintsPerTile = new();

private EntityQuery<TransformComponent> _transformQuery;
Comment on lines +33 to 38
Copy link
Contributor

@metalgearsloth metalgearsloth Nov 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't store this in a component it gets nuked on any downstreams trying persistence.

Also obviously not storing the grid itself so breaks on multi-grid.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fuck you're right, i forget that persistence is even a thing

private EntityQuery<MobThresholdsComponent> _mobThresholdQuery;
Expand All @@ -38,6 +52,10 @@ public override void Initialize()

SubscribeLocalEvent<FootPrintsComponent, ComponentStartup>(OnStartupComponent);
SubscribeLocalEvent<FootPrintsComponent, MoveEvent>(OnMove);
SubscribeLocalEvent<FootPrintComponent, ComponentInit>(OnFootPrintInit); // DeltaV
SubscribeLocalEvent<FootPrintComponent, ComponentRemove>(OnFootPrintRemove); // DeltaV
SubscribeLocalEvent<MapGridComponent, EntityTerminatingEvent>(OnGridTerminating); // DeltaV
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundEnd); // DeltaV
}

private void OnStartupComponent(EntityUid uid, FootPrintsComponent component, ComponentStartup args)
Expand All @@ -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<FootPrintComponent>(entity);

footPrintComponent.PrintOwner = uid;
Expand Down Expand Up @@ -92,6 +115,137 @@ private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent
_solution.TryAddReagent(footPrintComponent.Solution.Value, component.ReagentToTransfer, 1, out _);
}

/// <summary>
/// DeltaV: Checks if a new footprint can be created at the specified coordinates based on the per-tile limit.
/// </summary>
/// <returns>True if a new footprint can be created, false if the tile is full or invalid</returns>
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;
}

/// <summary>
/// DeltaV: Handles the initialization of a footprint component.
/// </summary>
private void OnFootPrintInit(Entity<FootPrintComponent> ent, ref ComponentInit args)
{
if (!TryGetTilePos(ent.Owner, out var tilePos))
return;

if (!_footprintsPerTile.TryGetValue(tilePos, out var footprints))
{
footprints = new Queue<EntityUid>();
_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);
}
}

/// <summary>
/// DeltaV: Handles cleanup when a footprint component is removed.
/// </summary>
private void OnFootPrintRemove(Entity<FootPrintComponent> ent, ref ComponentRemove args)
{
// Try to get tile pos if entity is still valid
Vector2i? tilePos = null;
if (!Deleted(ent) && !EntityManager.IsQueuedForDeletion(ent) && Transform(ent).ParentUid.IsValid())
{
TryGetTilePos(ent.Owner, out var pos);
tilePos = pos;
}

// If we couldn't get tile pos, search all tiles to find and remove this footprint
if (tilePos == null)
{
foreach (var (pos, prints) in _footprintsPerTile)
{
if (!prints.Contains(ent.Owner))
continue;

tilePos = pos;
break;
}
}

// Clean up the footprint tracking if we found it
if (tilePos.HasValue && _footprintsPerTile.TryGetValue(tilePos.Value, out var footprints))
{
var newQueue = new Queue<EntityUid>(footprints.Where(x => x != ent.Owner));
if (newQueue.Count > 0)
_footprintsPerTile[tilePos.Value] = newQueue;
else
_footprintsPerTile.Remove(tilePos.Value);
}
}

/// <summary>
/// DeltaV: Handles cleanup when a grid is being terminated, removing all footprint tracking data from that grid.
/// </summary>
private void OnGridTerminating(Entity<MapGridComponent> ent, ref EntityTerminatingEvent args)
{
// Find and remove all footprints that belong to this grid's tiles
var toRemove = new List<Vector2i>();

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);
}
}

/// <summary>
/// DeltaV: Attempts to get the tile position for a given entity.
/// </summary>
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;
}

/// <summary>
/// DeltaV: Clean up the dict on round end
/// </summary>
private void OnRoundEnd(RoundRestartCleanupEvent ev)
{
_footprintsPerTile.Clear();
}

private EntityCoordinates CalcCoords(EntityUid uid, FootPrintsComponent component, TransformComponent transform, bool state)
{
if (state)
Expand Down
Loading