diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs index 0122decdc2..2301d11248 100644 --- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs +++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs @@ -26,21 +26,18 @@ public sealed class StoreBoundUserInterface : BoundUserInterface _menu.OnListingButtonPressed += (_, listing) => { - if (_menu.CurrentBuyer != null) - SendMessage(new StoreBuyListingMessage(_menu.CurrentBuyer.Value, listing)); + SendMessage(new StoreBuyListingMessage(listing)); }; _menu.OnCategoryButtonPressed += (_, category) => { _menu.CurrentCategory = category; - if (_menu.CurrentBuyer != null) - SendMessage(new StoreRequestUpdateInterfaceMessage(_menu.CurrentBuyer.Value)); + SendMessage(new StoreRequestUpdateInterfaceMessage()); }; _menu.OnWithdrawAttempt += (_, type, amount) => { - if (_menu.CurrentBuyer != null) - SendMessage(new StoreRequestWithdrawMessage(_menu.CurrentBuyer.Value, type, amount)); + SendMessage(new StoreRequestWithdrawMessage(type, amount)); }; } protected override void UpdateState(BoundUserInterfaceState state) @@ -53,8 +50,6 @@ public sealed class StoreBoundUserInterface : BoundUserInterface switch (state) { case StoreUpdateState msg: - if (msg.Buyer != null) - _menu.CurrentBuyer = msg.Buyer; _menu.UpdateBalance(msg.Balance); _menu.PopulateStoreCategoryButtons(msg.Listings); _menu.UpdateListing(msg.Listings.ToList()); diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs index ca51c174b0..fff07c2262 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml.cs +++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs @@ -25,7 +25,6 @@ public sealed partial class StoreMenu : DefaultWindow public event Action? OnCategoryButtonPressed; public event Action? OnWithdrawAttempt; - public EntityUid? CurrentBuyer; public Dictionary Balance = new(); public string CurrentCategory = string.Empty; @@ -212,7 +211,6 @@ public sealed partial class StoreMenu : DefaultWindow public override void Close() { base.Close(); - CurrentBuyer = null; _withdrawWindow?.Close(); } diff --git a/Content.Server/Store/Systems/StoreSystem.Listings.cs b/Content.Server/Store/Systems/StoreSystem.Listings.cs index 9dd1ec96a8..55bd2ccfa3 100644 --- a/Content.Server/Store/Systems/StoreSystem.Listings.cs +++ b/Content.Server/Store/Systems/StoreSystem.Listings.cs @@ -63,23 +63,23 @@ public sealed partial class StoreSystem : EntitySystem /// /// Gets the available listings for a store /// - /// The person getting the listings. + /// 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 user, StoreComponent component) + public IEnumerable GetAvailableListings(EntityUid buyer, StoreComponent component) { - return GetAvailableListings(user, component.Listings, component.Categories, component.Owner); + return GetAvailableListings(buyer, component.Listings, component.Categories, component.Owner); } /// /// Gets the available listings for a user given an overall set of listings and categories to filter by. /// - /// The person getting the listings. + /// 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 user, HashSet? listings, HashSet categories, EntityUid? storeEntity = null) + public IEnumerable GetAvailableListings(EntityUid buyer, HashSet? listings, HashSet categories, EntityUid? storeEntity = null) { if (listings == null) listings = GetAllListings(); @@ -91,7 +91,7 @@ public sealed partial class StoreSystem : EntitySystem if (listing.Conditions != null) { - var args = new ListingConditionArgs(user, storeEntity, listing, EntityManager); + var args = new ListingConditionArgs(buyer, storeEntity, listing, EntityManager); var conditionsMet = true; foreach (var condition in listing.Conditions) diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 16504ac279..7d03fc19b7 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -27,7 +27,7 @@ public sealed partial class StoreSystem : EntitySystem private void InitializeUi() { - SubscribeLocalEvent((_,c,r) => UpdateUserInterface(r.CurrentBuyer, c)); + SubscribeLocalEvent((_,c,r) => UpdateUserInterface(r.Session.AttachedEntity, c)); SubscribeLocalEvent(OnBuyRequest); SubscribeLocalEvent(OnRequestWithdraw); } @@ -72,13 +72,9 @@ public sealed partial class StoreSystem : EntitySystem } //this is the person who will be passed into logic for all listing filtering. - var buyer = user; - if (buyer != null) //if we have no "buyer" for this update, then don't update the listings + if (user != null) //if we have no "buyer" for this update, then don't update the listings { - if (component.AccountOwner != null) //if we have one stored, then use that instead - buyer = component.AccountOwner.Value; - - component.LastAvailableListings = GetAvailableListings(buyer.Value, component).ToHashSet(); + component.LastAvailableListings = GetAvailableListings(component.AccountOwner ?? user.Value, component).ToHashSet(); } //dictionary for all currencies, including 0 values for currencies on the whitelist @@ -91,7 +87,10 @@ public sealed partial class StoreSystem : EntitySystem allCurrency[supported] = component.Balance[supported]; } - var state = new StoreUpdateState(buyer, component.LastAvailableListings, allCurrency); + // TODO: if multiple users are supposed to be able to interact with a single BUI & see different + // stores/listings, this needs to use session specific BUI states. + + var state = new StoreUpdateState(component.LastAvailableListings, allCurrency); _ui.SetUiState(ui, state); } @@ -107,13 +106,17 @@ public sealed partial class StoreSystem : EntitySystem return; } + if (msg.Session.AttachedEntity is not { Valid: true } buyer) + return; + //verify that we can actually buy this listing and it wasn't added if (!ListingHasCategory(listing, component.Categories)) return; + //condition checking because why not if (listing.Conditions != null) { - var args = new ListingConditionArgs(msg.Buyer, component.Owner, listing, EntityManager); + var args = new ListingConditionArgs(component.AccountOwner ?? buyer, component.Owner, listing, EntityManager); var conditionsMet = listing.Conditions.All(condition => condition.Condition(args)); if (!conditionsMet) @@ -135,15 +138,15 @@ public sealed partial class StoreSystem : EntitySystem //spawn entity if (listing.ProductEntity != null) { - var product = Spawn(listing.ProductEntity, Transform(msg.Buyer).Coordinates); - _hands.TryPickupAnyHand(msg.Buyer, product); + var product = Spawn(listing.ProductEntity, Transform(buyer).Coordinates); + _hands.PickupOrDrop(buyer, product); } //give action if (listing.ProductAction != null) { var action = new InstantAction(_proto.Index(listing.ProductAction)); - _actions.AddAction(msg.Buyer, action, null); + _actions.AddAction(buyer, action, null); } //broadcast event @@ -153,7 +156,7 @@ public sealed partial class StoreSystem : EntitySystem } //log dat shit. - if (TryComp(msg.Buyer, out var mind)) + if (TryComp(buyer, out var mind)) { _admin.Add(LogType.StorePurchase, LogImpact.Low, $"{ToPrettyString(mind.Owner):player} purchased listing \"{listing.Name}\" from {ToPrettyString(uid)}"); @@ -162,7 +165,7 @@ public sealed partial class StoreSystem : EntitySystem listing.PurchaseAmount++; //track how many times something has been purchased _audio.Play(component.BuySuccessSound, Filter.SinglePlayer(msg.Session), uid); //cha-ching! - UpdateUserInterface(msg.Buyer, component); + UpdateUserInterface(buyer, component); } /// @@ -186,8 +189,11 @@ public sealed partial class StoreSystem : EntitySystem if (proto.Cash == null || !proto.CanWithdraw) return; + if (msg.Session.AttachedEntity is not { Valid: true} buyer) + return; + FixedPoint2 amountRemaining = msg.Amount; - var coordinates = Transform(msg.Buyer).Coordinates; + var coordinates = Transform(buyer).Coordinates; var sortedCashValues = proto.Cash.Keys.OrderByDescending(x => x).ToList(); foreach (var value in sortedCashValues) @@ -210,7 +216,7 @@ public sealed partial class StoreSystem : EntitySystem var maxAmount = Math.Min(amountToRemove, stack.MaxCount); //limit it based on max stack amount _stack.SetCount(ent, maxAmount, stack); - _hands.TryPickupAnyHand(msg.Buyer, ent); + _hands.PickupOrDrop(buyer, ent); amountToRemove -= maxAmount; } } @@ -219,13 +225,13 @@ public sealed partial class StoreSystem : EntitySystem for (var i = 0; i < amountToSpawn; i++) { var ent = Spawn(cashId, coordinates); - _hands.TryPickupAnyHand(msg.Buyer, ent); + _hands.PickupOrDrop(buyer, ent); } } amountRemaining -= value * amountToSpawn; } component.Balance[msg.Currency] -= msg.Amount; - UpdateUserInterface(msg.Buyer, component); + UpdateUserInterface(buyer, component); } } diff --git a/Content.Shared/Store/ListingCondition.cs b/Content.Shared/Store/ListingCondition.cs index 77d9bacbcf..7b205bc160 100644 --- a/Content.Shared/Store/ListingCondition.cs +++ b/Content.Shared/Store/ListingCondition.cs @@ -17,7 +17,7 @@ public abstract class ListingCondition public abstract bool Condition(ListingConditionArgs args); } -/// The person purchasing the listing -/// The liting itself +/// Either the account owner, user, or an inanimate object (e.g., surplus bundle) +/// The listing itself /// An entitymanager for sane coding public readonly record struct ListingConditionArgs(EntityUid Buyer, EntityUid? StoreEntity, ListingData Listing, IEntityManager EntityManager); diff --git a/Content.Shared/Store/StoreUi.cs b/Content.Shared/Store/StoreUi.cs index f364c11377..6e3838d368 100644 --- a/Content.Shared/Store/StoreUi.cs +++ b/Content.Shared/Store/StoreUi.cs @@ -1,5 +1,4 @@ using Content.Shared.FixedPoint; -using Content.Shared.MobState; using Robust.Shared.Serialization; namespace Content.Shared.Store; @@ -13,15 +12,12 @@ public enum StoreUiKey : byte [Serializable, NetSerializable] public sealed class StoreUpdateState : BoundUserInterfaceState { - public readonly EntityUid? Buyer; - public readonly HashSet Listings; public readonly Dictionary Balance; - public StoreUpdateState(EntityUid? buyer, HashSet listings, Dictionary balance) + public StoreUpdateState(HashSet listings, Dictionary balance) { - Buyer = buyer; Listings = listings; Balance = balance; } @@ -44,24 +40,18 @@ public sealed class StoreInitializeState : BoundUserInterfaceState [Serializable, NetSerializable] public sealed class StoreRequestUpdateInterfaceMessage : BoundUserInterfaceMessage { - public EntityUid CurrentBuyer; - - public StoreRequestUpdateInterfaceMessage(EntityUid currentBuyer) + public StoreRequestUpdateInterfaceMessage() { - CurrentBuyer = currentBuyer; } } [Serializable, NetSerializable] public sealed class StoreBuyListingMessage : BoundUserInterfaceMessage { - public EntityUid Buyer; - public ListingData Listing; - public StoreBuyListingMessage(EntityUid buyer, ListingData listing) + public StoreBuyListingMessage(ListingData listing) { - Buyer = buyer; Listing = listing; } } @@ -69,15 +59,12 @@ public sealed class StoreBuyListingMessage : BoundUserInterfaceMessage [Serializable, NetSerializable] public sealed class StoreRequestWithdrawMessage : BoundUserInterfaceMessage { - public EntityUid Buyer; - public string Currency; public int Amount; - public StoreRequestWithdrawMessage(EntityUid buyer, string currency, int amount) + public StoreRequestWithdrawMessage(string currency, int amount) { - Buyer = buyer; Currency = currency; Amount = amount; }