1
0

ItemSystem.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Linq;
  3. using Content.Shared.Hands;
  4. using Content.Shared.Inventory.Events;
  5. using Content.Shared.Item;
  6. using Robust.Client.GameObjects;
  7. using Robust.Client.Graphics;
  8. using Robust.Client.ResourceManagement;
  9. using Robust.Shared.Serialization.TypeSerializers.Implementations;
  10. namespace Content.Client.Items.Systems;
  11. public sealed class ItemSystem : SharedItemSystem
  12. {
  13. [Dependency] private readonly IResourceCache _resCache = default!;
  14. public override void Initialize()
  15. {
  16. base.Initialize();
  17. SubscribeLocalEvent<ItemComponent, GetInhandVisualsEvent>(OnGetVisuals);
  18. // TODO is this still needed? Shouldn't containers occlude them?
  19. SubscribeLocalEvent<SpriteComponent, GotEquippedEvent>(OnEquipped);
  20. SubscribeLocalEvent<SpriteComponent, GotUnequippedEvent>(OnUnequipped);
  21. }
  22. private void OnUnequipped(EntityUid uid, SpriteComponent component, GotUnequippedEvent args)
  23. {
  24. component.Visible = true;
  25. }
  26. private void OnEquipped(EntityUid uid, SpriteComponent component, GotEquippedEvent args)
  27. {
  28. component.Visible = false;
  29. }
  30. #region InhandVisuals
  31. /// <summary>
  32. /// When an items visual state changes, notify and entities that are holding this item that their sprite may need updating.
  33. /// </summary>
  34. public override void VisualsChanged(EntityUid uid)
  35. {
  36. // if the item is in a container, it might be equipped to hands or inventory slots --> update visuals.
  37. if (Container.TryGetContainingContainer((uid, null, null), out var container))
  38. RaiseLocalEvent(container.Owner, new VisualsChangedEvent(GetNetEntity(uid), container.ID));
  39. }
  40. /// <summary>
  41. /// An entity holding this item is requesting visual information for in-hand sprites.
  42. /// </summary>
  43. private void OnGetVisuals(EntityUid uid, ItemComponent item, GetInhandVisualsEvent args)
  44. {
  45. var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}";
  46. // try get explicit visuals
  47. if (!item.InhandVisuals.TryGetValue(args.Location, out var layers))
  48. {
  49. // get defaults
  50. if (!TryGetDefaultVisuals(uid, item, defaultKey, out layers))
  51. return;
  52. }
  53. var i = 0;
  54. foreach (var layer in layers)
  55. {
  56. var key = layer.MapKeys?.FirstOrDefault();
  57. if (key == null)
  58. {
  59. key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
  60. i++;
  61. }
  62. args.Layers.Add((key, layer));
  63. }
  64. }
  65. /// <summary>
  66. /// If no explicit in-hand visuals were specified, this attempts to populate with default values.
  67. /// </summary>
  68. /// <remarks>
  69. /// Useful for lazily adding in-hand sprites without modifying yaml. And backwards compatibility.
  70. /// </remarks>
  71. private bool TryGetDefaultVisuals(EntityUid uid, ItemComponent item, string defaultKey, [NotNullWhen(true)] out List<PrototypeLayerData>? result)
  72. {
  73. result = null;
  74. RSI? rsi = null;
  75. if (item.RsiPath != null)
  76. rsi = _resCache.GetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / item.RsiPath).RSI;
  77. else if (TryComp(uid, out SpriteComponent? sprite))
  78. rsi = sprite.BaseRSI;
  79. if (rsi == null)
  80. return false;
  81. var state = (item.HeldPrefix == null)
  82. ? defaultKey
  83. : $"{item.HeldPrefix}-{defaultKey}";
  84. if (!rsi.TryGetState(state, out var _))
  85. return false;
  86. var layer = new PrototypeLayerData();
  87. layer.RsiPath = rsi.Path.ToString();
  88. layer.State = state;
  89. layer.MapKeys = new() { state };
  90. result = new() { layer };
  91. return true;
  92. }
  93. #endregion
  94. }