using System.Collections.Generic; using Robust.Shared.GameObjects; using Robust.Shared.IoC; 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; } [ViewVariables] public abstract List ExpectedEntities { 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)] [DataField("occludes")] public bool OccludesLight { get; set; } = true; /// [ViewVariables] public EntityUid Owner => Manager.Owner; /// [ViewVariables(VVAccess.ReadWrite)] [DataField("showEnts")] public bool ShowContents { get; set; } /// /// DO NOT CALL THIS METHOD DIRECTLY! /// You want instead. /// protected BaseContainer() { } /// public bool Insert(EntityUid toinsert, IEntityManager? entMan = null, TransformComponent? transform = null, TransformComponent? ownerTransform = null, MetaDataComponent? meta = null) { DebugTools.Assert(!Deleted); DebugTools.Assert(transform == null || transform.Owner == toinsert); DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner); IoCManager.Resolve(ref entMan); //Verify we can insert into this container if (!CanInsert(toinsert, entMan)) return false; transform ??= entMan.GetComponent(toinsert); // CanInsert already checks nullability of Parent (or container forgot to call base that does) if (toinsert.TryGetContainerMan(out var containerManager, entMan) && !containerManager.Remove(toinsert)) return false; // Can't remove from existing container, can't insert. // Attach to parent first so we can check IsInContainer more easily. ownerTransform ??= entMan.GetComponent(Owner); transform.AttachParent(ownerTransform); InternalInsert(toinsert, entMan, meta); // This is an edge case where the parent grid is the container being inserted into, so AttachParent would not unanchor. if (transform.Anchored) transform.Anchored = false; // 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; transform.LocalRotation = Angle.Zero; return true; } /// public virtual bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null) { DebugTools.Assert(!Deleted); // cannot insert into itself. if (Owner == toinsert) return false; IoCManager.Resolve(ref entMan); // no, you can't put maps or grids into containers if (entMan.HasComponent(toinsert) || entMan.HasComponent(toinsert)) return false; // Crucial, prevent circular insertion. if (entMan.GetComponent(toinsert) .ContainsEntity(entMan.GetComponent(Owner))) return false; //Improvement: Traverse the entire tree to make sure we are not creating a loop. //raise events var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(this, toinsert); entMan.EventBus.RaiseLocalEvent(Owner, insertAttemptEvent); if (insertAttemptEvent.Cancelled) return false; var gettingInsertedAttemptEvent = new ContainerGettingInsertedAttemptEvent(this, toinsert); entMan.EventBus.RaiseLocalEvent(toinsert, gettingInsertedAttemptEvent); if (gettingInsertedAttemptEvent.Cancelled) return false; return true; } /// public bool Remove(EntityUid toremove, IEntityManager? entMan = null, TransformComponent? xform = null, MetaDataComponent? meta = null) { DebugTools.Assert(!Deleted); DebugTools.AssertNotNull(Manager); DebugTools.AssertNotNull(toremove); IoCManager.Resolve(ref entMan); DebugTools.Assert(entMan.EntityExists(toremove)); DebugTools.Assert(xform == null || xform.Owner == toremove); if (!CanRemove(toremove, entMan)) return false; InternalRemove(toremove, entMan, meta); xform ??= entMan.GetComponent(toremove); xform.AttachParentToContainerOrGrid(entMan); return true; } /// public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null) { DebugTools.Assert(!Deleted); DebugTools.AssertNotNull(Manager); DebugTools.AssertNotNull(toRemove); IoCManager.Resolve(ref entMan); DebugTools.Assert(entMan.EntityExists(toRemove)); InternalRemove(toRemove, entMan, meta); } /// public virtual bool CanRemove(EntityUid toremove, IEntityManager? entMan = null) { DebugTools.Assert(!Deleted); if (!Contains(toremove)) return false; IoCManager.Resolve(ref entMan); //raise events var removeAttemptEvent = new ContainerIsRemovingAttemptEvent(this, toremove); entMan.EventBus.RaiseLocalEvent(Owner, removeAttemptEvent); if (removeAttemptEvent.Cancelled) return false; var gettingRemovedAttemptEvent = new ContainerGettingRemovedAttemptEvent(this, toremove); entMan.EventBus.RaiseLocalEvent(toremove, gettingRemovedAttemptEvent); if (gettingRemovedAttemptEvent.Cancelled) return false; return true; } /// public abstract bool Contains(EntityUid contained); /// public virtual void Shutdown() { Manager.InternalContainerShutdown(this); Deleted = true; } /// /// Implement to store the reference in whatever form you want /// /// /// protected virtual void InternalInsert(EntityUid toinsert, IEntityManager entMan, MetaDataComponent? meta = null) { DebugTools.Assert(!Deleted); DebugTools.Assert(meta == null || meta.Owner == toinsert); meta ??= entMan.GetComponent(toinsert); meta.Flags |= MetaDataFlags.InContainer; entMan.EventBus.RaiseLocalEvent(Owner, new EntInsertedIntoContainerMessage(toinsert, this)); entMan.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toinsert)); Manager.Dirty(entMan); } /// /// Implement to remove the reference you used to store the entity /// /// /// protected virtual void InternalRemove(EntityUid toremove, IEntityManager entMan, MetaDataComponent? meta = null) { DebugTools.Assert(!Deleted); DebugTools.AssertNotNull(Manager); DebugTools.AssertNotNull(toremove); DebugTools.Assert(entMan.EntityExists(toremove)); DebugTools.Assert(meta == null || meta.Owner == toremove); meta ??= entMan.GetComponent(toremove); meta.Flags &= ~MetaDataFlags.InContainer; entMan.EventBus.RaiseLocalEvent(Owner, new EntRemovedFromContainerMessage(toremove, this)); entMan.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toremove)); Manager.Dirty(entMan); } } }