ClothingSystem.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using Content.Shared.Clothing.Components;
  2. using Content.Shared.Hands.Components;
  3. using Content.Shared.Hands.EntitySystems;
  4. using Content.Shared.Humanoid;
  5. using Content.Shared.Interaction.Events;
  6. using Content.Shared.Inventory;
  7. using Content.Shared.Inventory.Events;
  8. using Content.Shared.Item;
  9. using Content.Shared.Strip.Components;
  10. using Robust.Shared.Containers;
  11. using Robust.Shared.GameStates;
  12. namespace Content.Shared.Clothing.EntitySystems;
  13. public abstract class ClothingSystem : EntitySystem
  14. {
  15. [Dependency] private readonly SharedItemSystem _itemSys = default!;
  16. [Dependency] private readonly SharedContainerSystem _containerSys = default!;
  17. [Dependency] private readonly InventorySystem _invSystem = default!;
  18. [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
  19. [Dependency] private readonly HideLayerClothingSystem _hideLayer = default!;
  20. public override void Initialize()
  21. {
  22. base.Initialize();
  23. SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
  24. SubscribeLocalEvent<ClothingComponent, ComponentGetState>(OnGetState);
  25. SubscribeLocalEvent<ClothingComponent, ComponentHandleState>(OnHandleState);
  26. SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
  27. SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
  28. SubscribeLocalEvent<ClothingComponent, ClothingEquipDoAfterEvent>(OnEquipDoAfter);
  29. SubscribeLocalEvent<ClothingComponent, ClothingUnequipDoAfterEvent>(OnUnequipDoAfter);
  30. SubscribeLocalEvent<ClothingComponent, BeforeItemStrippedEvent>(OnItemStripped);
  31. }
  32. private void OnUseInHand(Entity<ClothingComponent> ent, ref UseInHandEvent args)
  33. {
  34. if (args.Handled || !ent.Comp.QuickEquip)
  35. return;
  36. var user = args.User;
  37. if (!TryComp(user, out InventoryComponent? inv) ||
  38. !TryComp(user, out HandsComponent? hands))
  39. return;
  40. QuickEquip(ent, (user, inv, hands));
  41. args.Handled = true;
  42. args.ApplyDelay = false;
  43. }
  44. private void QuickEquip(
  45. Entity<ClothingComponent> toEquipEnt,
  46. Entity<InventoryComponent, HandsComponent> userEnt)
  47. {
  48. foreach (var slotDef in userEnt.Comp1.Slots)
  49. {
  50. if (!_invSystem.CanEquip(userEnt, toEquipEnt, slotDef.Name, out _, slotDef, userEnt, toEquipEnt))
  51. continue;
  52. if (_invSystem.TryGetSlotEntity(userEnt, slotDef.Name, out var slotEntity, userEnt))
  53. {
  54. // Item in slot has to be quick equipable as well
  55. if (TryComp(slotEntity, out ClothingComponent? item) && !item.QuickEquip)
  56. continue;
  57. if (!_invSystem.TryUnequip(userEnt, slotDef.Name, true, inventory: userEnt, checkDoafter: true))
  58. continue;
  59. if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true))
  60. continue;
  61. _handsSystem.PickupOrDrop(userEnt, slotEntity.Value, handsComp: userEnt);
  62. }
  63. else
  64. {
  65. if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true))
  66. continue;
  67. }
  68. break;
  69. }
  70. }
  71. protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
  72. {
  73. component.InSlot = args.Slot;
  74. component.InSlotFlag = args.SlotFlags;
  75. if ((component.Slots & args.SlotFlags) == SlotFlags.NONE)
  76. return;
  77. var gotEquippedEvent = new ClothingGotEquippedEvent(args.Equipee, component);
  78. RaiseLocalEvent(uid, ref gotEquippedEvent);
  79. var didEquippedEvent = new ClothingDidEquippedEvent((uid, component));
  80. RaiseLocalEvent(args.Equipee, ref didEquippedEvent);
  81. }
  82. protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
  83. {
  84. if ((component.Slots & args.SlotFlags) != SlotFlags.NONE)
  85. {
  86. var gotUnequippedEvent = new ClothingGotUnequippedEvent(args.Equipee, component);
  87. RaiseLocalEvent(uid, ref gotUnequippedEvent);
  88. var didUnequippedEvent = new ClothingDidUnequippedEvent((uid, component));
  89. RaiseLocalEvent(args.Equipee, ref didUnequippedEvent);
  90. }
  91. component.InSlot = null;
  92. component.InSlotFlag = null;
  93. }
  94. private void OnGetState(EntityUid uid, ClothingComponent component, ref ComponentGetState args)
  95. {
  96. args.State = new ClothingComponentState(component.EquippedPrefix);
  97. }
  98. private void OnHandleState(EntityUid uid, ClothingComponent component, ref ComponentHandleState args)
  99. {
  100. if (args.Current is not ClothingComponentState state)
  101. return;
  102. SetEquippedPrefix(uid, state.EquippedPrefix, component);
  103. }
  104. private void OnEquipDoAfter(Entity<ClothingComponent> ent, ref ClothingEquipDoAfterEvent args)
  105. {
  106. if (args.Handled || args.Cancelled || args.Target is not { } target)
  107. return;
  108. args.Handled = _invSystem.TryEquip(args.User, target, ent, args.Slot, clothing: ent.Comp, predicted: true, checkDoafter: false);
  109. }
  110. private void OnUnequipDoAfter(Entity<ClothingComponent> ent, ref ClothingUnequipDoAfterEvent args)
  111. {
  112. if (args.Handled || args.Cancelled || args.Target is not { } target)
  113. return;
  114. args.Handled = _invSystem.TryUnequip(args.User, target, args.Slot, clothing: ent.Comp, predicted: true, checkDoafter: false);
  115. if (args.Handled)
  116. _handsSystem.TryPickup(args.User, ent);
  117. }
  118. private void OnItemStripped(Entity<ClothingComponent> ent, ref BeforeItemStrippedEvent args)
  119. {
  120. args.Additive += ent.Comp.StripDelay;
  121. }
  122. #region Public API
  123. public void SetEquippedPrefix(EntityUid uid, string? prefix, ClothingComponent? clothing = null)
  124. {
  125. if (!Resolve(uid, ref clothing, false))
  126. return;
  127. if (clothing.EquippedPrefix == prefix)
  128. return;
  129. clothing.EquippedPrefix = prefix;
  130. _itemSys.VisualsChanged(uid);
  131. Dirty(uid, clothing);
  132. }
  133. public void SetSlots(EntityUid uid, SlotFlags slots, ClothingComponent? clothing = null)
  134. {
  135. if (!Resolve(uid, ref clothing))
  136. return;
  137. clothing.Slots = slots;
  138. Dirty(uid, clothing);
  139. }
  140. /// <summary>
  141. /// Copy all clothing specific visuals from another item.
  142. /// </summary>
  143. public void CopyVisuals(EntityUid uid, ClothingComponent otherClothing, ClothingComponent? clothing = null)
  144. {
  145. if (!Resolve(uid, ref clothing))
  146. return;
  147. clothing.ClothingVisuals = otherClothing.ClothingVisuals;
  148. clothing.EquippedPrefix = otherClothing.EquippedPrefix;
  149. clothing.RsiPath = otherClothing.RsiPath;
  150. _itemSys.VisualsChanged(uid);
  151. Dirty(uid, clothing);
  152. }
  153. public void SetLayerColor(ClothingComponent clothing, string slot, string mapKey, Color? color)
  154. {
  155. foreach (var layer in clothing.ClothingVisuals[slot])
  156. {
  157. if (layer.MapKeys == null)
  158. return;
  159. if (!layer.MapKeys.Contains(mapKey))
  160. continue;
  161. layer.Color = color;
  162. }
  163. }
  164. public void SetLayerState(ClothingComponent clothing, string slot, string mapKey, string state)
  165. {
  166. foreach (var layer in clothing.ClothingVisuals[slot])
  167. {
  168. if (layer.MapKeys == null)
  169. return;
  170. if (!layer.MapKeys.Contains(mapKey))
  171. continue;
  172. layer.State = state;
  173. }
  174. }
  175. #endregion
  176. }