using Content.Shared.Chat; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Hypospray.Events; using Content.Shared.Climbing.Events; using Content.Shared.Damage; using Content.Shared.Electrocution; using Content.Shared.Explosion; using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Gravity; using Content.Shared.IdentityManagement.Components; using Content.Shared.Inventory.Events; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; using Content.Shared.NameModifier.EntitySystems; using Content.Shared.Overlays; using Content.Shared.Radio; using Content.Shared.Slippery; using Content.Shared.Strip.Components; using Content.Shared.Temperature; using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Events; namespace Content.Shared.Inventory; public partial class InventorySystem { public void InitializeRelay() { SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); // by-ref events SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); // Eye/vision events SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); // ComponentActivatedClientSystems SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(OnGetEquipmentVerbs); } protected void RefRelayInventoryEvent(EntityUid uid, InventoryComponent component, ref T args) where T : IInventoryRelayEvent { RelayEvent((uid, component), ref args); } protected void RelayInventoryEvent(EntityUid uid, InventoryComponent component, T args) where T : IInventoryRelayEvent { RelayEvent((uid, component), args); } public void RelayEvent(Entity inventory, ref T args) where T : IInventoryRelayEvent { if (args.TargetSlots == SlotFlags.NONE) return; // this copies the by-ref event if it is a struct var ev = new InventoryRelayedEvent(args); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); while (enumerator.NextItem(out var item)) { RaiseLocalEvent(item, ev); } // and now we copy it back args = ev.Args; } public void RelayEvent(Entity inventory, T args) where T : IInventoryRelayEvent { if (args.TargetSlots == SlotFlags.NONE) return; var ev = new InventoryRelayedEvent(args); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); while (enumerator.NextItem(out var item)) { RaiseLocalEvent(item, ev); } } private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent args) { // Automatically relay stripping related verbs to all equipped clothing. var ev = new InventoryRelayedEvent>(args); var enumerator = new InventorySlotEnumerator(component); while (enumerator.NextItem(out var item, out var slotDef)) { if (!_strippable.IsStripHidden(slotDef, args.User) || args.User == uid) RaiseLocalEvent(item, ev); } } } /// /// Event wrapper for relayed events. /// /// /// This avoids nested inventory relays, and makes it easy to have certain events only handled by the initial /// target entity. E.g. health based movement speed modifiers should not be handled by a hat, even if that hat /// happens to be a dead mouse. Clothing that wishes to modify movement speed must subscribe to /// InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent> /// public sealed class InventoryRelayedEvent : EntityEventArgs { public TEvent Args; public InventoryRelayedEvent(TEvent args) { Args = args; } } public interface IClothingSlots { SlotFlags Slots { get; } } /// /// Events that should be relayed to inventory slots should implement this interface. /// public interface IInventoryRelayEvent { /// /// What inventory slots should this event be relayed to, if any? /// /// /// In general you may want to exclude , given that those items are not truly /// "equipped" by the user. /// public SlotFlags TargetSlots { get; } }