Files
RobustToolbox/Robust.Server/GameObjects/Components/Container/Container.cs
T
2019-09-09 12:22:32 -07:00

245 lines
7.9 KiB
C#

using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystemMessages;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Server.GameObjects.Components.Container
{
/// <summary>
/// Default implementation for containers,
/// cannot be inherited. If additional logic is needed,
/// this logic should go on the systems that are holding this container.
/// For example, inventory containers should be modified only through an inventory component.
/// </summary>
[UsedImplicitly]
public sealed class Container : BaseContainer
{
/// <summary>
/// The generic container class uses a list of entities
/// </summary>
private readonly List<IEntity> _containerList = new List<IEntity>();
/// <inheritdoc />
public Container(string id, IContainerManager manager) : base(id, manager) { }
/// <inheritdoc />
public override IReadOnlyCollection<IEntity> ContainedEntities => _containerList.AsReadOnly();
/// <inheritdoc />
protected override void InternalInsert(IEntity toinsert)
{
_containerList.Add(toinsert);
base.InternalInsert(toinsert);
}
/// <inheritdoc />
protected override void InternalRemove(IEntity toremove)
{
_containerList.Remove(toremove);
base.InternalRemove(toremove);
}
/// <inheritdoc />
public override bool Contains(IEntity contained)
{
return _containerList.Contains(contained);
}
/// <inheritdoc />
public override void Shutdown()
{
base.Shutdown();
foreach (var entity in _containerList)
{
entity.Delete();
}
}
}
/// <summary>
/// Base container class that all container inherit from.
/// </summary>
public abstract class BaseContainer : IContainer
{
/// <inheritdoc />
public IContainerManager Manager { get; private set; }
/// <inheritdoc />
[ViewVariables]
public string ID { get; }
/// <inheritdoc />
[ViewVariables]
public IEntity Owner => Manager?.Owner;
/// <inheritdoc />
[ViewVariables]
public bool Deleted { get; private set; }
/// <inheritdoc />
public abstract IReadOnlyCollection<IEntity> ContainedEntities { get; }
/// <summary>
/// DO NOT CALL THIS METHOD DIRECTLY!
/// You want <see cref="IContainerManager.MakeContainer{T}(string)" /> instead.
/// </summary>
protected BaseContainer(string id, IContainerManager manager)
{
DebugTools.Assert(!string.IsNullOrEmpty(id));
DebugTools.Assert(manager != null);
ID = id;
Manager = manager;
}
/// <inheritdoc />
public bool Insert(IEntity toinsert)
{
DebugTools.Assert(!Deleted);
//Verify we can insert and that the object got properly removed from its current location
if (!CanInsert(toinsert))
return false;
var transform = toinsert.Transform;
// The transform.Parent.Owner != Owner is there because map deserialization of containers still uses Insert()
// In which case the child is already parented. To us. Don't reject him hand him to the orphanage.
// Perhaps making it not use Insert() is a good idea but eh.
if (!transform.IsMapTransform && transform.Parent.Owner != Owner && !transform.Parent.Owner.GetComponent<IContainerManager>().Remove(toinsert))
{
// Can't detach the entity from its parent, can't insert.
return false;
}
InternalInsert(toinsert);
transform.AttachParent(Owner.Transform);
return true;
}
/// <summary>
/// Implement to store the reference in whatever form you want
/// </summary>
/// <param name="toinsert"></param>
protected virtual void InternalInsert(IEntity toinsert)
{
DebugTools.Assert(!Deleted);
Owner.EntityManager.RaiseEvent(Owner, new EntInsertedIntoContainerMessage(toinsert, this));
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toinsert, false));
}
/// <inheritdoc />
public virtual bool CanInsert(IEntity toinsert)
{
DebugTools.Assert(!Deleted);
// cannot insert into itself.
if (Owner == toinsert)
return false;
// Crucial, prevent circular insertion.
return !toinsert.Transform.ContainsEntity(Owner.Transform);
//Improvement: Traverse the entire tree to make sure we are not creating a loop.
}
/// <inheritdoc />
public bool Remove(IEntity toremove)
{
DebugTools.Assert(!Deleted);
if (toremove == null)
return true;
if (!CanRemove(toremove))
{
return false;
}
InternalRemove(toremove);
if (!toremove.IsValid())
return true;
toremove.Transform.DetachParent();
return true;
}
/// <inheritdoc />
public void ForceRemove(IEntity toRemove)
{
DebugTools.Assert(!Deleted);
InternalRemove(toRemove);
}
/// <summary>
/// Implement to remove the reference you used to store the entity
/// </summary>
/// <param name="toremove"></param>
protected virtual void InternalRemove(IEntity toremove)
{
DebugTools.Assert(!Deleted);
DebugTools.Assert(Manager != null);
DebugTools.Assert(toremove != null && toremove.IsValid());
Owner?.EntityManager.RaiseEvent(Owner, new EntRemovedFromContainerMessage(toremove, this));
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toremove, true));
}
/// <inheritdoc />
public virtual bool CanRemove(IEntity toremove)
{
DebugTools.Assert(!Deleted);
return Contains(toremove);
}
/// <inheritdoc />
public abstract bool Contains(IEntity contained);
/// <inheritdoc />
public virtual void Shutdown()
{
Deleted = true;
Manager = null;
}
}
/// <summary>
/// The contents of this container have been changed.
/// </summary>
public class ContainerContentsModifiedMessage : ComponentMessage
{
/// <summary>
/// Container whose contents were modified.
/// </summary>
public IContainer Container { get; }
/// <summary>
/// Entity that was added or removed from the container.
/// </summary>
public IEntity Entity { get; }
/// <summary>
/// If true, the entity was removed. If false, it was added to the container.
/// </summary>
public bool Removed { get; }
/// <summary>
/// Constructs a new instance of <see cref="ContainerContentsModifiedMessage"/>.
/// </summary>
/// <param name="container">Container whose contents were modified.</param>
/// <param name="entity">Entity that was added or removed in the container.</param>
/// <param name="removed">If true, the entity was removed. If false, it was added to the container.</param>
public ContainerContentsModifiedMessage(IContainer container, IEntity entity, bool removed)
{
Container = container;
Entity = entity;
Removed = removed;
}
}
}