]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Miscellaneous Body Decoupling (#38958)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Wed, 6 Aug 2025 19:01:20 +0000 (15:01 -0400)
committerGitHub <noreply@github.com>
Wed, 6 Aug 2025 19:01:20 +0000 (15:01 -0400)
16 files changed:
Content.Client/Anomaly/Effects/ClientInnerBodySystem.cs
Content.Client/CardboardBox/CardboardBoxSystem.cs
Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs
Content.Server/Body/Systems/BrainSystem.cs
Content.Server/Cargo/Systems/PricingSystem.cs
Content.Server/Kitchen/EntitySystems/SharpSystem.cs
Content.Server/Morgue/MorgueSystem.cs
Content.Shared/Anomaly/Components/InnerBodyAnomalyComponent.cs
Content.Shared/Disposal/Unit/SharedDisposalUnitSystem.cs
Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs
Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
Resources/Prototypes/Entities/Objects/Misc/implanters.yml
Resources/Prototypes/Entities/Objects/Weapons/Bombs/spider.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
Resources/Prototypes/Magic/mindswap_spell.yml
Resources/Prototypes/Magic/touch_spells.yml

index 15ebc8a993d7c907a4e87d8c68ed887f40821d8c..d96980fb1d20ae6925c28dd96abfd0ddb47eca2a 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Shared.Anomaly.Components;
 using Content.Shared.Anomaly.Effects;
-using Content.Shared.Body.Components;
+using Content.Shared.Humanoid;
 using Robust.Client.GameObjects;
 
 namespace Content.Client.Anomaly.Effects;
@@ -25,9 +25,8 @@ public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
 
         var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap);
 
-        if (TryComp<BodyComponent>(ent, out var body) &&
-            body.Prototype is not null &&
-            ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
+        if (TryComp<HumanoidAppearanceComponent>(ent, out var humanoidAppearance) &&
+            ent.Comp.SpeciesSprites.TryGetValue(humanoidAppearance.Species, out var speciesSprite))
         {
             _sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite);
         }
index e52ce03a76765df03981ec14c448304e083749a3..ecebe167277be0b70ed7b8765e0dfd21af40e1dd 100644 (file)
@@ -1,8 +1,8 @@
 using System.Numerics;
-using Content.Shared.Body.Components;
 using Content.Shared.CardboardBox;
 using Content.Shared.CardboardBox.Components;
 using Content.Shared.Examine;
+using Content.Shared.Mobs.Components;
 using Content.Shared.Movement.Components;
 using Robust.Client.GameObjects;
 
@@ -15,13 +15,13 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
     [Dependency] private readonly ExamineSystemShared _examine = default!;
     [Dependency] private readonly SpriteSystem _sprite = default!;
 
-    private EntityQuery<BodyComponent> _bodyQuery;
+    private EntityQuery<MobStateComponent> _mobStateQuery;
 
     public override void Initialize()
     {
         base.Initialize();
 
-        _bodyQuery = GetEntityQuery<BodyComponent>();
+        _mobStateQuery = GetEntityQuery<MobStateComponent>();
 
         SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
     }
@@ -66,8 +66,8 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
             if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
                 continue;
 
-            // no effect for anything too exotic
-            if (!_bodyQuery.HasComp(mob))
+            // no effect for non-mobs that have MobMover, such as mechs and vehicles.
+            if (!_mobStateQuery.HasComp(mob))
                 continue;
 
             var ent = Spawn(box.Effect, mapPos);
index 2570e2246a66f705ebbac725d9ded19994540613..dae3203f9ff4259e4de4adc81100955a347bc41a 100644 (file)
@@ -1,11 +1,9 @@
 #nullable enable
 using Content.Server.Cuffs;
-using Content.Shared.Body.Components;
 using Content.Shared.Cuffs.Components;
 using Content.Shared.Hands.Components;
 using Robust.Server.Console;
 using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
 
 namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
 {
@@ -22,9 +20,15 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
   components:
   - type: Cuffable
   - type: Hands
+    hands:
+      hand_right:
+        location: Right
+      hand_left:
+        location: Left
+    sortedHands:
+    - hand_right
+    - hand_left
   - type: ComplexInteraction
-  - type: Body
-    prototype: Human
 
 - type: entity
   name: HandcuffsDummy
@@ -47,7 +51,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
             HandsComponent hands = default!;
 
             var entityManager = server.ResolveDependency<IEntityManager>();
-            var mapManager = server.ResolveDependency<IMapManager>();
             var host = server.ResolveDependency<IServerConsoleHost>();
 
             var map = await pair.CreateTestMap();
@@ -73,7 +76,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
                 {
                     Assert.That(entityManager.TryGetComponent(human, out cuffed!), $"Human has no {nameof(CuffableComponent)}");
                     Assert.That(entityManager.TryGetComponent(human, out hands!), $"Human has no {nameof(HandsComponent)}");
-                    Assert.That(entityManager.TryGetComponent(human, out BodyComponent? _), $"Human has no {nameof(BodyComponent)}");
                     Assert.That(entityManager.TryGetComponent(cuffs, out HandcuffComponent? _), $"Handcuff has no {nameof(HandcuffComponent)}");
                     Assert.That(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}");
                 });
index 86d2cb61ffe931a50c98233ae89a903ee1c903a6..e916849a8161a2222505386cbf57577f2a450b60 100644 (file)
@@ -1,47 +1,46 @@
 using Content.Server.Body.Components;
 using Content.Server.Ghost.Components;
-using Content.Shared.Body.Components;
 using Content.Shared.Body.Events;
 using Content.Shared.Mind;
 using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Components;
 using Content.Shared.Pointing;
 
-namespace Content.Server.Body.Systems
+namespace Content.Server.Body.Systems;
+
+public sealed class BrainSystem : EntitySystem
 {
-    public sealed class BrainSystem : EntitySystem
-    {
-        [Dependency] private readonly SharedMindSystem _mindSystem = default!;
+    [Dependency] private readonly SharedMindSystem _mindSystem = default!;
 
-        public override void Initialize()
-        {
-            base.Initialize();
+    public override void Initialize()
+    {
+        base.Initialize();
 
-            SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
-            SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
-            SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
-        }
+        SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
+        SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
+        SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
+    }
 
-        private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
-        {
-            if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
-                return;
+    private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
+    {
+        if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
+            return;
 
-            EnsureComp<MindContainerComponent>(newEntity);
-            EnsureComp<MindContainerComponent>(oldEntity);
+        EnsureComp<MindContainerComponent>(newEntity);
+        EnsureComp<MindContainerComponent>(oldEntity);
 
-            var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity);
-            if (HasComp<BodyComponent>(newEntity))
-                ghostOnMove.MustBeDead = true;
+        var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity);
+        ghostOnMove.MustBeDead = HasComp<MobStateComponent>(newEntity); // Don't ghost living players out of their bodies.
 
-            if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind))
-                return;
+        if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind))
+            return;
 
-            _mindSystem.TransferTo(mindId, newEntity, mind: mind);
-        }
+        _mindSystem.TransferTo(mindId, newEntity, mind: mind);
+    }
 
-        private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
-        {
-            args.Cancel();
-        }
+    private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
+    {
+        args.Cancel();
     }
 }
+
index 5e449eb8daac61c251896dba9ae27db8ed2444fd..24ee4ffaf2f9eae6e1307624c8862f298f192ba9 100644 (file)
@@ -90,19 +90,22 @@ public sealed class PricingSystem : EntitySystem
         if (args.Handled)
             return;
 
-        if (!TryComp<BodyComponent>(uid, out var body) || !TryComp<MobStateComponent>(uid, out var state))
+        if (!TryComp<MobStateComponent>(uid, out var state))
         {
-            Log.Error($"Tried to get the mob price of {ToPrettyString(uid)}, which has no {nameof(BodyComponent)} and no {nameof(MobStateComponent)}.");
+            Log.Error($"Tried to get the mob price of {ToPrettyString(uid)}, which has no {nameof(MobStateComponent)}.");
             return;
         }
 
-        // TODO: Better handling of missing.
-        var partList = _bodySystem.GetBodyChildren(uid, body).ToList();
-        var totalPartsPresent = partList.Sum(_ => 1);
-        var totalParts = partList.Count;
+        var partPenalty = 0.0;
+        if (TryComp<BodyComponent>(uid, out var body))
+        {
+            var partList = _bodySystem.GetBodyChildren(uid, body).ToList();
+            var totalPartsPresent = partList.Sum(_ => 1);
+            var totalParts = partList.Count;
 
-        var partRatio = totalPartsPresent / (double) totalParts;
-        var partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty;
+            var partRatio = totalPartsPresent / (double) totalParts;
+            partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty;
+        }
 
         args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty);
     }
index 0ba9d0990ad41e2b27d43e20b565850700ba1078..0275e4d1a78a4400ad829b516a21777ac3621a3f 100644 (file)
@@ -117,19 +117,18 @@ public sealed class SharpSystem : EntitySystem
             popupEnt = Spawn(proto, coords.Offset(_robustRandom.NextVector2(0.25f)));
         }
 
-        var hasBody = TryComp<BodyComponent>(args.Args.Target.Value, out var body);
-
         // only show a big popup when butchering living things.
-        var popupType = PopupType.Small;
-        if (hasBody)
-            popupType = PopupType.LargeCaution;
+        // Meant to differentiate cutting up clothes and cutting up your boss.
+        var popupType = HasComp<MobStateComponent>(args.Args.Target.Value)
+            ? PopupType.LargeCaution
+            : PopupType.Small;
 
         _popupSystem.PopupEntity(Loc.GetString("butcherable-knife-butchered-success", ("target", args.Args.Target.Value), ("knife", Identity.Entity(uid, EntityManager))),
-            popupEnt, args.Args.User, popupType);
-
-        if (hasBody)
-            _bodySystem.GibBody(args.Args.Target.Value, body: body);
+            popupEnt,
+            args.Args.User,
+            popupType);
 
+        _bodySystem.GibBody(args.Args.Target.Value); // does nothing if ent can't be gibbed
         _destructibleSystem.DestroyEntity(args.Args.Target.Value);
 
         args.Handled = true;
index a07accf7772305b43b5c98e3d1e7a088d70baac5..92ed16a06b52490426bca0682046902e7f4fdfa2 100644 (file)
@@ -1,6 +1,6 @@
 using Content.Server.Storage.Components;
-using Content.Shared.Body.Components;
 using Content.Shared.Examine;
+using Content.Shared.Mobs.Components;
 using Content.Shared.Morgue;
 using Content.Shared.Morgue.Components;
 using Robust.Shared.Audio.Systems;
@@ -59,7 +59,7 @@ public sealed class MorgueSystem : EntitySystem
 
         foreach (var ent in storage.Contents.ContainedEntities)
         {
-            if (!hasMob && HasComp<BodyComponent>(ent))
+            if (!hasMob && HasComp<MobStateComponent>(ent))
                 hasMob = true;
 
             if (HasComp<ActorComponent>(ent))
index a7e07ae2b504c78fa1a14440fd5556ae99deb55e..dfc1d561c462b9b93ec92123155a2d25389ea2da 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.Anomaly.Effects;
 using Content.Shared.Body.Prototypes;
+using Content.Shared.Humanoid.Prototypes;
 using Robust.Shared.Audio;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
@@ -62,7 +63,7 @@ public sealed partial class InnerBodyAnomalyComponent : Component
     /// Ability to use unique sprites for different body types
     /// </summary>
     [DataField, AutoNetworkedField]
-    public Dictionary<ProtoId<BodyPrototype>, SpriteSpecifier> SpeciesSprites = new();
+    public Dictionary<ProtoId<SpeciesPrototype>, SpriteSpecifier> SpeciesSprites = new();
 
     /// <summary>
     /// The key of the entity layer into which the sprite will be inserted
index bdf8b5ba07e50f6c060b1a0b984d756712e0fe74..292cfd2b3cbd3121da42a9083111f373e989938c 100644 (file)
@@ -1,7 +1,6 @@
 using System.Linq;
 using Content.Shared.ActionBlocker;
 using Content.Shared.Administration.Logs;
-using Content.Shared.Body.Components;
 using Content.Shared.Climbing.Systems;
 using Content.Shared.Containers;
 using Content.Shared.Database;
@@ -15,6 +14,7 @@ using Content.Shared.Hands.EntitySystems;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction;
 using Content.Shared.Item;
+using Content.Shared.Mobs.Components;
 using Content.Shared.Movement.Events;
 using Content.Shared.Popups;
 using Content.Shared.Power;
@@ -450,7 +450,7 @@ public abstract class SharedDisposalUnitSystem : EntitySystem
             return false;
 
         var storable = HasComp<ItemComponent>(entity);
-        if (!storable && !HasComp<BodyComponent>(entity))
+        if (!storable && !HasComp<MobStateComponent>(entity))
             return false;
 
         if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) ||
index 9341168ba3efb89df4cb7f016d7786144b8ef882..630135f36ae9b9cd56e5f6b3f9e2cc8f27c313c1 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Shared.Body.Components;
 using Content.Shared.Morgue.Components;
 using Content.Shared.Standing;
 using Content.Shared.Storage.Components;
@@ -20,7 +19,9 @@ public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem
     {
         foreach (var ent in args.Contents)
         {
-            if (HasComp<BodyComponent>(ent) && !_standing.IsDown(ent))
+            // Explicitly check for standing state component, as entities without it will return false for IsDown()
+            // which prevents inserting any kind of non-mobs into this container (which is unintended)
+            if (TryComp<StandingStateComponent>(ent, out var standingState) && !_standing.IsDown(ent, standingState))
                 args.Contents.Remove(ent);
         }
     }
index 2b73a9349fc1b9619db3b9dba03fd3b35c833e6c..fd7cb87b2aaf2e808f016b546822f0851db7b065 100644 (file)
@@ -16,6 +16,7 @@ using Content.Shared.Verbs;
 using Content.Shared.Wall;
 using Content.Shared.Whitelist;
 using Content.Shared.ActionBlocker;
+using Content.Shared.Mobs.Components;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.GameStates;
@@ -355,7 +356,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
             return _whitelistSystem.IsValid(component.Whitelist, toInsert);
 
         // The inserted entity must be a mob or an item.
-        return HasComp<BodyComponent>(toInsert) || HasComp<ItemComponent>(toInsert);
+        return HasComp<MobStateComponent>(toInsert) || HasComp<ItemComponent>(toInsert);
     }
 
     public bool TryOpenStorage(EntityUid user, EntityUid target, bool silent = false)
index 79432e6fc1c72e0f80f1778e1552756558249570..e1918ef5e604b6c2525d9fae934517599de4219b 100644 (file)
@@ -13,7 +13,7 @@
     - type: Implanter
       whitelist:
         components:
-        - Body # no chair microbomb
+        - MobState # no chair microbomb
       blacklist:
         components:
         - Guardian # no holoparasite macrobomb wombo combo
index d00297a7236796efd1381f1b338f1382acde02e1..3d7991cf0fcc1a0539c77df5eb48f3522ef80913 100644 (file)
@@ -28,7 +28,7 @@
       components:
       - Anchorable
       - Item
-      - Body
+      - MobState
   - type: Explosive # Powerful explosion in a large radius. Will break underplating.
     explosionType: DemolitionCharge
     totalIntensity: 360
index 9e2b6d76c189a8890bc6a449c0965732d91aabee..d1b50429f95e96ecc2795df98da288e81cb7fb6b 100644 (file)
@@ -93,7 +93,7 @@
     disableWhenFirstOpened: true
     whitelist:
       components:
-        - Body
+        - MobState
   - type: LockVisuals
     stateLocked: cursed_door
     stateUnlocked: decursed_door
   - type: WhitelistTriggerCondition
     userWhitelist:
       components:
-      - Body
+      - MobState
 
 - type: entity
   id: ProjectilePolyboltMonkey
   - type: WhitelistTriggerCondition
     userWhitelist:
       components:
-      - Body
+      - MobState
 
 - type: entity
   id: ProjectilePolyboltDoor
   - type: WhitelistTriggerCondition
     userWhitelist:
       components:
-      - Body
+      - MobState
 
 - type: entity
   id: ProjectileIcicle
   - type: WhitelistTriggerCondition
     userWhitelist:
       components:
-      - Body
+      - MobState
index 8039f1207410b1bd28fa5e09808e835ae1dfc359..0a28a9683b746600d74d490e59de0160e3cc7063 100644 (file)
@@ -14,7 +14,7 @@
   - type: EntityTargetAction
     whitelist:
       components:
-      - Body # this also allows borgs because that supercode uses Body for no reason
+      - MobState
       - PAI # intended to mindswap pAIs and AIs
       - StationAiCore
     event: !type:MindSwapSpellEvent
index ba62a76421dba87572c79676fe42c968616f140f..f3b44eabb17c829cab3fb60c0e628cb8320bbca5 100644 (file)
@@ -12,7 +12,7 @@
   - type: EntityTargetAction
     whitelist:
       components:
-      - Body
+      - MobState
     canTargetSelf: false
 
 - type: entity