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