using Content.Shared.DragDrop; using JetBrains.Annotations; using Robust.Shared.Containers; namespace Content.Shared.Body; /// /// System responsible for coordinating entities with and their entities with . /// This system is primarily responsible for event relaying and the relationships between a body and its organs. /// It is not responsible for player-facing body features, e.g. "blood" or "breathing." /// Such features should be implemented in systems relying on the various events raised by this class. /// /// /// /// /// /// public sealed partial class BodySystem : EntitySystem { [Dependency] private SharedContainerSystem _container = default!; [Dependency] private EntityQuery _bodyQuery = default!; [Dependency] private EntityQuery _organQuery = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnBodyInit); SubscribeLocalEvent(OnBodyShutdown); SubscribeLocalEvent(OnCanDrag); SubscribeLocalEvent(OnBodyEntInserted); SubscribeLocalEvent(OnBodyEntRemoved); InitializeRelay(); } private void OnBodyInit(Entity ent, ref ComponentInit args) { ent.Comp.Organs = _container.EnsureContainer(ent, BodyComponent.ContainerID); } private void OnBodyShutdown(Entity ent, ref ComponentShutdown args) { if (ent.Comp.Organs is { } organs) _container.ShutdownContainer(organs); } private void OnBodyEntInserted(Entity ent, ref EntInsertedIntoContainerMessage args) { if (args.Container.ID != BodyComponent.ContainerID) return; if (!_organQuery.TryComp(args.Entity, out var organ)) return; var body = new OrganInsertedIntoEvent(args.Entity); RaiseLocalEvent(ent, ref body); var ev = new OrganGotInsertedEvent(ent); RaiseLocalEvent(args.Entity, ref ev); if (organ.Body != ent) { organ.Body = ent; Dirty(args.Entity, organ); } } private void OnBodyEntRemoved(Entity ent, ref EntRemovedFromContainerMessage args) { if (args.Container.ID != BodyComponent.ContainerID) return; if (!_organQuery.TryComp(args.Entity, out var organ)) return; var body = new OrganRemovedFromEvent(args.Entity); RaiseLocalEvent(ent, ref body); var ev = new OrganGotRemovedEvent(ent); RaiseLocalEvent(args.Entity, ref ev); if (organ.Body == null) return; organ.Body = null; Dirty(args.Entity, organ); } private void OnCanDrag(Entity ent, ref CanDragEvent args) { args.Handled = true; } /// /// Gets an enumerator of organs with a specific component. /// /// The entity to enumerate the organs of. /// The component. /// The enumerator of the entity's organs with the specified component. [PublicAPI] public IEnumerable> EnumerateOrgans(Entity body) where T : IComponent { if (!Resolve(body, ref body.Comp, false)) yield break; foreach (var organ in body.Comp.Organs?.ContainedEntities ?? []) { if (TryComp(organ, out var comp)) yield return (organ, _organQuery.Comp(organ), comp); } } /// /// Gets an enumerator of organs with a specific component using a . /// /// The entity to enumerate the organs of. /// The EntityQuery to use. /// The component. /// The enumerator of the entity's organs with the specified component. [PublicAPI] public IEnumerable> EnumerateOrgans(Entity body, EntityQuery query) where T : IComponent { if (!Resolve(body, ref body.Comp, false)) yield break; foreach (var organ in body.Comp.Organs?.ContainedEntities ?? []) { if (query.TryComp(organ, out var comp)) yield return (organ, _organQuery.Comp(organ), comp); } } }