MaskSystem.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. using Content.Shared.Actions;
  2. using Content.Shared.Clothing.Components;
  3. using Content.Shared.Foldable;
  4. using Content.Shared.Inventory;
  5. using Content.Shared.Inventory.Events;
  6. using Content.Shared.Popups;
  7. using Robust.Shared.Timing;
  8. namespace Content.Shared.Clothing.EntitySystems;
  9. public sealed class MaskSystem : EntitySystem
  10. {
  11. [Dependency] private readonly SharedActionsSystem _actionSystem = default!;
  12. [Dependency] private readonly InventorySystem _inventorySystem = default!;
  13. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  14. [Dependency] private readonly IGameTiming _timing = default!;
  15. [Dependency] private readonly ClothingSystem _clothing = default!;
  16. public override void Initialize()
  17. {
  18. base.Initialize();
  19. SubscribeLocalEvent<MaskComponent, ToggleMaskEvent>(OnToggleMask);
  20. SubscribeLocalEvent<MaskComponent, GetItemActionsEvent>(OnGetActions);
  21. SubscribeLocalEvent<MaskComponent, GotUnequippedEvent>(OnGotUnequipped);
  22. SubscribeLocalEvent<MaskComponent, FoldedEvent>(OnFolded);
  23. }
  24. private void OnGetActions(EntityUid uid, MaskComponent component, GetItemActionsEvent args)
  25. {
  26. if (_inventorySystem.InSlotWithFlags(uid, SlotFlags.MASK))
  27. args.AddAction(ref component.ToggleActionEntity, component.ToggleAction);
  28. }
  29. private void OnToggleMask(Entity<MaskComponent> ent, ref ToggleMaskEvent args)
  30. {
  31. var (uid, mask) = ent;
  32. if (mask.ToggleActionEntity == null || !mask.IsToggleable)
  33. return;
  34. // Masks are currently only toggleable via the action while equipped.
  35. // Its possible this might change in future?
  36. // TODO Inventory / Clothing
  37. // Add an easier way to check if clothing is equipped to a valid slot.
  38. if (!TryComp(ent, out ClothingComponent? clothing)
  39. || clothing.InSlotFlag is not { } slotFlag
  40. || !clothing.Slots.HasFlag(slotFlag))
  41. {
  42. return;
  43. }
  44. SetToggled((uid, mask), !mask.IsToggled);
  45. var dir = mask.IsToggled ? "down" : "up";
  46. var msg = $"action-mask-pull-{dir}-popup-message";
  47. _popupSystem.PopupClient(Loc.GetString(msg, ("mask", uid)), args.Performer, args.Performer);
  48. }
  49. private void OnGotUnequipped(EntityUid uid, MaskComponent mask, GotUnequippedEvent args)
  50. {
  51. // Masks are currently always un-toggled when unequipped.
  52. SetToggled((uid, mask), false);
  53. }
  54. private void OnFolded(Entity<MaskComponent> ent, ref FoldedEvent args)
  55. {
  56. // See FoldableClothingComponent
  57. if (!ent.Comp.DisableOnFolded)
  58. return;
  59. // While folded, we force the mask to be toggled / pulled down, so that its functionality as a mask is disabled,
  60. // and we also prevent it from being un-toggled. We also automatically untoggle it when it gets unfolded, so it
  61. // fully returns to its previous state when folded & unfolded.
  62. SetToggled(ent!, args.IsFolded, force: true);
  63. SetToggleable(ent!, !args.IsFolded);
  64. }
  65. public void SetToggled(Entity<MaskComponent?> mask, bool toggled, bool force = false)
  66. {
  67. if (_timing.ApplyingState)
  68. return;
  69. if (!Resolve(mask.Owner, ref mask.Comp))
  70. return;
  71. if (!force && !mask.Comp.IsToggleable)
  72. return;
  73. if (mask.Comp.IsToggled == toggled)
  74. return;
  75. mask.Comp.IsToggled = toggled;
  76. if (mask.Comp.ToggleActionEntity is { } action)
  77. _actionSystem.SetToggled(action, mask.Comp.IsToggled);
  78. // TODO Generalize toggling & clothing prefixes. See also FoldableClothingComponent
  79. var prefix = mask.Comp.IsToggled ? mask.Comp.EquippedPrefix : null;
  80. _clothing.SetEquippedPrefix(mask, prefix);
  81. // TODO Inventory / Clothing
  82. // Add an easier way to get the entity that is wearing clothing in a valid slot.
  83. EntityUid? wearer = null;
  84. if (TryComp(mask, out ClothingComponent? clothing)
  85. && clothing.InSlotFlag is {} slotFlag
  86. && clothing.Slots.HasFlag(slotFlag))
  87. {
  88. wearer = Transform(mask).ParentUid;
  89. }
  90. var maskEv = new ItemMaskToggledEvent(mask!, wearer);
  91. RaiseLocalEvent(mask, ref maskEv);
  92. if (wearer != null)
  93. {
  94. var wearerEv = new WearerMaskToggledEvent(mask!);
  95. RaiseLocalEvent(wearer.Value, ref wearerEv);
  96. }
  97. Dirty(mask);
  98. }
  99. public void SetToggleable(Entity<MaskComponent?> mask, bool toggleable)
  100. {
  101. if (_timing.ApplyingState)
  102. return;
  103. if (!Resolve(mask.Owner, ref mask.Comp))
  104. return;
  105. if (mask.Comp.IsToggleable == toggleable)
  106. return;
  107. if (mask.Comp.ToggleActionEntity is { } action)
  108. _actionSystem.SetEnabled(action, mask.Comp.IsToggleable);
  109. mask.Comp.IsToggleable = toggleable;
  110. Dirty(mask);
  111. }
  112. }