InventorySystem.Relay.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using Content.Shared.Armor;
  2. using Content.Shared.Chat;
  3. using Content.Shared.Chemistry;
  4. using Content.Shared.Chemistry.Hypospray.Events;
  5. using Content.Shared.Climbing.Events;
  6. using Content.Shared.Damage;
  7. using Content.Shared.Electrocution;
  8. using Content.Shared.Explosion;
  9. using Content.Shared.Eye.Blinding.Systems;
  10. using Content.Shared.Gravity;
  11. using Content.Shared.IdentityManagement.Components;
  12. using Content.Shared.Inventory.Events;
  13. using Content.Shared.Movement.Events;
  14. using Content.Shared.Movement.Systems;
  15. using Content.Shared.NameModifier.EntitySystems;
  16. using Content.Shared.Overlays;
  17. using Content.Shared.Radio;
  18. using Content.Shared.Slippery;
  19. using Content.Shared.Strip.Components;
  20. using Content.Shared.Temperature;
  21. using Content.Shared.Verbs;
  22. using Content.Shared.Weapons.Ranged.Events;
  23. namespace Content.Shared.Inventory;
  24. public partial class InventorySystem
  25. {
  26. /// <summary>
  27. /// Subscribes the inventory component to a range of events, enabling automatic relaying of relevant events to equipped items in specified inventory slots.
  28. /// </summary>
  29. public void InitializeRelay()
  30. {
  31. SubscribeLocalEvent<InventoryComponent, DamageModifyEvent>(RelayInventoryEvent);
  32. SubscribeLocalEvent<InventoryComponent, ElectrocutionAttemptEvent>(RelayInventoryEvent);
  33. SubscribeLocalEvent<InventoryComponent, SlipAttemptEvent>(RelayInventoryEvent);
  34. SubscribeLocalEvent<InventoryComponent, RefreshMovementSpeedModifiersEvent>(RelayInventoryEvent);
  35. SubscribeLocalEvent<InventoryComponent, BeforeStripEvent>(RelayInventoryEvent);
  36. SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent);
  37. SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
  38. SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
  39. SubscribeLocalEvent<InventoryComponent, RefreshNameModifiersEvent>(RelayInventoryEvent);
  40. SubscribeLocalEvent<InventoryComponent, TransformSpeakerNameEvent>(RelayInventoryEvent);
  41. SubscribeLocalEvent<InventoryComponent, SelfBeforeHyposprayInjectsEvent>(RelayInventoryEvent);
  42. SubscribeLocalEvent<InventoryComponent, TargetBeforeHyposprayInjectsEvent>(RelayInventoryEvent);
  43. SubscribeLocalEvent<InventoryComponent, SelfBeforeGunShotEvent>(RelayInventoryEvent);
  44. SubscribeLocalEvent<InventoryComponent, SelfBeforeClimbEvent>(RelayInventoryEvent);
  45. SubscribeLocalEvent<InventoryComponent, CoefficientQueryEvent>(RelayInventoryEvent);
  46. // by-ref events
  47. SubscribeLocalEvent<InventoryComponent, GetExplosionResistanceEvent>(RefRelayInventoryEvent);
  48. SubscribeLocalEvent<InventoryComponent, IsWeightlessEvent>(RefRelayInventoryEvent);
  49. SubscribeLocalEvent<InventoryComponent, GetSpeedModifierContactCapEvent>(RefRelayInventoryEvent);
  50. SubscribeLocalEvent<InventoryComponent, GetSlowedOverSlipperyModifierEvent>(RefRelayInventoryEvent);
  51. SubscribeLocalEvent<InventoryComponent, ModifySlowOnDamageSpeedEvent>(RefRelayInventoryEvent);
  52. // Eye/vision events
  53. SubscribeLocalEvent<InventoryComponent, CanSeeAttemptEvent>(RelayInventoryEvent);
  54. SubscribeLocalEvent<InventoryComponent, GetEyeProtectionEvent>(RelayInventoryEvent);
  55. SubscribeLocalEvent<InventoryComponent, GetBlurEvent>(RelayInventoryEvent);
  56. SubscribeLocalEvent<InventoryComponent, SolutionScanEvent>(RelayInventoryEvent);
  57. // ComponentActivatedClientSystems
  58. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowJobIconsComponent>>(RefRelayInventoryEvent);
  59. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowHealthBarsComponent>>(RefRelayInventoryEvent);
  60. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowHealthIconsComponent>>(RefRelayInventoryEvent);
  61. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowHungerIconsComponent>>(RefRelayInventoryEvent);
  62. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowThirstIconsComponent>>(RefRelayInventoryEvent);
  63. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowMindShieldIconsComponent>>(RefRelayInventoryEvent);
  64. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RefRelayInventoryEvent);
  65. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RefRelayInventoryEvent);
  66. SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowFactionIconsComponent>>(RefRelayInventoryEvent);
  67. SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs);
  68. }
  69. protected void RefRelayInventoryEvent<T>(EntityUid uid, InventoryComponent component, ref T args) where T : IInventoryRelayEvent
  70. {
  71. RelayEvent((uid, component), ref args);
  72. }
  73. protected void RelayInventoryEvent<T>(EntityUid uid, InventoryComponent component, T args) where T : IInventoryRelayEvent
  74. {
  75. RelayEvent((uid, component), args);
  76. }
  77. public void RelayEvent<T>(Entity<InventoryComponent> inventory, ref T args) where T : IInventoryRelayEvent
  78. {
  79. if (args.TargetSlots == SlotFlags.NONE)
  80. return;
  81. // this copies the by-ref event if it is a struct
  82. var ev = new InventoryRelayedEvent<T>(args);
  83. var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots);
  84. while (enumerator.NextItem(out var item))
  85. {
  86. RaiseLocalEvent(item, ev);
  87. }
  88. // and now we copy it back
  89. args = ev.Args;
  90. }
  91. public void RelayEvent<T>(Entity<InventoryComponent> inventory, T args) where T : IInventoryRelayEvent
  92. {
  93. if (args.TargetSlots == SlotFlags.NONE)
  94. return;
  95. var ev = new InventoryRelayedEvent<T>(args);
  96. var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots);
  97. while (enumerator.NextItem(out var item))
  98. {
  99. RaiseLocalEvent(item, ev);
  100. }
  101. }
  102. private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent<EquipmentVerb> args)
  103. {
  104. // Automatically relay stripping related verbs to all equipped clothing.
  105. var ev = new InventoryRelayedEvent<GetVerbsEvent<EquipmentVerb>>(args);
  106. var enumerator = new InventorySlotEnumerator(component);
  107. while (enumerator.NextItem(out var item, out var slotDef))
  108. {
  109. if (!_strippable.IsStripHidden(slotDef, args.User) || args.User == uid)
  110. RaiseLocalEvent(item, ev);
  111. }
  112. }
  113. }
  114. /// <summary>
  115. /// Event wrapper for relayed events.
  116. /// </summary>
  117. /// <remarks>
  118. /// This avoids nested inventory relays, and makes it easy to have certain events only handled by the initial
  119. /// target entity. E.g. health based movement speed modifiers should not be handled by a hat, even if that hat
  120. /// happens to be a dead mouse. Clothing that wishes to modify movement speed must subscribe to
  121. /// InventoryRelayedEvent&lt;RefreshMovementSpeedModifiersEvent&gt;
  122. /// </remarks>
  123. public sealed class InventoryRelayedEvent<TEvent> : EntityEventArgs
  124. {
  125. public TEvent Args;
  126. public InventoryRelayedEvent(TEvent args)
  127. {
  128. Args = args;
  129. }
  130. }
  131. public interface IClothingSlots
  132. {
  133. SlotFlags Slots { get; }
  134. }
  135. /// <summary>
  136. /// Events that should be relayed to inventory slots should implement this interface.
  137. /// </summary>
  138. public interface IInventoryRelayEvent
  139. {
  140. /// <summary>
  141. /// What inventory slots should this event be relayed to, if any?
  142. /// </summary>
  143. /// <remarks>
  144. /// In general you may want to exclude <see cref="SlotFlags.POCKET"/>, given that those items are not truly
  145. /// "equipped" by the user.
  146. /// </remarks>
  147. public SlotFlags TargetSlots { get; }
  148. }