using System.Diagnostics.CodeAnalysis; using Content.Shared.Body.Components; using Content.Shared.Body.Events; using Content.Shared.Body.Organ; using Content.Shared.Body.Part; using Content.Shared.Random.Helpers; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Map; namespace Content.Shared.Body.Systems; public partial class SharedBodySystem { [Dependency] private readonly SharedContainerSystem _container = default!; private void InitializeOrgans() { SubscribeLocalEvent(OnOrganGetState); SubscribeLocalEvent(OnOrganHandleState); } private OrganSlot? CreateOrganSlot(string slotId, EntityUid parent, BodyPartComponent? part = null) { if (!Resolve(parent, ref part, false)) return null; var slot = new OrganSlot(slotId, parent); part.Organs.Add(slotId, slot); return slot; } private bool CanInsertOrgan(EntityUid? organId, OrganSlot slot, OrganComponent? organ = null) { return organId != null && slot.Child == null && Resolve(organId.Value, ref organ, false) && Containers.TryGetContainer(slot.Parent, BodyContainerId, out var container) && _container.CanInsert(organId.Value, container); } private void OnOrganGetState(EntityUid uid, OrganComponent organ, ref ComponentGetState args) { args.State = new OrganComponentState(organ.Body, organ.ParentSlot); } private void OnOrganHandleState(EntityUid uid, OrganComponent organ, ref ComponentHandleState args) { if (args.Current is not OrganComponentState state) return; organ.Body = state.Body; organ.ParentSlot = state.Parent; } public bool InsertOrgan(EntityUid? organId, OrganSlot slot, OrganComponent? organ = null) { if (organId == null || !Resolve(organId.Value, ref organ, false) || !CanInsertOrgan(organId, slot, organ)) return false; DropOrgan(slot.Child); DropOrgan(organId, organ); var container = Containers.EnsureContainer(slot.Parent, BodyContainerId); if (!container.Insert(organId.Value)) return false; slot.Child = organId; organ.ParentSlot = slot; organ.Body = CompOrNull(slot.Parent)?.Body; DirtyAllComponents(slot.Parent); Dirty(organId.Value, organ); if (organ.Body == null) { RaiseLocalEvent(organId.Value, new AddedToPartEvent(slot.Parent)); } else { RaiseLocalEvent(organId.Value, new AddedToPartInBodyEvent(organ.Body.Value, slot.Parent)); } return true; } public void DirtyAllComponents(EntityUid uid) { // TODO just use containers. Please if (TryComp(uid, out BodyPartComponent? part)) Dirty(uid, part); if (TryComp(uid, out OrganComponent? organ)) Dirty(uid, organ); if (TryComp(uid, out BodyComponent? body)) Dirty(uid, body); } public bool AddOrganToFirstValidSlot( EntityUid? childId, EntityUid? parentId, OrganComponent? child = null, BodyPartComponent? parent = null) { if (childId == null || !Resolve(childId.Value, ref child, false) || parentId == null || !Resolve(parentId.Value, ref parent, false)) return false; foreach (var slot in parent.Organs.Values) { if (slot.Child == null) continue; InsertOrgan(childId, slot, child); return true; } return false; } public bool DropOrgan(EntityUid? organId, OrganComponent? organ = null) { if (organId == null || !Resolve(organId.Value, ref organ, false) || organ.ParentSlot is not { } slot) return false; var oldParent = CompOrNull(organ.ParentSlot.Parent); slot.Child = null; organ.ParentSlot = null; organ.Body = null; if (Containers.TryGetContainer(slot.Parent, BodyContainerId, out var container)) container.Remove(organId.Value); if (TryComp(organId, out TransformComponent? transform)) transform.AttachToGridOrMap(); organ.Owner.RandomOffset(0.25f); if (oldParent == null) return true; if (oldParent.Body != null) { RaiseLocalEvent(organId.Value, new RemovedFromPartInBodyEvent(oldParent.Body.Value, oldParent.Owner)); } else { RaiseLocalEvent(organId.Value, new RemovedFromPartEvent(oldParent.Owner)); } return true; } public bool DropOrganAt(EntityUid? organId, EntityCoordinates dropAt, OrganComponent? organ = null) { if (organId == null || !DropOrgan(organId, organ)) return false; if (TryComp(organId.Value, out TransformComponent? transform)) transform.Coordinates = dropAt; return true; } public bool DeleteOrgan(EntityUid? id, OrganComponent? part = null) { if (id == null || !Resolve(id.Value, ref part, false)) return false; DropOrgan(id, part); if (Deleted(id.Value)) return false; Del(id.Value); return true; } /// /// Returns a list of ValueTuples of and OrganComponent on each organ /// in the given body. /// /// The body entity id to check on. /// The body to check for organs on. /// The component to check for. public List<(T Comp, OrganComponent Organ)> GetBodyOrganComponents( EntityUid uid, BodyComponent? body = null) where T : Component { if (!Resolve(uid, ref body)) return new List<(T Comp, OrganComponent Organ)>(); var query = GetEntityQuery(); var list = new List<(T Comp, OrganComponent Organ)>(3); foreach (var organ in GetBodyOrgans(uid, body)) { if (query.TryGetComponent(organ.Id, out var comp)) list.Add((comp, organ.Component)); } return list; } /// /// Tries to get a list of ValueTuples of and OrganComponent on each organs /// in the given body. /// /// The body entity id to check on. /// The list of components. /// The body to check for organs on. /// The component to check for. /// Whether any were found. public bool TryGetBodyOrganComponents( EntityUid uid, [NotNullWhen(true)] out List<(T Comp, OrganComponent Organ)>? comps, BodyComponent? body = null) where T : Component { if (!Resolve(uid, ref body)) { comps = null; return false; } comps = GetBodyOrganComponents(uid, body); if (comps.Count != 0) return true; comps = null; return false; } }