using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.Containers
{
///
/// Base container class that all container inherit from.
///
public abstract class BaseContainer : IContainer
{
///
[ViewVariables]
public abstract IReadOnlyList ContainedEntities { get; }
///
public abstract string ContainerType { get; }
///
[ViewVariables]
public bool Deleted { get; private set; }
///
[ViewVariables]
public string ID { get; internal set; } = default!; // Make sure you set me in init
///
public IContainerManager Manager { get; internal set; } = default!; // Make sure you set me in init
///
[ViewVariables(VVAccess.ReadWrite)]
[field: DataField("occludes")]
public bool OccludesLight { get; set; } = true;
///
[ViewVariables]
public IEntity Owner => Manager.Owner;
///
[ViewVariables(VVAccess.ReadWrite)]
[field: DataField("showEnts")]
public bool ShowContents { get; set; }
///
/// DO NOT CALL THIS METHOD DIRECTLY!
/// You want instead.
///
protected BaseContainer() { }
///
public bool Insert(IEntity toinsert)
{
DebugTools.Assert(!Deleted);
//Verify we can insert into this container
if (!CanInsert(toinsert))
return false;
var transform = toinsert.Transform;
// CanInsert already checks nullability of Parent (or container forgot to call base that does)
if (toinsert.TryGetContainerMan(out var containerManager) && !containerManager.Remove(toinsert))
return false; // Can't remove from existing container, can't insert.
InternalInsert(toinsert);
transform.AttachParent(Owner.Transform);
// spatially move the object to the location of the container. If you don't want this functionality, the
// calling code can save the local position before calling this function, and apply it afterwords.
transform.LocalPosition = Vector2.Zero;
return true;
}
///
public virtual bool CanInsert(IEntity toinsert)
{
DebugTools.Assert(!Deleted);
// cannot insert into itself.
if (Owner == toinsert)
return false;
// no, you can't put maps or grids into containers
if (toinsert.HasComponent() || toinsert.HasComponent())
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.
}
///
public bool Remove(IEntity toremove)
{
DebugTools.Assert(!Deleted);
DebugTools.AssertNotNull(Manager);
DebugTools.AssertNotNull(toremove);
DebugTools.Assert(toremove.IsValid());
if (!CanRemove(toremove)) return false;
InternalRemove(toremove);
toremove.Transform.AttachParentToContainerOrGrid();
return true;
}
///
public void ForceRemove(IEntity toRemove)
{
DebugTools.Assert(!Deleted);
DebugTools.AssertNotNull(Manager);
DebugTools.AssertNotNull(toRemove);
DebugTools.Assert(toRemove.IsValid());
InternalRemove(toRemove);
}
///
public virtual bool CanRemove(IEntity toremove)
{
DebugTools.Assert(!Deleted);
return Contains(toremove);
}
///
public abstract bool Contains(IEntity contained);
///
public virtual void Shutdown()
{
Manager.InternalContainerShutdown(this);
Deleted = true;
}
///
/// Implement to store the reference in whatever form you want
///
///
protected virtual void InternalInsert(IEntity toinsert)
{
DebugTools.Assert(!Deleted);
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new EntInsertedIntoContainerMessage(toinsert, this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toinsert));
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toinsert, false));
Manager.Dirty();
}
///
/// Implement to remove the reference you used to store the entity
///
///
protected virtual void InternalRemove(IEntity toremove)
{
DebugTools.Assert(!Deleted);
DebugTools.AssertNotNull(Manager);
DebugTools.AssertNotNull(toremove);
DebugTools.Assert(toremove.IsValid());
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new EntRemovedFromContainerMessage(toremove, this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toremove));
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toremove, true));
Manager.Dirty();
}
}
}