|
|
@@ -1,3 +1,85 @@
|
|
|
+// SPDX-FileCopyrightText: 2022 Jezithyr <Jezithyr@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2023 Jezithyr <jezithyr@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2023 TemporalOroboros <TemporalOroboros@gmail.com>
|
|
|
+// 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 <DrSmugleaf@users.noreply.github.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 Ed <96445749+TheShuEd@users.noreply.github.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 Emisse <99158783+Emisse@users.noreply.github.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 EmoGarbage404 <retron404@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 Eoin Mcloughlin <helloworld@eoinrul.es>
|
|
|
+// 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 <ignaz.k@live.de>
|
|
|
+// SPDX-FileCopyrightText: 2024 Ilya246 <57039557+Ilya246@users.noreply.github.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 Joel Zimmerman <JoelZimmerman@users.noreply.github.com>
|
|
|
+// 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 <mervills.email@gmail.com>
|
|
|
+// 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 <whyteterry0092@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 PJBot <pieterjan.briers+bot@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 Piras314 <p1r4s@proton.me>
|
|
|
+// 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 <shadowstalkermll@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 Vasilis <vasilis@pikachu.systems>
|
|
|
+// 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 <deathride58@users.noreply.github.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 dffdff2423 <dffdff2423@gmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 eoineoineoin <github@eoinrul.es>
|
|
|
+// 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 <comedian_vs_clown@hotmail.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com>
|
|
|
+// SPDX-FileCopyrightText: 2024 plykiya <plykiya@protonmail.com>
|
|
|
+// 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 <j.o.luijkx@student.tudelft.nl>
|
|
|
+// 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 <aiden@djkraz.com>
|
|
|
+// 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 System.Linq;
|
|
|
using Content.Shared.Body.Components;
|
|
|
@@ -10,10 +92,21 @@
|
|
|
using Robust.Shared.Containers;
|
|
|
using Robust.Shared.Utility;
|
|
|
|
|
|
+// Shitmed Change Start
|
|
|
+using Content.Shared._Shitmed.Body.Events;
|
|
|
+using Content.Shared._Shitmed.Body.Part;
|
|
|
+using Content.Shared._Shitmed.BodyEffects;
|
|
|
+using Content.Shared.Humanoid;
|
|
|
+using Content.Shared.Inventory;
|
|
|
+using Content.Shared.Random;
|
|
|
+
|
|
|
namespace Content.Shared.Body.Systems;
|
|
|
|
|
|
public partial class SharedBodySystem
|
|
|
{
|
|
|
+ [Dependency] private readonly RandomHelperSystem _randomHelper = default!; // Shitmed Change
|
|
|
+ [Dependency] private readonly InventorySystem _inventorySystem = default!; // Shitmed Change
|
|
|
+
|
|
|
private void InitializeParts()
|
|
|
{
|
|
|
// TODO: This doesn't handle comp removal on child ents.
|
|
|
@@ -21,8 +114,192 @@ private void InitializeParts()
|
|
|
// If you modify this also see the Body partial for root parts.
|
|
|
SubscribeLocalEvent<BodyPartComponent, EntInsertedIntoContainerMessage>(OnBodyPartInserted);
|
|
|
SubscribeLocalEvent<BodyPartComponent, EntRemovedFromContainerMessage>(OnBodyPartRemoved);
|
|
|
+
|
|
|
+ // Shitmed Change Start
|
|
|
+ SubscribeLocalEvent<BodyPartComponent, MapInitEvent>(OnMapInit);
|
|
|
+ SubscribeLocalEvent<BodyPartComponent, ComponentRemove>(OnBodyPartRemove);
|
|
|
+ SubscribeLocalEvent<BodyPartComponent, AmputateAttemptEvent>(OnAmputateAttempt);
|
|
|
+ SubscribeLocalEvent<BodyPartComponent, BodyPartEnableChangedEvent>(OnPartEnableChanged);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void OnMapInit(Entity<BodyPartComponent> ent, ref MapInitEvent args)
|
|
|
+ {
|
|
|
+ if (ent.Comp.PartType == BodyPartType.Torso)
|
|
|
+ {
|
|
|
+ // For whatever reason this slot is initialized properly on the server, but not on the client.
|
|
|
+ // This seems to be an issue due to wiz-merge, on my old branch it was properly instantiating
|
|
|
+ // ItemInsertionSlot's container on both ends. It does show up properly on ItemSlotsComponent though.
|
|
|
+ _slots.AddItemSlot(ent, ent.Comp.ContainerName, ent.Comp.ItemInsertionSlot);
|
|
|
+ Dirty(ent, ent.Comp);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ent.Comp.OnAdd is not null || ent.Comp.OnRemove is not null)
|
|
|
+ EnsureComp<BodyPartEffectComponent>(ent);
|
|
|
+
|
|
|
+ foreach (var connection in ent.Comp.Children.Keys)
|
|
|
+ {
|
|
|
+ Containers.EnsureContainer<ContainerSlot>(ent, GetPartSlotContainerId(connection));
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var organ in ent.Comp.Organs.Keys)
|
|
|
+ {
|
|
|
+ Containers.EnsureContainer<ContainerSlot>(ent, GetOrganContainerId(organ));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void OnBodyPartRemove(Entity<BodyPartComponent> ent, ref ComponentRemove args)
|
|
|
+ {
|
|
|
+ if (ent.Comp.PartType == BodyPartType.Torso)
|
|
|
+ _slots.RemoveItemSlot(ent, ent.Comp.ItemInsertionSlot);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void OnPartEnableChanged(Entity<BodyPartComponent> partEnt, ref BodyPartEnableChangedEvent args)
|
|
|
+ {
|
|
|
+ if (!partEnt.Comp.CanEnable && args.Enabled)
|
|
|
+ return;
|
|
|
+
|
|
|
+ partEnt.Comp.Enabled = args.Enabled;
|
|
|
+
|
|
|
+ if (args.Enabled)
|
|
|
+ {
|
|
|
+ EnablePart(partEnt);
|
|
|
+ if (partEnt.Comp.Body is { Valid: true } bodyEnt)
|
|
|
+ RaiseLocalEvent(partEnt, new BodyPartComponentsModifyEvent(bodyEnt, true));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ DisablePart(partEnt);
|
|
|
+ if (partEnt.Comp.Body is { Valid: true } bodyEnt)
|
|
|
+ RaiseLocalEvent(partEnt, new BodyPartComponentsModifyEvent(bodyEnt, false));
|
|
|
+ }
|
|
|
+
|
|
|
+ Dirty(partEnt, partEnt.Comp);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void EnablePart(Entity<BodyPartComponent> partEnt)
|
|
|
+ {
|
|
|
+ if (!TryComp(partEnt.Comp.Body, out BodyComponent? body))
|
|
|
+ return;
|
|
|
+
|
|
|
+ // I hate having to hardcode these checks so much.
|
|
|
+ if (partEnt.Comp.PartType == BodyPartType.Leg)
|
|
|
+ AddLeg(partEnt, (partEnt.Comp.Body.Value, body));
|
|
|
+
|
|
|
+ if (partEnt.Comp.PartType == BodyPartType.Arm)
|
|
|
+ {
|
|
|
+ var hand = GetBodyChildrenOfType(partEnt.Comp.Body.Value, BodyPartType.Hand, symmetry: partEnt.Comp.Symmetry).FirstOrDefault();
|
|
|
+ if (hand != default)
|
|
|
+ {
|
|
|
+ var ev = new BodyPartEnabledEvent(hand);
|
|
|
+ RaiseLocalEvent(partEnt.Comp.Body.Value, ref ev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (partEnt.Comp.PartType == BodyPartType.Hand)
|
|
|
+ {
|
|
|
+ var ev = new BodyPartEnabledEvent(partEnt);
|
|
|
+ RaiseLocalEvent(partEnt.Comp.Body.Value, ref ev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Shitmed Change: This function handles dropping the items in an entity's slots if they lose all of a given part.
|
|
|
+ /// Such as their hands, feet, head, etc.
|
|
|
+ /// </summary>
|
|
|
+ public void DropSlotContents(Entity<BodyPartComponent> partEnt)
|
|
|
+ {
|
|
|
+ if (partEnt.Comp.Body is not null
|
|
|
+ && TryComp<InventoryComponent>(partEnt.Comp.Body, out var inventory) // Prevent error for non-humanoids
|
|
|
+ && GetBodyPartCount(partEnt.Comp.Body.Value, partEnt.Comp.PartType) == 1
|
|
|
+ && TryGetPartSlotContainerName(partEnt.Comp.PartType, out var containerNames))
|
|
|
+ {
|
|
|
+ foreach (var containerName in containerNames)
|
|
|
+ _inventorySystem.DropSlotContents(partEnt.Comp.Body.Value, containerName, inventory);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void DisablePart(Entity<BodyPartComponent> partEnt)
|
|
|
+ {
|
|
|
+ if (!TryComp(partEnt.Comp.Body, out BodyComponent? body))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (partEnt.Comp.PartType == BodyPartType.Leg)
|
|
|
+ RemoveLeg(partEnt, (partEnt.Comp.Body.Value, body));
|
|
|
+
|
|
|
+ if (partEnt.Comp.PartType == BodyPartType.Arm)
|
|
|
+ {
|
|
|
+ var hand = GetBodyChildrenOfType(partEnt.Comp.Body.Value, BodyPartType.Hand, symmetry: partEnt.Comp.Symmetry).FirstOrDefault();
|
|
|
+ if (hand != default)
|
|
|
+ {
|
|
|
+ var ev = new BodyPartDisabledEvent(hand);
|
|
|
+ RaiseLocalEvent(partEnt.Comp.Body.Value, ref ev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (partEnt.Comp.PartType == BodyPartType.Hand)
|
|
|
+ {
|
|
|
+ var ev = new BodyPartDisabledEvent(partEnt);
|
|
|
+ RaiseLocalEvent(partEnt.Comp.Body.Value, ref ev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: Refactor this crap. I hate it so much.
|
|
|
+ private void RemovePartEffect(Entity<BodyPartComponent> partEnt, Entity<BodyComponent?> bodyEnt)
|
|
|
+ {
|
|
|
+ if (TerminatingOrDeleted(bodyEnt)
|
|
|
+ || !Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
|
|
|
+ return;
|
|
|
+
|
|
|
+ RemovePartChildren(partEnt, bodyEnt, bodyEnt.Comp);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void RemovePartChildren(Entity<BodyPartComponent> partEnt, EntityUid bodyEnt, BodyComponent? body = null)
|
|
|
+ {
|
|
|
+ if (!Resolve(bodyEnt, ref body, logMissing: false))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (partEnt.Comp.Children.Any())
|
|
|
+ {
|
|
|
+ foreach (var slotId in partEnt.Comp.Children.Keys)
|
|
|
+ {
|
|
|
+ if (Containers.TryGetContainer(partEnt, GetPartSlotContainerId(slotId), out var container) &&
|
|
|
+ container is ContainerSlot slot &&
|
|
|
+ slot.ContainedEntity is { } childEntity &&
|
|
|
+ TryComp(childEntity, out BodyPartComponent? childPart))
|
|
|
+ {
|
|
|
+ var ev = new BodyPartEnableChangedEvent(false);
|
|
|
+ RaiseLocalEvent(childEntity, ref ev);
|
|
|
+ DropPart((childEntity, childPart));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Dirty(bodyEnt, body);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected virtual void DropPart(Entity<BodyPartComponent> partEnt)
|
|
|
+ {
|
|
|
+ DropSlotContents(partEnt);
|
|
|
+ // I don't know if this can cause issues, since any part that's being detached HAS to have a Body.
|
|
|
+ // though I really just want the compiler to shut the fuck up.
|
|
|
+ var body = partEnt.Comp.Body.GetValueOrDefault();
|
|
|
+ if (TryComp(partEnt, out TransformComponent? transform) && _gameTiming.IsFirstTimePredicted)
|
|
|
+ {
|
|
|
+ var enableEvent = new BodyPartEnableChangedEvent(false);
|
|
|
+ RaiseLocalEvent(partEnt, ref enableEvent);
|
|
|
+ var droppedEvent = new BodyPartDroppedEvent(partEnt);
|
|
|
+ RaiseLocalEvent(body, ref droppedEvent);
|
|
|
+ SharedTransform.AttachToGridOrMap(partEnt, transform);
|
|
|
+ _randomHelper.RandomOffset(partEnt, 0.5f);
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+ private void OnAmputateAttempt(Entity<BodyPartComponent> partEnt, ref AmputateAttemptEvent args) =>
|
|
|
+ DropPart(partEnt);
|
|
|
+
|
|
|
+ // Shitmed Change End
|
|
|
private void OnBodyPartInserted(Entity<BodyPartComponent> ent, ref EntInsertedIntoContainerMessage args)
|
|
|
{
|
|
|
// Body part inserted into another body part.
|
|
|
@@ -32,14 +309,17 @@ private void OnBodyPartInserted(Entity<BodyPartComponent> ent, ref EntInsertedIn
|
|
|
if (ent.Comp.Body is null)
|
|
|
return;
|
|
|
|
|
|
- if (TryComp(insertedUid, out BodyPartComponent? part))
|
|
|
+ if (TryComp(insertedUid, out BodyPartComponent? part) && slotId.Contains(PartSlotContainerIdPrefix + GetSlotFromBodyPart(part))) // Shitmed Change
|
|
|
{
|
|
|
AddPart(ent.Comp.Body.Value, (insertedUid, part), slotId);
|
|
|
RecursiveBodyUpdate((insertedUid, part), ent.Comp.Body.Value);
|
|
|
+ CheckBodyPart((insertedUid, part), GetTargetBodyPart(part), false); // Shitmed Change
|
|
|
}
|
|
|
|
|
|
- if (TryComp(insertedUid, out OrganComponent? organ))
|
|
|
+ if (TryComp(insertedUid, out OrganComponent? organ) && slotId.Contains(OrganSlotContainerIdPrefix + organ.SlotId)) // Shitmed Change
|
|
|
+ {
|
|
|
AddOrgan((insertedUid, organ), ent.Comp.Body.Value, ent);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private void OnBodyPartRemoved(Entity<BodyPartComponent> ent, ref EntRemovedFromContainerMessage args)
|
|
|
@@ -48,17 +328,32 @@ private void OnBodyPartRemoved(Entity<BodyPartComponent> ent, ref EntRemovedFrom
|
|
|
var removedUid = args.Entity;
|
|
|
var slotId = args.Container.ID;
|
|
|
|
|
|
- DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent.Comp.Body);
|
|
|
- DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent.Comp.Body);
|
|
|
-
|
|
|
- if (TryComp(removedUid, out BodyPartComponent? part) && part.Body is not null)
|
|
|
+ // Shitmed Change Start
|
|
|
+ if (TryComp(removedUid, out BodyPartComponent? part))
|
|
|
{
|
|
|
- RemovePart(part.Body.Value, (removedUid, part), slotId);
|
|
|
- RecursiveBodyUpdate((removedUid, part), null);
|
|
|
+ if (!slotId.Contains(PartSlotContainerIdPrefix + GetSlotFromBodyPart(part)))
|
|
|
+ return;
|
|
|
+
|
|
|
+ DebugTools.Assert(part.Body == ent.Comp.Body);
|
|
|
+
|
|
|
+ if (part.Body is not null)
|
|
|
+ {
|
|
|
+ CheckBodyPart((removedUid, part), GetTargetBodyPart(part), true);
|
|
|
+ RemovePart(part.Body.Value, (removedUid, part), slotId);
|
|
|
+ RecursiveBodyUpdate((removedUid, part), null);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (TryComp(removedUid, out OrganComponent? organ))
|
|
|
+ {
|
|
|
+ if (!slotId.Contains(OrganSlotContainerIdPrefix + organ.SlotId))
|
|
|
+ return;
|
|
|
+
|
|
|
+ DebugTools.Assert(organ.Body == ent.Comp.Body);
|
|
|
+
|
|
|
RemoveOrgan((removedUid, organ), ent);
|
|
|
+ }
|
|
|
+ // Shitmed Change End
|
|
|
}
|
|
|
|
|
|
private void RecursiveBodyUpdate(Entity<BodyPartComponent> ent, EntityUid? bodyUid)
|
|
|
@@ -93,6 +388,8 @@ private void RecursiveBodyUpdate(Entity<BodyPartComponent> ent, EntityUid? bodyU
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // The code for RemovePartEffect() should live here, because it literally is the point of this recursive function.
|
|
|
+ // But the debug asserts at the top plus existing tests need refactoring for this. So we'll be lazy.
|
|
|
foreach (var slotId in ent.Comp.Children.Keys)
|
|
|
{
|
|
|
if (!Containers.TryGetContainer(ent, GetPartSlotContainerId(slotId), out var container))
|
|
|
@@ -114,6 +411,9 @@ private void RecursiveBodyUpdate(Entity<BodyPartComponent> ent, EntityUid? bodyU
|
|
|
Dirty(partEnt, partEnt.Comp);
|
|
|
partEnt.Comp.Body = bodyEnt;
|
|
|
|
|
|
+ if (partEnt.Comp.Enabled && partEnt.Comp.Body is { Valid: true } body) // Shitmed Change
|
|
|
+ RaiseLocalEvent(partEnt, new BodyPartComponentsModifyEvent(body, true));
|
|
|
+
|
|
|
var ev = new BodyPartAddedEvent(slotId, partEnt);
|
|
|
RaiseLocalEvent(bodyEnt, ref ev);
|
|
|
|
|
|
@@ -127,12 +427,18 @@ private void RecursiveBodyUpdate(Entity<BodyPartComponent> ent, EntityUid? bodyU
|
|
|
{
|
|
|
Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false);
|
|
|
Dirty(partEnt, partEnt.Comp);
|
|
|
- partEnt.Comp.Body = null;
|
|
|
+
|
|
|
+ // Shitmed Change Start
|
|
|
+ if (partEnt.Comp.Body is { Valid: true } body)
|
|
|
+ RaiseLocalEvent(partEnt, new BodyPartComponentsModifyEvent(body, false));
|
|
|
+ partEnt.Comp.ParentSlot = null;
|
|
|
+ // Shitmed Change End
|
|
|
|
|
|
var ev = new BodyPartRemovedEvent(slotId, partEnt);
|
|
|
RaiseLocalEvent(bodyEnt, ref ev);
|
|
|
|
|
|
RemoveLeg(partEnt, bodyEnt);
|
|
|
+ RemovePartEffect(partEnt, bodyEnt); // Shitmed Change
|
|
|
PartRemoveDamage(bodyEnt, partEnt);
|
|
|
}
|
|
|
|
|
|
@@ -159,28 +465,13 @@ private void RemoveLeg(Entity<BodyPartComponent> legEnt, Entity<BodyComponent?>
|
|
|
bodyEnt.Comp.LegEntities.Remove(legEnt);
|
|
|
UpdateMovementSpeed(bodyEnt);
|
|
|
Dirty(bodyEnt, bodyEnt.Comp);
|
|
|
-
|
|
|
- if (!bodyEnt.Comp.LegEntities.Any())
|
|
|
- {
|
|
|
- Standing.Down(bodyEnt);
|
|
|
- }
|
|
|
+ Standing.Down(bodyEnt); // Shitmed Change
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartComponent> partEnt)
|
|
|
+ // Shitmed Change: made virtual, bleeding damage is done on server
|
|
|
+ protected virtual void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartComponent> partEnt)
|
|
|
{
|
|
|
- if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
|
|
|
- return;
|
|
|
-
|
|
|
- if (!_timing.ApplyingState
|
|
|
- && partEnt.Comp.IsVital
|
|
|
- && !GetBodyChildrenOfType(bodyEnt, partEnt.Comp.PartType, bodyEnt.Comp).Any()
|
|
|
- )
|
|
|
- {
|
|
|
- // TODO BODY SYSTEM KILL : remove this when wounding and required parts are implemented properly
|
|
|
- var damage = new DamageSpecifier(Prototypes.Index<DamageTypePrototype>("Bloodloss"), 300);
|
|
|
- Damageable.TryChangeDamage(bodyEnt, damage);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -260,6 +551,10 @@ private void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartCom
|
|
|
return null;
|
|
|
|
|
|
Containers.EnsureContainer<ContainerSlot>(partUid, GetPartSlotContainerId(slotId));
|
|
|
+ // Shitmed Change: Don't throw if the slot already exists
|
|
|
+ if (part.Children.TryGetValue(slotId, out var existing))
|
|
|
+ return existing;
|
|
|
+
|
|
|
var partSlot = new BodyPartSlot(slotId, partType);
|
|
|
part.Children.Add(slotId, partSlot);
|
|
|
Dirty(partUid, part);
|
|
|
@@ -288,7 +583,8 @@ private void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartCom
|
|
|
Containers.EnsureContainer<ContainerSlot>(partId.Value, GetPartSlotContainerId(slotId));
|
|
|
slot = new BodyPartSlot(slotId, partType);
|
|
|
|
|
|
- if (!part.Children.TryAdd(slotId, slot.Value))
|
|
|
+ if (!part.Children.ContainsKey(slotId) // Shitmed Change
|
|
|
+ && !part.Children.TryAdd(slotId, slot.Value))
|
|
|
return false;
|
|
|
|
|
|
Dirty(partId.Value, part);
|
|
|
@@ -389,6 +685,18 @@ private void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartCom
|
|
|
&& Containers.CanInsert(partId, container);
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Shitmed Change: Returns true if this parentId supports attaching a new part to the specified slot.
|
|
|
+ /// </summary>
|
|
|
+ public bool CanAttachToSlot(
|
|
|
+ EntityUid parentId,
|
|
|
+ string slotId,
|
|
|
+ BodyPartComponent? parentPart = null)
|
|
|
+ {
|
|
|
+ return Resolve(parentId, ref parentPart, logMissing: false)
|
|
|
+ && parentPart.Children.ContainsKey(slotId);
|
|
|
+ }
|
|
|
+
|
|
|
public bool AttachPartToRoot(
|
|
|
EntityUid bodyId,
|
|
|
EntityUid partId,
|
|
|
@@ -444,6 +752,14 @@ private void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartCom
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ part.ParentSlot = slot;
|
|
|
+
|
|
|
+ if (TryComp(parentPart.Body, out HumanoidAppearanceComponent? bodyAppearance)
|
|
|
+ && !HasComp<BodyPartAppearanceComponent>(partId)
|
|
|
+ && !TerminatingOrDeleted(parentPartId)
|
|
|
+ && !TerminatingOrDeleted(partId)) // Saw some exceptions involving these due to the spawn menu.
|
|
|
+ EnsureComp<BodyPartAppearanceComponent>(partId);
|
|
|
+
|
|
|
return Containers.Insert(partId, container);
|
|
|
}
|
|
|
|
|
|
@@ -656,11 +972,13 @@ public IEnumerable<BaseContainer> GetPartContainers(EntityUid id, BodyPartCompon
|
|
|
public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildrenOfType(
|
|
|
EntityUid bodyId,
|
|
|
BodyPartType type,
|
|
|
- BodyComponent? body = null)
|
|
|
+ BodyComponent? body = null,
|
|
|
+ // Shitmed Change
|
|
|
+ BodyPartSymmetry? symmetry = null)
|
|
|
{
|
|
|
foreach (var part in GetBodyChildren(bodyId, body))
|
|
|
{
|
|
|
- if (part.Component.PartType == type)
|
|
|
+ if (part.Component.PartType == type && (symmetry == null || part.Component.Symmetry == symmetry)) // Shitmed Change
|
|
|
yield return part;
|
|
|
}
|
|
|
}
|
|
|
@@ -722,6 +1040,108 @@ public IEnumerable<BaseContainer> GetPartContainers(EntityUid id, BodyPartCompon
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ // Shitmed Change Start
|
|
|
+ /// <summary>
|
|
|
+ /// Tries to get a list of ValueTuples of EntityUid and OrganComponent on each organ
|
|
|
+ /// in the given part.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="uid">The part entity id to check on.</param>
|
|
|
+ /// <param name="type">The type of component to check for.</param>
|
|
|
+ /// <param name="part">The part to check for organs on.</param>
|
|
|
+ /// <param name="organs">The organs found on the body part.</param>
|
|
|
+ /// <returns>Whether any were found.</returns>
|
|
|
+ /// <remarks>
|
|
|
+ /// This method is somewhat of a copout to the fact that we can't use reflection to generically
|
|
|
+ /// get the type of component on runtime due to sandboxing. So we simply do a HasComp check for each organ.
|
|
|
+ /// </remarks>
|
|
|
+ public bool TryGetBodyPartOrgans(
|
|
|
+ EntityUid uid,
|
|
|
+ Type type,
|
|
|
+ [NotNullWhen(true)] out List<(EntityUid Id, OrganComponent Organ)>? organs,
|
|
|
+ BodyPartComponent? part = null)
|
|
|
+ {
|
|
|
+ if (!Resolve(uid, ref part))
|
|
|
+ {
|
|
|
+ organs = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ var list = new List<(EntityUid Id, OrganComponent Organ)>();
|
|
|
+
|
|
|
+ foreach (var organ in GetPartOrgans(uid, part))
|
|
|
+ {
|
|
|
+ if (HasComp(organ.Id, type))
|
|
|
+ list.Add((organ.Id, organ.Component));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (list.Count != 0)
|
|
|
+ {
|
|
|
+ organs = list;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ organs = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool TryGetPartSlotContainerName(BodyPartType partType, out HashSet<string> containerNames)
|
|
|
+ {
|
|
|
+ containerNames = partType switch
|
|
|
+ {
|
|
|
+ BodyPartType.Hand => new() { "gloves" },
|
|
|
+ BodyPartType.Foot => new() { "shoes" },
|
|
|
+ BodyPartType.Head => new() { "eyes", "ears", "head", "mask" },
|
|
|
+ _ => new()
|
|
|
+ };
|
|
|
+ return containerNames.Count > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool TryGetPartFromSlotContainer(string slot, out BodyPartType? partType)
|
|
|
+ {
|
|
|
+ partType = slot switch
|
|
|
+ {
|
|
|
+ "gloves" => BodyPartType.Hand,
|
|
|
+ "shoes" => BodyPartType.Foot,
|
|
|
+ "eyes" or "ears" or "head" or "mask" => BodyPartType.Head,
|
|
|
+ _ => null
|
|
|
+ };
|
|
|
+ return partType is not null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int GetBodyPartCount(EntityUid bodyId, BodyPartType partType, BodyComponent? body = null)
|
|
|
+ {
|
|
|
+ if (!Resolve(bodyId, ref body, logMissing: false))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ int count = 0;
|
|
|
+ foreach (var part in GetBodyChildren(bodyId, body))
|
|
|
+ {
|
|
|
+ if (part.Component.PartType == partType)
|
|
|
+ count++;
|
|
|
+ }
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
+ public string GetSlotFromBodyPart(BodyPartComponent? part)
|
|
|
+ {
|
|
|
+ var slotName = "";
|
|
|
+
|
|
|
+ if (part is null)
|
|
|
+ return slotName;
|
|
|
+
|
|
|
+ if (part.SlotId != "")
|
|
|
+ slotName = part.SlotId;
|
|
|
+ else
|
|
|
+ slotName = part.PartType.ToString().ToLower();
|
|
|
+
|
|
|
+ if (part.Symmetry != BodyPartSymmetry.None)
|
|
|
+ return $"{part.Symmetry.ToString().ToLower()} {slotName}";
|
|
|
+ else
|
|
|
+ return slotName;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Shitmed Change End
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets the parent body part and all immediate child body parts for the partId.
|
|
|
/// </summary>
|