StoreSystem.Listings.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. using System.Diagnostics.CodeAnalysis;
  2. using Content.Shared.Mind;
  3. using Content.Shared.Store;
  4. using Content.Shared.Store.Components;
  5. using Robust.Shared.Prototypes;
  6. namespace Content.Server.Store.Systems;
  7. public sealed partial class StoreSystem
  8. {
  9. /// <summary>
  10. /// Refreshes all listings on a store.
  11. /// Do not use if you don't know what you're doing.
  12. /// </summary>
  13. /// <param name="component">The store to refresh</param>
  14. public void RefreshAllListings(StoreComponent component)
  15. {
  16. var previousState = component.FullListingsCatalog;
  17. var newState = GetAllListings();
  18. // if we refresh list with existing cost modifiers - they will be removed,
  19. // need to restore them
  20. if (previousState.Count != 0)
  21. {
  22. foreach (var previousStateListingItem in previousState)
  23. {
  24. if (!previousStateListingItem.IsCostModified
  25. || !TryGetListing(newState, previousStateListingItem.ID, out var found))
  26. {
  27. continue;
  28. }
  29. foreach (var (modifierSourceId, costModifier) in previousStateListingItem.CostModifiersBySourceId)
  30. {
  31. found.AddCostModifier(modifierSourceId, costModifier);
  32. }
  33. }
  34. }
  35. component.FullListingsCatalog = newState;
  36. }
  37. /// <summary>
  38. /// Gets all listings from a prototype.
  39. /// </summary>
  40. /// <returns>All the listings</returns>
  41. public HashSet<ListingDataWithCostModifiers> GetAllListings()
  42. {
  43. var clones = new HashSet<ListingDataWithCostModifiers>();
  44. foreach (var prototype in _proto.EnumeratePrototypes<ListingPrototype>())
  45. {
  46. clones.Add(new ListingDataWithCostModifiers(prototype));
  47. }
  48. return clones;
  49. }
  50. /// <summary>
  51. /// Adds a listing from an Id to a store
  52. /// </summary>
  53. /// <param name="component">The store to add the listing to</param>
  54. /// <param name="listingId">The id of the listing</param>
  55. /// <returns>Whether or not the listing was added successfully</returns>
  56. public bool TryAddListing(StoreComponent component, string listingId)
  57. {
  58. if (!_proto.TryIndex<ListingPrototype>(listingId, out var proto))
  59. {
  60. Log.Error("Attempted to add invalid listing.");
  61. return false;
  62. }
  63. return TryAddListing(component, proto);
  64. }
  65. /// <summary>
  66. /// Adds a listing to a store
  67. /// </summary>
  68. /// <param name="component">The store to add the listing to</param>
  69. /// <param name="listing">The listing</param>
  70. /// <returns>Whether or not the listing was add successfully</returns>
  71. public bool TryAddListing(StoreComponent component, ListingPrototype listing)
  72. {
  73. return component.FullListingsCatalog.Add(new ListingDataWithCostModifiers(listing));
  74. }
  75. /// <summary>
  76. /// Gets the available listings for a store
  77. /// </summary>
  78. /// <param name="buyer">Either the account owner, user, or an inanimate object (e.g., surplus bundle)</param>
  79. /// <param name="store"></param>
  80. /// <param name="component">The store the listings are coming from.</param>
  81. /// <returns>The available listings.</returns>
  82. public IEnumerable<ListingDataWithCostModifiers> GetAvailableListings(EntityUid buyer, EntityUid store, StoreComponent component)
  83. {
  84. return GetAvailableListings(buyer, component.FullListingsCatalog, component.Categories, store);
  85. }
  86. /// <summary>
  87. /// Gets the available listings for a user given an overall set of listings and categories to filter by.
  88. /// </summary>
  89. /// <param name="buyer">Either the account owner, user, or an inanimate object (e.g., surplus bundle)</param>
  90. /// <param name="listings">All of the listings that are available. If null, will just get all listings from the prototypes.</param>
  91. /// <param name="categories">What categories to filter by.</param>
  92. /// <param name="storeEntity">The physial entity of the store. Can be null.</param>
  93. /// <returns>The available listings.</returns>
  94. public IEnumerable<ListingDataWithCostModifiers> GetAvailableListings(
  95. EntityUid buyer,
  96. IReadOnlyCollection<ListingDataWithCostModifiers>? listings,
  97. HashSet<ProtoId<StoreCategoryPrototype>> categories,
  98. EntityUid? storeEntity = null
  99. )
  100. {
  101. listings ??= GetAllListings();
  102. foreach (var listing in listings)
  103. {
  104. if (!ListingHasCategory(listing, categories))
  105. continue;
  106. if (listing.Conditions != null)
  107. {
  108. var args = new ListingConditionArgs(GetBuyerMind(buyer), storeEntity, listing, EntityManager);
  109. var conditionsMet = true;
  110. foreach (var condition in listing.Conditions)
  111. {
  112. if (!condition.Condition(args))
  113. {
  114. conditionsMet = false;
  115. break;
  116. }
  117. }
  118. if (!conditionsMet)
  119. continue;
  120. }
  121. yield return listing;
  122. }
  123. }
  124. /// <summary>
  125. /// Returns the entity's mind entity, if it has one, to be used for listing conditions.
  126. /// If it doesn't have one, or is a mind entity already, it returns itself.
  127. /// </summary>
  128. /// <param name="buyer">The buying entity.</param>
  129. public EntityUid GetBuyerMind(EntityUid buyer)
  130. {
  131. if (!HasComp<MindComponent>(buyer) && _mind.TryGetMind(buyer, out var buyerMind, out var _))
  132. return buyerMind;
  133. return buyer;
  134. }
  135. /// <summary>
  136. /// Checks if a listing appears in a list of given categories
  137. /// </summary>
  138. /// <param name="listing">The listing itself.</param>
  139. /// <param name="categories">The categories to check through.</param>
  140. /// <returns>If the listing was present in one of the categories.</returns>
  141. public bool ListingHasCategory(ListingData listing, HashSet<ProtoId<StoreCategoryPrototype>> categories)
  142. {
  143. foreach (var cat in categories)
  144. {
  145. if (listing.Categories.Contains(cat))
  146. return true;
  147. }
  148. return false;
  149. }
  150. private bool TryGetListing(IReadOnlyCollection<ListingDataWithCostModifiers> collection, string listingId, [MaybeNullWhen(false)] out ListingDataWithCostModifiers found)
  151. {
  152. foreach(var current in collection)
  153. {
  154. if (current.ID == listingId)
  155. {
  156. found = current;
  157. return true;
  158. }
  159. }
  160. found = null!;
  161. return false;
  162. }
  163. }