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;
+
/// <summary>
/// The entity performing the action. NOT necessarily the one actually "receiving" the equipment.
/// </summary>
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;
+
/// <summary>
/// The entity performing the action. NOT necessarily the same as the entity whose equipment is being removed..
/// </summary>
SubscribeLocalEvent<InventoryComponent, SelfBeforeClimbEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, CoefficientQueryEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, ZombificationResistanceQueryEvent>(RelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, IsEquippingTargetAttemptEvent>(RelayInventoryEvent);
+ SubscribeLocalEvent<InventoryComponent, IsUnequippingTargetAttemptEvent>(RelayInventoryEvent);
// by-ref events
SubscribeLocalEvent<InventoryComponent, RefreshFrictionModifiersEvent>(RefRelayInventoryEvent);
--- /dev/null
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Inventory;
+
+/// <summary>
+/// Used to prevent items from being unequipped and equipped from slots that are listed in <see cref="Slots"/>.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SlotBlockSystem))]
+public sealed partial class SlotBlockComponent : Component
+{
+ /// <summary>
+ /// Slots that this entity should block.
+ /// </summary>
+ [DataField(required: true), AutoNetworkedField]
+ public SlotFlags Slots = SlotFlags.NONE;
+}
--- /dev/null
+using Content.Shared.Inventory.Events;
+
+namespace Content.Shared.Inventory;
+
+/// <summary>
+/// Handles prevention of items being unequipped and equipped from slots that are blocked by <see cref="SlotBlockComponent"/>.
+/// </summary>
+public sealed partial class SlotBlockSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<SlotBlockComponent, InventoryRelayedEvent<IsEquippingTargetAttemptEvent>>(OnEquipAttempt);
+ SubscribeLocalEvent<SlotBlockComponent, InventoryRelayedEvent<IsUnequippingTargetAttemptEvent>>(OnUnequipAttempt);
+ }
+
+ private void OnEquipAttempt(Entity<SlotBlockComponent> ent, ref InventoryRelayedEvent<IsEquippingTargetAttemptEvent> 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<SlotBlockComponent> ent, ref InventoryRelayedEvent<IsUnequippingTargetAttemptEvent> 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();
+ }
+}
--- /dev/null
+slot-block-component-blocked = This slot is blocked by {$item}!
id: ClothingHeadEVAHelmetBase
name: base space helmet
components:
+ - type: SlotBlock
+ slots: [ears, eyes, mask]
- type: BreathMask
- type: Item
size: Normal
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
- 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
id: ClothingOuterHardsuitBase
name: base hardsuit
components:
+ - type: SlotBlock
+ slots: [innerclothing, feet]
- type: PressureProtection
highPressureMultiplier: 0.3
lowPressureMultiplier: 1000
id: ClothingOuterEVASuitBase
name: base EVA Suit
components:
+ - type: SlotBlock
+ slots: [innerclothing, feet]
- type: PressureProtection
highPressureMultiplier: 0.6
lowPressureMultiplier: 1000