SharedItemMapperSystem.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. using System.Linq;
  2. using Content.Shared.Storage.Components;
  3. using Content.Shared.Whitelist;
  4. using JetBrains.Annotations;
  5. using Robust.Shared.Containers;
  6. namespace Content.Shared.Storage.EntitySystems;
  7. /// <summary>
  8. /// <c>ItemMapperSystem</c> is a system that on each initialization, insertion, removal of an entity from
  9. /// given <see cref="ItemMapperComponent"/> (with appropriate storage attached) will check each stored item to see
  10. /// if its tags/component, and overall quantity match <see cref="ItemMapperComponent.MapLayers"/>.
  11. /// </summary>
  12. [UsedImplicitly]
  13. public abstract class SharedItemMapperSystem : EntitySystem
  14. {
  15. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  16. [Dependency] private readonly SharedContainerSystem _container = default!;
  17. [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
  18. /// <inheritdoc />
  19. public override void Initialize()
  20. {
  21. base.Initialize();
  22. SubscribeLocalEvent<ItemMapperComponent, ComponentInit>(InitLayers);
  23. SubscribeLocalEvent<ItemMapperComponent, EntInsertedIntoContainerMessage>(MapperEntityInserted);
  24. SubscribeLocalEvent<ItemMapperComponent, EntRemovedFromContainerMessage>(MapperEntityRemoved);
  25. }
  26. private void InitLayers(EntityUid uid, ItemMapperComponent component, ComponentInit args)
  27. {
  28. foreach (var (layerName, val) in component.MapLayers)
  29. {
  30. val.Layer = layerName;
  31. }
  32. if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent))
  33. {
  34. var list = new List<string>(component.MapLayers.Keys);
  35. _appearance.SetData(uid, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent);
  36. }
  37. // Ensure appearance is correct with current contained entities.
  38. UpdateAppearance(uid, component);
  39. }
  40. private void MapperEntityRemoved(EntityUid uid, ItemMapperComponent itemMapper, EntRemovedFromContainerMessage args)
  41. {
  42. if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID))
  43. return;
  44. UpdateAppearance(uid, itemMapper);
  45. }
  46. private void MapperEntityInserted(EntityUid uid,
  47. ItemMapperComponent itemMapper,
  48. EntInsertedIntoContainerMessage args)
  49. {
  50. if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID))
  51. return;
  52. UpdateAppearance(uid, itemMapper);
  53. }
  54. private void UpdateAppearance(EntityUid uid, ItemMapperComponent? itemMapper = null)
  55. {
  56. if (!Resolve(uid, ref itemMapper))
  57. return;
  58. if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)
  59. && TryGetLayers(uid, itemMapper, out var containedLayers))
  60. {
  61. _appearance.SetData(uid,
  62. StorageMapVisuals.LayerChanged,
  63. new ShowLayerData(containedLayers),
  64. appearanceComponent);
  65. }
  66. }
  67. /// <summary>
  68. /// Method that iterates over storage of the entity in <paramref name="uid"/> and sets <paramref name="showLayers"/>
  69. /// according to <paramref name="itemMapper"/> definition. It will have O(n*m) time behavior
  70. /// (n - number of entities in container, and m - number of definitions in <paramref name="showLayers"/>).
  71. /// </summary>
  72. /// <param name="uid">EntityUid used to search the storage</param>
  73. /// <param name="itemMapper">component that contains definition used to map
  74. /// <see cref="EntityWhitelist">Whitelist</see> in <see cref="ItemMapperComponent.MapLayers"/> to string.
  75. /// </param>
  76. /// <param name="showLayers">list of <paramref name="itemMapper"/> layers that should be visible</param>
  77. /// <returns>false if <c>msg.Container.Owner</c> is not a storage, true otherwise.</returns>
  78. private bool TryGetLayers(EntityUid uid, ItemMapperComponent itemMapper, out List<string> showLayers)
  79. {
  80. var containedLayers = _container.GetAllContainers(uid)
  81. .Where(c => itemMapper.ContainerWhitelist?.Contains(c.ID) ?? true)
  82. .SelectMany(cont => cont.ContainedEntities)
  83. .ToArray();
  84. var list = new List<string>();
  85. foreach (var mapLayerData in itemMapper.MapLayers.Values)
  86. {
  87. var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPassOrNull(mapLayerData.Whitelist,
  88. ent));
  89. if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount)
  90. {
  91. list.Add(mapLayerData.Layer);
  92. }
  93. }
  94. showLayers = list;
  95. return true;
  96. }
  97. }