using Content.Shared.Atmos;
using Content.Shared.Disposal.Unit;
using Content.Shared.Popups;
using Robust.Shared.Map.Components;
using Robust.Shared.Random;
using System.Linq;
namespace Content.Shared.Disposal.Tube;
///
/// Handles the basic logic for determining which disposal tubes are connected
/// and which direction entities inside the tubes should move next.
///
public sealed partial class DisposalTubeSystem : EntitySystem
{
[Dependency] private IRobustRandom _random = default!;
[Dependency] private SharedPopupSystem _popups = default!;
[Dependency] private SharedMapSystem _map = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnGetTubeNextDirection);
}
private void OnGetTubeNextDirection(Entity ent, ref GetDisposalsNextDirectionEvent args)
{
var exits = GetTubeConnectableDirections(ent);
SelectNextDirection(ent, exits, ref args);
}
///
/// Returns a list of all potential exits to a disposal tube, accounting for its local rotation.
///
/// The disposal tube.
public Direction[] GetTubeConnectableDirections(Entity ent)
{
var rotation = Transform(ent).LocalRotation;
return ent.Comp.Exits
.Select(exit => new Angle(exit.ToAngle() + rotation).GetDir())
.ToArray();
}
///
/// Selects the best exit for a disposal tube, based on a curated list of choices.
///
/// The disposal tube.
/// The currated list of possible exits from the disposal tube.
/// The args for the 'get next direction' event.
public void SelectNextDirection(Entity ent, Direction[] exits, ref GetDisposalsNextDirectionEvent args)
{
if (args.Handled)
return;
if (exits.Length == 0)
return;
// The first exit is the default
args.Next = exits[0];
// This may change based on the potential exits available
// and our current direction of travel
var currentDirection = args.Holder.Comp.CurrentDirection;
switch (exits.Length)
{
// There is only one exit
case 1:
if (args.Next.GetOpposite() == currentDirection)
args.Next = Direction.Invalid;
return;
// If there are two exits
case 2:
if (args.Next.GetOpposite() == currentDirection)
args.Next = exits[1];
return;
// If there are more than two exits
default:
// Check that the default exit is valid
if (args.Next.GetOpposite() == currentDirection)
{
// If it isn't, pick one of the remaining exits at random
var directions = exits.Skip(1).ToArray();
args.Next = _random.Pick(directions);
}
return;
}
}
///
/// Tries to find an adjacent disposal tube in a specified direction.
///
/// The original disposal tube.
/// The specified direction.
/// The adjacent disposal tube.
public EntityUid? GetTubeInDirection(Entity ent, Direction direction)
{
var oppositeDirection = direction.GetOpposite();
var xform = Transform(ent);
if (!TryComp(xform.GridUid, out var grid))
return null;
var position = xform.Coordinates;
foreach (var entity in _map.GetInDir(xform.GridUid.Value, grid, position, direction))
{
if (!TryComp(entity, out DisposalTubeComponent? tube))
continue;
if (!CanConnect((entity, tube), oppositeDirection))
continue;
return entity;
}
return null;
}
///
/// Checks whether a disposal tube can make a valid connection in a specified direction.
///
/// The disposal tube.
/// The specified direction.
/// True if a valid conenction can be made.
public bool CanConnect(Entity ent, Direction direction)
{
if (!Transform(ent).Anchored)
return false;
var exits = GetTubeConnectableDirections(ent);
if (exits.Length == 0)
return false;
return exits.Contains(direction);
}
///
/// Creates a pop up message over a disposal tube, listing its potential exits.
///
/// The disposal tube.
/// The recipient of the pop up message.
public void PopupDirections(Entity ent, EntityUid recipient)
{
var exits = GetTubeConnectableDirections(ent);
if (exits.Length == 0)
return;
var directions = string.Join(", ", exits);
_popups.PopupEntity(Loc.GetString("disposal-tube-component-popup-directions-text", ("directions", directions)), ent, recipient);
}
}