Miscellaneous Body Decoupling (#38958)

This commit is contained in:
Nemanja
2025-08-06 15:01:20 -04:00
committed by GitHub
parent 2e0b11ea51
commit 9872a28d7f
16 changed files with 83 additions and 78 deletions

View File

@@ -1,6 +1,6 @@
using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Effects; using Content.Shared.Anomaly.Effects;
using Content.Shared.Body.Components; using Content.Shared.Humanoid;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
namespace Content.Client.Anomaly.Effects; namespace Content.Client.Anomaly.Effects;
@@ -25,9 +25,8 @@ public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap); var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap);
if (TryComp<BodyComponent>(ent, out var body) && if (TryComp<HumanoidAppearanceComponent>(ent, out var humanoidAppearance) &&
body.Prototype is not null && ent.Comp.SpeciesSprites.TryGetValue(humanoidAppearance.Species, out var speciesSprite))
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
{ {
_sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite); _sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite);
} }

View File

@@ -1,8 +1,8 @@
using System.Numerics; using System.Numerics;
using Content.Shared.Body.Components;
using Content.Shared.CardboardBox; using Content.Shared.CardboardBox;
using Content.Shared.CardboardBox.Components; using Content.Shared.CardboardBox.Components;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -15,13 +15,13 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
[Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly SpriteSystem _sprite = default!;
private EntityQuery<BodyComponent> _bodyQuery; private EntityQuery<MobStateComponent> _mobStateQuery;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
_bodyQuery = GetEntityQuery<BodyComponent>(); _mobStateQuery = GetEntityQuery<MobStateComponent>();
SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect); SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
} }
@@ -66,8 +66,8 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null)) if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
continue; continue;
// no effect for anything too exotic // no effect for non-mobs that have MobMover, such as mechs and vehicles.
if (!_bodyQuery.HasComp(mob)) if (!_mobStateQuery.HasComp(mob))
continue; continue;
var ent = Spawn(box.Effect, mapPos); var ent = Spawn(box.Effect, mapPos);

View File

@@ -1,11 +1,9 @@
#nullable enable #nullable enable
using Content.Server.Cuffs; using Content.Server.Cuffs;
using Content.Shared.Body.Components;
using Content.Shared.Cuffs.Components; using Content.Shared.Cuffs.Components;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Robust.Server.Console; using Robust.Server.Console;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
{ {
@@ -22,9 +20,15 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
components: components:
- type: Cuffable - type: Cuffable
- type: Hands - type: Hands
hands:
hand_right:
location: Right
hand_left:
location: Left
sortedHands:
- hand_right
- hand_left
- type: ComplexInteraction - type: ComplexInteraction
- type: Body
prototype: Human
- type: entity - type: entity
name: HandcuffsDummy name: HandcuffsDummy
@@ -47,7 +51,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
HandsComponent hands = default!; HandsComponent hands = default!;
var entityManager = server.ResolveDependency<IEntityManager>(); var entityManager = server.ResolveDependency<IEntityManager>();
var mapManager = server.ResolveDependency<IMapManager>();
var host = server.ResolveDependency<IServerConsoleHost>(); var host = server.ResolveDependency<IServerConsoleHost>();
var map = await pair.CreateTestMap(); 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 cuffed!), $"Human has no {nameof(CuffableComponent)}");
Assert.That(entityManager.TryGetComponent(human, out hands!), $"Human has no {nameof(HandsComponent)}"); 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(cuffs, out HandcuffComponent? _), $"Handcuff has no {nameof(HandcuffComponent)}");
Assert.That(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}"); Assert.That(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}");
}); });

View File

@@ -1,47 +1,46 @@
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Ghost.Components; using Content.Server.Ghost.Components;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events; using Content.Shared.Body.Events;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Mind.Components; using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Pointing; 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!;
public override void Initialize()
{ {
[Dependency] private readonly SharedMindSystem _mindSystem = default!; base.Initialize();
public override void Initialize() SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
{ SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
base.Initialize(); SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
}
SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid)); private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody)); {
SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt); if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
} return;
private void HandleMind(EntityUid newEntity, EntityUid oldEntity) EnsureComp<MindContainerComponent>(newEntity);
{ EnsureComp<MindContainerComponent>(oldEntity);
if (TerminatingOrDeleted(newEntity) || TerminatingOrDeleted(oldEntity))
return;
EnsureComp<MindContainerComponent>(newEntity); var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity);
EnsureComp<MindContainerComponent>(oldEntity); ghostOnMove.MustBeDead = HasComp<MobStateComponent>(newEntity); // Don't ghost living players out of their bodies.
var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity); if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind))
if (HasComp<BodyComponent>(newEntity)) return;
ghostOnMove.MustBeDead = true;
if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind)) _mindSystem.TransferTo(mindId, newEntity, mind: mind);
return; }
_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();
}
} }
} }

View File

@@ -90,19 +90,22 @@ public sealed class PricingSystem : EntitySystem
if (args.Handled) if (args.Handled)
return; 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; return;
} }
// TODO: Better handling of missing. var partPenalty = 0.0;
var partList = _bodySystem.GetBodyChildren(uid, body).ToList(); if (TryComp<BodyComponent>(uid, out var body))
var totalPartsPresent = partList.Sum(_ => 1); {
var totalParts = partList.Count; var partList = _bodySystem.GetBodyChildren(uid, body).ToList();
var totalPartsPresent = partList.Sum(_ => 1);
var totalParts = partList.Count;
var partRatio = totalPartsPresent / (double) totalParts; var partRatio = totalPartsPresent / (double) totalParts;
var partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty; partPenalty = component.Price * (1 - partRatio) * component.MissingBodyPartPenalty;
}
args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty); args.Price += (component.Price - partPenalty) * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty);
} }

View File

@@ -117,19 +117,18 @@ public sealed class SharpSystem : EntitySystem
popupEnt = Spawn(proto, coords.Offset(_robustRandom.NextVector2(0.25f))); 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. // only show a big popup when butchering living things.
var popupType = PopupType.Small; // Meant to differentiate cutting up clothes and cutting up your boss.
if (hasBody) var popupType = HasComp<MobStateComponent>(args.Args.Target.Value)
popupType = PopupType.LargeCaution; ? PopupType.LargeCaution
: PopupType.Small;
_popupSystem.PopupEntity(Loc.GetString("butcherable-knife-butchered-success", ("target", args.Args.Target.Value), ("knife", Identity.Entity(uid, EntityManager))), _popupSystem.PopupEntity(Loc.GetString("butcherable-knife-butchered-success", ("target", args.Args.Target.Value), ("knife", Identity.Entity(uid, EntityManager))),
popupEnt, args.Args.User, popupType); popupEnt,
args.Args.User,
if (hasBody) popupType);
_bodySystem.GibBody(args.Args.Target.Value, body: body);
_bodySystem.GibBody(args.Args.Target.Value); // does nothing if ent can't be gibbed
_destructibleSystem.DestroyEntity(args.Args.Target.Value); _destructibleSystem.DestroyEntity(args.Args.Target.Value);
args.Handled = true; args.Handled = true;

View File

@@ -1,6 +1,6 @@
using Content.Server.Storage.Components; using Content.Server.Storage.Components;
using Content.Shared.Body.Components;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Mobs.Components;
using Content.Shared.Morgue; using Content.Shared.Morgue;
using Content.Shared.Morgue.Components; using Content.Shared.Morgue.Components;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
@@ -59,7 +59,7 @@ public sealed class MorgueSystem : EntitySystem
foreach (var ent in storage.Contents.ContainedEntities) foreach (var ent in storage.Contents.ContainedEntities)
{ {
if (!hasMob && HasComp<BodyComponent>(ent)) if (!hasMob && HasComp<MobStateComponent>(ent))
hasMob = true; hasMob = true;
if (HasComp<ActorComponent>(ent)) if (HasComp<ActorComponent>(ent))

View File

@@ -1,5 +1,6 @@
using Content.Shared.Anomaly.Effects; using Content.Shared.Anomaly.Effects;
using Content.Shared.Body.Prototypes; using Content.Shared.Body.Prototypes;
using Content.Shared.Humanoid.Prototypes;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -62,7 +63,7 @@ public sealed partial class InnerBodyAnomalyComponent : Component
/// Ability to use unique sprites for different body types /// Ability to use unique sprites for different body types
/// </summary> /// </summary>
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public Dictionary<ProtoId<BodyPrototype>, SpriteSpecifier> SpeciesSprites = new(); public Dictionary<ProtoId<SpeciesPrototype>, SpriteSpecifier> SpeciesSprites = new();
/// <summary> /// <summary>
/// The key of the entity layer into which the sprite will be inserted /// The key of the entity layer into which the sprite will be inserted

View File

@@ -1,7 +1,6 @@
using System.Linq; using System.Linq;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components;
using Content.Shared.Climbing.Systems; using Content.Shared.Climbing.Systems;
using Content.Shared.Containers; using Content.Shared.Containers;
using Content.Shared.Database; using Content.Shared.Database;
@@ -15,6 +14,7 @@ using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Item; using Content.Shared.Item;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Power; using Content.Shared.Power;
@@ -450,7 +450,7 @@ public abstract class SharedDisposalUnitSystem : EntitySystem
return false; return false;
var storable = HasComp<ItemComponent>(entity); var storable = HasComp<ItemComponent>(entity);
if (!storable && !HasComp<BodyComponent>(entity)) if (!storable && !HasComp<MobStateComponent>(entity))
return false; return false;
if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) || if (_whitelistSystem.IsBlacklistPass(component.Blacklist, entity) ||

View File

@@ -1,4 +1,3 @@
using Content.Shared.Body.Components;
using Content.Shared.Morgue.Components; using Content.Shared.Morgue.Components;
using Content.Shared.Standing; using Content.Shared.Standing;
using Content.Shared.Storage.Components; using Content.Shared.Storage.Components;
@@ -20,7 +19,9 @@ public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem
{ {
foreach (var ent in args.Contents) 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); args.Contents.Remove(ent);
} }
} }

View File

@@ -16,6 +16,7 @@ using Content.Shared.Verbs;
using Content.Shared.Wall; using Content.Shared.Wall;
using Content.Shared.Whitelist; using Content.Shared.Whitelist;
using Content.Shared.ActionBlocker; using Content.Shared.ActionBlocker;
using Content.Shared.Mobs.Components;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
@@ -355,7 +356,7 @@ public abstract class SharedEntityStorageSystem : EntitySystem
return _whitelistSystem.IsValid(component.Whitelist, toInsert); return _whitelistSystem.IsValid(component.Whitelist, toInsert);
// The inserted entity must be a mob or an item. // 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) public bool TryOpenStorage(EntityUid user, EntityUid target, bool silent = false)

View File

@@ -13,7 +13,7 @@
- type: Implanter - type: Implanter
whitelist: whitelist:
components: components:
- Body # no chair microbomb - MobState # no chair microbomb
blacklist: blacklist:
components: components:
- Guardian # no holoparasite macrobomb wombo combo - Guardian # no holoparasite macrobomb wombo combo

View File

@@ -28,7 +28,7 @@
components: components:
- Anchorable - Anchorable
- Item - Item
- Body - MobState
- type: Explosive # Powerful explosion in a large radius. Will break underplating. - type: Explosive # Powerful explosion in a large radius. Will break underplating.
explosionType: DemolitionCharge explosionType: DemolitionCharge
totalIntensity: 360 totalIntensity: 360

View File

@@ -93,7 +93,7 @@
disableWhenFirstOpened: true disableWhenFirstOpened: true
whitelist: whitelist:
components: components:
- Body - MobState
- type: LockVisuals - type: LockVisuals
stateLocked: cursed_door stateLocked: cursed_door
stateUnlocked: decursed_door stateUnlocked: decursed_door
@@ -191,7 +191,7 @@
- type: WhitelistTriggerCondition - type: WhitelistTriggerCondition
userWhitelist: userWhitelist:
components: components:
- Body - MobState
- type: entity - type: entity
id: ProjectilePolyboltMonkey id: ProjectilePolyboltMonkey
@@ -205,7 +205,7 @@
- type: WhitelistTriggerCondition - type: WhitelistTriggerCondition
userWhitelist: userWhitelist:
components: components:
- Body - MobState
- type: entity - type: entity
id: ProjectilePolyboltDoor id: ProjectilePolyboltDoor
@@ -275,7 +275,7 @@
- type: WhitelistTriggerCondition - type: WhitelistTriggerCondition
userWhitelist: userWhitelist:
components: components:
- Body - MobState
- type: entity - type: entity
id: ProjectileIcicle id: ProjectileIcicle
@@ -305,4 +305,4 @@
- type: WhitelistTriggerCondition - type: WhitelistTriggerCondition
userWhitelist: userWhitelist:
components: components:
- Body - MobState

View File

@@ -14,7 +14,7 @@
- type: EntityTargetAction - type: EntityTargetAction
whitelist: whitelist:
components: components:
- Body # this also allows borgs because that supercode uses Body for no reason - MobState
- PAI # intended to mindswap pAIs and AIs - PAI # intended to mindswap pAIs and AIs
- StationAiCore - StationAiCore
event: !type:MindSwapSpellEvent event: !type:MindSwapSpellEvent

View File

@@ -12,7 +12,7 @@
- type: EntityTargetAction - type: EntityTargetAction
whitelist: whitelist:
components: components:
- Body - MobState
canTargetSelf: false canTargetSelf: false
- type: entity - type: entity