using System.Linq; using Content.Server.Store.Components; using Content.Shared.FixedPoint; using Content.Shared.Store; using FastAccessors; using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Store.Systems; public sealed partial class StoreSystem { // WD START [Dependency] private readonly IRobustRandom _random = default!; // WD private void ApplySales(IEnumerable listings, StorePresetPrototype store) { if (!store.Sales.Enabled) return; var count = _random.Next(store.Sales.MinItems, store.Sales.MaxItems + 1); listings = listings .Where(l => !l.SaleBlacklist && l.Cost.Any(x => x.Value > 1) && store.Categories.Overlaps(ChangedFormatCategories(l.Categories))) .OrderBy(_ => _random.Next()).Take(count).ToList(); foreach (var listing in listings) { var sale = GetSale(store.Sales.MinMultiplier, store.Sales.MaxMultiplier); var newCost = listing.Cost.ToDictionary(x => x.Key, x => FixedPoint2.New(Math.Max(1, (int) MathF.Round(x.Value.Float() * sale)))); if (listing.Cost.All(x => x.Value.Int() == newCost[x.Key].Int())) continue; var key = listing.Cost.First(x => x.Value > 0).Key; listing.OldCost = listing.Cost; listing.SaleAmount = 100 - (newCost[key] / listing.Cost[key] * 100).Int(); listing.Cost = newCost; listing.Categories = new() {store.Sales.SalesCategory}; } } private IEnumerable ChangedFormatCategories(List> categories) { var modified = from p in categories select p.Id; return modified; } private float GetSale(float minMultiplier, float maxMultiplier) { return _random.NextFloat() * (maxMultiplier - minMultiplier) + minMultiplier; } // WD END /// /// 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) { component.Listings = GetAllListings(); } /// /// Gets all listings from a prototype. /// /// All the listings public HashSet GetAllListings() { var allListings = _proto.EnumeratePrototypes(); var allData = new HashSet(); foreach (var listing in allListings) { allData.Add((ListingData) listing.Clone()); } return allData; } /// /// Adds a listing from an Id to a store /// /// The store to add the listing to /// The id of the listing /// Whetehr 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, ListingData listing) { return component.Listings.Add(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.Listings, 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, HashSet? 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(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; } } /// /// 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; } }