using System.Diagnostics.CodeAnalysis; using Content.Shared.Mind; using Content.Shared.Store; using Content.Shared.Store.Components; using Robust.Shared.Prototypes; namespace Content.Server.Store.Systems; public sealed partial class StoreSystem { /// /// Refreshes all listings on a store. /// Do not use if you don't know what you're doing. /// /// The store to refresh public void RefreshAllListings(StoreComponent component) { var previousState = component.FullListingsCatalog; var newState = GetAllListings(); // if we refresh list with existing cost modifiers - they will be removed, // need to restore them if (previousState.Count != 0) { foreach (var previousStateListingItem in previousState) { if (!previousStateListingItem.IsCostModified || !TryGetListing(newState, previousStateListingItem.ID, out var found)) { continue; } foreach (var (modifierSourceId, costModifier) in previousStateListingItem.CostModifiersBySourceId) { found.AddCostModifier(modifierSourceId, costModifier); } } } component.FullListingsCatalog = newState; } /// /// Gets all listings from a prototype. /// /// All the listings public HashSet GetAllListings() { var clones = new HashSet(); foreach (var prototype in _proto.EnumeratePrototypes()) { clones.Add(new ListingDataWithCostModifiers(prototype)); } return clones; } /// /// Adds a listing from an Id to a store /// /// The store to add the listing to /// The id of the listing /// Whether or not the listing was added successfully public bool TryAddListing(StoreComponent component, string listingId) { if (!_proto.TryIndex(listingId, out var proto)) { Log.Error("Attempted to add invalid listing."); return false; } return TryAddListing(component, proto); } /// /// Adds a listing to a store /// /// The store to add the listing to /// The listing /// Whether or not the listing was add successfully public bool TryAddListing(StoreComponent component, ListingPrototype listing) { return component.FullListingsCatalog.Add(new ListingDataWithCostModifiers(listing)); } /// /// Gets the available listings for a store /// /// Either the account owner, user, or an inanimate object (e.g., surplus bundle) /// /// The store the listings are coming from. /// The available listings. public IEnumerable GetAvailableListings(EntityUid buyer, EntityUid store, StoreComponent component) { return GetAvailableListings(buyer, component.FullListingsCatalog, component.Categories, store); } /// /// Gets the available listings for a user given an overall set of listings and categories to filter by. /// /// Either the account owner, user, or an inanimate object (e.g., surplus bundle) /// All of the listings that are available. If null, will just get all listings from the prototypes. /// What categories to filter by. /// The physial entity of the store. Can be null. /// The available listings. public IEnumerable GetAvailableListings( EntityUid buyer, IReadOnlyCollection? listings, HashSet> categories, EntityUid? storeEntity = null ) { listings ??= GetAllListings(); foreach (var listing in listings) { if (!ListingHasCategory(listing, categories)) continue; if (listing.Conditions != null) { var args = new ListingConditionArgs(GetBuyerMind(buyer), storeEntity, listing, EntityManager); var conditionsMet = true; foreach (var condition in listing.Conditions) { if (!condition.Condition(args)) { conditionsMet = false; break; } } if (!conditionsMet) continue; } yield return listing; } } /// /// Returns the entity's mind entity, if it has one, to be used for listing conditions. /// If it doesn't have one, or is a mind entity already, it returns itself. /// /// The buying entity. public EntityUid GetBuyerMind(EntityUid buyer) { if (!HasComp(buyer) && _mind.TryGetMind(buyer, out var buyerMind, out var _)) return buyerMind; return buyer; } /// /// Checks if a listing appears in a list of given categories /// /// The listing itself. /// The categories to check through. /// If the listing was present in one of the categories. public bool ListingHasCategory(ListingData listing, HashSet> categories) { foreach (var cat in categories) { if (listing.Categories.Contains(cat)) return true; } return false; } private bool TryGetListing(IReadOnlyCollection collection, string listingId, [MaybeNullWhen(false)] out ListingDataWithCostModifiers found) { foreach(var current in collection) { if (current.ID == listingId) { found = current; return true; } } found = null!; return false; } }