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.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);
}

View 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);

View 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)}");
});

View 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!;
public override void Initialize()
{
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
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);
ghostOnMove.MustBeDead = HasComp<MobStateComponent>(newEntity); // Don't ghost living players out of their bodies.
var ghostOnMove = EnsureComp<GhostOnMoveComponent>(newEntity);
if (HasComp<BodyComponent>(newEntity))
ghostOnMove.MustBeDead = true;
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();
}
}

View 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);
}

View 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;

View 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))

View 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

View 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) ||

View 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);
}
}

View 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)

View 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

View 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

View File

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

View 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

View File

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