diff --git a/Content.Shared/Inventory/Events/EquipAttemptEvents.cs b/Content.Shared/Inventory/Events/EquipAttemptEvents.cs
index af0961e075..2914dd469e 100644
--- a/Content.Shared/Inventory/Events/EquipAttemptEvents.cs
+++ b/Content.Shared/Inventory/Events/EquipAttemptEvents.cs
@@ -1,8 +1,10 @@
namespace Content.Shared.Inventory.Events;
public abstract class EquipAttemptBase(EntityUid equipee, EntityUid equipTarget, EntityUid equipment,
- SlotDefinition slotDefinition) : CancellableEntityEventArgs
+ SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent
{
+ public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
+
///
/// The entity performing the action. NOT necessarily the one actually "receiving" the equipment.
///
diff --git a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs
index 4824556753..a74b3e2d7c 100644
--- a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs
+++ b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs
@@ -1,8 +1,10 @@
namespace Content.Shared.Inventory.Events;
public abstract class UnequipAttemptEventBase(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment,
- SlotDefinition slotDefinition) : CancellableEntityEventArgs
+ SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent
{
+ public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET;
+
///
/// The entity performing the action. NOT necessarily the same as the entity whose equipment is being removed..
///
diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs
index 1ab9478a8d..84f2dc374b 100644
--- a/Content.Shared/Inventory/InventorySystem.Relay.cs
+++ b/Content.Shared/Inventory/InventorySystem.Relay.cs
@@ -48,6 +48,8 @@ public partial class InventorySystem
SubscribeLocalEvent(RelayInventoryEvent);
SubscribeLocalEvent(RelayInventoryEvent);
SubscribeLocalEvent(RelayInventoryEvent);
+ SubscribeLocalEvent(RelayInventoryEvent);
+ SubscribeLocalEvent(RelayInventoryEvent);
// by-ref events
SubscribeLocalEvent(RefRelayInventoryEvent);
diff --git a/Content.Shared/Inventory/SlotBlockComponent.cs b/Content.Shared/Inventory/SlotBlockComponent.cs
new file mode 100644
index 0000000000..dde706addf
--- /dev/null
+++ b/Content.Shared/Inventory/SlotBlockComponent.cs
@@ -0,0 +1,16 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Inventory;
+
+///
+/// Used to prevent items from being unequipped and equipped from slots that are listed in .
+///
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SlotBlockSystem))]
+public sealed partial class SlotBlockComponent : Component
+{
+ ///
+ /// Slots that this entity should block.
+ ///
+ [DataField(required: true), AutoNetworkedField]
+ public SlotFlags Slots = SlotFlags.NONE;
+}
diff --git a/Content.Shared/Inventory/SlotBlockSystem.cs b/Content.Shared/Inventory/SlotBlockSystem.cs
new file mode 100644
index 0000000000..9ac3361cb9
--- /dev/null
+++ b/Content.Shared/Inventory/SlotBlockSystem.cs
@@ -0,0 +1,35 @@
+using Content.Shared.Inventory.Events;
+
+namespace Content.Shared.Inventory;
+
+///
+/// Handles prevention of items being unequipped and equipped from slots that are blocked by .
+///
+public sealed partial class SlotBlockSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent>(OnEquipAttempt);
+ SubscribeLocalEvent>(OnUnequipAttempt);
+ }
+
+ private void OnEquipAttempt(Entity ent, ref InventoryRelayedEvent args)
+ {
+ if (args.Args.Cancelled || (args.Args.SlotFlags & ent.Comp.Slots) == 0)
+ return;
+
+ args.Args.Reason = Loc.GetString("slot-block-component-blocked", ("item", ent));
+ args.Args.Cancel();
+ }
+
+ private void OnUnequipAttempt(Entity ent, ref InventoryRelayedEvent args)
+ {
+ if (args.Args.Cancelled || (args.Args.SlotFlags & ent.Comp.Slots) == 0)
+ return;
+
+ args.Args.Reason = Loc.GetString("slot-block-component-blocked", ("item", ent));
+ args.Args.Cancel();
+ }
+}
diff --git a/Resources/Locale/en-US/inventory/components/slot-block-component.ftl b/Resources/Locale/en-US/inventory/components/slot-block-component.ftl
new file mode 100644
index 0000000000..55f870812b
--- /dev/null
+++ b/Resources/Locale/en-US/inventory/components/slot-block-component.ftl
@@ -0,0 +1 @@
+slot-block-component-blocked = This slot is blocked by {$item}!
diff --git a/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml b/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml
index 646a555037..8c52579188 100644
--- a/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml
+++ b/Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml
@@ -105,6 +105,8 @@
id: ClothingHeadEVAHelmetBase
name: base space helmet
components:
+ - type: SlotBlock
+ slots: [ears, eyes, mask]
- type: BreathMask
- type: Item
size: Normal
@@ -140,6 +142,8 @@
name: base hardsuit helmet
categories: [ HideSpawnMenu ]
components:
+ - type: SlotBlock
+ slots: [ears, eyes, mask]
- type: BreathMask
- type: Sprite
state: icon # default state used by most inheritors
@@ -181,11 +185,13 @@
- type: entity
abstract: true
# No parent since it isn't an item
- # must parent both this and the desired helmet base when using
+ # must parent both this and the desired helmet base when using
id: ClothingHeadSuitWithLightBase
name: base hardsuit helmet with light
categories: [ HideSpawnMenu ]
components:
+ - type: SlotBlock
+ slots: [ears, eyes, mask]
- type: Sprite
layers:
- state: icon
diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml
index 1ee9b76588..ef2119fd8d 100644
--- a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml
+++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml
@@ -105,6 +105,8 @@
id: ClothingOuterHardsuitBase
name: base hardsuit
components:
+ - type: SlotBlock
+ slots: [innerclothing, feet]
- type: PressureProtection
highPressureMultiplier: 0.3
lowPressureMultiplier: 1000
@@ -149,6 +151,8 @@
id: ClothingOuterEVASuitBase
name: base EVA Suit
components:
+ - type: SlotBlock
+ slots: [innerclothing, feet]
- type: PressureProtection
highPressureMultiplier: 0.6
lowPressureMultiplier: 1000