using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Teleportation.Components;
namespace Content.Shared.Teleportation.Systems;
///
/// Handles symmetrically linking two entities together, and removing links properly.
/// This does not do anything on its own (outside of deleting entities that have 0 links, if that option is true)
/// Systems can do whatever they please with the linked entities, such as .
///
public sealed class LinkedEntitySystem : EntitySystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
///
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnLinkShutdown);
}
private void OnLinkShutdown(EntityUid uid, LinkedEntityComponent component, ComponentShutdown args)
{
// Remove any links to this entity when deleted.
foreach (var ent in component.LinkedEntities.ToArray())
{
if (!Deleted(ent) && LifeStage(ent) < EntityLifeStage.Terminating && TryComp(ent, out var link))
{
TryUnlink(uid, ent, component, link);
}
}
}
#region Public API
///
/// Links two entities together. Does not require the existence of on either
/// already. Linking is symmetrical, so order doesn't matter.
///
/// The first entity to link
/// The second entity to link
/// Whether both entities should now delete once their links are removed
/// Whether linking was successful (e.g. they weren't already linked)
public bool TryLink(EntityUid first, EntityUid second, bool deleteOnEmptyLinks=false)
{
var firstLink = EnsureComp(first);
var secondLink = EnsureComp(second);
firstLink.DeleteOnEmptyLinks = deleteOnEmptyLinks;
secondLink.DeleteOnEmptyLinks = deleteOnEmptyLinks;
_appearance.SetData(first, LinkedEntityVisuals.HasAnyLinks, true);
_appearance.SetData(second, LinkedEntityVisuals.HasAnyLinks, true);
Dirty(first, firstLink);
Dirty(second, secondLink);
return firstLink.LinkedEntities.Add(second)
&& secondLink.LinkedEntities.Add(first);
}
///
/// Does a one-way link from source to target.
///
/// Whether both entities should now delete once their links are removed
public bool OneWayLink(EntityUid source, EntityUid target, bool deleteOnEmptyLinks=false)
{
var firstLink = EnsureComp(source);
firstLink.DeleteOnEmptyLinks = deleteOnEmptyLinks;
_appearance.SetData(source, LinkedEntityVisuals.HasAnyLinks, true);
Dirty(source, firstLink);
return firstLink.LinkedEntities.Add(target);
}
///
/// Unlinks two entities. Deletes either entity if
/// was true and its links are now empty. Symmetrical, so order doesn't matter.
///
/// The first entity to unlink
/// The second entity to unlink
/// Resolve comp
/// Resolve comp
/// Whether unlinking was successful (e.g. they both were actually linked to one another)
public bool TryUnlink(EntityUid first, EntityUid second,
LinkedEntityComponent? firstLink=null, LinkedEntityComponent? secondLink=null)
{
if (!Resolve(first, ref firstLink))
return false;
if (!Resolve(second, ref secondLink))
return false;
var success = firstLink.LinkedEntities.Remove(second)
&& secondLink.LinkedEntities.Remove(first);
_appearance.SetData(first, LinkedEntityVisuals.HasAnyLinks, firstLink.LinkedEntities.Any());
_appearance.SetData(second, LinkedEntityVisuals.HasAnyLinks, secondLink.LinkedEntities.Any());
Dirty(firstLink);
Dirty(secondLink);
if (firstLink.LinkedEntities.Count == 0 && firstLink.DeleteOnEmptyLinks)
QueueDel(first);
if (secondLink.LinkedEntities.Count == 0 && secondLink.DeleteOnEmptyLinks)
QueueDel(second);
return success;
}
///
/// Get the first entity this entity is linked to.
/// If multiple are linked only the first one is picked.
///
public bool GetLink(EntityUid uid, [NotNullWhen(true)] out EntityUid? dest, LinkedEntityComponent? comp = null)
{
dest = null;
if (!Resolve(uid, ref comp, false))
return false;
var first = comp.LinkedEntities.FirstOrDefault();
if (first != default)
{
dest = first;
return true;
}
return false;
}
#endregion
}