1
0

SharedItemSystem.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using Content.Shared.Hands.EntitySystems;
  2. using Content.Shared.Interaction;
  3. using Content.Shared.Verbs;
  4. using Content.Shared.Examine;
  5. using Content.Shared.Item.ItemToggle.Components;
  6. using Content.Shared.Storage;
  7. using JetBrains.Annotations;
  8. using Robust.Shared.Containers;
  9. using Robust.Shared.GameStates;
  10. using Robust.Shared.Prototypes;
  11. using Robust.Shared.Utility;
  12. namespace Content.Shared.Item;
  13. public abstract class SharedItemSystem : EntitySystem
  14. {
  15. [Dependency] private readonly IPrototypeManager _prototype = default!;
  16. [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
  17. [Dependency] protected readonly SharedContainerSystem Container = default!;
  18. public override void Initialize()
  19. {
  20. base.Initialize();
  21. SubscribeLocalEvent<ItemComponent, GetVerbsEvent<InteractionVerb>>(AddPickupVerb);
  22. SubscribeLocalEvent<ItemComponent, InteractHandEvent>(OnHandInteract);
  23. SubscribeLocalEvent<ItemComponent, AfterAutoHandleStateEvent>(OnItemAutoState);
  24. SubscribeLocalEvent<ItemComponent, ExaminedEvent>(OnExamine);
  25. SubscribeLocalEvent<ItemToggleSizeComponent, ItemToggledEvent>(OnItemToggle);
  26. }
  27. private void OnItemAutoState(EntityUid uid, ItemComponent component, ref AfterAutoHandleStateEvent args)
  28. {
  29. SetHeldPrefix(uid, component.HeldPrefix, force: true, component);
  30. }
  31. #region Public API
  32. public void SetSize(EntityUid uid, ProtoId<ItemSizePrototype> size, ItemComponent? component = null)
  33. {
  34. if (!Resolve(uid, ref component, false))
  35. return;
  36. component.Size = size;
  37. Dirty(uid, component);
  38. }
  39. public void SetShape(EntityUid uid, List<Box2i>? shape, ItemComponent? component = null)
  40. {
  41. if (!Resolve(uid, ref component, false))
  42. return;
  43. component.Shape = shape;
  44. Dirty(uid, component);
  45. }
  46. public void SetHeldPrefix(EntityUid uid, string? heldPrefix, bool force = false, ItemComponent? component = null)
  47. {
  48. if (!Resolve(uid, ref component, false))
  49. return;
  50. if (!force && component.HeldPrefix == heldPrefix)
  51. return;
  52. component.HeldPrefix = heldPrefix;
  53. Dirty(uid, component);
  54. VisualsChanged(uid);
  55. }
  56. /// <summary>
  57. /// Copy all item specific visuals from another item.
  58. /// </summary>
  59. public void CopyVisuals(EntityUid uid, ItemComponent otherItem, ItemComponent? item = null)
  60. {
  61. if (!Resolve(uid, ref item))
  62. return;
  63. item.RsiPath = otherItem.RsiPath;
  64. item.InhandVisuals = otherItem.InhandVisuals;
  65. item.HeldPrefix = otherItem.HeldPrefix;
  66. Dirty(uid, item);
  67. VisualsChanged(uid);
  68. }
  69. #endregion
  70. private void OnHandInteract(EntityUid uid, ItemComponent component, InteractHandEvent args)
  71. {
  72. if (args.Handled)
  73. return;
  74. args.Handled = _handsSystem.TryPickup(args.User, uid, animateUser: false);
  75. }
  76. private void AddPickupVerb(EntityUid uid, ItemComponent component, GetVerbsEvent<InteractionVerb> args)
  77. {
  78. if (args.Hands == null ||
  79. args.Using != null ||
  80. !args.CanAccess ||
  81. !args.CanInteract ||
  82. !_handsSystem.CanPickupAnyHand(args.User, args.Target, handsComp: args.Hands, item: component))
  83. return;
  84. InteractionVerb verb = new();
  85. verb.Act = () => _handsSystem.TryPickupAnyHand(args.User, args.Target, checkActionBlocker: false,
  86. handsComp: args.Hands, item: component);
  87. verb.Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/pickup.svg.192dpi.png"));
  88. // if the item already in a container (that is not the same as the user's), then change the text.
  89. // this occurs when the item is in their inventory or in an open backpack
  90. Container.TryGetContainingContainer((args.User, null, null), out var userContainer);
  91. if (Container.TryGetContainingContainer((args.Target, null, null), out var container) && container != userContainer)
  92. verb.Text = Loc.GetString("pick-up-verb-get-data-text-inventory");
  93. else
  94. verb.Text = Loc.GetString("pick-up-verb-get-data-text");
  95. args.Verbs.Add(verb);
  96. }
  97. private void OnExamine(EntityUid uid, ItemComponent component, ExaminedEvent args)
  98. {
  99. // show at end of message generally
  100. args.PushMarkup(Loc.GetString("item-component-on-examine-size",
  101. ("size", GetItemSizeLocale(component.Size))), priority: -1);
  102. }
  103. public ItemSizePrototype GetSizePrototype(ProtoId<ItemSizePrototype> id)
  104. {
  105. return _prototype.Index(id);
  106. }
  107. /// <summary>
  108. /// Notifies any entity that is holding or wearing this item that they may need to update their sprite.
  109. /// </summary>
  110. /// <remarks>
  111. /// This is used for updating both inhand sprites and clothing sprites, but it's here just cause it needs to
  112. /// be in one place.
  113. /// </remarks>
  114. public virtual void VisualsChanged(EntityUid owner)
  115. {
  116. }
  117. [PublicAPI]
  118. public string GetItemSizeLocale(ProtoId<ItemSizePrototype> size)
  119. {
  120. return Loc.GetString(GetSizePrototype(size).Name);
  121. }
  122. [PublicAPI]
  123. public int GetItemSizeWeight(ProtoId<ItemSizePrototype> size)
  124. {
  125. return GetSizePrototype(size).Weight;
  126. }
  127. /// <summary>
  128. /// Gets the default shape of an item.
  129. /// </summary>
  130. public IReadOnlyList<Box2i> GetItemShape(Entity<ItemComponent?> uid)
  131. {
  132. if (!Resolve(uid, ref uid.Comp))
  133. return new Box2i[] { };
  134. return uid.Comp.Shape ?? GetSizePrototype(uid.Comp.Size).DefaultShape;
  135. }
  136. /// <summary>
  137. /// Gets the default shape of an item.
  138. /// </summary>
  139. public IReadOnlyList<Box2i> GetItemShape(ItemComponent component)
  140. {
  141. return component.Shape ?? GetSizePrototype(component.Size).DefaultShape;
  142. }
  143. /// <summary>
  144. /// Gets the shape of an item, adjusting for rotation and offset.
  145. /// </summary>
  146. public IReadOnlyList<Box2i> GetAdjustedItemShape(Entity<ItemComponent?> entity, ItemStorageLocation location)
  147. {
  148. return GetAdjustedItemShape(entity, location.Rotation, location.Position);
  149. }
  150. /// <summary>
  151. /// Gets the shape of an item, adjusting for rotation and offset.
  152. /// </summary>
  153. public IReadOnlyList<Box2i> GetAdjustedItemShape(Entity<ItemComponent?> entity, Angle rotation, Vector2i position)
  154. {
  155. if (!Resolve(entity, ref entity.Comp))
  156. return new Box2i[] { };
  157. var shapes = GetItemShape(entity);
  158. var boundingShape = shapes.GetBoundingBox();
  159. var boundingCenter = ((Box2) boundingShape).Center;
  160. var matty = Matrix3Helpers.CreateTransform(boundingCenter, rotation);
  161. var drift = boundingShape.BottomLeft - matty.TransformBox(boundingShape).BottomLeft;
  162. var adjustedShapes = new List<Box2i>();
  163. foreach (var shape in shapes)
  164. {
  165. var transformed = matty.TransformBox(shape).Translated(drift);
  166. var floored = new Box2i(transformed.BottomLeft.Floored(), transformed.TopRight.Floored());
  167. var translated = floored.Translated(position);
  168. adjustedShapes.Add(translated);
  169. }
  170. return adjustedShapes;
  171. }
  172. /// <summary>
  173. /// Used to update the Item component on item toggle (specifically size).
  174. /// </summary>
  175. private void OnItemToggle(EntityUid uid, ItemToggleSizeComponent itemToggleSize, ItemToggledEvent args)
  176. {
  177. if (!TryComp(uid, out ItemComponent? item))
  178. return;
  179. if (args.Activated)
  180. {
  181. if (itemToggleSize.ActivatedShape != null)
  182. {
  183. // Set the deactivated shape to the default item's shape before it gets changed.
  184. itemToggleSize.DeactivatedShape ??= new List<Box2i>(GetItemShape(item));
  185. SetShape(uid, itemToggleSize.ActivatedShape, item);
  186. }
  187. if (itemToggleSize.ActivatedSize != null)
  188. {
  189. // Set the deactivated size to the default item's size before it gets changed.
  190. itemToggleSize.DeactivatedSize ??= item.Size;
  191. SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.ActivatedSize, item);
  192. }
  193. }
  194. else
  195. {
  196. if (itemToggleSize.DeactivatedShape != null)
  197. {
  198. SetShape(uid, itemToggleSize.DeactivatedShape, item);
  199. }
  200. if (itemToggleSize.DeactivatedSize != null)
  201. {
  202. SetSize(uid, (ProtoId<ItemSizePrototype>) itemToggleSize.DeactivatedSize, item);
  203. }
  204. }
  205. Dirty(uid, item);
  206. }
  207. }