// SPDX-FileCopyrightText: 2022 Jezithyr // SPDX-FileCopyrightText: 2023 Jezithyr // SPDX-FileCopyrightText: 2023 TemporalOroboros // SPDX-FileCopyrightText: 2023 Visne <39844191+Visne@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 0x6273 <0x40@keemail.me> // SPDX-FileCopyrightText: 2024 Alzore <140123969+Blackern5000@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Brandon Hu <103440971+Brandon-Huu@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 CaasGit <87243814+CaasGit@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Cojoke <83733158+Cojoke-dot@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 DrSmugleaf // SPDX-FileCopyrightText: 2024 Ed <96445749+TheShuEd@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Emisse <99158783+Emisse@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 EmoGarbage404 // SPDX-FileCopyrightText: 2024 Eoin Mcloughlin // SPDX-FileCopyrightText: 2024 Errant <35878406+Errant-4@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Flareguy <78941145+Flareguy@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Hrosts <35345601+Hrosts@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 IProduceWidgets <107586145+IProduceWidgets@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Ian // SPDX-FileCopyrightText: 2024 Ilya246 <57039557+Ilya246@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Joel Zimmerman // SPDX-FileCopyrightText: 2024 JustCone <141039037+JustCone14@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Killerqu00 <47712032+Killerqu00@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Ko4ergaPunk <62609550+Ko4ergaPunk@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Kukutis96513 <146854220+Kukutis96513@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Lye <128915833+Lyroth001@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 MerrytheManokit <167581110+MerrytheManokit@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Mervill // SPDX-FileCopyrightText: 2024 Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 MureixloI <132683811+MureixloI@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 NakataRin <45946146+NakataRin@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Nemanja <98561806+EmoGarbage404@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 OrangeMoronage9622 // SPDX-FileCopyrightText: 2024 PJBot // SPDX-FileCopyrightText: 2024 Pieter-Jan Briers // SPDX-FileCopyrightText: 2024 Piras314 // SPDX-FileCopyrightText: 2024 Plykiya <58439124+Plykiya@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Preston Smith <92108534+thetolbean@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Psychpsyo <60073468+Psychpsyo@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Repo <47093363+Titian3@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 RiceMar1244 <138547931+RiceMar1244@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Simon <63975668+Simyon264@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Stalen <33173619+stalengd@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 TakoDragon <69509841+BackeTako@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Thomas <87614336+Aeshus@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 TsjipTsjip <19798667+TsjipTsjip@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Ubaser <134914314+UbaserB@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 Unkn0wn_Gh0st // SPDX-FileCopyrightText: 2024 Vasilis // SPDX-FileCopyrightText: 2024 Vigers Ray <60344369+VigersRay@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 beck-thompson <107373427+beck-thompson@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 deathride58 // SPDX-FileCopyrightText: 2024 dffdff2423 // SPDX-FileCopyrightText: 2024 eoineoineoin // SPDX-FileCopyrightText: 2024 foboscheshir <156405958+foboscheshir@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 gluesniffler <159397573+gluesniffler@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 lzk <124214523+lzk228@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 metalgearsloth // SPDX-FileCopyrightText: 2024 nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 plykiya // SPDX-FileCopyrightText: 2024 saintmuntzer <47153094+saintmuntzer@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 shamp <140359015+shampunj@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 slarticodefast <161409025+slarticodefast@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 strO0pwafel <153459934+strO0pwafel@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 stroopwafel // SPDX-FileCopyrightText: 2024 themias <89101928+themias@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 to4no_fix <156101927+chavonadelal@users.noreply.github.com> // SPDX-FileCopyrightText: 2024 voidnull000 <18663194+voidnull000@users.noreply.github.com> // SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> // SPDX-FileCopyrightText: 2025 Aiden // SPDX-FileCopyrightText: 2025 deltanedas <39013340+deltanedas@users.noreply.github.com> // SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org> // // SPDX-License-Identifier: AGPL-3.0-or-later 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 Robust.Shared.Containers; // Shitmed Change using Content.Shared.Damage; using Content.Shared._Shitmed.BodyEffects; using Content.Shared._Shitmed.Body.Organ; namespace Content.Shared.Body.Systems; public partial class SharedBodySystem { // Shitmed Change Start private void InitializeOrgans() { SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnOrganEnableChanged); } private void OnMapInit(Entity ent, ref MapInitEvent args) { if (ent.Comp.OnAdd is not null || ent.Comp.OnRemove is not null) EnsureComp(ent); } // Shitmed Change End private void AddOrgan( Entity organEnt, EntityUid bodyUid, EntityUid parentPartUid) { organEnt.Comp.Body = bodyUid; var addedEv = new OrganAddedEvent(parentPartUid); RaiseLocalEvent(organEnt, ref addedEv); if (organEnt.Comp.Body is not null) { // Shitmed Change Start var addedInBodyEv = new OrganAddedToBodyEvent(bodyUid, parentPartUid); RaiseLocalEvent(organEnt, ref addedInBodyEv); var organEnabledEv = new OrganEnableChangedEvent(true); RaiseLocalEvent(organEnt, ref organEnabledEv); } // Shitmed Change End Dirty(organEnt, organEnt.Comp); } private void RemoveOrgan(Entity organEnt, EntityUid parentPartUid) { var removedEv = new OrganRemovedEvent(parentPartUid); RaiseLocalEvent(organEnt, ref removedEv); if (organEnt.Comp.Body is { Valid: true } bodyUid) { // Shitmed Change Start organEnt.Comp.OriginalBody = organEnt.Comp.Body; var organDisabledEv = new OrganEnableChangedEvent(false); RaiseLocalEvent(organEnt, ref organDisabledEv); // Shitmed Change End var removedInBodyEv = new OrganRemovedFromBodyEvent(bodyUid, parentPartUid); RaiseLocalEvent(organEnt, ref removedInBodyEv); } if (parentPartUid is { Valid: true } && TryComp(parentPartUid, out DamageableComponent? damageable) && damageable.TotalDamage > 200) TrySetOrganUsed(organEnt, true, organEnt.Comp); organEnt.Comp.Body = null; Dirty(organEnt, organEnt.Comp); } /// /// Creates the specified organ slot on the parent entity. /// private OrganSlot? CreateOrganSlot(Entity parentEnt, string slotId) { if (!Resolve(parentEnt, ref parentEnt.Comp, logMissing: false)) return null; Containers.EnsureContainer(parentEnt, GetOrganContainerId(slotId)); // Shitmed Change: Don't throw when a slot already exists if (parentEnt.Comp.Organs.TryGetValue(slotId, out var existing)) return existing; var slot = new OrganSlot(slotId); parentEnt.Comp.Organs.Add(slotId, slot); return slot; } /// /// Attempts to create the specified organ slot on the specified parent if it exists. /// public bool TryCreateOrganSlot( EntityUid? parent, string slotId, [NotNullWhen(true)] out OrganSlot? slot, BodyPartComponent? part = null) { slot = null; if (parent is null || !Resolve(parent.Value, ref part, logMissing: false)) { return false; } Containers.EnsureContainer(parent.Value, GetOrganContainerId(slotId)); slot = new OrganSlot(slotId); // Shitmed Change Start if (!part.Organs.ContainsKey(slotId) && !part.Organs.TryAdd(slotId, slot.Value)) return false; return true; // Shitmed Change End } /// /// Returns whether the slotId exists on the partId. /// public bool CanInsertOrgan( EntityUid partId, string slotId, BodyPartComponent? part = null) { return Resolve(partId, ref part) && part.Organs.ContainsKey(slotId); } /// /// Returns whether the specified organ slot exists on the partId. /// public bool CanInsertOrgan( EntityUid partId, OrganSlot slot, BodyPartComponent? part = null) { return CanInsertOrgan(partId, slot.Id, part); } public bool InsertOrgan( EntityUid partId, EntityUid organId, string slotId, BodyPartComponent? part = null, OrganComponent? organ = null) { if (!Resolve(organId, ref organ, logMissing: false) || !Resolve(partId, ref part, logMissing: false) || !CanInsertOrgan(partId, slotId, part)) { return false; } var containerId = GetOrganContainerId(slotId); return Containers.TryGetContainer(partId, containerId, out var container) && Containers.Insert(organId, container); } /// /// Removes the organ if it is inside of a body part. /// public bool RemoveOrgan(EntityUid organId, OrganComponent? organ = null) { if (!Containers.TryGetContainingContainer((organId, null, null), out var container)) return false; var parent = container.Owner; return HasComp(parent) && Containers.Remove(organId, container); } /// /// Tries to add this organ to any matching slot on this body part. /// public bool AddOrganToFirstValidSlot( EntityUid partId, EntityUid organId, BodyPartComponent? part = null, OrganComponent? organ = null) { if (!Resolve(partId, ref part, logMissing: false) || !Resolve(organId, ref organ, logMissing: false)) { return false; } foreach (var slotId in part.Organs.Keys) { InsertOrgan(partId, organId, slotId, part, organ); return true; } return false; } /// /// Returns a list of Entity<, > /// for each organ of the body /// /// The component that we want to return /// The body to check the organs of public List> GetBodyOrganEntityComps( Entity entity) where T : IComponent { if (!Resolve(entity, ref entity.Comp)) return new List>(); var query = GetEntityQuery(); var list = new List>(3); foreach (var organ in GetBodyOrgans(entity.Owner, entity.Comp)) { if (query.TryGetComponent(organ.Id, out var comp)) list.Add((organ.Id, 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 TryGetBodyOrganEntityComps( Entity entity, [NotNullWhen(true)] out List>? comps) where T : IComponent { if (!Resolve(entity.Owner, ref entity.Comp)) { comps = null; return false; } comps = GetBodyOrganEntityComps(entity); if (comps.Count != 0) return true; comps = null; return false; } // Shitmed Change Start public bool TrySetOrganUsed(EntityUid organId, bool used, OrganComponent? organ = null) { if (!Resolve(organId, ref organ) || organ.Used == used) return false; organ.Used = used; Dirty(organId, organ); return true; } private void OnOrganEnableChanged(Entity organEnt, ref OrganEnableChangedEvent args) { if (!organEnt.Comp.CanEnable && args.Enabled) return; organEnt.Comp.Enabled = args.Enabled; if (args.Enabled) EnableOrgan(organEnt); else DisableOrgan(organEnt); if (organEnt.Comp.Body is { Valid: true } bodyEnt) RaiseLocalEvent(organEnt, new OrganComponentsModifyEvent(bodyEnt, args.Enabled)); Dirty(organEnt, organEnt.Comp); } private void EnableOrgan(Entity organEnt) { if (!TryComp(organEnt.Comp.Body, out BodyComponent? body)) return; // I hate having to hardcode these checks so much. if (HasComp(organEnt)) { var ev = new OrganEnabledEvent(organEnt); RaiseLocalEvent(organEnt, ref ev); } } private void DisableOrgan(Entity organEnt) { if (!TryComp(organEnt.Comp.Body, out BodyComponent? body)) return; // I hate having to hardcode these checks so much. if (HasComp(organEnt)) { var ev = new OrganDisabledEvent(organEnt); RaiseLocalEvent(organEnt, ref ev); } } // Shitmed Change End }